From bcd5d2848d7681407376046cf6aea2159df93464 Mon Sep 17 00:00:00 2001
From: Alejandro Celaya <alejandrocelaya@gmail.com>
Date: Wed, 12 Oct 2022 12:47:58 +0200
Subject: [PATCH] Used PHPUnit mocks in RoleResolverTest instead of prophezy

---
 module/CLI/src/ApiKey/RoleResolver.php        |  5 +-
 .../CLI/src/ApiKey/RoleResolverInterface.php  |  3 -
 .../src/Command/Api/GenerateKeyCommand.php    |  4 +-
 .../CLI/src/Command/Api/ListKeysCommand.php   |  4 +-
 module/CLI/test/ApiKey/RoleResolverTest.php   | 64 +++++++++++--------
 module/Rest/src/ApiKey/Role.php               | 24 ++++---
 module/Rest/test/ApiKey/RoleTest.php          |  4 +-
 7 files changed, 61 insertions(+), 47 deletions(-)

diff --git a/module/CLI/src/ApiKey/RoleResolver.php b/module/CLI/src/ApiKey/RoleResolver.php
index 588a2fa2..c1ae8f05 100644
--- a/module/CLI/src/ApiKey/RoleResolver.php
+++ b/module/CLI/src/ApiKey/RoleResolver.php
@@ -7,6 +7,7 @@ namespace Shlinkio\Shlink\CLI\ApiKey;
 use Shlinkio\Shlink\CLI\Exception\InvalidRoleConfigException;
 use Shlinkio\Shlink\Core\Domain\DomainServiceInterface;
 use Shlinkio\Shlink\Rest\ApiKey\Model\RoleDefinition;
+use Shlinkio\Shlink\Rest\ApiKey\Role;
 use Symfony\Component\Console\Input\InputInterface;
 
 use function is_string;
@@ -19,8 +20,8 @@ class RoleResolver implements RoleResolverInterface
 
     public function determineRoles(InputInterface $input): array
     {
-        $domainAuthority = $input->getOption(self::DOMAIN_ONLY_PARAM);
-        $author = $input->getOption(self::AUTHOR_ONLY_PARAM);
+        $domainAuthority = $input->getOption(Role::DOMAIN_SPECIFIC->paramName());
+        $author = $input->getOption(Role::AUTHORED_SHORT_URLS->paramName());
 
         $roleDefinitions = [];
         if ($author) {
diff --git a/module/CLI/src/ApiKey/RoleResolverInterface.php b/module/CLI/src/ApiKey/RoleResolverInterface.php
index 98d50483..92a04594 100644
--- a/module/CLI/src/ApiKey/RoleResolverInterface.php
+++ b/module/CLI/src/ApiKey/RoleResolverInterface.php
@@ -9,9 +9,6 @@ use Symfony\Component\Console\Input\InputInterface;
 
 interface RoleResolverInterface
 {
-    public const AUTHOR_ONLY_PARAM = 'author-only';
-    public const DOMAIN_ONLY_PARAM = 'domain-only';
-
     /**
      * @return RoleDefinition[]
      */
diff --git a/module/CLI/src/Command/Api/GenerateKeyCommand.php b/module/CLI/src/Command/Api/GenerateKeyCommand.php
index b24619ef..12adcd57 100644
--- a/module/CLI/src/Command/Api/GenerateKeyCommand.php
+++ b/module/CLI/src/Command/Api/GenerateKeyCommand.php
@@ -32,8 +32,8 @@ class GenerateKeyCommand extends Command
 
     protected function configure(): void
     {
-        $authorOnly = RoleResolverInterface::AUTHOR_ONLY_PARAM;
-        $domainOnly = RoleResolverInterface::DOMAIN_ONLY_PARAM;
+        $authorOnly = Role::AUTHORED_SHORT_URLS->paramName();
+        $domainOnly = Role::DOMAIN_SPECIFIC->paramName();
         $help = <<<HELP
         The <info>%command.name%</info> generates a new valid API key.
 
diff --git a/module/CLI/src/Command/Api/ListKeysCommand.php b/module/CLI/src/Command/Api/ListKeysCommand.php
index 0e98af31..59f5b534 100644
--- a/module/CLI/src/Command/Api/ListKeysCommand.php
+++ b/module/CLI/src/Command/Api/ListKeysCommand.php
@@ -62,8 +62,8 @@ class ListKeysCommand extends Command
             $rowData[] = $apiKey->isAdmin() ? 'Admin' : implode("\n", $apiKey->mapRoles(
                 fn (Role $role, array $meta) =>
                     empty($meta)
-                        ? Role::toFriendlyName($role)
-                        : sprintf('%s: %s', Role::toFriendlyName($role), Role::domainAuthorityFromMeta($meta)),
+                        ? $role->toFriendlyName()
+                        : sprintf('%s: %s', $role->toFriendlyName(), Role::domainAuthorityFromMeta($meta)),
             ));
 
             return $rowData;
diff --git a/module/CLI/test/ApiKey/RoleResolverTest.php b/module/CLI/test/ApiKey/RoleResolverTest.php
index 21f541a2..245a7106 100644
--- a/module/CLI/test/ApiKey/RoleResolverTest.php
+++ b/module/CLI/test/ApiKey/RoleResolverTest.php
@@ -4,27 +4,27 @@ declare(strict_types=1);
 
 namespace ShlinkioTest\Shlink\CLI\ApiKey;
 
+use PHPUnit\Framework\MockObject\MockObject;
 use PHPUnit\Framework\TestCase;
-use Prophecy\PhpUnit\ProphecyTrait;
-use Prophecy\Prophecy\ObjectProphecy;
 use Shlinkio\Shlink\CLI\ApiKey\RoleResolver;
 use Shlinkio\Shlink\CLI\Exception\InvalidRoleConfigException;
 use Shlinkio\Shlink\Core\Domain\DomainServiceInterface;
 use Shlinkio\Shlink\Core\Domain\Entity\Domain;
 use Shlinkio\Shlink\Rest\ApiKey\Model\RoleDefinition;
+use Shlinkio\Shlink\Rest\ApiKey\Role;
 use Symfony\Component\Console\Input\InputInterface;
 
+use function Functional\map;
+
 class RoleResolverTest extends TestCase
 {
-    use ProphecyTrait;
-
     private RoleResolver $resolver;
-    private ObjectProphecy $domainService;
+    private MockObject $domainService;
 
     protected function setUp(): void
     {
-        $this->domainService = $this->prophesize(DomainServiceInterface::class);
-        $this->resolver = new RoleResolver($this->domainService->reveal(), 'default.com');
+        $this->domainService = $this->createMock(DomainServiceInterface::class);
+        $this->resolver = new RoleResolver($this->domainService, 'default.com');
     }
 
     /**
@@ -36,61 +36,65 @@ class RoleResolverTest extends TestCase
         array $expectedRoles,
         int $expectedDomainCalls,
     ): void {
-        $getDomain = $this->domainService->getOrCreate('example.com')->willReturn(
-            Domain::withAuthority('example.com')->setId('1'),
-        );
+        $this->domainService
+            ->expects($this->exactly($expectedDomainCalls))
+            ->method('getOrCreate')
+            ->with($this->equalTo('example.com'))
+            ->willReturn(Domain::withAuthority('example.com')->setId('1'));
 
         $result = $this->resolver->determineRoles($input);
 
         self::assertEquals($expectedRoles, $result);
-        $getDomain->shouldHaveBeenCalledTimes($expectedDomainCalls);
     }
 
     public function provideRoles(): iterable
     {
         $domain = Domain::withAuthority('example.com')->setId('1');
         $buildInput = function (array $definition): InputInterface {
-            $input = $this->prophesize(InputInterface::class);
+            $input = $this->createStub(InputInterface::class);
+            $input->method('getOption')->willReturnMap(
+                map($definition, static fn (mixed $returnValue, string $param) => [$param, $returnValue]),
+            );
 
-            foreach ($definition as $name => $value) {
-                $input->getOption($name)->willReturn($value);
-            }
-
-            return $input->reveal();
+            return $input;
         };
 
         yield 'no roles' => [
-            $buildInput([RoleResolver::DOMAIN_ONLY_PARAM => null, RoleResolver::AUTHOR_ONLY_PARAM => false]),
+            $buildInput([Role::DOMAIN_SPECIFIC->paramName() => null, Role::AUTHORED_SHORT_URLS->paramName() => false]),
             [],
             0,
         ];
         yield 'domain role only' => [
-            $buildInput([RoleResolver::DOMAIN_ONLY_PARAM => 'example.com', RoleResolver::AUTHOR_ONLY_PARAM => false]),
+            $buildInput(
+                [Role::DOMAIN_SPECIFIC->paramName() => 'example.com', Role::AUTHORED_SHORT_URLS->paramName() => false],
+            ),
             [RoleDefinition::forDomain($domain)],
             1,
         ];
         yield 'false domain role' => [
-            $buildInput([RoleResolver::DOMAIN_ONLY_PARAM => false]),
+            $buildInput([Role::DOMAIN_SPECIFIC->paramName() => false]),
             [],
             0,
         ];
         yield 'true domain role' => [
-            $buildInput([RoleResolver::DOMAIN_ONLY_PARAM => true]),
+            $buildInput([Role::DOMAIN_SPECIFIC->paramName() => true]),
             [],
             0,
         ];
         yield 'string array domain role' => [
-            $buildInput([RoleResolver::DOMAIN_ONLY_PARAM => ['foo', 'bar']]),
+            $buildInput([Role::DOMAIN_SPECIFIC->paramName() => ['foo', 'bar']]),
             [],
             0,
         ];
         yield 'author role only' => [
-            $buildInput([RoleResolver::DOMAIN_ONLY_PARAM => null, RoleResolver::AUTHOR_ONLY_PARAM => true]),
+            $buildInput([Role::DOMAIN_SPECIFIC->paramName() => null, Role::AUTHORED_SHORT_URLS->paramName() => true]),
             [RoleDefinition::forAuthoredShortUrls()],
             0,
         ];
         yield 'both roles' => [
-            $buildInput([RoleResolver::DOMAIN_ONLY_PARAM => 'example.com', RoleResolver::AUTHOR_ONLY_PARAM => true]),
+            $buildInput(
+                [Role::DOMAIN_SPECIFIC->paramName() => 'example.com', Role::AUTHORED_SHORT_URLS->paramName() => true],
+            ),
             [RoleDefinition::forAuthoredShortUrls(), RoleDefinition::forDomain($domain)],
             1,
         ];
@@ -99,12 +103,16 @@ class RoleResolverTest extends TestCase
     /** @test */
     public function exceptionIsThrownWhenTryingToAddDomainOnlyLinkedToDefaultDomain(): void
     {
-        $input = $this->prophesize(InputInterface::class);
-        $input->getOption(RoleResolver::DOMAIN_ONLY_PARAM)->willReturn('default.com');
-        $input->getOption(RoleResolver::AUTHOR_ONLY_PARAM)->willReturn(null);
+        $input = $this->createStub(InputInterface::class);
+        $input
+            ->method('getOption')
+            ->willReturnMap([
+                [Role::DOMAIN_SPECIFIC->paramName(), 'default.com'],
+                [Role::AUTHORED_SHORT_URLS->paramName(), null],
+            ]);
 
         $this->expectException(InvalidRoleConfigException::class);
 
-        $this->resolver->determineRoles($input->reveal());
+        $this->resolver->determineRoles($input);
     }
 }
diff --git a/module/Rest/src/ApiKey/Role.php b/module/Rest/src/ApiKey/Role.php
index f852571d..5a4edb81 100644
--- a/module/Rest/src/ApiKey/Role.php
+++ b/module/Rest/src/ApiKey/Role.php
@@ -17,6 +17,22 @@ enum Role: string
     case AUTHORED_SHORT_URLS = 'AUTHORED_SHORT_URLS';
     case DOMAIN_SPECIFIC = 'DOMAIN_SPECIFIC';
 
+    public function toFriendlyName(): string
+    {
+        return match ($this) {
+            self::AUTHORED_SHORT_URLS => 'Author only',
+            self::DOMAIN_SPECIFIC => 'Domain only',
+        };
+    }
+
+    public function paramName(): string
+    {
+        return match ($this) {
+            self::AUTHORED_SHORT_URLS => 'author-only',
+            self::DOMAIN_SPECIFIC => 'domain-only',
+        };
+    }
+
     public static function toSpec(ApiKeyRole $role, ?string $context = null): Specification
     {
         return match ($role->role()) {
@@ -42,12 +58,4 @@ enum Role: string
     {
         return $meta['authority'] ?? '';
     }
-
-    public static function toFriendlyName(Role $role): string
-    {
-        return match ($role) {
-            self::AUTHORED_SHORT_URLS => 'Author only',
-            self::DOMAIN_SPECIFIC => 'Domain only',
-        };
-    }
 }
diff --git a/module/Rest/test/ApiKey/RoleTest.php b/module/Rest/test/ApiKey/RoleTest.php
index f3cc64b2..715b89b8 100644
--- a/module/Rest/test/ApiKey/RoleTest.php
+++ b/module/Rest/test/ApiKey/RoleTest.php
@@ -99,9 +99,9 @@ class RoleTest extends TestCase
      * @test
      * @dataProvider provideRoleNames
      */
-    public function getsExpectedRoleFriendlyName(Role $roleName, string $expectedFriendlyName): void
+    public function getsExpectedRoleFriendlyName(Role $role, string $expectedFriendlyName): void
     {
-        self::assertEquals($expectedFriendlyName, Role::toFriendlyName($roleName));
+        self::assertEquals($expectedFriendlyName, $role->toFriendlyName());
     }
 
     public function provideRoleNames(): iterable