feat(tui): maintain session family index in data context
This commit is contained in:
parent
eabf85aea2
commit
ce228bfd7c
5 changed files with 183 additions and 21 deletions
|
|
@ -915,3 +915,122 @@ test("projects live context updates with their message ID", async () => {
|
|||
app.renderer.destroy()
|
||||
}
|
||||
})
|
||||
|
||||
function sessionInfo(id: string, parentID: string | undefined) {
|
||||
return {
|
||||
id,
|
||||
parentID,
|
||||
projectID: "proj_test",
|
||||
cost: 0,
|
||||
tokens: { input: 0, output: 0, reasoning: 0, cache: { read: 0, write: 0 } },
|
||||
time: { created: 0, updated: 0 },
|
||||
title: id,
|
||||
location: { directory },
|
||||
}
|
||||
}
|
||||
|
||||
// Mounts a DataProvider whose `/api/session/:id` responses are driven by the
|
||||
// given parent map (sessionID -> parentID). Roots omit the entry. Reused across
|
||||
// the family-index tests below.
|
||||
async function mountData(parents: Record<string, string>) {
|
||||
const calls = createFetch((url) => {
|
||||
const match = url.pathname.match(/^\/api\/session\/([^/]+)$/)
|
||||
if (match && match[1] !== "active") return json({ data: sessionInfo(match[1], parents[match[1]]) })
|
||||
})
|
||||
let data!: ReturnType<typeof useData>
|
||||
let ready!: () => void
|
||||
const mounted = new Promise<void>((resolve) => {
|
||||
ready = resolve
|
||||
})
|
||||
function Probe() {
|
||||
data = useData()
|
||||
onMount(ready)
|
||||
return <box />
|
||||
}
|
||||
const app = await testRender(() => (
|
||||
<TestTuiContexts>
|
||||
<SDKProvider client={createClient(calls.fetch)} api={createApi(calls.fetch)}>
|
||||
<ProjectProvider>
|
||||
<DataProvider>
|
||||
<Probe />
|
||||
</DataProvider>
|
||||
</ProjectProvider>
|
||||
</SDKProvider>
|
||||
</TestTuiContexts>
|
||||
))
|
||||
await mounted
|
||||
return { data, app }
|
||||
}
|
||||
|
||||
test("groups an orphan child under its missing parent until the root arrives", async () => {
|
||||
const { data, app } = await mountData({ child: "root" })
|
||||
try {
|
||||
await data.session.refresh("child")
|
||||
// Parent info is absent, so the missing parent is the furthest-known ancestor.
|
||||
expect(data.session.root("child")).toBe("root")
|
||||
expect(data.session.family("child")).toEqual(["child"])
|
||||
expect(data.session.family("root")).toEqual(["child"])
|
||||
|
||||
await data.session.refresh("root")
|
||||
expect(data.session.root("root")).toBe("root")
|
||||
// The tentative root entry folds into the now-known root's family.
|
||||
expect(data.session.family("child")).toEqual(["child", "root"])
|
||||
expect(data.session.family("root")).toEqual(["child", "root"])
|
||||
} finally {
|
||||
app.renderer.destroy()
|
||||
}
|
||||
})
|
||||
|
||||
test("indexes arbitrarily deep nesting under a single root", async () => {
|
||||
const { data, app } = await mountData({ grandchild: "child", child: "root" })
|
||||
try {
|
||||
await data.session.refresh("grandchild")
|
||||
expect(data.session.root("grandchild")).toBe("child")
|
||||
expect(data.session.family("grandchild")).toEqual(["grandchild"])
|
||||
|
||||
await data.session.refresh("child")
|
||||
// grandchild's tentative family (keyed by the missing "child") merges up
|
||||
// toward the still-missing "root".
|
||||
expect(data.session.root("child")).toBe("root")
|
||||
expect(data.session.family("grandchild")).toEqual(["grandchild", "child"])
|
||||
|
||||
await data.session.refresh("root")
|
||||
expect(data.session.root("grandchild")).toBe("root")
|
||||
expect(data.session.root("child")).toBe("root")
|
||||
expect(data.session.family("root")).toEqual(["grandchild", "child", "root"])
|
||||
} finally {
|
||||
app.renderer.destroy()
|
||||
}
|
||||
})
|
||||
|
||||
test("re-registering an existing session is idempotent", async () => {
|
||||
const { data, app } = await mountData({ grandchild: "child", child: "root" })
|
||||
try {
|
||||
await data.session.refresh("grandchild")
|
||||
await data.session.refresh("child")
|
||||
await data.session.refresh("root")
|
||||
const before = data.session.family("root")
|
||||
expect(before).toEqual(["grandchild", "child", "root"])
|
||||
|
||||
await data.session.refresh("child")
|
||||
await data.session.refresh("root")
|
||||
await data.session.refresh("grandchild")
|
||||
expect(data.session.family("root")).toEqual(before)
|
||||
expect(data.session.family("root")).toHaveLength(3)
|
||||
} finally {
|
||||
app.renderer.destroy()
|
||||
}
|
||||
})
|
||||
|
||||
test("stops at the last non-repeating ancestor on a parent cycle", async () => {
|
||||
const { data, app } = await mountData({ x: "y", y: "x" })
|
||||
try {
|
||||
await data.session.refresh("x")
|
||||
await data.session.refresh("y")
|
||||
// Does not hang; walking up from "y" stops before re-entering "x".
|
||||
expect(data.session.root("y")).toBe("x")
|
||||
expect(data.session.family("y")).toEqual(["x", "y"])
|
||||
} finally {
|
||||
app.renderer.destroy()
|
||||
}
|
||||
})
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue