import { mkdtempSync, rmSync } from "node:fs";import { mkdir, readFile, rm, writeFile } from "node:fs/promises";import { tmpdir } from "node:os";import { join } from "node:path";import { describe, expect, it } from "@effect/vitest";import { Effect, FileSystem, Layer, Path, PlatformError } from "effect"; import { acquireDaemonStartLock, canonicalDaemonHost, currentDaemonScopeId, isPidAlive, readDaemonPointer, readDaemonRecord, releaseDaemonStartLock, removeDaemonPointer, removeDaemonRecord, writeDaemonPointer, writeDaemonRecord,} from "../apps/cli/src/daemon-state"; const fileSystemError = (method: string, cause: unknown) => PlatformError.systemError({ _tag: "Unknown", module: "FileSystem", method, description: "FileSystem operation failed", cause, }); const fileSystemLayer = FileSystem.layerNoop({ makeDirectory: (path, options) => Effect.tryPromise({ try: () => mkdir(path, { recursive: options?.recursive, mode: options?.mode }), catch: (cause) => fileSystemError("makeDirectory", cause), }), writeFileString: (path, data, options) => Effect.tryPromise({ try: () => writeFile(path, data, { encoding: "utf8", flag: options?.flag }), catch: (cause) => fileSystemError("writeFileString", cause), }), readFileString: (path, encoding = "utf8") => Effect.tryPromise({ try: () => readFile(path, { encoding: encoding as BufferEncoding }), catch: (cause) => fileSystemError("readFileString", cause), }), remove: (path, options) => Effect.tryPromise({ try: () => rm(path, { recursive: options?.recursive ?? false, force: options?.force ?? false }), catch: (cause) => fileSystemError("remove", cause), }),}); const daemonStateLayer = Layer.merge(fileSystemLayer, Path.layer); const withDaemonDataDir = <A, E>(effect: Effect.Effect<A, E, FileSystem.FileSystem | Path.Path>) => Effect.acquireUseRelease( Effect.sync(() => { const prev = process.env.EXECUTOR_DATA_DIR; const dir = mkdtempSync(join(tmpdir(), "executor-daemon-state-test-")); process.env.EXECUTOR_DATA_DIR = dir; return { dir, prev }; }), () => effect, ({ dir, prev }) => Effect.sync(() => { if (prev === undefined) { delete process.env.EXECUTOR_DATA_DIR; } else { process.env.EXECUTOR_DATA_DIR = prev; } rmSync(dir, { recursive: true, force: true }); }), ).pipe(Effect.provide(daemonStateLayer)); describe("daemon state", () => { it("normalizes local host aliases", () => { expect(canonicalDaemonHost("localhost")).toBe("localhost"); expect(canonicalDaemonHost("127.0.0.1")).toBe("localhost"); expect(canonicalDaemonHost("::1")).toBe("localhost"); expect(canonicalDaemonHost("0.0.0.0")).toBe("localhost"); expect(canonicalDaemonHost("api.example.com")).toBe("api.example.com"); }); it.effect("writes, reads, and removes daemon records", () => withDaemonDataDir( Effect.gen(function* () { yield* writeDaemonRecord({ hostname: "127.0.0.1", port: 4788, pid: 12345, scopeDir: "/tmp/scope", }); const stored = yield* readDaemonRecord({ hostname: "localhost", port: 4788 }); expect(stored).toEqual({ version: 1, hostname: "localhost", port: 4788, pid: 12345, startedAt: expect.any(String), scopeDir: "/tmp/scope", }); yield* removeDaemonRecord({ hostname: "localhost", port: 4788 }); const after = yield* readDaemonRecord({ hostname: "localhost", port: 4788 }); expect(after).toBeNull(); }), ), ); it.effect("writes, reads, and removes daemon pointers", () => withDaemonDataDir( Effect.gen(function* () { yield* writeDaemonPointer({ hostname: "127.0.0.1", port: 5799, pid: 24680, scopeId: "scope:/tmp/project", scopeDir: "/tmp/project", token: "tok_123", }); const stored = yield* readDaemonPointer({ hostname: "localhost", scopeId: "scope:/tmp/project", }); expect(stored).toEqual({ version: 1, hostname: "localhost", port: 5799, pid: 24680, startedAt: expect.any(String), scopeId: "scope:/tmp/project", scopeDir: "/tmp/project", token: "tok_123", }); yield* removeDaemonPointer({ hostname: "localhost", scopeId: "scope:/tmp/project", }); const after = yield* readDaemonPointer({ hostname: "localhost", scopeId: "scope:/tmp/project", }); expect(after).toBeNull(); }), ), ); it.effect("serializes daemon startup with lock files", () => withDaemonDataDir( Effect.gen(function* () { const lock = yield* acquireDaemonStartLock({ hostname: "localhost", scopeId: "scope:/tmp/project", }); const second = yield* acquireDaemonStartLock({ hostname: "localhost", scopeId: "scope:/tmp/project", }).pipe(Effect.flip); expect(second.message).toContain("already in progress"); yield* releaseDaemonStartLock(lock); const third = yield* acquireDaemonStartLock({ hostname: "localhost", scopeId: "scope:/tmp/project", }); yield* releaseDaemonStartLock(third); }), ), ); it("derives scope id from EXECUTOR_SCOPE_DIR or cwd", () => { const prev = process.env.EXECUTOR_SCOPE_DIR; process.env.EXECUTOR_SCOPE_DIR = "/tmp/explicit-scope"; expect(currentDaemonScopeId()).toBe("scope:/tmp/explicit-scope"); if (prev === undefined) { delete process.env.EXECUTOR_SCOPE_DIR; } else { process.env.EXECUTOR_SCOPE_DIR = prev; } expect(currentDaemonScopeId()).toContain("cwd:"); }); it("detects live and invalid pids", () => { expect(isPidAlive(process.pid)).toBe(true); expect(isPidAlive(-1)).toBe(false); });});
import { mkdtempSync, rmSync } from "node:fs";
import { mkdir, readFile, rm, writeFile } from "node:fs/promises";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { describe, expect, it } from "@effect/vitest";
import { Effect, FileSystem, Layer, Path, PlatformError } from "effect";
import {
acquireDaemonStartLock,
canonicalDaemonHost,
currentDaemonScopeId,
isPidAlive,
readDaemonPointer,
readDaemonRecord,
releaseDaemonStartLock,
removeDaemonPointer,
removeDaemonRecord,
writeDaemonPointer,
writeDaemonRecord,
} from "../apps/cli/src/daemon-state";
const fileSystemError = (method: string, cause: unknown) =>
PlatformError.systemError({
_tag: "Unknown",
module: "FileSystem",
method,
description: "FileSystem operation failed",
cause,
});
const fileSystemLayer = FileSystem.layerNoop({
makeDirectory: (path, options) =>
Effect.tryPromise({
try: () => mkdir(path, { recursive: options?.recursive, mode: options?.mode }),
catch: (cause) => fileSystemError("makeDirectory", cause),
}),
writeFileString: (path, data, options) =>
Effect.tryPromise({
try: () => writeFile(path, data, { encoding: "utf8", flag: options?.flag }),
catch: (cause) => fileSystemError("writeFileString", cause),
}),
readFileString: (path, encoding = "utf8") =>
Effect.tryPromise({
try: () => readFile(path, { encoding: encoding as BufferEncoding }),
catch: (cause) => fileSystemError("readFileString", cause),
}),
remove: (path, options) =>
Effect.tryPromise({
try: () =>
rm(path, { recursive: options?.recursive ?? false, force: options?.force ?? false }),
catch: (cause) => fileSystemError("remove", cause),
}),
});
const daemonStateLayer = Layer.merge(fileSystemLayer, Path.layer);
const withDaemonDataDir = <A, E>(effect: Effect.Effect<A, E, FileSystem.FileSystem | Path.Path>) =>
Effect.acquireUseRelease(
Effect.sync(() => {
const prev = process.env.EXECUTOR_DATA_DIR;
const dir = mkdtempSync(join(tmpdir(), "executor-daemon-state-test-"));
process.env.EXECUTOR_DATA_DIR = dir;
return { dir, prev };
}),
() => effect,
({ dir, prev }) =>
Effect.sync(() => {
if (prev === undefined) {
delete process.env.EXECUTOR_DATA_DIR;
} else {
process.env.EXECUTOR_DATA_DIR = prev;
}
rmSync(dir, { recursive: true, force: true });
}),
).pipe(Effect.provide(daemonStateLayer));
describe("daemon state", () => {
it("normalizes local host aliases", () => {
expect(canonicalDaemonHost("localhost")).toBe("localhost");
expect(canonicalDaemonHost("127.0.0.1")).toBe("localhost");
expect(canonicalDaemonHost("::1")).toBe("localhost");
expect(canonicalDaemonHost("0.0.0.0")).toBe("localhost");
expect(canonicalDaemonHost("api.example.com")).toBe("api.example.com");
});
it.effect("writes, reads, and removes daemon records", () =>
withDaemonDataDir(
Effect.gen(function* () {
yield* writeDaemonRecord({
hostname: "127.0.0.1",
port: 4788,
pid: 12345,
scopeDir: "/tmp/scope",
});
const stored = yield* readDaemonRecord({ hostname: "localhost", port: 4788 });
expect(stored).toEqual({
version: 1,
hostname: "localhost",
port: 4788,
pid: 12345,
startedAt: expect.any(String),
scopeDir: "/tmp/scope",
});
yield* removeDaemonRecord({ hostname: "localhost", port: 4788 });
const after = yield* readDaemonRecord({ hostname: "localhost", port: 4788 });
expect(after).toBeNull();
}),
),
);
it.effect("writes, reads, and removes daemon pointers", () =>
withDaemonDataDir(
Effect.gen(function* () {
yield* writeDaemonPointer({
hostname: "127.0.0.1",
port: 5799,
pid: 24680,
scopeId: "scope:/tmp/project",
scopeDir: "/tmp/project",
token: "tok_123",
});
const stored = yield* readDaemonPointer({
hostname: "localhost",
scopeId: "scope:/tmp/project",
});
expect(stored).toEqual({
version: 1,
hostname: "localhost",
port: 5799,
pid: 24680,
startedAt: expect.any(String),
scopeId: "scope:/tmp/project",
scopeDir: "/tmp/project",
token: "tok_123",
});
yield* removeDaemonPointer({
hostname: "localhost",
scopeId: "scope:/tmp/project",
});
const after = yield* readDaemonPointer({
hostname: "localhost",
scopeId: "scope:/tmp/project",
});
expect(after).toBeNull();
}),
),
);
it.effect("serializes daemon startup with lock files", () =>
withDaemonDataDir(
Effect.gen(function* () {
const lock = yield* acquireDaemonStartLock({
hostname: "localhost",
scopeId: "scope:/tmp/project",
});
const second = yield* acquireDaemonStartLock({
hostname: "localhost",
scopeId: "scope:/tmp/project",
}).pipe(Effect.flip);
expect(second.message).toContain("already in progress");
yield* releaseDaemonStartLock(lock);
const third = yield* acquireDaemonStartLock({
hostname: "localhost",
scopeId: "scope:/tmp/project",
});
yield* releaseDaemonStartLock(third);
}),
),
);
it("derives scope id from EXECUTOR_SCOPE_DIR or cwd", () => {
const prev = process.env.EXECUTOR_SCOPE_DIR;
process.env.EXECUTOR_SCOPE_DIR = "/tmp/explicit-scope";
expect(currentDaemonScopeId()).toBe("scope:/tmp/explicit-scope");
if (prev === undefined) {
delete process.env.EXECUTOR_SCOPE_DIR;
} else {
process.env.EXECUTOR_SCOPE_DIR = prev;
}
expect(currentDaemonScopeId()).toContain("cwd:");
});
it("detects live and invalid pids", () => {
expect(isPidAlive(process.pid)).toBe(true);
expect(isPidAlive(-1)).toBe(false);
});
});