database()->rollBack();
// We need to insert these outside of a transaction, because FULLTEXT indexing,
// which is needed for search, doesn't happen in transactions.
// We clean it up explcitly at the end.
$this->database()->table('discussions')->insert([
['id' => 1, 'title' => 'DISCUSSION 1', 'created_at' => Carbon::now()->toDateTimeString(), 'user_id' => 1, 'comment_count' => 1],
['id' => 2, 'title' => 'DISCUSSION 2', 'created_at' => Carbon::now()->toDateTimeString(), 'user_id' => 1, 'comment_count' => 1],
]);
$this->database()->table('posts')->insert([
['id' => 1, 'discussion_id' => 1, 'created_at' => Carbon::now()->toDateTimeString(), 'user_id' => 1, 'type' => 'comment', 'content' => 'not in text
'],
['id' => 2, 'discussion_id' => 2, 'created_at' => Carbon::now()->toDateTimeString(), 'user_id' => 1, 'type' => 'comment', 'content' => 'lightsail in text
'],
]);
// We need to call these again, since we rolled back the transaction started by `::app()`.
$this->database()->beginTransaction();
$this->populateDatabase();
}
/**
* @inheritDoc
*/
protected function tearDown(): void
{
parent::tearDown();
$this->database()->table('discussions')->whereIn('id', [1, 2])->delete();
$this->database()->table('posts')->whereIn('id', [1, 2])->delete();
}
public function searchDiscussions($query, $limit = null)
{
$this->app();
$actor = User::find(1);
$criteria = new SearchCriteria($actor, ['q' => $query]);
return $this->app()->getContainer()->make(DiscussionSearcher::class)->search($criteria, $limit)->getResults();
}
/**
* @test
*/
public function works_as_expected_with_no_modifications()
{
$this->prepDb();
$searchForAll = json_encode($this->searchDiscussions('in text', 5));
$this->assertContains('DISCUSSION 1', $searchForAll);
$this->assertContains('DISCUSSION 2', $searchForAll);
$searchForSecond = json_encode($this->searchDiscussions('lightsail', 5));
$this->assertNotContains('DISCUSSION 1', $searchForSecond);
$this->assertContains('DISCUSSION 2', $searchForSecond);
}
/**
* @test
*/
public function custom_full_text_gambit_has_effect_if_added()
{
$this->extend((new Extend\SimpleFlarumSearch(DiscussionSearcher::class))->setFullTextGambit(NoResultFullTextGambit::class));
$this->assertEquals('[]', json_encode($this->searchDiscussions('in text', 5)));
}
/**
* @test
*/
public function custom_filter_gambit_has_effect_if_added()
{
$this->extend((new Extend\SimpleFlarumSearch(DiscussionSearcher::class))->addGambit(NoResultFilterGambit::class));
$this->prepDb();
$withResultSearch = json_encode($this->searchDiscussions('noResult:0', 5));
$this->assertContains('DISCUSSION 1', $withResultSearch);
$this->assertContains('DISCUSSION 2', $withResultSearch);
$this->assertEquals('[]', json_encode($this->searchDiscussions('noResult:1', 5)));
}
/**
* @test
*/
public function search_mutator_has_effect_if_added()
{
$this->extend((new Extend\SimpleFlarumSearch(DiscussionSearcher::class))->addSearchMutator(function ($search, $criteria) {
$search->getquery()->whereRaw('1=0');
}));
$this->prepDb();
$this->assertEquals('[]', json_encode($this->searchDiscussions('in text', 5)));
}
/**
* @test
*/
public function search_mutator_has_effect_if_added_with_invokable_class()
{
$this->extend((new Extend\SimpleFlarumSearch(DiscussionSearcher::class))->addSearchMutator(CustomSearchMutator::class));
$this->prepDb();
$this->assertEquals('[]', json_encode($this->searchDiscussions('in text', 5)));
}
}
class NoResultFullTextGambit implements GambitInterface
{
/**
* {@inheritdoc}
*/
public function apply(SearchState $search, $searchValue)
{
$search->getQuery()
->whereRaw('0=1');
}
}
class NoResultFilterGambit extends AbstractRegexGambit
{
/**
* {@inheritdoc}
*/
public function getGambitPattern()
{
return 'noResult:(.+)';
}
/**
* {@inheritdoc}
*/
public function conditions(SearchState $search, array $matches, $negate)
{
$noResults = trim($matches[1], ' ');
if ($noResults == '1') {
$search->getQuery()
->whereRaw('0=1');
}
}
}
class CustomSearchMutator
{
public function __invoke($search, $criteria)
{
$search->getQuery()->whereRaw('1=0');
}
}