2026-07-21 20:52:20 +03:00
|
|
|
import { beforeEach, describe, expect, test } from "bun:test"
|
|
|
|
|
import type { Event } from "@opencode-ai/sdk/v2/client"
|
|
|
|
|
import {
|
|
|
|
|
applyGlobalSessionStatusEvent,
|
|
|
|
|
applyGlobalSessionStatusSnapshot,
|
|
|
|
|
useGlobalSessionStatusStore,
|
|
|
|
|
} from "./global-session-status"
|
2026-07-23 15:45:53 +03:00
|
|
|
import { resetSessionOrdering, useSessionOrderingStore } from "./session-ordering"
|
2026-07-21 20:52:20 +03:00
|
|
|
|
|
|
|
|
beforeEach(() => {
|
|
|
|
|
useGlobalSessionStatusStore.setState({ statusById: new Map() })
|
2026-07-23 15:45:53 +03:00
|
|
|
resetSessionOrdering()
|
2026-07-21 20:52:20 +03:00
|
|
|
})
|
|
|
|
|
|
|
|
|
|
describe("global session status index", () => {
|
|
|
|
|
test("preserves full retry status details from live events", () => {
|
|
|
|
|
applyGlobalSessionStatusEvent("/repo", {
|
|
|
|
|
type: "session.status",
|
|
|
|
|
properties: {
|
|
|
|
|
sessionID: "session-a",
|
|
|
|
|
status: { type: "retry", attempt: 2, message: "waiting" },
|
|
|
|
|
},
|
|
|
|
|
} as Event)
|
|
|
|
|
|
|
|
|
|
expect(useGlobalSessionStatusStore.getState().statusById.get("session-a")?.status).toEqual({
|
|
|
|
|
type: "retry",
|
|
|
|
|
attempt: 2,
|
|
|
|
|
message: "waiting",
|
|
|
|
|
})
|
|
|
|
|
})
|
|
|
|
|
|
2026-07-23 15:45:53 +03:00
|
|
|
test("promotes on active and settled lifecycle edges only", () => {
|
|
|
|
|
applyGlobalSessionStatusEvent("/repo", {
|
|
|
|
|
type: "session.status",
|
|
|
|
|
properties: { sessionID: "session-a", status: { type: "busy" } },
|
|
|
|
|
} as Event)
|
|
|
|
|
const busyRank = useSessionOrderingStore.getState().rankById.get("session-a")
|
|
|
|
|
|
|
|
|
|
applyGlobalSessionStatusEvent("/repo", {
|
|
|
|
|
type: "session.status",
|
|
|
|
|
properties: { sessionID: "session-a", status: { type: "retry", attempt: 1, message: "wait", next: 1 } },
|
|
|
|
|
} as Event)
|
|
|
|
|
expect(useSessionOrderingStore.getState().rankById.get("session-a")).toBe(busyRank)
|
|
|
|
|
|
|
|
|
|
applyGlobalSessionStatusEvent("/repo", {
|
|
|
|
|
type: "session.idle",
|
|
|
|
|
properties: { sessionID: "session-a" },
|
|
|
|
|
} as Event)
|
|
|
|
|
const idleRank = useSessionOrderingStore.getState().rankById.get("session-a")
|
|
|
|
|
expect(idleRank).toBeGreaterThan(busyRank ?? 0)
|
|
|
|
|
|
|
|
|
|
applyGlobalSessionStatusEvent("/repo", {
|
|
|
|
|
type: "session.error",
|
|
|
|
|
properties: { sessionID: "session-a" },
|
|
|
|
|
} as Event)
|
|
|
|
|
expect(useSessionOrderingStore.getState().rankById.get("session-a")).toBe(idleRank)
|
|
|
|
|
})
|
|
|
|
|
|
2026-07-21 20:52:20 +03:00
|
|
|
test("authoritative snapshots clear absent active entries for their directory", () => {
|
|
|
|
|
applyGlobalSessionStatusSnapshot("/repo", { "session-a": { type: "busy" } }, ["session-a"])
|
|
|
|
|
expect(useGlobalSessionStatusStore.getState().statusById.get("session-a")?.status.type).toBe("busy")
|
|
|
|
|
|
|
|
|
|
applyGlobalSessionStatusSnapshot("/repo", {}, ["session-a"])
|
|
|
|
|
expect(useGlobalSessionStatusStore.getState().statusById.has("session-a")).toBe(false)
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
test("clears an explicitly idle known session when directory aliases differ", () => {
|
|
|
|
|
applyGlobalSessionStatusSnapshot("/canonical/repo", { "session-a": { type: "busy" } }, ["session-a"])
|
|
|
|
|
|
|
|
|
|
applyGlobalSessionStatusSnapshot("/alias/repo", { "session-a": { type: "idle" } }, ["session-a"])
|
|
|
|
|
|
|
|
|
|
expect(useGlobalSessionStatusStore.getState().statusById.has("session-a")).toBe(false)
|
|
|
|
|
})
|
|
|
|
|
})
|