1import { assert, it } from "@effect/vitest";
2import { CommandId, ProjectId, type Project } from "@t3tools/contracts";
3import * as Effect from "effect/Effect";
4import * as Ref from "effect/Ref";
5
6import { projectMutationOperation } from "./ProjectMutation.ts";
7import { type ProjectService } from "./ProjectService.ts";
8
9const projectId = ProjectId.make("project:mutation-mapping");
10const project = {
11 id: projectId,
12 title: "Mapping",
13 workspaceRoot: "/work/mapping",
14 repositoryIdentity: null,
15 faviconPath: null,
16 projectIcon: null,
17 defaultModelSelection: null,
18 defaultThreadEnvMode: null,
19 autoPull: false,
20 scripts: [],
21 createdAt: "2026-09-05T00:00:00.000Z",
22 updatedAt: "2026-09-05T00:00:00.000Z",
23 deletedAt: null,
24} satisfies Project;
25
26it.effect("preserves every project mutation field", () =>
27 Effect.gen(function* () {
28 const calls = yield* Ref.make<ReadonlyArray<unknown>>([]);
29 const projects: Pick<ProjectService["Service"], "create" | "delete" | "update"> = {
30 create: (input) =>
31 Ref.update(calls, (entries) => [...entries, input]).pipe(Effect.as(project)),
32 update: (input) =>
33 Ref.update(calls, (entries) => [...entries, input]).pipe(Effect.as(project)),
34 delete: (input) =>
35 Ref.update(calls, (entries) => [...entries, input]).pipe(Effect.as(project)),
36 };
37
38 yield* projectMutationOperation(projects, {
39 type: "project.create",
40 commandId: CommandId.make("command:create"),
41 projectId,
42 title: "Created",
43 workspaceRoot: "/work/created",
44 createWorkspaceRootIfMissing: true,
45 defaultModelSelection: null,
46 scripts: [],
47 });
48 yield* projectMutationOperation(projects, {
49 type: "project.update",
50 commandId: CommandId.make("command:update"),
51 projectId,
52 title: "Updated",
53 workspaceRoot: "/work/updated",
54 defaultModelSelection: null,
55 autoPull: false,
56 projectIcon: null,
57 faviconPath: null,
58 defaultThreadEnvMode: null,
59 scripts: [],
60 });
61 yield* projectMutationOperation(projects, {
62 type: "project.delete",
63 commandId: CommandId.make("command:delete"),
64 projectId,
65 force: true,
66 });
67
68 assert.deepEqual(yield* Ref.get(calls), [
69 {
70 commandId: "command:create",
71 projectId,
72 title: "Created",
73 workspaceRoot: "/work/created",
74 createWorkspaceRootIfMissing: true,
75 defaultModelSelection: null,
76 scripts: [],
77 },
78 {
79 commandId: "command:update",
80 projectId,
81 title: "Updated",
82 workspaceRoot: "/work/updated",
83 defaultModelSelection: null,
84 autoPull: false,
85 projectIcon: null,
86 faviconPath: null,
87 defaultThreadEnvMode: null,
88 scripts: [],
89 },
90 { commandId: "command:delete", projectId, force: true },
91 ]);
92 }),
93);