refactor(tui): wire generated client reads (#34381)

This commit is contained in:
Kit Langton 2026-06-29 11:36:10 -04:00 committed by GitHub
commit 381d67572e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
28 changed files with 509 additions and 427 deletions

View file

@ -7,7 +7,7 @@ import { ProjectProvider, useProject } from "../../../../src/context/project"
import { SDKProvider } from "../../../../src/context/sdk"
import { SyncProvider, useSync } from "../../../../src/context/sync"
import { ExitProvider } from "../../../../src/context/exit"
import { createClient, createEventStream, createFetch, type FetchHandler } from "../../../fixture/tui-sdk"
import { createApi, createClient, createEventStream, createFetch, type FetchHandler } from "../../../fixture/tui-sdk"
import { TestTuiContexts } from "../../../fixture/tui-environment"
export { createEventStream, createFetch, directory, json, worktree } from "../../../fixture/tui-sdk"
@ -47,7 +47,7 @@ export async function mount(override?: FetchHandler, state?: string) {
<TestTuiContexts paths={state ? { state } : undefined}>
<ArgsProvider>
<KVProvider>
<SDKProvider client={createClient(calls.fetch)}>
<SDKProvider client={createClient(calls.fetch)} api={createApi(calls.fetch)}>
<ProjectProvider>
<ExitProvider exit={() => {}}>
<SyncProvider>

View file

@ -6,7 +6,7 @@ import { onMount } from "solid-js"
import { ProjectProvider } from "../../../src/context/project"
import { SDKProvider } from "../../../src/context/sdk"
import { DataProvider, useData } from "../../../src/context/data"
import { createClient, createEventStream, createFetch, directory, json } from "../../fixture/tui-sdk"
import { createApi, createClient, createEventStream, createFetch, directory, json } from "../../fixture/tui-sdk"
import { TestTuiContexts } from "../../fixture/tui-environment"
async function wait(fn: () => boolean, timeout = 2000) {
@ -69,7 +69,7 @@ test("refreshes resources into reactive getters", async () => {
const app = await testRender(() => (
<TestTuiContexts>
<SDKProvider client={createClient(calls.fetch)}>
<SDKProvider client={createClient(calls.fetch)} api={createApi(calls.fetch)}>
<ProjectProvider>
<DataProvider>
<Probe />
@ -139,7 +139,7 @@ test("reconnects the event stream and bootstraps fresh data", async () => {
const app = await testRender(() => (
<TestTuiContexts>
<SDKProvider client={createClient(calls.fetch)}>
<SDKProvider client={createClient(calls.fetch)} api={createApi(calls.fetch)}>
<ProjectProvider>
<DataProvider>
<Probe />
@ -172,8 +172,7 @@ test("reconnects the event stream and bootstraps fresh data", async () => {
test("tracks session status from active sessions and execution events", async () => {
const events = createEventStream()
const calls = createFetch((url) => {
if (url.pathname === "/api/session/active")
return json({ data: { "session-active": { type: "running" } } })
if (url.pathname === "/api/session/active") return json({ data: { "session-active": { type: "running" } } })
}, events)
let data!: ReturnType<typeof useData>
@ -184,7 +183,7 @@ test("tracks session status from active sessions and execution events", async ()
const app = await testRender(() => (
<TestTuiContexts>
<SDKProvider client={createClient(calls.fetch)}>
<SDKProvider client={createClient(calls.fetch)} api={createApi(calls.fetch)}>
<ProjectProvider>
<DataProvider>
<Probe />
@ -296,7 +295,7 @@ test("refreshes integrations after integration updates", async () => {
const app = await testRender(() => (
<TestTuiContexts>
<SDKProvider client={createClient(calls.fetch)}>
<SDKProvider client={createClient(calls.fetch)} api={createApi(calls.fetch)}>
<ProjectProvider>
<DataProvider>
<Probe />
@ -337,7 +336,7 @@ test("refreshes effective catalog data after catalog updates", async () => {
const app = await testRender(() => (
<TestTuiContexts>
<SDKProvider client={createClient(calls.fetch)}>
<SDKProvider client={createClient(calls.fetch)} api={createApi(calls.fetch)}>
<ProjectProvider>
<DataProvider>
<box />
@ -382,7 +381,7 @@ test("refreshes references after updates", async () => {
const app = await testRender(() => (
<TestTuiContexts>
<SDKProvider client={createClient(calls.fetch)}>
<SDKProvider client={createClient(calls.fetch)} api={createApi(calls.fetch)}>
<ProjectProvider>
<DataProvider>
<Probe />
@ -415,7 +414,7 @@ test("adds and dismisses permission requests from live events", async () => {
const app = await testRender(() => (
<TestTuiContexts>
<SDKProvider client={createClient(calls.fetch)}>
<SDKProvider client={createClient(calls.fetch)} api={createApi(calls.fetch)}>
<ProjectProvider>
<DataProvider>
<Probe />
@ -480,7 +479,7 @@ test("adds and dismisses question requests from live events", async () => {
const app = await testRender(() => (
<TestTuiContexts>
<SDKProvider client={createClient(calls.fetch)}>
<SDKProvider client={createClient(calls.fetch)} api={createApi(calls.fetch)}>
<ProjectProvider>
<DataProvider>
<Probe />
@ -548,7 +547,7 @@ test("settles pending tools when a live failure arrives", async () => {
const app = await testRender(() => (
<TestTuiContexts>
<SDKProvider client={createClient(calls.fetch)}>
<SDKProvider client={createClient(calls.fetch)} api={createApi(calls.fetch)}>
<ProjectProvider>
<DataProvider>
<Probe />
@ -677,7 +676,7 @@ test("renders admitted prompts only after they become model-visible", async () =
const app = await testRender(() => (
<TestTuiContexts>
<SDKProvider client={createClient(calls.fetch)}>
<SDKProvider client={createClient(calls.fetch)} api={createApi(calls.fetch)}>
<ProjectProvider>
<DataProvider>
<Probe />
@ -750,7 +749,7 @@ test("projects live context updates with their message ID", async () => {
const app = await testRender(() => (
<TestTuiContexts>
<SDKProvider client={createClient(calls.fetch)}>
<SDKProvider client={createClient(calls.fetch)} api={createApi(calls.fetch)}>
<ProjectProvider>
<DataProvider>
<Probe />

View file

@ -1,12 +1,13 @@
/** @jsxImportSource @opentui/solid */
import { describe, expect, test } from "bun:test"
import type { OpenCodeClient } from "@opencode-ai/client"
import { testRender } from "@opentui/solid"
import type { OpencodeClient, V2Event } from "@opencode-ai/sdk/v2"
import { onMount } from "solid-js"
import { ProjectProvider, useProject } from "../../../src/context/project"
import { SDKProvider, useSDK } from "../../../src/context/sdk"
import { useEvent } from "../../../src/context/event"
import { createClient, createEventStream, createFetch, directory } from "../../fixture/tui-sdk"
import { createApi, createClient, createEventStream, createFetch } from "../../fixture/tui-sdk"
import { TestTuiContexts } from "../../fixture/tui-environment"
const projectID = "proj_test"
@ -46,7 +47,7 @@ function update(version: string): V2Event {
}
}
async function mount(reload?: () => Promise<OpencodeClient>) {
async function mount(reload?: () => Promise<{ client: OpencodeClient; api: OpenCodeClient }>) {
const events = createEventStream()
const calls = createFetch(undefined, events)
const seen: V2Event[] = []
@ -60,7 +61,7 @@ async function mount(reload?: () => Promise<OpencodeClient>) {
const app = await testRender(() => (
<TestTuiContexts>
<SDKProvider client={createClient(calls.fetch)} reload={reload}>
<SDKProvider client={createClient(calls.fetch)} api={createApi(calls.fetch)} reload={reload}>
<ProjectProvider>
<Probe
onReady={async (ctx) => {
@ -150,7 +151,8 @@ describe("useEvent", () => {
test("reloads the host and reconnects the event stream", async () => {
let calls = 0
const events = createEventStream()
const replacement = createClient(createFetch(undefined, events).fetch)
const replacementCalls = createFetch(undefined, events)
const replacement = { client: createClient(replacementCalls.fetch), api: createApi(replacementCalls.fetch) }
const { app, sdk, seen } = await mount(async () => {
calls += 1
return replacement
@ -164,19 +166,21 @@ describe("useEvent", () => {
await wait(() => seen.some((item) => item.type === "vcs.branch.updated" && item.data.branch === "reloaded"))
expect(calls).toBe(1)
expect(sdk.client).toBe(replacement)
expect(sdk.client).toBe(replacement.client)
expect(sdk.api).toBe(replacement.api)
} finally {
app.renderer.destroy()
}
})
test("keeps the current event stream alive while the host reload is pending", async () => {
let complete!: (client: OpencodeClient) => void
const pending = new Promise<OpencodeClient>((resolve) => {
let complete!: (client: { client: OpencodeClient; api: OpenCodeClient }) => void
const pending = new Promise<{ client: OpencodeClient; api: OpenCodeClient }>((resolve) => {
complete = resolve
})
const replacementEvents = createEventStream()
const replacement = createClient(createFetch(undefined, replacementEvents).fetch)
const replacementCalls = createFetch(undefined, replacementEvents)
const replacement = { client: createClient(replacementCalls.fetch), api: createApi(replacementCalls.fetch) }
const { app, emit, sdk, seen } = await mount(() => pending)
try {
@ -188,7 +192,8 @@ describe("useEvent", () => {
expect(sdk.connection.status()).toBe("connected")
complete(replacement)
await reload
expect(sdk.client).toBe(replacement)
expect(sdk.client).toBe(replacement.client)
expect(sdk.api).toBe(replacement.api)
} finally {
app.renderer.destroy()
}