import { afterEach, describe, expect, it } from "bun:test"; import { ExternalInformationClient, geocodeInput, webSearchInput } from "../../src/product/external-information-client.js"; import { bindSessionCredential, credentialRefreshCoordinator, refreshSessionCredential, releaseSessionCredential } from "../../src/product/credential-refresh.js"; const originalFetch = globalThis.fetch; const session = "external-information-test"; afterEach(() => { globalThis.fetch = originalFetch; releaseSessionCredential(session); }); describe("typed external information", () => { it("uses the fixed Server endpoint and trusted project, translating freshness", async () => { bindSessionCredential(session, { authorization: "Bearer TEST", projectId: "trusted" }); globalThis.fetch = (async (url: Parameters[0], init?: RequestInit) => { expect(String(url)).toBe("http://server.test/api/v1/web-searches"); expect(new Headers(init?.headers).get("Authorization")).toBe("Bearer TEST"); expect(new Headers(init?.headers).get("X-Project-Id")).toBe("trusted"); expect(JSON.parse(String(init?.body))).toEqual({ query: "供水政策", freshness: "oneWeek", summary: true, count: 10, include: ["gov.cn"] }); return Response.json({ data: { webPages: { value: [{ url: "https://gov.cn/example", name: "example" }] } } }); }) as unknown as typeof fetch; const result = await new ExternalInformationClient("http://server.test").webSearch(session, webSearchInput.parse({ query: "供水政策", freshness: "one_week", include: ["gov.cn"] })); expect(result).toHaveProperty("data.webPages.value"); }); it("normalizes valid Tianditu coordinates for zoom_to_map and rejects fabricated coordinates", async () => { bindSessionCredential(session, { authorization: "Bearer TEST", projectId: "trusted" }); globalThis.fetch = (async (url: Parameters[0], init?: RequestInit) => { expect(new URL(String(url)).pathname).toBe("/api/v1/geocoding-requests"); expect(JSON.parse(String(init?.body))).toEqual({ keyword: "北京市人民政府" }); return Response.json({ status: "0", location: { lon: "116.4", lat: "39.9", level: 1 } }); }) as unknown as typeof fetch; expect(await new ExternalInformationClient("http://server.test").geocode(session, { keyword: "北京市人民政府" })).toMatchObject({ source_crs: "EPSG:4326", center: [116.4, 39.9], location: { lon: 116.4, lat: 39.9 } }); for (const data of [null, { status: "0", location: { lon: "", lat: " " } }, { status: "1" }, { status: "0", location: { lon: 181, lat: 39 } }, { status: "0", location: { lon: null, lat: null } }]) { globalThis.fetch = (async () => Response.json(data)) as unknown as typeof fetch; await expect(new ExternalInformationClient("http://server.test").geocode(session, { keyword: "北京市人民政府" })).rejects.toThrow("no valid coordinate"); } }); it("renews a rejected credential once and never turns a 403 into a renewal", async () => { bindSessionCredential(session, { authorization: "Bearer OLD", projectId: "trusted" }); let requests = 0; const unsubscribe = credentialRefreshCoordinator.subscribe(session, (event) => { if (event.type === "credential_refresh_required") refreshSessionCredential(session, { authorization: "Bearer NEW", projectId: "trusted" }, event.requestId); }); try { globalThis.fetch = (async (_url: Parameters[0], init?: RequestInit) => { requests++; return new Headers(init?.headers).get("Authorization") === "Bearer OLD" ? new Response(null, { status: 401 }) : Response.json({ status: "0", location: { lon: 116, lat: 39 } }); }) as unknown as typeof fetch; await new ExternalInformationClient("http://server.test").geocode(session, { keyword: "北京" }); expect(requests).toBe(2); globalThis.fetch = (async () => { requests++; return new Response(null, { status: 403 }); }) as unknown as typeof fetch; await expect(new ExternalInformationClient("http://server.test").geocode(session, { keyword: "北京" })).rejects.toMatchObject({ status: 403 }); expect(requests).toBe(3); } finally { unsubscribe(); } }); it("rejects scope, URL overrides, empty queries and invalid counts before dispatch", async () => { expect(webSearchInput.safeParse({ query: "test", url: "http://untrusted" }).success).toBeFalse(); expect(webSearchInput.safeParse({ query: "test", project_id: "other" }).success).toBeFalse(); expect(webSearchInput.safeParse({ query: "test", count: 51 }).success).toBeFalse(); expect(webSearchInput.safeParse({ query: "test", include: ["https://example.com/path"] }).success).toBeFalse(); expect(geocodeInput.safeParse({ keyword: " " }).success).toBeFalse(); await expect(new ExternalInformationClient().geocode(session, { keyword: "北京" })).rejects.toMatchObject({ status: 401 }); }); });