77use DateTimeImmutable ;
88use Illuminate \Database \Eloquent \Collection as EloquentCollection ;
99use Laravel \Scout \Builder ;
10+ use Laravel \Scout \Engines \MeilisearchEngine ;
11+ use Laravel \Scout \Jobs \RemoveFromSearch ;
12+ use Laravel \Scout \Tests \Unit \AlgoliaEngineTest ;
1013use Mockery as m ;
1114use MongoDB \BSON \Document ;
1215use MongoDB \BSON \Regex ;
2023use MongoDB \Model \BSONDocument ;
2124use PHPUnit \Framework \Attributes \DataProvider ;
2225
26+ use function serialize ;
27+ use function unserialize ;
28+
2329/** Unit tests that do not require an Atlas Search cluster */
2430class ScoutEngineTest extends TestCase
2531{
32+ private const EXPECTED_SEARCH_OPTIONS = ['allowDiskUse ' => true , 'typeMap ' => ['root ' => 'object ' , 'document ' => 'array ' , 'array ' => 'array ' ]];
33+
2634 /** @param callable(): Builder $builder */
2735 #[DataProvider('provideSearchPipelines ' )]
2836 public function testSearch (Closure $ builder , array $ expectedPipeline ): void
@@ -36,8 +44,7 @@ public function testSearch(Closure $builder, array $expectedPipeline): void
3644 $ cursor = m::mock (CursorInterface::class);
3745 $ cursor ->shouldReceive ('toArray ' )->once ()->with ()->andReturn ($ data );
3846
39- $ options = ['allowDiskUse ' => true , 'typeMap ' => ['root ' => 'object ' , 'document ' => 'array ' , 'array ' => 'array ' ]];
40- $ collection ->shouldReceive ('aggregate ' )->once ()->with ($ expectedPipeline , $ options )->andReturn ($ cursor );
47+ $ collection ->shouldReceive ('aggregate ' )->once ()->with ($ expectedPipeline , self ::EXPECTED_SEARCH_OPTIONS )->andReturn ($ cursor );
4148
4249 $ engine = new ScoutEngine ($ database , softDelete: false , prefix: '' );
4350 $ result = $ engine ->search ($ builder ());
@@ -157,6 +164,66 @@ function () {
157164
158165 public function testPaginate ()
159166 {
167+ $ perPage = 5 ;
168+ $ page = 3 ;
169+
170+ $ database = m::mock (Database::class);
171+ $ collection = m::mock (Collection::class);
172+ $ cursor = m::mock (CursorInterface::class);
173+ $ database ->shouldReceive ('selectCollection ' )
174+ ->with ('table_searchable ' )
175+ ->andReturn ($ collection );
176+ $ collection ->shouldReceive ('aggregate ' )
177+ ->once ()
178+ ->withArgs (function (...$ args ) {
179+ self ::assertSame ([
180+ [
181+ '$search ' => [
182+ 'index ' => 'scout ' ,
183+ 'text ' => [
184+ 'query ' => 'mustang ' ,
185+ 'path ' => [
186+ 'wildcard ' => '* ' ,
187+ ],
188+ 'fuzzy ' => [
189+ 'maxEdits ' => 2 ,
190+ ],
191+ ],
192+ 'count ' => [
193+ 'type ' => 'lowerBound ' ,
194+ ],
195+ 'sort ' => [
196+ 'name ' => -1 ,
197+ ],
198+ ],
199+ ],
200+ [
201+ '$addFields ' => [
202+ 'search_meta ' => '$$SEARCH_META ' ,
203+ ],
204+ ],
205+ [
206+ '$limit ' => 5 ,
207+ ],
208+ [
209+ '$skip ' => 10 ,
210+ ],
211+ ], $ args [0 ]);
212+
213+ $ this ->assertSame (self ::EXPECTED_SEARCH_OPTIONS , $ args [1 ]);
214+
215+ return true ;
216+ })
217+ ->andReturn ($ cursor );
218+ $ cursor ->shouldReceive ('toArray ' )
219+ ->once ()
220+ ->with ()
221+ ->andReturn ([['_id ' => 'key_1 ' ], ['_id ' => 'key_2 ' ]]);
222+
223+ $ engine = new ScoutEngine ($ database , softDelete: false , prefix: '' );
224+ $ builder = new Builder (new SearchableModel (), 'mustang ' );
225+ $ builder ->orderBy ('name ' , 'desc ' );
226+ $ engine ->paginate ($ builder , $ perPage , $ page );
160227 }
161228
162229 #[DataProvider('provideResultsForMapIds ' )]
@@ -254,18 +321,17 @@ public function testUpdateWithSoftDelete(): void
254321 [
255322 'updateOne ' => [
256323 ['_id ' => 'key_1 ' ],
257- ['$setOnInsert ' => ['_id ' => 'key_1 ' ], '$set ' => ['id ' => 1 , ' date ' => new UTCDateTime ( $ date ) ]],
324+ ['$setOnInsert ' => ['_id ' => 'key_1 ' ], '$set ' => ['id ' => 1 ]],
258325 ['upsert ' => true ],
259326 ],
260327 ],
261328 ]);
262329
330+ $ model = new SearchableModel (['id ' => 1 ]);
331+ $ model ->delete ();
332+
263333 $ engine = new ScoutEngine ($ database , softDelete: false , prefix: '' );
264- $ engine ->update (EloquentCollection::make ([
265- (new SearchableModel ([
266- 'id ' => 1 ,
267- ])),
268- ]));
334+ $ engine ->update (EloquentCollection::make ([$ model ]));
269335 }
270336
271337 public function testDelete (): void
@@ -286,6 +352,28 @@ public function testDelete(): void
286352 ]));
287353 }
288354
355+ /** @see AlgoliaEngineTest::test_delete_with_removeable_scout_collection_using_custom_search_key */
356+ public function testDeleteWithRemoveableScoutCollection (): void
357+ {
358+ $ job = new RemoveFromSearch (EloquentCollection::make ([
359+ new SearchableModel (['id ' => 5 , 'scout_key ' => 'key_5 ' ]),
360+ ]));
361+
362+ $ job = unserialize (serialize ($ job ));
363+
364+ $ database = m::mock (Database::class);
365+ $ collection = m::mock (Collection::class);
366+ $ database ->shouldReceive ('selectCollection ' )
367+ ->with ('table_indexable ' )
368+ ->andReturn ($ collection );
369+ $ collection ->shouldReceive ('deleteMany ' )
370+ ->once ()
371+ ->with (['_id ' => ['$in ' => ['key_5 ' ]]]);
372+
373+ $ engine = new ScoutEngine ($ database , softDelete: false , prefix: 'ignored_prefix_ ' );
374+ $ engine ->delete ($ job ->models );
375+ }
376+
289377 public function testDeleteAll (): void
290378 {
291379 $ collectionNames = ['scout-prefix-table1 ' , 'scout-prefix-table2 ' ];
0 commit comments