Co-authored-by: Brendan Allan <14191578+Brendonovich@users.noreply.github.com> Co-authored-by: Kit Langton <kit.langton@gmail.com> Co-authored-by: opencode-agent[bot] <opencode-agent[bot]@users.noreply.github.com> Co-authored-by: Affan Ali <93028901+affanali2k3@users.noreply.github.com> Co-authored-by: affanali2k3 <affanalikhanxx@gmail.com> Co-authored-by: Frank <frank@anoma.ly> Co-authored-by: opencode-agent[bot] <219766164+opencode-agent[bot]@users.noreply.github.com> Co-authored-by: 𝓛𝓲𝓽𝓽𝓵𝓮 𝓕𝓻𝓪𝓷𝓴 <little-frank@opencord.local> Co-authored-by: Aiden Cline <63023139+rekram1-node@users.noreply.github.com> Co-authored-by: Jay V <air@live.ca> Co-authored-by: Dax Raad <d@ironbay.co> Co-authored-by: Aarav Sareen <96787824+arvsrn@users.noreply.github.com> Co-authored-by: OpeOginni <107570612+OpeOginni@users.noreply.github.com> Co-authored-by: Luke Parker <10430890+Hona@users.noreply.github.com> Co-authored-by: Ben Guthrie <benjee.012@gmail.com> Co-authored-by: Dax <mail@thdxr.com> Co-authored-by: Filip <34747899+neriousy@users.noreply.github.com> Co-authored-by: Max Anderson <max.a.anderson95@gmail.com> Co-authored-by: Brendan Allan <git@brendonovich.dev> Co-authored-by: Jack <jack@anoma.ly> Co-authored-by: Shoubhit Dash <shoubhit2005@gmail.com> Co-authored-by: Dustin Deus <deusdustin@gmail.com> Co-authored-by: starptech <starptech@starptechs-MBP.fritz.box> Co-authored-by: Aiden Cline <aidenpcline@gmail.com> Co-authored-by: usrnk1 <7547651+usrnk1@users.noreply.github.com> Co-authored-by: Jay <53023+jayair@users.noreply.github.com> Co-authored-by: runvip <164729189+runvip@users.noreply.github.com> Co-authored-by: opencode <opencode@sst.dev> Co-authored-by: Julian Coy <julian@ex-machina.co> Co-authored-by: Vladimir Glafirov <vglafirov@gitlab.com>
231 lines
7.5 KiB
TypeScript
231 lines
7.5 KiB
TypeScript
import { expect, test } from "bun:test"
|
|
import {
|
|
clearWslDistroState,
|
|
requireWslIpcString,
|
|
requireWslIpcStrings,
|
|
wslServerIdToRestart,
|
|
wslTerminalArgs,
|
|
} from "./policy"
|
|
import {
|
|
expectOpencodeVersion,
|
|
pendingRestartAfterWslInstall,
|
|
pollWslHealth,
|
|
wslServerIdsToStartOnInitialize,
|
|
} from "./startup"
|
|
import { createWslServersController, type WslServerConfig } from "./servers"
|
|
|
|
let persistedServers: WslServerConfig[] = []
|
|
let releaseOpencodeResolve: (() => void) | undefined
|
|
|
|
test("starts every configured WSL server on initialization", () => {
|
|
expect(
|
|
wslServerIdsToStartOnInitialize([
|
|
{ id: "wsl:Debian", distro: "Debian" },
|
|
{ id: "wsl:Ubuntu-24.04", distro: "Ubuntu-24.04" },
|
|
]),
|
|
).toEqual(["wsl:Debian", "wsl:Ubuntu-24.04"])
|
|
})
|
|
|
|
test("rejects an update that did not install the desktop version", () => {
|
|
expect(() => expectOpencodeVersion("1.16.2", "1.16.2")).not.toThrow()
|
|
expect(() => expectOpencodeVersion("1.14.35", "1.16.2")).toThrow(
|
|
"OpenCode update finished but Debian still reports 1.14.35; expected 1.16.2",
|
|
)
|
|
})
|
|
|
|
test("restarts an existing distro server after updating OpenCode", () => {
|
|
expect(
|
|
wslServerIdToRestart(
|
|
[
|
|
{
|
|
config: { id: "wsl:Debian", distro: "Debian" },
|
|
runtime: { kind: "ready", url: "", username: null, password: null },
|
|
},
|
|
],
|
|
"Debian",
|
|
),
|
|
).toBe("wsl:Debian")
|
|
expect(wslServerIdToRestart([], "Debian")).toBeUndefined()
|
|
})
|
|
|
|
test("clears cached distro probes when removing a WSL server", () => {
|
|
expect(
|
|
clearWslDistroState(
|
|
{ Debian: { name: "Debian", canExecute: true, hasBash: true, hasCurl: true, error: null } },
|
|
{
|
|
Debian: {
|
|
distro: "Debian",
|
|
resolvedPath: "/home/luke/.opencode/bin/opencode",
|
|
version: "1.16.2",
|
|
expectedVersion: "1.16.2",
|
|
matchesDesktop: true,
|
|
error: null,
|
|
},
|
|
},
|
|
"Debian",
|
|
),
|
|
).toEqual({ distroProbes: {}, opencodeChecks: {} })
|
|
})
|
|
|
|
test("opens terminals for distro names containing spaces", () => {
|
|
expect(wslTerminalArgs("Ubuntu Preview")).toEqual(["/c", "start", "", "wsl", "-d", "Ubuntu Preview"])
|
|
})
|
|
|
|
test("stops health polling when sidecar startup settles", async () => {
|
|
const abort = new AbortController()
|
|
let checks = 0
|
|
const polling = pollWslHealth(
|
|
async () => {
|
|
checks++
|
|
return false
|
|
},
|
|
abort.signal,
|
|
1,
|
|
)
|
|
|
|
await new Promise((resolve) => setTimeout(resolve, 5))
|
|
abort.abort()
|
|
await polling
|
|
const settled = checks
|
|
await new Promise((resolve) => setTimeout(resolve, 5))
|
|
expect(checks).toBe(settled)
|
|
})
|
|
|
|
test("validates WSL IPC identifiers at the module boundary", () => {
|
|
expect(requireWslIpcString("distro", "Debian")).toBe("Debian")
|
|
expect(requireWslIpcStrings("distro", ["Debian", "Ubuntu"])).toEqual(["Debian", "Ubuntu"])
|
|
expect(() => requireWslIpcString("distro", "")).toThrow("Invalid distro")
|
|
expect(() => requireWslIpcString("server id", undefined)).toThrow("Invalid server id")
|
|
expect(() => requireWslIpcStrings("distro", [])).toThrow("Invalid distro")
|
|
})
|
|
|
|
test("derives a required Windows restart from the post-install runtime probe", () => {
|
|
expect(pendingRestartAfterWslInstall({ available: false, version: null, error: "WSL unavailable" })).toBe(true)
|
|
expect(pendingRestartAfterWslInstall({ available: true, version: "WSL version: 2.6.1", error: null })).toBe(false)
|
|
})
|
|
|
|
test("ignores stale background OpenCode checks after removing a WSL server", async () => {
|
|
persistedServers = []
|
|
releaseOpencodeResolve = undefined
|
|
const controller = createWslServersController(
|
|
"1.16.2",
|
|
async () => ({
|
|
listener: {
|
|
stop: () => undefined,
|
|
onExit: () => undefined,
|
|
},
|
|
url: "http://127.0.0.1:4096",
|
|
username: "opencode",
|
|
password: "secret",
|
|
}),
|
|
testControllerOptions(),
|
|
)
|
|
|
|
await controller.addServer("Debian")
|
|
await waitFor(() => !!releaseOpencodeResolve)
|
|
await controller.removeServer("wsl:Debian")
|
|
releaseOpencodeResolve?.()
|
|
await new Promise((resolve) => setTimeout(resolve, 0))
|
|
|
|
expect(controller.getState().servers).toEqual([])
|
|
expect(controller.getState().opencodeChecks).toEqual({})
|
|
})
|
|
|
|
test("ignores stale startup OpenCode checks after removing a WSL server", async () => {
|
|
persistedServers = [{ id: "wsl:Debian", distro: "Debian" }]
|
|
releaseOpencodeResolve = undefined
|
|
const controller = createWslServersController(
|
|
"1.16.2",
|
|
async () => new Promise<never>(() => undefined),
|
|
testControllerOptions(),
|
|
)
|
|
|
|
await controller.initialize()
|
|
await waitFor(() => !!releaseOpencodeResolve)
|
|
await controller.removeServer("wsl:Debian")
|
|
releaseOpencodeResolve?.()
|
|
await new Promise((resolve) => setTimeout(resolve, 0))
|
|
|
|
expect(controller.getState().servers).toEqual([])
|
|
expect(controller.getState().opencodeChecks).toEqual({})
|
|
})
|
|
|
|
test("probes addable distros in parallel before checking OpenCode", async () => {
|
|
persistedServers = []
|
|
const started: string[] = []
|
|
const release = new Map<string, () => void>()
|
|
const opencode: string[] = []
|
|
const controller = createWslServersController("1.16.2", async () => new Promise<never>(() => undefined), {
|
|
...testControllerOptions(),
|
|
probeDistro: async (distro) => {
|
|
started.push(distro)
|
|
await new Promise<void>((resolve) => release.set(distro, resolve))
|
|
return { name: distro, canExecute: true, hasBash: true, hasCurl: true, error: null }
|
|
},
|
|
resolveOpencode: async (distro) => {
|
|
opencode.push(distro)
|
|
return "/home/me/.opencode/bin/opencode"
|
|
},
|
|
})
|
|
|
|
const task = controller.probeAddable(["Debian", "Ubuntu"])
|
|
await waitFor(() => started.length === 2)
|
|
expect(started).toEqual(["Debian", "Ubuntu"])
|
|
expect(opencode).toEqual([])
|
|
release.get("Debian")?.()
|
|
release.get("Ubuntu")?.()
|
|
await task
|
|
|
|
expect(Object.keys(controller.getState().distroProbes)).toEqual(["Debian", "Ubuntu"])
|
|
expect(opencode).toEqual(["Debian", "Ubuntu"])
|
|
expect(Object.keys(controller.getState().opencodeChecks)).toEqual(["Debian", "Ubuntu"])
|
|
})
|
|
|
|
test("does not check OpenCode in addable distros that cannot execute commands", async () => {
|
|
persistedServers = []
|
|
const opencode: string[] = []
|
|
const controller = createWslServersController("1.16.2", async () => new Promise<never>(() => undefined), {
|
|
...testControllerOptions(),
|
|
probeDistro: async (distro) => ({
|
|
name: distro,
|
|
canExecute: distro === "Debian",
|
|
hasBash: distro === "Debian",
|
|
hasCurl: distro === "Debian",
|
|
error: distro === "Debian" ? null : "Open Ubuntu once to finish setup",
|
|
}),
|
|
resolveOpencode: async (distro) => {
|
|
opencode.push(distro)
|
|
return "/home/me/.opencode/bin/opencode"
|
|
},
|
|
})
|
|
|
|
await controller.probeAddable(["Debian", "Ubuntu"])
|
|
|
|
expect(Object.keys(controller.getState().distroProbes)).toEqual(["Debian", "Ubuntu"])
|
|
expect(opencode).toEqual(["Debian"])
|
|
expect(Object.keys(controller.getState().opencodeChecks)).toEqual(["Debian"])
|
|
})
|
|
|
|
async function waitFor(check: () => boolean) {
|
|
for (let attempt = 0; attempt < 20; attempt++) {
|
|
if (check()) return
|
|
await new Promise((resolve) => setTimeout(resolve, 0))
|
|
}
|
|
throw new Error("Timed out waiting for condition")
|
|
}
|
|
|
|
function testControllerOptions() {
|
|
return {
|
|
readServers: () => persistedServers,
|
|
writeServers: (servers: WslServerConfig[]) => {
|
|
persistedServers = servers
|
|
},
|
|
readCommandVersion: async () => "1.16.2",
|
|
resolveOpencode: async () => {
|
|
await new Promise<void>((resolve) => {
|
|
releaseOpencodeResolve = resolve
|
|
})
|
|
return "/home/me/.opencode/bin/opencode"
|
|
},
|
|
}
|
|
}
|