diff --git a/src/web/backend/phpunit.xml b/src/web/backend/phpunit.xml index 4e7cb3d0b5d5ff43422745f9a4e3a0b8a4757278..85c670a30f34521dd8637059319a374653cca5b0 100644 --- a/src/web/backend/phpunit.xml +++ b/src/web/backend/phpunit.xml @@ -30,6 +30,7 @@ ./app/Console ./src + ./src diff --git a/src/web/backend/src/Gateways/Tokens/InMemoryTokensRepository.php b/src/web/backend/src/Gateways/Tokens/InMemoryTokensRepository.php index 0c36440f4da37d840ea7117ee28c4c7200c3c448..cf612fce7f7d7eb9e3fd6ad5007688614881dcd4 100644 --- a/src/web/backend/src/Gateways/Tokens/InMemoryTokensRepository.php +++ b/src/web/backend/src/Gateways/Tokens/InMemoryTokensRepository.php @@ -24,6 +24,14 @@ class InMemoryTokensRepository implements TokensRepository return $token; } + /** + * @return Token[] + */ + public function all(): array + { + return $this->tokens; + } + /** @inheritDoc */ public function findValidToken(string $tokenToMatch): ?Token diff --git a/src/web/backend/src/UseCases/Users/Authenticate/Authenticate.php b/src/web/backend/src/UseCases/Users/Authenticate/Authenticate.php index 7ea4522a8ad778b989b4d0179f15ce47cf1b5050..6284ff3950b3cc05f53b298c0df3063892fd6706 100644 --- a/src/web/backend/src/UseCases/Users/Authenticate/Authenticate.php +++ b/src/web/backend/src/UseCases/Users/Authenticate/Authenticate.php @@ -13,12 +13,26 @@ use Source\Exceptions\AuthenticationException; class Authenticate implements AuthenticateUseCase { + /** + * @var \Source\Gateways\Users\UsersRepository + */ protected UsersRepository $users; + /** + * @var \Source\Gateways\Tokens\TokensRepository + */ protected TokensRepository $tokens; + /** + * @var \Source\Gateways\Saml\SamlRepository + */ protected SamlRepository $saml; + /** + * @param \Source\Gateways\Users\UsersRepository $users + * @param \Source\Gateways\Saml\SamlRepository $saml + * @param \Source\Gateways\Tokens\TokensRepository $tokens + */ public function __construct(UsersRepository $users, SamlRepository $saml, TokensRepository $tokens) { $this->saml = $saml; @@ -59,6 +73,9 @@ class Authenticate implements AuthenticateUseCase $presenter->present($response); } + /** + * @inheritDoc + */ public function handToSaml(array $options = []): string { return $this->saml->login($options); diff --git a/src/web/backend/tests/Unit/Source/UseCases/Users/Authenticate/AttemptUseCaseTest.php b/src/web/backend/tests/Unit/Source/UseCases/Users/Authenticate/AttemptUseCaseTest.php new file mode 100644 index 0000000000000000000000000000000000000000..8bc47544139b125e8a61779121d5e152a2de6fba --- /dev/null +++ b/src/web/backend/tests/Unit/Source/UseCases/Users/Authenticate/AttemptUseCaseTest.php @@ -0,0 +1,117 @@ + self::VALID_EMAIL, + 'password' => self::VALID_PASS, + ] + ): void { + $this->useCase->attempt($this->presenter, $credentials); + + $this->response = $this->presenter->response; + } + + /** + * @return \Source\Entities\User + */ + protected function createUser(): User + { + return $this->users->create(new User(0, '', '', '', '', self::VALID_EMAIL, self::VALID_PASS, '')); + } + + /** + * @test + * @throws \Source\Exceptions\AuthenticationException + * @throws \Source\Exceptions\EntityNotFoundException + */ + public function it_calls_present_on_presenter(): void + { + $this->createUser(); + + $this->handleTest([ + 'email' => self::VALID_EMAIL, + 'password' => self::VALID_PASS, + ]); + + $this->assertTrue($this->presenter->wasPresenterCalled()); + } + + public function attemptAuthenticationProvider(): array + { + return [ + [[]], + [['email' => 'eem',]], + [['password' => 'pass']], + [['email' => self::VALID_EMAIL, 'password' => 'ree']], + [['email' => 'non-existent', 'password' => self::VALID_PASS]], + ]; + } + + /** + * @test + * @dataProvider attemptAuthenticationProvider + * @param array $credentials + * @throws \Source\Exceptions\AuthenticationException + * @throws \Source\Exceptions\EntityNotFoundException + */ + public function it_cannot_find_users_for_invalid_input(array $credentials): void + { + $this->createUser(); + + $this->expectException(AuthenticationException::class); + + $this->handleTest($credentials); + } + + /** + * @test + * @throws \Source\Exceptions\AuthenticationException + * @throws \Source\Exceptions\EntityNotFoundException + */ + public function it_creates_a_token_for_the_user(): void + { + $user = $this->createUser(); + + $this->handleTest(); + + $this->assertCount(1, $this->tokens->all()); + $token = $this->tokens->all()[0]; + $this->assertLessThan(Carbon::now()->addDays(2), $token->getExpiresAt()); + $this->assertGreaterThan(Carbon::now()->addDays(1), $token->getExpiresAt()); + $this->assertEquals($user->getId(), $token->getUserId()); + $this->assertEquals(60, strlen($token->getTokenString())); + $this->assertEquals($token, $this->response->getToken()); + } + + /** + * @test + * @throws \Source\Exceptions\AuthenticationException + * @throws \Source\Exceptions\EntityNotFoundException + */ + public function it_authenticates_a_user(): void + { + $user = $this->createUser(); + + $this->handleTest(); + + $this->assertEquals($user, $this->response->getUser()); + } +} diff --git a/src/web/backend/tests/Unit/Source/UseCases/Users/Authenticate/PresenterStub.php b/src/web/backend/tests/Unit/Source/UseCases/Users/Authenticate/PresenterStub.php new file mode 100644 index 0000000000000000000000000000000000000000..43ff0e908baf1f0bb76f5b0b9e178019937d4a63 --- /dev/null +++ b/src/web/backend/tests/Unit/Source/UseCases/Users/Authenticate/PresenterStub.php @@ -0,0 +1,30 @@ +presenterCalled = true; + $this->response = $responseModel; + } + + public function wasPresenterCalled(): bool + { + return $this->presenterCalled; + } + + public function getViewModel(): array + { + return []; + } +} diff --git a/src/web/backend/tests/Unit/Source/UseCases/Users/Authenticate/PresenterTest.php b/src/web/backend/tests/Unit/Source/UseCases/Users/Authenticate/PresenterTest.php new file mode 100644 index 0000000000000000000000000000000000000000..ef5fabc97f7ae694fb41cf837fde19e447aff763 --- /dev/null +++ b/src/web/backend/tests/Unit/Source/UseCases/Users/Authenticate/PresenterTest.php @@ -0,0 +1,148 @@ +presenter = new APIPresenter(); + } + + /** + * @param \Source\Entities\User $user + * @param \Source\Entities\Token $token + */ + public function handleTest(User $user, Token $token): void + { + $this->model = new ResponseModel($user, $token); + + $this->presenter->present($this->model); + + $this->response = $this->presenter->getViewModel(); + } + + /** + * @test + * @throws \Exception + */ + public function it_presents_the_token(): void + { + $expires = new Carbon('2020-03-04'); + $user = new User( + 0, + 'first', + 'last', + 'display', + 'emplid', + 'email', + 'password', + 'doorcode', + $expires, + null, + null + ); + + $token = new Token(0, 0, 'token', 'nomen', $expires); + + $this->handleTest($user, $token); + + $minutes = $expires->diffInMinutes(Carbon::now()); + + $this->assertEquals([ + 'value' => 'token', + 'expires_at' => '2020-03-04T00:00:00+00:00', + 'minutes' => $minutes, + ], $this->response['token']); + } + + /** + * @test + */ + public function it_presents_the_token_with_no_expire(): void + { + $expires = null; + $user = new User( + 0, + 'first', + 'last', + 'display', + 'emplid', + 'email', + 'password', + 'doorcode', + $expires, + null, + null + ); + + $token = new Token(0, 0, 'token', 'nomen', $expires); + + $this->handleTest($user, $token); + + $this->assertEquals([ + 'value' => 'token', + 'expires_at' => null, + 'minutes' => 0, + ], $this->response['token']); + } + + /** @test */ + public function it_formats_a_user(): void + { + $user = new User( + 0, + 'first', + 'last', + 'display', + 'emplid', + 'email', + 'password', + 'doorcode', + new Carbon('2020-02-02'), + null, + null + ); + + $token = new Token(0, 0, 'token', 'nomen', new Carbon('2020-03-04')); + + $this->handleTest($user, $token); + + $this->assertEquals([ + 'id' => 0, + 'first_name' => 'first', + 'last_name' => 'last', + 'display_name' => 'display', + 'emplid' => 'emplid', + 'email' => 'email', + 'expires_at' => '2020-02-02T00:00:00+00:00', + 'created_at' => null, + 'updated_at' => null, + ], $this->response['user']); + } +} diff --git a/src/web/backend/tests/Unit/Source/UseCases/Users/Authenticate/UseCaseBaseTest.php b/src/web/backend/tests/Unit/Source/UseCases/Users/Authenticate/UseCaseBaseTest.php new file mode 100644 index 0000000000000000000000000000000000000000..46a7f016e55e9fa3068001f70f0b219405c7edb5 --- /dev/null +++ b/src/web/backend/tests/Unit/Source/UseCases/Users/Authenticate/UseCaseBaseTest.php @@ -0,0 +1,65 @@ +users = new InMemoryUsersRepository(); + $this->tokens = new InMemoryTokensRepository(); + $this->saml = new InMemorySamlRepository($this->loginUrl, $this->logoutUrl); + $this->useCase = new Authenticate($this->users, $this->saml, $this->tokens); + $this->presenter = new PresenterStub(); + } +} diff --git a/src/web/backend/tests/Unit/Source/UseCases/Users/GetUser/PresenterTest.php b/src/web/backend/tests/Unit/Source/UseCases/Users/GetUser/PresenterTest.php index f3c23d5aab6583f7b5071ee3a03a8bd0bea2a314..943b2ebaef190882b9db4fc316f199bf1630f85c 100644 --- a/src/web/backend/tests/Unit/Source/UseCases/Users/GetUser/PresenterTest.php +++ b/src/web/backend/tests/Unit/Source/UseCases/Users/GetUser/PresenterTest.php @@ -11,10 +11,19 @@ use Source\UseCases\Users\GetUser\ResponseModel; class PresenterTest extends TestCase { + /** + * @var \Source\UseCases\Users\GetUser\APIPresenter + */ protected APIPresenter $presenter; + /** + * @var \Source\UseCases\Users\GetUser\ResponseModel + */ protected ResponseModel $model; + /** + * @var array + */ protected array $response; public function setUp(): void @@ -52,21 +61,18 @@ class PresenterTest extends TestCase $this->handleTest($user); - $this->assertEquals( - [ - 'user' => [ - 'id' => 0, - 'first_name' => 'first', - 'last_name' => 'last', - 'display_name' => 'display', - 'emplid' => 'emplid', - 'email' => 'email', - 'expires_at' => '2020-02-02T00:00:00+00:00', - 'created_at' => null, - 'updated_at' => null, - ], + $this->assertEquals([ + 'user' => [ + 'id' => 0, + 'first_name' => 'first', + 'last_name' => 'last', + 'display_name' => 'display', + 'emplid' => 'emplid', + 'email' => 'email', + 'expires_at' => '2020-02-02T00:00:00+00:00', + 'created_at' => null, + 'updated_at' => null, ], - $this->response - ); + ], $this->response); } }