| 1 | import { assert, it } from "@effect/vitest"; |
| 2 | import { CommandId, ProjectId, type Project } from "@t3tools/contracts"; |
| 3 | import * as Effect from "effect/Effect"; |
| 4 | import * as Ref from "effect/Ref"; |
| 5 | |
| 6 | import { projectMutationOperation } from "./ProjectMutation.ts"; |
| 7 | import { type ProjectService } from "./ProjectService.ts"; |
| 8 | |
| 9 | const projectId = ProjectId.make("project:mutation-mapping"); |
| 10 | const 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 | |
| 26 | it.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 | ); |