302e9b45ab
Co-authored-by: Luke Parker <10430890+Hona@users.noreply.github.com> Co-authored-by: opencode-agent[bot] <219766164+opencode-agent[bot]@users.noreply.github.com> Co-authored-by: Aarav Sareen <96787824+arvsrn@users.noreply.github.com> Co-authored-by: Brendan Allan <14191578+Brendonovich@users.noreply.github.com> Co-authored-by: opencode-agent[bot] <opencode-agent[bot]@users.noreply.github.com> Co-authored-by: Aiden Cline <rekram1-node@users.noreply.github.com> Co-authored-by: opencode <opencode@sst.dev> Co-authored-by: Jay <53023+jayair@users.noreply.github.com> Co-authored-by: David Hill <1879069+iamdavidhill@users.noreply.github.com> Co-authored-by: BB84 <110078428+BB-84C@users.noreply.github.com> Co-authored-by: Aiden Cline <aidenpcline@gmail.com> Co-authored-by: Dax <mail@thdxr.com> Co-authored-by: Brendan Allan <git@brendonovich.dev> Co-authored-by: Jay V <air@live.ca> Co-authored-by: Adam <2363879+adamdotdevin@users.noreply.github.com> Co-authored-by: Aiden Cline <63023139+rekram1-node@users.noreply.github.com> Co-authored-by: Dustin Deus <deusdustin@gmail.com> Co-authored-by: Frank <frank@anoma.ly> Co-authored-by: usrnk1 <7547651+usrnk1@users.noreply.github.com> Co-authored-by: Jack <jack@anoma.ly> Co-authored-by: Sebastian <hasta84@gmail.com> Co-authored-by: Jérôme Benoit <jerome.benoit@sap.com> Co-authored-by: Test User <test@test.com> Co-authored-by: Simon Klee <hello@simonklee.dk> Co-authored-by: Rahul A Mistry <149420892+ProdigyRahul@users.noreply.github.com> Co-authored-by: Qiping Li <liqiping1991@gmail.com> Co-authored-by: liqiping <liqiping@msh.team> Co-authored-by: OpeOginni <107570612+OpeOginni@users.noreply.github.com> Co-authored-by: Matthias Reso <13337103+mreso@users.noreply.github.com> Co-authored-by: tobwen <1864057+tobwen@users.noreply.github.com> Co-authored-by: Daniel Polito <danielbpolito@gmail.com> Co-authored-by: opencode <noreply@opencode.ai> Co-authored-by: Devin R Leopold <devin.leopold@gmail.com> Co-authored-by: Zach Bruggeman <mail@bruggie.com> Co-authored-by: Zach Bruggeman <zbruggeman@ramp.com> Co-authored-by: Kit Langton <kit.langton@gmail.com> Co-authored-by: David Siewert <david1gruppenplan@gmail.com> Co-authored-by: Andrei Dziahel <develop7@develop7.info> Co-authored-by: adityachaudhary99 <adityaachaudhary2003@gmail.com> Co-authored-by: Vladimir Glafirov <vglafirov@gitlab.com> Co-authored-by: Matt Carey <mcarey@cloudflare.com>
244 lines
8.1 KiB
TypeScript
244 lines
8.1 KiB
TypeScript
import { describe, expect, test } from "bun:test"
|
|
import { createStore } from "solid-js/store"
|
|
import { QueryClient } from "@tanstack/solid-query"
|
|
import type { Config, OpencodeClient, Project } from "@opencode-ai/sdk/v2/client"
|
|
import type { AgentApi, CatalogApi, CommandApi, ReferenceApi } from "@opencode-ai/client/promise"
|
|
import type { NormalizedProviderListResponse } from "@opencode-ai/session-ui/context"
|
|
import {
|
|
bootstrapDirectory,
|
|
loadAgentsQuery,
|
|
loadCommands,
|
|
loadPathQuery,
|
|
loadProjectsQuery,
|
|
loadProvidersQuery,
|
|
loadReferencesQuery,
|
|
} from "./bootstrap"
|
|
import type { State, VcsCache } from "./types"
|
|
import { ServerScope } from "@/utils/server-scope"
|
|
import type { ServerApi } from "@/utils/server"
|
|
|
|
type ProjectApi = ServerApi["project"]
|
|
|
|
const provider = { all: new Map(), connected: [], default: {} } satisfies NormalizedProviderListResponse
|
|
const api = {
|
|
agent: { list: async () => ({ location: {}, data: [] }) },
|
|
provider: { list: async () => ({ location: {}, data: [] }) },
|
|
model: {
|
|
list: async () => ({ location: {}, data: [] }),
|
|
default: async () => ({ location: {}, data: null }),
|
|
},
|
|
permission: { request: { list: async () => ({ location: {}, data: [] }) } },
|
|
project: {
|
|
list: async () => [],
|
|
current: async () => ({ id: "project", directory: "/project" }),
|
|
},
|
|
question: { request: { list: async () => ({ location: {}, data: [] }) } },
|
|
reference: { list: async () => ({ location: {}, data: [] }) },
|
|
vcs: { get: async () => ({ location: {}, data: {} }) },
|
|
} as unknown as ServerApi
|
|
|
|
function directoryState() {
|
|
return createStore<State>({
|
|
status: "loading",
|
|
agent: [],
|
|
command: [],
|
|
reference: [],
|
|
project: "",
|
|
projectMeta: undefined,
|
|
icon: undefined,
|
|
provider_ready: true,
|
|
provider,
|
|
config: {},
|
|
path: { state: "", config: "", worktree: "/project", directory: "/project", home: "/home" },
|
|
session: [],
|
|
sessionTotal: 0,
|
|
session_status: {},
|
|
session_working(id: string) {
|
|
return this.session_status[id]?.type !== "idle"
|
|
},
|
|
session_diff: {},
|
|
todo: {},
|
|
permission: {},
|
|
question: {},
|
|
mcp_ready: true,
|
|
mcp: {},
|
|
mcp_resource: {},
|
|
lsp_ready: true,
|
|
lsp: [],
|
|
vcs: undefined,
|
|
limit: 5,
|
|
message: {},
|
|
session_message: {},
|
|
part: {},
|
|
part_text_accum_delta: {},
|
|
})
|
|
}
|
|
|
|
describe("bootstrapDirectory", () => {
|
|
test("uses legacy MCP endpoints while refreshing a v1 directory", async () => {
|
|
const mcpReads: string[] = []
|
|
const [store, setStore] = directoryState()
|
|
|
|
await bootstrapDirectory({
|
|
directory: "/project",
|
|
scope: ServerScope.local,
|
|
mcp: true,
|
|
global: {
|
|
config: {} satisfies Config,
|
|
path: { state: "", config: "", worktree: "/project", directory: "/project", home: "/home" },
|
|
project: [{ id: "project", worktree: "/project" } as Project],
|
|
provider,
|
|
},
|
|
sdk: {
|
|
app: { agents: async () => ({ data: [{ name: "build", mode: "primary" }] }) },
|
|
config: { get: async () => ({ data: {} }) },
|
|
session: { status: async () => ({ data: {} }) },
|
|
vcs: { get: async () => ({ data: undefined }) },
|
|
command: {
|
|
list: async () => {
|
|
mcpReads.push("command")
|
|
return { data: [] }
|
|
},
|
|
},
|
|
permission: { list: async () => ({ data: [] }) },
|
|
question: { list: async () => ({ data: [] }) },
|
|
v2: { reference: { list: async () => ({ data: { data: [] } }) } },
|
|
mcp: {
|
|
status: async () => {
|
|
mcpReads.push("status")
|
|
return { data: {} }
|
|
},
|
|
},
|
|
experimental: {
|
|
resource: {
|
|
list: async () => {
|
|
mcpReads.push("resource")
|
|
return { data: {} }
|
|
},
|
|
},
|
|
},
|
|
provider: { list: async () => ({ data: { all: [], connected: [], default: {} } }) },
|
|
} as unknown as OpencodeClient,
|
|
api,
|
|
store,
|
|
setStore,
|
|
vcsCache: { setStore() {} } as unknown as VcsCache,
|
|
loadSessions() {},
|
|
translate: (key) => key,
|
|
queryClient: new QueryClient(),
|
|
protocol: Promise.resolve("v1"),
|
|
})
|
|
|
|
expect(store.status).toBe("partial")
|
|
|
|
await new Promise((resolve) => setTimeout(resolve, 80))
|
|
|
|
expect(store.status).toBe("complete")
|
|
expect(mcpReads.sort()).toEqual(["command", "resource", "status"])
|
|
})
|
|
})
|
|
|
|
describe("query keys", () => {
|
|
test("partitions identical directories by server scope", () => {
|
|
const client = {} as Parameters<typeof loadPathQuery>[2]
|
|
const api = {} as CatalogApi
|
|
const remote = "https://debian.example" as typeof ServerScope.local
|
|
|
|
expect([...loadPathQuery(ServerScope.local, "/repo", client).queryKey]).toEqual(["local", "/repo", "path"])
|
|
expect([...loadPathQuery(remote, "/repo", client).queryKey]).toEqual(["https://debian.example", "/repo", "path"])
|
|
expect([...loadProvidersQuery(remote, null, api).queryKey]).toEqual(["https://debian.example", null, "providers"])
|
|
})
|
|
|
|
test("loads the current provider and model catalog", async () => {
|
|
const calls: unknown[] = []
|
|
const api = {
|
|
provider: {
|
|
list: async (input: unknown) => {
|
|
calls.push(["provider", input])
|
|
return { location: {}, data: [{ id: "openai", name: "OpenAI", package: "@ai-sdk/openai" }] }
|
|
},
|
|
},
|
|
model: {
|
|
list: async (input: unknown) => {
|
|
calls.push(["model", input])
|
|
return { location: {}, data: [] }
|
|
},
|
|
default: async (input: unknown) => {
|
|
calls.push(["default", input])
|
|
return { location: {}, data: null }
|
|
},
|
|
},
|
|
} as unknown as CatalogApi
|
|
|
|
const result = await new QueryClient().fetchQuery(loadProvidersQuery(ServerScope.local, "/repo", api))
|
|
|
|
expect(calls).toEqual([
|
|
["provider", { location: { directory: "/repo" } }],
|
|
["model", { location: { directory: "/repo" } }],
|
|
["default", { location: { directory: "/repo" } }],
|
|
])
|
|
expect(result.connected).toEqual(["openai"])
|
|
})
|
|
|
|
test("loads agents from the current location-scoped endpoint", async () => {
|
|
const calls: unknown[] = []
|
|
const api = {
|
|
list: async (input: unknown) => {
|
|
calls.push(input)
|
|
return { location: {}, data: [] }
|
|
},
|
|
} as unknown as AgentApi
|
|
|
|
const result = await new QueryClient().fetchQuery(loadAgentsQuery(ServerScope.local, "/repo", api))
|
|
|
|
expect(calls).toEqual([{ location: { directory: "/repo" } }])
|
|
expect(result).toEqual([])
|
|
})
|
|
|
|
test("loads commands from the current location-scoped endpoint", async () => {
|
|
const calls: unknown[] = []
|
|
const api = {
|
|
list: async (input: unknown) => {
|
|
calls.push(input)
|
|
return {
|
|
location: {},
|
|
data: [{ name: "review", template: "Review files" /* source: "command" as const */ }],
|
|
}
|
|
},
|
|
} as unknown as CommandApi
|
|
|
|
const result = await loadCommands("/repo", api)
|
|
|
|
expect(calls).toEqual([{ location: { directory: "/repo" } }])
|
|
expect(result).toEqual([{ name: "review", template: "Review files" /* source: "command" */ }])
|
|
})
|
|
|
|
test("loads projects from the current endpoint", async () => {
|
|
const api = {
|
|
list: async () => [
|
|
{ id: "b", worktree: "/b", time: { created: 1, updated: 1 }, sandboxes: [] },
|
|
{ id: "a", worktree: "/a", time: { created: 1, updated: 1 }, sandboxes: [] },
|
|
],
|
|
} as unknown as ProjectApi
|
|
|
|
const result = await new QueryClient().fetchQuery(loadProjectsQuery(ServerScope.local, api))
|
|
|
|
expect(result.map((project) => project.id)).toEqual(["a", "b"])
|
|
})
|
|
|
|
test("loads references from the current location-scoped endpoint", async () => {
|
|
const calls: unknown[] = []
|
|
const api = {
|
|
list: async (input: unknown) => {
|
|
calls.push(input)
|
|
return { location: {}, data: [{ name: "AGENTS.md", path: "/repo/AGENTS.md", source: "instructions" }] }
|
|
},
|
|
} as unknown as ReferenceApi
|
|
|
|
const result = await new QueryClient().fetchQuery(loadReferencesQuery(ServerScope.local, "/repo", api))
|
|
|
|
expect(calls).toEqual([{ location: { directory: "/repo" } }])
|
|
expect(result).toHaveLength(1)
|
|
})
|
|
})
|