test(sync): cover the archived session query boundary

Add coverage for the archived-only narrowing: mixed pages return archived
records only, active pages stay unfiltered, a page that is full upstream
but fully filtered out keeps paginating, onPage receives only accepted
records, and id dedupe still stops a repeating page.

Document the OpenCode `archived` flag semantics in the owning store
documentation.
This commit is contained in:
Alexandre Reyes Martins
2026-08-02 11:51:25 +00:00
parent 7eb43fc5d5
commit b54741bab5
2 changed files with 159 additions and 0 deletions
+1
View File
@@ -56,6 +56,7 @@ User-visible session ordering is also not owned by the global cache array order.
Global refresh rules:
- The OpenCode `archived` list flag means "also include archived sessions": the server only drops its `time_archived IS NULL` condition. `listGlobalSessionPages` therefore narrows archived requests to records carrying `time.archived`, at the data boundary, so the archived cache never holds active sessions and no consumer has to re-derive that. Pagination progress stays measured on the raw response, so a page that is full upstream but filtered out here is not mistaken for the last page.
- Per-directory refresh is bounded to two requests across callers and prioritizes the current directory.
- Each directory is an independent completeness scope. A failed directory preserves its previous sessions while successful directories reconcile normally.
- Fetch failure must remain distinguishable from a successful empty list; failed scopes cannot destructively clear cached sessions.
@@ -96,6 +96,164 @@ describe('listGlobalSessionPages', () => {
expect(sessions.map((session) => session.id)).toEqual(['ses_root', 'ses_child_1', 'ses_child_2'])
})
test('returns only archived sessions when archived pages are requested', async () => {
const apiClient = {
experimental: {
session: {
list: async () => ({
// The server treats `archived: true` as "include archived", so the
// response mixes active and archived records.
data: [
{ id: 'ses_active', time: { created: 1, updated: 20 } },
{ id: 'ses_archived', time: { created: 1, updated: 10, archived: 15 } },
],
response: { headers: new Headers() },
}),
},
},
} as unknown as OpencodeClient
const sessions = await listGlobalSessionPages(apiClient, { archived: true, pageSize: 500 })
expect(sessions.map((session) => session.id)).toEqual(['ses_archived'])
})
test('keeps every record when active pages are requested', async () => {
const apiClient = {
experimental: {
session: {
list: async () => ({
data: [
{ id: 'ses_active_1', time: { updated: 20 } },
{ id: 'ses_active_2', time: { updated: 10 } },
],
response: { headers: new Headers() },
}),
},
},
} as unknown as OpencodeClient
const sessions = await listGlobalSessionPages(apiClient, { archived: false, pageSize: 500 })
expect(sessions.map((session) => session.id)).toEqual(['ses_active_1', 'ses_active_2'])
})
test('keeps paginating archived pages that are full of non-archived records', async () => {
const calls: Array<Record<string, unknown>> = []
const apiClient = {
experimental: {
session: {
list: async (options: Record<string, unknown>) => {
calls.push(options)
if (options.cursor === undefined) {
return {
data: [
{ id: 'ses_active_1', time: { updated: 30 } },
{ id: 'ses_active_2', time: { updated: 20 } },
],
response: { headers: new Headers({ 'x-next-cursor': '20' }) },
}
}
return {
data: [
{ id: 'ses_archived', time: { updated: 10, archived: 12 } },
],
response: { headers: new Headers() },
}
},
},
},
} as unknown as OpencodeClient
const sessions = await listGlobalSessionPages(apiClient, { archived: true, pageSize: 2 })
// A page that is full upstream but fully filtered out here must not be
// mistaken for the last page: pagination progress is measured on the raw
// response, not on the accepted records.
expect(calls).toHaveLength(2)
expect(sessions.map((session) => session.id)).toEqual(['ses_archived'])
})
test('reports only accepted records to onPage for archived pages', async () => {
const pages: string[][] = []
const apiClient = {
experimental: {
session: {
list: async () => ({
data: [
{ id: 'ses_active', time: { updated: 20 } },
{ id: 'ses_archived', time: { updated: 10, archived: 12 } },
],
response: { headers: new Headers() },
}),
},
},
} as unknown as OpencodeClient
await listGlobalSessionPages(apiClient, {
archived: true,
pageSize: 500,
onPage: (sessions) => pages.push(sessions.map((session) => session.id)),
})
expect(pages).toEqual([['ses_archived']])
})
test('does not notify onPage for an archived page with no archived records', async () => {
const pages: string[][] = []
const apiClient = {
experimental: {
session: {
list: async () => ({
data: [{ id: 'ses_active', time: { updated: 20 } }],
response: { headers: new Headers() },
}),
},
},
} as unknown as OpencodeClient
const sessions = await listGlobalSessionPages(apiClient, {
archived: true,
pageSize: 500,
onPage: (page) => pages.push(page.map((session) => session.id)),
})
expect(sessions).toEqual([])
expect(pages).toEqual([])
})
test('dedupes archived records by id and stops when a page repeats known ids', async () => {
const calls: Array<Record<string, unknown>> = []
const page = [
{ id: 'ses_archived_1', time: { updated: 30, archived: 31 } },
{ id: 'ses_archived_2', time: { updated: 20, archived: 21 } },
]
const apiClient = {
experimental: {
session: {
list: async (options: Record<string, unknown>) => {
calls.push(options)
return {
data: page,
response: {
headers: new Headers({
'x-next-cursor': options.cursor === undefined ? '20' : '10',
}),
},
}
},
},
},
} as unknown as OpencodeClient
const sessions = await listGlobalSessionPages(apiClient, { archived: true, pageSize: 2 })
// The second page repeats ids already seen, so the dedupe guard stops the
// loop and no record is returned twice.
expect(calls).toHaveLength(2)
expect(sessions.map((session) => session.id)).toEqual(['ses_archived_1', 'ses_archived_2'])
})
test('retries SDK error responses before treating the load as failed', async () => {
let calls = 0
const apiClient = {