import { describe, expect, it } from "vitest";import { MessageId, CommandId, DEFAULT_PROVIDER_INTERACTION_MODE, ProjectId, ThreadId, type OrchestrationCommand, type OrchestrationReadModel,} from "@t3tools/contracts";import { Effect } from "effect"; import { findThreadById, listThreadsByProjectId, requireNonNegativeInteger, requireThread, requireThreadAbsent,} from "./commandInvariants.ts"; const now = new Date().toISOString(); const readModel: OrchestrationReadModel = { snapshotSequence: 2, updatedAt: now, projects: [ { id: ProjectId.make("project-a"), title: "Project A", workspaceRoot: "/tmp/project-a", defaultModelSelection: { provider: "codex", model: "gpt-5-codex", }, scripts: [], createdAt: now, updatedAt: now, deletedAt: null, }, { id: ProjectId.make("project-b"), title: "Project B", workspaceRoot: "/tmp/project-b", defaultModelSelection: { provider: "codex", model: "gpt-5-codex", }, scripts: [], createdAt: now, updatedAt: now, deletedAt: null, }, ], threads: [ { id: ThreadId.make("thread-1"), projectId: ProjectId.make("project-a"), title: "Thread A", modelSelection: { provider: "codex", model: "gpt-5-codex", }, interactionMode: DEFAULT_PROVIDER_INTERACTION_MODE, runtimeMode: "full-access", branch: null, worktreePath: null, createdAt: now, updatedAt: now, archivedAt: null, latestTurn: null, messages: [], session: null, activities: [], proposedPlans: [], checkpoints: [], deletedAt: null, }, { id: ThreadId.make("thread-2"), projectId: ProjectId.make("project-b"), title: "Thread B", modelSelection: { provider: "codex", model: "gpt-5-codex", }, interactionMode: DEFAULT_PROVIDER_INTERACTION_MODE, runtimeMode: "full-access", branch: null, worktreePath: null, createdAt: now, updatedAt: now, archivedAt: null, latestTurn: null, messages: [], session: null, activities: [], proposedPlans: [], checkpoints: [], deletedAt: null, }, ],}; const messageSendCommand: OrchestrationCommand = { type: "thread.turn.start", commandId: CommandId.make("cmd-1"), threadId: ThreadId.make("thread-1"), message: { messageId: MessageId.make("msg-1"), role: "user", text: "hello", attachments: [], }, interactionMode: DEFAULT_PROVIDER_INTERACTION_MODE, runtimeMode: "approval-required", createdAt: now,}; describe("commandInvariants", () => { it("finds threads by id and project", () => { expect(findThreadById(readModel, ThreadId.make("thread-1"))?.projectId).toBe("project-a"); expect(findThreadById(readModel, ThreadId.make("missing"))).toBeUndefined(); expect( listThreadsByProjectId(readModel, ProjectId.make("project-b")).map((thread) => thread.id), ).toEqual([ThreadId.make("thread-2")]); }); it("requires existing thread", async () => { const thread = await Effect.runPromise( requireThread({ readModel, command: messageSendCommand, threadId: ThreadId.make("thread-1"), }), ); expect(thread.id).toBe(ThreadId.make("thread-1")); await expect( Effect.runPromise( requireThread({ readModel, command: messageSendCommand, threadId: ThreadId.make("missing"), }), ), ).rejects.toThrow("does not exist"); }); it("requires missing thread for create flows", async () => { await Effect.runPromise( requireThreadAbsent({ readModel, command: { type: "thread.create", commandId: CommandId.make("cmd-2"), threadId: ThreadId.make("thread-3"), projectId: ProjectId.make("project-a"), title: "new", modelSelection: { provider: "codex", model: "gpt-5-codex", }, interactionMode: DEFAULT_PROVIDER_INTERACTION_MODE, runtimeMode: "full-access", branch: null, worktreePath: null, createdAt: now, }, threadId: ThreadId.make("thread-3"), }), ); await expect( Effect.runPromise( requireThreadAbsent({ readModel, command: { type: "thread.create", commandId: CommandId.make("cmd-3"), threadId: ThreadId.make("thread-1"), projectId: ProjectId.make("project-a"), title: "dup", modelSelection: { provider: "codex", model: "gpt-5-codex", }, interactionMode: DEFAULT_PROVIDER_INTERACTION_MODE, runtimeMode: "full-access", branch: null, worktreePath: null, createdAt: now, }, threadId: ThreadId.make("thread-1"), }), ), ).rejects.toThrow("already exists"); }); it("requires non-negative integers", async () => { await Effect.runPromise( requireNonNegativeInteger({ commandType: "thread.checkpoint.revert", field: "turnCount", value: 0, }), ); await expect( Effect.runPromise( requireNonNegativeInteger({ commandType: "thread.checkpoint.revert", field: "turnCount", value: -1, }), ), ).rejects.toThrow("greater than or equal to 0"); });});
import { describe, expect, it } from "vitest";
import {
MessageId,
CommandId,
DEFAULT_PROVIDER_INTERACTION_MODE,
ProjectId,
ThreadId,
type OrchestrationCommand,
type OrchestrationReadModel,
} from "@t3tools/contracts";
import { Effect } from "effect";
import {
findThreadById,
listThreadsByProjectId,
requireNonNegativeInteger,
requireThread,
requireThreadAbsent,
} from "./commandInvariants.ts";
const now = new Date().toISOString();
const readModel: OrchestrationReadModel = {
snapshotSequence: 2,
updatedAt: now,
projects: [
{
id: ProjectId.make("project-a"),
title: "Project A",
workspaceRoot: "/tmp/project-a",
defaultModelSelection: {
provider: "codex",
model: "gpt-5-codex",
},
scripts: [],
createdAt: now,
updatedAt: now,
deletedAt: null,
},
{
id: ProjectId.make("project-b"),
title: "Project B",
workspaceRoot: "/tmp/project-b",
defaultModelSelection: {
provider: "codex",
model: "gpt-5-codex",
},
scripts: [],
createdAt: now,
updatedAt: now,
deletedAt: null,
},
],
threads: [
{
id: ThreadId.make("thread-1"),
projectId: ProjectId.make("project-a"),
title: "Thread A",
modelSelection: {
provider: "codex",
model: "gpt-5-codex",
},
interactionMode: DEFAULT_PROVIDER_INTERACTION_MODE,
runtimeMode: "full-access",
branch: null,
worktreePath: null,
createdAt: now,
updatedAt: now,
archivedAt: null,
latestTurn: null,
messages: [],
session: null,
activities: [],
proposedPlans: [],
checkpoints: [],
deletedAt: null,
},
{
id: ThreadId.make("thread-2"),
projectId: ProjectId.make("project-b"),
title: "Thread B",
modelSelection: {
provider: "codex",
model: "gpt-5-codex",
},
interactionMode: DEFAULT_PROVIDER_INTERACTION_MODE,
runtimeMode: "full-access",
branch: null,
worktreePath: null,
createdAt: now,
updatedAt: now,
archivedAt: null,
latestTurn: null,
messages: [],
session: null,
activities: [],
proposedPlans: [],
checkpoints: [],
deletedAt: null,
},
],
};
const messageSendCommand: OrchestrationCommand = {
type: "thread.turn.start",
commandId: CommandId.make("cmd-1"),
threadId: ThreadId.make("thread-1"),
message: {
messageId: MessageId.make("msg-1"),
role: "user",
text: "hello",
attachments: [],
},
interactionMode: DEFAULT_PROVIDER_INTERACTION_MODE,
runtimeMode: "approval-required",
createdAt: now,
};
describe("commandInvariants", () => {
it("finds threads by id and project", () => {
expect(findThreadById(readModel, ThreadId.make("thread-1"))?.projectId).toBe("project-a");
expect(findThreadById(readModel, ThreadId.make("missing"))).toBeUndefined();
expect(
listThreadsByProjectId(readModel, ProjectId.make("project-b")).map((thread) => thread.id),
).toEqual([ThreadId.make("thread-2")]);
});
it("requires existing thread", async () => {
const thread = await Effect.runPromise(
requireThread({
readModel,
command: messageSendCommand,
threadId: ThreadId.make("thread-1"),
}),
);
expect(thread.id).toBe(ThreadId.make("thread-1"));
await expect(
Effect.runPromise(
requireThread({
readModel,
command: messageSendCommand,
threadId: ThreadId.make("missing"),
}),
),
).rejects.toThrow("does not exist");
});
it("requires missing thread for create flows", async () => {
await Effect.runPromise(
requireThreadAbsent({
readModel,
command: {
type: "thread.create",
commandId: CommandId.make("cmd-2"),
threadId: ThreadId.make("thread-3"),
projectId: ProjectId.make("project-a"),
title: "new",
modelSelection: {
provider: "codex",
model: "gpt-5-codex",
},
interactionMode: DEFAULT_PROVIDER_INTERACTION_MODE,
runtimeMode: "full-access",
branch: null,
worktreePath: null,
createdAt: now,
},
threadId: ThreadId.make("thread-3"),
}),
);
await expect(
Effect.runPromise(
requireThreadAbsent({
readModel,
command: {
type: "thread.create",
commandId: CommandId.make("cmd-3"),
threadId: ThreadId.make("thread-1"),
projectId: ProjectId.make("project-a"),
title: "dup",
modelSelection: {
provider: "codex",
model: "gpt-5-codex",
},
interactionMode: DEFAULT_PROVIDER_INTERACTION_MODE,
runtimeMode: "full-access",
branch: null,
worktreePath: null,
createdAt: now,
},
threadId: ThreadId.make("thread-1"),
}),
),
).rejects.toThrow("already exists");
});
it("requires non-negative integers", async () => {
await Effect.runPromise(
requireNonNegativeInteger({
commandType: "thread.checkpoint.revert",
field: "turnCount",
value: 0,
}),
);
await expect(
Effect.runPromise(
requireNonNegativeInteger({
commandType: "thread.checkpoint.revert",
field: "turnCount",
value: -1,
}),
),
).rejects.toThrow("greater than or equal to 0");
});
});