import { describe, it, expect, vi, afterEach } from "vitest"; import { aresLookupByIco, aresSearchByName } from "../services/ares.service"; // ARES is a true external — mock global fetch (the only allowed mock class). const SUBJECT = { ico: "22599851", obchodniJmeno: "BOHA Automation s.r.o.", dic: "CZ22599851", sidlo: { nazevStatu: "Česká republika", nazevObce: "Turnov", nazevCastiObce: "Turnov", nazevUlice: "Nádražní", cisloDomovni: 485, psc: 51101, textovaAdresa: "Nádražní 485, 51101 Turnov", }, }; function mockFetchOnce(status: number, body?: unknown) { return vi.spyOn(globalThis, "fetch").mockResolvedValueOnce({ ok: status >= 200 && status < 300, status, json: async () => body, } as Response); } afterEach(() => { vi.restoreAllMocks(); }); describe("aresLookupByIco", () => { it("maps a subject to the normalized prefill shape", async () => { mockFetchOnce(200, SUBJECT); const res = await aresLookupByIco("22599851"); expect(res).toEqual({ ico: "22599851", dic: "CZ22599851", name: "BOHA Automation s.r.o.", street: "Nádražní 485", city: "Turnov", postal_code: "511 01", country: "Česká republika", }); }); it("builds Prague-style orientation numbers as 'Ulice 485/12a'", async () => { mockFetchOnce(200, { ...SUBJECT, sidlo: { ...SUBJECT.sidlo, cisloOrientacni: 12, cisloOrientacniPismeno: "a", }, }); const res = await aresLookupByIco("22599851"); expect("street" in res && res.street).toBe("Nádražní 485/12a"); }); it("rejects a non-8-digit IČO without calling ARES", async () => { const spy = vi.spyOn(globalThis, "fetch"); expect(await aresLookupByIco("123")).toEqual({ error: "invalid_ico" }); expect(await aresLookupByIco("abcdefgh")).toEqual({ error: "invalid_ico", }); expect(spy).not.toHaveBeenCalled(); }); it("accepts an IČO with stray whitespace", async () => { const spy = mockFetchOnce(200, SUBJECT); const res = await aresLookupByIco(" 225 99851 "); expect("ico" in res && res.ico).toBe("22599851"); expect(String(spy.mock.calls[0][0])).toContain("/22599851"); }); it("maps 404 to not_found and network failure to ares_unavailable", async () => { mockFetchOnce(404); expect(await aresLookupByIco("12345678")).toEqual({ error: "not_found" }); vi.spyOn(globalThis, "fetch").mockRejectedValueOnce(new Error("ETIMEDOUT")); expect(await aresLookupByIco("12345678")).toEqual({ error: "ares_unavailable", }); }); }); describe("aresSearchByName", () => { it("maps the result list and sends the name in the POST body", async () => { const spy = mockFetchOnce(200, { ekonomickeSubjekty: [SUBJECT] }); const res = await aresSearchByName("BOHA Automation"); expect(Array.isArray(res)).toBe(true); if (Array.isArray(res)) { expect(res).toHaveLength(1); expect(res[0].name).toBe("BOHA Automation s.r.o."); expect(res[0].postal_code).toBe("511 01"); } const init = spy.mock.calls[0][1] as RequestInit; expect(JSON.parse(String(init.body))).toMatchObject({ obchodniJmeno: "BOHA Automation", pocet: 10, }); }); it("returns [] for a blank query without calling ARES", async () => { const spy = vi.spyOn(globalThis, "fetch"); expect(await aresSearchByName(" ")).toEqual([]); expect(spy).not.toHaveBeenCalled(); }); it("maps upstream failure to ares_unavailable", async () => { mockFetchOnce(503); expect(await aresSearchByName("BOHA")).toEqual({ error: "ares_unavailable", }); }); });