feat(P06): integration \u0026 hardening — version 0.8.0, agent tests, E2E, docs, fallbacks
---ci---
project: ci
phase: 6
milestone: v0.8
status: complete
decisions:
- id: D-037
decision: v0.8.0 release with 6 phases complete
rationale: All verification layers now deliver what they claim
confidence: 0.95
requirements:
covered: [INT-01, INT-02, INT-03, INT-04, INT-05, INT-06, INT-07, INT-08]
---/ci---
INT-06: Version bumped to 0.8.0 in package.json and src/version.ts.
INT-07: New test suites for SecurityAuditorAgent (5 tests), DocWriterAgent
(5 tests), DebuggerAgent (5 tests), ChallengerAgent (4 tests).
INT-08: Zod validation test suite with 9 cases: valid input, missing
fields, path traversal, absolute paths, contradictory success+error,
invalid operation, negative tokens, fail+error, emptyBackendResult.
INT-04: ciagent review command now has mechanical fallback — runs
CodeReviewerAgent regex review without backend.
INT-05: ciagent debug command now has mechanical fallback — runs
DebuggerAgent stack trace parsing + git bisect without backend.
INT-01: E2E verification test — fixture with defects fails L3/L4; clean
project passes all 4 layers.
INT-02: AGENTS.md updated — removed 'not yet implemented' caveats for
L2/L3/L4; updated test count to 44 suites, 454 tests.
INT-03: PROJECT.md updated — removed Out of Scope for STRIDE,
multi-persona review, and behavioral test generation.
This commit is contained in:
@@ -0,0 +1,129 @@
|
||||
import { validateBackendResult, BackendResultSchema, emptyBackendResult } from "../backends/types.js";
|
||||
|
||||
describe("BackendResult Zod Validation", () => {
|
||||
it("accepts valid BackendResult", () => {
|
||||
const valid = {
|
||||
success: true,
|
||||
output: "Task completed",
|
||||
artifacts: [{ path: "src/app.ts", content: "export const x = 1;", operation: "create" as const }],
|
||||
decisions: [],
|
||||
escalations: [],
|
||||
usage: { input_tokens: 100, output_tokens: 50, total_tokens: 150, estimated_cost_usd: 0.01 },
|
||||
};
|
||||
|
||||
const result = validateBackendResult(valid);
|
||||
expect(result.result).not.toBeNull();
|
||||
expect(result.errors).toHaveLength(0);
|
||||
expect(result.result?.success).toBe(true);
|
||||
});
|
||||
|
||||
it("rejects BackendResult missing success field", () => {
|
||||
const invalid = {
|
||||
output: "Task completed",
|
||||
artifacts: [],
|
||||
decisions: [],
|
||||
escalations: [],
|
||||
usage: { input_tokens: 100, output_tokens: 50, total_tokens: 150, estimated_cost_usd: 0.01 },
|
||||
};
|
||||
|
||||
const result = validateBackendResult(invalid);
|
||||
expect(result.result).toBeNull();
|
||||
expect(result.errors.length).toBeGreaterThan(0);
|
||||
});
|
||||
|
||||
it("rejects artifact with path traversal", () => {
|
||||
const malicious = {
|
||||
success: true,
|
||||
output: "ok",
|
||||
artifacts: [{ path: "../../etc/shadow", content: "pwned", operation: "create" as const }],
|
||||
decisions: [],
|
||||
escalations: [],
|
||||
usage: { input_tokens: 0, output_tokens: 0, total_tokens: 0, estimated_cost_usd: 0 },
|
||||
};
|
||||
|
||||
const result = validateBackendResult(malicious);
|
||||
expect(result.result).toBeNull();
|
||||
expect(result.errors.some((e) => e.includes("path traversal"))).toBe(true);
|
||||
});
|
||||
|
||||
it("rejects artifact with absolute path", () => {
|
||||
const malicious = {
|
||||
success: true,
|
||||
output: "ok",
|
||||
artifacts: [{ path: "/etc/passwd", content: "", operation: "create" as const }],
|
||||
decisions: [],
|
||||
escalations: [],
|
||||
usage: { input_tokens: 0, output_tokens: 0, total_tokens: 0, estimated_cost_usd: 0 },
|
||||
};
|
||||
|
||||
const result = validateBackendResult(malicious);
|
||||
expect(result.result).toBeNull();
|
||||
expect(result.errors.some((e) => e.includes("absolute"))).toBe(true);
|
||||
});
|
||||
|
||||
it("rejects success=true with error message", () => {
|
||||
const contradictory = {
|
||||
success: true,
|
||||
output: "ok",
|
||||
artifacts: [],
|
||||
decisions: [],
|
||||
escalations: [],
|
||||
usage: { input_tokens: 0, output_tokens: 0, total_tokens: 0, estimated_cost_usd: 0 },
|
||||
error: "Something went wrong",
|
||||
};
|
||||
|
||||
const result = validateBackendResult(contradictory);
|
||||
expect(result.result).toBeNull();
|
||||
expect(result.errors.some((e) => e.includes("success") && e.includes("error"))).toBe(true);
|
||||
});
|
||||
|
||||
it("rejects invalid artifact operation", () => {
|
||||
const invalid = {
|
||||
success: true,
|
||||
output: "ok",
|
||||
artifacts: [{ path: "a.ts", content: "", operation: "explode" }],
|
||||
decisions: [],
|
||||
escalations: [],
|
||||
usage: { input_tokens: 0, output_tokens: 0, total_tokens: 0, estimated_cost_usd: 0 },
|
||||
};
|
||||
|
||||
const result = validateBackendResult(invalid);
|
||||
expect(result.result).toBeNull();
|
||||
});
|
||||
|
||||
it("rejects negative token usage", () => {
|
||||
const invalid = {
|
||||
success: true,
|
||||
output: "ok",
|
||||
artifacts: [],
|
||||
decisions: [],
|
||||
escalations: [],
|
||||
usage: { input_tokens: -10, output_tokens: 0, total_tokens: 0, estimated_cost_usd: 0 },
|
||||
};
|
||||
|
||||
const result = validateBackendResult(invalid);
|
||||
expect(result.result).toBeNull();
|
||||
});
|
||||
|
||||
it("accepts empty success=false with error", () => {
|
||||
const fail = {
|
||||
success: false,
|
||||
output: "",
|
||||
artifacts: [],
|
||||
decisions: [],
|
||||
escalations: [],
|
||||
usage: { input_tokens: 0, output_tokens: 0, total_tokens: 0, estimated_cost_usd: 0 },
|
||||
error: "Connection refused",
|
||||
};
|
||||
|
||||
const result = validateBackendResult(fail);
|
||||
expect(result.result).not.toBeNull();
|
||||
expect(result.result?.success).toBe(false);
|
||||
});
|
||||
|
||||
it("emptyBackendResult returns success=false", () => {
|
||||
const result = emptyBackendResult("test error");
|
||||
expect(result.success).toBe(false);
|
||||
expect(result.error).toBe("test error");
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user