Skip to content
Source
Sign inSign up

ProjectStore.test.ts

100 lines3.8 KB

1import { assert, it } from "@effect/vitest";
2import { EventId, ProjectId, ProviderInstanceId } from "@t3tools/contracts";
3import * as Effect from "effect/Effect";
4import * as Layer from "effect/Layer";
5import * as Option from "effect/Option";
6import * as SqlClient from "effect/sql/SqlClient";
7​
8import * as SqlitePersistence from "../persistence/Sqlite.ts";
9import * as ProjectStore from "./ProjectStore.ts";
10​
11it.layer(ProjectStore.layer.pipe(Layer.provideMerge(SqlitePersistence.layerMemory)))(
12 "ProjectStoreV2",
13 (it) => {
14 it.effect("stores a model selection without options as JSON without an options key", () =>
15 Effect.gen(function* () {
16 const projects = yield* ProjectStore.ProjectStoreV2;
17 const sql = yield* SqlClient.SqlClient;
18 const projectId = ProjectId.make("project-null-options");
19 const modelSelection = { instanceId: ProviderInstanceId.make("codex"), model: "gpt-5.4" };
20 yield* projects.apply({
21 sequence: 1,
22 eventId: EventId.make("event-null-options"),
23 aggregateKind: "project",
24 aggregateId: projectId,
25 occurredAt: "2026-03-24T00:00:00.000Z",
26 commandId: null,
27 causationEventId: null,
28 correlationId: null,
29 metadata: {},
30 type: "project.created",
31 payload: {
32 projectId,
33 title: "Null options project",
34 workspaceRoot: "/tmp/project-null-options",
35 defaultModelSelection: modelSelection,
36 scripts: [],
37 createdAt: "2026-03-24T00:00:00.000Z",
38 updatedAt: "2026-03-24T00:00:00.000Z",
39 },
40 });
41​
42 const rows = yield* sql<{ readonly defaultModelSelection: string | null }>`
43 SELECT default_model_selection_json AS "defaultModelSelection"
44 FROM projection_projects
45 WHERE project_id = ${projectId}
46 `;
47 assert.strictEqual(rows[0]?.defaultModelSelection, JSON.stringify(modelSelection));
48 assert.deepStrictEqual(
49 Option.getOrNull(yield* projects.get(projectId))?.defaultModelSelection,
50 modelSelection,
51 );
52 }),
53 );
54​
55 it.effect("keeps pin and archive times across later updates", () =>
56 Effect.gen(function* () {
57 const projects = yield* ProjectStore.ProjectStoreV2;
58 const projectId = ProjectId.make("project-organized");
59 const at = "2026-03-24T00:00:00.000Z";
60 const event = { aggregateKind: "project", aggregateId: projectId, occurredAt: at } as const;
61 const meta = { commandId: null, causationEventId: null, correlationId: null, metadata: {} };
62 yield* projects.apply({
63 ...event,
64 ...meta,
65 sequence: 1,
66 eventId: EventId.make("event-organized-created"),
67 type: "project.created",
68 payload: {
69 projectId,
70 title: "Organized",
71 workspaceRoot: "/tmp/project-organized",
72 defaultModelSelection: null,
73 scripts: [],
74 createdAt: at,
75 updatedAt: at,
76 },
77 });
78 yield* projects.apply({
79 ...event,
80 ...meta,
81 sequence: 2,
82 eventId: EventId.make("event-organized-pinned"),
83 type: "project.meta-updated",
84 payload: { projectId, pinnedAt: at, archivedAt: at, updatedAt: at },
85 });
86 yield* projects.apply({
87 ...event,
88 ...meta,
89 sequence: 3,
90 eventId: EventId.make("event-organized-renamed"),
91 type: "project.meta-updated",
92 payload: { projectId, title: "Renamed", updatedAt: at },
93 });
94 const project = Option.getOrNull(yield* projects.get(projectId));
95 assert.strictEqual(project?.pinnedAt, at);
96 assert.strictEqual(project?.archivedAt, at);
97 }),
98 );
99 },
100);