Add contract test for Promotion

This commit is contained in:
Wojdylak 2023-11-21 13:14:02 +01:00
parent f4a3a3e722
commit 609931506e
6 changed files with 518 additions and 1 deletions

View file

@ -15,7 +15,18 @@ namespace Sylius\Tests\Api\Admin;
use Sylius\Component\Core\Model\PromotionInterface;
use Sylius\Component\Core\Promotion\Action\FixedDiscountPromotionActionCommand;
use Sylius\Component\Core\Promotion\Action\PercentageDiscountPromotionActionCommand;
use Sylius\Component\Core\Promotion\Action\ShippingPercentageDiscountPromotionActionCommand;
use Sylius\Component\Core\Promotion\Action\UnitFixedDiscountPromotionActionCommand;
use Sylius\Component\Core\Promotion\Action\UnitPercentageDiscountPromotionActionCommand;
use Sylius\Component\Core\Promotion\Checker\Rule\ContainsProductRuleChecker;
use Sylius\Component\Core\Promotion\Checker\Rule\CustomerGroupRuleChecker;
use Sylius\Component\Core\Promotion\Checker\Rule\HasTaxonRuleChecker;
use Sylius\Component\Core\Promotion\Checker\Rule\NthOrderRuleChecker;
use Sylius\Component\Core\Promotion\Checker\Rule\ShippingCountryRuleChecker;
use Sylius\Component\Core\Promotion\Checker\Rule\TotalOfItemsFromTaxonRuleChecker;
use Sylius\Component\Promotion\Checker\Rule\CartQuantityRuleChecker;
use Sylius\Component\Promotion\Checker\Rule\ItemTotalRuleChecker;
use Sylius\Tests\Api\JsonApiTestCase;
use Sylius\Tests\Api\Utils\AdminUserLoginTrait;
use Symfony\Component\HttpFoundation\Response;
@ -64,7 +75,14 @@ final class PromotionsTest extends JsonApiTestCase
/** @test */
public function it_creates_promotion(): void
{
$this->loadFixturesFromFiles(['authentication/api_administrator.yaml', 'channel.yaml']);
$this->loadFixturesFromFiles([
'authentication/api_administrator.yaml',
'channel.yaml',
'country.yaml',
'customer_group.yaml',
'promotion/product.yaml',
'promotion/taxon.yaml',
]);
$header = array_merge($this->logInAdminUser('api@example.com'), self::CONTENT_TYPE_HEADER);
$this->client->request(
@ -95,6 +113,60 @@ final class PromotionsTest extends JsonApiTestCase
'count' => 6
],
],
[
'type' => CustomerGroupRuleChecker::TYPE,
'configuration' => [
'group_code' => 'vip'
],
],
[
'type' => NthOrderRuleChecker::TYPE,
'configuration' => [
'nth' => 2
],
],
[
'type' => ShippingCountryRuleChecker::TYPE,
'configuration' => [
'country' => 'US'
],
],
[
'type' => HasTaxonRuleChecker::TYPE,
'configuration' => [
'taxons' => ['MUGS', 'CAPS']
],
],
[
'type' => TotalOfItemsFromTaxonRuleChecker::TYPE,
'configuration' => [
'WEB' => [
'taxon' => 'MUGS',
'amount' => 1000,
],
'MOBILE' => [
'taxon' => 'CAPS',
'amount' => 2000,
]
],
],
[
'type' => ContainsProductRuleChecker::TYPE,
'configuration' => [
'product_code' => 'CAP',
],
],
[
'type' => ItemTotalRuleChecker::TYPE,
'configuration' => [
'WEB' => [
'amount' => 333,
],
'MOBILE' => [
'amount' => 222,
]
],
],
],
'actions' => [
[
@ -103,6 +175,49 @@ final class PromotionsTest extends JsonApiTestCase
'WEB' => [
'amount' => 1000,
],
'MOBILE' => [
'amount' => 2000,
],
],
],
[
'type' => UnitFixedDiscountPromotionActionCommand::TYPE,
'configuration' => [
'WEB' => [
'amount' => 3000,
'filters' => [
'price_range_filter' => [
'min' => 1000,
'max' => 10000,
],
],
],
'MOBILE' => [
'amount' => 4000,
],
],
],
[
'type' => PercentageDiscountPromotionActionCommand::TYPE,
'configuration' => [
'percentage' => 5,
],
],
[
'type' => UnitPercentageDiscountPromotionActionCommand::TYPE,
'configuration' => [
'WEB' => [
'percentage' => 10,
],
'MOBILE' => [
'percentage' => 100,
],
],
],
[
'type' => ShippingPercentageDiscountPromotionActionCommand::TYPE,
'configuration' => [
'percentage' => 20,
],
],
],
@ -185,6 +300,145 @@ final class PromotionsTest extends JsonApiTestCase
);
}
/** @test */
public function it_does_not_create_a_promotion_with_invalid_rules(): 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',
'rules' => [
[
'type' => CartQuantityRuleChecker::TYPE,
'configuration' => [
'count' => 'invalid'
],
],
[
'type' => CustomerGroupRuleChecker::TYPE,
'configuration' => [
'group_code' => 'wrong'
],
],
[
'type' => NthOrderRuleChecker::TYPE,
'configuration' => [
'nth' => 'invalid'
],
],
[
'type' => ShippingCountryRuleChecker::TYPE,
'configuration' => [
'country' => 'wrong'
],
],
[
'type' => HasTaxonRuleChecker::TYPE,
'configuration' => [
'taxons' => ['wrong']
],
],
[
'type' => TotalOfItemsFromTaxonRuleChecker::TYPE,
'configuration' => [
'WEB' => [
'taxon' => 'wrong',
'amount' => 'invalid',
]
],
],
[
'type' => ContainsProductRuleChecker::TYPE,
'configuration' => [
'product_code' => 'wrong',
],
],
[
'type' => ItemTotalRuleChecker::TYPE,
'configuration' => [
'MOBILE' => [
]
],
],
],
], JSON_THROW_ON_ERROR),
);
$this->assertResponse(
$this->client->getResponse(),
'admin/promotion/post_promotion_with_invalid_rules_response',
Response::HTTP_UNPROCESSABLE_ENTITY,
);
}
/** @test */
public function it_does_not_create_a_promotion_with_invalid_actions(): 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',
'actions' => [
[
'type' => FixedDiscountPromotionActionCommand::TYPE,
'configuration' => [
'WEB' => [
'amount' => 'invalid',
],
],
],
[
'type' => UnitFixedDiscountPromotionActionCommand::TYPE,
'configuration' => [
'WEB' => [
'filters' => 'invalid'
],
],
],
[
'type' => PercentageDiscountPromotionActionCommand::TYPE,
'configuration' => [
'percentage' => -5,
],
],
[
'type' => UnitPercentageDiscountPromotionActionCommand::TYPE,
'configuration' => [
'WEB' => [
'percentage' => 110,
],
'MOBILE' => [
],
],
],
[
'type' => ShippingPercentageDiscountPromotionActionCommand::TYPE,
'configuration' => [
],
],
],
], JSON_THROW_ON_ERROR),
);
$this->assertResponse(
$this->client->getResponse(),
'admin/promotion/post_promotion_with_invalid_actions_response',
Response::HTTP_UNPROCESSABLE_ENTITY,
);
}
/** @test */
public function it_updates_promotion(): void
{

View file

@ -0,0 +1,9 @@
Sylius\Component\Core\Model\Product:
product_cap:
code: 'CAP'
channels: ['@channel_web']
currentLocale: 'en_US'
product_socks:
code: 'SOCKS'
channels: ['@channel_web']
currentLocale: 'en_US'

View file

@ -0,0 +1,5 @@
Sylius\Component\Core\Model\Taxon:
taxon_mugs:
code: 'MUGS'
taxon_caps:
code: 'CAPS'

View file

@ -26,6 +26,84 @@
"configuration": {
"count": 6
}
},
{
"@id": "\/api\/v2\/admin\/promotion-rules\/@integer@",
"@type": "PromotionRule",
"id": @integer@,
"type": "customer_group",
"configuration": {
"group_code": "vip"
}
},
{
"@id": "\/api\/v2\/admin\/promotion-rules\/@integer@",
"@type": "PromotionRule",
"id": @integer@,
"type": "nth_order",
"configuration": {
"nth": 2
}
},
{
"@id": "\/api\/v2\/admin\/promotion-rules\/@integer@",
"@type": "PromotionRule",
"id": @integer@,
"type": "shipping_country",
"configuration": {
"country": "US"
}
},
{
"@id": "\/api\/v2\/admin\/promotion-rules\/@integer@",
"@type": "PromotionRule",
"id": @integer@,
"type": "has_taxon",
"configuration": {
"taxons": [
"MUGS",
"CAPS"
]
}
},
{
"@id": "\/api\/v2\/admin\/promotion-rules\/@integer@",
"@type": "PromotionRule",
"id": @integer@,
"type": "total_of_items_from_taxon",
"configuration": {
"WEB": {
"taxon": "MUGS",
"amount": 1000
},
"MOBILE": {
"taxon": "CAPS",
"amount": 2000
}
}
},
{
"@id": "\/api\/v2\/admin\/promotion-rules\/@integer@",
"@type": "PromotionRule",
"id": @integer@,
"type": "contains_product",
"configuration": {
"product_code": "CAP"
}
},
{
"@id": "\/api\/v2\/admin\/promotion-rules\/@integer@",
"@type": "PromotionRule",
"id": @integer@,
"type": "item_total",
"configuration": {
"WEB": {
"amount": 333
},
"MOBILE": {
"amount": 222
}
}
}
],
"actions": [
@ -37,8 +115,63 @@
"configuration": {
"WEB": {
"amount": 1000
},
"MOBILE": {
"amount": 2000
}
}
},
{
"@id": "\/api\/v2\/admin\/promotion-actions\/@integer@",
"@type": "PromotionAction",
"id": @integer@,
"type": "unit_fixed_discount",
"configuration": {
"WEB": {
"amount": 3000,
"filters": {
"price_range_filter": {
"min": 1000,
"max": 10000
}
}
},
"MOBILE": {
"amount": 4000
}
}
},
{
"@id": "\/api\/v2\/admin\/promotion-actions\/@integer@",
"@type": "PromotionAction",
"id": @integer@,
"type": "order_percentage_discount",
"configuration": {
"percentage": 5
}
},
{
"@id": "\/api\/v2\/admin\/promotion-actions\/@integer@",
"@type": "PromotionAction",
"id": @integer@,
"type": "unit_percentage_discount",
"configuration": {
"WEB": {
"percentage": 10
},
"MOBILE": {
"percentage": 100
}
}
},
{
"@id": "\/api\/v2\/admin\/promotion-actions\/@integer@",
"@type": "PromotionAction",
"id": @integer@,
"type": "shipping_percentage_discount",
"configuration": {
"percentage": 20
}
}
],
"appliesToDiscounted": false,

View file

@ -0,0 +1,53 @@
{
"@context": "\/api\/v2\/contexts\/ConstraintViolationList",
"@type": "ConstraintViolationList",
"hydra:title": "An error occurred",
"hydra:description": "actions[0].configuration[WEB][amount]: This value should be of type numeric.\nactions[0].configuration[MOBILE]: This field is missing.\nactions[1].configuration[WEB][amount]: This field is missing.\nactions[1].configuration[WEB][filters]: This value should be of type array.\nactions[1].configuration[MOBILE]: This field is missing.\nactions[2].configuration[percentage]: The percentage discount must be between 0% and 100%.\nactions[3].configuration[WEB][percentage]: The percentage discount must be between 0% and 100%.\nactions[3].configuration[MOBILE][percentage]: This field is missing.\nactions[4].configuration[percentage]: This field is missing.",
"violations": [
{
"propertyPath": "actions[0].configuration[WEB][amount]",
"message": "This value should be of type numeric.",
"code": @string@
},
{
"propertyPath": "actions[0].configuration[MOBILE]",
"message": "This field is missing.",
"code": @string@
},
{
"propertyPath": "actions[1].configuration[WEB][amount]",
"message": "This field is missing.",
"code": @string@
},
{
"propertyPath": "actions[1].configuration[WEB][filters]",
"message": "This value should be of type array.",
"code": @string@
},
{
"propertyPath": "actions[1].configuration[MOBILE]",
"message": "This field is missing.",
"code": @string@
},
{
"propertyPath": "actions[2].configuration[percentage]",
"message": "The percentage discount must be between 0% and 100%.",
"code": @string@
},
{
"propertyPath": "actions[3].configuration[WEB][percentage]",
"message": "The percentage discount must be between 0% and 100%.",
"code": @string@
},
{
"propertyPath": "actions[3].configuration[MOBILE][percentage]",
"message": "This field is missing.",
"code": @string@
},
{
"propertyPath": "actions[4].configuration[percentage]",
"message": "This field is missing.",
"code": @string@
}
]
}

View file

@ -0,0 +1,63 @@
{
"@context": "\/api\/v2\/contexts\/ConstraintViolationList",
"@type": "ConstraintViolationList",
"hydra:title": "An error occurred",
"hydra:description": "rules[0].configuration[count]: This value should be of type numeric.\nrules[1].configuration[group_code]: Customer group with code wrong does not exist.\nrules[2].configuration[nth]: This value should be of type numeric.\nrules[3].configuration[country]: Country with code wrong does not exist.\nrules[4].configuration[taxons][0]: Taxon with code wrong does not exist.\nrules[5].configuration[WEB][amount]: This value should be of type numeric.\nrules[5].configuration[WEB][taxon]: Taxon with code wrong does not exist.\nrules[5].configuration[MOBILE]: This field is missing.\nrules[6].configuration[product_code]: Product with code wrong does not exist.\nrules[7].configuration[WEB]: This field is missing.\nrules[7].configuration[MOBILE][amount]: This field is missing.",
"violations": [
{
"propertyPath": "rules[0].configuration[count]",
"message": "This value should be of type numeric.",
"code": @string@
},
{
"propertyPath": "rules[1].configuration[group_code]",
"message": "Customer group with code wrong does not exist.",
"code": null
},
{
"propertyPath": "rules[2].configuration[nth]",
"message": "This value should be of type numeric.",
"code": @string@
},
{
"propertyPath": "rules[3].configuration[country]",
"message": "Country with code wrong does not exist.",
"code": null
},
{
"propertyPath": "rules[4].configuration[taxons][0]",
"message": "Taxon with code wrong does not exist.",
"code": null
},
{
"propertyPath": "rules[5].configuration[WEB][amount]",
"message": "This value should be of type numeric.",
"code": @string@
},
{
"propertyPath": "rules[5].configuration[WEB][taxon]",
"message": "Taxon with code wrong does not exist.",
"code": null
},
{
"propertyPath": "rules[5].configuration[MOBILE]",
"message": "This field is missing.",
"code": @string@
},
{
"propertyPath": "rules[6].configuration[product_code]",
"message": "Product with code wrong does not exist.",
"code": null
},
{
"propertyPath": "rules[7].configuration[WEB]",
"message": "This field is missing.",
"code": @string@
},
{
"propertyPath": "rules[7].configuration[MOBILE][amount]",
"message": "This field is missing.",
"code": @string@
}
]
}