import { Effect } from "effect"; import { describe, expect, it } from "vitest"; import { SmokeContractError, advanceSmokeLoop, decodeSmokeChatText, decodeSmokeModelCatalog, decodeSmokePlan, decodeSmokeReview, decodeSmokeWorkerReport, initialSmokeLoopState, redactSmokeText, selectSmokeModelRoles, validateSmokeCapabilities, } from "./smoke"; const validRoleSelection = () => ({ plannerReviewerModel: "glm-5.2", plannerReviewerProvider: "cheaptricks", workerModel: "minimax-m3", workerProvider: "cheaptricks", workerToolCallMode: "serial" as const, }); const run = (effect: Effect.Effect): A => Effect.runSync(effect); describe("smoke primitives", () => { it("selects the planner/reviewer and serial M3 worker roles", () => { const roles = run(selectSmokeModelRoles(validRoleSelection())); expect(roles.worker).toMatchObject({ model: "minimax-m3", role: "worker", toolCallMode: "serial", }); expect(roles.plannerReviewer).toMatchObject({ model: "glm-5.2", role: "planner-reviewer", }); }); it("rejects a non-M3 worker or non-GLM planner", () => { const worker = validRoleSelection(); worker.workerModel = "glm-5.2"; const workerError = run(Effect.flip(selectSmokeModelRoles(worker))); expect(workerError).toBeInstanceOf(SmokeContractError); expect(workerError.reason).toBe("InvalidModelRole"); const planner = validRoleSelection(); planner.plannerReviewerModel = "minimax-m3"; const plannerError = run(Effect.flip(selectSmokeModelRoles(planner))); expect(plannerError.reason).toBe("InvalidModelRole"); }); it("advances only through the serial smoke loop", () => { let state = initialSmokeLoopState(); state = run(advanceSmokeLoop(state, "preflight-passed")); state = run(advanceSmokeLoop(state, "plan-validated")); state = run(advanceSmokeLoop(state, "worker-succeeded")); state = run(advanceSmokeLoop(state, "review-approved")); expect(state.phase).toBe("completed"); expect(state.history).toEqual([ "preflight", "planning", "worker-running", "reviewing", "completed", ]); }); it("turns contract failures into a terminal state", () => { const state = run( advanceSmokeLoop( initialSmokeLoopState(), "contract-failed", "daemon is offline" ) ); expect(state.phase).toBe("failed"); expect(state.lastError).toBe("daemon is offline"); }); it("rejects out-of-order loop transitions", () => { const error = run( Effect.flip(advanceSmokeLoop(initialSmokeLoopState(), "review-approved")) ); expect(error).toBeInstanceOf(SmokeContractError); expect(error.reason).toBe("InvalidTransition"); }); it("validates model reports at the primitive boundary", () => { const plan = run( decodeSmokePlan({ acceptanceCriteria: ["The button renders"], nonGoals: [], steps: ["Add the button", "Run the web check"], summary: "Add a small button to the disposable project", }) ); const worker = run( decodeSmokeWorkerReport({ status: "completed", summary: "Changed the project and ran the targeted check.", }) ); const review = run( decodeSmokeReview({ approved: true, findings: [], summary: "The change matches the acceptance criteria.", }) ); expect(plan.steps).toHaveLength(2); expect(worker.status).toBe("completed"); expect(review.approved).toBe(true); }); it("rejects empty or malformed model output", () => { const empty = run( Effect.flip( decodeSmokeChatText({ choices: [{ message: { content: " " } }] }) ) ); expect(empty.reason).toBe("InvalidModelResponse"); const malformed = run(Effect.flip(decodeSmokeReview({ approved: true }))); expect(malformed.reason).toBe("InvalidReport"); }); it("validates the canonical CPA model catalog", () => { const catalog = run( decodeSmokeModelCatalog({ data: [{ id: "minimax-m3" }, { id: "glm-5.2" }], }) ); expect(catalog.data.map(({ id }) => id)).toEqual(["minimax-m3", "glm-5.2"]); }); it("fails a capability report when any required check fails", () => { const error = run( Effect.flip( validateSmokeCapabilities([ { id: "web", message: "ready", passed: true }, { id: "daemon", message: "daemon is offline", passed: false }, ]) ) ); expect(error.reason).toBe("InvalidReport"); expect(error.message).toContain("daemon is offline"); }); it("redacts secrets without changing unrelated text", () => { expect( redactSmokeText("status token=secret-key endpoint=ok", ["secret-key"]) ).toBe("status token=[REDACTED] endpoint=ok"); }); });