diff --git a/README.md b/README.md index e208e86d1..e50807bd4 100644 --- a/README.md +++ b/README.md @@ -373,7 +373,7 @@ $consumerMessage = new ExampleMessageConsumer(); $callback = [$consumerMessage, 'ProcessSong']; $builder->setCallback($callback); -$builder->verify(); +$this->assertTrue($builder->verify()); ``` diff --git a/example/src/MessageConsumer/ExampleMessageConsumer.php b/example/src/MessageConsumer/ExampleMessageConsumer.php index 0a8219440..77851aa69 100644 --- a/example/src/MessageConsumer/ExampleMessageConsumer.php +++ b/example/src/MessageConsumer/ExampleMessageConsumer.php @@ -4,7 +4,7 @@ class ExampleMessageConsumer { - public function ProcessText($message) + public function ProcessText(string $message): object { $obj = \json_decode($message); print ' [x] Processed ' . \print_r($obj->contents->text, true) . "\n"; @@ -12,7 +12,7 @@ public function ProcessText($message) return $obj; } - public function ProcessSong($message) + public function ProcessSong(string $message): object { $obj = \json_decode($message); print ' [x] Processed ' . \print_r($obj->contents->song, true) . "\n"; diff --git a/example/tests/MessageConsumer/ExampleMessageConsumerTest.php b/example/tests/MessageConsumer/ExampleMessageConsumerTest.php index e9bda5d01..569733450 100644 --- a/example/tests/MessageConsumer/ExampleMessageConsumerTest.php +++ b/example/tests/MessageConsumer/ExampleMessageConsumerTest.php @@ -2,8 +2,6 @@ namespace MessageConsumer; -require_once __DIR__ . '/../../src/MessageConsumer/ExampleMessageConsumer.php'; - use Exception; use PhpPact\Consumer\MessageBuilder; use PhpPact\Config\PactConfigInterface; @@ -28,18 +26,6 @@ public static function setUpBeforeClass(): void ->setPactDir(__DIR__ . '/../../output/'); } - public static function tearDownAfterClass(): void - { - parent::tearDownAfterClass(); - - // build out brokerHttpService as your example - /* - $brokerHttpService = new BrokerHttpClient(new GuzzleClient(), new Uri($pactBrokerUri)); - $brokerHttpService->publishJson($json, $consumerVersion); - $brokerHttpService->tag($this->mockServerConfig->getConsumer(), $consumerVersion, $tag); - */ - } - /** * @throws Exception */ @@ -53,7 +39,7 @@ public function testProcessText() $metadata = ['queue'=>'wind cries', 'routing_key'=>'wind cries']; $builder - ->given('a message', ['foo']) + ->given('a message', ['foo' => 'bar']) ->expectsToReceive('an alligator named Mary exists') ->withMetadata($metadata) ->withContent($contents); @@ -63,11 +49,7 @@ public function testProcessText() $callback = [$consumerMessage, 'ProcessText']; $builder->setCallback($callback); - $hasException = false; - - $builder->verify(); - - $this->assertTrue(true, 'Expects to reach this true statement by running verify()'); + $this->assertTrue($builder->verify()); } /** @@ -93,14 +75,6 @@ public function testProcessSong() $callback = [$consumerMessage, 'ProcessSong']; $builder->setCallback($callback); - $hasException = false; - - try { - $builder->verify(); - } catch (Exception $e) { - $hasException = true; - } - - $this->assertFalse($hasException, 'Expects verification to pass without exceptions being thrown'); + $this->assertTrue($builder->verify()); } } diff --git a/src/PhpPact/Consumer/Driver/Interaction/MessageDriver.php b/src/PhpPact/Consumer/Driver/Interaction/MessageDriver.php new file mode 100644 index 000000000..0a9def471 --- /dev/null +++ b/src/PhpPact/Consumer/Driver/Interaction/MessageDriver.php @@ -0,0 +1,55 @@ +id = $this->client->call('pactffi_new_message_interaction', $this->pactDriver->getId(), $description); + } + + public function withContents(?string $contentType = null, ?string $body = null): void + { + if (is_null($body)) { + return; + } + $data = StringData::createFrom($body); + $this->client->call('pactffi_message_with_contents', $this->id, $contentType, $data->getValue(), $data->getSize()); + } + + public function expectsToReceive(string $description): void + { + $this->client->call('pactffi_message_expects_to_receive', $this->id, $description); + } + + public function given(array $providerStates): void + { + foreach ($providerStates as $providerState) { + $this->client->call('pactffi_message_given', $this->id, $providerState->getName()); + foreach ($providerState->getParams() as $key => $value) { + $this->client->call('pactffi_message_given_with_param', $this->id, $providerState->getName(), (string) $key, (string) $value); + } + } + } + + public function withMetadata(array $metadata): void + { + foreach ($metadata as $key => $value) { + $this->client->call('pactffi_message_with_metadata', $this->id, (string) $key, (string) $value); + } + } + + public function reify(): string + { + return $this->client->call('pactffi_message_reify', $this->id); + } + + public function writePactAndCleanUp(): void + { + $this->pactDriver->writePact(); + $this->pactDriver->cleanUp(); + } +} diff --git a/src/PhpPact/Consumer/Driver/Interaction/MessageDriverInterface.php b/src/PhpPact/Consumer/Driver/Interaction/MessageDriverInterface.php new file mode 100644 index 000000000..39c561a3c --- /dev/null +++ b/src/PhpPact/Consumer/Driver/Interaction/MessageDriverInterface.php @@ -0,0 +1,26 @@ + $metadata + */ + public function withMetadata(array $metadata): void; + + public function reify(): string; + + public function writePactAndCleanUp(): void; +} diff --git a/src/PhpPact/Consumer/Factory/MessageRegistryFactory.php b/src/PhpPact/Consumer/Factory/MessageRegistryFactory.php new file mode 100644 index 000000000..ccb83f7fd --- /dev/null +++ b/src/PhpPact/Consumer/Factory/MessageRegistryFactory.php @@ -0,0 +1,22 @@ +config = $config; - $this->message = new Message(); - $this->pactMessage = new PactMessage(); + $this->registry = $registry instanceof MessageRegistryInterface ? $registry : MessageRegistryFactory::create($registry); + $this->message = new Message(); } /** @@ -82,7 +82,7 @@ public function withMetadata(array $metadata): self * * @param mixed $contents required to be in the message */ - public function withContent($contents): self + public function withContent(mixed $contents): self { $this->message->setContents($contents); @@ -94,7 +94,9 @@ public function withContent($contents): self */ public function reify(): string { - return $this->pactMessage->reify($this->message); + $this->registry->registerMessage($this->message); + + return $this->registry->reify(); } /** @@ -107,18 +109,16 @@ public function verifyMessage(callable $callback, ?string $description = null): { $this->setCallback($callback, $description); - return $this->verify($description); + return $this->verify(); } /** * Verify the use of the pact by calling the callback * It also calls finalize to write the pact * - * @param null|string $description description of the pact and thus callback - * * @throws \Exception if callback is not set */ - public function verify(?string $description = null): bool + public function verify(): bool { if (\count($this->callback) < 1) { throw new \Exception('Callbacks need to exist to run verify.'); @@ -133,21 +133,9 @@ public function verify(?string $description = null): bool \call_user_func($callback, $pactJson); } - return $this->writePact(); + return $this->registry->writePactAndCleanUp(); } catch (\Exception $e) { return false; } } - - /** - * Write the Pact without deleting the interactions. - * @throws \JsonException - */ - public function writePact(): bool - { - // you do not want to save the reified json - $pactJson = \json_encode($this->message, JSON_THROW_ON_ERROR); - - return $this->pactMessage->update($pactJson, $this->config->getConsumer(), $this->config->getProvider(), $this->config->getPactDir()); - } } diff --git a/src/PhpPact/Consumer/Model/Message.php b/src/PhpPact/Consumer/Model/Message.php index af2c047dd..1b8b3a4e1 100644 --- a/src/PhpPact/Consumer/Model/Message.php +++ b/src/PhpPact/Consumer/Model/Message.php @@ -14,7 +14,7 @@ class Message /** * @var array */ - private array $metadata; + private array $metadata = []; private mixed $contents; @@ -43,11 +43,19 @@ public function getMetadata(): array */ public function setMetadata(array $metadata): self { - $this->metadata = $metadata; + $this->metadata = []; + foreach ($metadata as $key => $value) { + $this->setMetadataValue($key, $value); + } return $this; } + private function setMetadataValue(string $key, string $value): void + { + $this->metadata[$key] = $value; + } + public function getContents(): mixed { return $this->contents; diff --git a/src/PhpPact/Consumer/Service/MessageRegistry.php b/src/PhpPact/Consumer/Service/MessageRegistry.php new file mode 100644 index 000000000..bfe3bba04 --- /dev/null +++ b/src/PhpPact/Consumer/Service/MessageRegistry.php @@ -0,0 +1,78 @@ +newInteraction($message) + ->given($message) + ->expectsToReceive($message) + ->withMetadata($message) + ->withContents($message); + } + + private function newInteraction(Message $message): self + { + $this->driver->newInteraction($message->getDescription()); + + return $this; + } + + private function given(Message $message): self + { + $this->driver->given($message->getProviderStates()); + + return $this; + } + + private function expectsToReceive(Message $message): self + { + $this->driver->expectsToReceive($message->getDescription()); + + return $this; + } + + private function withMetadata(Message $message): self + { + $this->driver->withMetadata($message->getMetadata()); + + return $this; + } + + private function withContents(Message $message): self + { + if (\is_string($message->getContents())) { + $contents = $message->getContents(); + $contentType = 'text/plain'; + } else { + $contents = \json_encode($message->getContents(), JSON_THROW_ON_ERROR); + $contentType = 'application/json'; + } + + $this->driver->withContents($contentType, $contents); + + return $this; + } + + public function reify(): string + { + return $this->driver->reify(); + } + + public function writePactAndCleanUp(): bool + { + $this->driver->writePactAndCleanUp(); + + return true; + } +} diff --git a/src/PhpPact/Consumer/Service/MessageRegistryInterface.php b/src/PhpPact/Consumer/Service/MessageRegistryInterface.php new file mode 100644 index 000000000..2edf8fbb9 --- /dev/null +++ b/src/PhpPact/Consumer/Service/MessageRegistryInterface.php @@ -0,0 +1,14 @@ +value; + } + + public function getSize(): int + { + return $this->size; + } + + public static function createFrom(string $value): ?self + { + $length = \strlen($value); + $size = $length + 1; + $cData = FFI::new("uint8_t[{$size}]"); + FFI::memcpy($cData, $value, $length); + + return new self($cData, $size); + } +} diff --git a/src/PhpPact/Standalone/Installer/Model/Scripts.php b/src/PhpPact/Standalone/Installer/Model/Scripts.php index 243bf45ac..02932804a 100644 --- a/src/PhpPact/Standalone/Installer/Model/Scripts.php +++ b/src/PhpPact/Standalone/Installer/Model/Scripts.php @@ -41,11 +41,6 @@ public static function getBroker(): string return self::$destinationDir . '/bin/pact-ruby-standalone/bin/pact-broker' . self::getSuffix(); } - public static function getPactMessage(): string - { - return self::$destinationDir . '/bin/pact-ruby-standalone/bin/pact-message' . self::getSuffix(); - } - private static function getSuffix(): string { return (PHP_OS_FAMILY === 'Windows' ? '.bat' : ''); diff --git a/src/PhpPact/Standalone/PactMessage/PactMessage.php b/src/PhpPact/Standalone/PactMessage/PactMessage.php deleted file mode 100644 index b96b29d36..000000000 --- a/src/PhpPact/Standalone/PactMessage/PactMessage.php +++ /dev/null @@ -1,47 +0,0 @@ -runBlocking(); - - $output = $process->getOutput(); - \preg_replace("/\r|\n/", '', $output); - - return $output; - } - - /** - * Update a pact with the given message, or create the pact if it does not exist. The MESSAGE_JSON may be in the legacy Ruby JSON format or the v2+ format. - */ - public function update(string $pactJson, string $consumer, string $provider, string $pactDir): bool - { - $arguments = []; - $arguments[] = 'update'; - $arguments[] = "--consumer={$consumer}"; - $arguments[] = "--provider={$provider}"; - $arguments[] = "--pact-dir={$pactDir}"; - $arguments[] = "'" . $pactJson . "'"; - - $process = new ProcessRunner(Scripts::getPactMessage(), $arguments); - $process->runBlocking(); - - \sleep(1); - - return true; - } -} diff --git a/tests/PhpPact/Consumer/Model/MessageTest.php b/tests/PhpPact/Consumer/Model/MessageTest.php new file mode 100644 index 000000000..165ed8a81 --- /dev/null +++ b/tests/PhpPact/Consumer/Model/MessageTest.php @@ -0,0 +1,34 @@ + 'bar']; + $metadata = ['queue' => 'foo', 'routing_key' => 'bar']; + $contents = 'test'; + + $subject = (new Message()) + ->setDescription($description) + ->addProviderState($providerStateName, $providerStateParams) + ->setMetadata($metadata) + ->setContents($contents); + + static::assertSame($description, $subject->getDescription()); + $providerStates = $subject->getProviderStates(); + static::assertCount(1, $providerStates); + static::assertContainsOnlyInstancesOf(ProviderState::class, $providerStates); + static::assertEquals($providerStateName, $providerStates[0]->getName()); + static::assertEquals($providerStateParams, $providerStates[0]->getParams()); + static::assertSame($metadata, $subject->getMetadata()); + static::assertSame($contents, $subject->getContents()); + } +}