mirror of
https://github.com/anomalyco/opencode.git
synced 2026-10-25 14:20:05 +00:00
93 lines
4.1 KiB
TypeScript
93 lines
4.1 KiB
TypeScript
import { NodeServices } from "@effect/platform-node"
|
|
import { expect } from "bun:test"
|
|
import { Effect, FileSystem } from "effect"
|
|
import { realpathSync, statSync } from "node:fs"
|
|
import path from "node:path"
|
|
import { RetainedImage } from "../src/services/retained-image"
|
|
import { testEffect } from "../../core/test/lib/effect"
|
|
|
|
const it = testEffect(NodeServices.layer)
|
|
// Hard links to a running image are a Windows concern, and /tmp is often another filesystem elsewhere.
|
|
const windows = process.platform === "win32" ? it.live : it.live.skip
|
|
|
|
windows("retain hard-links the running image for the scope and sweeps only links of exited processes", () =>
|
|
Effect.gen(function* () {
|
|
const fs = yield* FileSystem.FileSystem
|
|
const directory = yield* fs.makeTempDirectoryScoped({ prefix: "opencode-retained-" })
|
|
// pid 999999999 does not exist; pid 4 is System, alive but not openable (EPERM).
|
|
const kept = ["opencode-service-4-aa.exe", `opencode-service-${process.pid}-bb.exe`, "unrelated.exe"]
|
|
yield* Effect.forEach([...kept, "opencode-upgrade-999999999-cc.exe"], (name) =>
|
|
fs.writeFileString(path.join(directory, name), name),
|
|
)
|
|
const image = statSync(realpathSync(process.execPath))
|
|
yield* Effect.scoped(
|
|
Effect.gen(function* () {
|
|
const link = yield* RetainedImage.retain(directory, "upgrade")
|
|
if (!link) return yield* Effect.die("Expected a link")
|
|
expect(path.basename(link)).toMatch(new RegExp(`^opencode-upgrade-${process.pid}-[0-9a-f]{8}\\.exe$`))
|
|
expect(statSync(link).ino).toBe(image.ino)
|
|
expect(statSync(link).nlink).toBe(image.nlink + 1)
|
|
expect((yield* fs.readDirectory(directory)).sort()).toEqual([...kept, path.basename(link)].sort())
|
|
}),
|
|
)
|
|
// The running test process does not lock the image's other links, so the release removes it.
|
|
expect((yield* fs.readDirectory(directory)).sort()).toEqual(kept.sort())
|
|
}),
|
|
)
|
|
|
|
windows("retain tolerates a directory it cannot link into", () =>
|
|
Effect.gen(function* () {
|
|
const fs = yield* FileSystem.FileSystem
|
|
const root = yield* fs.makeTempDirectoryScoped({ prefix: "opencode-retained-" })
|
|
const file = path.join(root, "not-a-directory")
|
|
yield* fs.writeFileString(file, "")
|
|
expect(yield* Effect.scoped(RetainedImage.retain(file, "service"))).toBeUndefined()
|
|
}),
|
|
)
|
|
|
|
windows("relocate moves only retained links out of a directory", () =>
|
|
Effect.gen(function* () {
|
|
const fs = yield* FileSystem.FileSystem
|
|
const root = yield* fs.makeTempDirectoryScoped({ prefix: "opencode-retained-" })
|
|
const cache = path.join(root, "cache")
|
|
const tmp = path.join(root, "tmp")
|
|
yield* fs.makeDirectory(cache)
|
|
yield* fs.writeFileString(path.join(cache, "opencode-upgrade-1-ab.exe"), "link")
|
|
yield* fs.writeFileString(path.join(cache, "models.json"), "{}")
|
|
yield* RetainedImage.relocate(cache, tmp)
|
|
expect(yield* fs.readDirectory(cache)).toEqual(["models.json"])
|
|
expect(yield* fs.readDirectory(tmp)).toEqual(["opencode-upgrade-1-ab.exe"])
|
|
}),
|
|
)
|
|
|
|
windows("installed accepts package-manager and curl binaries only", () =>
|
|
Effect.gen(function* () {
|
|
const home = path.join("C:", "Users", "someone")
|
|
const original = process.execPath
|
|
yield* Effect.addFinalizer(() => Effect.sync(() => (process.execPath = original)))
|
|
const cases = [
|
|
[path.join(home, ".bun", "install", "global", "node_modules", "@opencode", "cli", "bin", "opencode.exe"), true],
|
|
[path.join(home, ".opencode", "bin", "opencode.exe"), true],
|
|
[
|
|
path.join(
|
|
home,
|
|
"AppData",
|
|
"Local",
|
|
"Microsoft",
|
|
"WinGet",
|
|
"Packages",
|
|
"Anomaly.OpenCode_Microsoft.Winget.Source_8wekyb3d8bbwe",
|
|
"opencode.exe",
|
|
),
|
|
true,
|
|
],
|
|
[path.join(home, "AppData", "Local", "Programs", "OpenCode", "resources", "opencode-cli.exe"), false],
|
|
[path.join(home, "node_modules", "bun", "bin", "bun.exe"), false],
|
|
] as const
|
|
const results = cases.map(([executable]) => {
|
|
process.execPath = executable
|
|
return RetainedImage.installed(home)
|
|
})
|
|
expect(results).toEqual(cases.map(([, expected]) => expected))
|
|
}),
|
|
)
|