import { afterEach, describe, expect, it } from "bun:test"; import { ProjectDataClient, projectDataQuerySchema } from "../../src/product/project-data-client.js"; import { bindSessionCredential, releaseSessionCredential } from "../../src/product/credential-refresh.js"; import { historyChart } from "../../src/product/history-chart.js"; const originalFetch = globalThis.fetch; afterEach(() => { globalThis.fetch = originalFetch; releaseSessionCredential("query-session"); }); describe("controlled project queries", () => { it("dispatches SCADA history to the existing fields endpoint using monitored_value", async () => { bindSessionCredential("query-session", { authorization: "Bearer TEST", projectId: "trusted-project" }); globalThis.fetch = (async (url) => { const parsed = new URL(String(url)); expect(parsed.pathname).toBe("/api/v1/timeseries/scada-readings/fields"); expect(parsed.searchParams.get("field")).toBe("monitored_value"); expect(parsed.searchParams.get("device_ids")).toBe("D1,D2"); return Response.json([{ device_id: "D1", value: 20 }]); }) as typeof fetch; await new ProjectDataClient("http://server.test").query("query-session", { source: "scada_history", device_ids: ["D1", "D2"], field: "monitored_value", start: "2026-09-01T00:00:00Z", end: "2026-09-01T01:00:00Z" }); }); it("uses fixed routes and the bound project credential, including encoded asset IDs", async () => { bindSessionCredential("query-session", { authorization: "Bearer TEST", projectId: "trusted-project" }); let requested: URL | undefined; globalThis.fetch = (async (url, init) => { requested = new URL(String(url)); expect(new Headers(init?.headers).get("X-Project-Id")).toBe("trusted-project"); return Response.json({ pressure: 20 }); }) as typeof fetch; expect(await new ProjectDataClient("http://server.test").query("query-session", { source: "element_properties", element_type: "junction", element_id: "J&network=other" })).toEqual({ pressure: 20 }); expect(requested?.pathname).toBe("/api/v1/junctions/properties"); expect(requested?.searchParams.get("junction")).toBe("J&network=other"); expect(requested?.searchParams.has("network")).toBeFalse(); }); it("rejects arbitrary routes, project overrides and excessive time windows before dispatch", () => { expect(projectDataQuerySchema.safeParse({ source: "scada_devices", project_id: "other" }).success).toBeFalse(); expect(projectDataQuerySchema.safeParse({ source: "http", url: "http://other" }).success).toBeFalse(); expect(projectDataQuerySchema.safeParse({ source: "scada_history", device_ids: ["D1"], field: "monitored_value", start: "2026-09-01T00:00:00Z", end: "2026-09-03T00:00:00Z" }).success).toBeFalse(); }); it("keeps returned units, drops null samples and never manufactures missing history", () => { expect(() => historyChart({ series: [] }, "J1")).toThrow("NO_HISTORY_DATA"); const chart = historyChart({ series: [{ element_id: "J1", metric: "pressure", source: "scada_raw", source_unit: "m", points: [{ time: "t1", value: null }, { time: "t2", value: 20 }] }] }, "J1"); expect(chart.title).toContain("(m)"); expect(chart.x_data).toEqual(["t2"]); expect(chart.series[0].data).toEqual([20]); }); });