feat(m2): ship isolated task dashboard surface
This commit is contained in:
@@ -0,0 +1,68 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { SurfaceInstanceManager } from "./surface-instance-manager";
|
||||
import { SurfaceRegistry } from "./surface-registry";
|
||||
|
||||
const APP = "lineup.task-dashboard";
|
||||
const VERSION = "0.1.0";
|
||||
const CONVERSATION = "conversation_surface_001";
|
||||
const OPENED = "2026-08-03T10:30:00.000Z";
|
||||
const UPDATED = "2026-08-03T10:31:00.000Z";
|
||||
|
||||
function createManager(): { registry: SurfaceRegistry; manager: SurfaceInstanceManager } {
|
||||
const registry = new SurfaceRegistry();
|
||||
registry.enable(APP, VERSION, OPENED);
|
||||
return { registry, manager: new SurfaceInstanceManager(registry) };
|
||||
}
|
||||
|
||||
function openPayload(state: object = { title: "Build", percent: 10 }): object {
|
||||
return { instance_id: "task:001", app: { app_id: APP, version: VERSION }, state };
|
||||
}
|
||||
|
||||
describe("SurfaceInstanceManager", () => {
|
||||
it("opens only an explicitly enabled local app and makes same-instance open idempotent", () => {
|
||||
const { manager } = createManager();
|
||||
expect(manager.open(openPayload(), CONVERSATION, OPENED)).toMatchObject({ disposition: "accepted", instance: { phase: "opening", state: { title: "Build", percent: 10 } } });
|
||||
expect(manager.open(openPayload({ title: "Ignored patch" }), CONVERSATION, UPDATED)).toMatchObject({ disposition: "duplicate", instance: { state: { title: "Build", percent: 10 } } });
|
||||
});
|
||||
|
||||
it("rejects an unenabled app, an app-version mismatch, and injected bundle fields", () => {
|
||||
const registry = new SurfaceRegistry();
|
||||
const manager = new SurfaceInstanceManager(registry);
|
||||
expect(manager.open(openPayload(), CONVERSATION, OPENED).disposition).toBe("disabled");
|
||||
registry.enable(APP, VERSION, OPENED);
|
||||
expect(manager.open({ ...openPayload(), app: { app_id: APP, version: "1.0.0" } }, CONVERSATION, OPENED).disposition).toBe("version_mismatch");
|
||||
expect(manager.open({ ...openPayload(), bundle: "<script>" }, CONVERSATION, OPENED).disposition).toBe("bundle_override_forbidden");
|
||||
});
|
||||
|
||||
it("moves to ready once and permits only state replacement through patch", () => {
|
||||
const { manager } = createManager();
|
||||
manager.open(openPayload(), CONVERSATION, OPENED);
|
||||
expect(manager.ready("task:001", UPDATED)).toMatchObject({ disposition: "accepted", instance: { phase: "ready" } });
|
||||
expect(manager.ready("task:001", UPDATED).disposition).toBe("duplicate");
|
||||
expect(manager.patch("task:001", { title: "Build", percent: 60 }, UPDATED)).toMatchObject({ disposition: "accepted", instance: { state: { title: "Build", percent: 60 }, app_id: APP, version: VERSION } });
|
||||
expect(manager.patch("task:001", { date: new Date() }, UPDATED).disposition).toBe("invalid_state");
|
||||
});
|
||||
|
||||
it("closes instances and never restores disabled, malformed, or unknown snapshots", () => {
|
||||
const { registry, manager } = createManager();
|
||||
manager.open(openPayload(), CONVERSATION, OPENED);
|
||||
const snapshot = manager.snapshot();
|
||||
expect(manager.close("task:001").disposition).toBe("accepted");
|
||||
expect(manager.get("task:001")).toBeUndefined();
|
||||
manager.restore(snapshot);
|
||||
expect(manager.get("task:001")).toMatchObject({ phase: "opening", state: { title: "Build", percent: 10 } });
|
||||
registry.disable(APP, VERSION);
|
||||
manager.restore(snapshot);
|
||||
expect(manager.snapshot().instances).toEqual([]);
|
||||
manager.restore({ version: 1, instances: [{ instance_id: "bad", conversation_id: CONVERSATION, app_id: "unknown.surface", version: VERSION, state: {}, phase: "ready", opened_at: OPENED, updated_at: UPDATED }] });
|
||||
expect(manager.snapshot().instances).toEqual([]);
|
||||
});
|
||||
|
||||
it("treats a conflicting reuse of an instance id as a safe no-op", () => {
|
||||
const { manager } = createManager();
|
||||
manager.open(openPayload(), CONVERSATION, OPENED);
|
||||
expect(manager.open(openPayload(), "another_conversation", UPDATED)).toMatchObject({ disposition: "conflict", instance: { conversation_id: CONVERSATION } });
|
||||
expect(manager.patch("missing", {}, UPDATED).disposition).toBe("missing");
|
||||
expect(manager.close("missing").disposition).toBe("missing");
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user