test(cli): isolate service registration lease

This commit is contained in:
Aiden Cline 2026-07-22 21:36:34 +00:00
commit 0548ad718f
3 changed files with 87 additions and 69 deletions

View file

@ -8,6 +8,7 @@ import fs from "node:fs/promises"
import os from "node:os"
import path from "node:path"
import { ServiceConfig } from "../src/services/service-config"
import { ServiceRegistration } from "../src/services/service-registration"
test("managed service ports are stable per installation channel", () => {
expect(ServiceConfig.defaultPort("latest")).toBe(0xc0de)
@ -413,32 +414,37 @@ test("port contender recognizes an incumbent registered during the bind race", a
}
}, 45_000)
test("stale dead registration is replaced after binding the selected port", async () => {
test("service registration replaces a stale owner with the bound address", async () => {
const root = await fs.mkdtemp(path.join(os.tmpdir(), "opencode-service-stale-"))
const port = await availablePort()
const registration = path.join(root, "state", "opencode", "service-local.json")
await fs.mkdir(path.join(root, "config", "opencode"), { recursive: true })
await fs.mkdir(path.dirname(registration), { recursive: true })
await fs.writeFile(path.join(root, "config", "opencode", "service-local.json"), JSON.stringify({ port }))
await fs.writeFile(
registration,
JSON.stringify({ id: "dead", version: "dead", url: `http://127.0.0.1:${port}`, pid: 2_147_483_647 }),
JSON.stringify({ id: "dead", version: "dead", url: "http://127.0.0.1:4321", pid: 2_147_483_647 }),
)
const owner = Bun.spawn([process.execPath, path.join(import.meta.dir, "../src/index.ts"), "serve", "--service"], {
env: serviceEnv(root),
stderr: "pipe",
stdout: "ignore",
})
try {
const info = await waitForInfo(registration, (value) => value.id !== "dead", 60_000)
expect(new URL(info.url).port).toBe(String(port))
expect(info.pid).toBe(owner.pid)
const cleanup = await Effect.runPromise(
ServiceRegistration.register(
{ _tag: "TcpAddress", hostname: "127.0.0.1", port: 4321 },
"secret",
"owner",
registration,
Effect.never,
).pipe(Effect.scoped, Effect.provide(NodeFileSystem.layer)),
)
expect(await Bun.file(registration).json()).toEqual({
id: "owner",
version: OPENCODE_VERSION,
url: "http://127.0.0.1:4321",
pid: process.pid,
password: "secret",
})
await Effect.runPromise(cleanup.pipe(Effect.provide(NodeFileSystem.layer)))
expect(await Bun.file(registration).exists()).toBe(false)
} finally {
owner.kill("SIGTERM")
await owner.exited
await fs.rm(root, { recursive: true, force: true })
}
}, 75_000)
})
test("a failed service stays registered and owns the selected port until stopped", async () => {
const root = await fs.mkdtemp(path.join(os.tmpdir(), "opencode-service-failed-"))
@ -479,9 +485,8 @@ test("a failed service stays registered and owns the selected port until stopped
}
}, 30_000)
async function waitForInfo(file: string, accept: (info: Info) => boolean = () => true, timeout = 20_000) {
const deadline = Date.now() + timeout
while (Date.now() < deadline) {
async function waitForInfo(file: string, accept: (info: Info) => boolean = () => true) {
for (let attempt = 0; attempt < 400; attempt++) {
const value = await Bun.file(file)
.json()
.catch(() => undefined)