Nothing catches a subtest that calls a fixture built on its parent test's t #226
Open
opened 2026-10-16 01:56:24 +00:00 by cgalo5758
·
0 comments
Labels
Clear labels
accessibility
area/billing
area/catalog
area/discourse
area/domains
area/entitlements
area/fedwiki
area/identity
area/integrations
area/licensing
area/member-ui
area/meta
area/operator-ui
area/ops
area/testing
duplicate
good-first-issue
invalid
privacy
security
upstream
wontfix
A barrier for people using assistive technology or a keyboard alone.
The Stripe mirror, checkout, subscriptions, invoices, fulfillment.
Products, prices, plan ladders, purchasability.
The Discourse integration.
The domains registry, claims, placements, the certificate ask.
Entitlement sets, rules, grants, pools, provisioning.
The Federated Wiki integration and farm sync.
Sign-in, sessions, persons, organizations, workspaces, roles.
The provider registry, outbox and webhooks in general.
Licenses, the contributor agreement, SPDX headers.
Member pages.
The repository itself, its contributing guide, CI, the tracker and the workflow.
Operator pages, forms, lists, the design system.
Deployment, configuration, migrations, workflows, instance settings.
The test stack, screens, lint, walkthroughs.
Closed because another issue already covers it.
Small, self-contained, and explained enough to be a first contribution.
Closed because it is not a ticket for this repository.
Touches what a person's data reveals.
Touches authentication, authorization, secrets or data exposure.
Waits on another repository or project before it can move.
Closed because it will not be done, with the reason in the last comment.
kind
bug
The software does something other than what it promises; closed when it again does what it promises.
kind
debt
Code, tests or tooling to clean up with nothing visible changing; closed when they are cleaner.
kind
design
A question to settle before work can be defined; closed when the decision is written down.
kind
docs
Documentation that is wrong or missing; closed when it says the right thing.
kind
enhancement
Something the software does not do yet; closed when it does.
priority
critical
Blocks the active milestone or harms members now.
priority
high
Next in line inside the active milestone.
priority
low
Inside the active milestone, when nothing else is left.
priority
medium
Inside the active milestone, after the high ones.
Milestone
No items
No Milestone
Projects
Clear projects
No projects
No Assignees
Notifications
Due Date
No due date set.
Dependencies
No dependencies set.
Reference: wiki-cafe/member-console#226
Reference in New Issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
What a contributor runs into
A World (
internal/testkit/world/world.go:43-48), adbtest.Q(internal/testkit/dbtest/query.go:39-48) and most fixtures a package writes for itself keep thetesting.TBthey were built with and fail through it.docs/testing.mdstates the rule for the test kit: a Q belongs to the test or subtest that built it ("Opening the database"), a subtest builds its own World ("Fixtures"), and a browser helper never storest(the browser tests' rules under the end-to-end tests). Nothing checks the rule, and a table test breaks it easily: it builds one fixture on the parent'stand calls the fixture's methods inside eacht.Runclosure.A rough scan by syntax finds about 30 such closures in 9 test files of
internal/server. Two of them:TestMemberRouting_AsPlanSwitchTimingRoutes(internal/server/grant_resumption_member_test.go:596) buildsw := newWikiWorld(t). Each subtest callsw.orgOf(line 614), which fails withw.t.Fatalf(line 104), on the parent'st.TestCheckoutRefusesATierBelowOrAtAGrantedRung(internal/server/billing_checkout_offer_db_test.go:158) buildsw := newCheckoutOfferWorld(t). Each subtest callsw.postReal, whosepostfails withw.t.Fatalf("Checkout reached Stripe: …")(line 120).Review found the same pattern five more times while tests moved onto the kit, and each was fixed by hand.
Why it costs
When a subtest calls
Fatalon its parent'st, Go logs the message under the parent, fails the subtest with "subtest may have called FailNow on a parent test" instead of the fixture's message, and stops the parent, so the parent's remaining subtests do not run. A cleanup that a fixture registers through the storedtruns when the parent ends, not when the subtest does. Whoever reads the output looks for the failure in the wrong test, and a table loses every row after the one that failed. Review keeps catching the pattern because nothing else does.Where
The test files the scan names, all in
internal/server;docs/testing.md. The project lint (internal/lint,go run . lint) parses Go without type information today.scripts/sqlc-unusedalready loads every package and its test variants with types throughgolang.org/x/tools/go/packages.Done when
make lintreports at.Runclosure that uses a value declared outside the closure whose type is*testing.Tortesting.TB, or holds one in a field, directly or by embedding: a World, a Q, a package's own fixture. It reports a method call on such a value and the value passed to a function. Reading a plain field, such as an id, passes, and so doesq.Tx(), the rebindingdocs/testing.mdprescribes. The check needs type information, so it either sits besidescripts/sqlc-unusedor brings typed loading into the project lint.t.tfails the check.docs/testing.mdstates the rule once for every value that holds at, from the kit or not, and names the check.