doatlas-2 / artifacts /api-server /src /lib /__tests__ /dispatch-validation.test.ts
Iostream-Li's picture
Add files using upload-large-folder tool
ff78003 verified
/**
* Task #242 (B1) — 漏洞 2 修复测试:dispatchNetworkTool ajv input_contract 校验。
*
* 需要 DATABASE_URL,跳过否则。
*
* 4 cases:
* 1. 合法 args → matched=true、result OK,无 violations 行
* 2. 缺必填字段 → INPUT_CONTRACT_VIOLATION,1 violations 行,enforce=true
* 3. 类型错 → INPUT_CONTRACT_VIOLATION
* 4. additionalProperties=false 时多余字段 → INPUT_CONTRACT_VIOLATION
*
* + observed_only 模式 case:DOATLAS_INPUT_CONTRACT_ENFORCE=false 时
* 即使违规也继续派发(行写但 mode='observed_only')。
*/
import { test } from "node:test";
import assert from "node:assert/strict";
import { eq } from "drizzle-orm";
const SKIP = !process.env["DATABASE_URL"];
test(
"dispatch-validation: ajv input_contract enforced + violations table",
{ skip: SKIP && "DATABASE_URL not set" },
async (t) => {
const { db, toolNetworks, networkVersions, networkDispatchViolations } =
await import("@workspace/db");
const tn = await import("../tool-network.ts");
const { newId } = await import("../ids.ts");
const networkName = `b1_validation_${Math.random().toString(16).slice(2, 8)}`;
const inputContract = {
type: "object",
required: ["q", "top_k"],
properties: {
q: { type: "string" },
top_k: { type: "integer", minimum: 1, maximum: 100 },
},
additionalProperties: false,
};
// seed network + active variant
const networkId = newId("tnet");
const variantId = newId("nver");
await db.insert(toolNetworks).values({
id: networkId,
name: networkName,
problemClassPath: "ligand.screening.binding",
description: "B1 dispatch-validation fixture",
inputContract,
outputContract: { type: "object" },
internalGraph: { nodes: ["echo"] },
capabilityTags: [],
builderModelTier: "strong",
status: "active",
});
await db.insert(networkVersions).values({
id: variantId,
networkId,
versionLabel: "v1",
internalGraph: { nodes: ["echo"] },
config: {},
status: "active",
builtBy: "system",
builderModelTier: "strong",
});
await db
.update(toolNetworks)
.set({ activeVariantId: variantId })
.where(eq(toolNetworks.id, networkId));
// Register a no-op runner so legal-arg case can succeed
tn.registerNetworkRunner(networkName, async () => ({
output: { ok: true },
steps: [],
durationMs: 1,
}));
// Force enforce mode for deterministic test
const prev = process.env["DOATLAS_INPUT_CONTRACT_ENFORCE"];
process.env["DOATLAS_INPUT_CONTRACT_ENFORCE"] = "true";
t.after(async () => {
if (prev === undefined) delete process.env["DOATLAS_INPUT_CONTRACT_ENFORCE"];
else process.env["DOATLAS_INPUT_CONTRACT_ENFORCE"] = prev;
await db.delete(networkDispatchViolations).where(eq(networkDispatchViolations.networkName, networkName));
await db.update(toolNetworks).set({ activeVariantId: null }).where(eq(toolNetworks.id, networkId));
await db.delete(networkVersions).where(eq(networkVersions.id, variantId));
await db.delete(toolNetworks).where(eq(toolNetworks.id, networkId));
});
await t.test("legal args → matched, no violation row", async () => {
const r = await tn.dispatchNetworkTool(
`run_${networkName}`,
{ q: "egfr", top_k: 10 },
undefined,
0,
);
assert.equal(r.matched, true);
assert.equal(r.error, undefined);
const rows = await db
.select()
.from(networkDispatchViolations)
.where(eq(networkDispatchViolations.networkName, networkName));
assert.equal(rows.length, 0, "no violation row for legal args");
});
await t.test("missing required field → INPUT_CONTRACT_VIOLATION", async () => {
const r = await tn.dispatchNetworkTool(
`run_${networkName}`,
{ q: "egfr" }, // missing top_k
undefined,
0,
);
assert.equal(r.matched, true);
assert.ok(r.error, "must report error");
assert.equal(r.error!.error_code, "INPUT_CONTRACT_VIOLATION");
assert.match(r.error!.error, /top_k|required/);
});
await t.test("wrong type → INPUT_CONTRACT_VIOLATION", async () => {
const r = await tn.dispatchNetworkTool(
`run_${networkName}`,
{ q: 123, top_k: 5 },
undefined,
0,
);
assert.equal(r.matched, true);
assert.equal(r.error?.error_code, "INPUT_CONTRACT_VIOLATION");
});
await t.test("additional property → INPUT_CONTRACT_VIOLATION", async () => {
const r = await tn.dispatchNetworkTool(
`run_${networkName}`,
{ q: "x", top_k: 10, extra: true },
undefined,
0,
);
assert.equal(r.matched, true);
assert.equal(r.error?.error_code, "INPUT_CONTRACT_VIOLATION");
});
await t.test("violations rows accumulate", async () => {
const rows = await db
.select()
.from(networkDispatchViolations)
.where(eq(networkDispatchViolations.networkName, networkName));
assert.ok(rows.length >= 3, `expected ≥3 violations, got ${rows.length}`);
assert.ok(rows.every((r) => r.mode === "enforced"));
});
await t.test("observed_only mode falls through and still dispatches", async () => {
process.env["DOATLAS_INPUT_CONTRACT_ENFORCE"] = "false";
const r = await tn.dispatchNetworkTool(
`run_${networkName}`,
{ q: 999, top_k: "no" }, // both wrong types
undefined,
0,
);
assert.equal(r.matched, true);
// observed_only → runner still ran successfully
assert.equal(r.error, undefined);
assert.ok(r.result, "runner result returned in observed_only mode");
const rows = await db
.select()
.from(networkDispatchViolations)
.where(eq(networkDispatchViolations.networkName, networkName));
assert.ok(rows.some((r) => r.mode === "observed_only"));
});
},
);