Add a ratchet lint for full-module vi.mock calls - #412
Merged
Conversation
haksungjang
force-pushed
the
mock-audit-lint-390
branch
from
September 6, 2026 16:01
3aea98c to
35413aa
Compare
shape, so a new export in the real module silently becomes undefined in every test that already mocks it that way, breaking a test that never touched the change. Add scripts/mock-audit.mjs, which detects full-module mocks (a factory present, no importOriginal/importActual), diffs the factory's declared keys against the real module's exports, and reports the gap. Freeze the current 151 full-module mocks in a baseline that can only shrink, same shape as token-lint.mjs and problem-detail-lint.mjs, and wire it into npm run lint.
haksungjang
force-pushed
the
mock-audit-lint-390
branch
from
September 6, 2026 21:02
35413aa to
966d3ea
Compare
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.
Summary
vi.mock("@/lib/api", () => ({...}))hand-writes the mocked module's shape. When the real module gains an export, every test that already mocks it that way keeps compiling and silently hands the new exportundefined, and the test that breaks is rarely the one whose PR added the export. This is what happened whenpostLogin's MFA union return type shipped: unrelated tests failed for a reason invisible from their own diff (#390).This does not migrate the existing full mocks; that is a larger change requiring each one to be checked against what its component actually renders. Instead:
apps/frontend/scripts/mock-audit.mjsstatically detects full-modulevi.mockcalls (a factory is present and it doesn't callimportOriginal/vi.importActual), resolves the mocked specifier to its source file, and diffs the factory's declared keys against the module's real value-level exports (type-only exports are excluded, since they don't exist at runtime).apps/frontend/scripts/mock-audit-baseline.jsonfreezes the current count of full-module mocks per file (151 across 88 files), same ratchet shape astoken-lint.mjsandproblem-detail-lint.mjs: a new full mock, or a rise in an existing file's count, fails; a drop must be re-recorded with--updateso debt paid down can't be quietly re-spent.npm run lint(mock:lintscript) so CI enforces it without a workflow change.87 of the 151 existing full mocks already have an undeclared-export gap (most visibly the two heaviest offenders,
@/lib/apiand@/lib/projectsApi). The tool reports these prominently but does not fail on them, since they predate the gate; it exists to stop the count from growing, and the gap detail is there so the actual bug shape is visible to whoever touches that mock next.Test plan
npm run lint(eslint + mock:lint) greennpm run typecheckgreennpm run test(235 files / 2624 tests) greennode scripts/i18n-check.cjsgreen (no new UI strings)node tools/em-dash/lint.mjs --base origin/maingreen--update(which only succeeds once it matches the real count), so an unjustified increase can't silently landRefs #390 (audit + lint only, per the issue's decided scope; migrating the 51 existing mocks to
importOriginalis explicitly out of scope for this change)