diff --git a/src/Http/FakeHttp.php b/src/Http/FakeHttp.php index 2c20cd6..4a1b4c9 100644 --- a/src/Http/FakeHttp.php +++ b/src/Http/FakeHttp.php @@ -421,9 +421,7 @@ class FakeHttp $bindings[ActorProviderInterface::class] = new FakeActorProvider($this->actor); $bindings[TokenStorageInterface::class] = new FakeTokenStorage(); - $transport = new TransportRegistry(); - $transport->setTransport('testing', new HeaderTransport(static::AUTH_TOKEN_HEADER_KEY)); - $bindings[TransportRegistry::class] = $transport; + $bindings[TransportRegistry::class] = $this->createFakeTransportRegistry(); } if ($this->session) { @@ -461,4 +459,29 @@ class FakeHttp throw new \InvalidArgumentException('$data should be an array or an object.'); } } + + /** + * The fake token travels in the {@see self::AUTH_TOKEN_HEADER_KEY} header, so every transport the + * application has configured is replaced by one that reads it. The configured names are kept because + * {@see \Spiral\Auth\Middleware\AuthTransportMiddleware} resolves a transport by name. + */ + private function createFakeTransportRegistry(): TransportRegistry + { + $registry = new TransportRegistry(); + $transport = new HeaderTransport(static::AUTH_TOKEN_HEADER_KEY); + + try { + $configured = $this->container->get(TransportRegistry::class); + foreach (\array_keys($configured->getTransports()) as $name) { + $registry->setTransport((string) $name, $transport); + } + } catch (\Throwable) { + // Nothing to copy the names from; the fake transport is all there is. + } + + $registry->setTransport('testing', $transport); + $registry->setDefaultTransport('testing'); + + return $registry; + } } diff --git a/tests/src/Http/FakeHttpAuthTransportTest.php b/tests/src/Http/FakeHttpAuthTransportTest.php new file mode 100644 index 0000000..3c34f1b --- /dev/null +++ b/tests/src/Http/FakeHttpAuthTransportTest.php @@ -0,0 +1,55 @@ +fakeHttp() + ->withActor($actor) + ->withMiddleware(ActorReportingMiddleware::class, HeaderAuthMiddleware::class) + ->get('/get/query-params'); + + $response->assertOk(); + $response->assertBodySame($actor::class); + } + + public function testActorIsAuthenticatedThroughEveryTransport(): void + { + $actor = new \stdClass(); + + $response = $this->fakeHttp() + ->withActor($actor) + ->withMiddleware(ActorReportingMiddleware::class, AuthMiddleware::class) + ->get('/get/query-params'); + + $response->assertOk(); + $response->assertBodySame($actor::class); + } + + public function testRequestWithoutActorStaysUnauthenticated(): void + { + $response = $this->fakeHttp() + ->withMiddleware(ActorReportingMiddleware::class, HeaderAuthMiddleware::class) + ->get('/get/query-params'); + + $response->assertOk(); + $response->assertBodySame(ActorReportingMiddleware::NO_ACTOR); + } +} diff --git a/tests/src/Http/Stub/ActorReportingMiddleware.php b/tests/src/Http/Stub/ActorReportingMiddleware.php new file mode 100644 index 0000000..9066187 --- /dev/null +++ b/tests/src/Http/Stub/ActorReportingMiddleware.php @@ -0,0 +1,26 @@ +getAttribute(AuthMiddleware::ATTRIBUTE); + $actor = $context instanceof AuthContextInterface ? $context->getActor() : null; + + return new Response(200, body: $actor === null ? self::NO_ACTOR : $actor::class); + } +} diff --git a/tests/src/Http/Stub/HeaderAuthMiddleware.php b/tests/src/Http/Stub/HeaderAuthMiddleware.php new file mode 100644 index 0000000..5998142 --- /dev/null +++ b/tests/src/Http/Stub/HeaderAuthMiddleware.php @@ -0,0 +1,37 @@ +container->get(ScopeInterface::class), + $this->container->get(ActorProviderInterface::class), + $this->container->get(TokenStorageInterface::class), + $this->container->get(TransportRegistry::class), + ); + + return $middleware->process($request, $handler); + } +}