diff --git a/integrations/search-osm-aliases/__tests__/provider.test.ts b/integrations/search-osm-aliases/__tests__/provider.test.ts index 1cdc061f3..fa2fb073b 100644 --- a/integrations/search-osm-aliases/__tests__/provider.test.ts +++ b/integrations/search-osm-aliases/__tests__/provider.test.ts @@ -6,6 +6,7 @@ describe("OSM alias suggestion provider", () => { it("uses normalized parameterized lookup and maps exact code evidence", async () => { const execute = vi .fn() + .mockResolvedValueOnce([{ exists: true }]) .mockResolvedValueOnce([{ epoch: "epoch-1", status: "ready" }]) .mockResolvedValueOnce([ { @@ -32,7 +33,7 @@ describe("OSM alias suggestion provider", () => { { signal: new AbortController().signal, deadlineAt: Number.POSITIVE_INFINITY }, ); - expect(execute.mock.calls[1]?.[1]).toEqual(["fra", true, 8, null, null, true]); + expect(execute.mock.calls[2]?.[1]).toEqual(["fra", true, 8, null, null, true]); expect(result.suggestions[0]).toMatchObject({ id: "osm:node/42", ids: { osm: "node/42", wikidata: "Q46033" }, @@ -63,6 +64,7 @@ describe("OSM alias suggestion provider", () => { it("passes lowercase acronym intent and proximity to the SQL gate", async () => { const execute = vi .fn() + .mockResolvedValueOnce([{ exists: true }]) .mockResolvedValueOnce([{ epoch: "epoch-2", status: "ready" }]) .mockResolvedValueOnce([]); const provider = createOsmAliasSuggestionProvider( @@ -74,7 +76,23 @@ describe("OSM alias suggestion provider", () => { { signal: new AbortController().signal, deadlineAt: Number.POSITIVE_INFINITY }, ); - expect(execute.mock.calls[1]?.[1]).toEqual(["uncc", true, 4, -80.73, 35.3, false]); - expect(String(execute.mock.calls[1]?.[0])).toContain("ST_DWithin"); + expect(execute.mock.calls[2]?.[1]).toEqual(["uncc", true, 4, -80.73, 35.3, false]); + expect(String(execute.mock.calls[2]?.[0])).toContain("ST_DWithin"); + }); + + it("returns nothing, without reading the index, until an index is built", async () => { + const execute = vi.fn().mockResolvedValueOnce([{ exists: false }]); + const provider = createOsmAliasSuggestionProvider( + createMockIntegrationContext({ db: { execute } }), + ); + + const result = await provider.searchSuggestions( + { query: "FRA", lang: "en", limit: 8 }, + { signal: new AbortController().signal, deadlineAt: Number.POSITIVE_INFINITY }, + ); + + expect(result).toEqual({ suggestions: [], attributions: [], freshnessSeconds: 86_400 }); + expect(execute).toHaveBeenCalledTimes(1); + expect(String(execute.mock.calls[0]?.[0])).toContain("to_regclass('osm_search.index_state')"); }); }); diff --git a/integrations/search-osm-aliases/provider.ts b/integrations/search-osm-aliases/provider.ts index 50b56cfb1..ec8bf1b88 100644 --- a/integrations/search-osm-aliases/provider.ts +++ b/integrations/search-osm-aliases/provider.ts @@ -125,6 +125,16 @@ export function createOsmAliasSuggestionProvider( ): Promise { signal.throwIfAborted(); if (!ctx.db) return { suggestions: [], attributions: [], freshnessSeconds: 86_400 }; + // Until the data-manager builds a first index there is no schema to + // read, which is not a fault of the provider. + const published = await ctx.db.execute<{ exists: boolean }[]>( + "SELECT to_regclass('osm_search.index_state') IS NOT NULL AS exists", + undefined, + { signal }, + ); + if (!published[0]?.exists) { + return { suggestions: [], attributions: [], freshnessSeconds: 86_400 }; + } const states = await ctx.db.execute( "SELECT epoch, status FROM osm_search.index_state WHERE singleton = 1", undefined,