diff --git a/helper/FFI/ClientTrait.php b/helper/FFI/ClientTrait.php index ebfc7342..9322728a 100644 --- a/helper/FFI/ClientTrait.php +++ b/helper/FFI/ClientTrait.php @@ -8,6 +8,8 @@ use PhpPact\Consumer\Driver\Exception\InteractionPendingNotSetException; use PhpPact\Consumer\Driver\Exception\PactFileNotWrittenException; use PhpPact\Consumer\Driver\Exception\PactNotModifiedException; +use PhpPact\Consumer\Exception\MockServerNotStartedException; +use PhpPact\Consumer\Exception\MockServerPactFileNotWrittenException; use PhpPact\FFI\ClientInterface; use PhpPact\Plugin\Exception\PluginNotLoadedException; use PHPUnit\Framework\Constraint\Constraint; @@ -350,4 +352,81 @@ protected function expectsUsingPlugin(int $pact, string $name, ?string $version, }); } } + + protected function expectsCleanupMockServer(int $port, bool $result): void + { + $this->client + ->expects($this->once()) + ->method('cleanupMockServer') + ->with($port) + ->willReturn($result); + } + + protected function expectsMockServerMatched(int $port, bool $result): void + { + $this->client + ->expects($this->once()) + ->method('mockServerMatched') + ->with($port) + ->willReturn($result); + } + + protected function expectsMockServerMismatches(int $port, string $result, bool $matched): void + { + if (!$matched) { + $this->client + ->expects($this->once()) + ->method('mockServerMismatches') + ->with($port) + ->willReturn($result); + } else { + $this->client + ->expects($this->never()) + ->method('mockServerMismatches'); + } + } + + protected function expectsWritePactFile(int $port, string $directory, bool $overwrite, int $result, bool $matched): void + { + if ($matched) { + $this->client + ->expects($this->once()) + ->method('writePactFile') + ->with($port, $directory, $overwrite) + ->willReturn($result); + if ($result) { + $this->expectException(MockServerPactFileNotWrittenException::class); + $this->expectExceptionMessage(match ($result) { + 1 => 'A general panic was caught', + 2 => 'The pact file was not able to be written', + 3 => 'A mock server with the provided port was not found', + default => 'Unknown error', + }); + } + } else { + $this->client + ->expects($this->never()) + ->method('writePactFile'); + } + } + + protected function expectsCreateMockServerForTransport(int $pact, string $host, int $port, string $transport, ?string $transportConfig, int $result): void + { + $this->client + ->expects($this->once()) + ->method('createMockServerForTransport') + ->with($pact, $host, $port, $transport, $transportConfig) + ->willReturn($result); + if ($result < 0) { + $this->expectException(MockServerNotStartedException::class); + $this->expectExceptionMessage(match ($result) { + -1 => 'An invalid handle was received. Handles should be created with `pactffi_new_pact`', + -2 => 'Transport_config is not valid JSON', + -3 => 'The mock server could not be started', + -4 => 'The method panicked', + -5 => 'The address is not valid', + default => 'Unknown error', + }); + } + } } diff --git a/src/PhpPact/Consumer/Service/MockServer.php b/src/PhpPact/Consumer/Service/MockServer.php index b34dd9fa..996a1c7c 100644 --- a/src/PhpPact/Consumer/Service/MockServer.php +++ b/src/PhpPact/Consumer/Service/MockServer.php @@ -22,8 +22,7 @@ public function __construct( public function start(): void { - $port = $this->client->call( - 'pactffi_create_mock_server_for_transport', + $port = $this->client->createMockServerForTransport( $this->pactDriver->getPact()->handle, $this->config->getHost(), $this->config->getPort(), @@ -66,8 +65,7 @@ protected function getTransportConfig(): ?string public function writePact(): void { - $error = $this->client->call( - 'pactffi_write_pact_file', + $error = $this->client->writePactFile( $this->config->getPort(), $this->config->getPactDir(), $this->config->getPactFileWriteMode() === PactConfigInterface::MODE_OVERWRITE @@ -79,19 +77,20 @@ public function writePact(): void public function cleanUp(): void { - $this->client->call('pactffi_cleanup_mock_server', $this->config->getPort()); + $success = $this->client->cleanupMockServer($this->config->getPort()); + if (!$success) { + trigger_error(sprintf("Can not clean up mock server: Mock server with the given port number '%s' does not exist, or the function panics", $this->config->getPort()), E_USER_WARNING); + } $this->pactDriver->cleanUp(); } private function isMatched(): bool { - return $this->client->call('pactffi_mock_server_matched', $this->config->getPort()); + return $this->client->mockServerMatched($this->config->getPort()); } private function getMismatches(): string { - $cData = $this->client->call('pactffi_mock_server_mismatches', $this->config->getPort()); - - return FFI::string($cData); + return $this->client->mockServerMismatches($this->config->getPort()); } } diff --git a/src/PhpPact/FFI/Client.php b/src/PhpPact/FFI/Client.php index 75bf924d..6006bb2f 100644 --- a/src/PhpPact/FFI/Client.php +++ b/src/PhpPact/FFI/Client.php @@ -247,6 +247,59 @@ public function usingPlugin(int $pact, string $name, ?string $version): int return $result; } + public function cleanupMockServer(int $port): bool + { + $method = 'pactffi_cleanup_mock_server'; + $result = $this->call($method, $port); + if (!is_bool($result)) { + throw new InvalidResultException(sprintf('Invalid result of "%s". Expected "boolean", but got "%s"', $method, get_debug_type($result))); + } + return $result; + } + + public function mockServerMatched(int $port): bool + { + $method = 'pactffi_mock_server_matched'; + $result = $this->call($method, $port); + if (!is_bool($result)) { + throw new InvalidResultException(sprintf('Invalid result of "%s". Expected "boolean", but got "%s"', $method, get_debug_type($result))); + } + return $result; + } + + public function mockServerMismatches(int $port): string + { + $method = 'pactffi_mock_server_mismatches'; + $result = $this->call($method, $port); + if ($result === null) { + return ''; + } + if (!$result instanceof CData) { + throw new InvalidResultException(sprintf('Invalid result of "%s". Expected "%s", but got "%s"', $method, CData::class, get_debug_type($result))); + } + return FFI::string($result); + } + + public function writePactFile(int $port, string $directory, bool $overwrite): int + { + $method = 'pactffi_write_pact_file'; + $result = $this->call($method, $port, $directory, $overwrite); + if (!is_int($result)) { + throw new InvalidResultException(sprintf('Invalid result of "%s". Expected "integer", but got "%s"', $method, get_debug_type($result))); + } + return $result; + } + + public function createMockServerForTransport(int $pact, string $host, int $port, string $transport, ?string $transportConfig): int + { + $method = 'pactffi_create_mock_server_for_transport'; + $result = $this->call($method, $pact, $host, $port, $transport, $transportConfig); + if (!is_int($result)) { + throw new InvalidResultException(sprintf('Invalid result of "%s". Expected "integer", but got "%s"', $method, get_debug_type($result))); + } + return $result; + } + public function getInteractionPartRequest(): int { return $this->getEnum('InteractionPart_Request'); diff --git a/src/PhpPact/FFI/ClientInterface.php b/src/PhpPact/FFI/ClientInterface.php index 71f78a11..544fca5b 100644 --- a/src/PhpPact/FFI/ClientInterface.php +++ b/src/PhpPact/FFI/ClientInterface.php @@ -55,6 +55,16 @@ public function cleanupPlugins(int $pact): void; public function usingPlugin(int $pact, string $name, ?string $version): int; + public function cleanupMockServer(int $port): bool; + + public function mockServerMatched(int $port): bool; + + public function mockServerMismatches(int $port): string; + + public function writePactFile(int $port, string $directory, bool $overwrite): int; + + public function createMockServerForTransport(int $pact, string $host, int $port, string $transport, ?string $transportConfig): int; + public function getInteractionPartRequest(): int; public function getInteractionPartResponse(): int; diff --git a/tests/PhpPact/Consumer/Service/MockServerTest.php b/tests/PhpPact/Consumer/Service/MockServerTest.php index 0da0bd90..b07f7493 100644 --- a/tests/PhpPact/Consumer/Service/MockServerTest.php +++ b/tests/PhpPact/Consumer/Service/MockServerTest.php @@ -2,10 +2,8 @@ namespace PhpPactTest\Consumer\Service; -use FFI; use PhpPact\Config\PactConfigInterface; use PhpPact\Consumer\Driver\Pact\PactDriverInterface; -use PhpPact\Consumer\Exception\MockServerNotStartedException; use PhpPact\Consumer\Exception\MockServerPactFileNotWrittenException; use PhpPact\Consumer\Model\Pact\Pact; use PhpPact\Consumer\Service\MockServer; @@ -56,21 +54,7 @@ public function testStart(int $returnedPort, bool $secure): void ->expects($this->once()) ->method('getPact') ->willReturn(new Pact($this->pactHandle)); - $calls = [ - ['pactffi_create_mock_server_for_transport', $this->pactHandle, $this->host, $this->port, $this->getTransport($secure), null, $returnedPort], - ]; - $this->assertClientCalls($calls); - if ($returnedPort < 0) { - $this->expectException(MockServerNotStartedException::class); - $this->expectExceptionMessage(match ($returnedPort) { - -1 => 'An invalid handle was received. Handles should be created with `pactffi_new_pact`', - -2 => 'Transport_config is not valid JSON', - -3 => 'The mock server could not be started', - -4 => 'The method panicked', - -5 => 'The address is not valid', - default => 'Unknown error', - }); - } + $this->expectsCreateMockServerForTransport($this->pactHandle, $this->host, $this->port, $this->getTransport($secure), null, $returnedPort); $this->mockServer->start(); $this->assertSame($returnedPort, $this->config->getPort()); } @@ -81,16 +65,10 @@ public function testVerify(bool $matched): void { $this->config->setPort($this->port); $this->config->setPactDir($this->pactDir); - $calls = $matched ? [ - ['pactffi_mock_server_matched', $this->port, $matched], - ['pactffi_write_pact_file', $this->port, $this->pactDir, false, 0], - ['pactffi_cleanup_mock_server', $this->port, null], - ] : [ - ['pactffi_mock_server_matched', $this->port, $matched], - ['pactffi_mock_server_mismatches', $this->port, FFI::new('char[1]')], - ['pactffi_cleanup_mock_server', $this->port, null], - ]; - $this->assertClientCalls($calls); + $this->expectsMockServerMatched($this->port, $matched); + $this->expectsWritePactFile($this->port, $this->pactDir, false, 0, $matched); + $this->expectsMockServerMismatches($this->port, '', $matched); + $this->expectsCleanupMockServer($this->port, true); $this->pactDriver ->expects($this->once()) ->method('cleanUp'); @@ -114,10 +92,7 @@ public function testWritePact(int $error, string $writeMode): void $this->config->setPort($this->port); $this->config->setPactDir($this->pactDir); $this->config->setPactFileWriteMode($writeMode); - $calls = [ - ['pactffi_write_pact_file', $this->port, $this->pactDir, $writeMode === PactConfigInterface::MODE_OVERWRITE, $error], - ]; - $this->assertClientCalls($calls); + $this->expectsWritePactFile($this->port, $this->pactDir, $writeMode === PactConfigInterface::MODE_OVERWRITE, $error, true); if ($error) { $this->expectException(MockServerPactFileNotWrittenException::class); $this->expectExceptionMessage(match ($error) { @@ -130,13 +105,12 @@ public function testWritePact(int $error, string $writeMode): void $this->mockServer->writePact(); } - public function testCleanUp(): void + #[TestWith([true])] + #[TestWith([false])] + public function testCleanUp(bool $success): void { $this->config->setPort($this->port); - $calls = [ - ['pactffi_cleanup_mock_server', $this->port, null], - ]; - $this->assertClientCalls($calls); + $this->expectsCleanupMockServer($this->port, $success); $this->pactDriver ->expects($this->once()) ->method('cleanUp'); diff --git a/tests/PhpPact/FFI/ClientTest.php b/tests/PhpPact/FFI/ClientTest.php index 5ece2e18..8c3824c1 100644 --- a/tests/PhpPact/FFI/ClientTest.php +++ b/tests/PhpPact/FFI/ClientTest.php @@ -165,6 +165,36 @@ public function testUsingPlugin(): void $this->assertSame(2, $result); } + public function testCleanupMockServer(): void + { + $result = $this->client->cleanupMockServer(1234); + $this->assertFalse($result); + } + + public function testMockServerMatched(): void + { + $result = $this->client->mockServerMatched(1234); + $this->assertFalse($result); + } + + public function testMockServerMismatches(): void + { + $result = $this->client->mockServerMismatches(1234); + $this->assertSame('', $result); + } + + public function testWritePactFile(): void + { + $result = $this->client->writePactFile(1234, 'test', true); + $this->assertSame(3, $result); + } + + public function testCreateMockServerForTransport(): void + { + $result = $this->client->createMockServerForTransport(1, 'localhost', 1234, 'http', null); + $this->assertSame(-1, $result); + } + public function testGetInteractionPartRequest(): void { $this->assertSame(0, $this->client->getInteractionPartRequest());