fix(mobile): recover pending questions after cold start
This commit is contained in:
@@ -70,6 +70,7 @@ import { getRuntimeKey } from "@/lib/runtime-switch"
|
||||
const {
|
||||
createEventRoutingIndex,
|
||||
handleEvent,
|
||||
resyncBlockingRequestsForActiveDirectory,
|
||||
resyncBlockingRequestsForDirectory,
|
||||
setActiveSession,
|
||||
} = await import("../sync-context")
|
||||
@@ -130,6 +131,34 @@ describe("resyncBlockingRequestsForDirectory", () => {
|
||||
expect(listPendingPermissionsCalls[0]).toEqual({ directories: ["/repo"] })
|
||||
})
|
||||
|
||||
test("resume recovery refreshes blocking requests only for the active materialized directory", async () => {
|
||||
const childStores = new ChildStoreManager()
|
||||
childStores.ensureChild("/resume-active", { bootstrap: false }).setState({
|
||||
session: [{ id: "ses_a", title: "ses_a", time: { created: 1, updated: 1 }, version: "1" } as State["session"][number]],
|
||||
})
|
||||
childStores.ensureChild("/resume-inactive", { bootstrap: false }).setState({
|
||||
session: [{ id: "ses_b", title: "ses_b", time: { created: 1, updated: 1 }, version: "1" } as State["session"][number]],
|
||||
})
|
||||
pendingQuestionsResponse = [buildQuestion()]
|
||||
|
||||
await resyncBlockingRequestsForActiveDirectory("/resume-active", childStores)
|
||||
|
||||
expect(listPendingQuestionsCalls).toEqual([{ directories: ["/resume-active"] }])
|
||||
expect(listPendingPermissionsCalls).toEqual([{ directories: ["/resume-active"] }])
|
||||
expect(childStores.getChild("/resume-active")?.getState().question.ses_a?.[0]?.id).toBe("que_1")
|
||||
expect(childStores.getChild("/resume-inactive")?.getState().question.ses_b).toBe(undefined)
|
||||
})
|
||||
|
||||
test("resume recovery does not materialize or fetch an unopened directory", async () => {
|
||||
const childStores = new ChildStoreManager()
|
||||
|
||||
await resyncBlockingRequestsForActiveDirectory("/unopened", childStores)
|
||||
|
||||
expect(childStores.getChild("/unopened")).toBe(undefined)
|
||||
expect(listPendingQuestionsCalls).toHaveLength(0)
|
||||
expect(listPendingPermissionsCalls).toHaveLength(0)
|
||||
})
|
||||
|
||||
test("merges newly fetched questions/permissions into the directory store", async () => {
|
||||
const store = createDirectoryStore({})
|
||||
pendingQuestionsResponse = [buildQuestion()]
|
||||
@@ -187,6 +216,36 @@ describe("resyncBlockingRequestsForDirectory", () => {
|
||||
expect(listPendingPermissionsCalls).toHaveLength(0)
|
||||
})
|
||||
|
||||
test("recovers an explicit session candidate before directory bootstrap materializes it", async () => {
|
||||
const store = createDirectoryStore({ session: [] })
|
||||
pendingQuestionsResponse = [buildQuestion()]
|
||||
|
||||
await resyncBlockingRequestsForDirectory("/repo", store, ["ses_a"], { includePermissions: false })
|
||||
|
||||
expect(listPendingQuestionsCalls).toEqual([{ directories: ["/repo"] }])
|
||||
expect(listPendingPermissionsCalls).toHaveLength(0)
|
||||
expect(store.getState().question.ses_a?.[0]?.id).toBe("que_1")
|
||||
})
|
||||
|
||||
test("limits explicit question-only recovery to the requested session", async () => {
|
||||
const store = createDirectoryStore({
|
||||
session: [
|
||||
{ id: "ses_a", title: "ses_a", time: { created: 1, updated: 1 }, version: "1" },
|
||||
{ id: "ses_b", title: "ses_b", time: { created: 1, updated: 1 }, version: "1" },
|
||||
] as State["session"],
|
||||
})
|
||||
pendingQuestionsResponse = [
|
||||
buildQuestion(),
|
||||
buildQuestion({ id: "que_b", sessionID: "ses_b" }),
|
||||
]
|
||||
|
||||
await resyncBlockingRequestsForDirectory("/repo", store, ["ses_a"], { includePermissions: false })
|
||||
|
||||
expect(store.getState().question.ses_a?.[0]?.id).toBe("que_1")
|
||||
expect(store.getState().question.ses_b).toBe(undefined)
|
||||
expect(listPendingPermissionsCalls).toHaveLength(0)
|
||||
})
|
||||
|
||||
// Regression: prior to the fix, listPendingQuestions silently returned [] on
|
||||
// fetch failure, indistinguishable from a successful empty server response.
|
||||
// The resync then walked the candidate set and deleted any question that
|
||||
|
||||
Reference in New Issue
Block a user