mirror of
https://github.com/shlinkio/shlink.git
synced 2025-01-26 16:26:39 -06:00
Add unit test for RedirectRuleHandler
This commit is contained in:
parent
eb40dc2d5d
commit
8751d6c315
@ -60,24 +60,26 @@ class RedirectRuleHandler implements RedirectRuleHandlerInterface
|
||||
$io->table(['Priority', 'Conditions', 'Redirect to'], $listing);
|
||||
}
|
||||
|
||||
$action = $io->choice(
|
||||
$action = RedirectRuleHandlerAction::from($io->choice(
|
||||
'What do you want to do next?',
|
||||
[
|
||||
'Add new rule',
|
||||
'Remove existing rule',
|
||||
'Re-arrange rule',
|
||||
'Discard changes',
|
||||
'Save and exit',
|
||||
],
|
||||
'Save and exit',
|
||||
);
|
||||
enumValues(RedirectRuleHandlerAction::class),
|
||||
RedirectRuleHandlerAction::SAVE->value,
|
||||
));
|
||||
|
||||
return match ($action) {
|
||||
'Add new rule' => $this->manageRules($io, $shortUrl, $this->addRule($shortUrl, $io, $rules)),
|
||||
'Remove existing rule' => $this->manageRules($io, $shortUrl, $this->removeRule($io, $rules)),
|
||||
'Re-arrange rule' => $this->manageRules($io, $shortUrl, $this->reArrangeRule($io, $rules)),
|
||||
'Save and exit' => $rules,
|
||||
default => null,
|
||||
RedirectRuleHandlerAction::ADD => $this->manageRules(
|
||||
$io,
|
||||
$shortUrl,
|
||||
$this->addRule($shortUrl, $io, $rules),
|
||||
),
|
||||
RedirectRuleHandlerAction::REMOVE => $this->manageRules($io, $shortUrl, $this->removeRule($io, $rules)),
|
||||
RedirectRuleHandlerAction::RE_ARRANGE => $this->manageRules(
|
||||
$io,
|
||||
$shortUrl,
|
||||
$this->reArrangeRule($io, $rules),
|
||||
),
|
||||
RedirectRuleHandlerAction::SAVE => $rules,
|
||||
RedirectRuleHandlerAction::DISCARD => null,
|
||||
};
|
||||
}
|
||||
|
||||
|
12
module/CLI/src/RedirectRule/RedirectRuleHandlerAction.php
Normal file
12
module/CLI/src/RedirectRule/RedirectRuleHandlerAction.php
Normal file
@ -0,0 +1,12 @@
|
||||
<?php
|
||||
|
||||
namespace Shlinkio\Shlink\CLI\RedirectRule;
|
||||
|
||||
enum RedirectRuleHandlerAction: string
|
||||
{
|
||||
case ADD = 'Add new rule';
|
||||
case REMOVE = 'Remove existing rule';
|
||||
case RE_ARRANGE = 'Re-arrange rule';
|
||||
case SAVE = 'Save and exit';
|
||||
case DISCARD = 'Discard changes';
|
||||
}
|
252
module/CLI/test/RedirectRule/RedirectRuleHandlerTest.php
Normal file
252
module/CLI/test/RedirectRule/RedirectRuleHandlerTest.php
Normal file
@ -0,0 +1,252 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace ShlinkioTest\Shlink\CLI\RedirectRule;
|
||||
|
||||
use Doctrine\Common\Collections\ArrayCollection;
|
||||
use PHPUnit\Framework\Attributes\DataProvider;
|
||||
use PHPUnit\Framework\Attributes\Test;
|
||||
use PHPUnit\Framework\MockObject\MockObject;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Shlinkio\Shlink\CLI\RedirectRule\RedirectRuleHandler;
|
||||
use Shlinkio\Shlink\CLI\RedirectRule\RedirectRuleHandlerAction;
|
||||
use Shlinkio\Shlink\Core\Model\DeviceType;
|
||||
use Shlinkio\Shlink\Core\RedirectRule\Entity\RedirectCondition;
|
||||
use Shlinkio\Shlink\Core\RedirectRule\Entity\ShortUrlRedirectRule;
|
||||
use Shlinkio\Shlink\Core\RedirectRule\Model\RedirectConditionType;
|
||||
use Shlinkio\Shlink\Core\ShortUrl\Entity\ShortUrl;
|
||||
use Symfony\Component\Console\Style\StyleInterface;
|
||||
|
||||
use function sprintf;
|
||||
|
||||
class RedirectRuleHandlerTest extends TestCase
|
||||
{
|
||||
private RedirectRuleHandler $handler;
|
||||
private StyleInterface & MockObject $io;
|
||||
private ShortUrl $shortUrl;
|
||||
private RedirectCondition $cond1;
|
||||
private RedirectCondition $cond2;
|
||||
private RedirectCondition $cond3;
|
||||
/** @var ShortUrlRedirectRule[] */
|
||||
private array $rules;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
$this->io = $this->createMock(StyleInterface::class);
|
||||
$this->shortUrl = ShortUrl::withLongUrl('https://example.com');
|
||||
$this->cond1 = RedirectCondition::forLanguage('es-AR');
|
||||
$this->cond2 = RedirectCondition::forQueryParam('foo', 'bar');
|
||||
$this->cond3 = RedirectCondition::forDevice(DeviceType::ANDROID);
|
||||
$this->rules = [
|
||||
new ShortUrlRedirectRule($this->shortUrl, 3, 'https://example.com/one', new ArrayCollection(
|
||||
[$this->cond1],
|
||||
)),
|
||||
new ShortUrlRedirectRule($this->shortUrl, 8, 'https://example.com/two', new ArrayCollection(
|
||||
[$this->cond2, $this->cond3],
|
||||
)),
|
||||
new ShortUrlRedirectRule($this->shortUrl, 5, 'https://example.com/three', new ArrayCollection(
|
||||
[$this->cond1, $this->cond3],
|
||||
)),
|
||||
];
|
||||
|
||||
$this->handler = new RedirectRuleHandler();
|
||||
}
|
||||
|
||||
#[Test, DataProvider('provideExitActions')]
|
||||
public function commentIsDisplayedWhenRulesListIsEmpty(
|
||||
RedirectRuleHandlerAction $action,
|
||||
?array $expectedResult,
|
||||
): void {
|
||||
$this->io->expects($this->once())->method('choice')->willReturn($action->value);
|
||||
$this->io->expects($this->once())->method('newLine');
|
||||
$this->io->expects($this->once())->method('text')->with('<comment>// No rules found.</comment>');
|
||||
$this->io->expects($this->never())->method('table');
|
||||
|
||||
$result = $this->handler->manageRules($this->io, $this->shortUrl, []);
|
||||
|
||||
self::assertEquals($expectedResult, $result);
|
||||
}
|
||||
|
||||
#[Test, DataProvider('provideExitActions')]
|
||||
public function rulesAreDisplayedWhenRulesListIsEmpty(
|
||||
RedirectRuleHandlerAction $action,
|
||||
): void {
|
||||
$comment = fn (string $value) => sprintf('<comment>%s</comment>', $value);
|
||||
|
||||
$this->io->expects($this->once())->method('choice')->willReturn($action->value);
|
||||
$this->io->expects($this->never())->method('newLine');
|
||||
$this->io->expects($this->never())->method('text');
|
||||
$this->io->expects($this->once())->method('table')->with($this->isType('array'), [
|
||||
['1', $comment($this->cond1->toHumanFriendly()), 'https://example.com/one'],
|
||||
[
|
||||
'2',
|
||||
$comment($this->cond2->toHumanFriendly()) . ' AND ' . $comment($this->cond3->toHumanFriendly()),
|
||||
'https://example.com/two',
|
||||
],
|
||||
[
|
||||
'3',
|
||||
$comment($this->cond1->toHumanFriendly()) . ' AND ' . $comment($this->cond3->toHumanFriendly()),
|
||||
'https://example.com/three',
|
||||
],
|
||||
]);
|
||||
|
||||
$this->handler->manageRules($this->io, $this->shortUrl, $this->rules);
|
||||
}
|
||||
|
||||
public static function provideExitActions(): iterable
|
||||
{
|
||||
yield 'discard' => [RedirectRuleHandlerAction::DISCARD, null];
|
||||
yield 'save' => [RedirectRuleHandlerAction::SAVE, []];
|
||||
}
|
||||
|
||||
#[Test, DataProvider('provideDeviceConditions')]
|
||||
/**
|
||||
* @param RedirectCondition[] $expectedConditions
|
||||
*/
|
||||
public function newRulesCanBeAdded(
|
||||
RedirectConditionType $type,
|
||||
array $expectedConditions,
|
||||
bool $continue = false,
|
||||
): void {
|
||||
$this->io->expects($this->any())->method('ask')->willReturnCallback(
|
||||
fn (string $message): string|int => match ($message) {
|
||||
'Rule priority (the lower the value, the higher the priority)' => 2, // Add in between existing rules
|
||||
'Long URL to redirect when the rule matches' => 'https://example.com/new-two',
|
||||
'Language to match?' => 'en-US',
|
||||
'Query param name?' => 'foo',
|
||||
'Query param value?' => 'bar',
|
||||
default => '',
|
||||
},
|
||||
);
|
||||
$this->io->expects($this->any())->method('choice')->willReturnCallback(
|
||||
function (string $message) use (&$callIndex, $type): string {
|
||||
$callIndex++;
|
||||
|
||||
if ($message === 'Type of the condition?') {
|
||||
return $type->value;
|
||||
} elseif ($message === 'Device to match?') {
|
||||
return DeviceType::ANDROID->value;
|
||||
}
|
||||
|
||||
// First we select remove action to trigger code branch, then save to finish execution
|
||||
$action = $callIndex === 1 ? RedirectRuleHandlerAction::ADD : RedirectRuleHandlerAction::SAVE;
|
||||
return $action->value;
|
||||
},
|
||||
);
|
||||
|
||||
$continueCallCount = 0;
|
||||
$this->io->method('confirm')->willReturnCallback(function () use (&$continueCallCount, $continue) {
|
||||
$continueCallCount++;
|
||||
return $continueCallCount < 2 && $continue;
|
||||
});
|
||||
|
||||
$result = $this->handler->manageRules($this->io, $this->shortUrl, $this->rules);
|
||||
|
||||
self::assertEquals([
|
||||
$this->rules[0],
|
||||
new ShortUrlRedirectRule($this->shortUrl, 2, 'https://example.com/new-two', new ArrayCollection(
|
||||
$expectedConditions,
|
||||
)),
|
||||
$this->rules[1],
|
||||
$this->rules[2],
|
||||
], $result);
|
||||
}
|
||||
|
||||
public static function provideDeviceConditions(): iterable
|
||||
{
|
||||
yield 'device' => [RedirectConditionType::DEVICE, [RedirectCondition::forDevice(DeviceType::ANDROID)]];
|
||||
yield 'language' => [RedirectConditionType::LANGUAGE, [RedirectCondition::forLanguage('en-US')]];
|
||||
yield 'query param' => [RedirectConditionType::QUERY_PARAM, [RedirectCondition::forQueryParam('foo', 'bar')]];
|
||||
yield 'multiple query params' => [
|
||||
RedirectConditionType::QUERY_PARAM,
|
||||
[RedirectCondition::forQueryParam('foo', 'bar'), RedirectCondition::forQueryParam('foo', 'bar')],
|
||||
true,
|
||||
];
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function existingRulesCanBeRemoved(): void
|
||||
{
|
||||
$callIndex = 0;
|
||||
$this->io->expects($this->exactly(3))->method('choice')->willReturnCallback(
|
||||
function (string $message) use (&$callIndex): string {
|
||||
$callIndex++;
|
||||
|
||||
if ($message === 'What rule do you want to delete?') {
|
||||
return 'https://example.com/two'; // Second rule to be removed
|
||||
}
|
||||
|
||||
// First we select remove action to trigger code branch, then save to finish execution
|
||||
$action = $callIndex === 1 ? RedirectRuleHandlerAction::REMOVE : RedirectRuleHandlerAction::SAVE;
|
||||
return $action->value;
|
||||
},
|
||||
);
|
||||
$this->io->expects($this->never())->method('warning');
|
||||
|
||||
$result = $this->handler->manageRules($this->io, $this->shortUrl, $this->rules);
|
||||
|
||||
self::assertEquals([$this->rules[0], $this->rules[2]], $result);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function warningIsPrintedWhenTryingToRemoveRuleFromEmptyList(): void
|
||||
{
|
||||
$callIndex = 0;
|
||||
$this->io->expects($this->exactly(2))->method('choice')->willReturnCallback(
|
||||
function () use (&$callIndex): string {
|
||||
$callIndex++;
|
||||
$action = $callIndex === 1 ? RedirectRuleHandlerAction::REMOVE : RedirectRuleHandlerAction::DISCARD;
|
||||
return $action->value;
|
||||
},
|
||||
);
|
||||
$this->io->expects($this->once())->method('warning')->with('There are no rules to remove');
|
||||
|
||||
$this->handler->manageRules($this->io, $this->shortUrl, []);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function existingRulesCanBeReArranged(): void
|
||||
{
|
||||
$this->io->expects($this->any())->method('ask')->willReturnCallback(
|
||||
fn (string $message): string|int => match ($message) {
|
||||
'Rule priority (the lower the value, the higher the priority)' => 1,
|
||||
default => '',
|
||||
},
|
||||
);
|
||||
$this->io->expects($this->exactly(3))->method('choice')->willReturnCallback(
|
||||
function (string $message) use (&$callIndex): string {
|
||||
$callIndex++;
|
||||
|
||||
if ($message === 'What rule do you want to re-arrange?') {
|
||||
return 'https://example.com/two'; // Second rule to be re-arrange
|
||||
}
|
||||
|
||||
// First we select remove action to trigger code branch, then save to finish execution
|
||||
$action = $callIndex === 1 ? RedirectRuleHandlerAction::RE_ARRANGE : RedirectRuleHandlerAction::SAVE;
|
||||
return $action->value;
|
||||
},
|
||||
);
|
||||
$this->io->expects($this->never())->method('warning');
|
||||
|
||||
$result = $this->handler->manageRules($this->io, $this->shortUrl, $this->rules);
|
||||
|
||||
self::assertEquals([$this->rules[1], $this->rules[0], $this->rules[2]], $result);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function warningIsPrintedWhenTryingToReArrangeRuleFromEmptyList(): void
|
||||
{
|
||||
$callIndex = 0;
|
||||
$this->io->expects($this->exactly(2))->method('choice')->willReturnCallback(
|
||||
function () use (&$callIndex): string {
|
||||
$callIndex++;
|
||||
$action = $callIndex === 1 ? RedirectRuleHandlerAction::RE_ARRANGE : RedirectRuleHandlerAction::DISCARD;
|
||||
return $action->value;
|
||||
},
|
||||
);
|
||||
$this->io->expects($this->once())->method('warning')->with('There are no rules to re-arrange');
|
||||
|
||||
$this->handler->manageRules($this->io, $this->shortUrl, []);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user