mirror of
https://github.com/Sylius/Sylius.git
synced 2026-07-05 20:57:12 +00:00
Add tests for channel-based promotion eligibility and application
This commit is contained in:
parent
d32696a6da
commit
16ac32798b
4 changed files with 900 additions and 0 deletions
|
|
@ -0,0 +1,69 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Sylius package.
|
||||
*
|
||||
* (c) Sylius Sp. z o.o.
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Tests\Sylius\Bundle\CoreBundle\DependencyInjection\Compiler;
|
||||
|
||||
use Matthias\SymfonyDependencyInjectionTest\PhpUnit\AbstractCompilerPassTestCase;
|
||||
use PHPUnit\Framework\Attributes\CoversClass;
|
||||
use Sylius\Bundle\CoreBundle\DependencyInjection\Compiler\OverridePromotionServicesPass;
|
||||
use Sylius\Component\Core\Promotion\Action\ChannelAwarePromotionApplicator;
|
||||
use Sylius\Component\Core\Promotion\Checker\Eligibility\ChannelAwarePromotionRulesEligibilityChecker;
|
||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
use Symfony\Component\DependencyInjection\Definition;
|
||||
|
||||
#[CoversClass(OverridePromotionServicesPass::class)]
|
||||
final class OverridePromotionServicesPassTest extends AbstractCompilerPassTestCase
|
||||
{
|
||||
protected function registerCompilerPass(ContainerBuilder $container): void
|
||||
{
|
||||
$container->addCompilerPass(new OverridePromotionServicesPass());
|
||||
}
|
||||
|
||||
public function testItReplacesClassOfRulesEligibilityChecker(): void
|
||||
{
|
||||
$this->setDefinition('sylius.checker.promotion.rules_eligibility', new Definition(\stdClass::class));
|
||||
|
||||
$this->compile();
|
||||
|
||||
$this->assertContainerBuilderHasService(
|
||||
'sylius.checker.promotion.rules_eligibility',
|
||||
ChannelAwarePromotionRulesEligibilityChecker::class,
|
||||
);
|
||||
}
|
||||
|
||||
public function testItReplacesClassOfPromotionApplicator(): void
|
||||
{
|
||||
$this->setDefinition('sylius.action.applicator.promotion', new Definition(\stdClass::class));
|
||||
|
||||
$this->compile();
|
||||
|
||||
$this->assertContainerBuilderHasService(
|
||||
'sylius.action.applicator.promotion',
|
||||
ChannelAwarePromotionApplicator::class,
|
||||
);
|
||||
}
|
||||
|
||||
public function testItDoesNothingIfRulesEligibilityCheckerServiceDoesNotExist(): void
|
||||
{
|
||||
$this->compile();
|
||||
|
||||
$this->assertContainerBuilderNotHasService('sylius.checker.promotion.rules_eligibility');
|
||||
}
|
||||
|
||||
public function testItDoesNothingIfPromotionApplicatorServiceDoesNotExist(): void
|
||||
{
|
||||
$this->compile();
|
||||
|
||||
$this->assertContainerBuilderNotHasService('sylius.action.applicator.promotion');
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,185 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Sylius package.
|
||||
*
|
||||
* (c) Sylius Sp. z o.o.
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Tests\Sylius\Bundle\CoreBundle\Validator\Constraints;
|
||||
|
||||
use PHPUnit\Framework\Attributes\CoversClass;
|
||||
use PHPUnit\Framework\MockObject\MockObject;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Sylius\Bundle\CoreBundle\Validator\Constraints\PromotionConfigurationChannelCodes;
|
||||
use Sylius\Bundle\CoreBundle\Validator\Constraints\PromotionConfigurationChannelCodesValidator;
|
||||
use Sylius\Component\Channel\Repository\ChannelRepositoryInterface;
|
||||
use Sylius\Component\Core\Model\ChannelInterface;
|
||||
use Sylius\Component\Core\Promotion\ChannelAwareConfigurationInterface;
|
||||
use Symfony\Component\Validator\Constraint;
|
||||
use Symfony\Component\Validator\Context\ExecutionContextInterface;
|
||||
use Symfony\Component\Validator\Violation\ConstraintViolationBuilderInterface;
|
||||
|
||||
#[CoversClass(PromotionConfigurationChannelCodesValidator::class)]
|
||||
final class PromotionConfigurationChannelCodesValidatorTest extends TestCase
|
||||
{
|
||||
private ChannelRepositoryInterface&MockObject $channelRepository;
|
||||
|
||||
private ExecutionContextInterface&MockObject $executionContext;
|
||||
|
||||
private PromotionConfigurationChannelCodesValidator $validator;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
$this->channelRepository = $this->createMock(ChannelRepositoryInterface::class);
|
||||
$this->executionContext = $this->createMock(ExecutionContextInterface::class);
|
||||
$this->validator = new PromotionConfigurationChannelCodesValidator($this->channelRepository);
|
||||
$this->validator->initialize($this->executionContext);
|
||||
}
|
||||
|
||||
public function testItIsAConstraintValidator(): void
|
||||
{
|
||||
$this->assertInstanceOf(PromotionConfigurationChannelCodesValidator::class, $this->validator);
|
||||
}
|
||||
|
||||
public function testItThrowsAnExceptionIfConstraintIsWrongType(): void
|
||||
{
|
||||
$this->expectException(\InvalidArgumentException::class);
|
||||
|
||||
$this->channelRepository->expects($this->never())->method('findOneByCode');
|
||||
$this->executionContext->expects($this->never())->method('buildViolation');
|
||||
|
||||
$this->validator->validate([], $this->createMock(Constraint::class));
|
||||
}
|
||||
|
||||
public function testItDoesNothingIfValueIsNotAnArray(): void
|
||||
{
|
||||
$this->channelRepository->expects($this->never())->method('findOneByCode');
|
||||
$this->executionContext->expects($this->never())->method('buildViolation');
|
||||
|
||||
$this->validator->validate('not_an_array', new PromotionConfigurationChannelCodes());
|
||||
}
|
||||
|
||||
public function testItDoesNothingIfChannelsKeyIsMissing(): void
|
||||
{
|
||||
$this->channelRepository->expects($this->never())->method('findOneByCode');
|
||||
$this->executionContext->expects($this->never())->method('buildViolation');
|
||||
|
||||
$this->validator->validate(['count' => 5], new PromotionConfigurationChannelCodes());
|
||||
}
|
||||
|
||||
public function testItDoesNothingIfChannelsListIsEmpty(): void
|
||||
{
|
||||
$this->channelRepository->expects($this->never())->method('findOneByCode');
|
||||
$this->executionContext->expects($this->never())->method('buildViolation');
|
||||
|
||||
$this->validator->validate(
|
||||
[ChannelAwareConfigurationInterface::CHANNELS_CONFIGURATION_KEY => []],
|
||||
new PromotionConfigurationChannelCodes(),
|
||||
);
|
||||
}
|
||||
|
||||
public function testItDoesNotAddViolationIfAllChannelCodesExist(): void
|
||||
{
|
||||
$channel = $this->createMock(ChannelInterface::class);
|
||||
|
||||
$this->channelRepository
|
||||
->expects($this->exactly(2))
|
||||
->method('findOneByCode')
|
||||
->willReturn($channel);
|
||||
|
||||
$this->executionContext->expects($this->never())->method('buildViolation');
|
||||
|
||||
$this->validator->validate(
|
||||
[ChannelAwareConfigurationInterface::CHANNELS_CONFIGURATION_KEY => ['WEB', 'MOBILE']],
|
||||
new PromotionConfigurationChannelCodes(),
|
||||
);
|
||||
}
|
||||
|
||||
public function testItAddsViolationForInvalidChannelCode(): void
|
||||
{
|
||||
$this->channelRepository
|
||||
->expects($this->once())
|
||||
->method('findOneByCode')
|
||||
->with('INVALID_CODE')
|
||||
->willReturn(null);
|
||||
|
||||
$violationBuilder = $this->createMock(ConstraintViolationBuilderInterface::class);
|
||||
$violationBuilder
|
||||
->expects($this->once())
|
||||
->method('setParameter')
|
||||
->with('{{ channelCode }}', 'INVALID_CODE')
|
||||
->willReturn($violationBuilder);
|
||||
$violationBuilder
|
||||
->expects($this->once())
|
||||
->method('atPath')
|
||||
->with('[' . ChannelAwareConfigurationInterface::CHANNELS_CONFIGURATION_KEY . ']')
|
||||
->willReturn($violationBuilder);
|
||||
$violationBuilder->expects($this->once())->method('addViolation');
|
||||
|
||||
$this->executionContext
|
||||
->expects($this->once())
|
||||
->method('buildViolation')
|
||||
->with((new PromotionConfigurationChannelCodes())->invalidChannelCodeMessage)
|
||||
->willReturn($violationBuilder);
|
||||
|
||||
$this->validator->validate(
|
||||
[ChannelAwareConfigurationInterface::CHANNELS_CONFIGURATION_KEY => ['INVALID_CODE']],
|
||||
new PromotionConfigurationChannelCodes(),
|
||||
);
|
||||
}
|
||||
|
||||
public function testItAddsOneViolationPerInvalidChannelCode(): void
|
||||
{
|
||||
$channel = $this->createMock(ChannelInterface::class);
|
||||
|
||||
$this->channelRepository
|
||||
->expects($this->exactly(3))
|
||||
->method('findOneByCode')
|
||||
->willReturnMap([
|
||||
['WEB', $channel],
|
||||
['INVALID_ONE', null],
|
||||
['INVALID_TWO', null],
|
||||
]);
|
||||
|
||||
$violationBuilder = $this->createMock(ConstraintViolationBuilderInterface::class);
|
||||
$violationBuilder->method('setParameter')->willReturn($violationBuilder);
|
||||
$violationBuilder->method('atPath')->willReturn($violationBuilder);
|
||||
$violationBuilder->expects($this->exactly(2))->method('addViolation');
|
||||
|
||||
$this->executionContext
|
||||
->expects($this->exactly(2))
|
||||
->method('buildViolation')
|
||||
->willReturn($violationBuilder);
|
||||
|
||||
$this->validator->validate(
|
||||
[ChannelAwareConfigurationInterface::CHANNELS_CONFIGURATION_KEY => ['WEB', 'INVALID_ONE', 'INVALID_TWO']],
|
||||
new PromotionConfigurationChannelCodes(),
|
||||
);
|
||||
}
|
||||
|
||||
public function testItAddsViolationForNonStringChannelCode(): void
|
||||
{
|
||||
$this->channelRepository->expects($this->never())->method('findOneByCode');
|
||||
|
||||
$violationBuilder = $this->createMock(ConstraintViolationBuilderInterface::class);
|
||||
$violationBuilder->method('setParameter')->willReturn($violationBuilder);
|
||||
$violationBuilder->method('atPath')->willReturn($violationBuilder);
|
||||
$violationBuilder->expects($this->once())->method('addViolation');
|
||||
|
||||
$this->executionContext
|
||||
->expects($this->once())
|
||||
->method('buildViolation')
|
||||
->willReturn($violationBuilder);
|
||||
|
||||
$this->validator->validate(
|
||||
[ChannelAwareConfigurationInterface::CHANNELS_CONFIGURATION_KEY => [42]],
|
||||
new PromotionConfigurationChannelCodes(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,368 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Sylius package.
|
||||
*
|
||||
* (c) Sylius Sp. z o.o.
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Tests\Sylius\Component\Core\Promotion\Action;
|
||||
|
||||
use Doctrine\Common\Collections\ArrayCollection;
|
||||
use PHPUnit\Framework\Attributes\CoversClass;
|
||||
use PHPUnit\Framework\MockObject\MockObject;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Sylius\Component\Channel\Model\ChannelInterface;
|
||||
use Sylius\Component\Core\Model\OrderInterface;
|
||||
use Sylius\Component\Core\Promotion\Action\ChannelAwarePromotionApplicator;
|
||||
use Sylius\Component\Core\Promotion\ChannelAwareConfigurationInterface;
|
||||
use Sylius\Component\Promotion\Action\PromotionActionCommandInterface;
|
||||
use Sylius\Component\Promotion\Action\PromotionApplicatorInterface;
|
||||
use Sylius\Component\Promotion\Model\PromotionActionInterface;
|
||||
use Sylius\Component\Promotion\Model\PromotionInterface;
|
||||
use Sylius\Component\Promotion\Model\PromotionSubjectInterface;
|
||||
use Sylius\Component\Registry\ServiceRegistryInterface;
|
||||
|
||||
#[CoversClass(ChannelAwarePromotionApplicator::class)]
|
||||
final class ChannelAwarePromotionApplicatorTest extends TestCase
|
||||
{
|
||||
private MockObject&PromotionActionCommandInterface $actionCommand;
|
||||
|
||||
private MockObject&PromotionActionInterface $action;
|
||||
|
||||
private MockObject&PromotionInterface $promotion;
|
||||
|
||||
private MockObject&OrderInterface $order;
|
||||
|
||||
private MockObject&PromotionSubjectInterface $subject;
|
||||
|
||||
private ChannelInterface&MockObject $channel;
|
||||
|
||||
private MockObject&ServiceRegistryInterface $serviceRegistry;
|
||||
|
||||
private ChannelAwarePromotionApplicator $applicator;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
$this->actionCommand = $this->createMock(PromotionActionCommandInterface::class);
|
||||
$this->action = $this->createMock(PromotionActionInterface::class);
|
||||
$this->promotion = $this->createMock(PromotionInterface::class);
|
||||
$this->order = $this->createMock(OrderInterface::class);
|
||||
$this->subject = $this->createMock(PromotionSubjectInterface::class);
|
||||
$this->channel = $this->createMock(ChannelInterface::class);
|
||||
$this->serviceRegistry = $this->createMock(ServiceRegistryInterface::class);
|
||||
$this->applicator = new ChannelAwarePromotionApplicator($this->serviceRegistry);
|
||||
}
|
||||
|
||||
public function testShouldImplementPromotionApplicatorInterfaceAndChannelAwareConfigurationInterface(): void
|
||||
{
|
||||
$this->assertInstanceOf(PromotionApplicatorInterface::class, $this->applicator);
|
||||
$this->assertInstanceOf(ChannelAwareConfigurationInterface::class, $this->applicator);
|
||||
}
|
||||
|
||||
public function testShouldExecuteActionAndAddPromotionIfActionReturnsTrue(): void
|
||||
{
|
||||
$this->promotion->expects($this->once())->method('getActions')->willReturn(
|
||||
new ArrayCollection([$this->action]),
|
||||
);
|
||||
|
||||
$this->action->expects($this->exactly(2))->method('getConfiguration')->willReturn([]);
|
||||
$this->action->expects($this->once())->method('getType')->willReturn('order_percentage_discount');
|
||||
|
||||
$this->serviceRegistry->expects($this->once())->method('get')->willReturn($this->actionCommand);
|
||||
$this->actionCommand
|
||||
->expects($this->once())
|
||||
->method('execute')
|
||||
->with($this->order, [], $this->promotion)
|
||||
->willReturn(true);
|
||||
|
||||
$this->order->expects($this->once())->method('addPromotion')->with($this->promotion);
|
||||
|
||||
$this->applicator->apply($this->order, $this->promotion);
|
||||
}
|
||||
|
||||
public function testShouldNotAddPromotionIfActionReturnsFalse(): void
|
||||
{
|
||||
$this->promotion->expects($this->once())->method('getActions')->willReturn(
|
||||
new ArrayCollection([$this->action]),
|
||||
);
|
||||
|
||||
$this->action->expects($this->exactly(2))->method('getConfiguration')->willReturn([]);
|
||||
$this->action->expects($this->once())->method('getType')->willReturn('order_percentage_discount');
|
||||
|
||||
$this->serviceRegistry->expects($this->once())->method('get')->willReturn($this->actionCommand);
|
||||
$this->actionCommand
|
||||
->expects($this->once())
|
||||
->method('execute')
|
||||
->with($this->order, [], $this->promotion)
|
||||
->willReturn(false);
|
||||
|
||||
$this->order->expects($this->never())->method('addPromotion');
|
||||
|
||||
$this->applicator->apply($this->order, $this->promotion);
|
||||
}
|
||||
|
||||
public function testShouldSkipActionDuringApplyIfChannelNotMatching(): void
|
||||
{
|
||||
$this->promotion->expects($this->once())->method('getActions')->willReturn(
|
||||
new ArrayCollection([$this->action]),
|
||||
);
|
||||
|
||||
$this->action
|
||||
->expects($this->once())
|
||||
->method('getConfiguration')
|
||||
->willReturn([ChannelAwareConfigurationInterface::CHANNELS_CONFIGURATION_KEY => ['WEB']]);
|
||||
|
||||
$this->order->method('getChannel')->willReturn($this->channel);
|
||||
$this->channel->method('getCode')->willReturn('MOBILE');
|
||||
|
||||
$this->serviceRegistry->expects($this->never())->method('get');
|
||||
$this->actionCommand->expects($this->never())->method('execute');
|
||||
$this->order->expects($this->never())->method('addPromotion');
|
||||
|
||||
$this->applicator->apply($this->order, $this->promotion);
|
||||
}
|
||||
|
||||
public function testShouldExecuteActionDuringApplyIfChannelMatches(): void
|
||||
{
|
||||
$this->promotion->expects($this->once())->method('getActions')->willReturn(
|
||||
new ArrayCollection([$this->action]),
|
||||
);
|
||||
|
||||
$this->action
|
||||
->expects($this->exactly(2))
|
||||
->method('getConfiguration')
|
||||
->willReturn([ChannelAwareConfigurationInterface::CHANNELS_CONFIGURATION_KEY => ['WEB']]);
|
||||
$this->action->expects($this->once())->method('getType')->willReturn('order_percentage_discount');
|
||||
|
||||
$this->order->method('getChannel')->willReturn($this->channel);
|
||||
$this->channel->method('getCode')->willReturn('WEB');
|
||||
|
||||
$this->serviceRegistry->expects($this->once())->method('get')->willReturn($this->actionCommand);
|
||||
$this->actionCommand
|
||||
->expects($this->once())
|
||||
->method('execute')
|
||||
->with($this->order, [], $this->promotion)
|
||||
->willReturn(true);
|
||||
|
||||
$this->order->expects($this->once())->method('addPromotion')->with($this->promotion);
|
||||
|
||||
$this->applicator->apply($this->order, $this->promotion);
|
||||
}
|
||||
|
||||
public function testShouldExecuteActionIfChannelsListIsEmpty(): void
|
||||
{
|
||||
$this->promotion->expects($this->once())->method('getActions')->willReturn(
|
||||
new ArrayCollection([$this->action]),
|
||||
);
|
||||
|
||||
$this->action
|
||||
->expects($this->exactly(2))
|
||||
->method('getConfiguration')
|
||||
->willReturn([ChannelAwareConfigurationInterface::CHANNELS_CONFIGURATION_KEY => []]);
|
||||
$this->action->expects($this->once())->method('getType')->willReturn('order_percentage_discount');
|
||||
|
||||
$this->serviceRegistry->expects($this->once())->method('get')->willReturn($this->actionCommand);
|
||||
$this->actionCommand
|
||||
->expects($this->once())
|
||||
->method('execute')
|
||||
->with($this->order, [], $this->promotion)
|
||||
->willReturn(true);
|
||||
|
||||
$this->order->expects($this->once())->method('addPromotion')->with($this->promotion);
|
||||
|
||||
$this->applicator->apply($this->order, $this->promotion);
|
||||
}
|
||||
|
||||
public function testShouldNotSkipActionIfSubjectIsNotOrderInterface(): void
|
||||
{
|
||||
$this->promotion->expects($this->once())->method('getActions')->willReturn(
|
||||
new ArrayCollection([$this->action]),
|
||||
);
|
||||
|
||||
$this->action
|
||||
->expects($this->exactly(2))
|
||||
->method('getConfiguration')
|
||||
->willReturn([ChannelAwareConfigurationInterface::CHANNELS_CONFIGURATION_KEY => ['WEB']]);
|
||||
$this->action->expects($this->once())->method('getType')->willReturn('order_percentage_discount');
|
||||
|
||||
$this->serviceRegistry->expects($this->once())->method('get')->willReturn($this->actionCommand);
|
||||
$this->actionCommand
|
||||
->expects($this->once())
|
||||
->method('execute')
|
||||
->with($this->subject, [], $this->promotion)
|
||||
->willReturn(true);
|
||||
|
||||
$this->subject->expects($this->once())->method('addPromotion')->with($this->promotion);
|
||||
|
||||
$this->applicator->apply($this->subject, $this->promotion);
|
||||
}
|
||||
|
||||
public function testShouldStripChannelsKeyFromConfigurationDuringApply(): void
|
||||
{
|
||||
$this->promotion->expects($this->once())->method('getActions')->willReturn(
|
||||
new ArrayCollection([$this->action]),
|
||||
);
|
||||
|
||||
$this->action
|
||||
->expects($this->exactly(2))
|
||||
->method('getConfiguration')
|
||||
->willReturn([
|
||||
'amount' => 100,
|
||||
ChannelAwareConfigurationInterface::CHANNELS_CONFIGURATION_KEY => ['WEB'],
|
||||
]);
|
||||
$this->action->expects($this->once())->method('getType')->willReturn('order_fixed_discount');
|
||||
|
||||
$this->order->method('getChannel')->willReturn($this->channel);
|
||||
$this->channel->method('getCode')->willReturn('WEB');
|
||||
|
||||
$this->serviceRegistry->expects($this->once())->method('get')->willReturn($this->actionCommand);
|
||||
$this->actionCommand
|
||||
->expects($this->once())
|
||||
->method('execute')
|
||||
->with($this->order, ['amount' => 100], $this->promotion)
|
||||
->willReturn(true);
|
||||
|
||||
$this->order->expects($this->once())->method('addPromotion')->with($this->promotion);
|
||||
|
||||
$this->applicator->apply($this->order, $this->promotion);
|
||||
}
|
||||
|
||||
public function testShouldAddPromotionIfAtLeastOneActionAppliedDespiteOtherBeingSkipped(): void
|
||||
{
|
||||
$secondAction = $this->createMock(PromotionActionInterface::class);
|
||||
|
||||
$this->promotion->expects($this->once())->method('getActions')->willReturn(
|
||||
new ArrayCollection([$this->action, $secondAction]),
|
||||
);
|
||||
|
||||
$this->action
|
||||
->expects($this->once())
|
||||
->method('getConfiguration')
|
||||
->willReturn([ChannelAwareConfigurationInterface::CHANNELS_CONFIGURATION_KEY => ['WEB']]);
|
||||
|
||||
$secondAction->expects($this->exactly(2))->method('getConfiguration')->willReturn([]);
|
||||
$secondAction->expects($this->once())->method('getType')->willReturn('order_percentage_discount');
|
||||
|
||||
$this->order->method('getChannel')->willReturn($this->channel);
|
||||
$this->channel->method('getCode')->willReturn('MOBILE');
|
||||
|
||||
$this->serviceRegistry->expects($this->once())->method('get')->willReturn($this->actionCommand);
|
||||
$this->actionCommand
|
||||
->expects($this->once())
|
||||
->method('execute')
|
||||
->with($this->order, [], $this->promotion)
|
||||
->willReturn(true);
|
||||
|
||||
$this->order->expects($this->once())->method('addPromotion')->with($this->promotion);
|
||||
|
||||
$this->applicator->apply($this->order, $this->promotion);
|
||||
}
|
||||
|
||||
public function testShouldNotAddPromotionIfAllActionsSkippedDueToChannel(): void
|
||||
{
|
||||
$secondAction = $this->createMock(PromotionActionInterface::class);
|
||||
|
||||
$this->promotion->expects($this->once())->method('getActions')->willReturn(
|
||||
new ArrayCollection([$this->action, $secondAction]),
|
||||
);
|
||||
|
||||
$this->action
|
||||
->expects($this->once())
|
||||
->method('getConfiguration')
|
||||
->willReturn([ChannelAwareConfigurationInterface::CHANNELS_CONFIGURATION_KEY => ['WEB']]);
|
||||
$secondAction
|
||||
->expects($this->once())
|
||||
->method('getConfiguration')
|
||||
->willReturn([ChannelAwareConfigurationInterface::CHANNELS_CONFIGURATION_KEY => ['WEB']]);
|
||||
|
||||
$this->order->method('getChannel')->willReturn($this->channel);
|
||||
$this->channel->method('getCode')->willReturn('MOBILE');
|
||||
|
||||
$this->serviceRegistry->expects($this->never())->method('get');
|
||||
$this->order->expects($this->never())->method('addPromotion');
|
||||
|
||||
$this->applicator->apply($this->order, $this->promotion);
|
||||
}
|
||||
|
||||
public function testShouldSkipActionDuringRevertIfChannelNotMatching(): void
|
||||
{
|
||||
$this->promotion->expects($this->once())->method('getActions')->willReturn(
|
||||
new ArrayCollection([$this->action]),
|
||||
);
|
||||
|
||||
$this->action
|
||||
->expects($this->once())
|
||||
->method('getConfiguration')
|
||||
->willReturn([ChannelAwareConfigurationInterface::CHANNELS_CONFIGURATION_KEY => ['WEB']]);
|
||||
|
||||
$this->order->method('getChannel')->willReturn($this->channel);
|
||||
$this->channel->method('getCode')->willReturn('MOBILE');
|
||||
|
||||
$this->serviceRegistry->expects($this->never())->method('get');
|
||||
$this->actionCommand->expects($this->never())->method('revert');
|
||||
$this->order->expects($this->once())->method('removePromotion')->with($this->promotion);
|
||||
|
||||
$this->applicator->revert($this->order, $this->promotion);
|
||||
}
|
||||
|
||||
public function testShouldRevertActionIfChannelMatches(): void
|
||||
{
|
||||
$this->promotion->expects($this->once())->method('getActions')->willReturn(
|
||||
new ArrayCollection([$this->action]),
|
||||
);
|
||||
|
||||
$this->action
|
||||
->expects($this->exactly(2))
|
||||
->method('getConfiguration')
|
||||
->willReturn([ChannelAwareConfigurationInterface::CHANNELS_CONFIGURATION_KEY => ['WEB']]);
|
||||
$this->action->expects($this->once())->method('getType')->willReturn('order_percentage_discount');
|
||||
|
||||
$this->order->method('getChannel')->willReturn($this->channel);
|
||||
$this->channel->method('getCode')->willReturn('WEB');
|
||||
|
||||
$this->serviceRegistry->expects($this->once())->method('get')->willReturn($this->actionCommand);
|
||||
$this->actionCommand
|
||||
->expects($this->once())
|
||||
->method('revert')
|
||||
->with($this->order, [], $this->promotion);
|
||||
|
||||
$this->order->expects($this->once())->method('removePromotion')->with($this->promotion);
|
||||
|
||||
$this->applicator->revert($this->order, $this->promotion);
|
||||
}
|
||||
|
||||
public function testShouldStripChannelsKeyFromConfigurationDuringRevert(): void
|
||||
{
|
||||
$this->promotion->expects($this->once())->method('getActions')->willReturn(
|
||||
new ArrayCollection([$this->action]),
|
||||
);
|
||||
|
||||
$this->action
|
||||
->expects($this->exactly(2))
|
||||
->method('getConfiguration')
|
||||
->willReturn([
|
||||
'amount' => 100,
|
||||
ChannelAwareConfigurationInterface::CHANNELS_CONFIGURATION_KEY => ['WEB'],
|
||||
]);
|
||||
$this->action->expects($this->once())->method('getType')->willReturn('order_fixed_discount');
|
||||
|
||||
$this->order->method('getChannel')->willReturn($this->channel);
|
||||
$this->channel->method('getCode')->willReturn('WEB');
|
||||
|
||||
$this->serviceRegistry->expects($this->once())->method('get')->willReturn($this->actionCommand);
|
||||
$this->actionCommand
|
||||
->expects($this->once())
|
||||
->method('revert')
|
||||
->with($this->order, ['amount' => 100], $this->promotion);
|
||||
|
||||
$this->order->expects($this->once())->method('removePromotion')->with($this->promotion);
|
||||
|
||||
$this->applicator->revert($this->order, $this->promotion);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,278 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Sylius package.
|
||||
*
|
||||
* (c) Sylius Sp. z o.o.
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Tests\Sylius\Component\Core\Promotion\Checker\Eligibility;
|
||||
|
||||
use Doctrine\Common\Collections\ArrayCollection;
|
||||
use PHPUnit\Framework\Attributes\CoversClass;
|
||||
use PHPUnit\Framework\MockObject\MockObject;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Sylius\Component\Channel\Model\ChannelInterface;
|
||||
use Sylius\Component\Core\Model\OrderInterface;
|
||||
use Sylius\Component\Core\Promotion\ChannelAwareConfigurationInterface;
|
||||
use Sylius\Component\Core\Promotion\Checker\Eligibility\ChannelAwarePromotionRulesEligibilityChecker;
|
||||
use Sylius\Component\Promotion\Checker\Eligibility\PromotionEligibilityCheckerInterface;
|
||||
use Sylius\Component\Promotion\Checker\Rule\RuleCheckerInterface;
|
||||
use Sylius\Component\Promotion\Model\PromotionInterface;
|
||||
use Sylius\Component\Promotion\Model\PromotionRuleInterface;
|
||||
use Sylius\Component\Promotion\Model\PromotionSubjectInterface;
|
||||
use Sylius\Component\Registry\ServiceRegistryInterface;
|
||||
|
||||
#[CoversClass(ChannelAwarePromotionRulesEligibilityChecker::class)]
|
||||
final class ChannelAwarePromotionRulesEligibilityCheckerTest extends TestCase
|
||||
{
|
||||
private MockObject&RuleCheckerInterface $ruleChecker;
|
||||
|
||||
private MockObject&PromotionRuleInterface $rule;
|
||||
|
||||
private MockObject&PromotionInterface $promotion;
|
||||
|
||||
private MockObject&OrderInterface $order;
|
||||
|
||||
private MockObject&PromotionSubjectInterface $subject;
|
||||
|
||||
private ChannelInterface&MockObject $channel;
|
||||
|
||||
private MockObject&ServiceRegistryInterface $serviceRegistry;
|
||||
|
||||
private ChannelAwarePromotionRulesEligibilityChecker $checker;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
$this->ruleChecker = $this->createMock(RuleCheckerInterface::class);
|
||||
$this->rule = $this->createMock(PromotionRuleInterface::class);
|
||||
$this->promotion = $this->createMock(PromotionInterface::class);
|
||||
$this->order = $this->createMock(OrderInterface::class);
|
||||
$this->subject = $this->createMock(PromotionSubjectInterface::class);
|
||||
$this->channel = $this->createMock(ChannelInterface::class);
|
||||
$this->serviceRegistry = $this->createMock(ServiceRegistryInterface::class);
|
||||
$this->checker = new ChannelAwarePromotionRulesEligibilityChecker($this->serviceRegistry);
|
||||
}
|
||||
|
||||
public function testShouldImplementPromotionEligibilityCheckerInterfaceAndChannelAwareConfigurationInterface(): void
|
||||
{
|
||||
$this->assertInstanceOf(PromotionEligibilityCheckerInterface::class, $this->checker);
|
||||
$this->assertInstanceOf(ChannelAwareConfigurationInterface::class, $this->checker);
|
||||
}
|
||||
|
||||
public function testShouldReturnEligibleIfPromotionHasNoRules(): void
|
||||
{
|
||||
$this->promotion->expects($this->once())->method('hasRules')->willReturn(false);
|
||||
$this->promotion->expects($this->never())->method('getRules');
|
||||
|
||||
$this->assertTrue($this->checker->isEligible($this->order, $this->promotion));
|
||||
}
|
||||
|
||||
public function testShouldReturnEligibleIfAllRulesPassWithNoChannelRestriction(): void
|
||||
{
|
||||
$secondRule = $this->createMock(PromotionRuleInterface::class);
|
||||
|
||||
$this->promotion->expects($this->once())->method('hasRules')->willReturn(true);
|
||||
$this->promotion->expects($this->once())->method('getRules')->willReturn(
|
||||
new ArrayCollection([$this->rule, $secondRule]),
|
||||
);
|
||||
|
||||
$this->rule->expects($this->exactly(2))->method('getConfiguration')->willReturn([]);
|
||||
$this->rule->expects($this->once())->method('getType')->willReturn('item_total');
|
||||
$secondRule->expects($this->exactly(2))->method('getConfiguration')->willReturn([]);
|
||||
$secondRule->expects($this->once())->method('getType')->willReturn('cart_quantity');
|
||||
|
||||
$this->serviceRegistry->expects($this->exactly(2))->method('get')->willReturn($this->ruleChecker);
|
||||
$this->ruleChecker->expects($this->exactly(2))->method('isEligible')->willReturn(true);
|
||||
|
||||
$this->assertTrue($this->checker->isEligible($this->order, $this->promotion));
|
||||
}
|
||||
|
||||
public function testShouldReturnNotEligibleIfAnyRuleFails(): void
|
||||
{
|
||||
$secondRule = $this->createMock(PromotionRuleInterface::class);
|
||||
|
||||
$this->promotion->expects($this->once())->method('hasRules')->willReturn(true);
|
||||
$this->promotion->expects($this->once())->method('getRules')->willReturn(
|
||||
new ArrayCollection([$this->rule, $secondRule]),
|
||||
);
|
||||
|
||||
$this->rule->expects($this->exactly(2))->method('getConfiguration')->willReturn([]);
|
||||
$this->rule->expects($this->once())->method('getType')->willReturn('item_total');
|
||||
$secondRule->expects($this->exactly(2))->method('getConfiguration')->willReturn([]);
|
||||
$secondRule->expects($this->once())->method('getType')->willReturn('cart_quantity');
|
||||
|
||||
$firstRuleChecker = $this->createMock(RuleCheckerInterface::class);
|
||||
$secondRuleChecker = $this->createMock(RuleCheckerInterface::class);
|
||||
|
||||
$this->serviceRegistry
|
||||
->expects($this->exactly(2))
|
||||
->method('get')
|
||||
->willReturnOnConsecutiveCalls($firstRuleChecker, $secondRuleChecker);
|
||||
|
||||
$firstRuleChecker->expects($this->once())->method('isEligible')->willReturn(true);
|
||||
$secondRuleChecker->expects($this->once())->method('isEligible')->willReturn(false);
|
||||
|
||||
$this->assertFalse($this->checker->isEligible($this->order, $this->promotion));
|
||||
}
|
||||
|
||||
public function testShouldStopCheckingRulesAfterFirstFailure(): void
|
||||
{
|
||||
$secondRule = $this->createMock(PromotionRuleInterface::class);
|
||||
|
||||
$this->promotion->expects($this->once())->method('hasRules')->willReturn(true);
|
||||
$this->promotion->expects($this->once())->method('getRules')->willReturn(
|
||||
new ArrayCollection([$this->rule, $secondRule]),
|
||||
);
|
||||
|
||||
$this->rule->expects($this->exactly(2))->method('getConfiguration')->willReturn([]);
|
||||
$this->rule->expects($this->once())->method('getType')->willReturn('item_total');
|
||||
$secondRule->expects($this->never())->method('getType');
|
||||
$secondRule->expects($this->never())->method('getConfiguration');
|
||||
|
||||
$this->serviceRegistry->expects($this->once())->method('get')->willReturn($this->ruleChecker);
|
||||
$this->ruleChecker->expects($this->once())->method('isEligible')->willReturn(false);
|
||||
|
||||
$this->assertFalse($this->checker->isEligible($this->order, $this->promotion));
|
||||
}
|
||||
|
||||
public function testShouldSkipRuleIfCurrentChannelNotInChannelsList(): void
|
||||
{
|
||||
$this->promotion->expects($this->once())->method('hasRules')->willReturn(true);
|
||||
$this->promotion->expects($this->once())->method('getRules')->willReturn(
|
||||
new ArrayCollection([$this->rule]),
|
||||
);
|
||||
|
||||
$this->rule
|
||||
->expects($this->once())
|
||||
->method('getConfiguration')
|
||||
->willReturn([ChannelAwareConfigurationInterface::CHANNELS_CONFIGURATION_KEY => ['WEB']]);
|
||||
|
||||
$this->order->method('getChannel')->willReturn($this->channel);
|
||||
$this->channel->method('getCode')->willReturn('MOBILE');
|
||||
|
||||
$this->serviceRegistry->expects($this->never())->method('get');
|
||||
|
||||
$this->assertTrue($this->checker->isEligible($this->order, $this->promotion));
|
||||
}
|
||||
|
||||
public function testShouldNotSkipRuleIfCurrentChannelIsInChannelsList(): void
|
||||
{
|
||||
$this->promotion->expects($this->once())->method('hasRules')->willReturn(true);
|
||||
$this->promotion->expects($this->once())->method('getRules')->willReturn(
|
||||
new ArrayCollection([$this->rule]),
|
||||
);
|
||||
|
||||
$this->rule
|
||||
->expects($this->exactly(2))
|
||||
->method('getConfiguration')
|
||||
->willReturn([ChannelAwareConfigurationInterface::CHANNELS_CONFIGURATION_KEY => ['WEB']]);
|
||||
$this->rule->expects($this->once())->method('getType')->willReturn('item_total');
|
||||
|
||||
$this->order->method('getChannel')->willReturn($this->channel);
|
||||
$this->channel->method('getCode')->willReturn('WEB');
|
||||
|
||||
$this->serviceRegistry->expects($this->once())->method('get')->willReturn($this->ruleChecker);
|
||||
$this->ruleChecker->expects($this->once())->method('isEligible')->willReturn(true);
|
||||
|
||||
$this->assertTrue($this->checker->isEligible($this->order, $this->promotion));
|
||||
}
|
||||
|
||||
public function testShouldNotSkipRuleIfChannelsListIsEmpty(): void
|
||||
{
|
||||
$this->promotion->expects($this->once())->method('hasRules')->willReturn(true);
|
||||
$this->promotion->expects($this->once())->method('getRules')->willReturn(
|
||||
new ArrayCollection([$this->rule]),
|
||||
);
|
||||
|
||||
$this->rule
|
||||
->expects($this->exactly(2))
|
||||
->method('getConfiguration')
|
||||
->willReturn([ChannelAwareConfigurationInterface::CHANNELS_CONFIGURATION_KEY => []]);
|
||||
$this->rule->expects($this->once())->method('getType')->willReturn('item_total');
|
||||
|
||||
$this->serviceRegistry->expects($this->once())->method('get')->willReturn($this->ruleChecker);
|
||||
$this->ruleChecker->expects($this->once())->method('isEligible')->willReturn(true);
|
||||
|
||||
$this->assertTrue($this->checker->isEligible($this->order, $this->promotion));
|
||||
}
|
||||
|
||||
public function testShouldNotSkipRuleIfSubjectIsNotAnOrderInterface(): void
|
||||
{
|
||||
$this->promotion->expects($this->once())->method('hasRules')->willReturn(true);
|
||||
$this->promotion->expects($this->once())->method('getRules')->willReturn(
|
||||
new ArrayCollection([$this->rule]),
|
||||
);
|
||||
|
||||
$this->rule
|
||||
->expects($this->exactly(2))
|
||||
->method('getConfiguration')
|
||||
->willReturn([ChannelAwareConfigurationInterface::CHANNELS_CONFIGURATION_KEY => ['WEB']]);
|
||||
$this->rule->expects($this->once())->method('getType')->willReturn('item_total');
|
||||
|
||||
$this->serviceRegistry->expects($this->once())->method('get')->willReturn($this->ruleChecker);
|
||||
$this->ruleChecker->expects($this->once())->method('isEligible')->willReturn(true);
|
||||
|
||||
$this->assertTrue($this->checker->isEligible($this->subject, $this->promotion));
|
||||
}
|
||||
|
||||
public function testShouldStripChannelsKeyFromConfigurationBeforePassingToRuleChecker(): void
|
||||
{
|
||||
$this->promotion->expects($this->once())->method('hasRules')->willReturn(true);
|
||||
$this->promotion->expects($this->once())->method('getRules')->willReturn(
|
||||
new ArrayCollection([$this->rule]),
|
||||
);
|
||||
|
||||
$this->rule
|
||||
->expects($this->exactly(2))
|
||||
->method('getConfiguration')
|
||||
->willReturn([
|
||||
'count' => 3,
|
||||
ChannelAwareConfigurationInterface::CHANNELS_CONFIGURATION_KEY => ['WEB'],
|
||||
]);
|
||||
$this->rule->expects($this->once())->method('getType')->willReturn('cart_quantity');
|
||||
|
||||
$this->order->method('getChannel')->willReturn($this->channel);
|
||||
$this->channel->method('getCode')->willReturn('WEB');
|
||||
|
||||
$this->serviceRegistry->expects($this->once())->method('get')->willReturn($this->ruleChecker);
|
||||
$this->ruleChecker
|
||||
->expects($this->once())
|
||||
->method('isEligible')
|
||||
->with($this->order, ['count' => 3])
|
||||
->willReturn(true);
|
||||
|
||||
$this->assertTrue($this->checker->isEligible($this->order, $this->promotion));
|
||||
}
|
||||
|
||||
public function testShouldReturnEligibleIfAllRulesForCurrentChannelAreSkipped(): void
|
||||
{
|
||||
$secondRule = $this->createMock(PromotionRuleInterface::class);
|
||||
|
||||
$this->promotion->expects($this->once())->method('hasRules')->willReturn(true);
|
||||
$this->promotion->expects($this->once())->method('getRules')->willReturn(
|
||||
new ArrayCollection([$this->rule, $secondRule]),
|
||||
);
|
||||
|
||||
$this->rule
|
||||
->expects($this->once())
|
||||
->method('getConfiguration')
|
||||
->willReturn([ChannelAwareConfigurationInterface::CHANNELS_CONFIGURATION_KEY => ['WEB']]);
|
||||
$secondRule
|
||||
->expects($this->once())
|
||||
->method('getConfiguration')
|
||||
->willReturn([ChannelAwareConfigurationInterface::CHANNELS_CONFIGURATION_KEY => ['WEB']]);
|
||||
|
||||
$this->order->method('getChannel')->willReturn($this->channel);
|
||||
$this->channel->method('getCode')->willReturn('MOBILE');
|
||||
|
||||
$this->serviceRegistry->expects($this->never())->method('get');
|
||||
|
||||
$this->assertTrue($this->checker->isEligible($this->order, $this->promotion));
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue