singularity-forge/src/resources/extensions/gsd/tests/smart-entry-draft.test.ts
ace-pm 172753c3b2 refactor(forge): complete gsd → forge rebrand across native, logging, and build system
- Rename native Rust crates: gsd-engine → forge-engine, gsd-ast → forge-ast, gsd-grep → forge-grep
- Update all crate dependencies (Cargo.toml, .rs source) and N-API artifacts
- Mass rename log prefix [gsd] → [forge] across 81 files (scripts, src/, extensions, tests)
- Rename log prefix "gsd-db:" → "forge-db:" in template literals
- Update nix flake: add sf-run-native devShell with Rust toolchain for native addon builds
- Update CI workflow artifact names (build-native.yml)
- Verify only packages/native/* touched (no upstream pi-* packages renamed)

Rationale: Complete gsd-2 → singularity-forge rebrand (2026-04-15). Native addon is
sf-run-specific; all gsd-prefixed logging and crate names must align with new identity.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 14:11:45 +02:00

123 lines
4.1 KiB
TypeScript

import { mkdtempSync, mkdirSync, rmSync, writeFileSync, readFileSync } from "node:fs";
import { join } from "node:path";
import { tmpdir } from "node:os";
import { deriveState } from "../state.js";
import { resolveMilestoneFile } from "../paths.js";
let passed = 0;
let failed = 0;
function assert(condition: boolean, message: string): void {
if (condition) {
passed++;
} else {
failed++;
console.error(` FAIL: ${message}`);
}
}
// ─── Fixture: milestone with only CONTEXT-DRAFT.md ──────────────────────
const tmpBase = mkdtempSync(join(tmpdir(), "gsd-smart-entry-draft-test-"));
const gsd = join(tmpBase, ".gsd");
mkdirSync(join(gsd, "milestones", "M001"), { recursive: true });
const draftContent = `# M001: Test Milestone — Context\n\n**Status:** Draft\n\nSeed material from a prior discussion.\n`;
writeFileSync(
join(gsd, "milestones", "M001", "M001-CONTEXT-DRAFT.md"),
draftContent,
);
// ─── Test: deriveState returns 'needs-discussion' for draft-only milestone ───
const state = await deriveState(tmpBase);
assert(
state.phase === "needs-discussion",
`phase should be 'needs-discussion' for draft-only milestone, got: "${state.phase}"`,
);
assert(
state.activeMilestone?.id === "M001",
`active milestone should be M001, got: "${state.activeMilestone?.id}"`,
);
// ─── Test: resolveMilestoneFile resolves CONTEXT-DRAFT ─────────────────────
const draftFile = resolveMilestoneFile(tmpBase, "M001", "CONTEXT-DRAFT");
assert(
draftFile !== null && draftFile !== undefined,
`resolveMilestoneFile should resolve CONTEXT-DRAFT, got: ${draftFile}`,
);
assert(
draftFile!.endsWith("M001-CONTEXT-DRAFT.md"),
`resolved path should end with M001-CONTEXT-DRAFT.md, got: "${draftFile}"`,
);
// ─── Test: CONTEXT.md is NOT resolved (only draft exists) ──────────────────
const contextFile = resolveMilestoneFile(tmpBase, "M001", "CONTEXT");
assert(
contextFile === null || contextFile === undefined,
`resolveMilestoneFile should NOT resolve CONTEXT when only CONTEXT-DRAFT exists, got: "${contextFile}"`,
);
// ─── Static: guided-flow.ts has 'needs-discussion' branch ─────────────────
const guidedFlowSource = readFileSync(
join(import.meta.dirname, "..", "guided-flow.ts"),
"utf-8",
);
assert(
guidedFlowSource.includes('state.phase === "needs-discussion"'),
"guided-flow.ts should have 'needs-discussion' phase check in showWorkflowEntry",
);
// Check the branch has draft-aware menu options
const branchIdx = guidedFlowSource.indexOf('state.phase === "needs-discussion"');
const branchChunk = guidedFlowSource.slice(branchIdx, branchIdx + 4000);
assert(
branchChunk.includes("discuss_draft"),
"needs-discussion branch should have 'discuss_draft' option",
);
assert(
branchChunk.includes("discuss_fresh"),
"needs-discussion branch should have 'discuss_fresh' option",
);
assert(
branchChunk.includes("skip_milestone"),
"needs-discussion branch should have 'skip_milestone' option",
);
assert(
branchChunk.includes("CONTEXT-DRAFT"),
"needs-discussion branch should load CONTEXT-DRAFT via resolveMilestoneFile",
);
assert(
branchChunk.includes("Draft Seed") || branchChunk.includes("draftContent"),
"discuss_draft path should include draft content as seed in the dispatched prompt",
);
assert(
branchChunk.includes("return"),
"needs-discussion branch should return early (not fall through to generic no-roadmap menu)",
);
// ─── Cleanup ──────────────────────────────────────────────────────────────
rmSync(tmpBase, { recursive: true, force: true });
// ─── Results ──────────────────────────────────────────────────────────────
console.log(`\nsmart-entry-draft: ${passed} passed, ${failed} failed`);
if (failed > 0) process.exit(1);