userRepo = new FakeUserRepository(); $this->sessionRepo = new FakeSessionRepository(); $this->clock = new FakeClock( new DateTimeImmutable('2025-01-01T12:00:00+00:00') ); $this->user = $this->userRepo->create(new CreateUserDto( email: new EmailAddress('test@test.com'), passwordHash: '', isAdmin: false, )); $this->middleware = new AuthMiddleware( $this->sessionRepo, $this->userRepo, $this->clock, ); } private function makeApiRequest( ?string $cookieToken = null ): ServerRequestInterface { $request = new ServerRequestFactory() ->createServerRequest('GET', 'http://localhost/api/texts'); if ($cookieToken !== null) { $request = $request->withCookieParams([ 'auth_token' => $cookieToken, ]); } return $request; } private function makeHtmlRequest( ?string $cookieToken = null ): ServerRequestInterface { $request = new ServerRequestFactory() ->createServerRequest('GET', 'http://localhost/home') ->withHeader('Accept', 'text/html'); if ($cookieToken !== null) { $request = $request->withCookieParams([ 'auth_token' => $cookieToken, ]); } return $request; } private function makeHandler(): RequestHandlerInterface { return new class implements RequestHandlerInterface { public ?ServerRequestInterface $capturedRequest = null; public function handle( ServerRequestInterface $request ): \Psr\Http\Message\ResponseInterface { $this->capturedRequest = $request; return new Response(200); } }; } public function test_returns_401_json_when_cookie_missing(): void { $response = $this->middleware->process( $this->makeApiRequest(), $this->makeHandler(), ); $this->assertEquals(401, $response->getStatusCode()); $this->assertStringContainsString( 'application/json', $response->getHeaderLine('Content-Type') ); } public function test_returns_401_when_token_not_in_repo(): void { $response = $this->middleware->process( $this->makeApiRequest('unknown-token'), $this->makeHandler(), ); $this->assertEquals(401, $response->getStatusCode()); } public function test_returns_401_when_token_expired(): void { $this->sessionRepo->create(new CreateSessionDto( token: 'expired-token', userId: $this->user->getId(), createdAt: new DateTimeImmutable('2024-12-01T00:00:00+00:00'), expiresAt: new DateTimeImmutable('2024-12-08T00:00:00+00:00'), )); $response = $this->middleware->process( $this->makeApiRequest('expired-token'), $this->makeHandler(), ); $this->assertEquals(401, $response->getStatusCode()); } public function test_attaches_user_to_request_on_success(): void { $this->sessionRepo->create(new CreateSessionDto( token: 'valid-token', userId: $this->user->getId(), createdAt: new DateTimeImmutable('2025-01-01T00:00:00+00:00'), expiresAt: new DateTimeImmutable('2025-01-08T00:00:00+00:00'), )); $handler = $this->makeHandler(); $this->middleware->process( $this->makeApiRequest('valid-token'), $handler, ); $attached = $handler->capturedRequest->getAttribute('user'); $this->assertInstanceOf(User::class, $attached); $this->assertEquals( 'test@test.com', $attached->getEmail()->value() ); } public function test_redirects_to_login_when_html_unauthenticated(): void { $response = $this->middleware->process( $this->makeHtmlRequest(), $this->makeHandler(), ); $this->assertEquals(302, $response->getStatusCode()); $this->assertEquals('/login', $response->getHeaderLine('Location')); } }