From faa4e13a2934fa0116cefe6de9fd5534f930de67 Mon Sep 17 00:00:00 2001 From: Hayden Date: Tue, 18 Jun 2024 13:38:32 +0700 Subject: [PATCH 01/30] test: alias --- tests/Feature/AliasTest.php | 39 +++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 tests/Feature/AliasTest.php diff --git a/tests/Feature/AliasTest.php b/tests/Feature/AliasTest.php new file mode 100644 index 00000000..884f35ae --- /dev/null +++ b/tests/Feature/AliasTest.php @@ -0,0 +1,39 @@ + "companies", + "collection_name" => "companies_june11", + ]; + + protected function setUp(): void + { + parent::setUp(); + + $aliasedCollection = [ + 'collection_name' => 'companies_june11' + ]; + $this->client()->aliases->upsert('companies', $aliasedCollection); + } + + 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(); + } +} From f0cc7f408416a8cefc4363b601bb0b7d2984a5b4 Mon Sep 17 00:00:00 2001 From: Hayden Date: Tue, 18 Jun 2024 13:48:38 +0700 Subject: [PATCH 02/30] ci: phpunit verbose --- .github/workflows/tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 From db8e89af62242855cc11c3c4556b89f99235e358 Mon Sep 17 00:00:00 2001 From: Hayden Date: Tue, 18 Jun 2024 14:01:49 +0700 Subject: [PATCH 03/30] test: aliases --- tests/Feature/AliasTest.php | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/tests/Feature/AliasTest.php b/tests/Feature/AliasTest.php index 884f35ae..5651fe83 100644 --- a/tests/Feature/AliasTest.php +++ b/tests/Feature/AliasTest.php @@ -36,4 +36,11 @@ public function testCanDeleteAlias(): void $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); + } } From 9a865aa795fd40c1558a1848504fb4e716b949ff Mon Sep 17 00:00:00 2001 From: Hayden Date: Tue, 18 Jun 2024 15:06:53 +0700 Subject: [PATCH 04/30] test: health --- tests/Feature/HealthTest.php | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 tests/Feature/HealthTest.php 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']); + } +} From 8c0779051e8aca64bcae1eb0fbb071c56609e3a6 Mon Sep 17 00:00:00 2001 From: Hayden Date: Tue, 18 Jun 2024 15:58:38 +0700 Subject: [PATCH 05/30] test: key --- tests/Feature/KeyTest.php | 41 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 tests/Feature/KeyTest.php diff --git a/tests/Feature/KeyTest.php b/tests/Feature/KeyTest.php new file mode 100644 index 00000000..5efbc632 --- /dev/null +++ b/tests/Feature/KeyTest.php @@ -0,0 +1,41 @@ +client()->keys->create([ + 'description' => 'Admin key.', + 'actions' => ['*'], + 'collections' => ['*'] + ]);; + $this->keyId = $response['id']; + $this->assertEquals($response['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(); + } +} From c7e42dfd46aa62a20588306046090c4a3770af19 Mon Sep 17 00:00:00 2001 From: Hayden Date: Wed, 19 Jun 2024 11:33:29 +0700 Subject: [PATCH 06/30] test: alias upsert --- tests/Feature/AliasTest.php | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/tests/Feature/AliasTest.php b/tests/Feature/AliasTest.php index 5651fe83..71465b77 100644 --- a/tests/Feature/AliasTest.php +++ b/tests/Feature/AliasTest.php @@ -11,6 +11,7 @@ class AliasTest extends TestCase "name" => "companies", "collection_name" => "companies_june11", ]; + private $upsertResponse = null; protected function setUp(): void { @@ -19,7 +20,12 @@ protected function setUp(): void $aliasedCollection = [ 'collection_name' => 'companies_june11' ]; - $this->client()->aliases->upsert('companies', $aliasedCollection); + $this->upsertResponse = $this->client()->aliases->upsert('companies', $aliasedCollection); + } + + public function testCanUpsertAnAlias(): void + { + $this->assertEquals($this->sampleAliasResponse, $this->upsertResponse); } public function testCanRetrieveAlias(): void From f2d043e1b455fbb1965e89245118e5d9d838711c Mon Sep 17 00:00:00 2001 From: Hayden Date: Wed, 19 Jun 2024 11:44:16 +0700 Subject: [PATCH 07/30] test: key create --- tests/Feature/KeyTest.php | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/tests/Feature/KeyTest.php b/tests/Feature/KeyTest.php index 5efbc632..036df34f 100644 --- a/tests/Feature/KeyTest.php +++ b/tests/Feature/KeyTest.php @@ -9,18 +9,25 @@ class KeyTest extends TestCase { private $keyId = null; + private $createKeyResponse = null; + protected function setUp(): void { parent::setUp(); - $response = $this->client()->keys->create([ + $res = $this->client()->keys->create([ 'description' => 'Admin key.', 'actions' => ['*'], 'collections' => ['*'] - ]);; - $this->keyId = $response['id']; - $this->assertEquals($response['description'], 'Admin key.'); + ]); + $this->createKeyResponse = $res; + $this->keyId = $res['id']; + } + + public function testCanCreateAKey(): void + { + $this->assertEquals($this->createKeyResponse['description'], 'Admin key.'); } public function testCanRetrieveAKey(): void From 4baaa8c2e729f751b8ba79278d5965a3ecc2b19b Mon Sep 17 00:00:00 2001 From: Hayden Date: Wed, 19 Jun 2024 11:54:37 +0700 Subject: [PATCH 08/30] test: alias tear down --- tests/TestCase.php | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/tests/TestCase.php b/tests/TestCase.php index 61512692..a2ebab50 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 []; } @@ -84,5 +84,10 @@ private function tearDownTypesense(): void foreach ($collections as $collection) { $this->typesenseClient->collections[$collection['name']]->delete(); } + + $aliases = $this->typesenseClient->aliases->retrieve(); + foreach ($aliases['aliases'] as $alias) { + $this->typesenseClient->aliases[$alias['name']]->delete(); + } } } From 478c6a0f05d517c8f31d823a1a37db4a3b3e03d2 Mon Sep 17 00:00:00 2001 From: Hayden Date: Wed, 19 Jun 2024 17:34:05 +0700 Subject: [PATCH 09/30] test: multi search --- tests/Feature/MultiSearchTest.php | 56 +++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) create mode 100644 tests/Feature/MultiSearchTest.php diff --git a/tests/Feature/MultiSearchTest.php b/tests/Feature/MultiSearchTest.php new file mode 100644 index 00000000..880d3fa8 --- /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->assertEquals(2, count($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 + ]); + } +} From 1e98b56f366e52cc5225236bb1113fe0eb92802f Mon Sep 17 00:00:00 2001 From: Hayden Date: Wed, 19 Jun 2024 17:56:17 +0700 Subject: [PATCH 10/30] test: operations --- tests/Feature/OperationsTest.php | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 tests/Feature/OperationsTest.php 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']); + } +} From e4a4a10de6043a98711ee1a7d8d86f62ec4b1595 Mon Sep 17 00:00:00 2001 From: Hayden Date: Wed, 19 Jun 2024 18:43:39 +0700 Subject: [PATCH 11/30] test: AnalyticsRule & AnalyticRules --- tests/Feature/AnalyticsRuleTest.php | 57 +++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) create mode 100644 tests/Feature/AnalyticsRuleTest.php diff --git a/tests/Feature/AnalyticsRuleTest.php b/tests/Feature/AnalyticsRuleTest.php new file mode 100644 index 00000000..3f56d864 --- /dev/null +++ b/tests/Feature/AnalyticsRuleTest.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->assertEquals(count($returnData['rules']), 1); + } +} From 7a31611a646bca9e26aee1b0e636bed29465e51e Mon Sep 17 00:00:00 2001 From: Hayden Date: Wed, 19 Jun 2024 18:44:03 +0700 Subject: [PATCH 12/30] chore: composer run test verbose --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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" } From 5aa3b0653de5aed45131eac333e2b9b2ee13a63c Mon Sep 17 00:00:00 2001 From: Hayden Date: Wed, 19 Jun 2024 19:05:27 +0700 Subject: [PATCH 13/30] test: metrics --- tests/Feature/MetricsTest.php | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 tests/Feature/MetricsTest.php 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); + } +} From e4037628f92922e22c68cb07c8814fd76555f744 Mon Sep 17 00:00:00 2001 From: Hayden Date: Wed, 19 Jun 2024 19:18:07 +0700 Subject: [PATCH 14/30] test: debug --- tests/Feature/DebugTest.php | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 tests/Feature/DebugTest.php 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); + } +} From 999ee501b75c527798ee1fca5227d2324c447ab2 Mon Sep 17 00:00:00 2001 From: Hayden Date: Wed, 19 Jun 2024 21:08:32 +0700 Subject: [PATCH 15/30] refactor: alias test teardown --- tests/Feature/AliasTest.php | 8 ++++++++ tests/TestCase.php | 5 ----- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/tests/Feature/AliasTest.php b/tests/Feature/AliasTest.php index 71465b77..d9b33770 100644 --- a/tests/Feature/AliasTest.php +++ b/tests/Feature/AliasTest.php @@ -23,6 +23,14 @@ protected function setUp(): void $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); diff --git a/tests/TestCase.php b/tests/TestCase.php index a2ebab50..f2ddd007 100644 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -84,10 +84,5 @@ private function tearDownTypesense(): void foreach ($collections as $collection) { $this->typesenseClient->collections[$collection['name']]->delete(); } - - $aliases = $this->typesenseClient->aliases->retrieve(); - foreach ($aliases['aliases'] as $alias) { - $this->typesenseClient->aliases[$alias['name']]->delete(); - } } } From 10be755f2f26729326641f329aacd0549b005ed9 Mon Sep 17 00:00:00 2001 From: Hayden Date: Wed, 19 Jun 2024 21:10:46 +0700 Subject: [PATCH 16/30] test: override & overrides --- tests/Feature/OverrideTest.php | 62 ++++++++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) create mode 100644 tests/Feature/OverrideTest.php diff --git a/tests/Feature/OverrideTest.php b/tests/Feature/OverrideTest.php new file mode 100644 index 00000000..ec6cc598 --- /dev/null +++ b/tests/Feature/OverrideTest.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->assertEquals(1, count($returnData['overrides'])); + } +} From 29fc0efcc491342e728a7c4d1d90ada23cb3afc2 Mon Sep 17 00:00:00 2001 From: Hayden Date: Wed, 19 Jun 2024 21:56:41 +0700 Subject: [PATCH 17/30] test: presets --- tests/Feature/PresetsTest.php | 62 +++++++++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) create mode 100644 tests/Feature/PresetsTest.php diff --git a/tests/Feature/PresetsTest.php b/tests/Feature/PresetsTest.php new file mode 100644 index 00000000..bbd2c6fa --- /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->assertEquals(0, count($returnPresets['presets'])); + } + + public function testCanRetrieveAllPresets(): void + { + $returnData = $this->client()->presets->get(); + $this->assertEquals(1, count($returnData['presets'])); + } +} From f43e14c4da786fad521ece3807ab947193ffb80a Mon Sep 17 00:00:00 2001 From: Hayden Date: Thu, 20 Jun 2024 08:25:36 +0700 Subject: [PATCH 18/30] test: analytics events --- tests/Feature/AnalyticsEventsTest.php | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 tests/Feature/AnalyticsEventsTest.php diff --git a/tests/Feature/AnalyticsEventsTest.php b/tests/Feature/AnalyticsEventsTest.php new file mode 100644 index 00000000..d0a188c9 --- /dev/null +++ b/tests/Feature/AnalyticsEventsTest.php @@ -0,0 +1,20 @@ +client()->analytics->rules()->even + // $this->assertEquals($returnData['name'], $this->ruleName); + // } + + public function testNeedImplementationForAnalyticsEvents(): void + { + $this->assertTrue(true); + } +} From af960c6986627e472c64679701033d586a8306cf Mon Sep 17 00:00:00 2001 From: Hayden Date: Thu, 20 Jun 2024 08:44:48 +0700 Subject: [PATCH 19/30] test: client --- tests/Feature/ClientTest.php | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 tests/Feature/ClientTest.php 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); + } +} From ac03da08cf6bf42af944cffa47b7d1290c461bda Mon Sep 17 00:00:00 2001 From: Hayden Date: Thu, 20 Jun 2024 09:44:07 +0700 Subject: [PATCH 20/30] test: collection & collections --- tests/Feature/CollectionTest.php | 47 ++++++++++++++++++++++++-------- 1 file changed, 35 insertions(+), 12 deletions(-) diff --git a/tests/Feature/CollectionTest.php b/tests/Feature/CollectionTest.php index 2850d8e0..8f5c6811 100644 --- a/tests/Feature/CollectionTest.php +++ b/tests/Feature/CollectionTest.php @@ -7,34 +7,57 @@ class CollectionTest extends TestCase { - public function testCanCreateCollection(): void + private $createCollectionRes = null; + + + protected function setUp(): void { + parent::setUp(); + $schema = $this->getSchema('books'); + $this->createCollectionRes = $this->client()->collections->create($schema); + } - $response = $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 testCanRetrieveCollection(): void + public function testCanUpdateACollection(): void { - $schema = $this->getSchema('books'); - $this->client()->collections->create($schema); + $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('books', $response['name']); + $this->assertEquals(5, count($response['fields'])); } - public function testCanDeleteCollection(): void + public function testCanDeleteACollection(): void { - $schema = $this->getSchema('books'); - $this->client()->collections->create($schema); - $this->client()->collections['books']->delete(); $this->expectException(ObjectNotFound::class); - $this->client()->collections['books']->retrieve(); } + + public function testCanRetrieveAllCollections(): void + { + $response = $this->client()->collections->retrieve(); + $this->assertEquals(1, count($response)); + } } From a4a2412d11dca9ac6ed7344de730a6a7ef78e0bb Mon Sep 17 00:00:00 2001 From: Hayden Date: Thu, 20 Jun 2024 11:00:30 +0700 Subject: [PATCH 21/30] test: document --- tests/Feature/DocumentTest.php | 59 ++++++++++++++++++++++++++++++++++ tests/data/books.data.json | 17 ++++------ 2 files changed, 65 insertions(+), 11 deletions(-) create mode 100644 tests/Feature/DocumentTest.php 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/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 From 99a08a1308dea858f42068bd069994d2b24780bd Mon Sep 17 00:00:00 2001 From: Hayden Date: Thu, 20 Jun 2024 15:46:21 +0700 Subject: [PATCH 22/30] test: stopwords --- tests/Feature/OverrideTest.php | 2 +- tests/Feature/StopwordsTest.php | 59 +++++++++++++++++++++++++++++++++ 2 files changed, 60 insertions(+), 1 deletion(-) create mode 100644 tests/Feature/StopwordsTest.php diff --git a/tests/Feature/OverrideTest.php b/tests/Feature/OverrideTest.php index ec6cc598..b881452a 100644 --- a/tests/Feature/OverrideTest.php +++ b/tests/Feature/OverrideTest.php @@ -56,7 +56,7 @@ public function testCanDeleteAnOverride(): void public function testCanRetrieveAllOverrides(): void { - $returnData = $this->client()->collections['books']->overrides->retrieve();; + $returnData = $this->client()->collections['books']->overrides->retrieve(); $this->assertEquals(1, count($returnData['overrides'])); } } diff --git a/tests/Feature/StopwordsTest.php b/tests/Feature/StopwordsTest.php new file mode 100644 index 00000000..909fba1d --- /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->assertEquals(1, count($returnData['stopwords'])); + } +} From b8ba482955d76296e880f2511083cdce3ded1d6f Mon Sep 17 00:00:00 2001 From: Hayden Date: Thu, 20 Jun 2024 16:22:04 +0700 Subject: [PATCH 23/30] test: synonym & synonyms --- tests/Feature/SynonymsTest.php | 52 ++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) create mode 100644 tests/Feature/SynonymsTest.php diff --git a/tests/Feature/SynonymsTest.php b/tests/Feature/SynonymsTest.php new file mode 100644 index 00000000..be40f637 --- /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->assertEquals(1, count($returnData['synonyms'])); + } +} From 7d0814535315df1e8535e657c9e101158abb4893 Mon Sep 17 00:00:00 2001 From: Hayden Date: Thu, 20 Jun 2024 16:42:33 +0700 Subject: [PATCH 24/30] test: retrieve all keys & generateScopedSearchKey --- tests/Feature/KeyTest.php | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/tests/Feature/KeyTest.php b/tests/Feature/KeyTest.php index 036df34f..513d30da 100644 --- a/tests/Feature/KeyTest.php +++ b/tests/Feature/KeyTest.php @@ -25,6 +25,14 @@ protected function setUp(): void $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.'); @@ -45,4 +53,24 @@ public function testCanDeleteAKey(): void $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); + } } From 34702cc181ccc78b58548a48b24a1034bf45ff5b Mon Sep 17 00:00:00 2001 From: Hayden Date: Thu, 20 Jun 2024 16:46:20 +0700 Subject: [PATCH 25/30] rename file to plural noun --- tests/Feature/{AliasTest.php => AliasesTest.php} | 0 tests/Feature/{AnalyticsRuleTest.php => AnalyticsRulesTest.php} | 0 tests/Feature/{CollectionTest.php => CollectionsTest.php} | 0 tests/Feature/{KeyTest.php => KeysTest.php} | 0 tests/Feature/{OverrideTest.php => OverridesTest.php} | 0 5 files changed, 0 insertions(+), 0 deletions(-) rename tests/Feature/{AliasTest.php => AliasesTest.php} (100%) rename tests/Feature/{AnalyticsRuleTest.php => AnalyticsRulesTest.php} (100%) rename tests/Feature/{CollectionTest.php => CollectionsTest.php} (100%) rename tests/Feature/{KeyTest.php => KeysTest.php} (100%) rename tests/Feature/{OverrideTest.php => OverridesTest.php} (100%) diff --git a/tests/Feature/AliasTest.php b/tests/Feature/AliasesTest.php similarity index 100% rename from tests/Feature/AliasTest.php rename to tests/Feature/AliasesTest.php diff --git a/tests/Feature/AnalyticsRuleTest.php b/tests/Feature/AnalyticsRulesTest.php similarity index 100% rename from tests/Feature/AnalyticsRuleTest.php rename to tests/Feature/AnalyticsRulesTest.php diff --git a/tests/Feature/CollectionTest.php b/tests/Feature/CollectionsTest.php similarity index 100% rename from tests/Feature/CollectionTest.php rename to tests/Feature/CollectionsTest.php diff --git a/tests/Feature/KeyTest.php b/tests/Feature/KeysTest.php similarity index 100% rename from tests/Feature/KeyTest.php rename to tests/Feature/KeysTest.php diff --git a/tests/Feature/OverrideTest.php b/tests/Feature/OverridesTest.php similarity index 100% rename from tests/Feature/OverrideTest.php rename to tests/Feature/OverridesTest.php From 0c0632aa0547a6ff218457902751f424743e3665 Mon Sep 17 00:00:00 2001 From: Hayden Date: Thu, 20 Jun 2024 16:52:25 +0700 Subject: [PATCH 26/30] fix: test failed class cannot be found --- tests/Feature/AliasesTest.php | 2 +- tests/Feature/AnalyticsRulesTest.php | 2 +- tests/Feature/CollectionsTest.php | 2 +- tests/Feature/KeysTest.php | 2 +- tests/Feature/OverridesTest.php | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/Feature/AliasesTest.php b/tests/Feature/AliasesTest.php index d9b33770..a1e1a5d8 100644 --- a/tests/Feature/AliasesTest.php +++ b/tests/Feature/AliasesTest.php @@ -5,7 +5,7 @@ use Tests\TestCase; use Typesense\Exceptions\ObjectNotFound; -class AliasTest extends TestCase +class AliasesTest extends TestCase { private $sampleAliasResponse = [ "name" => "companies", diff --git a/tests/Feature/AnalyticsRulesTest.php b/tests/Feature/AnalyticsRulesTest.php index 3f56d864..008662f3 100644 --- a/tests/Feature/AnalyticsRulesTest.php +++ b/tests/Feature/AnalyticsRulesTest.php @@ -5,7 +5,7 @@ use Tests\TestCase; use Typesense\Exceptions\ObjectNotFound; -class AnalyticsRuleTest extends TestCase +class AnalyticsRulesTest extends TestCase { private $ruleName = 'product_queries_aggregation'; private $ruleConfiguration = [ diff --git a/tests/Feature/CollectionsTest.php b/tests/Feature/CollectionsTest.php index 8f5c6811..68ba8855 100644 --- a/tests/Feature/CollectionsTest.php +++ b/tests/Feature/CollectionsTest.php @@ -5,7 +5,7 @@ use Tests\TestCase; use Typesense\Exceptions\ObjectNotFound; -class CollectionTest extends TestCase +class CollectionsTest extends TestCase { private $createCollectionRes = null; diff --git a/tests/Feature/KeysTest.php b/tests/Feature/KeysTest.php index 513d30da..658d0393 100644 --- a/tests/Feature/KeysTest.php +++ b/tests/Feature/KeysTest.php @@ -6,7 +6,7 @@ use Typesense\Exceptions\ObjectNotFound; -class KeyTest extends TestCase +class KeysTest extends TestCase { private $keyId = null; private $createKeyResponse = null; diff --git a/tests/Feature/OverridesTest.php b/tests/Feature/OverridesTest.php index b881452a..9b70f293 100644 --- a/tests/Feature/OverridesTest.php +++ b/tests/Feature/OverridesTest.php @@ -6,7 +6,7 @@ use Typesense\Exceptions\ObjectNotFound; -class OverrideTest extends TestCase +class OverridesTest extends TestCase { private $overrideUpsertRes = null; private $overrideId = 'customize-book'; From fa539001ca2230447c5aa6d4b0013e0be7aab8e8 Mon Sep 17 00:00:00 2001 From: Hayden Date: Thu, 20 Jun 2024 16:57:24 +0700 Subject: [PATCH 27/30] refactor: use assertCount() --- tests/Feature/AnalyticsRulesTest.php | 2 +- tests/Feature/CollectionsTest.php | 2 +- tests/Feature/MultiSearchTest.php | 2 +- tests/Feature/OverridesTest.php | 2 +- tests/Feature/PresetsTest.php | 4 ++-- tests/Feature/StopwordsTest.php | 2 +- tests/Feature/SynonymsTest.php | 2 +- 7 files changed, 8 insertions(+), 8 deletions(-) diff --git a/tests/Feature/AnalyticsRulesTest.php b/tests/Feature/AnalyticsRulesTest.php index 008662f3..0d863c90 100644 --- a/tests/Feature/AnalyticsRulesTest.php +++ b/tests/Feature/AnalyticsRulesTest.php @@ -52,6 +52,6 @@ public function testCanDeleteARule(): void public function testCanRetrieveAllRules(): void { $returnData = $this->client()->analytics->rules()->retrieve(); - $this->assertEquals(count($returnData['rules']), 1); + $this->assertCount(1, $returnData['rules']); } } diff --git a/tests/Feature/CollectionsTest.php b/tests/Feature/CollectionsTest.php index 68ba8855..0fe4fdb0 100644 --- a/tests/Feature/CollectionsTest.php +++ b/tests/Feature/CollectionsTest.php @@ -58,6 +58,6 @@ public function testCanDeleteACollection(): void public function testCanRetrieveAllCollections(): void { $response = $this->client()->collections->retrieve(); - $this->assertEquals(1, count($response)); + $this->assertCount(1, $response); } } diff --git a/tests/Feature/MultiSearchTest.php b/tests/Feature/MultiSearchTest.php index 880d3fa8..4611d5bd 100644 --- a/tests/Feature/MultiSearchTest.php +++ b/tests/Feature/MultiSearchTest.php @@ -33,7 +33,7 @@ protected function setUp(): void public function testCanPerformAMultiSearch(): void { $returnData = $this->client()->multiSearch->perform($this->searchRequests, $this->commonSearchParams); - $this->assertEquals(2, count($returnData['results'])); + $this->assertCount(2, $returnData['results']); } public function testCanLimitNumberOfRequestsInOneMultiSearch(): void diff --git a/tests/Feature/OverridesTest.php b/tests/Feature/OverridesTest.php index 9b70f293..1ac19b33 100644 --- a/tests/Feature/OverridesTest.php +++ b/tests/Feature/OverridesTest.php @@ -57,6 +57,6 @@ public function testCanDeleteAnOverride(): void public function testCanRetrieveAllOverrides(): void { $returnData = $this->client()->collections['books']->overrides->retrieve(); - $this->assertEquals(1, count($returnData['overrides'])); + $this->assertCount(1, $returnData['overrides']); } } diff --git a/tests/Feature/PresetsTest.php b/tests/Feature/PresetsTest.php index bbd2c6fa..982bbdd6 100644 --- a/tests/Feature/PresetsTest.php +++ b/tests/Feature/PresetsTest.php @@ -51,12 +51,12 @@ public function testCanDeleteAPreset(): void $this->assertEquals($this->presetName, $returnData['name']); $returnPresets = $this->client()->presets->get(); - $this->assertEquals(0, count($returnPresets['presets'])); + $this->assertCount(0, $returnPresets['presets']); } public function testCanRetrieveAllPresets(): void { $returnData = $this->client()->presets->get(); - $this->assertEquals(1, count($returnData['presets'])); + $this->assertCount(1, $returnData['presets']); } } diff --git a/tests/Feature/StopwordsTest.php b/tests/Feature/StopwordsTest.php index 909fba1d..508512b2 100644 --- a/tests/Feature/StopwordsTest.php +++ b/tests/Feature/StopwordsTest.php @@ -54,6 +54,6 @@ public function testCanDeleteAStopword(): void public function testCanRetrieveAllStopwords(): void { $returnData = $this->client()->stopwords->getAll(); - $this->assertEquals(1, count($returnData['stopwords'])); + $this->assertCount(1, $returnData['stopwords']); } } diff --git a/tests/Feature/SynonymsTest.php b/tests/Feature/SynonymsTest.php index be40f637..6cd5db05 100644 --- a/tests/Feature/SynonymsTest.php +++ b/tests/Feature/SynonymsTest.php @@ -47,6 +47,6 @@ public function testCanDeleteASynonymById(): void public function testCanRetrieveAllSynonyms(): void { $returnData = $this->synonyms->retrieve(); - $this->assertEquals(1, count($returnData['synonyms'])); + $this->assertCount(1, $returnData['synonyms']); } } From 216f0a54ff718f36a8e59eabb349c1de7565aa23 Mon Sep 17 00:00:00 2001 From: Hayden Date: Fri, 21 Jun 2024 11:31:53 +0700 Subject: [PATCH 28/30] test: documents --- tests/Feature/DocumentsTest.php | 85 +++++++++++++++++++++++++++++++-- 1 file changed, 81 insertions(+), 4 deletions(-) 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']); + } } From 369d525d832a86cddd4f735b6bbf429379a8f831 Mon Sep 17 00:00:00 2001 From: Hayden Date: Fri, 21 Jun 2024 17:04:51 +0700 Subject: [PATCH 29/30] test: config random execution --- phpunit.xml.dist | 1 + 1 file changed, 1 insertion(+) 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" > From 0b36de4ba1fae0d90e2f18c4c7fdfb9414a276e7 Mon Sep 17 00:00:00 2001 From: Hayden Date: Fri, 21 Jun 2024 17:08:59 +0700 Subject: [PATCH 30/30] test: mark analytics envents as incomplete --- tests/Feature/AnalyticsEventsTest.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/Feature/AnalyticsEventsTest.php b/tests/Feature/AnalyticsEventsTest.php index d0a188c9..05384f5c 100644 --- a/tests/Feature/AnalyticsEventsTest.php +++ b/tests/Feature/AnalyticsEventsTest.php @@ -15,6 +15,8 @@ class AnalyticsEventsTest extends TestCase public function testNeedImplementationForAnalyticsEvents(): void { - $this->assertTrue(true); + $this->markTestIncomplete( + 'This test has not been implemented yet.', + ); } }