diff --git a/phpunit.xml.dist b/phpunit.xml.dist index 893b9ff..f6ab3a0 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -1,7 +1,7 @@ - + ./tests diff --git a/src/Validators/ReplicateWebhookValidator.php b/src/Validators/ReplicateWebhookValidator.php index 636b0b8..098aee3 100644 --- a/src/Validators/ReplicateWebhookValidator.php +++ b/src/Validators/ReplicateWebhookValidator.php @@ -20,8 +20,8 @@ public function validate(array $input): array { $filteredWebhookConfig = $this->validateWebhookConfig($input); - $this->validateWebhookUrl($filteredWebhookConfig['webhook']); - $this->validateWebhookEventFilters($filteredWebhookConfig['webhook_events_filter']); + $this->validateWebhookUrl($filteredWebhookConfig['webhook'] ?? []); + $this->validateWebhookEventFilters($filteredWebhookConfig['webhook_events_filter'] ?? null); return [ 'webhook' => $filteredWebhookConfig['webhook'], @@ -74,13 +74,17 @@ private function validateWebhookUrl(string $url): void /** * Validates webhook event filters. * - * @param array $filters + * @param array|null $filters * * @return void * @throws ReplicateWebhookInputException */ - private function validateWebhookEventFilters(array $filters): void + private function validateWebhookEventFilters(?array $filters): void { + if (null === $filters) { + throw new ReplicateWebhookInputException('Empty webhook event filter provided'); + } + $allowedWebhookEventFilters = ['start', 'output', 'logs', 'completed']; $disallowedWebhookEventFilters = array_diff($filters, $allowedWebhookEventFilters); diff --git a/tests/BaseTest.php b/tests/BaseTest.php deleted file mode 100644 index 4ff8a40..0000000 --- a/tests/BaseTest.php +++ /dev/null @@ -1,15 +0,0 @@ -assertTrue(true); - } -} \ No newline at end of file diff --git a/tests/Commands/GetPredictionCommandTest.php b/tests/Commands/GetPredictionCommandTest.php new file mode 100644 index 0000000..ee3218c --- /dev/null +++ b/tests/Commands/GetPredictionCommandTest.php @@ -0,0 +1,56 @@ + 'application/json'], + $responseBody + ); + + $mockClient = $this->createMock(Client::class); + $mockClient->expects($this->once()) + ->method('get') + ->with('predictions/1') + ->willReturn($mockResponse); + + $command = new GetPredictionCommand($mockClient); + $result = $command->handle('1'); + + $this->assertSame('1', $result->id); + $this->assertSame('v1', $result->version); + $this->assertSame([ + 'get' => 'https://api.replicate.com/v1/predictions/foo', + 'cancel' => 'https://api.replicate.com/v1/predictions/foo/cancel', + ], $result->urls); + $this->assertSame('succeeded', $result->status); + $this->assertSame([ + 'prompt' => 'foo', + ], $result->input); + $this->assertEquals(DateTimeImmutable::createFromFormat('Y-m-d\TH:i:s.u\Z', '2022-01-01T01:23:45.678900Z'), $result->created_at); + $this->assertEquals(DateTimeImmutable::createFromFormat('Y-m-d\TH:i:s.u\Z', '2022-01-01T01:23:46.678900Z'), $result->started_at); + $this->assertEquals(DateTimeImmutable::createFromFormat('Y-m-d\TH:i:s.u\Z', '2022-01-01T01:23:47.678900Z'), $result->completed_at); + $this->assertSame('api', $result->source); + $this->assertSame([ + "https://replicate.delivery/pbxt/foo/out-0.png" + ], $result->output); + $this->assertSame("Using seed", $result->logs); + $this->assertSame([ + 'predict_time' => 0.95, + ], $result->metrics); + $this->assertNull($result->error); + } +} \ No newline at end of file diff --git a/tests/Fixtures/PredictionDataFixture.php b/tests/Fixtures/PredictionDataFixture.php new file mode 100644 index 0000000..fd43381 --- /dev/null +++ b/tests/Fixtures/PredictionDataFixture.php @@ -0,0 +1,34 @@ + '1', + 'version' => 'v1', + 'urls' => [ + 'get' => 'https://api.replicate.com/v1/predictions/foo', + 'cancel' => 'https://api.replicate.com/v1/predictions/foo/cancel', + ], + 'status' => 'succeeded', + 'input' => [ + 'prompt' => 'foo', + ], + 'created_at' => '2022-01-01T01:23:45.678900Z', + 'started_at' => '2022-01-01T01:23:46.678900Z', + 'completed_at' => '2022-01-01T01:23:47.678900Z', + 'source' => 'api', + 'output' => [ + "https://replicate.delivery/pbxt/foo/out-0.png" + ], + 'logs' => "Using seed", + 'metrics' => [ + 'predict_time' => 0.95, + ], + 'error' => null, + ]; + } +} \ No newline at end of file diff --git a/tests/Fixtures/PredictionsDataFixture.php b/tests/Fixtures/PredictionsDataFixture.php new file mode 100644 index 0000000..386e096 --- /dev/null +++ b/tests/Fixtures/PredictionsDataFixture.php @@ -0,0 +1,64 @@ + 'https://api.replicate.com/v1/predictions', + 'next' => 'https://api.replicate.com/v1/predictions?cursor=foo', + 'results' => [ + [ + 'id' => '1', + 'version' => 'v1', + 'urls' => [ + 'get' => 'https://api.replicate.com/v1/predictions/foo', + 'cancel' => 'https://api.replicate.com/v1/predictions/foo/cancel', + ], + 'status' => 'succeeded', + 'input' => [ + 'prompt' => 'foo', + ], + 'created_at' => '2022-01-01T01:23:45.678900Z', + 'started_at' => '2022-01-01T01:23:46.678900Z', + 'completed_at' => '2022-01-01T01:23:47.678900Z', + 'source' => 'api', + 'output' => [ + "https://replicate.delivery/pbxt/foo/out-0.png" + ], + 'logs' => "Using seed", + 'metrics' => [ + 'predict_time' => 0.95, + ], + 'error' => null, + ], + [ + 'id' => '2', + 'version' => 'v1', + 'urls' => [ + 'get' => 'https://api.replicate.com/v1/predictions/foo', + 'cancel' => 'https://api.replicate.com/v1/predictions/foo/cancel', + ], + 'status' => 'succeeded', + 'input' => [ + 'prompt' => 'foo', + ], + 'created_at' => '2022-01-01T01:23:45.678900Z', + 'started_at' => '2022-01-01T01:23:46.678900Z', + 'completed_at' => '2022-01-01T01:23:47.678900Z', + 'source' => 'api', + 'output' => [ + "https://replicate.delivery/pbxt/foo/out-0.png" + ], + 'logs' => "Using seed", + 'metrics' => [ + 'predict_time' => 0.95, + ], + 'error' => null, + ], + ], + ]; + } +} \ No newline at end of file diff --git a/tests/Mappers/PredictionMapperTest.php b/tests/Mappers/PredictionMapperTest.php new file mode 100644 index 0000000..c078387 --- /dev/null +++ b/tests/Mappers/PredictionMapperTest.php @@ -0,0 +1,41 @@ +predictionMapper = new PredictionMapper(); + } + + public function testMap(): void + { + $data = PredictionDataFixture::get(); + + $prediction = $this->predictionMapper->map($data); + + $this->assertSame('1', $prediction->id); + $this->assertSame('v1', $prediction->version); + $this->assertSame($data['urls'], $prediction->urls); + $this->assertSame('succeeded', $prediction->status); + $this->assertSame($data['input'], $prediction->input); + $this->assertEquals(DateTimeImmutable::createFromFormat('Y-m-d\TH:i:s.u\Z', $data['created_at']), $prediction->created_at); + $this->assertEquals(DateTimeImmutable::createFromFormat('Y-m-d\TH:i:s.u\Z', $data['started_at']), $prediction->started_at); + $this->assertEquals(DateTimeImmutable::createFromFormat('Y-m-d\TH:i:s.u\Z', $data['completed_at']), $prediction->completed_at); + $this->assertSame($data['source'], $prediction->source); + $this->assertSame($data['output'], $prediction->output); + $this->assertSame($data['logs'], $prediction->logs); + $this->assertSame($data['metrics'], $prediction->metrics); + $this->assertSame($data['error'], $prediction->error); + } +} diff --git a/tests/Model/PredictionsTest.php b/tests/Model/PredictionsTest.php new file mode 100644 index 0000000..7e7a1ec --- /dev/null +++ b/tests/Model/PredictionsTest.php @@ -0,0 +1,26 @@ +assertEquals('https://api.replicate.com/v1/predictions', $predictions->previous); + $this->assertEquals('https://api.replicate.com/v1/predictions?cursor=foo', $predictions->next); + $this->assertCount(2, $predictions->results); + $this->assertInstanceOf(Prediction::class, $predictions->results[0]); + $this->assertInstanceOf(Prediction::class, $predictions->results[1]); + $this->assertEquals('1', $predictions->results[0]->id); + $this->assertEquals('2', $predictions->results[1]->id); + } +} \ No newline at end of file diff --git a/tests/Validators/ReplicateWebhookValidatorTest.php b/tests/Validators/ReplicateWebhookValidatorTest.php new file mode 100644 index 0000000..db27e70 --- /dev/null +++ b/tests/Validators/ReplicateWebhookValidatorTest.php @@ -0,0 +1,76 @@ + 'https://example.com', + 'webhook_events_filter' => ['start', 'output', 'logs', 'completed'] + ]; + $expectedResult = [ + 'webhook' => 'https://example.com', + 'webhook_events_filter' => ['start', 'output', 'logs', 'completed'] + ]; + $this->assertSame($expectedResult, $validator->validate($input)); + } + + public function test_validate_with_missing_key(): void + { + $validator = new ReplicateWebhookValidator(); + + $input = [ + 'webhook' => 'https://example.com', + ]; + $this->expectException(ReplicateWebhookInputException::class); + $validator->validate($input); + } + + public function test_validate_with_extra_key(): void + { + $validator = new ReplicateWebhookValidator(); + + $input = [ + 'webhook' => 'https://example.com', + 'webhook_events_filter' => ['start', 'output', 'logs', 'completed'], + 'extra_key' => 'extra_value' + ]; + $this->expectException(ReplicateWebhookInputException::class); + $validator->validate($input); + } + + public function test_validate_with_invalid_webhook_url(): void + { + $validator = new ReplicateWebhookValidator(); + + $input = [ + 'webhook' => 'invalid_url', + 'webhook_events_filter' => ['start', 'output', 'logs', 'completed'] + ]; + $this->expectException(ReplicateWebhookInputException::class); + $validator->validate($input); + } + + public function test_validate_with_invalid_webhook_event_filter(): void + { + $validator = new ReplicateWebhookValidator(); + + $input = [ + 'webhook' => 'https://example.com', + 'webhook_events_filter' => ['start', 'output', 'invalid_filter', 'completed'] + ]; + $this->expectException(ReplicateWebhookInputException::class); + $validator->validate($input); + } +}