From b2fb8660a15ca70a3f912326c55438761453a1ff Mon Sep 17 00:00:00 2001 From: Day Dreamer <87640074+HugoXK@users.noreply.github.com> Date: Wed, 11 Nov 2026 17:46:39 +0800 Subject: [PATCH] core: the read-only reads test could not see a dry board or an empty metrics table (#2847) Two rows of core/test/readonly-window-reads.test.js passed on a tree that drew the screen empty: - `tasks` asked only whether some card in the list had id `task-1`, so a list of duplicates or a card whose view had lost the fields the row paints stayed green. It now pins the list itself: one row, plus that card's own id, title, column, queueRole and status. - `project-board` checked the metrics through `Object.values(metrics).every(value => value === null)`, which is vacuously true on `{}`: an answer with no metric keys at all drew a header of missing numbers and passed. It now pins the seven keys the header draws, then their null values, and keeps `ok === false` / `reason: no-credentials`. Red first, on the base tree, with each read path broken in turn (the task view dropping `title`, `emptyMetrics()` answering `{}`): the old assertions stayed green 3/3 twice; the new ones named both rows and failed twice; with the read paths restored they pass 3/3 twice. Test-only: no source, no client and no patch logic change. The overlay copy `patches/ds-bot/files/test/readonly-window-reads.test.js` is `generate`d and byte-identical to the core copy. Fixes #2847 --- core/test/readonly-window-reads.test.js | 10 +++++++++- .../ds-bot/files/test/readonly-window-reads.test.js | 10 +++++++++- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/core/test/readonly-window-reads.test.js b/core/test/readonly-window-reads.test.js index 7f8cfc422..353a7d182 100644 --- a/core/test/readonly-window-reads.test.js +++ b/core/test/readonly-window-reads.test.js @@ -68,7 +68,11 @@ const seed = work => ({ // A row's payload may be a function of the answers above it, because opening a decision reads a // name the board itself listed (#2041), not one this file made up. const READS = [ - ['tasks', {}, a => a.ok === true && a.value.some(task => task.id === 'task-1')], + // The list itself, not "the card is somewhere in it": one row, and the fields that row paints. + // `some()` stayed green on a list of duplicates and on a row the view had stripped to its id. + ['tasks', {}, a => a.ok === true && Array.isArray(a.value) && a.value.length === 1 + && a.value[0].id === 'task-1' && a.value[0].title === 'Read the board' + && a.value[0].column === 'waiting' && a.value[0].queueRole === 'Dev' && a.value[0].status === 'open'], ['projects', {}, a => a.ok === true && a.value.projects.some(project => project.id === 'project-p1')], ['project-read', { id: 'project-p1' }, a => a.ok === true && a.value.project?.id === 'project-p1'], ['project-of', { kind: 'bot', id: 'a' }, a => a.ok === true && a.value.project?.id === 'project-p1'], @@ -79,6 +83,10 @@ const READS = [ && a.value.counts?.waiting === 1 && a.value.counts.inflight === 0 && a.value.counts.blocked === 0 // A host with no GitHub credential says so and reads `null`, never 0 (#1829). && a.value.metrics?.ok === false && a.value.metrics.reason === 'no-credentials' + // Every metric row the header draws has to be there: `Object.values(...).every(null)` is + // vacuously true on `{}`, so a `metrics: {}` answer drew an empty table and stayed green. + && Object.keys(a.value.metrics.metrics).sort().join(',') + === 'issueCloseMedianMs,issuesClosedToday,issuesOpenedToday,prMergeMedianMs,prsMergedToday,prsOpenedToday,taskDoneMedianMs' && Object.values(a.value.metrics.metrics).every(value => value === null)], // The dossier's rows are the Project's people (#1864): the Chief slot first. `{ rows: [] }` and // `{ rows: [null, null] }` both passed a shape-only check while the panel drew nobody. diff --git a/patches/ds-bot/files/test/readonly-window-reads.test.js b/patches/ds-bot/files/test/readonly-window-reads.test.js index 7f8cfc422..353a7d182 100644 --- a/patches/ds-bot/files/test/readonly-window-reads.test.js +++ b/patches/ds-bot/files/test/readonly-window-reads.test.js @@ -68,7 +68,11 @@ const seed = work => ({ // A row's payload may be a function of the answers above it, because opening a decision reads a // name the board itself listed (#2041), not one this file made up. const READS = [ - ['tasks', {}, a => a.ok === true && a.value.some(task => task.id === 'task-1')], + // The list itself, not "the card is somewhere in it": one row, and the fields that row paints. + // `some()` stayed green on a list of duplicates and on a row the view had stripped to its id. + ['tasks', {}, a => a.ok === true && Array.isArray(a.value) && a.value.length === 1 + && a.value[0].id === 'task-1' && a.value[0].title === 'Read the board' + && a.value[0].column === 'waiting' && a.value[0].queueRole === 'Dev' && a.value[0].status === 'open'], ['projects', {}, a => a.ok === true && a.value.projects.some(project => project.id === 'project-p1')], ['project-read', { id: 'project-p1' }, a => a.ok === true && a.value.project?.id === 'project-p1'], ['project-of', { kind: 'bot', id: 'a' }, a => a.ok === true && a.value.project?.id === 'project-p1'], @@ -79,6 +83,10 @@ const READS = [ && a.value.counts?.waiting === 1 && a.value.counts.inflight === 0 && a.value.counts.blocked === 0 // A host with no GitHub credential says so and reads `null`, never 0 (#1829). && a.value.metrics?.ok === false && a.value.metrics.reason === 'no-credentials' + // Every metric row the header draws has to be there: `Object.values(...).every(null)` is + // vacuously true on `{}`, so a `metrics: {}` answer drew an empty table and stayed green. + && Object.keys(a.value.metrics.metrics).sort().join(',') + === 'issueCloseMedianMs,issuesClosedToday,issuesOpenedToday,prMergeMedianMs,prsMergedToday,prsOpenedToday,taskDoneMedianMs' && Object.values(a.value.metrics.metrics).every(value => value === null)], // The dossier's rows are the Project's people (#1864): the Chief slot first. `{ rows: [] }` and // `{ rows: [null, null] }` both passed a shape-only check while the panel drew nobody.