Move project and host queries into native MCP with host-owned OAuth/context, while retaining memory, skills, actions and publication plugins. Add nearby-model queries and explicit chat/task/learning model defaults. Adopt Responses HTTP/SSE and native steer/queue inbox delivery, preserving scoped session ownership, pending input edits and investigation communication. Disable local-provider discovery: Server /health allowed native vLLM to probe /v1/models on port 8000. A native before/after regression prevents recurrence. Validation: 170 focused Bun tests; service/plugin typechecks; native discovery, provider-discovery and input-inbox smoke checks; no paid model inference.
64 lines
3.3 KiB
TypeScript
64 lines
3.3 KiB
TypeScript
import { describe, expect, it } from "bun:test";
|
|
import { historyChart, queryHistoryChart } from "../../src/product/history-chart.js";
|
|
describe("history chart", () => {
|
|
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]);
|
|
});
|
|
});
|
|
|
|
const series = { element_id: "J1", metric: "pressure", source: "raw", source_unit: "m", device_id: "D1" };
|
|
it("continues past a null first page to use real later samples", async () => {
|
|
const pages: number[][] = [];
|
|
const chart = await queryHistoryChart("J1", async (limit, offset) => {
|
|
pages.push([limit, offset]);
|
|
return { content: [], structuredContent: { page: { limit, offset, has_more: offset === 0 }, truncated: offset === 0,
|
|
data: { series: [{ ...series, points: offset === 0 ? Array.from({ length: limit }, (_, i) => ({ time: String(i), value: null })) : [{ time: "500", value: 25 }] }] } } };
|
|
});
|
|
expect(pages).toEqual([[500, 0], [500, 500]]);
|
|
expect(chart.series[0].data).toEqual([25]);
|
|
});
|
|
|
|
it("stops within a fixed budget and distinguishes unread history from absent history", async () => {
|
|
let calls = 0;
|
|
await expect(queryHistoryChart("J1", async (limit, offset) => {
|
|
calls++;
|
|
return { content: [], structuredContent: { page: { limit, offset, has_more: true }, data: { series: [{ ...series, points: [{ time: String(offset), value: null }] }] } } };
|
|
})).rejects.toThrow("仍有未读取数据");
|
|
expect(calls).toBe(2);
|
|
});
|
|
|
|
it("shrinks oversized pages without skipping data or retrying unrelated failures", async () => {
|
|
const calls: number[][] = [];
|
|
const chart = await queryHistoryChart("J1", async (limit, offset) => {
|
|
calls.push([limit, offset]);
|
|
if (limit > 125) return { isError: true, content: [], structuredContent: { error: { code: "RESULT_TOO_LARGE" } } };
|
|
return { content: [], structuredContent: { page: { limit, offset, has_more: offset === 0 }, data: {
|
|
series: [{ ...series, points: [{ time: String(offset), value: offset === 0 ? null : 25 }] }],
|
|
} } };
|
|
});
|
|
expect(calls).toEqual([[500, 0], [250, 0], [125, 0], [125, 125]]);
|
|
expect(chart.series[0].data).toEqual([25]);
|
|
let failures = 0;
|
|
await expect(queryHistoryChart("J1", async () => {
|
|
failures++;
|
|
return { isError: true, content: [], structuredContent: { error: { code: "ACCESS_DENIED" } } };
|
|
})).rejects.toThrow("历史查询暂不可用");
|
|
expect(failures).toBe(1);
|
|
});
|
|
|
|
it("bounds displayed values and identifies a partial query", async () => {
|
|
let calls = 0;
|
|
const chart = await queryHistoryChart("J1", async (limit, offset) => {
|
|
calls++;
|
|
return { content: [], structuredContent: { page: { limit, offset, has_more: true },
|
|
data: { series: [{ ...series, points: Array.from({ length: 100 }, (_, i) => ({ time: String(i), value: i })) }] } } };
|
|
});
|
|
expect(calls).toBe(1);
|
|
expect(chart.series[0].data).toHaveLength(100);
|
|
expect(chart.title).toContain("未覆盖完整时间窗");
|
|
});
|