Files
next-tjwater-agent/tests/product/projectDataClient.test.ts
T
jiang 7383ed6d13 feat(agent): migrate OpenCode 2 runtime and sandbox tools
Upgrade the runtime and native plugin API to OpenCode 2.0.24. Restore
session-scoped learning, credential renewal, typed project queries and
controlled frontend actions from the legacy Agent.

Replace host Shell execution with an approved Landlock/seccomp executor
and persistent per-session workspaces. Add typed Bocha search and Tianditu
geocoding bridges using the owning session credential. Require a working
kernel sandbox before startup and persist workspace files in deployments.

Validation completed before this commit: TypeScript checks, 150 Bun tests,
Docker build and UID 10001 sandbox execution, native Shell approval and
session recovery. Tianditu succeeded; Bocha currently reports exhausted
account quota. Unrelated investigation-report contract work is excluded.
2026-10-08 15:46:37 +08:00

46 lines
3.2 KiB
TypeScript

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]);
});
});