Skip to content

test(data-objectstack): re-point v3-compat's vacuous PaginatedResult block at real ObjectStackAdapter.find() - #4717

Merged
yinlianghui merged 1 commit into
mainfrom
claude/issue-4712-paginated-result-vacuous-block
Aug 15, 2026
Merged

test(data-objectstack): re-point v3-compat's vacuous PaginatedResult block at real ObjectStackAdapter.find()#4717
yinlianghui merged 1 commit into
mainfrom
claude/issue-4712-paginated-result-vacuous-block

Conversation

@yinlianghui

@yinlianghuiyinlianghui commented Aug 15, 2026

Copy link
Copy Markdown
Collaborator

Fixes#4712

What was wrong

packages/data-objectstack/src/v3-compat.test.ts's PaginatedResult API block asserted only on an inline object literal it constructed itself, with no import from ./index or anywhere else in the package:

constresult={data: [{id: '1'}],total: 10,page: 1,pageSize: 5,hasMore: true};expect(result.data).toHaveLength(1);expect(result.total).toBe(10);expect(result.hasMore).toBe(true);

This passes unconditionally regardless of what production code does — it is unreachable from any real query path and cannot fail in a way that signals anything about the codebase (dormant/tautological coverage).

Measurement (route A vs B)

  • grep -rn "PaginatedResult" under packages/ finds no interface, type, or export literally named PaginatedResult anywhere in this repo (only prose hits in CHANGELOG.md referencing an old upstream @objectstack v3.0.0 migration note).
  • The real production shape the block's comment was gesturing at is QueryResult (generic over T), exported from @object-ui/types (packages/types/src/data.ts:109) with exactly the data/total/page/pageSize/hasMore fields the literal hand-copied.
  • QueryResult is returned by ObjectStackAdapter.find() (packages/data-objectstack/src/index.ts:1418), via the private normalizeQueryResult() (index.ts:2565), which computes total (total ?? count ?? records.length), the hasMore fallback (prefers the server's hasMore, else records.length === $top), and page (floor($skip / $top) + 1).
  • That computation had zero coverage anywhere else in this packagequeryDataset.test.ts and listViews.test.ts (cited by the issue) exercise other parts of the adapter's return shape but never total/hasMore/page/pageSize on .find().

Route A: a real production surface exists to pin, and it was genuinely uncovered, so the block is re-pointed rather than deleted.

What changed

The block now constructs an ObjectStackAdapter with a mocked fetch (same pattern as orderby-serialization.test.ts / queryDataset.test.ts in this package) and drives the realfind() to normalizeQueryResult() path:

  • normalizes a server records/total/hasMore envelope into the returned QueryResult (data/total/hasMore/pageSize/page)
  • computes page from $skip/$top
  • falls back to the page-local hasMore estimate (records.length === $top) when the server omits it
  • reports hasMore: false for a short (non-full) page with no server hint

Positive control (route A requires one)

Before pushing, I temporarily hardcoded hasMore = false and page = 1 in normalizeQueryResult() as an uncommitted local edit (reverted afterward with git checkout against the already-committed branch, pointed at packages/data-objectstack/src/index.ts — the fix commit went in first per this repo's reverse-verification discipline), and re-ran the suite:

FAIL packages/data-objectstack/src/v3-compat.test.ts (4 tests | 3 failed)
× normalizes the server records/total/hasMore envelope into QueryResult
× computes page from $skip/$top
× falls back to a page-local hasMore estimate when the server omits it

3 of 4 new assertions went red (the 4th — "reports hasMore false for a short page" — stayed green because the mutation coincidentally also produced hasMore: false, which is expected and not a gap: that test is there to guard against hasMore defaulting to true, the opposite direction). Reverting restored all 4 green, confirming the block now actually exercises and can detect drift in production code, unlike the block it replaces.

Tests

At final commit 820b4d716:

pnpm --filter '@object-ui/data-objectstack^...' build # deps built (types, core)
pnpm exec vitest run packages/data-objectstack/ --maxWorkers=2
Test Files 37 passed (37)
Tests 526 passed (526)
pnpm --filter @object-ui/data-objectstack type-check # tsc --noEmit — clean
pnpm --filter @object-ui/data-objectstack lint # 0 errors, 396 pre-existing no-explicit-any warnings (unrelated to this change)
node scripts/check-changeset-presence.mjs # empty-frontmatter changeset declared
node scripts/check-changeset-no-major.mjs # pass
node scripts/check-control-bytes.mjs # pass, 4237 files scanned

Changeset

Test-only change — added .changeset/paginated-result-vacuous-block-4712.md with empty frontmatter (declares "releases nothing", the repo's explicit first-class pass for internal-only changes).


Generated by Claude Code

…block at ObjectStackAdapter.find()
The `PaginatedResult API` block asserted only on an inline object literal it
constructed itself, with no import from `./index` or anywhere else in the
package — it could not fail in a way that signalled anything about
production code (objectui#4712).
No interface or export named `PaginatedResult` exists anywhere in packages/.
The real production shape is `QueryResult<T>` (packages/types/src/data.ts),
returned by `ObjectStackAdapter.find()` via the private
`normalizeQueryResult()`, whose total/hasMore/page/pageSize computation had
zero coverage anywhere else in this package. The block now drives
`ObjectStackAdapter.find()` through a mocked transport and asserts on its
real returned QueryResult, including the hasMore fallback and the page
calculation from $skip/$top.
Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RnQd8iMMUwXQEV1crFmQiQ
@vercel

vercelBot commented Aug 15, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
ProjectDeploymentActionsUpdated (UTC)
objectuiIgnoredIgnoredAug 15, 2026 3:55pm

Request Review

@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

MetricValueBudget
Main entry (gzip)24.7 KB350 KB
Entry fileindex-CB9xsYbE.js
StatusPASS

📦 Bundle Size Report

PackageSizeGzipped
app-shell (index.js)9.56KB3.59KB
app-shell (runtime-config.js)7.42KB2.32KB
app-shell (types.js)0.01KB0.04KB
app-shell (urlParams.js)8.92KB3.41KB
auth (AuthContext.js)0.31KB0.24KB
auth (AuthGuard.js)1.17KB0.53KB
auth (AuthProvider.js)25.13KB5.40KB
auth (AuthShell.js)3.49KB1.40KB
auth (ForgotPasswordForm.js)12.21KB3.45KB
auth (LoginForm.js)18.13KB5.39KB
auth (PreviewBanner.js)0.90KB0.50KB
auth (RegisterForm.js)6.64KB2.21KB
auth (SocialSignInButtons.js)9.60KB3.89KB
auth (UserMenu.js)3.40KB1.22KB
auth (auth-gate-events.js)1.29KB0.66KB
auth (authStyles.js)5.04KB1.72KB
auth (createAuthClient.js)38.46KB10.17KB
auth (createAuthenticatedFetch.js)6.34KB2.43KB
auth (index.js)2.35KB1.07KB
auth (org-roles.js)6.66KB2.78KB
auth (phone-identifier.js)1.11KB0.66KB
auth (types.js)0.59KB0.35KB
auth (useAuth.js)5.02KB0.88KB
auth (useIsWorkspaceAdmin.js)1.61KB0.85KB
collaboration (CommentThread.js)26.07KB7.56KB
collaboration (LiveCursors.js)3.17KB1.27KB
collaboration (PresenceAvatars.js)6.49KB2.64KB
collaboration (PresenceProvider.js)2.79KB1.13KB
collaboration (index.js)1.65KB0.73KB
collaboration (useCollaborationTranslation.js)6.05KB2.52KB
collaboration (useCommentSearch.js)1.98KB0.88KB
collaboration (useConflictResolution.js)7.75KB1.86KB
collaboration (useMentionNotifications.js)1.81KB0.68KB
collaboration (usePresence.js)6.33KB1.84KB
collaboration (useRealtimeSubscription.js)7.91KB2.01KB
components (index.js)489.91KB108.67KB
core (index.js)3.79KB1.52KB
create-plugin (index.js)10.08KB3.26KB
data-objectstack (index.js)154.13KB42.23KB
fields (index.js)230.37KB57.17KB
i18n (LocalizationContext.js)1.76KB0.96KB
i18n (currency.js)1.22KB0.64KB
i18n (i18n.js)4.32KB1.77KB
i18n (index.js)3.35KB1.38KB
i18n (pickLocalized.js)3.69KB1.73KB
i18n (provider.js)23.12KB7.62KB
i18n (useDisplayLocale.js)2.84KB1.45KB
i18n (useObjectLabel.js)27.59KB6.63KB
i18n (useSafeTranslation.js)7.77KB3.13KB
layout (index.js)38.98KB10.85KB
mobile (MobileProvider.js)0.92KB0.49KB
mobile (ResponsiveContainer.js)0.94KB0.38KB
mobile (breakpoints.js)1.51KB0.70KB
mobile (createOfflineDataSource.js)5.61KB1.74KB
mobile (index.js)1.50KB0.62KB
mobile (offlineQueue.js)3.91KB1.35KB
mobile (pwa.js)0.97KB0.49KB
mobile (serviceWorker.js)1.48KB0.62KB
mobile (serviceWorkerSource.js)3.41KB1.48KB
mobile (useBreakpoint.js)1.54KB0.65KB
mobile (useGesture.js)6.96KB1.98KB
mobile (useOfflineSync.js)1.99KB0.72KB
mobile (usePullToRefresh.js)2.53KB0.85KB
mobile (useResponsive.js)0.71KB0.42KB
mobile (useResponsiveConfig.js)1.36KB0.63KB
mobile (useSpecGesture.js)4.32KB1.64KB
mobile (useTouchTarget.js)1.01KB0.54KB
permissions (MePermissionsProvider.js)8.75KB3.06KB
permissions (PermissionContext.js)0.31KB0.25KB
permissions (PermissionGuard.js)0.89KB0.45KB
permissions (PermissionProvider.js)3.67KB1.12KB
permissions (evaluator.js)4.41KB1.44KB
permissions (index.js)0.91KB0.41KB
permissions (store.js)0.91KB0.42KB
permissions (useFieldPermissions.js)1.28KB0.52KB
permissions (usePermissions.js)1.55KB0.71KB
plugin-ai (index.js)15.75KB3.80KB
plugin-calendar (index.js)46.86KB12.91KB
plugin-charts (index.js)64.44KB18.28KB
plugin-chatbot (index.js)181.21KB43.14KB
plugin-dashboard (index.js)121.90KB31.76KB
plugin-designer (index.js)212.39KB42.83KB
plugin-detail (index.js)239.86KB59.99KB
plugin-editor (index.js)2.46KB1.10KB
plugin-form (index.js)114.72KB27.70KB
plugin-gantt (index.js)164.30KB40.02KB
plugin-grid (index.js)192.10KB51.24KB
plugin-kanban (index.js)52.74KB14.53KB
plugin-list (index.js)111.14KB26.92KB
plugin-map (index.js)18.16KB5.81KB
plugin-markdown (index.js)13.72KB4.69KB
plugin-report (index.js)41.38KB11.09KB
plugin-timeline (index.js)26.68KB7.66KB
plugin-tree (index.js)8.50KB2.88KB
plugin-view (index.js)83.81KB20.49KB
providers (DataSourceProvider.js)0.75KB0.39KB
providers (MetadataProvider.js)1.37KB0.59KB
providers (ThemeProvider.js)1.90KB0.85KB
providers (UploadProvider.js)11.71KB3.53KB
providers (index.js)0.44KB0.22KB
providers (types.js)0.01KB0.04KB
react-runtime (index.js)5.67KB2.37KB
react (LazyPluginLoader.js)3.77KB1.33KB
react (SchemaRenderer.js)27.64KB9.44KB
react (data-invalidation.js)5.05KB2.08KB
react (index.js)1.28KB0.68KB
react (schema-input.js)1.45KB0.83KB
react (spec-input.js)0.20KB0.18KB
sdui-parser (codegen.js)4.09KB1.74KB
sdui-parser (index.js)4.47KB2.03KB
sdui-parser (parse.js)10.04KB2.82KB
sdui-parser (types.js)0.29KB0.24KB
sdui-parser (validate.js)4.69KB1.48KB
types (ai.js)0.20KB0.17KB
types (api-types.js)0.20KB0.18KB
types (app.js)2.87KB0.99KB
types (base.js)0.20KB0.18KB
types (blocks.js)0.20KB0.18KB
types (complex.js)0.20KB0.18KB
types (crud.js)0.20KB0.18KB
types (dashboard-filter-alias.js)6.23KB2.74KB
types (data-display.js)0.20KB0.18KB
types (data-protocol.js)0.20KB0.19KB
types (data.js)0.20KB0.18KB
types (designer.js)1.87KB0.85KB
types (disclosure.js)0.20KB0.18KB
types (error-code.js)1.54KB0.88KB
types (feedback.js)0.20KB0.18KB
types (field-types.js)0.20KB0.18KB
types (form.js)0.20KB0.18KB
types (http-retry.js)4.32KB2.02KB
types (index.js)3.05KB1.52KB
types (layout.js)0.20KB0.18KB
types (managed-by.js)0.19KB0.18KB
types (mobile.js)2.59KB1.31KB
types (navigation.js)0.20KB0.18KB
types (objectql.js)0.20KB0.18KB
types (overlay.js)0.20KB0.18KB
types (permissions.js)0.20KB0.18KB
types (plugin-scope.js)0.20KB0.18KB
types (record-components.js)0.20KB0.19KB
types (record-semantics.js)1.28KB0.67KB
types (registry.js)0.20KB0.18KB
types (reports.js)0.20KB0.18KB
types (spec-report.js)5.05KB1.93KB
types (system-fields.js)3.33KB1.54KB
types (theme.js)0.20KB0.18KB
types (ui-action.js)3.40KB1.71KB
types (views.js)0.20KB0.18KB
types (widget.js)0.20KB0.18KB

Size Limits

  • ✅ Core packages should be < 50KB gzipped
  • ✅ Component packages should be < 100KB gzipped
  • ⚠️ Plugin packages should be < 150KB gzipped

@yinlianghuiClaude

Copy link
Copy Markdown
CollaboratorAuthor

Review verdict: ACCEPT (reviewer of record, PM session session_01RnQd8iMMUwXQEV1crFmQiQ).

Verified against this PR, not the report: two changed files, Fixes #4712 the only closing keyword. Route A is the right call and the measurement earns it — no PaginatedResult export exists to pin, but the block's hand-copied literal was shadowing QueryResult, whose normalizeQueryResult() computation (total coalescing, hasMore server-else-page-local fallback, page from $skip/$top) had zero coverage anywhere in the package, so the re-point converts tautological green into the package's only pin on that math. The positive control is honest in the useful way: 3 of 4 assertions red under the mutation, with the fourth's survival explained directionally (it guards the opposite default) rather than glossed. Reverse-verification hygiene followed (fix committed first, mutation uncommitted, restore verified). Local gates pinned to 820b4d716, matching this PR's current head.

Will flip ready + enable auto-merge once Lint, Type Check, and Test shards 1–4 conclude success on this head.


Generated by Claude Code

@yinlianghui
yinlianghui marked this pull request as ready for review August 15, 2026 16:33
@yinlianghui
yinlianghui added this pull request to the merge queueAug 15, 2026
Merged via the queue into main with commit 4dae765Aug 15, 2026
29 of 30 checks passed
@yinlianghui
yinlianghui deleted the claude/issue-4712-paginated-result-vacuous-block branch August 15, 2026 16:33
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[finding] data-objectstack's v3-compat.test.ts "PaginatedResult API" test asserts only on an inline object literal — it exercises no production code

2 participants

@yinlianghui@claude