| import { describe, expect, test } from "bun:test" |
| import type { OpencodeClient } from "@opencode-ai/sdk/v2/client" |
| import type { |
| McpListInput, |
| McpResourceCatalogInput, |
| SessionApi, |
| SessionInfo, |
| SessionListInput, |
| } from "@opencode-ai/client/promise" |
| import { QueryClient } from "@tanstack/solid-query" |
| import { canDisposeDirectory, pickDirectoriesToEvict } from "./global-sync/eviction" |
| import { estimateRootSessionTotal, loadRootSessions } from "./global-sync/session-load" |
| import { loadActiveSessionsQuery, loadMcpQuery, loadMcpResourcesQuery, seedActiveSessionStatuses } from "./server-sync" |
| import { ServerScope } from "@/utils/server-scope" |
| import { createServerSession } from "./server-session" |
| import type { ServerApi } from "@/utils/server" |
|
|
| type McpApi = ServerApi["mcp"] |
|
|
| describe("MCP queries", () => { |
| test("loads current servers for the requested location", async () => { |
| const calls: unknown[] = [] |
| const queryClient = new QueryClient() |
| const result = await queryClient.fetchQuery( |
| loadMcpQuery(ServerScope.local, "/project", { |
| list: async (input: McpListInput = {}) => { |
| calls.push(input) |
| return { |
| location: { directory: "/project", project: { id: "project", directory: "/project" } }, |
| data: [ |
| { name: "docs", status: { status: "connected" } }, |
| { name: "search", status: { status: "pending" } }, |
| ], |
| } |
| }, |
| } as unknown as McpApi), |
| ) |
|
|
| expect(calls).toEqual([{ location: { directory: "/project" } }]) |
| expect(result).toEqual({ docs: { status: "connected" }, search: { status: "pending" } }) |
| }) |
|
|
| test("loads and keys the current resource catalog", async () => { |
| const calls: unknown[] = [] |
| const queryClient = new QueryClient() |
| const result = await queryClient.fetchQuery( |
| loadMcpResourcesQuery(ServerScope.local, "/project", { |
| resource: { |
| catalog: async (input: McpResourceCatalogInput = {}) => { |
| calls.push(input) |
| return { |
| location: { directory: "/project", project: { id: "project", directory: "/project" } }, |
| data: { |
| resources: [{ server: "docs", name: "Guide", uri: "docs://guide" }], |
| templates: [], |
| }, |
| } |
| }, |
| }, |
| } as unknown as McpApi), |
| ) |
|
|
| expect(calls).toEqual([{ location: { directory: "/project" } }]) |
| expect(result).toEqual({ "docs:docs://guide": { server: "docs", name: "Guide", uri: "docs://guide" } }) |
| }) |
| }) |
|
|
| describe("active session query", () => { |
| test("loads active sessions immediately and once per server cache", async () => { |
| let calls = 0 |
| const queryClient = new QueryClient() |
| const options = loadActiveSessionsQuery(ServerScope.local, { |
| active: async () => { |
| calls++ |
| return { ses_running: { type: "running" } } |
| }, |
| }) |
|
|
| expect(await queryClient.fetchQuery(options)).toEqual({ ses_running: { type: "running" } }) |
| expect(await queryClient.fetchQuery(options)).toEqual({ ses_running: { type: "running" } }) |
| expect(calls).toBe(1) |
| expect(options.enabled).toBe(true) |
| expect([...options.queryKey]).toEqual([ServerScope.local, "activeSessions"]) |
| }) |
|
|
| test("does not overwrite statuses already written by events", () => { |
| const session = createServerSession({} as OpencodeClient) |
| session.set("session_status", "ses_retry", { type: "retry", attempt: 2, message: "retrying", next: 10 }) |
|
|
| seedActiveSessionStatuses(session, { |
| ses_running: { type: "running" }, |
| ses_retry: { type: "running" }, |
| }) |
|
|
| expect(session.data.session_status.ses_running).toEqual({ type: "busy" }) |
| expect(session.data.session_status.ses_retry).toEqual({ |
| type: "retry", |
| attempt: 2, |
| message: "retrying", |
| next: 10, |
| }) |
| }) |
| }) |
|
|
| describe("pickDirectoriesToEvict", () => { |
| test("keeps pinned stores and evicts idle stores", () => { |
| const now = 5_000 |
| const picks = pickDirectoriesToEvict({ |
| stores: ["a", "b", "c", "d"], |
| state: new Map([ |
| ["a", { lastAccessAt: 1_000 }], |
| ["b", { lastAccessAt: 4_900 }], |
| ["c", { lastAccessAt: 4_800 }], |
| ["d", { lastAccessAt: 3_000 }], |
| ]), |
| pins: new Set(["a"]), |
| max: 2, |
| ttl: 1_500, |
| now, |
| }) |
|
|
| expect(picks).toEqual(["d", "c"]) |
| }) |
| }) |
|
|
| describe("loadRootSessions", () => { |
| test("loads and normalizes a limited page of root sessions", async () => { |
| const calls: SessionListInput[] = [] |
|
|
| const result = await loadRootSessions({ |
| api: { |
| list: async (query = {}) => { |
| calls.push(query) |
| return { data: [sessionInfo("session-1")], cursor: {} } |
| }, |
| } satisfies Pick<SessionApi, "list">, |
| directory: "dir", |
| limit: 10, |
| }) |
|
|
| expect(result.data).toEqual([ |
| expect.objectContaining({ id: "session-1", directory: "dir", slug: "session-1", version: "" }), |
| ]) |
| expect(result.limited).toBe(true) |
| expect(calls).toEqual([{ directory: "dir", parentID: null, limit: 10, order: "desc" }]) |
| }) |
|
|
| test("propagates list failures", () => { |
| expect( |
| loadRootSessions({ |
| api: { |
| list: async () => { |
| throw new Error("failed") |
| }, |
| } satisfies Pick<SessionApi, "list">, |
| directory: "dir", |
| limit: 25, |
| }), |
| ).rejects.toThrow("failed") |
| }) |
| }) |
|
|
| function sessionInfo(id: string) { |
| return { |
| id, |
| projectID: "project-1", |
| agent: "build", |
| model: { id: "model-1", providerID: "provider-1" }, |
| cost: 0, |
| tokens: { input: 0, output: 0, reasoning: 0, cache: { read: 0, write: 0 } }, |
| time: { created: 1, updated: 1 }, |
| title: id, |
| location: { directory: "dir" }, |
| } as SessionInfo |
| } |
|
|
| describe("estimateRootSessionTotal", () => { |
| test("keeps exact total for full fetches", () => { |
| expect(estimateRootSessionTotal({ count: 42, limit: 10, limited: false })).toBe(42) |
| }) |
|
|
| test("marks has-more for full-limit limited fetches", () => { |
| expect(estimateRootSessionTotal({ count: 10, limit: 10, limited: true })).toBe(11) |
| }) |
|
|
| test("keeps exact total when limited fetch is under limit", () => { |
| expect(estimateRootSessionTotal({ count: 9, limit: 10, limited: true })).toBe(9) |
| }) |
| }) |
|
|
| describe("canDisposeDirectory", () => { |
| test("rejects pinned or inflight directories", () => { |
| expect( |
| canDisposeDirectory({ |
| directory: "dir", |
| hasStore: true, |
| pinned: true, |
| booting: false, |
| loadingSessions: false, |
| }), |
| ).toBe(false) |
| expect( |
| canDisposeDirectory({ |
| directory: "dir", |
| hasStore: true, |
| pinned: false, |
| booting: true, |
| loadingSessions: false, |
| }), |
| ).toBe(false) |
| expect( |
| canDisposeDirectory({ |
| directory: "dir", |
| hasStore: true, |
| pinned: false, |
| booting: false, |
| loadingSessions: true, |
| }), |
| ).toBe(false) |
| }) |
|
|
| test("accepts idle unpinned directory store", () => { |
| expect( |
| canDisposeDirectory({ |
| directory: "dir", |
| hasStore: true, |
| pinned: false, |
| booting: false, |
| loadingSessions: false, |
| }), |
| ).toBe(true) |
| }) |
| }) |
|
|