feat: project demo lifecycle facts
This commit is contained in:
@@ -0,0 +1,58 @@
|
|||||||
|
import { describe, expect, it, vi } from "vitest";
|
||||||
|
import { loadCanonicalDemoRecording } from "../demo/timeline/replay.js";
|
||||||
|
import { initialDemoTimelineState } from "../demo/timeline/reducer.js";
|
||||||
|
import type { DemoTimelineController } from "../demo/useDemoTimeline.js";
|
||||||
|
import { projectDemoLifecycleFacts } from "./demo-lifecycle-facts.js";
|
||||||
|
|
||||||
|
const controller = (): DemoTimelineController => {
|
||||||
|
const recording = loadCanonicalDemoRecording();
|
||||||
|
return {
|
||||||
|
state: {
|
||||||
|
...initialDemoTimelineState,
|
||||||
|
mode: "replay",
|
||||||
|
phase: "paused",
|
||||||
|
events: recording.events,
|
||||||
|
appliedCount: recording.events.length,
|
||||||
|
autoplay: false,
|
||||||
|
},
|
||||||
|
inFlight: false,
|
||||||
|
interruptPayload: null,
|
||||||
|
output: null,
|
||||||
|
trace: null,
|
||||||
|
missingDeploymentMessage: null,
|
||||||
|
recordingId: recording.recordingId,
|
||||||
|
canStart: true,
|
||||||
|
setMode: vi.fn(),
|
||||||
|
start: vi.fn(),
|
||||||
|
pause: vi.fn(),
|
||||||
|
play: vi.fn(),
|
||||||
|
next: vi.fn(async () => {}),
|
||||||
|
submitSelectedIssues: vi.fn(async () => {}),
|
||||||
|
cancelReview: vi.fn(async () => {}),
|
||||||
|
restart: vi.fn(),
|
||||||
|
primeReplayToStage: vi.fn(),
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
describe("projectDemoLifecycleFacts", () => {
|
||||||
|
it("projects prepared draft context without pretending it was runtime evidence", () => {
|
||||||
|
const facts = projectDemoLifecycleFacts(controller());
|
||||||
|
expect(facts.draft.label).toBe("lda report workflow");
|
||||||
|
expect(facts.draft.source).toContain("examples/lda_report_workflow");
|
||||||
|
expect(facts.draft.status).toBe("prepared context");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("projects artifact and deployment facts from deployment inspect evidence", () => {
|
||||||
|
const facts = projectDemoLifecycleFacts(controller());
|
||||||
|
expect(facts.artifact).toEqual({ id: "lda_report_case_study", version: 1 });
|
||||||
|
expect(facts.deployment.id).toBe("lda_report_case_study.default");
|
||||||
|
expect(facts.deployment.driftPolicy).toBe("block");
|
||||||
|
expect(facts.deployment.bindings).toContainEqual(["local.lda_docs", "local.lda_docs"]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("projects run readiness from the run start event", () => {
|
||||||
|
const facts = projectDemoLifecycleFacts(controller());
|
||||||
|
expect(facts.run.id).toBe("run_recorded_lda_report");
|
||||||
|
expect(facts.run.status).toBe("interrupted");
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,81 @@
|
|||||||
|
import type { DemoTimelineController } from "../demo/useDemoTimeline.js";
|
||||||
|
|
||||||
|
export type DemoLifecycleFacts = {
|
||||||
|
readonly draft: {
|
||||||
|
readonly label: string;
|
||||||
|
readonly source: string;
|
||||||
|
readonly status: string;
|
||||||
|
};
|
||||||
|
readonly artifact: {
|
||||||
|
readonly id: string;
|
||||||
|
readonly version: number | null;
|
||||||
|
};
|
||||||
|
readonly deployment: {
|
||||||
|
readonly id: string;
|
||||||
|
readonly driftPolicy: string;
|
||||||
|
readonly bindings: ReadonlyArray<readonly [string, string]>;
|
||||||
|
};
|
||||||
|
readonly run: {
|
||||||
|
readonly id: string | null;
|
||||||
|
readonly status: string;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const deploymentInspect = (demo: DemoTimelineController) =>
|
||||||
|
demo.state.events.find((event) => event.stage === "deployment_check");
|
||||||
|
|
||||||
|
const runStart = (demo: DemoTimelineController) =>
|
||||||
|
demo.state.events.find((event) => event.stage === "run_start");
|
||||||
|
|
||||||
|
const readBindings = (value: unknown): ReadonlyArray<readonly [string, string]> => {
|
||||||
|
if (!Array.isArray(value)) return [];
|
||||||
|
return value.flatMap((entry) => {
|
||||||
|
if (!Array.isArray(entry) || entry.length !== 2) return [];
|
||||||
|
const [from, to] = entry;
|
||||||
|
return typeof from === "string" && typeof to === "string" ? [[from, to] as const] : [];
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Presentation-only lifecycle facts. Draft context is prepared example context;
|
||||||
|
* artifact/deployment/run facts come from replay evidence when available.
|
||||||
|
*/
|
||||||
|
export const projectDemoLifecycleFacts = (demo: DemoTimelineController): DemoLifecycleFacts => {
|
||||||
|
const deployment = deploymentInspect(demo);
|
||||||
|
const deploymentInterpreted = deployment?.interpreted as
|
||||||
|
| {
|
||||||
|
id?: string;
|
||||||
|
artifactId?: string;
|
||||||
|
artifactVersion?: number;
|
||||||
|
driftPolicy?: string;
|
||||||
|
bindings?: unknown;
|
||||||
|
}
|
||||||
|
| undefined;
|
||||||
|
const run = runStart(demo);
|
||||||
|
const runInterpreted = run?.interpreted as
|
||||||
|
| { runId?: string; status?: string }
|
||||||
|
| undefined;
|
||||||
|
|
||||||
|
return {
|
||||||
|
draft: {
|
||||||
|
label: "lda report workflow",
|
||||||
|
source: "examples/lda_report_workflow",
|
||||||
|
status: "prepared context",
|
||||||
|
},
|
||||||
|
artifact: {
|
||||||
|
id: deploymentInterpreted?.artifactId ?? "unavailable",
|
||||||
|
version: typeof deploymentInterpreted?.artifactVersion === "number"
|
||||||
|
? deploymentInterpreted.artifactVersion
|
||||||
|
: null,
|
||||||
|
},
|
||||||
|
deployment: {
|
||||||
|
id: deploymentInterpreted?.id ?? "unavailable",
|
||||||
|
driftPolicy: deploymentInterpreted?.driftPolicy ?? "unavailable",
|
||||||
|
bindings: readBindings(deploymentInterpreted?.bindings),
|
||||||
|
},
|
||||||
|
run: {
|
||||||
|
id: runInterpreted?.runId ?? run?.resultingIds.runId ?? null,
|
||||||
|
status: runInterpreted?.status ?? "not started",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
};
|
||||||
Reference in New Issue
Block a user