mirror of
https://gitee.com/zhangsken/opencode.git
synced 2026-10-16 10:30:01 +08:00
8c94e9005f
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>
225 lines
7.3 KiB
TypeScript
225 lines
7.3 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/session-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 () => ({}) }),
|
|
mcpResources: (directory: string) => ({ queryKey: [directory, "mcpResources"], queryFn: async () => ({}) }),
|
|
lsp: (directory: string) => ({ queryKey: [directory, "lsp"], queryFn: async () => [] }),
|
|
references: (directory: string) => ({ queryKey: [directory, "references"], 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(store.limit).toBe(5)
|
|
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(6)
|
|
const query = querySingles[offset + 1]
|
|
const resourceQuery = querySingles[offset + 2]
|
|
if (!query) throw new Error("query required")
|
|
if (!resourceQuery) throw new Error("resource query required")
|
|
expect(query().enabled).toBe(false)
|
|
expect(resourceQuery().enabled).toBe(false)
|
|
|
|
setStore("status", "complete")
|
|
manager.child("/project", { bootstrap: false, mcp: true })
|
|
expect(query().enabled).toBe(true)
|
|
expect(resourceQuery().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()
|
|
}
|
|
})
|
|
})
|