Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
use App\Packages\Domains\WorldHeritage\Ports\Dto\HeritageSearchResult;
use App\Packages\Domains\WorldHeritage\Ports\WorldHeritageSearchPort;
use App\Packages\Features\QueryUseCases\ListQuery\AlgoliaSearchListQuery;
use Illuminate\Support\Facades\Log;

class AlgoliaWorldHeritageSearchAdapter implements WorldHeritageSearchPort
{
Expand Down Expand Up @@ -152,11 +153,26 @@ public function search(

$hits = $response['hits'] ?? [];

$ids = array_values(array_filter(array_map(static function (array $h) {
return isset($h['id'])
? (int) $h['id']
: (isset($h['objectID']) ? (int) $h['objectID'] : null);
}, $hits)));
$ids = [];
foreach ($hits as $hit) {
$rawId = $hit['id'] ?? $hit['objectID'] ?? null;

if ($rawId === null) {
continue;
}

if (!is_numeric($rawId)) {
// Don't silently coerce a non-numeric id to 0 and drop it via
// array_filter: that would hide an Algolia/DB data inconsistency.
Log::warning('Algolia search hit has a non-numeric id', [
'id' => $hit['id'] ?? null,
'objectID' => $hit['objectID'] ?? null,
]);
continue;
}

$ids[] = (int) $rawId;
}

$nbHits = (int) ($response['nbHits'] ?? 0);
$nbPages = (int) ($response['nbPages'] ?? 0);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,9 @@
use App\Enums\StudyRegion;
use App\Packages\Domains\WorldHeritage\Adapter\AlgoliaWorldHeritageSearchAdapter;
use App\Packages\Features\QueryUseCases\ListQuery\AlgoliaSearchListQuery;
use Illuminate\Support\Facades\Log;
use Mockery;
use PHPUnit\Framework\TestCase;
use Tests\TestCase;

class AlgoliaWorldHeritageSearchAdapterTest extends TestCase
{
Expand Down Expand Up @@ -216,6 +217,53 @@ public function test_search_by_country_name_that_does_not_resolve_still_runs_ful
$this->assertSame([7, 8], $result->ids);
}

public function test_search_logs_and_skips_a_hit_with_a_non_numeric_id_instead_of_silently_dropping_it(): void
{
$indexName = 'world_heritage';
$adapter = new AlgoliaWorldHeritageSearchAdapter($this->client, $indexName);

$q = new AlgoliaSearchListQuery(
keyword: 'temple',
countryName: null,
countryIso3: null,
region: null,
category: null,
yearFrom: null,
yearTo: null,
criteria: [],
isEndangered: null,
currentPage: 1,
perPage: 30,
);

$this->client
->shouldReceive('searchSingleIndex')
->once()
->andReturn([
'hits' => [
['id' => 10],
['id' => 'not-a-number', 'objectID' => 'broken-1'],
],
'nbHits' => 2,
'nbPages' => 1,
'page' => 0,
'hitsPerPage' => 30,
]);

Log::shouldReceive('warning')
->once()
->with('Algolia search hit has a non-numeric id', [
'id' => 'not-a-number',
'objectID' => 'broken-1',
]);

$result = $adapter->search($q, currentPage: 1, perPage: 30);

// The valid hit survives; the broken one is logged and skipped rather
// than silently coerced to 0 and dropped via array_filter.
$this->assertSame([10], $result->ids);
}

public function test_search_combines_keyword_with_country_iso3_filter_instead_of_dropping_keyword(): void
{
$indexName = 'world_heritage';
Expand Down
Loading