import { CommandId, EventId, ProjectId, type OrchestrationEvent } from "@t3tools/contracts";import { expect, it } from "@effect/vitest";import * as Effect from "effect/Effect";import * as NodeServices from "@effect/platform-node/NodeServices"; import { decideOrchestrationCommand } from "./decider.ts";import { createEmptyReadModel, projectEvent } from "./projector.ts"; const now = "2026-01-01T00:00:00.000Z";const projectId = ProjectId.make("project-env-mode"); const seedProjectCreated = (sequence: number): OrchestrationEvent => ({ sequence, eventId: EventId.make(`evt-project-env-mode-${sequence}`), aggregateKind: "project", aggregateId: projectId, type: "project.created", occurredAt: now, commandId: CommandId.make(`cmd-project-env-mode-${sequence}`), causationEventId: null, correlationId: CommandId.make(`cmd-project-env-mode-${sequence}`), metadata: {}, payload: { projectId, title: "Env mode", workspaceRoot: "/tmp/env-mode", defaultModelSelection: null, scripts: [], createdAt: now, updatedAt: now, },}); it.layer(NodeServices.layer)("decider project defaultThreadEnvMode", (it) => { it.effect("propagates defaultThreadEnvMode through meta.update into the read model", () => Effect.gen(function* () { const readModel = yield* projectEvent(createEmptyReadModel(now), seedProjectCreated(1)); expect(readModel.projects[0]?.defaultThreadEnvMode).toBeNull(); const result = yield* decideOrchestrationCommand({ command: { type: "project.meta.update", commandId: CommandId.make("cmd-project-env-mode-set"), projectId, defaultThreadEnvMode: "worktree", }, readModel, }); const event = Array.isArray(result) ? result[0] : result; expect(event.type).toBe("project.meta-updated"); expect((event.payload as { defaultThreadEnvMode?: unknown }).defaultThreadEnvMode).toBe( "worktree", ); const updated = yield* projectEvent(readModel, { ...event, sequence: 2 }); expect(updated.projects[0]?.defaultThreadEnvMode).toBe("worktree"); }), ); it.effect("omits the field when unset and clears it on explicit null", () => Effect.gen(function* () { const readModel = yield* projectEvent(createEmptyReadModel(now), seedProjectCreated(1)); const unrelated = yield* decideOrchestrationCommand({ command: { type: "project.meta.update", commandId: CommandId.make("cmd-project-env-mode-title"), projectId, title: "Renamed", }, readModel, }); const unrelatedEvent = Array.isArray(unrelated) ? unrelated[0] : unrelated; expect("defaultThreadEnvMode" in (unrelatedEvent.payload as object)).toBe(false); const set = yield* decideOrchestrationCommand({ command: { type: "project.meta.update", commandId: CommandId.make("cmd-project-env-mode-set"), projectId, defaultThreadEnvMode: "worktree", }, readModel, }); const setEvent = Array.isArray(set) ? set[0] : set; const afterSet = yield* projectEvent(readModel, { ...setEvent, sequence: 2 }); const clear = yield* decideOrchestrationCommand({ command: { type: "project.meta.update", commandId: CommandId.make("cmd-project-env-mode-clear"), projectId, defaultThreadEnvMode: null, }, readModel: afterSet, }); const clearEvent = Array.isArray(clear) ? clear[0] : clear; const afterClear = yield* projectEvent(afterSet, { ...clearEvent, sequence: 3 }); expect(afterClear.projects[0]?.defaultThreadEnvMode).toBeNull(); }), );});
import { CommandId, EventId, ProjectId, type OrchestrationEvent } from "@t3tools/contracts";
import { expect, it } from "@effect/vitest";
import * as Effect from "effect/Effect";
import * as NodeServices from "@effect/platform-node/NodeServices";
import { decideOrchestrationCommand } from "./decider.ts";
import { createEmptyReadModel, projectEvent } from "./projector.ts";
const now = "2026-01-01T00:00:00.000Z";
const projectId = ProjectId.make("project-env-mode");
const seedProjectCreated = (sequence: number): OrchestrationEvent => ({
sequence,
eventId: EventId.make(`evt-project-env-mode-${sequence}`),
aggregateKind: "project",
aggregateId: projectId,
type: "project.created",
occurredAt: now,
commandId: CommandId.make(`cmd-project-env-mode-${sequence}`),
causationEventId: null,
correlationId: CommandId.make(`cmd-project-env-mode-${sequence}`),
metadata: {},
payload: {
projectId,
title: "Env mode",
workspaceRoot: "/tmp/env-mode",
defaultModelSelection: null,
scripts: [],
createdAt: now,
updatedAt: now,
},
});
it.layer(NodeServices.layer)("decider project defaultThreadEnvMode", (it) => {
it.effect("propagates defaultThreadEnvMode through meta.update into the read model", () =>
Effect.gen(function* () {
const readModel = yield* projectEvent(createEmptyReadModel(now), seedProjectCreated(1));
expect(readModel.projects[0]?.defaultThreadEnvMode).toBeNull();
const result = yield* decideOrchestrationCommand({
command: {
type: "project.meta.update",
commandId: CommandId.make("cmd-project-env-mode-set"),
projectId,
defaultThreadEnvMode: "worktree",
},
readModel,
});
const event = Array.isArray(result) ? result[0] : result;
expect(event.type).toBe("project.meta-updated");
expect((event.payload as { defaultThreadEnvMode?: unknown }).defaultThreadEnvMode).toBe(
"worktree",
);
const updated = yield* projectEvent(readModel, { ...event, sequence: 2 });
expect(updated.projects[0]?.defaultThreadEnvMode).toBe("worktree");
}),
);
it.effect("omits the field when unset and clears it on explicit null", () =>
Effect.gen(function* () {
const readModel = yield* projectEvent(createEmptyReadModel(now), seedProjectCreated(1));
const unrelated = yield* decideOrchestrationCommand({
command: {
type: "project.meta.update",
commandId: CommandId.make("cmd-project-env-mode-title"),
projectId,
title: "Renamed",
},
readModel,
});
const unrelatedEvent = Array.isArray(unrelated) ? unrelated[0] : unrelated;
expect("defaultThreadEnvMode" in (unrelatedEvent.payload as object)).toBe(false);
const set = yield* decideOrchestrationCommand({
command: {
type: "project.meta.update",
commandId: CommandId.make("cmd-project-env-mode-set"),
projectId,
defaultThreadEnvMode: "worktree",
},
readModel,
});
const setEvent = Array.isArray(set) ? set[0] : set;
const afterSet = yield* projectEvent(readModel, { ...setEvent, sequence: 2 });
const clear = yield* decideOrchestrationCommand({
command: {
type: "project.meta.update",
commandId: CommandId.make("cmd-project-env-mode-clear"),
projectId,
defaultThreadEnvMode: null,
},
readModel: afterSet,
});
const clearEvent = Array.isArray(clear) ? clear[0] : clear;
const afterClear = yield* projectEvent(afterSet, { ...clearEvent, sequence: 3 });
expect(afterClear.projects[0]?.defaultThreadEnvMode).toBeNull();
}),
);
});