diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml
index 121e0041..6890e295 100644
--- a/.github/workflows/tests.yml
+++ b/.github/workflows/tests.yml
@@ -48,3 +48,45 @@ jobs:
- name: Execute tests
run: vendor/bin/phpunit --verbose
+
+ tests-using-meilisearch:
+ runs-on: ubuntu-22.04
+
+ services:
+ meilisearch:
+ image: getmeili/meilisearch:latest
+ ports:
+ - 7700:7700
+ env:
+ MEILI_MASTER_KEY: masterKey
+ MEILI_NO_ANALYTICS: true
+
+ strategy:
+ fail-fast: true
+ matrix:
+ php: ['8.0', 8.1, 8.2, 8.3]
+
+ name: PHP ${{ matrix.php }} using Meilisearch
+
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v4
+
+ - name: Setup PHP
+ uses: shivammathur/setup-php@v2
+ with:
+ php-version: ${{ matrix.php }}
+ extensions: dom, curl, libxml, mbstring, zip
+ ini-values: error_reporting=E_ALL
+ tools: composer:v2
+ coverage: none
+
+ - name: Install dependencies
+ run: |
+ composer update --prefer-dist --no-interaction --no-progress
+
+ - name: Execute tests
+ run: vendor/bin/phpunit --verbose --group external-network,meilisearch
+ env:
+ MEILISEARCH_HOST: "http://localhost:7700"
+ MEILISEARCH_KEY: 'masterKey'
diff --git a/phpunit.xml.dist b/phpunit.xml.dist
index 8357487f..7ac4f3c2 100644
--- a/phpunit.xml.dist
+++ b/phpunit.xml.dist
@@ -18,11 +18,25 @@
./tests/Feature
+
+ ./tests/Integration
+
+
+
+ external-network
+
+
+
+
diff --git a/testbench.yaml b/testbench.yaml
index 91cd677b..5511d482 100644
--- a/testbench.yaml
+++ b/testbench.yaml
@@ -2,3 +2,6 @@ providers:
- Laravel\Scout\ScoutServiceProvider
migrations: true
+
+workbench:
+ install: true
diff --git a/tests/Feature/MeilisearchEngineTest.php b/tests/Feature/MeilisearchEngineTest.php
index 7ea798b1..60726b40 100644
--- a/tests/Feature/MeilisearchEngineTest.php
+++ b/tests/Feature/MeilisearchEngineTest.php
@@ -12,7 +12,10 @@ class MeilisearchEngineTest extends TestCase
protected function defineEnvironment($app)
{
- $app->make('config')->set('scout.driver', 'meilisearch');
+ $app->make('config')->set([
+ 'scout.driver' => 'meilisearch',
+ 'scout.meilisearch.host' => 'http://localhost:7700',
+ ]);
}
public function test_the_meilisearch_client_can_be_initialized()
diff --git a/tests/Fixtures/User.php b/tests/Fixtures/User.php
new file mode 100644
index 00000000..908961f9
--- /dev/null
+++ b/tests/Fixtures/User.php
@@ -0,0 +1,24 @@
+
+ */
+ public function toSearchableArray(): array
+ {
+ return [
+ 'id' => (int) $this->id,
+ 'name' => $this->name,
+ ];
+ }
+}
diff --git a/tests/Integration/AlgoliaSearchableTest.php b/tests/Integration/AlgoliaSearchableTest.php
new file mode 100644
index 00000000..42c1b23c
--- /dev/null
+++ b/tests/Integration/AlgoliaSearchableTest.php
@@ -0,0 +1,171 @@
+markTestSkipped();
+ }
+
+ $this->defineScoutEnvironment($app);
+ }
+
+ /**
+ * Define database migrations.
+ *
+ * @return void
+ */
+ protected function defineDatabaseMigrations()
+ {
+ $this->defineScoutDatabaseMigrations();
+ }
+
+ /**
+ * Perform any work that should take place once the database has finished refreshing.
+ *
+ * @return void
+ */
+ protected function afterRefreshingDatabase()
+ {
+ $this->importScoutIndexFrom(User::class);
+ }
+
+ public function test_it_can_use_basic_search()
+ {
+ $results = $this->itCanUseBasicSearch();
+
+ $this->assertSame([
+ 11 => 'Larry Casper',
+ 1 => 'Laravel Framework',
+ 44 => 'Amos Larson Sr.',
+ 43 => 'Dana Larson Sr.',
+ 42 => 'Dax Larkin',
+ 41 => 'Gudrun Larkin',
+ 40 => 'Otis Larson MD',
+ 39 => 'Linkwood Larkin',
+ 20 => 'Prof. Larry Prosacco DVM',
+ 12 => 'Reta Larkin',
+ ], $results->pluck('name', 'id')->all());
+ }
+
+ public function test_it_can_use_basic_search_with_query_callback()
+ {
+ $results = $this->itCanUseBasicSearchWithQueryCallback();
+
+ $this->assertSame([
+ 1 => 'Laravel Framework',
+ 44 => 'Amos Larson Sr.',
+ 43 => 'Dana Larson Sr.',
+ 42 => 'Dax Larkin',
+ 41 => 'Gudrun Larkin',
+ 40 => 'Otis Larson MD',
+ 12 => 'Reta Larkin',
+ ], $results->pluck('name', 'id')->all());
+ }
+
+ public function test_it_can_use_basic_search_to_fetch_keys()
+ {
+ $results = $this->itCanUseBasicSearchToFetchKeys();
+
+ $this->assertSame([
+ '11',
+ '1',
+ '44',
+ '43',
+ '42',
+ '41',
+ '40',
+ '39',
+ '20',
+ '12',
+ ], $results->all());
+ }
+
+ public function test_it_can_use_basic_search_with_query_callback_to_fetch_keys()
+ {
+ $results = $this->itCanUseBasicSearchWithQueryCallbackToFetchKeys();
+
+ $this->assertSame([
+ '11',
+ '1',
+ '44',
+ '43',
+ '42',
+ '41',
+ '40',
+ '39',
+ '20',
+ '12',
+ ], $results->all());
+ }
+
+ public function test_it_return_same_keys_with_query_callback()
+ {
+ $this->assertSame(
+ $this->itCanUseBasicSearchToFetchKeys()->all(),
+ $this->itCanUseBasicSearchWithQueryCallbackToFetchKeys()->all()
+ );
+ }
+
+ public function test_it_can_use_paginated_search()
+ {
+ [$page1, $page2] = $this->itCanUsePaginatedSearch();
+
+ $this->assertSame([
+ 11 => 'Larry Casper',
+ 1 => 'Laravel Framework',
+ 44 => 'Amos Larson Sr.',
+ 43 => 'Dana Larson Sr.',
+ 42 => 'Dax Larkin',
+ ], $page1->pluck('name', 'id')->all());
+
+ $this->assertSame([
+ 41 => 'Gudrun Larkin',
+ 40 => 'Otis Larson MD',
+ 39 => 'Linkwood Larkin',
+ 20 => 'Prof. Larry Prosacco DVM',
+ 12 => 'Reta Larkin',
+ ], $page2->pluck('name', 'id')->all());
+ }
+
+ public function test_it_can_use_paginated_search_with_query_callback()
+ {
+ [$page1, $page2] = $this->itCanUsePaginatedSearchWithQueryCallback();
+
+ $this->assertSame([
+ 1 => 'Laravel Framework',
+ 44 => 'Amos Larson Sr.',
+ 43 => 'Dana Larson Sr.',
+ 42 => 'Dax Larkin',
+ ], $page1->pluck('name', 'id')->all());
+
+ $this->assertSame([
+ 41 => 'Gudrun Larkin',
+ 40 => 'Otis Larson MD',
+ 12 => 'Reta Larkin',
+ ], $page2->pluck('name', 'id')->all());
+ }
+
+ protected static function scoutDriver(): string
+ {
+ return 'algolia';
+ }
+}
diff --git a/tests/Integration/CollectionSearchableTest.php b/tests/Integration/CollectionSearchableTest.php
new file mode 100644
index 00000000..04d19baf
--- /dev/null
+++ b/tests/Integration/CollectionSearchableTest.php
@@ -0,0 +1,39 @@
+defineScoutEnvironment($app);
+ }
+
+ public function test_it_can_use_basic_search_with_query_callback_to_fetch_keys()
+ {
+ $results = $this->itCanUseBasicSearchWithQueryCallbackToFetchKeys();
+
+ $this->assertNotSame([
+ 44,
+ 43,
+ 42,
+ 41,
+ 40,
+ 12,
+ 1,
+ ], $results->all());
+
+ $this->assertSame($this->itCanUseBasicSearchToFetchKeys()->all(), $results->all());
+ }
+
+ protected static function scoutDriver(): string
+ {
+ return 'collection';
+ }
+}
diff --git a/tests/Integration/DatabaseSearchableTest.php b/tests/Integration/DatabaseSearchableTest.php
new file mode 100644
index 00000000..be35871d
--- /dev/null
+++ b/tests/Integration/DatabaseSearchableTest.php
@@ -0,0 +1,137 @@
+defineScoutEnvironment($app);
+ }
+
+ /**
+ * Define database migrations.
+ */
+ protected function defineDatabaseMigrations(): void
+ {
+ $this->defineScoutDatabaseMigrations();
+ }
+
+ public function test_it_can_use_basic_search()
+ {
+ $results = $this->itCanUseBasicSearch();
+
+ $this->assertSame([
+ 44 => 'Amos Larson Sr.',
+ 43 => 'Dana Larson Sr.',
+ 42 => 'Dax Larkin',
+ 41 => 'Gudrun Larkin',
+ 40 => 'Otis Larson MD',
+ 39 => 'Linkwood Larkin',
+ 20 => 'Prof. Larry Prosacco DVM',
+ 12 => 'Reta Larkin',
+ 11 => 'Larry Casper',
+ 1 => 'Laravel Framework',
+ ], $results->pluck('name', 'id')->all());
+ }
+
+ public function test_it_can_use_basic_search_with_query_callback()
+ {
+ $results = $this->itCanUseBasicSearchWithQueryCallback();
+
+ $this->assertSame([
+ 44 => 'Amos Larson Sr.',
+ 43 => 'Dana Larson Sr.',
+ 42 => 'Dax Larkin',
+ 41 => 'Gudrun Larkin',
+ 40 => 'Otis Larson MD',
+ 12 => 'Reta Larkin',
+ 1 => 'Laravel Framework',
+ ], $results->pluck('name', 'id')->all());
+ }
+
+ public function test_it_can_use_basic_search_to_fetch_keys()
+ {
+ $results = $this->itCanUseBasicSearchToFetchKeys();
+
+ $this->assertSame([
+ 44,
+ 43,
+ 42,
+ 41,
+ 40,
+ 39,
+ 20,
+ 12,
+ 11,
+ 1,
+ ], $results->all());
+ }
+
+ public function test_it_can_use_basic_search_with_query_callback_to_fetch_keys()
+ {
+ $results = $this->itCanUseBasicSearchWithQueryCallbackToFetchKeys();
+
+ $this->assertSame([
+ 44,
+ 43,
+ 42,
+ 41,
+ 40,
+ 12,
+ 1,
+ ], $results->all());
+ }
+
+ public function test_it_can_use_paginated_search()
+ {
+ [$page1, $page2] = $this->itCanUsePaginatedSearch();
+
+ $this->assertSame([
+ 44 => 'Amos Larson Sr.',
+ 43 => 'Dana Larson Sr.',
+ 42 => 'Dax Larkin',
+ 41 => 'Gudrun Larkin',
+ 40 => 'Otis Larson MD',
+ ], $page1->pluck('name', 'id')->all());
+
+ $this->assertSame([
+ 39 => 'Linkwood Larkin',
+ 20 => 'Prof. Larry Prosacco DVM',
+ 12 => 'Reta Larkin',
+ 11 => 'Larry Casper',
+ 1 => 'Laravel Framework',
+ ], $page2->pluck('name', 'id')->all());
+ }
+
+ public function test_it_can_use_paginated_search_with_query_callback()
+ {
+ [$page1, $page2] = $this->itCanUsePaginatedSearchWithQueryCallback();
+
+ $this->assertSame([
+ 44 => 'Amos Larson Sr.',
+ 43 => 'Dana Larson Sr.',
+ 42 => 'Dax Larkin',
+ 41 => 'Gudrun Larkin',
+ 40 => 'Otis Larson MD',
+ ], $page1->pluck('name', 'id')->all());
+
+ $this->assertSame([
+ 12 => 'Reta Larkin',
+ 1 => 'Laravel Framework',
+ ], $page2->pluck('name', 'id')->all());
+ }
+
+ protected static function scoutDriver(): string
+ {
+ return 'database';
+ }
+}
diff --git a/tests/Integration/MeilisearchSearchableTest.php b/tests/Integration/MeilisearchSearchableTest.php
new file mode 100644
index 00000000..5405b077
--- /dev/null
+++ b/tests/Integration/MeilisearchSearchableTest.php
@@ -0,0 +1,173 @@
+markTestSkipped();
+
+ return;
+ }
+
+ $this->defineScoutEnvironment($app);
+ }
+
+ /**
+ * Define database migrations.
+ *
+ * @return void
+ */
+ protected function defineDatabaseMigrations()
+ {
+ $this->defineScoutDatabaseMigrations();
+ }
+
+ /**
+ * Perform any work that should take place once the database has finished refreshing.
+ *
+ * @return void
+ */
+ protected function afterRefreshingDatabase()
+ {
+ $this->importScoutIndexFrom(User::class);
+ }
+
+ public function test_it_can_use_basic_search()
+ {
+ $results = $this->itCanUseBasicSearch();
+
+ $this->assertSame([
+ 1 => 'Laravel Framework',
+ 11 => 'Larry Casper',
+ 12 => 'Reta Larkin',
+ 39 => 'Linkwood Larkin',
+ 40 => 'Otis Larson MD',
+ 41 => 'Gudrun Larkin',
+ 42 => 'Dax Larkin',
+ 43 => 'Dana Larson Sr.',
+ 44 => 'Amos Larson Sr.',
+ 20 => 'Prof. Larry Prosacco DVM',
+ ], $results->pluck('name', 'id')->all());
+ }
+
+ public function test_it_can_use_basic_search_with_query_callback()
+ {
+ $results = $this->itCanUseBasicSearchWithQueryCallback();
+
+ $this->assertSame([
+ 1 => 'Laravel Framework',
+ 12 => 'Reta Larkin',
+ 40 => 'Otis Larson MD',
+ 41 => 'Gudrun Larkin',
+ 42 => 'Dax Larkin',
+ 43 => 'Dana Larson Sr.',
+ 44 => 'Amos Larson Sr.',
+ ], $results->pluck('name', 'id')->all());
+ }
+
+ public function test_it_can_use_basic_search_to_fetch_keys()
+ {
+ $results = $this->itCanUseBasicSearchToFetchKeys();
+
+ $this->assertSame([
+ 1,
+ 11,
+ 12,
+ 39,
+ 40,
+ 41,
+ 42,
+ 43,
+ 44,
+ 20,
+ ], $results->all());
+ }
+
+ public function test_it_can_use_basic_search_with_query_callback_to_fetch_keys()
+ {
+ $results = $this->itCanUseBasicSearchWithQueryCallbackToFetchKeys();
+
+ $this->assertSame([
+ 1,
+ 11,
+ 12,
+ 39,
+ 40,
+ 41,
+ 42,
+ 43,
+ 44,
+ 20,
+ ], $results->all());
+ }
+
+ public function test_it_return_same_keys_with_query_callback()
+ {
+ $this->assertSame(
+ $this->itCanUseBasicSearchToFetchKeys()->all(),
+ $this->itCanUseBasicSearchWithQueryCallbackToFetchKeys()->all()
+ );
+ }
+
+ public function test_it_can_use_paginated_search()
+ {
+ [$page1, $page2] = $this->itCanUsePaginatedSearch();
+
+ $this->assertSame([
+ 1 => 'Laravel Framework',
+ 11 => 'Larry Casper',
+ 12 => 'Reta Larkin',
+ 39 => 'Linkwood Larkin',
+ 40 => 'Otis Larson MD',
+ ], $page1->pluck('name', 'id')->all());
+
+ $this->assertSame([
+ 41 => 'Gudrun Larkin',
+ 42 => 'Dax Larkin',
+ 43 => 'Dana Larson Sr.',
+ 44 => 'Amos Larson Sr.',
+ 20 => 'Prof. Larry Prosacco DVM',
+ ], $page2->pluck('name', 'id')->all());
+ }
+
+ public function test_it_can_use_paginated_search_with_query_callback()
+ {
+ [$page1, $page2] = $this->itCanUsePaginatedSearchWithQueryCallback();
+
+ $this->assertSame([
+ 1 => 'Laravel Framework',
+ 12 => 'Reta Larkin',
+ 40 => 'Otis Larson MD',
+ ], $page1->pluck('name', 'id')->all());
+
+ $this->assertSame([
+ 41 => 'Gudrun Larkin',
+ 42 => 'Dax Larkin',
+ 43 => 'Dana Larson Sr.',
+ 44 => 'Amos Larson Sr.',
+ ], $page2->pluck('name', 'id')->all());
+ }
+
+ protected static function scoutDriver(): string
+ {
+ return 'meilisearch';
+ }
+}
diff --git a/tests/Integration/SearchableTests.php b/tests/Integration/SearchableTests.php
new file mode 100644
index 00000000..9856f719
--- /dev/null
+++ b/tests/Integration/SearchableTests.php
@@ -0,0 +1,107 @@
+set('scout.driver', static::scoutDriver());
+ }
+
+ /**
+ * Define database migrations.
+ */
+ protected function defineScoutDatabaseMigrations(): void
+ {
+ $this->loadLaravelMigrations();
+
+ $collect = LazyCollection::make(function () {
+ yield ['name' => 'Laravel Framework'];
+
+ foreach (range(2, 10) as $key) {
+ yield ['name' => "Example {$key}"];
+ }
+
+ yield ['name' => 'Larry Casper', 'email_verified_at' => null];
+ yield ['name' => 'Reta Larkin'];
+
+ foreach (range(13, 19) as $key) {
+ yield ['name' => "Example {$key}"];
+ }
+
+ yield ['name' => 'Prof. Larry Prosacco DVM', 'email_verified_at' => null];
+
+ foreach (range(21, 38) as $key) {
+ yield ['name' => "Example {$key}", 'email_verified_at' => null];
+ }
+
+ yield ['name' => 'Linkwood Larkin', 'email_verified_at' => null];
+ yield ['name' => 'Otis Larson MD'];
+ yield ['name' => 'Gudrun Larkin'];
+ yield ['name' => 'Dax Larkin'];
+ yield ['name' => 'Dana Larson Sr.'];
+ yield ['name' => 'Amos Larson Sr.'];
+ });
+
+ UserFactory::new()
+ ->times(44)
+ ->state(new Sequence(...$collect->all()))
+ ->create();
+ }
+
+ protected function itCanUseBasicSearch()
+ {
+ return User::search('lar')->take(10)->get();
+ }
+
+ protected function itCanUseBasicSearchWithQueryCallback()
+ {
+ return User::search('lar')->take(10)->query(function ($query) {
+ return $query->whereNotNull('email_verified_at');
+ })->get();
+ }
+
+ protected function itCanUseBasicSearchToFetchKeys()
+ {
+ return User::search('lar')->take(10)->keys();
+ }
+
+ protected function itCanUseBasicSearchWithQueryCallbackToFetchKeys()
+ {
+ return User::search('lar')->take(10)->query(function ($query) {
+ return $query->whereNotNull('email_verified_at');
+ })->keys();
+ }
+
+ protected function itCanUsePaginatedSearch()
+ {
+ return [
+ User::search('lar')->take(10)->paginate(5, 'page', 1),
+ User::search('lar')->take(10)->paginate(5, 'page', 2),
+ ];
+ }
+
+ protected function itCanUsePaginatedSearchWithQueryCallback()
+ {
+ $queryCallback = function ($query) {
+ return $query->whereNotNull('email_verified_at');
+ };
+
+ return [
+ User::search('lar')->take(10)->query($queryCallback)->paginate(5, 'page', 1),
+ User::search('lar')->take(10)->query($queryCallback)->paginate(5, 'page', 2),
+ ];
+ }
+}
diff --git a/tests/Integration/TestCase.php b/tests/Integration/TestCase.php
new file mode 100644
index 00000000..718f7305
--- /dev/null
+++ b/tests/Integration/TestCase.php
@@ -0,0 +1,41 @@
+ $model]);
+ }
+
+ artisan($this, 'scout:import', ['model' => $model]);
+
+ sleep(1);
+ }
+
+ /**
+ * Clean up the testing environment before the next test case.
+ *
+ * @return void
+ */
+ public static function tearDownAfterClass(): void
+ {
+ remote('scout:delete-all-indexes', [
+ 'SCOUT_DRIVER' => static::scoutDriver(),
+ ])->mustRun();
+
+ parent::tearDownAfterClass();
+ }
+
+ abstract protected static function scoutDriver(): string;
+}