[ApiBundle] Add contract tests for Promotion resources

This commit is contained in:
Wojdylak 2023-11-14 15:33:32 +01:00
parent 1c32900ec5
commit 214f066b08
11 changed files with 407 additions and 18 deletions

View file

@ -40,7 +40,6 @@ final class PromotionDocumentationModifier implements DocumentationModifierInter
return $docs->withPaths($paths);
}
public function addDescription(Paths $paths, string $path, string $method): void
{
$pathItem = $paths->getPath($path);
@ -51,7 +50,7 @@ final class PromotionDocumentationModifier implements DocumentationModifierInter
"%s\n\n Allowed rule types: `%s` \n\n Allowed action types: `%s`",
$operation->getDescription(),
implode('`, `', array_keys($this->ruleTypes)),
implode('`, `', array_keys($this->actionTypes))
implode('`, `', array_keys($this->actionTypes)),
);
$operation = $operation->withDescription($description);

View file

@ -67,6 +67,7 @@ api_platform:
Sylius\Bundle\ApiBundle\Exception\TranslationLocaleMismatchException: 422
Sylius\Bundle\ApiBundle\Exception\ZoneCannotBeRemoved: 422
Sylius\Bundle\ApiBundle\Exception\CannotRemoveMenuTaxonException: 409
Sylius\Bundle\ApiBundle\Exception\PromotionCannotBeRemoved: 422
Sylius\Bundle\LocaleBundle\Checker\Exception\LocaleIsUsedException: 422
Sylius\Bundle\UserBundle\Exception\UserNotFoundException: 404
Symfony\Component\Serializer\Exception\UnexpectedValueException: 400

View file

@ -14,6 +14,8 @@ declare(strict_types=1);
namespace Sylius\Tests\Api\Admin;
use Sylius\Component\Core\Model\PromotionInterface;
use Sylius\Component\Core\Promotion\Action\FixedDiscountPromotionActionCommand;
use Sylius\Component\Promotion\Checker\Rule\CartQuantityRuleChecker;
use Sylius\Tests\Api\JsonApiTestCase;
use Sylius\Tests\Api\Utils\AdminUserLoginTrait;
use Symfony\Component\HttpFoundation\Response;
@ -25,7 +27,7 @@ final class PromotionsTest extends JsonApiTestCase
/** @test */
public function it_gets_a_promotion(): void
{
$fixtures = $this->loadFixturesFromFiles(['authentication/api_administrator.yaml', 'channel.yaml', 'promotion.yaml']);
$fixtures = $this->loadFixturesFromFiles(['authentication/api_administrator.yaml', 'channel.yaml', 'promotion/promotion.yaml']);
$header = array_merge($this->logInAdminUser('api@example.com'), self::CONTENT_TYPE_HEADER);
/** @var PromotionInterface $promotion */
@ -47,7 +49,7 @@ final class PromotionsTest extends JsonApiTestCase
/** @test */
public function it_gets_promotions(): void
{
$this->loadFixturesFromFiles(['authentication/api_administrator.yaml', 'channel.yaml', 'promotion.yaml']);
$this->loadFixturesFromFiles(['authentication/api_administrator.yaml', 'channel.yaml', 'promotion/promotion.yaml']);
$header = array_merge($this->logInAdminUser('api@example.com'), self::CONTENT_TYPE_HEADER);
$this->client->request(method: 'GET', uri: '/api/v2/admin/promotions', server: $header);
@ -61,6 +63,105 @@ final class PromotionsTest extends JsonApiTestCase
/** @test */
public function it_creates_promotion(): void
{
$this->loadFixturesFromFiles(['authentication/api_administrator.yaml', 'channel.yaml']);
$header = array_merge($this->logInAdminUser('api@example.com'), self::CONTENT_TYPE_HEADER);
$this->client->request(
method: 'POST',
uri: '/api/v2/admin/promotions',
server: $header,
content: json_encode([
'name' => 'T-Shirts discount',
'code' => 'tshirts_discount',
'channels' => [
'/api/v2/admin/channels/WEB',
],
'description' => 'T-Shirts discount',
'priority' => 22,
'appliesToDiscounted' => false,
'exclusive' => true,
"usageLimit" => 3,
"couponBased" => true,
'startsAt' => '2023-10-04 12:30:00',
'endsAt' => '2023-11-04 12:30:00',
'translations' => ['en_US' => [
'label' => 'T-Shirts discount',
]],
'rules' => [
[
'type' => CartQuantityRuleChecker::TYPE,
'configuration' => [
'count' => 6
],
],
],
'actions' => [
[
'type' => FixedDiscountPromotionActionCommand::TYPE,
'configuration' => [
'WEB' => [
'amount' => 1000,
],
],
],
],
], JSON_THROW_ON_ERROR),
);
$this->assertResponse(
$this->client->getResponse(),
'admin/promotion/post_promotion_response',
Response::HTTP_CREATED,
);
}
/** @test */
public function it_does_not_create_a_promotion_without_required_data(): void
{
$this->loadFixturesFromFiles(['authentication/api_administrator.yaml']);
$header = array_merge($this->logInAdminUser('api@example.com'), self::CONTENT_TYPE_HEADER);
$this->client->request(
method: 'POST',
uri: '/api/v2/admin/promotions',
server: $header,
content: json_encode([], JSON_THROW_ON_ERROR),
);
$this->assertResponse(
$this->client->getResponse(),
'admin/promotion/post_promotion_without_required_data_response',
Response::HTTP_UNPROCESSABLE_ENTITY,
);
}
/** @test */
public function it_does_not_create_a_promotion_with_taken_code(): void
{
$this->loadFixturesFromFiles(['authentication/api_administrator.yaml', 'channel.yaml', 'promotion/promotion.yaml']);
$header = array_merge($this->logInAdminUser('api@example.com'), self::CONTENT_TYPE_HEADER);
$this->client->request(
method: 'POST',
uri: '/api/v2/admin/promotions',
server: $header,
content: json_encode([
'name' => '50% Off on your first order',
'code' => '50_off',
], JSON_THROW_ON_ERROR),
);
$this->assertResponse(
$this->client->getResponse(),
'admin/promotion/post_promotion_with_taken_code_response',
Response::HTTP_UNPROCESSABLE_ENTITY,
);
}
/** @test */
public function it_does_not_create_a_promotion_with_end_date_earlier_than_start_date(): void
{
$this->loadFixturesFromFiles(['authentication/api_administrator.yaml']);
$header = array_merge($this->logInAdminUser('api@example.com'), self::CONTENT_TYPE_HEADER);
@ -72,17 +173,135 @@ final class PromotionsTest extends JsonApiTestCase
content: json_encode([
'name' => 'T-Shirts discount',
'code' => 'tshirts_discount',
'appliesToDiscounted' => false,
'startsAt' => '2023-12-04 12:30:00',
'endsAt' => '2023-11-04 12:30:00',
], JSON_THROW_ON_ERROR),
);
$this->assertResponse(
$this->client->getResponse(),
'admin/promotion/post_promotion_with_invalid_dates_response',
Response::HTTP_UNPROCESSABLE_ENTITY,
);
}
/** @test */
public function it_updates_promotion(): void
{
$fixtures = $this->loadFixturesFromFiles(['authentication/api_administrator.yaml', 'channel.yaml', 'promotion/promotion.yaml']);
$header = array_merge($this->logInAdminUser('api@example.com'), self::CONTENT_TYPE_HEADER);
/** @var PromotionInterface $promotion */
$promotion = $fixtures['promotion_50_off'];
$this->client->request(
method: 'PUT',
uri: sprintf('/api/v2/admin/promotions/%s', $promotion->getCode()),
server: $header,
content: json_encode([
'name' => 'Christmas',
'code' => 'new_code',
'appliesToDiscounted' => true,
'exclusive' => true,
"usageLimit" => 11,
"couponBased" => false,
'rules' => [
[
'type' => CartQuantityRuleChecker::TYPE,
'configuration' => [
'count' => 1
],
],
],
'actions' => [
[
'type' => FixedDiscountPromotionActionCommand::TYPE,
'configuration' => [
'WEB' => [
'amount' => 2,
],
],
],
],
'channels' => [
'/api/v2/admin/channels/MOBILE',
],
'translations' => ['en_US' => [
'label' => 'T-Shirts discount',
'@id' => sprintf('/api/v2/admin/promotion-translations/%s', $promotion->getTranslation('en_US')->getId()),
'label' => 'Christmas',
]],
], JSON_THROW_ON_ERROR),
);
$this->assertResponse(
$this->client->getResponse(),
'admin/promotion/post_promotion_response',
Response::HTTP_CREATED,
'admin/promotion/put_promotion_response',
Response::HTTP_OK,
);
}
/** @test */
public function it_updates_promotion_with_null_priority(): void
{
$fixtures = $this->loadFixturesFromFiles(['authentication/api_administrator.yaml', 'channel.yaml', 'promotion/promotion.yaml']);
$header = array_merge($this->logInAdminUser('api@example.com'), self::CONTENT_TYPE_HEADER);
/** @var PromotionInterface $promotion */
$promotion = $fixtures['promotion_50_off'];
$this->client->request(
method: 'PUT',
uri: sprintf('/api/v2/admin/promotions/%s', $promotion->getCode()),
server: $header,
content: json_encode([
'priority' => null,
], JSON_THROW_ON_ERROR),
);
$this->assertResponse(
$this->client->getResponse(),
'admin/promotion/put_promotion_with_null_priority_response',
Response::HTTP_OK,
);
}
/** @test */
public function it_deletes_a_promotion(): void
{
$fixtures = $this->loadFixturesFromFiles(['authentication/api_administrator.yaml', 'channel.yaml', 'promotion/promotion.yaml']);
$header = array_merge($this->logInAdminUser('api@example.com'), self::CONTENT_TYPE_HEADER);
/** @var PromotionInterface $promotion */
$promotion = $fixtures['promotion_50_off'];
$this->client->request(
method: 'DELETE',
uri: sprintf('/api/v2/admin/promotions/%s', $promotion->getCode()),
server: $header,
);
$this->assertResponseCode($this->client->getResponse(), Response::HTTP_NO_CONTENT);
}
/** @test */
public function it_does_not_delete_the_promotion_in_use(): void
{
$fixtures = $this->loadFixturesFromFiles([
'authentication/api_administrator.yaml',
'channel.yaml',
'promotion/promotion.yaml',
'promotion/promotion_order.yaml',
]);
$header = array_merge($this->logInAdminUser('api@example.com'), self::CONTENT_TYPE_HEADER);
/** @var PromotionInterface $promotion */
$promotion = $fixtures['promotion_50_off'];
$this->client->request(
method: 'DELETE',
uri: sprintf('/api/v2/admin/promotions/%s', $promotion->getCode()),
server: $header,
);
$this->assertResponseCode($this->client->getResponse(), Response::HTTP_UNPROCESSABLE_ENTITY);
}
}

View file

@ -0,0 +1,8 @@
Sylius\Component\Core\Model\Order:
order:
channel: "@channel_web"
currencyCode: "USD"
localeCode: "en_US"
state: "new"
tokenValue: "token"
promotions: ['@promotion_50_off']

View file

@ -2,21 +2,45 @@
"@context": "\/api\/v2\/contexts\/Promotion",
"@id": "\/api\/v2\/admin\/promotions\/tshirts_discount",
"@type": "Promotion",
"channels": [],
"channels": [
"\/api\/v2\/admin\/channels\/WEB"
],
"id":"@integer@",
"code": "tshirts_discount",
"name": "T-Shirts discount",
"description": null,
"priority": 0,
"exclusive": false,
"usageLimit": null,
"description": "T-Shirts discount",
"priority": 22,
"exclusive": true,
"usageLimit": 3,
"used": 0,
"startsAt": null,
"endsAt": null,
"couponBased": false,
"startsAt": "2023-10-04 12:30:00",
"endsAt": "2023-11-04 12:30:00",
"couponBased": true,
"coupons": [],
"rules": [],
"actions": [],
"rules": [
{
"@id": "\/api\/v2\/admin\/promotion-rules\/@integer@",
"@type": "PromotionRule",
"id": @integer@,
"type": "cart_quantity",
"configuration": {
"count": 6
}
}
],
"actions": [
{
"@id": "\/api\/v2\/admin\/promotion-actions\/@integer@",
"@type": "PromotionAction",
"id": @integer@,
"type": "order_fixed_discount",
"configuration": {
"WEB": {
"amount": 1000
}
}
}
],
"appliesToDiscounted": false,
"createdAt": @string@,
"updatedAt": @string@,

View file

@ -0,0 +1,13 @@
{
"@context": "\/api\/v2\/contexts\/ConstraintViolationList",
"@type": "ConstraintViolationList",
"hydra:title": "An error occurred",
"hydra:description": "endsAt: End date cannot be set prior start date.",
"violations": [
{
"propertyPath": "endsAt",
"message": "End date cannot be set prior start date.",
"code": null
}
]
}

View file

@ -0,0 +1,13 @@
{
"@context": "\/api\/v2\/contexts\/ConstraintViolationList",
"@type": "ConstraintViolationList",
"hydra:title": "An error occurred",
"hydra:description": "code: The promotion with given code already exists.",
"violations": [
{
"propertyPath": "code",
"message": "The promotion with given code already exists.",
"code": @string@
}
]
}

View file

@ -0,0 +1,18 @@
{
"@context": "\/api\/v2\/contexts\/ConstraintViolationList",
"@type": "ConstraintViolationList",
"hydra:title": "An error occurred",
"hydra:description": "code: Please enter promotion code.\nname: Please enter promotion name.",
"violations": [
{
"propertyPath": "code",
"message": "Please enter promotion code.",
"code": @string@
},
{
"propertyPath": "name",
"message": "Please enter promotion name.",
"code": @string@
}
]
}

View file

@ -0,0 +1,55 @@
{
"@context": "\/api\/v2\/contexts\/Promotion",
"@id": "\/api\/v2\/admin\/promotions\/50_off",
"@type": "Promotion",
"channels": [
"\/api\/v2\/admin\/channels\/MOBILE"
],
"id": @integer@,
"code": "50_off",
"name": "Christmas",
"description": "Get 50% off of your first purchase",
"priority": 1,
"exclusive": true,
"usageLimit": 11,
"used": 0,
"startsAt": null,
"endsAt": null,
"couponBased": false,
"coupons": [],
"rules": [
{
"@id": "\/api\/v2\/admin\/promotion-rules\/@integer@",
"@type": "PromotionRule",
"id": @integer@,
"type": "cart_quantity",
"configuration": {
"count": 1
}
}
],
"actions": [
{
"@id": "\/api\/v2\/admin\/promotion-actions\/@integer@",
"@type": "PromotionAction",
"id": @integer@,
"type": "order_fixed_discount",
"configuration": {
"WEB": {
"amount": 2
}
}
}
],
"appliesToDiscounted": true,
"createdAt": @date@,
"updatedAt": @date@,
"translations": {
"en_US": {
"@id": "\/api\/v2\/admin\/promotion-translations\/@integer@",
"@type": "PromotionTranslation",
"id": @integer@,
"label": "Christmas"
}
}
}

View file

@ -0,0 +1,39 @@
{
"@context": "\/api\/v2\/contexts\/Promotion",
"@id": "\/api\/v2\/admin\/promotions\/50_off",
"@type": "Promotion",
"channels": [
"\/api\/v2\/admin\/channels\/MOBILE"
],
"id": @integer@,
"code": "50_off",
"name": "50% Off on your first order",
"description": "Get 50% off of your first purchase",
"priority": 2,
"exclusive": true,
"usageLimit": 1,
"used": 0,
"startsAt": null,
"endsAt": null,
"couponBased": false,
"coupons": [
],
"rules": [
],
"actions": [
],
"appliesToDiscounted": false,
"createdAt": @date@,
"updatedAt": @date@,
"translations": {
"en_US": {
"@id": "\/api\/v2\/admin\/promotion-translations\/@integer@",
"@type": "PromotionTranslation",
"id": @integer@,
"label": "-50% on first order!"
}
}
}