mirror of
https://github.com/anomalyco/opencode.git
synced 2026-04-24 06:45:22 +00:00
test meh
This commit is contained in:
134
packages/opencode/test/cli/tui/plugin-api-state-facade.test.ts
Normal file
134
packages/opencode/test/cli/tui/plugin-api-state-facade.test.ts
Normal file
@@ -0,0 +1,134 @@
|
||||
import { expect, test } from "bun:test"
|
||||
import fs from "fs/promises"
|
||||
import path from "path"
|
||||
import { pathToFileURL } from "url"
|
||||
import { tmpdir } from "../../fixture/fixture"
|
||||
import { mockTuiRuntime } from "../../fixture/tui-runtime"
|
||||
import { createTuiPluginApi } from "../../fixture/tui-plugin"
|
||||
|
||||
const { TuiPluginRuntime } = await import("../../../src/cli/cmd/tui/plugin/runtime")
|
||||
|
||||
test("exposes expanded plugin state facade", async () => {
|
||||
await using tmp = await tmpdir({
|
||||
init: async (dir) => {
|
||||
const pluginPath = path.join(dir, "state-plugin.ts")
|
||||
const pluginSpec = pathToFileURL(pluginPath).href
|
||||
const marker = path.join(dir, "state-marker.json")
|
||||
|
||||
await Bun.write(
|
||||
pluginPath,
|
||||
`export default {
|
||||
tui: async (api, options) => {
|
||||
const row = {
|
||||
path_directory: api.state.path.directory,
|
||||
path_config: api.state.path.config,
|
||||
vcs_branch: api.state.vcs?.branch ?? null,
|
||||
workspace_count: api.state.workspace.list().length,
|
||||
workspace_hit: api.state.workspace.get(options.workspace_id)?.id ?? null,
|
||||
diff_count: api.state.session.diff(options.session_id).length,
|
||||
todo_count: api.state.session.todo(options.session_id).length,
|
||||
status: api.state.session.status(options.session_id)?.type ?? null,
|
||||
permission_count: api.state.session.permission(options.session_id).length,
|
||||
question_count: api.state.session.question(options.session_id).length,
|
||||
part_count: api.state.part(options.message_id).length,
|
||||
}
|
||||
await Bun.write(options.marker, JSON.stringify(row))
|
||||
},
|
||||
}
|
||||
`,
|
||||
)
|
||||
|
||||
return {
|
||||
marker,
|
||||
pluginSpec,
|
||||
}
|
||||
},
|
||||
})
|
||||
|
||||
const restore = mockTuiRuntime(tmp.path, [
|
||||
[
|
||||
tmp.extra.pluginSpec,
|
||||
{
|
||||
marker: tmp.extra.marker,
|
||||
session_id: "ses_1",
|
||||
message_id: "msg_1",
|
||||
workspace_id: "ws_1",
|
||||
},
|
||||
],
|
||||
])
|
||||
|
||||
try {
|
||||
await TuiPluginRuntime.init(
|
||||
createTuiPluginApi({
|
||||
state: {
|
||||
path: {
|
||||
state: "/tmp/project/.opencode/state",
|
||||
config: "/tmp/project/.opencode/config",
|
||||
worktree: "/tmp/project",
|
||||
directory: "/tmp/project",
|
||||
},
|
||||
vcs: {
|
||||
branch: "dev",
|
||||
},
|
||||
workspace: {
|
||||
list() {
|
||||
return [
|
||||
{
|
||||
id: "ws_1",
|
||||
type: "worktree",
|
||||
branch: "dev",
|
||||
name: "Workspace 1",
|
||||
directory: "/tmp/ws_1",
|
||||
extra: null,
|
||||
projectID: "project_1",
|
||||
},
|
||||
]
|
||||
},
|
||||
get(workspaceID) {
|
||||
if (workspaceID !== "ws_1") return
|
||||
return {
|
||||
id: "ws_1",
|
||||
type: "worktree",
|
||||
branch: "dev",
|
||||
name: "Workspace 1",
|
||||
directory: "/tmp/ws_1",
|
||||
extra: null,
|
||||
projectID: "project_1",
|
||||
}
|
||||
},
|
||||
},
|
||||
session: {
|
||||
diff(sessionID) {
|
||||
if (sessionID !== "ses_1") return []
|
||||
return [{ file: "src/app.ts", additions: 2, deletions: 1 }]
|
||||
},
|
||||
todo(sessionID) {
|
||||
if (sessionID !== "ses_1") return []
|
||||
return [{ content: "ship", status: "pending" }]
|
||||
},
|
||||
status(sessionID) {
|
||||
if (sessionID !== "ses_1") return
|
||||
return { type: "idle" }
|
||||
},
|
||||
},
|
||||
},
|
||||
}),
|
||||
)
|
||||
|
||||
const row = JSON.parse(await fs.readFile(tmp.extra.marker, "utf8")) as Record<string, unknown>
|
||||
expect(row.path_directory).toBe("/tmp/project")
|
||||
expect(row.path_config).toBe("/tmp/project/.opencode/config")
|
||||
expect(row.vcs_branch).toBe("dev")
|
||||
expect(row.workspace_count).toBe(1)
|
||||
expect(row.workspace_hit).toBe("ws_1")
|
||||
expect(row.diff_count).toBe(1)
|
||||
expect(row.todo_count).toBe(1)
|
||||
expect(row.status).toBe("idle")
|
||||
expect(row.permission_count).toBe(0)
|
||||
expect(row.question_count).toBe(0)
|
||||
expect(row.part_count).toBe(0)
|
||||
} finally {
|
||||
await TuiPluginRuntime.dispose()
|
||||
restore()
|
||||
}
|
||||
})
|
||||
@@ -0,0 +1,80 @@
|
||||
import { expect, test } from "bun:test"
|
||||
import fs from "fs/promises"
|
||||
import path from "path"
|
||||
import { pathToFileURL } from "url"
|
||||
import { createOpencodeClient } from "@opencode-ai/sdk/v2"
|
||||
import { tmpdir } from "../../fixture/fixture"
|
||||
import { mockTuiRuntime } from "../../fixture/tui-runtime"
|
||||
import { createTuiPluginApi } from "../../fixture/tui-plugin"
|
||||
|
||||
const { TuiPluginRuntime } = await import("../../../src/cli/cmd/tui/plugin/runtime")
|
||||
|
||||
test("api.client tracks runtime client rebinds", async () => {
|
||||
await using tmp = await tmpdir({
|
||||
init: async (dir) => {
|
||||
const pluginPath = path.join(dir, "rebind-plugin.ts")
|
||||
const pluginSpec = pathToFileURL(pluginPath).href
|
||||
const marker = path.join(dir, "rebind-marker.json")
|
||||
|
||||
await Bun.write(
|
||||
pluginPath,
|
||||
`export default {
|
||||
tui: async (api, options) => {
|
||||
const one = api.client.global
|
||||
const one_scoped = api.scopedClient(options.workspace_id)
|
||||
api.workspace.set(options.workspace_id)
|
||||
const two = api.client.global
|
||||
const two_scoped = api.scopedClient(options.workspace_id)
|
||||
await Bun.write(
|
||||
options.marker,
|
||||
JSON.stringify({
|
||||
rebound: one !== two,
|
||||
scoped_ok: !!one_scoped && !!two_scoped,
|
||||
}),
|
||||
)
|
||||
},
|
||||
}
|
||||
`,
|
||||
)
|
||||
|
||||
return {
|
||||
marker,
|
||||
pluginSpec,
|
||||
}
|
||||
},
|
||||
})
|
||||
|
||||
const restore = mockTuiRuntime(tmp.path, [[tmp.extra.pluginSpec, { marker: tmp.extra.marker, workspace_id: "ws_1" }]])
|
||||
const local = createOpencodeClient({ baseUrl: "http://localhost:4096" })
|
||||
const scoped = createOpencodeClient({
|
||||
baseUrl: "http://localhost:4096",
|
||||
experimental_workspaceID: "ws_1",
|
||||
})
|
||||
let cur = local
|
||||
|
||||
try {
|
||||
await TuiPluginRuntime.init(
|
||||
createTuiPluginApi({
|
||||
client: () => cur,
|
||||
scopedClient: (_workspaceID?: string) => scoped,
|
||||
workspace: {
|
||||
current: () => undefined,
|
||||
set: (workspaceID) => {
|
||||
cur = workspaceID ? scoped : local
|
||||
},
|
||||
},
|
||||
}),
|
||||
)
|
||||
|
||||
const hit = JSON.parse(await fs.readFile(tmp.extra.marker, "utf8")) as {
|
||||
rebound: boolean
|
||||
scoped_ok: boolean
|
||||
}
|
||||
|
||||
expect(hit.rebound).toBe(true)
|
||||
expect(hit.scoped_ok).toBe(true)
|
||||
} finally {
|
||||
await TuiPluginRuntime.dispose()
|
||||
restore()
|
||||
}
|
||||
})
|
||||
@@ -81,7 +81,9 @@ function themeCurrent(): HostPluginApi["theme"]["current"] {
|
||||
}
|
||||
|
||||
type Opts = {
|
||||
client?: HostPluginApi["client"]
|
||||
client?: HostPluginApi["client"] | (() => HostPluginApi["client"])
|
||||
scopedClient?: HostPluginApi["scopedClient"]
|
||||
workspace?: Partial<HostPluginApi["workspace"]>
|
||||
renderer?: HostPluginApi["renderer"]
|
||||
count?: Count
|
||||
keybind?: Partial<HostPluginApi["keybind"]>
|
||||
@@ -91,7 +93,11 @@ type Opts = {
|
||||
ready?: HostPluginApi["state"]["ready"]
|
||||
config?: HostPluginApi["state"]["config"]
|
||||
provider?: HostPluginApi["state"]["provider"]
|
||||
path?: HostPluginApi["state"]["path"]
|
||||
vcs?: HostPluginApi["state"]["vcs"]
|
||||
workspace?: Partial<HostPluginApi["state"]["workspace"]>
|
||||
session?: Partial<HostPluginApi["state"]["session"]>
|
||||
part?: HostPluginApi["state"]["part"]
|
||||
lsp?: HostPluginApi["state"]["lsp"]
|
||||
mcp?: HostPluginApi["state"]["mcp"]
|
||||
}
|
||||
@@ -109,6 +115,22 @@ type Opts = {
|
||||
export function createTuiPluginApi(opts: Opts = {}): HostPluginApi {
|
||||
const kv: Record<string, unknown> = {}
|
||||
const count = opts.count
|
||||
const own = createOpencodeClient({
|
||||
baseUrl: "http://localhost:4096",
|
||||
})
|
||||
const fallback = () => own
|
||||
const read =
|
||||
typeof opts.client === "function"
|
||||
? opts.client
|
||||
: opts.client
|
||||
? () => opts.client as HostPluginApi["client"]
|
||||
: fallback
|
||||
const client = () => read()
|
||||
const scopedClient = opts.scopedClient ?? ((_workspaceID?: string) => client())
|
||||
const workspace: HostPluginApi["workspace"] = {
|
||||
current: opts.workspace?.current ?? (() => undefined),
|
||||
set: opts.workspace?.set ?? (() => {}),
|
||||
}
|
||||
let depth = 0
|
||||
let size: "medium" | "large" = "medium"
|
||||
const has = opts.theme?.has ?? (() => false)
|
||||
@@ -144,15 +166,12 @@ export function createTuiPluginApi(opts: Opts = {}): HostPluginApi {
|
||||
get version() {
|
||||
return opts.app?.version ?? "0.0.0-test"
|
||||
},
|
||||
get directory() {
|
||||
return opts.app?.directory ?? "~"
|
||||
},
|
||||
},
|
||||
client:
|
||||
opts.client ??
|
||||
createOpencodeClient({
|
||||
baseUrl: "http://localhost:4096",
|
||||
}),
|
||||
get client() {
|
||||
return client()
|
||||
},
|
||||
scopedClient,
|
||||
workspace,
|
||||
event: {
|
||||
on: () => {
|
||||
if (count) count.event_add += 1
|
||||
@@ -243,12 +262,26 @@ export function createTuiPluginApi(opts: Opts = {}): HostPluginApi {
|
||||
get provider() {
|
||||
return opts.state?.provider ?? []
|
||||
},
|
||||
get path() {
|
||||
return opts.state?.path ?? { state: "", config: "", worktree: "", directory: "" }
|
||||
},
|
||||
get vcs() {
|
||||
return opts.state?.vcs
|
||||
},
|
||||
workspace: {
|
||||
list: opts.state?.workspace?.list ?? (() => []),
|
||||
get: opts.state?.workspace?.get ?? (() => undefined),
|
||||
},
|
||||
session: {
|
||||
count: opts.state?.session?.count ?? (() => 0),
|
||||
diff: opts.state?.session?.diff ?? (() => []),
|
||||
todo: opts.state?.session?.todo ?? (() => []),
|
||||
messages: opts.state?.session?.messages ?? (() => []),
|
||||
status: opts.state?.session?.status ?? (() => undefined),
|
||||
permission: opts.state?.session?.permission ?? (() => []),
|
||||
question: opts.state?.session?.question ?? (() => []),
|
||||
},
|
||||
part: opts.state?.part ?? (() => []),
|
||||
lsp: opts.state?.lsp ?? (() => []),
|
||||
mcp: opts.state?.mcp ?? (() => []),
|
||||
},
|
||||
|
||||
41
packages/opencode/test/fixture/tui-runtime.ts
Normal file
41
packages/opencode/test/fixture/tui-runtime.ts
Normal file
@@ -0,0 +1,41 @@
|
||||
import { spyOn } from "bun:test"
|
||||
import path from "path"
|
||||
import { TuiConfig } from "../../src/config/tui"
|
||||
|
||||
type PluginSpec = string | [string, Record<string, unknown>]
|
||||
|
||||
function name(spec: string) {
|
||||
if (spec.startsWith("file://")) {
|
||||
return path.parse(new URL(spec).pathname).name
|
||||
}
|
||||
return path.parse(spec).name
|
||||
}
|
||||
|
||||
export function mockTuiRuntime(dir: string, plugin: PluginSpec[]) {
|
||||
process.env.OPENCODE_PLUGIN_META_FILE = path.join(dir, "plugin-meta.json")
|
||||
const meta = Object.fromEntries(
|
||||
plugin.map((item) => {
|
||||
const spec = Array.isArray(item) ? item[0] : item
|
||||
return [
|
||||
name(spec),
|
||||
{
|
||||
scope: "local" as const,
|
||||
source: path.join(dir, "tui.json"),
|
||||
},
|
||||
]
|
||||
}),
|
||||
)
|
||||
const get = spyOn(TuiConfig, "get").mockResolvedValue({
|
||||
plugin,
|
||||
plugin_meta: meta,
|
||||
})
|
||||
const wait = spyOn(TuiConfig, "waitForDependencies").mockResolvedValue()
|
||||
const cwd = spyOn(process, "cwd").mockImplementation(() => dir)
|
||||
|
||||
return () => {
|
||||
cwd.mockRestore()
|
||||
get.mockRestore()
|
||||
wait.mockRestore()
|
||||
delete process.env.OPENCODE_PLUGIN_META_FILE
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user