mirror of
https://github.com/anomalyco/opencode.git
synced 2026-07-23 10:45:33 -04:00
72 lines
2.3 KiB
TypeScript
72 lines
2.3 KiB
TypeScript
import { expect } from "bun:test"
|
|
import { ProcessLock } from "@opencode-ai/core/util/process-lock"
|
|
import { Effect } from "effect"
|
|
import fs from "node:fs/promises"
|
|
import os from "node:os"
|
|
import path from "node:path"
|
|
import { it } from "../lib/effect"
|
|
|
|
const worker = path.join(import.meta.dir, "../fixture/process-lock-worker.ts")
|
|
|
|
it.live(
|
|
"releases ownership when the scope closes",
|
|
Effect.gen(function* () {
|
|
const root = yield* temp("opencode-process-lock-")
|
|
const file = path.join(root, "service.lock")
|
|
yield* Effect.scoped(ProcessLock.acquire(file))
|
|
yield* Effect.scoped(ProcessLock.acquire(file))
|
|
}),
|
|
)
|
|
|
|
it.live(
|
|
"releases ownership when the process dies",
|
|
Effect.gen(function* () {
|
|
const root = yield* temp("opencode-process-lock-death-")
|
|
const file = path.join(root, "service.lock")
|
|
const ready = path.join(root, "ready")
|
|
const child = yield* Effect.acquireRelease(
|
|
Effect.sync(() =>
|
|
Bun.spawn([process.execPath, worker, JSON.stringify({ file, ready })], {
|
|
stdout: "ignore",
|
|
stderr: "pipe",
|
|
}),
|
|
),
|
|
(child) =>
|
|
Effect.promise(async () => {
|
|
kill(child)
|
|
await child.exited
|
|
}),
|
|
)
|
|
yield* Effect.promise(async () => {
|
|
for (let attempt = 0; attempt < 100 && !(await Bun.file(ready).exists()); attempt++) await Bun.sleep(20)
|
|
})
|
|
expect(yield* Effect.promise(() => Bun.file(ready).exists())).toBe(true)
|
|
|
|
const error = yield* Effect.scoped(ProcessLock.acquire(file)).pipe(Effect.flip)
|
|
expect(error._tag).toBe("ProcessLockHeldError")
|
|
|
|
if (process.platform !== "win32") {
|
|
process.kill(child.pid, "SIGSTOP")
|
|
const paused = yield* Effect.scoped(ProcessLock.acquire(file)).pipe(Effect.flip)
|
|
expect(paused._tag).toBe("ProcessLockHeldError")
|
|
process.kill(child.pid, "SIGCONT")
|
|
}
|
|
|
|
kill(child)
|
|
yield* Effect.promise(() => child.exited)
|
|
yield* Effect.scoped(ProcessLock.acquire(file))
|
|
}),
|
|
)
|
|
|
|
function temp(prefix: string) {
|
|
return Effect.acquireRelease(
|
|
Effect.promise(() => fs.mkdtemp(path.join(os.tmpdir(), prefix))),
|
|
(root) => Effect.promise(() => fs.rm(root, { recursive: true, force: true })),
|
|
)
|
|
}
|
|
|
|
function kill(child: Bun.Subprocess) {
|
|
if (process.platform === "win32") return child.kill()
|
|
return child.kill("SIGKILL")
|
|
}
|