test(gsd): add regression tests for wave 5 consistency fixes
Tests isClosedStatus coverage for projections, upsertDecision seq preservation (ON CONFLICT DO UPDATE vs INSERT OR REPLACE), and event schema versioning (v:2 field in new events).
This commit is contained in:
parent
5c9ee8f10d
commit
d7ce14a50a
1 changed files with 107 additions and 0 deletions
|
|
@ -0,0 +1,107 @@
|
|||
// GSD State Machine — Wave 5 Consistency Regression Tests
|
||||
// Validates isClosedStatus usage in projections, upsertDecision seq preservation,
|
||||
// and event schema versioning.
|
||||
|
||||
import { describe, test } from "node:test";
|
||||
import assert from "node:assert/strict";
|
||||
import { mkdtempSync, rmSync } from "node:fs";
|
||||
import { join } from "node:path";
|
||||
import { tmpdir } from "node:os";
|
||||
import { isClosedStatus } from "../status-guards.js";
|
||||
import { openDatabase, closeDatabase, upsertDecision, _getAdapter } from "../gsd-db.js";
|
||||
|
||||
// ── Fix 19: isClosedStatus covers all closed statuses ──
|
||||
|
||||
describe("isClosedStatus used by projections", () => {
|
||||
test("skipped is closed (projections now show checked)", () => {
|
||||
assert.ok(isClosedStatus("skipped"));
|
||||
});
|
||||
test("complete is closed", () => {
|
||||
assert.ok(isClosedStatus("complete"));
|
||||
});
|
||||
test("done is closed", () => {
|
||||
assert.ok(isClosedStatus("done"));
|
||||
});
|
||||
test("in-progress is not closed", () => {
|
||||
assert.ok(!isClosedStatus("in-progress"));
|
||||
});
|
||||
});
|
||||
|
||||
// ── Fix 20: upsertDecision preserves seq on update ──
|
||||
|
||||
describe("upsertDecision preserves seq column", () => {
|
||||
test("seq is preserved when decision is re-upserted", () => {
|
||||
const tmp = mkdtempSync(join(tmpdir(), "gsd-upsert-test-"));
|
||||
const dbPath = join(tmp, "gsd.db");
|
||||
try {
|
||||
openDatabase(dbPath);
|
||||
const adapter = _getAdapter();
|
||||
assert.ok(adapter, "adapter must be available");
|
||||
|
||||
// Insert two decisions
|
||||
upsertDecision({
|
||||
id: "D001", when_context: "ctx1", scope: "s1",
|
||||
decision: "d1", choice: "c1", rationale: "r1",
|
||||
revisable: "yes", made_by: "agent", superseded_by: null,
|
||||
});
|
||||
upsertDecision({
|
||||
id: "D002", when_context: "ctx2", scope: "s2",
|
||||
decision: "d2", choice: "c2", rationale: "r2",
|
||||
revisable: "yes", made_by: "agent", superseded_by: null,
|
||||
});
|
||||
|
||||
// Get original seq values
|
||||
const rows1 = adapter.prepare("SELECT id, seq FROM decisions ORDER BY seq").all() as Array<{ id: string; seq: number }>;
|
||||
assert.strictEqual(rows1[0].id, "D001");
|
||||
assert.strictEqual(rows1[1].id, "D002");
|
||||
const d001OriginalSeq = rows1[0].seq;
|
||||
|
||||
// Re-upsert D001 with updated content
|
||||
upsertDecision({
|
||||
id: "D001", when_context: "updated", scope: "s1",
|
||||
decision: "d1-updated", choice: "c1", rationale: "r1",
|
||||
revisable: "yes", made_by: "agent", superseded_by: null,
|
||||
});
|
||||
|
||||
// Verify seq is preserved (not moved to end)
|
||||
const rows2 = adapter.prepare("SELECT id, seq FROM decisions ORDER BY seq").all() as Array<{ id: string; seq: number }>;
|
||||
assert.strictEqual(rows2[0].id, "D001", "D001 should still be first by seq");
|
||||
assert.strictEqual(rows2[0].seq, d001OriginalSeq, "D001 seq should be preserved");
|
||||
assert.strictEqual(rows2[1].id, "D002", "D002 should still be second");
|
||||
|
||||
// Verify content was updated
|
||||
const updated = adapter.prepare("SELECT decision FROM decisions WHERE id = 'D001'").get() as { decision: string };
|
||||
assert.strictEqual(updated.decision, "d1-updated");
|
||||
|
||||
closeDatabase();
|
||||
} finally {
|
||||
rmSync(tmp, { recursive: true, force: true });
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// ── Fix 23: Event schema versioning ──
|
||||
|
||||
describe("WorkflowEvent v field", () => {
|
||||
test("appendEvent includes v:2 in output", async () => {
|
||||
const tmp = mkdtempSync(join(tmpdir(), "gsd-event-v-test-"));
|
||||
try {
|
||||
const { appendEvent } = await import("../workflow-events.js");
|
||||
appendEvent(tmp, {
|
||||
cmd: "test-event",
|
||||
params: { foo: "bar" },
|
||||
ts: new Date().toISOString(),
|
||||
actor: "system",
|
||||
});
|
||||
|
||||
const { readFileSync } = await import("node:fs");
|
||||
const logPath = join(tmp, ".gsd", "event-log.jsonl");
|
||||
const line = readFileSync(logPath, "utf-8").trim();
|
||||
const event = JSON.parse(line);
|
||||
assert.strictEqual(event.v, 2, "New events should have v:2");
|
||||
assert.strictEqual(event.cmd, "test-event");
|
||||
} finally {
|
||||
rmSync(tmp, { recursive: true, force: true });
|
||||
}
|
||||
});
|
||||
});
|
||||
Loading…
Add table
Reference in a new issue