diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index c522d5c0..35d27c20 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -26,4 +26,4 @@ jobs: coverage: xdebug - uses: php-actions/composer@v6 - name: Run tests - run: vendor/bin/phpunit --coverage-text + run: vendor/bin/phpunit --coverage-text --testdox diff --git a/composer.json b/composer.json index 2407692a..6d2e3a0d 100644 --- a/composer.json +++ b/composer.json @@ -65,7 +65,7 @@ "Composer\\Config::disableProcessTimeout", "docker-compose up" ], - "test": "vendor/bin/phpunit", + "test": "vendor/bin/phpunit --testdox", "lint": "phpcs -v", "lint:fix": "phpcbf" } diff --git a/phpunit.xml.dist b/phpunit.xml.dist index cc66b457..d534f668 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -10,6 +10,7 @@ failOnWarning="true" failOnNotice="true" failOnRisky="true" + executionOrder="random" > diff --git a/tests/Feature/AliasesTest.php b/tests/Feature/AliasesTest.php new file mode 100644 index 00000000..a1e1a5d8 --- /dev/null +++ b/tests/Feature/AliasesTest.php @@ -0,0 +1,60 @@ + "companies", + "collection_name" => "companies_june11", + ]; + private $upsertResponse = null; + + protected function setUp(): void + { + parent::setUp(); + + $aliasedCollection = [ + 'collection_name' => 'companies_june11' + ]; + $this->upsertResponse = $this->client()->aliases->upsert('companies', $aliasedCollection); + } + + protected function tearDown(): void + { + $aliases = $this->client()->aliases->retrieve(); + foreach ($aliases['aliases'] as $alias) { + $this->client()->aliases[$alias['name']]->delete(); + } + } + + public function testCanUpsertAnAlias(): void + { + $this->assertEquals($this->sampleAliasResponse, $this->upsertResponse); + } + + public function testCanRetrieveAlias(): void + { + $response = $this->client()->aliases['companies']->retrieve(); + $this->assertEquals($this->sampleAliasResponse, $response); + } + + public function testCanDeleteAlias(): void + { + $response = $this->client()->aliases['companies']->delete(); + $this->assertEquals($this->sampleAliasResponse, $response); + + $this->expectException(ObjectNotFound::class); + $this->client()->aliases['companies']->retrieve(); + } + + public function testCanRetrieveAllAliases(): void + { + $response = $this->client()->aliases->retrieve(); + + $this->assertEquals(['aliases' => [0 => $this->sampleAliasResponse]], $response); + } +} diff --git a/tests/Feature/AnalyticsEventsTest.php b/tests/Feature/AnalyticsEventsTest.php new file mode 100644 index 00000000..05384f5c --- /dev/null +++ b/tests/Feature/AnalyticsEventsTest.php @@ -0,0 +1,22 @@ +client()->analytics->rules()->even + // $this->assertEquals($returnData['name'], $this->ruleName); + // } + + public function testNeedImplementationForAnalyticsEvents(): void + { + $this->markTestIncomplete( + 'This test has not been implemented yet.', + ); + } +} diff --git a/tests/Feature/AnalyticsRulesTest.php b/tests/Feature/AnalyticsRulesTest.php new file mode 100644 index 00000000..0d863c90 --- /dev/null +++ b/tests/Feature/AnalyticsRulesTest.php @@ -0,0 +1,57 @@ + "popular_queries", + "params" => [ + "source" => [ + "collections" => ["products"] + ], + "destination" => [ + "collection" => "product_queries" + ], + "expand_query" => false, + "limit" => 1000 + ] + ]; + private $ruleUpsertResponse = null; + + protected function setUp(): void + { + parent::setUp(); + $this->ruleUpsertResponse = $this->client()->analytics->rules()->upsert($this->ruleName, $this->ruleConfiguration); + } + + public function testCanUpsertARule(): void + { + $this->assertEquals($this->ruleName, $this->ruleUpsertResponse['name']); + } + + public function testCanRetrieveARule(): void + { + $returnData = $this->client()->analytics->rules()->{$this->ruleName}->retrieve(); + $this->assertEquals($returnData['name'], $this->ruleName); + } + + public function testCanDeleteARule(): void + { + $returnData = $this->client()->analytics->rules()->{$this->ruleName}->delete(); + $this->assertEquals($returnData['name'], $this->ruleName); + + $this->expectException(ObjectNotFound::class); + $this->client()->analytics->rules()->{$this->ruleName}->retrieve(); + } + + public function testCanRetrieveAllRules(): void + { + $returnData = $this->client()->analytics->rules()->retrieve(); + $this->assertCount(1, $returnData['rules']); + } +} diff --git a/tests/Feature/ClientTest.php b/tests/Feature/ClientTest.php new file mode 100644 index 00000000..42d0760c --- /dev/null +++ b/tests/Feature/ClientTest.php @@ -0,0 +1,34 @@ +assertInstanceOf(Collections::class, $this->client()->collections); + $this->assertInstanceOf(Stopwords::class, $this->client()->stopwords); + $this->assertInstanceOf(Aliases::class, $this->client()->aliases); + $this->assertInstanceOf(Keys::class, $this->client()->keys); + $this->assertInstanceOf(Debug::class, $this->client()->debug); + $this->assertInstanceOf(Metrics::class, $this->client()->metrics); + $this->assertInstanceOf(Health::class, $this->client()->health); + $this->assertInstanceOf(Operations::class, $this->client()->operations); + $this->assertInstanceOf(MultiSearch::class, $this->client()->multiSearch); + $this->assertInstanceOf(Presets::class, $this->client()->presets); + $this->assertInstanceOf(Analytics::class, $this->client()->analytics); + } +} diff --git a/tests/Feature/CollectionTest.php b/tests/Feature/CollectionTest.php deleted file mode 100644 index 2850d8e0..00000000 --- a/tests/Feature/CollectionTest.php +++ /dev/null @@ -1,40 +0,0 @@ -getSchema('books'); - - $response = $this->client()->collections->create($schema); - - $this->assertEquals('books', $response['name']); - } - - public function testCanRetrieveCollection(): void - { - $schema = $this->getSchema('books'); - $this->client()->collections->create($schema); - - $response = $this->client()->collections['books']->retrieve(); - - $this->assertEquals('books', $response['name']); - } - - public function testCanDeleteCollection(): void - { - $schema = $this->getSchema('books'); - $this->client()->collections->create($schema); - - $this->client()->collections['books']->delete(); - - $this->expectException(ObjectNotFound::class); - - $this->client()->collections['books']->retrieve(); - } -} diff --git a/tests/Feature/CollectionsTest.php b/tests/Feature/CollectionsTest.php new file mode 100644 index 00000000..0fe4fdb0 --- /dev/null +++ b/tests/Feature/CollectionsTest.php @@ -0,0 +1,63 @@ +getSchema('books'); + $this->createCollectionRes = $this->client()->collections->create($schema); + } + + public function testCanCreateACollection(): void + { + $this->assertEquals('books', $this->createCollectionRes['name']); + } + + public function testCanRetrieveACollection(): void + { + $response = $this->client()->collections['books']->retrieve(); + $this->assertEquals('books', $response['name']); + } + + public function testCanUpdateACollection(): void + { + $update_schema = [ + 'fields' => [ + [ + 'name' => 'isbn', + 'drop' => true + ] + ] + ]; + $response = $this->client()->collections['books']->update($update_schema); + $this->assertEquals('isbn', $response['fields'][0]['name']); + $this->assertArrayHasKey('drop', $response['fields'][0]); + + $response = $this->client()->collections['books']->retrieve(); + $this->assertEquals(5, count($response['fields'])); + } + + public function testCanDeleteACollection(): void + { + $this->client()->collections['books']->delete(); + + $this->expectException(ObjectNotFound::class); + $this->client()->collections['books']->retrieve(); + } + + public function testCanRetrieveAllCollections(): void + { + $response = $this->client()->collections->retrieve(); + $this->assertCount(1, $response); + } +} diff --git a/tests/Feature/DebugTest.php b/tests/Feature/DebugTest.php new file mode 100644 index 00000000..5546ba18 --- /dev/null +++ b/tests/Feature/DebugTest.php @@ -0,0 +1,14 @@ +client()->debug->retrieve(); + $this->assertArrayHasKey('version', $returnData); + } +} diff --git a/tests/Feature/DocumentTest.php b/tests/Feature/DocumentTest.php new file mode 100644 index 00000000..1d55151c --- /dev/null +++ b/tests/Feature/DocumentTest.php @@ -0,0 +1,59 @@ +setUpCollection('books'); + $this->setUpDocuments('books'); + + $this->testDocument = $this->client()->collections['books']->documents[$this->documentId]; + } + + public function testCanRetrieveADocumentById(): void + { + $response = $this->testDocument->retrieve(); + $this->assertEquals($this->documentId, $response['id']); + } + + public function testCanUpdateADocumentById(): void + { + $partialDocument = [ + "title" => "hello there :D", + ]; + $response = $this->testDocument->update($partialDocument); + $this->assertEquals("hello there :D", $response['title']); + } + + public function testCanUpdateADocumentWithDirtyValuesById(): void + { + $partialDocument = [ + "title" => 1, + ]; + $response = $this->testDocument->update( + $partialDocument, + [ + "dirty_values" => "coerce_or_reject", + ] + ); + $this->assertIsString($response['title']); + } + + public function testCanDeleteADocumentById(): void + { + $response = $this->testDocument->delete(); + $this->assertEquals($this->documentId, $response['id']); + + $this->expectException(ObjectNotFound::class); + $this->testDocument->retrieve(); + } +} diff --git a/tests/Feature/DocumentsTest.php b/tests/Feature/DocumentsTest.php index eb78ba7f..e38d8a1a 100644 --- a/tests/Feature/DocumentsTest.php +++ b/tests/Feature/DocumentsTest.php @@ -3,25 +3,102 @@ namespace Feature; use Tests\TestCase; +use Typesense\Documents; class DocumentsTest extends TestCase { + private ?Documents $testDocuments = null; + private $document = [ + "id" => "4", + "title" => "Book 4", + "description" => "Test...", + "authors" => ["Hayden"], + "isbn" => "1", + "publisher" => "AwesomeBooks", + "pages" => 9 + ]; + protected function setUp(): void { parent::setUp(); - $this->setUpCollection('books'); $this->setUpDocuments('books'); + + $this->testDocuments = $this->client()->collections['books']->documents; + } + + public function testCanSearchForDocumentsInACollection(): void + { + $response = $this->testDocuments->search([ + 'q' => 'book', + 'query_by' => 'title', + ]); + $this->assertEquals('book', $response['request_params']['q']); + $this->assertGreaterThan(0, $response['hits']); + } + + public function testCanCreateADocument(): void + { + $response = $this->testDocuments->create($this->document); + $this->assertEquals($this->document, $response); + } + + public function testCanUpsertADocument(): void + { + $documentWithDifferentId = [...$this->document, 'id' => '1']; // id 1 already exists in the collection + + $response = $this->testDocuments->upsert($documentWithDifferentId); + $this->assertEquals($documentWithDifferentId, $response); + } + + public function testCanImportJsonlDocumentsWithQueryParameter(): void + { + $documentWithDifferentId = [...$this->document, 'id' => '1']; + $jsonlDocuments = join(PHP_EOL, array_map( + fn ($item) => json_encode($item), + [$this->document, $documentWithDifferentId] + )); + + $response = $this->testDocuments->import($jsonlDocuments, [ + 'action' => 'upsert' + ]); + $this->assertEquals("{\"success\":true}\n{\"success\":true}", $response); } - public function testCanUpdateDocumentsByFilter(): void + public function testCanImportArrayOfDocuments(): void + { + $documentWithDifferentId = [...$this->document, 'id' => '5']; + + $response = $this->testDocuments->import([$this->document, $documentWithDifferentId]); + $this->assertEquals(1, $response[0]['success']); + $this->assertEquals(1, $response[1]['success']); + } + + public function testCanExportDocumentsWithControlParameter(): void + { + $this->testDocuments->create($this->document); + + $response = $this->testDocuments->export([ + "filter_by" => "id:=4" + ]); + $this->assertEquals($this->document, json_decode($response, true)); + } + + public function testCanUpdateDocumentsByQuery(): void { $document = ['publisher' => 'Renamed Publisher']; - $response = $this->client()->collections['books']->documents->update($document, [ + $response = $this->testDocuments->update($document, [ 'filter_by' => 'publisher:=AwesomeBooks' ]); - $this->assertGreaterThan(0, $response['num_updated']); } + + public function testCanDeleteDocumentsByQuery(): void + { + $response = $this->testDocuments->delete([ + 'filter_by' => 'publisher:=AwesomeBooks' + ]); + $this->assertGreaterThan(0, $response['num_deleted']); + } } diff --git a/tests/Feature/HealthTest.php b/tests/Feature/HealthTest.php new file mode 100644 index 00000000..4b979868 --- /dev/null +++ b/tests/Feature/HealthTest.php @@ -0,0 +1,14 @@ +client()->health->retrieve(); + $this->assertIsBool($response['ok']); + } +} diff --git a/tests/Feature/KeysTest.php b/tests/Feature/KeysTest.php new file mode 100644 index 00000000..658d0393 --- /dev/null +++ b/tests/Feature/KeysTest.php @@ -0,0 +1,76 @@ +client()->keys->create([ + 'description' => 'Admin key.', + 'actions' => ['*'], + 'collections' => ['*'] + ]); + $this->createKeyResponse = $res; + $this->keyId = $res['id']; + } + + protected function tearDown(): void + { + $keys = $this->client()->keys->retrieve(); + foreach ($keys['keys'] as $key) { + $this->client()->keys[$key['id']]->delete(); + } + } + + public function testCanCreateAKey(): void + { + $this->assertEquals($this->createKeyResponse['description'], 'Admin key.'); + } + + public function testCanRetrieveAKey(): void + { + $key = $this->client()->keys[$this->keyId]->retrieve(); + + $this->assertEquals($key['id'], $this->keyId); + } + + public function testCanDeleteAKey(): void + { + $key = $this->client()->keys[$this->keyId]->delete(); + $this->assertEquals($key['id'], $this->keyId); + + $this->expectException(ObjectNotFound::class); + $key = $this->client()->keys[$this->keyId]->retrieve(); + } + + public function testCanRetrieveAllKeys(): void + { + $keys = $this->client()->keys->retrieve(); + $this->assertCount(1, $keys['keys']); + } + + public function testCanGenerateScopedSearchKey(): void + { + // The following keys were generated and verified to work with an actual Typesense server + // We're only verifying that the algorithm works as expected client-side + $searchKey = "RN23GFr1s6jQ9kgSNg2O7fYcAUXU7127"; + $scopedSearchKey = + "SC9sT0hncHFwTHNFc3U3d3psRDZBUGNXQUViQUdDNmRHSmJFQnNnczJ4VT1STjIzeyJmaWx0ZXJfYnkiOiJjb21wYW55X2lkOjEyNCJ9"; + + $result = $this->client()->keys->generateScopedSearchKey($searchKey, [ + "filter_by" => "company_id:124", + ]); + $this->assertEquals($scopedSearchKey, $result); + } +} diff --git a/tests/Feature/MetricsTest.php b/tests/Feature/MetricsTest.php new file mode 100644 index 00000000..c4ca3d27 --- /dev/null +++ b/tests/Feature/MetricsTest.php @@ -0,0 +1,14 @@ +client()->metrics->retrieve(); + $this->assertArrayHasKey('system_memory_used_bytes', $returnData); + } +} diff --git a/tests/Feature/MultiSearchTest.php b/tests/Feature/MultiSearchTest.php new file mode 100644 index 00000000..4611d5bd --- /dev/null +++ b/tests/Feature/MultiSearchTest.php @@ -0,0 +1,56 @@ + [ + [ + 'q' => 'book 1', + ], + [ + 'q' => 'book 2' + ] + ] + ]; + private $commonSearchParams = [ + 'query_by' => 'title', + 'collection' => 'books', + ]; + + protected function setUp(): void + { + parent::setUp(); + + $this->setUpCollection('books'); + $this->setUpDocuments('books'); + } + + public function testCanPerformAMultiSearch(): void + { + $returnData = $this->client()->multiSearch->perform($this->searchRequests, $this->commonSearchParams); + $this->assertCount(2, $returnData['results']); + } + + public function testCanLimitNumberOfRequestsInOneMultiSearch(): void + { + $searchRequests = [ + 'searches' => [ + ...$this->searchRequests['searches'], + [ + 'q' => 'book 3' + ] + ] + ]; + + $this->expectException(RequestMalformed::class); + $this->client()->multiSearch->perform($searchRequests, [ + "limit_multi_searches" => 2, + ...$this->commonSearchParams + ]); + } +} diff --git a/tests/Feature/OperationsTest.php b/tests/Feature/OperationsTest.php new file mode 100644 index 00000000..0af53256 --- /dev/null +++ b/tests/Feature/OperationsTest.php @@ -0,0 +1,21 @@ +client()->operations->perform("snapshot", ["snapshot_path" => "/tmp/typesense-data-snapshot"]); + $this->assertIsBool($returnData['success']); + } + + public function testCanReElectLeader(): void + { + $returnData = $this->client()->operations->perform("vote"); + $this->assertIsBool($returnData['success']); + } +} diff --git a/tests/Feature/OverridesTest.php b/tests/Feature/OverridesTest.php new file mode 100644 index 00000000..1ac19b33 --- /dev/null +++ b/tests/Feature/OverridesTest.php @@ -0,0 +1,62 @@ +setUpCollection('books'); + + $override = [ + "rule" => [ + "query" => "book", + "match" => "exact" + ], + "includes" => [ + ["id" => "422", "position" => 1], + ], + "excludes" => [ + ["id" => "287"] + ] + ]; + + $returnData = $this->client()->collections['books']->overrides->upsert($this->overrideId, $override); + $this->overrideUpsertRes = $returnData; + } + + public function testCanCreateAnOverride(): void + { + $this->assertEquals($this->overrideId, $this->overrideUpsertRes['id']); + } + + public function testCanRetrieveAnOverride(): void + { + $returnData = $this->client()->collections['books']->overrides[$this->overrideId]->retrieve(); + $this->assertEquals($this->overrideId, $returnData['id']); + } + + public function testCanDeleteAnOverride(): void + { + $returnData = $this->client()->collections['books']->overrides[$this->overrideId]->delete(); + $this->assertEquals($this->overrideId, $returnData['id']); + + $this->expectException(ObjectNotFound::class); + $this->client()->collections['books']->overrides[$this->overrideId]->retrieve(); + } + + public function testCanRetrieveAllOverrides(): void + { + $returnData = $this->client()->collections['books']->overrides->retrieve(); + $this->assertCount(1, $returnData['overrides']); + } +} diff --git a/tests/Feature/PresetsTest.php b/tests/Feature/PresetsTest.php new file mode 100644 index 00000000..982bbdd6 --- /dev/null +++ b/tests/Feature/PresetsTest.php @@ -0,0 +1,62 @@ +client()->presets->put([ + 'preset_name' => $this->presetName, + 'preset_data' => [ + 'value' => [ + 'query_by' => "*", + ], + ] + ]); + $this->presetUpsertRes = $returnData; + } + + protected function tearDown(): void + { + $presets = $this->client()->presets->get(); + foreach ($presets['presets'] as $preset) { + $this->client()->presets->delete($preset['name']); + } + } + + public function testCanUpsertAPreset(): void + { + $this->assertEquals($this->presetName, $this->presetUpsertRes['name']); + } + + //* Currently there isn't a method for retrieving a preset by name + // public function testCanRetrieveAPreset(): void + // { + // $returnData = $this->client()->presets->get($this->presetName); + // $this->assertEquals($this->presetName, $returnData['name']); + // } + + public function testCanDeleteAPreset(): void + { + $returnData = $this->client()->presets->delete($this->presetName); + $this->assertEquals($this->presetName, $returnData['name']); + + $returnPresets = $this->client()->presets->get(); + $this->assertCount(0, $returnPresets['presets']); + } + + public function testCanRetrieveAllPresets(): void + { + $returnData = $this->client()->presets->get(); + $this->assertCount(1, $returnData['presets']); + } +} diff --git a/tests/Feature/StopwordsTest.php b/tests/Feature/StopwordsTest.php new file mode 100644 index 00000000..508512b2 --- /dev/null +++ b/tests/Feature/StopwordsTest.php @@ -0,0 +1,59 @@ +stopwordsUpsertRes = $this->client()->stopwords->put( + [ + "stopwords_name" => "stopword_set1", + "stopwords_data" => ["Germany"], + ] + ); + } + + protected function tearDown(): void + { + $stopwords = $this->client()->stopwords->getAll(); + foreach ($stopwords['stopwords'] as $stopword) { + $this->client()->stopwords->delete($stopword['id']); + } + } + + public function testCanUpsertAStopword(): void + { + $this->assertEquals("stopword_set1", $this->stopwordsUpsertRes['id']); + $this->assertEquals(["Germany"], $this->stopwordsUpsertRes['stopwords']); + } + + public function testCanRetrieveAStopword(): void + { + $returnData = $this->client()->stopwords->get("stopword_set1"); + $this->assertEquals("stopword_set1", $returnData['stopwords']['id']); + } + + public function testCanDeleteAStopword(): void + { + $returnData = $this->client()->stopwords->delete("stopword_set1"); + $this->assertEquals("stopword_set1", $returnData['id']); + + $this->expectException(ObjectNotFound::class); + $this->client()->stopwords->get("stopword_set1"); + } + + public function testCanRetrieveAllStopwords(): void + { + $returnData = $this->client()->stopwords->getAll(); + $this->assertCount(1, $returnData['stopwords']); + } +} diff --git a/tests/Feature/SynonymsTest.php b/tests/Feature/SynonymsTest.php new file mode 100644 index 00000000..6cd5db05 --- /dev/null +++ b/tests/Feature/SynonymsTest.php @@ -0,0 +1,52 @@ + ["blazer", "coat", "jacket"] + ]; + + protected function setUp(): void + { + parent::setUp(); + $this->setUpCollection('books'); + + $this->synonyms = $this->client()->collections['books']->synonyms; + $this->upsertResponse = $this->synonyms->upsert('coat-synonyms', $this->synonymData); + } + + public function testCanUpsertASynonym(): void + { + $this->assertEquals('coat-synonyms', $this->upsertResponse['id']); + $this->assertEquals($this->synonymData['synonyms'], $this->upsertResponse['synonyms']); + } + + public function testCanRetrieveASynonymById(): void + { + $returnData = $this->synonyms['coat-synonyms']->retrieve(); + $this->assertEquals('coat-synonyms', $returnData['id']); + } + + public function testCanDeleteASynonymById(): void + { + $returnData = $this->synonyms['coat-synonyms']->delete(); + $this->assertEquals('coat-synonyms', $returnData['id']); + + $this->expectException(ObjectNotFound::class); + $this->synonyms['coat-synonyms']->retrieve(); + } + + public function testCanRetrieveAllSynonyms(): void + { + $returnData = $this->synonyms->retrieve(); + $this->assertCount(1, $returnData['synonyms']); + } +} diff --git a/tests/TestCase.php b/tests/TestCase.php index 61512692..f2ddd007 100644 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -40,7 +40,7 @@ protected function getData(string $name): array private function loadFromDataDir(string $path): array { - if (! file_exists($path)) { + if (!file_exists($path)) { return []; } diff --git a/tests/data/books.data.json b/tests/data/books.data.json index 1ec5615d..02570e3d 100644 --- a/tests/data/books.data.json +++ b/tests/data/books.data.json @@ -1,32 +1,27 @@ [ { + "id": "1", "title": "Book 1", "description": "Wonderful Description", - "authors": [ - "Jane", - "John" - ], + "authors": ["Jane", "John"], "isbn": "1234567890", "publisher": "AwesomeBooks", "pages": 123 }, { + "id": "2", "title": "Book 2", "description": "Another helpful description", - "authors": [ - "Jeff", - "Mia" - ], + "authors": ["Jeff", "Mia"], "isbn": "1234567891", "publisher": "AwesomeBooks", "pages": 150 }, { + "id": "3", "title": "Book 3", "description": "Another useless description", - "authors": [ - "Martin" - ], + "authors": ["Martin"], "isbn": "1234567892", "publisher": "PubLishEr", "pages": 268