Files
opencode/packages/app/test-browser/ssh-health.test.ts

149 lines
4.5 KiB
TypeScript

import { expect, test } from "bun:test"
import { createRoot } from "solid-js"
import { createStore } from "solid-js/store"
import { createServerHealth, type ServerHealth } from "../src/runtime/server/health"
import { ServerConnection } from "../src/runtime/server/registry"
test("SSH health is checked only with an active tunnel, and cancellation clears stale failures", async () => {
const requests: ReturnType<typeof Promise.withResolvers<ServerHealth>>[] = []
const app = createRoot((dispose) => {
const [state, setState] = createStore<{ state: ServerConnection.Plugin["state"] }>({ state: "stopped" })
const connection: ServerConnection.Plugin = {
type: "plugin",
key: "ssh:fixture",
plugin: "ssh",
connecting: false,
authenticationRequired: false,
managed: true,
http: { url: "http://127.0.0.1:12345" },
get state() {
return state.state
},
}
const health = createServerHealth(
() => [connection],
() => true,
() => {
const request = Promise.withResolvers<ServerHealth>()
requests.push(request)
return request.promise
},
)
return { dispose, setState, health: () => health.status[ServerConnection.key(connection)] }
})
try {
expect(app.health()).toBeUndefined()
app.setState("state", "starting")
app.setState("state", "auth")
app.setState("state", "stopped")
expect(requests).toHaveLength(0)
expect(app.health()).toBeUndefined()
app.setState("state", "ready")
expect(requests).toHaveLength(1)
expect(app.health()?.checking).toBe(true)
app.setState("state", "auth")
requests[0]?.resolve({ healthy: false })
await Promise.resolve()
expect(app.health()).toBeUndefined()
app.setState("state", "failed")
expect(app.health()?.healthy).toBe(false)
app.setState("state", "auth")
expect(app.health()).toBeUndefined()
app.setState("state", "ready")
requests[1]?.resolve({ healthy: false })
await Promise.resolve()
expect(app.health()).toEqual({ healthy: false })
} finally {
app.dispose()
}
})
test("a late failure from the old endpoint cannot overwrite the new endpoint check", async () => {
const requests: ReturnType<typeof Promise.withResolvers<ServerHealth>>[] = []
const app = createRoot((dispose) => {
const [state, setState] = createStore({ url: "http://127.0.0.1:0" })
const connection: ServerConnection.Plugin = {
type: "plugin",
key: "ssh:fixture",
plugin: "ssh",
state: "ready",
connecting: false,
authenticationRequired: false,
managed: true,
get http() {
return { url: state.url }
},
}
const health = createServerHealth(
() => [connection],
() => true,
() => {
const request = Promise.withResolvers<ServerHealth>()
requests.push(request)
return request.promise
},
)
return { dispose, setState, health: () => health.status[ServerConnection.key(connection)] }
})
try {
app.setState({ url: "http://127.0.0.1:12345" })
requests[0]?.resolve({ healthy: false })
await Promise.resolve()
expect(app.health()).toEqual({ healthy: false, checking: true })
requests[1]?.resolve({ healthy: true })
await Promise.resolve()
expect(app.health()).toEqual({ healthy: true })
} finally {
app.dispose()
}
})
test("retry probes again before the next poll and resolves once that round answers", async () => {
const requests: ReturnType<typeof Promise.withResolvers<ServerHealth>>[] = []
const connection: ServerConnection.Http = { type: "http", http: { url: "http://127.0.0.1:12346" } }
const app = createRoot((dispose) => {
const health = createServerHealth(
() => [connection],
() => true,
() => {
const request = Promise.withResolvers<ServerHealth>()
requests.push(request)
return request.promise
},
)
return { dispose, retry: health.retry, health: () => health.status[ServerConnection.key(connection)] }
})
try {
requests[0]?.resolve({ healthy: false })
await Promise.resolve()
expect(app.health()?.healthy).toBe(false)
const state = { done: false }
const retried = app.retry().then(() => (state.done = true))
expect(requests).toHaveLength(2)
await Promise.resolve()
expect(state.done).toBe(false)
requests[1]?.resolve({ healthy: true })
await retried
expect(app.health()?.healthy).toBe(true)
} finally {
app.dispose()
}
})