diff --git a/app/Actions/SearchPage/SearchAction.php b/app/Actions/SearchPage/SearchAction.php index 4f6d43d..ffcac0c 100644 --- a/app/Actions/SearchPage/SearchAction.php +++ b/app/Actions/SearchPage/SearchAction.php @@ -36,15 +36,23 @@ private function addKeywordQuery(Builder $builder, string $keyword): void $builder->where(function (Builder $builder) use ($keyword): void { foreach (explode(' ', $keyword) as $word) { $word = trim($word); + if ($word === '') { + continue; + } + if (str_starts_with($word, '-')) { $word = trim(substr($word, 1)); if ($word !== '' && $word !== '0') { - $builder->where('title', 'not like', sprintf('%%%s%%', $word)); - $builder->orWhere('text', 'not like', sprintf('%%%s%%', $word)); + $builder->where(function (Builder $builder) use ($word): void { + $builder->where('title', 'not like', sprintf('%%%s%%', $word)); + $builder->where('text', 'not like', sprintf('%%%s%%', $word)); + }); } } else { - $builder->where('title', 'like', sprintf('%%%s%%', $word)); - $builder->orWhere('text', 'like', sprintf('%%%s%%', $word)); + $builder->where(function (Builder $builder) use ($word): void { + $builder->where('title', 'like', sprintf('%%%s%%', $word)); + $builder->orWhere('text', 'like', sprintf('%%%s%%', $word)); + }); } } }); diff --git a/tests/Feature/Actions/SearchPage/SearchActionTest.php b/tests/Feature/Actions/SearchPage/SearchActionTest.php new file mode 100644 index 0000000..d61badf --- /dev/null +++ b/tests/Feature/Actions/SearchPage/SearchActionTest.php @@ -0,0 +1,131 @@ +pak128 = Pak::factory()->create(['slug' => PakSlug::Pak128]); + $this->pak64 = Pak::factory()->create(['slug' => PakSlug::Pak64]); + } + + public function test_filters_by_site_and_pak(): void + { + $page1 = Page::factory()->create(['site_name' => SiteName::Japan]); + $page1->paks()->attach($this->pak128); + + $page2 = Page::factory()->create(['site_name' => SiteName::Portal]); + $page2->paks()->attach($this->pak128); + + $page3 = Page::factory()->create(['site_name' => SiteName::Japan]); + $page3->paks()->attach($this->pak64); + + $searchAction = new SearchAction; + + $result = $searchAction([ + 'keyword' => '', + 'sites' => [SiteName::Japan->value], + 'paks' => [PakSlug::Pak128->value], + ]); + + $this->assertCount(1, $result); + $this->assertSame($page1->id, $result->first()->id); + } + + public function test_filters_by_keyword_including_exclude_logic(): void + { + $page1 = Page::factory()->create([ + 'site_name' => SiteName::Japan, + 'title' => 'Train Addon', + 'text' => 'This is a train', + ]); + $page1->paks()->attach($this->pak128); + + $page2 = Page::factory()->create([ + 'site_name' => SiteName::Japan, + 'title' => 'Bus Addon', + 'text' => 'This is a bus', + ]); + $page2->paks()->attach($this->pak128); + + $page3 = Page::factory()->create([ + 'site_name' => SiteName::Japan, + 'title' => 'Electric Train Addon', + 'text' => 'This is an electric train', + ]); + $page3->paks()->attach($this->pak128); + + $searchAction = new SearchAction; + + $baseQuery = [ + 'sites' => [SiteName::Japan->value], + 'paks' => [PakSlug::Pak128->value], + ]; + + // Search for 'Train' + $result = $searchAction(array_merge($baseQuery, ['keyword' => 'Train'])); + $this->assertCount(2, $result); // page1, page3 + + // Search for 'Train -Electric' + $result = $searchAction(array_merge($baseQuery, ['keyword' => 'Train -Electric'])); + $this->assertCount(1, $result); // page1 only + + // Search for 'Bus' + $result = $searchAction(array_merge($baseQuery, ['keyword' => 'Bus'])); + $this->assertCount(1, $result); // page2 + $this->assertSame($page2->id, $result->first()->id); + } + + public function test_orders_by_last_modified_desc(): void + { + $page1 = Page::factory()->create([ + 'site_name' => SiteName::Japan, + 'last_modified' => now()->subDays(2), + ]); + $page1->paks()->attach($this->pak128); + + $page2 = Page::factory()->create([ + 'site_name' => SiteName::Japan, + 'last_modified' => now(), + ]); + $page2->paks()->attach($this->pak128); + + $page3 = Page::factory()->create([ + 'site_name' => SiteName::Japan, + 'last_modified' => now()->subDays(1), + ]); + $page3->paks()->attach($this->pak128); + + $searchAction = new SearchAction; + + $result = $searchAction([ + 'keyword' => '', + 'sites' => [SiteName::Japan->value], + 'paks' => [PakSlug::Pak128->value], + ]); + + $this->assertCount(3, $result); + $this->assertSame($page2->id, $result->items()[0]->id); + $this->assertSame($page3->id, $result->items()[1]->id); + $this->assertSame($page1->id, $result->items()[2]->id); + } +} diff --git a/tests/Feature/Actions/SyncNotion/SyncActionTest.php b/tests/Feature/Actions/SyncNotion/SyncActionTest.php new file mode 100644 index 0000000..1604f57 --- /dev/null +++ b/tests/Feature/Actions/SyncNotion/SyncActionTest.php @@ -0,0 +1,96 @@ +create(['slug' => PakSlug::Pak128]); + + // This page should be created in Notion + $pageToCreate = Page::factory()->create([ + 'title' => 'New Addon', + 'site_name' => SiteName::Japan, + 'url' => 'https://example.com/new', + 'last_modified' => now(), + ]); + $pageToCreate->paks()->attach($pak); + + // This page exists in both DB and Notion, should be updated + Page::factory()->create([ + 'title' => 'Updated Addon', + 'site_name' => SiteName::Japan, + 'url' => 'https://example.com/update', + 'last_modified' => now(), + ]); + + $database = Database::fromArray([ + 'id' => 'test_database_id', + 'created_time' => '2023-01-01T00:00:00.000Z', + 'last_edited_time' => '2023-01-01T00:00:00.000Z', + 'title' => [], + 'description' => [], + 'icon' => null, + 'cover' => null, + 'properties' => [ + 'Title' => [ + 'id' => 'title', + 'type' => 'title', + 'name' => 'Title', + 'title' => [], + ], + 'パックセット' => [ + 'id' => 'prop_id', + 'type' => 'multi_select', + 'name' => 'パックセット', + 'multi_select' => [ + 'options' => [ + ['name' => '128', 'id' => 'opt_128', 'color' => 'default'], + ], + ], + ], + ], + 'parent' => ['type' => 'workspace', 'workspace' => true], + 'url' => 'https://notion.so', + 'is_inline' => false, + ]); + + $notionPageToUpdate = NotionPage::create(PageParent::database('test_database_id')); + $notionPageToUpdate = $notionPageToUpdate->addProperty('URL', Url::create('https://example.com/update')); + + $notionPageToDelete = NotionPage::create(PageParent::database('test_database_id')); + $notionPageToDelete = $notionPageToDelete->addProperty('URL', Url::create('https://example.com/delete')); + + $mock = Mockery::mock(Notion::class); + $mock->shouldReceive('databases->find')->with('test_database_id')->andReturn($database); + $mock->shouldReceive('databases->queryAllPages')->with($database)->andReturn([$notionPageToUpdate, $notionPageToDelete]); + + $mock->shouldReceive('pages->delete')->once()->with($notionPageToDelete); + + $mock->shouldReceive('pages->update')->once()->with(Mockery::on(fn (NotionPage $notionPage): bool => $notionPage->getProperty('URL')->url === 'https://example.com/update')); + + $mock->shouldReceive('pages->create')->once()->with(Mockery::on(fn (NotionPage $notionPage): bool => $notionPage->getProperty('URL')->url === 'https://example.com/new')); + + $syncAction = new SyncAction($mock); + $syncAction('test_database_id', 10); + } +}