import * as NodeServices from "@effect/platform-node/NodeServices";import { assert, describe, it } from "@effect/vitest";import * as Effect from "effect/Effect";import * as FileSystem from "effect/FileSystem";import * as Option from "effect/Option";import * as Path from "effect/Path"; import { findRunningServerPid, findRunningServerPidSync } from "./server-runtime-probe.ts"; /** A pid that cannot belong to a live process (above the usual pid_max). */const DEAD_PID = 0x7ffffffe; const writeRuntimeState = Effect.fn(function* (input: { readonly baseDir: string; readonly stateDir: "dev" | "userdata"; readonly contents: string;}) { const fileSystem = yield* FileSystem.FileSystem; const path = yield* Path.Path; const directory = path.join(input.baseDir, input.stateDir); yield* fileSystem.makeDirectory(directory, { recursive: true }); yield* fileSystem.writeFileString(path.join(directory, "server-runtime.json"), input.contents);}); const runtimeStateJson = (pid: number) => JSON.stringify({ version: 1, pid, port: 16_602, origin: "http://127.0.0.1:16602", startedAt: "2026-07-20T00:00:00.000Z", }); const makeBaseDir = Effect.fnUntraced(function* () { const fileSystem = yield* FileSystem.FileSystem; return yield* fileSystem.makeTempDirectoryScoped({ prefix: "t3-runtime-probe-test-" });}); describe("findRunningServerPid", () => { it.effect("reports the pid of a live server in the dev state directory", () => Effect.gen(function* () { const baseDir = yield* makeBaseDir(); yield* writeRuntimeState({ baseDir, stateDir: "dev", contents: runtimeStateJson(process.pid), }); assert.equal(Option.getOrThrow(yield* findRunningServerPid(baseDir)), process.pid); }).pipe(Effect.provide(NodeServices.layer)), ); // An explicit --home-dir puts runtime state in userdata instead, and the // caller of a seed has no reason to know which of the two was used. it.effect("also finds a live server in the userdata state directory", () => Effect.gen(function* () { const baseDir = yield* makeBaseDir(); yield* writeRuntimeState({ baseDir, stateDir: "userdata", contents: runtimeStateJson(process.pid), }); assert.equal(Option.getOrThrow(yield* findRunningServerPid(baseDir)), process.pid); }).pipe(Effect.provide(NodeServices.layer)), ); // A server killed with SIGKILL never clears its state file. Treating that as // "running" would block seeding for good, with nothing to clear it. it.effect("ignores a state file whose process is gone", () => Effect.gen(function* () { const baseDir = yield* makeBaseDir(); yield* writeRuntimeState({ baseDir, stateDir: "dev", contents: runtimeStateJson(DEAD_PID) }); assert.isTrue(Option.isNone(yield* findRunningServerPid(baseDir))); }).pipe(Effect.provide(NodeServices.layer)), ); // A half-written file says nothing about liveness either way. it.effect("ignores a state file it cannot parse", () => Effect.gen(function* () { const baseDir = yield* makeBaseDir(); yield* writeRuntimeState({ baseDir, stateDir: "dev", contents: "{not json" }); assert.isTrue(Option.isNone(yield* findRunningServerPid(baseDir))); }).pipe(Effect.provide(NodeServices.layer)), ); // `process.kill` reads these as signal-group targets, not process lookups, so // neither throws — a naive probe calls both "live" and blocks seeding forever // on a file no server ever wrote. for (const pid of [0, -1]) { it.effect(`ignores a state file recording pid ${String(pid)}`, () => Effect.gen(function* () { const baseDir = yield* makeBaseDir(); yield* writeRuntimeState({ baseDir, stateDir: "dev", contents: runtimeStateJson(pid) }); assert.isTrue(Option.isNone(yield* findRunningServerPid(baseDir))); }).pipe(Effect.provide(NodeServices.layer)), ); } it.effect("reports nothing when no state files exist", () => Effect.gen(function* () { const baseDir = yield* makeBaseDir(); assert.isTrue(Option.isNone(yield* findRunningServerPid(baseDir))); }).pipe(Effect.provide(NodeServices.layer)), );}); /** * The sync probe exists only because `seedDevDatabase` holds an open SQLite * transaction and cannot await an Effect. Two implementations of one rule drift * apart silently, so every case is asserted against both rather than trusting * them to agree. */describe("findRunningServerPidSync agrees with the Effect probe", () => { const cases = [ { name: "a live server in dev", stateDir: "dev", contents: runtimeStateJson(process.pid) }, { name: "a live server in userdata", stateDir: "userdata", contents: runtimeStateJson(process.pid), }, { name: "a dead pid", stateDir: "dev", contents: runtimeStateJson(DEAD_PID) }, { name: "pid 0", stateDir: "dev", contents: runtimeStateJson(0) }, { name: "pid -1", stateDir: "dev", contents: runtimeStateJson(-1) }, { name: "unparseable json", stateDir: "dev", contents: "{not json" }, { name: "json without a pid", stateDir: "dev", contents: JSON.stringify({ version: 1 }) }, ] as const; for (const testCase of cases) { it.effect(testCase.name, () => Effect.gen(function* () { const baseDir = yield* makeBaseDir(); yield* writeRuntimeState({ baseDir, stateDir: testCase.stateDir, contents: testCase.contents, }); const viaEffect = Option.getOrUndefined(yield* findRunningServerPid(baseDir)); assert.equal(findRunningServerPidSync(baseDir), viaEffect); }).pipe(Effect.provide(NodeServices.layer)), ); } it.effect("both report nothing when no state files exist", () => Effect.gen(function* () { const baseDir = yield* makeBaseDir(); assert.isTrue(Option.isNone(yield* findRunningServerPid(baseDir))); assert.isUndefined(findRunningServerPidSync(baseDir)); }).pipe(Effect.provide(NodeServices.layer)), );}); // A caller that has resolved which database it is about to touch must not be// blocked by a server using only the other one.describe("scoping to one state directory", () => { it.effect("ignores a live server in the other directory", () => Effect.gen(function* () { const baseDir = yield* makeBaseDir(); yield* writeRuntimeState({ baseDir, stateDir: "dev", contents: runtimeStateJson(process.pid), }); assert.isUndefined(findRunningServerPidSync(baseDir, "userdata")); assert.isTrue(Option.isNone(yield* findRunningServerPid(baseDir, "userdata"))); }).pipe(Effect.provide(NodeServices.layer)), ); it.effect("still reports a live server in the named directory", () => Effect.gen(function* () { const baseDir = yield* makeBaseDir(); yield* writeRuntimeState({ baseDir, stateDir: "dev", contents: runtimeStateJson(process.pid), }); assert.equal(findRunningServerPidSync(baseDir, "dev"), process.pid); }).pipe(Effect.provide(NodeServices.layer)), );});
import * as NodeServices from "@effect/platform-node/NodeServices";
import { assert, describe, it } from "@effect/vitest";
import * as Effect from "effect/Effect";
import * as FileSystem from "effect/FileSystem";
import * as Option from "effect/Option";
import * as Path from "effect/Path";
import { findRunningServerPid, findRunningServerPidSync } from "./server-runtime-probe.ts";
/** A pid that cannot belong to a live process (above the usual pid_max). */
const DEAD_PID = 0x7ffffffe;
const writeRuntimeState = Effect.fn(function* (input: {
readonly baseDir: string;
readonly stateDir: "dev" | "userdata";
readonly contents: string;
}) {
const fileSystem = yield* FileSystem.FileSystem;
const path = yield* Path.Path;
const directory = path.join(input.baseDir, input.stateDir);
yield* fileSystem.makeDirectory(directory, { recursive: true });
yield* fileSystem.writeFileString(path.join(directory, "server-runtime.json"), input.contents);
});
const runtimeStateJson = (pid: number) =>
JSON.stringify({
version: 1,
pid,
port: 16_602,
origin: "http://127.0.0.1:16602",
startedAt: "2026-07-20T00:00:00.000Z",
});
const makeBaseDir = Effect.fnUntraced(function* () {
const fileSystem = yield* FileSystem.FileSystem;
return yield* fileSystem.makeTempDirectoryScoped({ prefix: "t3-runtime-probe-test-" });
});
describe("findRunningServerPid", () => {
it.effect("reports the pid of a live server in the dev state directory", () =>
Effect.gen(function* () {
const baseDir = yield* makeBaseDir();
yield* writeRuntimeState({
baseDir,
stateDir: "dev",
contents: runtimeStateJson(process.pid),
});
assert.equal(Option.getOrThrow(yield* findRunningServerPid(baseDir)), process.pid);
}).pipe(Effect.provide(NodeServices.layer)),
);
// An explicit --home-dir puts runtime state in userdata instead, and the
// caller of a seed has no reason to know which of the two was used.
it.effect("also finds a live server in the userdata state directory", () =>
Effect.gen(function* () {
const baseDir = yield* makeBaseDir();
yield* writeRuntimeState({
baseDir,
stateDir: "userdata",
contents: runtimeStateJson(process.pid),
});
assert.equal(Option.getOrThrow(yield* findRunningServerPid(baseDir)), process.pid);
}).pipe(Effect.provide(NodeServices.layer)),
);
// A server killed with SIGKILL never clears its state file. Treating that as
// "running" would block seeding for good, with nothing to clear it.
it.effect("ignores a state file whose process is gone", () =>
Effect.gen(function* () {
const baseDir = yield* makeBaseDir();
yield* writeRuntimeState({ baseDir, stateDir: "dev", contents: runtimeStateJson(DEAD_PID) });
assert.isTrue(Option.isNone(yield* findRunningServerPid(baseDir)));
}).pipe(Effect.provide(NodeServices.layer)),
);
// A half-written file says nothing about liveness either way.
it.effect("ignores a state file it cannot parse", () =>
Effect.gen(function* () {
const baseDir = yield* makeBaseDir();
yield* writeRuntimeState({ baseDir, stateDir: "dev", contents: "{not json" });
assert.isTrue(Option.isNone(yield* findRunningServerPid(baseDir)));
}).pipe(Effect.provide(NodeServices.layer)),
);
// `process.kill` reads these as signal-group targets, not process lookups, so
// neither throws — a naive probe calls both "live" and blocks seeding forever
// on a file no server ever wrote.
for (const pid of [0, -1]) {
it.effect(`ignores a state file recording pid ${String(pid)}`, () =>
Effect.gen(function* () {
const baseDir = yield* makeBaseDir();
yield* writeRuntimeState({ baseDir, stateDir: "dev", contents: runtimeStateJson(pid) });
assert.isTrue(Option.isNone(yield* findRunningServerPid(baseDir)));
}).pipe(Effect.provide(NodeServices.layer)),
);
}
it.effect("reports nothing when no state files exist", () =>
Effect.gen(function* () {
const baseDir = yield* makeBaseDir();
assert.isTrue(Option.isNone(yield* findRunningServerPid(baseDir)));
}).pipe(Effect.provide(NodeServices.layer)),
);
});
/**
* The sync probe exists only because `seedDevDatabase` holds an open SQLite
* transaction and cannot await an Effect. Two implementations of one rule drift
* apart silently, so every case is asserted against both rather than trusting
* them to agree.
*/
describe("findRunningServerPidSync agrees with the Effect probe", () => {
const cases = [
{ name: "a live server in dev", stateDir: "dev", contents: runtimeStateJson(process.pid) },
{
name: "a live server in userdata",
stateDir: "userdata",
contents: runtimeStateJson(process.pid),
},
{ name: "a dead pid", stateDir: "dev", contents: runtimeStateJson(DEAD_PID) },
{ name: "pid 0", stateDir: "dev", contents: runtimeStateJson(0) },
{ name: "pid -1", stateDir: "dev", contents: runtimeStateJson(-1) },
{ name: "unparseable json", stateDir: "dev", contents: "{not json" },
{ name: "json without a pid", stateDir: "dev", contents: JSON.stringify({ version: 1 }) },
] as const;
for (const testCase of cases) {
it.effect(testCase.name, () =>
Effect.gen(function* () {
const baseDir = yield* makeBaseDir();
yield* writeRuntimeState({
baseDir,
stateDir: testCase.stateDir,
contents: testCase.contents,
});
const viaEffect = Option.getOrUndefined(yield* findRunningServerPid(baseDir));
assert.equal(findRunningServerPidSync(baseDir), viaEffect);
}).pipe(Effect.provide(NodeServices.layer)),
);
}
it.effect("both report nothing when no state files exist", () =>
Effect.gen(function* () {
const baseDir = yield* makeBaseDir();
assert.isTrue(Option.isNone(yield* findRunningServerPid(baseDir)));
assert.isUndefined(findRunningServerPidSync(baseDir));
}).pipe(Effect.provide(NodeServices.layer)),
);
});
// A caller that has resolved which database it is about to touch must not be
// blocked by a server using only the other one.
describe("scoping to one state directory", () => {
it.effect("ignores a live server in the other directory", () =>
Effect.gen(function* () {
const baseDir = yield* makeBaseDir();
yield* writeRuntimeState({
baseDir,
stateDir: "dev",
contents: runtimeStateJson(process.pid),
});
assert.isUndefined(findRunningServerPidSync(baseDir, "userdata"));
assert.isTrue(Option.isNone(yield* findRunningServerPid(baseDir, "userdata")));
}).pipe(Effect.provide(NodeServices.layer)),
);
it.effect("still reports a live server in the named directory", () =>
Effect.gen(function* () {
const baseDir = yield* makeBaseDir();
yield* writeRuntimeState({
baseDir,
stateDir: "dev",
contents: runtimeStateJson(process.pid),
});
assert.equal(findRunningServerPidSync(baseDir, "dev"), process.pid);
}).pipe(Effect.provide(NodeServices.layer)),
);
});