Repository navigation
test(plugin-charts): serve ObjectChart's option-color probe from a double, not the network - #4115
Merged
Conversation
…uble, not the network Fixes objectui#4106. Test-only. Product code is untouched. All 12 real-network escapes in the plugin-charts / plugin-dashboard suites come from one call site pair in ObjectChart's category option-color effect, which reads /api/v1/meta/dataset/<dataset> and /api/v1/meta/object/<object> off the GLOBAL fetch. Under happy-dom those are real requests to the default origin; the effect swallows the rejection by design (best-effort), so the suites stayed green while stderr filled with `connect ECONNREFUSED 127.0.0.1:3000`. Answer the probe from a recording double in the four escaping files, and give its previously-unasserted request shape and success path real coverage. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017Qqyix2QcnpUC9XeYVDzx3
|
The latest updates on your projects. Learn more about Vercel for GitHub. |
Contributor
✅ Console Performance Budget
📦 Bundle Size Report
Size Limits
|
yinlianghui
marked this pull request as ready for review
August 10, 2026 10:56
This was referenced Aug 10, 2026
Merged
akarma-synetal
pushed a commit
to akarma-synetal/objectui
that referenced
this pull request
Sep 1, 2026
…t-burnable, zero re-exports (objectstack-ai#6883) * test(types): close the objectstack-ai#4115 ledger's last batch with measured verdicts, not re-exports Batch 8/8 of the objectstack#4115 any-erasure ledger covered `JoinNode`, `NavigationItem`, `NavigationItemSchema` and `JoinedReportBlock`, all filed as "blocked on objectstack#4171 — re-export when the upstream erasure lifts". objectstack-ai#4171 closed completed on 2026-07-30. Re-measuring in the BUILT dist at spec 17.2.0 (not in source, and not from the issue's state) finds none of the four is burnable, for three different reasons — none of them the one the ledger recorded: - `JoinNode`: the spec no longer exports the symbol at all (TS2305 from both `/ui` and `/data`); spec 17.0.0 retired the cluster. Nothing to bind to. - `NavigationItem`: upstream is precise now, but the three semantic blockers objectstack-ai#3177 measured are untouched by objectstack-ai#4171. - `NavigationItemSchema`: upstream is precise now too — and that is exactly what makes the burn-down dangerous, because the live blocker is runtime shape. Referencing the spec's schema would make `objectui validate` reject metadata this renderer accepts today. - `JoinedReportBlock`: still erased, to `unknown`, by a cause `objectstack-ai#4171` never covered (a bare `z.ZodTypeAny`, not recursion). So the batch lands zero re-exports and converts the remaining debt into pins that name their own release condition: - `app.zod.ts` carried a reason that measurement has made FALSE — "the spec's is `z.ZodType<any>` and would validate nothing". Left alone, the next triage checks that claim, finds it false, and lands the regression this ledger exists to prevent. Replaced with the measured reason. - `JoinNode` absence is now pinned in the file that already owns "the spec does not own X", so a re-minted upstream name is caught. - The `JoinedReportBlock` pin no longer reads as pending on a closed issue. - `navigation-spec-parity.test.ts` gains the only mechanical guard for the CURRENT blocker: five pieces of metadata objectui accepts and the spec rejects, behind two positive controls. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013hfmP9hoMd3dJwTh85J4yB * chore(types): clear the objectstack-ai#4115 machine-readable ledger, recording batch 8 as declared dialects `check-spec-symbol-derivation.mjs` carries the ledger's enforceable half: a `DEBT` map keyed to objectstack#4115 whose `@object-ui/types` entry was exactly the three batch-8 symbols that still collide with a spec export, reported by the gate as "3 untriaged collisions". They are not untriaged — they are measured, and the measurement says they must NOT be burned down. That is what the guard's `ALLOW` registry is for: "a same-name symbol that is narrower or wider ON PURPOSE belongs here with that purpose written down". So the three move DEBT to ALLOW with their measured reasons and their release conditions named as pins, and the DEBT map empties. before: 13 declared dialects, 3 untriaged collisions in 1 packages after: 16 declared dialects, 0 untriaged collisions in 0 packages Also corrects the `FormField` ALLOW reason, which asserted "the spec's FormField type erases to `any` in its dist (objectstack#4171), so a re-export would delete typing outright". That is false now and `spec-derived-unions.test.ts` already pins it false (`_specFormFieldIsNoLongerAny`). It is the same defect class as the batch this PR closes — a spent `any`-erasure justification standing in for the real one — and the real reason (the two-layer split, guard header case 2c) is unaffected by precision. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013hfmP9hoMd3dJwTh85J4yB --------- Co-authored-by: Claude <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #4106
Test-only. Product code is untouched.
Premise: reproduced, still valid
On
origin/main@877385a76, both packages' suites from the repo root:Green suite, polluted stderr — the fail-open shape the card described. 96 stderr lines / 8 per error block = 12 escaped requests, 2
ECONNREFUSEDlines each = 24. The arithmetic closes before any bisecting starts.The bisect: one effect, two call sites, 100% of the noise
Per the card's inherited method (and PR #4105's worked example), I instrumented
fetch/XMLHttpRequest/EventSource/WebSocketin the root setup file and attributed every escape to its test file rather than bisecting file by file.plugin-charts/src/ObjectChart.drillNavigate.test.tsx/api/v1/meta/object/opportunityplugin-charts/src/__tests__/ObjectChart.compareTo.test.tsx/api/v1/meta/object/dealplugin-charts/src/ObjectChart.elementDataSource.test.tsx/api/v1/meta/object/accountplugin-charts/src/ObjectChart.datasetMeasureLabel.test.tsx/api/v1/meta/dataset/showcase_task_metrics12 requests x 2 lines = the 24
ECONNREFUSEDlines exactly. All attributed, 0 unexplained.Two findings worth stating plainly, because both differ from what the card assumed:
The root cause is confirmed different from plugin-detail 测试套件里有测试向
127.0.0.1:3000发起真实网络请求(ECONNREFUSED 噪音,未走 MSW) #3339. Nothing here touchesuseRecordEditableor/api/v1/security/explain. Every escape isObjectChart's category option-color effect (ObjectChart.tsx:328), which reads the globalfetchat two call sites: line 344 (/api/v1/meta/dataset/..., to learn a dataset's underlying object) and line 352 (/api/v1/meta/object/..., for the field options). The effect is best-effort and swallows every rejection — which is exactly why the suites stayed green while stderr filled up.plugin-dashboardemits zero live requests. The card named both packages, but attribution puts all 12 inplugin-charts.plugin-dashboardresolves the same/api/v1/meta/object/...document inDatasetWidget, and its suite already answers it from a double (DatasetWidget.relabel.test.tsx:70) — which is why it is silent. Nothing to fix there; it is the precedent this PR follows.Per-file counts are explained too, not just the total:
elementDataSourcerenders 4 tests but its unresolvable-viewcase never resolves anobjectName, so it makes 3 probes;datasetMeasureLabel's single dataset probe fails, so the second (object) hop never runs.The fix
A recording test double scoped to the four escaping files, following the sibling package's existing pattern and #4105's.
{}as the default answer reproduces the failed request's observable outcome exactly:buildOptionColorMap(undefined)andbuildDimensionLabelMap(undefined)both returnnull(packages/core/src/utils/chart-series.ts:198,275), which is the same state the swallowed rejection's catch branch already set. No existing assertion changes meaning.No global error swallowing. The double records every URL it is handed, and each file's
afterEachfails on anything outside its one expected endpoint — so a NEW escape becomes a failing assertion instead of fresh stderr noise. That guard covers every test in those files, not just the ones that probe today.Product fallbacks untouched — but the global-
fetchread is a genuine product bug, so per the card it is reported rather than silently fixed here: filed as #4114 (ObjectChartnever consultsSchemaRendererContext.apiFetch, though it already reads that context at line 253, so a hosted console's auth/base-URL channel is bypassed for these two metadata reads).ObjectChart.elementDataSource.test.tsxis the demonstration case — it mounts a realSchemaRendererProviderand the probe still escapes.No MSW harness stood up, per the card.
Keeping the assertions meaningful
The escaped request was asserted by nobody, and because it always failed, the effect's success path had never once executed in this package's suite. New file
ObjectChart.optionColors.test.tsxgives it real coverage from the component's own wiring (the map builders are unit-tested one level down in@object-ui/core; what is new is that ObjectChart asks for the right documents, in the right order, and feeds the results to the renderer):categoryColors, keyed by both stored value and display label;categoryColorsand no failure.Verification
Acceptance bar, the card's own command:
Reverse verification, both directions predicted before running:
git checkout origin/main --on the four files, new file held aside) — noise returns at exactly 24ECONNREFUSED, and the suite is still green at 439 tests. Confirms the doubles are what stop it, and that the escape never had a test watching it.buildOptionColorMap(undefined)at line 355, so field options are never read) turns it red:AssertionError: expected undefined to be truthy, 1 failed | 2 passed. The other two stay green as predicted, since dimension labels come from a different builder and the no-options case expectsundefinedeither way. Restored afterwards;git diff HEAD -- packages/plugin-charts/src/ObjectChart.tsxis empty.Gates:
pnpm --filter @object-ui/plugin-charts type-checkgreen (afterpnpm --filter '@object-ui/plugin-charts^...' build— the fresh-worktree dependency closure),eslinton all five files 0 errors (15 pre-existingno-explicit-anywarnings, same style as the surrounding mocks), control-byte self-scan clean.scripts/check-changeset-presence.mjsarbitrated and required the empty-frontmatter changeset for a test-only change — its sanctioned pass, not a workaround;check-changeset-no-major.mjsgreen. (skip-changesetis not a real label in this repo, per objectui#3724.)Deliberately not in this PR
apiFetchbypass (ObjectChart's option-color / dimension-label probe reads the globalfetch, bypassing the host'sapiFetchchannel #4114) — a product contract change, out of scope for a hygiene card.plugin-dashboard— verified clean by attribution rather than assumed; adding doubles there would be theatre.Generated by Claude Code