From d42e1189af4a20debcd826857a74224d5ffc7d52 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9s=20Ignacio=20Torres?= Date: Sun, 17 May 2026 00:33:27 -0700 Subject: [PATCH] test: add missing unit tests --- .../Actions/Pds/XrpcExceptionTest.php | 58 +++++++++ .../AppView/GuzzleAppViewClientTest.php | 116 ++++++++++++++++++ 2 files changed, 174 insertions(+) create mode 100644 tests/Application/Actions/Pds/XrpcExceptionTest.php create mode 100644 tests/Infrastructure/Atproto/AppView/GuzzleAppViewClientTest.php diff --git a/tests/Application/Actions/Pds/XrpcExceptionTest.php b/tests/Application/Actions/Pds/XrpcExceptionTest.php new file mode 100644 index 0000000..eba9787 --- /dev/null +++ b/tests/Application/Actions/Pds/XrpcExceptionTest.php @@ -0,0 +1,58 @@ +assertSame('InvalidRequest', $exception->getError()); + $this->assertSame('boom', $exception->getMessage()); + $this->assertSame(400, $exception->getStatusCode()); + } + + public function testConstructorAcceptsCustomStatusCode(): void + { + $exception = new XrpcException('AuthRequired', 'missing token', 401); + + $this->assertSame('AuthRequired', $exception->getError()); + $this->assertSame(401, $exception->getStatusCode()); + } + + public function testInvalidRequestUsesInvalidRequestErrorCode(): void + { + $exception = XrpcException::invalidRequest('something is off'); + + $this->assertSame('InvalidRequest', $exception->getError()); + $this->assertSame('something is off', $exception->getMessage()); + $this->assertSame(400, $exception->getStatusCode()); + } + + public function testInvalidRequestSupportsCustomErrorCode(): void + { + $exception = XrpcException::invalidRequest('Handle too short', 'InvalidHandle'); + + $this->assertSame('InvalidHandle', $exception->getError()); + $this->assertSame('Handle too short', $exception->getMessage()); + $this->assertSame(400, $exception->getStatusCode()); + } + + public function testMissingParamFormatsMessage(): void + { + $exception = XrpcException::missingParam('com.atproto.foo.bar', 'baz'); + + $this->assertSame('InvalidRequest', $exception->getError()); + $this->assertSame( + 'Invalid com.atproto.foo.bar params: Missing required key "baz"', + $exception->getMessage() + ); + $this->assertSame(400, $exception->getStatusCode()); + } +} diff --git a/tests/Infrastructure/Atproto/AppView/GuzzleAppViewClientTest.php b/tests/Infrastructure/Atproto/AppView/GuzzleAppViewClientTest.php new file mode 100644 index 0000000..c9f4796 --- /dev/null +++ b/tests/Infrastructure/Atproto/AppView/GuzzleAppViewClientTest.php @@ -0,0 +1,116 @@ + $queue + * @param list $history + */ + private function makeClient(array $queue, array &$history = []): GuzzleAppViewClient + { + $mock = new MockHandler($queue); + $stack = HandlerStack::create($mock); + $stack->push(Middleware::history($history)); + + $httpClient = new Client([ + 'handler' => $stack, + 'base_uri' => 'https://api.bsky.app/', + ]); + + return new GuzzleAppViewClient($httpClient); + } + + public function testResolveHandleReturnsDidOnSuccess(): void + { + $history = []; + $client = $this->makeClient( + [new Response(200, [], json_encode(['did' => 'did:plc:abc']))], + $history + ); + + $did = $client->resolveHandle('bob.bsky.social'); + + $this->assertSame('did:plc:abc', $did); + $this->assertCount(1, $history); + + /** @var \Psr\Http\Message\RequestInterface $sent */ + $sent = $history[0]['request']; + $this->assertSame('GET', $sent->getMethod()); + $this->assertSame( + 'https://api.bsky.app/xrpc/com.atproto.identity.resolveHandle', + (string) $sent->getUri()->withQuery('') + ); + $this->assertSame('handle=bob.bsky.social', $sent->getUri()->getQuery()); + } + + public function testResolveHandleWrapsNon2xxResponseAsAppViewException(): void + { + $request = new Request('GET', 'xrpc/com.atproto.identity.resolveHandle'); + $client = $this->makeClient([ + new \GuzzleHttp\Exception\BadResponseException( + 'Bad Request', + $request, + new Response(400, [], json_encode(['error' => 'InvalidRequest', 'message' => 'nope'])) + ), + ]); + + $this->expectException(AppViewException::class); + $client->resolveHandle('missing.bsky.social'); + } + + public function testResolveHandleWrapsTransportErrorAsAppViewException(): void + { + $request = new Request('GET', 'xrpc/com.atproto.identity.resolveHandle'); + $client = $this->makeClient([ + new ConnectException('connection refused', $request), + ]); + + $this->expectException(AppViewException::class); + $client->resolveHandle('bob.bsky.social'); + } + + public function testResolveHandleThrowsWhenDidFieldMissing(): void + { + $client = $this->makeClient([ + new Response(200, [], json_encode(['notTheDid' => 'oops'])), + ]); + + $this->expectException(AppViewException::class); + $client->resolveHandle('bob.bsky.social'); + } + + public function testResolveHandleThrowsWhenResponseIsNotJson(): void + { + $client = $this->makeClient([ + new Response(200, [], 'this is not json'), + ]); + + $this->expectException(AppViewException::class); + $client->resolveHandle('bob.bsky.social'); + } + + public function testResolveHandleThrowsWhenDidIsNotAString(): void + { + $client = $this->makeClient([ + new Response(200, [], json_encode(['did' => 123])), + ]); + + $this->expectException(AppViewException::class); + $client->resolveHandle('bob.bsky.social'); + } +} -- 2.51.2