1import { assert, it } from "@effect/vitest";
2import {
3 DEFAULT_SERVER_SETTINGS,
4 DEFAULT_MODEL,
5 ProjectId,
6 ProviderInstanceId,
7} from "@t3tools/contracts";
8import * as Deferred from "effect/Deferred";
9import * as Effect from "effect/Effect";
10import * as Exit from "effect/Exit";
11import * as Fiber from "effect/Fiber";
12import * as Ref from "effect/Ref";
13
14import * as GitVcsDriver from "./vcs/GitVcsDriver.ts";
15
16import * as ServerConfig from "./config.ts";
17import * as ServerRuntimeStartup from "./serverRuntimeStartup.ts";
18
19it("uses the canonical Codex model for auto-bootstrap", () => {
20 assert.deepEqual(ServerRuntimeStartup.getAutoBootstrapThreadModelSelection(), {
21 instanceId: ProviderInstanceId.make("codex"),
22 model: DEFAULT_MODEL,
23 });
24});
25
26it.effect("starts without scanning or rebuilding projection history", () =>
27 Effect.gen(function* () {
28 const calls = yield* Ref.make<ReadonlyArray<string>>([]);
29 const record = (label: string) => Ref.update(calls, (current) => [...current, label]);
30
31 const result = yield* ServerRuntimeStartup.runOrderedV2StartupPhases({
32 importLegacyShells: record("import"),
33 recover: record("recover").pipe(Effect.as({ closedRequests: 2 })),
34 recoverDelegatedTasks: record("delegated"),
35 startEffectWorker: record("worker"),
36 autoBootstrap: record("bootstrap").pipe(Effect.as({ projectId: "project-1" })),
37 });
38
39 // Delegated recovery reads the runs recovery terminalizes, and settles them
40 // before the worker runs restart continuations that would otherwise race it.
41 assert.deepEqual(yield* Ref.get(calls), [
42 "import",
43 "recover",
44 "delegated",
45 "worker",
46 "bootstrap",
47 ]);
48 assert.deepEqual(result, {
49 recovery: { closedRequests: 2 },
50 bootstrap: { projectId: "project-1" },
51 });
52 }),
53);
54
55it.effect("interrupts the effect worker when awareness relay startup fails", () =>
56 Effect.scoped(
57 Effect.gen(function* () {
58 const workerInterrupted = yield* Ref.make(false);
59 const workerFiberRef = yield* Ref.make<Fiber.Fiber<void, never> | null>(null);
60
61 const exit = yield* ServerRuntimeStartup.startEffectWorkerWithRelay({
62 runWorker: Effect.never.pipe(Effect.ensuring(Ref.set(workerInterrupted, true))),
63 startRelay: Effect.yieldNow.pipe(
64 Effect.andThen(Effect.die("awareness relay startup failed")),
65 ),
66 workerFiberRef,
67 }).pipe(Effect.exit);
68
69 assert.isTrue(Exit.isFailure(exit));
70 assert.isTrue(yield* Ref.get(workerInterrupted));
71 assert.isNull(yield* Ref.get(workerFiberRef));
72 }),
73 ),
74);
75
76it.effect("queues commands until startup signals readiness", () =>
77 Effect.scoped(
78 Effect.gen(function* () {
79 const gate = yield* ServerRuntimeStartup.makeCommandGate;
80 const count = yield* Ref.make(0);
81 const queued = yield* gate
82 .enqueueCommand(Ref.updateAndGet(count, (value) => value + 1))
83 .pipe(Effect.forkScoped);
84
85 yield* Effect.yieldNow;
86 assert.equal(yield* Ref.get(count), 0);
87 yield* gate.signalCommandReady;
88 assert.equal(yield* Fiber.join(queued), 1);
89 }),
90 ),
91);
92
93it.effect("enqueueCommand fails queued work when readiness fails", () =>
94 Effect.scoped(
95 Effect.gen(function* () {
96 const commandGate = yield* ServerRuntimeStartup.makeCommandGate;
97 const failure = yield* Deferred.make<void, never>();
98
99 const queuedCommandFiber = yield* commandGate
100 .enqueueCommand(Deferred.await(failure).pipe(Effect.as("should-not-run")))
101 .pipe(Effect.forkScoped);
102
103 yield* commandGate.failCommandReady(
104 new ServerRuntimeStartup.ServerRuntimeStartupError({
105 mode: "web",
106 host: "127.0.0.1",
107 port: 3773,
108 cause: new Error("test startup failure"),
109 }),
110 );
111
112 const error = yield* Effect.flip(Fiber.join(queuedCommandFiber));
113 assert.equal(error.message, "Server runtime startup failed before command readiness.");
114 }),
115 ),
116);
117
118it.effect("resolveWelcomeBase derives cwd and project name from server config", () =>
119 Effect.gen(function* () {
120 const welcome = yield* ServerRuntimeStartup.resolveWelcomeBase.pipe(
121 Effect.provideService(ServerConfig.ServerConfig, {
122 cwd: "/tmp/startup-project",
123 } as never),
124 );
125
126 assert.deepStrictEqual(welcome, {
127 cwd: "/tmp/startup-project",
128 projectName: "startup-project",
129 });
130 }),
131);
132
133it.effect("automatic pull only updates enabled, behind, clean default-branch checkouts", () =>
134 Effect.gen(function* () {
135 const pulled: string[] = [];
136 const git = {
137 statusDetails: (cwd: string) =>
138 Effect.succeed({
139 isRepo: true,
140 isDefaultBranch: cwd !== "/feature",
141 hasUpstream: true,
142 hasWorkingTreeChanges: cwd === "/dirty",
143 aheadCount: cwd === "/ahead" ? 1 : 0,
144 behindCount: cwd === "/current" ? 0 : 1,
145 } as never),
146 pullCurrentBranch: (cwd: string) =>
147 Effect.sync(() => {
148 pulled.push(cwd);
149 return {
150 status: "pulled" as const,
151 refName: "main",
152 upstreamRef: "origin/main",
153 };
154 }),
155 } as unknown as GitVcsDriver.GitVcsDriver["Service"];
156 const project = (workspaceRoot: string) =>
157 ({ id: ProjectId.make(workspaceRoot), workspaceRoot }) as never;
158 const overrides = (entries: Record<string, boolean>) => ({
159 ...DEFAULT_SERVER_SETTINGS,
160 projectSettingsOverrides: Object.fromEntries(
161 Object.entries(entries).map(([root, defaultAutoPull]) => [
162 ProjectId.make(root),
163 { defaultAutoPull },
164 ]),
165 ),
166 });
167
168 yield* ServerRuntimeStartup.autoPullProjects(
169 [
170 project("/clean"),
171 project("/current"),
172 project("/dirty"),
173 project("/ahead"),
174 project("/feature"),
175 project("/disabled"),
176 ],
177 overrides({
178 "/clean": true,
179 "/current": true,
180 "/dirty": true,
181 "/ahead": true,
182 "/feature": true,
183 "/disabled": false,
184 }),
185 ).pipe(Effect.provideService(GitVcsDriver.GitVcsDriver, git));
186
187 assert.deepStrictEqual(pulled, ["/clean"]);
188
189 pulled.length = 0;
190 yield* ServerRuntimeStartup.autoPullProjects(
191 [project("/inherited"), project("/opted-out"), project("/dirty")],
192 { ...overrides({ "/opted-out": false }), defaultAutoPull: true },
193 ).pipe(Effect.provideService(GitVcsDriver.GitVcsDriver, git));
194 assert.deepStrictEqual(pulled, ["/inherited"]);
195 }),
196);