search() accumulated results across up to 3 pages in a local array, but a failure on page 2 or 3 (e.g. Google's next_page_token not active yet despite the 2.5s wait, or a transient error) propagated out and lost the whole accumulated array, not just the failing page. The caller's catch only logs and continues, discarding leads that were already successfully fetched. Now only rethrows (preserving existing behavior) when nothing has been accumulated yet; a failure after at least one successful page logs and returns what was already collected. Found via a full-project audit.
89 lines
3.2 KiB
JavaScript
89 lines
3.2 KiB
JavaScript
import test from 'node:test';
|
|
import assert from 'node:assert/strict';
|
|
import { collect } from '../src/sources/google-places.mjs';
|
|
|
|
function fakeCtx() {
|
|
const logs = [];
|
|
return { emit: () => {}, log: (message) => logs.push(message), isCancelled: () => false, logs };
|
|
}
|
|
|
|
// Antes, uma falha na pagina 2/3 (comum: o next_page_token do Google ainda
|
|
// nao ativou apesar da espera de 2.5s entre paginas, ou uma falha transitoria
|
|
// de rede) fazia search() lancar e descartar TODOS os leads ja obtidos nas
|
|
// paginas anteriores bem-sucedidas, nao so a pagina que falhou.
|
|
test('falha na pagina 2 nao descarta os leads ja obtidos na pagina 1', async () => {
|
|
process.env.GOOGLE_PLACES_API_KEY = 'chave-teste';
|
|
const originalFetch = globalThis.fetch;
|
|
let textsearchCalls = 0;
|
|
|
|
globalThis.fetch = async (url) => {
|
|
const href = url.toString();
|
|
if (href.includes('textsearch')) {
|
|
textsearchCalls += 1;
|
|
if (textsearchCalls === 1) {
|
|
return new Response(JSON.stringify({
|
|
status: 'OK',
|
|
results: [{ place_id: 'p1', name: 'Pizzaria Um' }],
|
|
next_page_token: 'tok-pagina-2'
|
|
}));
|
|
}
|
|
// Pagina 2: falha transitoria (token ainda nao ativo, erro do provedor, etc.)
|
|
return new Response(JSON.stringify({ status: 'INVALID_REQUEST' }));
|
|
}
|
|
if (href.includes('details')) {
|
|
return new Response(JSON.stringify({ result: { place_id: 'p1', name: 'Pizzaria Um' } }));
|
|
}
|
|
throw new Error(`fetch inesperado: ${href}`);
|
|
};
|
|
|
|
try {
|
|
const ctx = fakeCtx();
|
|
const params = {
|
|
uf: 'BA',
|
|
cities: ['Salvador'],
|
|
segments: [{ name: 'restaurantes', queries: ['pizzaria'], interest: 'comida', pains: [] }],
|
|
limits: { maxPerQuery: 10, googlePages: 3 }
|
|
};
|
|
|
|
const batches = [];
|
|
for await (const batch of collect(params, ctx)) batches.push(batch);
|
|
|
|
assert.equal(batches.length, 1, JSON.stringify(batches));
|
|
assert.equal(batches[0].leads.length, 1);
|
|
assert.equal(batches[0].leads[0].company.name, 'Pizzaria Um');
|
|
assert.ok(ctx.logs.some((message) => /pagina 2/.test(message)), ctx.logs.join(' | '));
|
|
} finally {
|
|
globalThis.fetch = originalFetch;
|
|
delete process.env.GOOGLE_PLACES_API_KEY;
|
|
}
|
|
});
|
|
|
|
// Uma falha logo na primeira pagina (nada acumulado ainda) continua
|
|
// propagando e sendo tratada pelo chamador (loga e pula a query inteira) —
|
|
// esse caminho nao muda.
|
|
test('falha na primeira pagina ainda propaga e pula a query', async () => {
|
|
process.env.GOOGLE_PLACES_API_KEY = 'chave-teste';
|
|
const originalFetch = globalThis.fetch;
|
|
|
|
globalThis.fetch = async () => new Response(JSON.stringify({ status: 'INVALID_REQUEST' }));
|
|
|
|
try {
|
|
const ctx = fakeCtx();
|
|
const params = {
|
|
uf: 'BA',
|
|
cities: ['Salvador'],
|
|
segments: [{ name: 'restaurantes', queries: ['pizzaria'], interest: 'comida', pains: [] }],
|
|
limits: { maxPerQuery: 10, googlePages: 3 }
|
|
};
|
|
|
|
const batches = [];
|
|
for await (const batch of collect(params, ctx)) batches.push(batch);
|
|
|
|
assert.equal(batches.length, 0);
|
|
assert.ok(ctx.logs.some((message) => /falhou em/.test(message)), ctx.logs.join(' | '));
|
|
} finally {
|
|
globalThis.fetch = originalFetch;
|
|
delete process.env.GOOGLE_PLACES_API_KEY;
|
|
}
|
|
});
|