Files
fusion/packages/engine/src/__tests__/required-workflow-artifacts.test.ts
gsxdsm cb57093d03 refactor: domain folder layout (types, API, core, engine) (#2398)
## Summary

Wave 17 organizes Fusion into **domain folders** (stacks on #2397).

### Layout
- **core/types/** — board, task, agents, settings, merge, workflow,
mesh, …
- **core/src/** — agents, ai, async-stores, workflows, tasks, config,
db, …
- **dashboard/app/api/** — client, tasks, agents, git, missions,
planning, …
- **engine/src/** — agents, auth, execution, merge, missions, overseer,
worktree, …

Root keepers retained for large entrypoints (`store.ts`, `executor.ts`,
`merger.ts`, …).

Public barrels (`@fusion/core`, `@fusion/engine`, `app/api.ts` → legacy)
stay stable.

## Test plan
- [x] `@fusion/core` typecheck
- [x] `@fusion/engine` typecheck (pre-existing playwright-core noise
only)
- [ ] CI merge gate

**Stack:** #2394 → #2397 → **this PR**
2026-08-03 00:20:53 -07:00

55 lines
2.4 KiB
TypeScript

import { describe, expect, it } from "vitest";
import type { WorkflowIr } from "@fusion/core";
import {
parseRequiredArtifactMissingValue,
requiredArtifactMissingValue,
isRequiredArtifactReadFailedValue,
requiredArtifactReadFailedValue,
workflowEntryArtifacts,
} from "../execution/required-workflow-artifacts.js";
// FNXC:WorkflowArtifacts 2026-07-21-17:00: This suite locks the typed missing/read
// distinction and the non-empty planning/step-source contract used by every gate.
describe("required workflow artifact contracts", () => {
it("treats planning-owned and step-source declarations as workflow-entry inputs", () => {
const ir = {
version: "v2",
name: "artifact inputs",
columns: [],
nodes: [],
edges: [],
artifacts: [
{ key: "PROMPT.md", producedBy: "planning", role: "step-source" },
{ key: "manual-context", producedBy: "manual", role: "context" },
{ key: "steps", role: "step-source" },
],
} as unknown as WorkflowIr;
expect(workflowEntryArtifacts(ir).map((artifact) => artifact.key)).toEqual(["PROMPT.md", "steps"]);
});
it("keeps storage-read failures distinct from confirmed missing artifacts", () => {
const value = requiredArtifactReadFailedValue("PROMPT.md");
expect(value).toBe("required-artifact-read-failed:PROMPT.md");
expect(isRequiredArtifactReadFailedValue(value)).toBe(true);
expect(parseRequiredArtifactMissingValue(value)).toBeNull();
});
it("round-trips a deduplicated typed missing-artifact failure", () => {
const value = requiredArtifactMissingValue([" PROMPT.md ", "PROMPT.md", "steps"]);
expect(value).toBe('required-artifact-missing:["PROMPT.md","steps"]');
expect(parseRequiredArtifactMissingValue(value)).toEqual(["PROMPT.md", "steps"]);
expect(parseRequiredArtifactMissingValue("required-artifact-missing:PROMPT.md,steps")).toEqual(["PROMPT.md", "steps"]);
expect(parseRequiredArtifactMissingValue("failed")).toBeNull();
});
it("round-trips artifact keys containing the legacy delimiter", () => {
const value = requiredArtifactMissingValue(["plans,primary.md", "steps"]);
expect(parseRequiredArtifactMissingValue(value)).toEqual(["plans,primary.md", "steps"]);
});
it("rejects an empty missing-artifact payload instead of emitting an unparsable value", () => {
expect(() => requiredArtifactMissingValue(["", " "])).toThrow("At least one required artifact key is needed");
});
});