Skip to content

Pin cursor-pagination behaviors left untested in #1131#1136

Merged
alistair3149 merged 10 commits into
fix/1062-totalrows-count-oraclefrom
pr-1131-review-fixes
Jul 24, 2026
Merged

Pin cursor-pagination behaviors left untested in #1131#1136
alistair3149 merged 10 commits into
fix/1062-totalrows-count-oraclefrom
pr-1131-review-fixes

Conversation

@JeroenDeDauw

Copy link
Copy Markdown
Member

Follows-up to #1131 — review-fix tests for the three coverage gaps found in its AI review, plus one doc-block correction. No production logic changes.

  • Indeterminate pager half of the determinate flip. Each list page keeps CdxTable's totalRows undefined while a response carries a non-null cursor; only the null-cursor flip was tested. An "unconditional flip" mutation survived the whole vitest suite while disabling Next on every full page mid-listing. Adds the indeterminate test to all three pages, including a first LayoutsPage spec (which also pins its determinate flip).
  • buildPage back-fill over unloadable rows. A readable-but-unparseable page (reachable through XML import, which bypasses validateSave — the Imported revisions do not update the graph projection #1022 flow) must not consume page space or surface in the walk. The branch had zero executions; a mutation counting skips against the limit survived the suite. Pinned on the Mapping endpoint with an imported Mapping missing its schemas key.
  • Keyset batch-boundary drain. The 100-row batch loop in the three name lookups never iterated in any test; while(false) (truncate every listing at 100 rows) survived. Adds >100-row drains via a shared bare-page insert helper; the Schema case also denies the row sitting exactly on the batch boundary.
  • Trait doc correction. buildPage's cursor is the last served item's page ID, so a trailing skipped row is re-scanned by the next page; the old sentence claimed skips are never re-consumed.

Every test was sabotage-checked: the target mutation applied → test fails on the pinned behavior → mutation reverted → green. Full local make phpunit (1976 tests), make cs, vitest (1107 tests, single-worker) and make ts-lint pass.

Merge note: master has since gained its own LayoutsPage.spec.ts (delete-dialog tests, totalRows-shaped mocks). When #1131 takes a master merge, resolve the add/add by folding this PR's two pager tests into master's scaffolding and converting its mocks to the nextCursor shape.

AI-authored — Claude Code, Fable 5 (max); produced by the /pr-review run on #1131 for @JeroenDeDauw, no revisions; diff not yet human-reviewed; sabotage-checked TDD (every test seen failing against its target mutation first), full PHPUnit/vitest/phpcs/phpstan/ts-lint green locally, CI pending.

Production notes

Review orchestration and finding verification by Fable 5 (max); test implementation by an Opus 4.8 (max) subagent against a fully specified brief. Findings confirmed by independent refute-first verifier agents before any test was written.

JeroenDeDauw and others added 3 commits July 23, 2026 02:14
buildPage skips a list row whose summary fails to load without spending page space, so a full page stays full and a one-at-a-time walk never yields an empty page mid-listing. Pin this on the Mapping endpoint with an XML-imported Mapping missing its schemas key, and correct the trait doc: the cursor is the last served item's page ID, so a malformed row after it is re-scanned and skipped by the next page. Review-fix tests for PR #1131.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The Schema, Layout and Mapping name lookups page their namespace in 100-row keyset batches, but no test exceeded a single batch. Drive each past 100 rows via a new bulk bare-page insert helper and assert every row is yielded once in page-ID order. The Schema case also denies the row sitting on the batch boundary, showing the keyset anchor advances past unreadable rows so later batches still arrive. Review-fix tests for PR #1131.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The Schema, Layout and Mapping list pages leave totalRows undefined on a full page carrying a non-null cursor, keeping CdxTable indeterminate so Next stays enabled; only the determinate null-cursor flip was covered. Pin the indeterminate case for all three pages, adding a first spec for LayoutsPage (which also covers its determinate flip). Review-fix tests for PR #1131.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@JeroenDeDauw
JeroenDeDauw marked this pull request as ready for review July 23, 2026 14:34
The denied row at the boundary does not distinguish anchor-advance
variants (an anchor that only advances on yields re-scans one row and
produces identical output); what it uniquely pins is that the drain's
continue decision counts fetched rows, not yielded ones.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
alistair3149 and others added 6 commits July 24, 2026 14:47
Both testUnloadableMappingDoesNotConsumePageSpace and
testWalkingPastAnUnloadableMappingNeverYieldsAnEmptyPage assert result sets
that are byte-identical to the listing you get when the imported Beta page does
not exist at all. So a silently no-op import — a future import-time validation,
an export-format change that makes the str_replace miss, a slot/model change —
would leave only Alpha and Gamma, keep both tests green, and never run the
loader's skip branch they exist to pin.

Guard the fixture's premise: assert the two substitutions took effect on the
dump, then assert the import created a distinct Beta page sitting between Alpha
and Gamma in page-ID order. Also drop the no-op markPageTableAsUsed() call —
the class has no setUp truncation and MW 1.43 resets changed tables via
ChangedTablesTracker, so it did nothing and no sibling test in the class uses it.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
IDatabase::insert() is marked @internal for callers outside the rdbms library,
which puts it on the same removal track as the other raw write methods (MW 1.43
turns such deprecations into test failures). Use the query builder, matching the
newSelectQueryBuilder() read three lines below in the same helper and the only
other raw-row insert in the suite (DatabaseDeletedSubjectPageIdsLookupTest).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The drain and batch-boundary tests hardcoded the private
READABLE_NAMES_BATCH_SIZE of 100 in three ways (120 bulk rows, the boundary
index [99], prose comments). Raising the constant above the row count would turn
every one of them into a single-batch test that still passes, letting the exact
while(false) truncation mutation they were written to kill survive again.

Expose the constant and derive the bulk row count (BATCH_SIZE + 20) and the
boundary index (BATCH_SIZE - 1) from it, so a changed batch size fails the tests
instead of hollowing them out. Give the Layout and Mapping drains the Schema
test's full ordered [pageId => name] assertion and matching name
(DrainsEveryBatchInPageIdOrder) — the previous count + first-key + last-key
shape passed a mid-run wrong-order or wrong-pairing regression that the Schema
variant caught. Drop the stale "4 setUp Schemas" count from the doc-block.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The Codex-internal pager selector and its English "Next page" label were
copy-pasted across five sites in three specs. A missed find() returns an empty
DOMWrapper whose attributes( 'disabled' ) is undefined, so an enabled-state
toBeUndefined() assertion would silently pass against a button that is not in
the DOM.

Extract findNextPageButton(), which owns the selector and asserts the button
exists, and call it from all five sites.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The PR's premise that "only the null-cursor determinate flip was untested" held
for SchemasPage, but MappingsPage had no pager test at all and gained only the
indeterminate half. A totalRows.value = undefined mutation (drop the determinate
branch) survived the whole MappingsPage suite, shipping a listing that ends on a
page boundary with Next enabled forever and a click serving an empty page.

Add the determinate test mirroring SchemasPage: a full page with a null cursor
disables Next and shows "of 10".

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The content guard added in 544a979 asserted assertStringNotContainsString(
'"schemas"', $xml ) immediately after str_replace( '"schemas"', '"schemaX"',
$xml ) — a post-condition str_replace makes true unconditionally, so it could
never fail and guarded nothing (an independent review caught this). Assert the
replacement token instead, assertStringContainsString( '"schemaX"', $xml ),
which fails precisely when the substitution matched nothing and Beta would stay
loadable. This mirrors the title guard, which already checks its replacement
target. Verified by sabotage: a non-matching content needle now fails the guard.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@alistair3149
alistair3149 merged commit 17c40dc into fix/1062-totalrows-count-oracle Jul 24, 2026
12 checks passed
@alistair3149
alistair3149 deleted the pr-1131-review-fixes branch July 24, 2026 18:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants