From 4c600528b29fd8b076c3b8f98bbf7d953a375d18 Mon Sep 17 00:00:00 2001 From: Daniel Seifert Date: Wed, 8 Jan 2025 16:49:50 +0100 Subject: [PATCH] add tests --- tests/unit/ConnectionTest.php | 248 ++++++++++++++++++ .../Exceptions/CommunicationExceptionTest.php | 53 ++++ 2 files changed, 301 insertions(+) create mode 100644 tests/unit/ConnectionTest.php create mode 100644 tests/unit/Exceptions/CommunicationExceptionTest.php diff --git a/tests/unit/ConnectionTest.php b/tests/unit/ConnectionTest.php new file mode 100644 index 0000000..be2262e --- /dev/null +++ b/tests/unit/ConnectionTest.php @@ -0,0 +1,248 @@ + + * @link https://www.oxidmodule.com + */ + +declare(strict_types=1); + +namespace D3\KlicktippPhpClient\tests\unit; + +use D3\KlicktippPhpClient\Connection; +use D3\KlicktippPhpClient\Exceptions\CommunicationException; +use D3\KlicktippPhpClient\Exceptions\NoCredentialsException; +use D3\KlicktippPhpClient\Exceptions\ResponseContentException; +use D3\KlicktippPhpClient\tests\TestCase; +use Exception; +use Generator; +use GuzzleHttp\Client; +use GuzzleHttp\ClientInterface; +use GuzzleHttp\Cookie\CookieJar; +use GuzzleHttp\Exception\ConnectException; +use GuzzleHttp\Exception\RequestException; +use GuzzleHttp\Psr7\Response; +use GuzzleHttp\Psr7\Stream; +use Psr\Http\Message\ResponseInterface; +use ReflectionException; + +/** + * @covers \D3\KlicktippPhpClient\Connection + */ +class ConnectionTest extends TestCase +{ + /** + * @test + * @covers \D3\KlicktippPhpClient\Connection::__construct + * @covers \D3\KlicktippPhpClient\Connection::getClientKey + * @covers \D3\KlicktippPhpClient\Connection::getSecretKey + * @covers \D3\KlicktippPhpClient\Connection::getCookiesJar + * @dataProvider constructDataProvider + */ + public function testConstruct(string $userName, string $password, bool $expectException): void + { + if ($expectException) { + $this->expectException(NoCredentialsException::class); + } + + $sut = new Connection($userName, $password); + + $this->assertSame( + $userName, + $sut->getClientKey() + ); + $this->assertSame( + $password, + $sut->getSecretKey() + ); + $this->assertInstanceOf( + CookieJar::class, + $sut->getCookiesJar() + ); + } + + public static function constructDataProvider(): Generator + { + yield 'all credentials set' => ['myuser', 'mypassword', false]; + yield 'missing username' => ['', 'mypassword', true]; + yield 'missing password' => ['myuser', '', true]; + yield 'no credentials given' => ['', '', true]; + } + + /** + * @test + * @return void + * @covers \D3\KlicktippPhpClient\Connection::setClient + * @covers \D3\KlicktippPhpClient\Connection::getClient + */ + public function testSetClient(): void + { + $clientMock = $this->getMockBuilder(Client::class) + ->setConstructorArgs([]) + ->getMock(); + + $sut = new Connection('foo', 'bar'); + $sut->setClient($clientMock); + + $this->assertSame( + $clientMock, + $sut->getClient() + ); + } + + /** + * @test + * @return void + * @covers \D3\KlicktippPhpClient\Connection::getClient + */ + public function testGetUnconfiguredClient(): void + { + $sut = new Connection('foo', 'bar'); + + $this->assertInstanceOf( + ClientInterface::class, + $sut->getClient() + ); + } + + /** + * @test + * @throws ReflectionException + * @covers \D3\KlicktippPhpClient\Connection::request + * @dataProvider requestDataProvider + */ + public function testRequest(?Exception $thrownException, ?string $expectedException): void + { + $responseMock = $this->getMockBuilder(Response::class) + ->disableOriginalConstructor() + ->onlyMethods(get_class_methods(Response::class)) + ->getMock(); + + $clientMock = $this->getMockBuilder(Client::class) + ->disableOriginalConstructor() + ->onlyMethods(['request']) + ->getMock(); + $clientMock->expects($this->once())->method('request')->will( + $thrownException ? + $this->throwException($thrownException) : + $this->returnValue($responseMock) + ); + + $sutMock = $this->getMockBuilder(Connection::class) + ->setConstructorArgs(['foo', 'bar']) + ->onlyMethods(['getClient', 'getCookiesJar', 'parseResponse']) + ->getMock(); + $sutMock->method('getClient')->willReturn($clientMock); + + if ($expectedException) { + $this->expectException($expectedException); + } + + $this->assertInstanceOf( + ResponseInterface::class, + $this->callMethod( + $sutMock, + 'request', + ['POST', 'fixture.php', ['body' => 'requBody']] + ) + ); + } + + public static function requestDataProvider(): Generator + { + yield 'passed' => [null, null]; + yield 'request exception' => [ + new RequestException( + 'foo', + new \GuzzleHttp\Psr7\Request('POST', 'fixture.php'), + new Response(200, [], 'respBody') + ), CommunicationException::class]; + yield 'guzzle exception' => [ + new ConnectException( + 'foo', + new \GuzzleHttp\Psr7\Request('POST', 'fixture.php') + ), CommunicationException::class]; + } + + /** + * @test + * @return void + * @throws ReflectionException + * @covers \D3\KlicktippPhpClient\Connection::requestAndParse + */ + public function testRequestAndParse(): void + { + $expected = ['foo' => 'bar']; + + $responseMock = $this->getMockBuilder(Response::class) + ->disableOriginalConstructor() + ->getMock(); + + $sutMock = $this->getMockBuilder(Connection::class) + ->disableOriginalConstructor() + ->onlyMethods(['request', 'parseResponse']) + ->getMock(); + $sutMock->expects($this->once())->method('request')->willReturn($responseMock); + $sutMock->expects($this->once())->method('parseResponse')->willReturn($expected); + + $this->assertSame( + $expected, + $this->callMethod( + $sutMock, + 'requestAndParse', + ['POST', 'endpoint.php', ['options'=>'']] + ) + ); + } + + /** + * @test + * @throws ReflectionException + * @covers \D3\KlicktippPhpClient\Connection::parseResponse + * @dataProvider parseResponseDataProvider + */ + public function testParseResponse(int $status, $content, $expected, bool $expectException): void + { + $sut = new Connection('foo', 'bar'); + + $responseBodyMock = $this->getMockBuilder(Stream::class) + ->disableOriginalConstructor() + ->onlyMethods(['getContents']) + ->getMock(); + $responseBodyMock->method('getContents')->willReturn(json_encode($content)); + + $responseMock = $this->getMockBuilder(Response::class) + ->setConstructorArgs([$status, [], $content]) + ->onlyMethods(['getStatusCode']) + ->getMock(); + $responseMock->method('getStatusCode')->willReturn( $status); + + if ($expectException) { + $this->expectException(ResponseContentException::class); + } + + $this->assertSame( + $expected, + $this->callMethod( + $sut, + 'parseResponse', + [$responseMock] + ) + ); + } + + public static function parseResponseDataProvider(): Generator + { + yield '200' => [200, '{"foo": "bar"}', ['foo' => 'bar'], false]; + yield '204' => [204, '{"foo": "bar"}', [], false]; + yield 'no array return' => [200, '"foo"', [], true]; + } +} \ No newline at end of file diff --git a/tests/unit/Exceptions/CommunicationExceptionTest.php b/tests/unit/Exceptions/CommunicationExceptionTest.php new file mode 100644 index 0000000..ef2fce6 --- /dev/null +++ b/tests/unit/Exceptions/CommunicationExceptionTest.php @@ -0,0 +1,53 @@ + + * @link https://www.oxidmodule.com + */ + +declare(strict_types=1); + +namespace D3\KlicktippPhpClient\tests\unit\Exceptions; + +use D3\KlicktippPhpClient\Exceptions\CommunicationException; +use D3\KlicktippPhpClient\tests\TestCase; +use ReflectionException; + +/** + * @covers \D3\KlicktippPhpClient\Exceptions\CommunicationException + */ +class CommunicationExceptionTest extends TestCase +{ + /** + * @test + * @return void + * @throws ReflectionException + * @covers \D3\KlicktippPhpClient\Exceptions\CommunicationException::__construct + */ + public function testConstructor(): void + { + /** @var CommunicationException $sutMock */ + $sutMock = $this->getMockBuilder(CommunicationException::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->callMethod( + $sutMock, + '__construct', + ['myMessage'] + ); + + $this->assertSame( + 'Klicktipp error: myMessage', + $sutMock->getMessage() + ); + } +} \ No newline at end of file