7f33576f46
Co-authored-by: Brendan Allan <git@brendonovich.dev>
218 lines
6.8 KiB
TypeScript
218 lines
6.8 KiB
TypeScript
import { beforeAll, describe, expect, mock, test } from "bun:test"
|
|
import { createRoot, getOwner, type Owner } from "solid-js"
|
|
import { createStore } from "solid-js/store"
|
|
import type { NormalizedProviderListResponse } from "@opencode-ai/ui/context"
|
|
import type { State } from "./types"
|
|
import type { QueryOptionsApi } from "../server-sync"
|
|
import { ServerScope } from "@/utils/server-scope"
|
|
|
|
let createChildStoreManager: typeof import("./child-store").createChildStoreManager
|
|
const querySingles: Array<() => { queryKey?: unknown[]; enabled?: boolean }> = []
|
|
const persist: typeof import("@/utils/persist").persisted = (_target, store) => [
|
|
store[0],
|
|
store[1],
|
|
null,
|
|
Object.assign(() => true, { promise: undefined }),
|
|
]
|
|
|
|
const child = () => createStore({} as State)
|
|
const provider = { all: new Map(), connected: [], default: {} } satisfies NormalizedProviderListResponse
|
|
|
|
const queryOptionsApi = {
|
|
globalConfig: () => ({ queryKey: ["globalConfig"], queryFn: async () => ({}) }),
|
|
projects: () => ({ queryKey: ["projects"], queryFn: async () => [] }),
|
|
providers: (directory: string | null) => ({ queryKey: [directory, "providers"], queryFn: async () => provider }),
|
|
path: (directory: string | null) => ({
|
|
queryKey: [directory, "path"],
|
|
queryFn: async () => ({
|
|
state: "",
|
|
config: "",
|
|
worktree: "",
|
|
directory: directory ?? "",
|
|
home: "",
|
|
}),
|
|
}),
|
|
agents: (directory: string) => ({ queryKey: [directory, "agents"], queryFn: async () => [] }),
|
|
mcp: (directory: string) => ({ queryKey: [directory, "mcp"], queryFn: async () => ({}) }),
|
|
lsp: (directory: string) => ({ queryKey: [directory, "lsp"], queryFn: async () => [] }),
|
|
sessions: (directory: string) => ({ queryKey: [directory, "loadSessions"] as const }),
|
|
} as unknown as QueryOptionsApi
|
|
|
|
function createOwner(callback: (owner: Owner) => void) {
|
|
return createRoot((dispose) => {
|
|
const owner = getOwner()
|
|
if (!owner) throw new Error("owner required")
|
|
callback(owner)
|
|
|
|
return dispose
|
|
})
|
|
}
|
|
|
|
beforeAll(async () => {
|
|
mock.module("@tanstack/solid-query", () => ({
|
|
useQuery: (options: () => { queryKey?: unknown[]; enabled?: boolean }) => {
|
|
querySingles.push(options)
|
|
return {
|
|
get isLoading() {
|
|
return options().queryKey?.[1] === "path"
|
|
},
|
|
get data() {
|
|
if (options().queryKey?.[1] === "path") throw new Error("pending path data read")
|
|
if (options().queryKey?.[1] === "mcp") return options().enabled ? { demo: { status: "disabled" } } : undefined
|
|
if (options().queryKey?.[1] === "lsp") return []
|
|
if (options().queryKey?.[1] === "providers") return provider
|
|
return undefined
|
|
},
|
|
}
|
|
},
|
|
}))
|
|
|
|
createChildStoreManager = (await import("./child-store")).createChildStoreManager
|
|
})
|
|
|
|
describe("createChildStoreManager", () => {
|
|
test("does not evict the active directory during mark", () => {
|
|
const owner = createRoot((dispose) => {
|
|
const current = getOwner()
|
|
dispose()
|
|
return current
|
|
})
|
|
if (!owner) throw new Error("owner required")
|
|
|
|
const manager = createChildStoreManager({
|
|
owner,
|
|
scope: ServerScope.local,
|
|
persist,
|
|
isBooting: () => false,
|
|
isLoadingSessions: () => false,
|
|
onBootstrap() {},
|
|
onMcp() {},
|
|
onDispose() {},
|
|
translate: (key) => key,
|
|
queryOptions: queryOptionsApi,
|
|
global: { provider },
|
|
})
|
|
|
|
Array.from({ length: 30 }, (_, index) => `/pinned-${index}`).forEach((directory) => {
|
|
manager.children[directory] = child()
|
|
manager.pin(directory)
|
|
})
|
|
|
|
const directory = "/active"
|
|
manager.children[directory] = child()
|
|
manager.mark(directory)
|
|
|
|
expect(manager.children[directory]).toBeDefined()
|
|
})
|
|
|
|
test("starts new child stores as loading and bootstraps them on first access", () => {
|
|
const bootstraps: string[] = []
|
|
let manager: ReturnType<typeof createChildStoreManager> | undefined
|
|
|
|
const dispose = createOwner((owner) => {
|
|
manager = createChildStoreManager({
|
|
owner,
|
|
scope: ServerScope.local,
|
|
persist,
|
|
isBooting: () => false,
|
|
isLoadingSessions: () => false,
|
|
onBootstrap(directory) {
|
|
bootstraps.push(directory)
|
|
},
|
|
onMcp() {},
|
|
onDispose() {},
|
|
translate: (key) => key,
|
|
queryOptions: queryOptionsApi,
|
|
global: { provider },
|
|
})
|
|
})
|
|
|
|
try {
|
|
if (!manager) throw new Error("manager required")
|
|
|
|
const [store] = manager.child("/project")
|
|
|
|
expect(store.status).toBe("loading")
|
|
expect(bootstraps).toEqual(["/project"])
|
|
} finally {
|
|
dispose()
|
|
}
|
|
})
|
|
|
|
test("provides the requested directory while the path query is pending", () => {
|
|
let manager: ReturnType<typeof createChildStoreManager> | undefined
|
|
|
|
const dispose = createOwner((owner) => {
|
|
manager = createChildStoreManager({
|
|
owner,
|
|
scope: ServerScope.local,
|
|
persist,
|
|
isBooting: () => false,
|
|
isLoadingSessions: () => false,
|
|
onBootstrap() {},
|
|
onMcp() {},
|
|
onDispose() {},
|
|
translate: (key) => key,
|
|
queryOptions: queryOptionsApi,
|
|
global: { provider },
|
|
})
|
|
})
|
|
|
|
try {
|
|
if (!manager) throw new Error("manager required")
|
|
|
|
const [store] = manager.child("/project", { bootstrap: false })
|
|
|
|
expect(store.path.directory).toBe("/project")
|
|
expect(store.path.worktree).toBe("")
|
|
} finally {
|
|
dispose()
|
|
}
|
|
})
|
|
|
|
test("enables MCP only when requested for the directory", () => {
|
|
let manager: ReturnType<typeof createChildStoreManager> | undefined
|
|
const offset = querySingles.length
|
|
const mcpLoads: string[] = []
|
|
|
|
const dispose = createOwner((owner) => {
|
|
manager = createChildStoreManager({
|
|
owner,
|
|
scope: ServerScope.local,
|
|
persist,
|
|
isBooting: () => false,
|
|
isLoadingSessions: () => false,
|
|
onBootstrap() {},
|
|
onMcp(directory) {
|
|
mcpLoads.push(directory)
|
|
},
|
|
onDispose() {},
|
|
translate: (key) => key,
|
|
queryOptions: queryOptionsApi,
|
|
global: { provider },
|
|
})
|
|
})
|
|
|
|
try {
|
|
if (!manager) throw new Error("manager required")
|
|
const [store, setStore] = manager.child("/project", { bootstrap: false })
|
|
expect(querySingles.length - offset).toBe(4)
|
|
const query = querySingles[offset + 1]
|
|
if (!query) throw new Error("query required")
|
|
expect(query().enabled).toBe(false)
|
|
|
|
setStore("status", "complete")
|
|
manager.child("/project", { bootstrap: false, mcp: true })
|
|
expect(query().enabled).toBe(true)
|
|
expect(store.mcp).toEqual({ demo: { status: "disabled" } })
|
|
expect(mcpLoads).toEqual(["/project"])
|
|
|
|
manager.disableMcp("/project")
|
|
expect(query().enabled).toBe(false)
|
|
expect(manager.mcp("/project")).toBe(false)
|
|
} finally {
|
|
dispose()
|
|
}
|
|
})
|
|
})
|