import assert from "node:assert/strict"; import { existsSync, mkdirSync, mkdtempSync, readFileSync } from "node:fs"; import { tmpdir } from "node:os"; import path from "node:path"; import test, { before } from "node:test"; /** Rota POST /api/cache/sync e gravação concorrente no cache JSON, em pasta temporária. */ let POST: (req: import("next/server").NextRequest) => Promise; let NextRequestCtor: typeof import("next/server").NextRequest; let JsonCacheManager: typeof import("../lib/jsonCacheManager.ts").JsonCacheManager; let cacheFile: string; before(async () => { const dir = mkdtempSync(path.join(tmpdir(), "nutriscan-sync-")); mkdirSync(path.join(dir, "data")); cacheFile = path.join(dir, "data", "products-cache.json"); process.chdir(dir); console.log = () => {}; console.error = () => {}; ({ POST } = await import("../app/api/cache/sync/route.ts")); ({ NextRequest: NextRequestCtor } = await import("next/server")); ({ JsonCacheManager } = await import("../lib/jsonCacheManager.ts")); }); async function post(body: string) { const req = new NextRequestCtor("http://localhost/api/cache/sync", { method: "POST", headers: { "Content-Type": "application/json" }, body, }); const res = await POST(req); return { status: res.status, body: (await res.json()) as Record }; } const readCache = () => JSON.parse(readFileSync(cacheFile, "utf-8")) as { products: Record }; test("corpo que não é JSON, sem products ou com products fora de lista responde 400", async () => { for (const body of ["não é json", "{}", "null", '{"products":"x"}', '{"products":{"a":1}}']) { const { status, body: json } = await post(body); assert.equal(status, 400, body); assert.equal(json.error, "Produtos inválidos"); } }); test("lista vazia é aceita", async () => { const { status, body } = await post('{"products":[]}'); assert.equal(status, 200); assert.deepEqual(body, { success: true, saved: 0, failed: 0 }); }); test("só produtos com code numérico são gravados; os demais contam como falha", async () => { const products = [ { code: "7622210575975", product_name: "Válido" }, { code: "__proto__", product_name: "Chave perigosa" }, { code: "abc", product_name: "Não numérico" }, { code: 123, product_name: "Número" }, { product_name: "Sem code" }, null, "texto", ]; const { status, body } = await post(JSON.stringify({ products })); assert.equal(status, 200); assert.deepEqual(body, { success: true, saved: 1, failed: 6 }); assert.deepEqual(Object.keys(readCache().products), ["7622210575975"]); }); test("gravações simultâneas não corrompem o arquivo nem deixam temporário", async () => { const manager = new JsonCacheManager(); const batches = Array.from({ length: 25 }, (_, b) => Array.from({ length: 40 }, (_, i) => ({ code: String(1000 + b * 40 + i), product_name: `Produto ${b}-${i}` })) ); await Promise.all(batches.map((batch) => manager.saveProducts(batch))); const saved = readCache(); assert.equal(Object.keys(saved.products).length, 1000 + 1); assert.equal(existsSync(`${cacheFile}.tmp`), false); });