Observation-class finding, measured while implementing #14342 (repairing that package's hidden test-type errors). Filed unassigned; nothing was changed for it — the #14342 ruling is mechanical repairs only, no assertion moves.
Blocked-by: #14342
Unlock-action: re-check PR #14627
Measured
On b195676a7 (the #14342 branch; the shape is identical on origin/main at 7085f9053), packages/metadata/src/metadata-history.test.ts wraps every assertion body in a truthiness guard over the method under test:
| line | guard |
|---|
| 47 | if (manager.getHistory) { |
| 81 | if (manager.getHistory) { |
| 111 | if (manager.rollback) { |
| 146 | if (manager.diff) { |
| 167 | if (manager.getHistory) { |
| 197 | if (manager.getHistory) { |
| 207 | if (manager.getHistory) { |
| 222 | if (manager.rollback) { |
All three methods are unconditional members of MetadataManager — packages/metadata/src/metadata-manager.ts declares async getHistory( at 3097, async rollback( at 3121, async diff( at 3171, and none of the three is written optional (?) anywhere in the package.
So each guard is always true today, and that is exactly the problem: the assertions are structurally optional. Rename, retire or accidentally drop one of the three methods and the guard goes false, the block is skipped, the it() finishes without asserting, and the suite reports green. The failure direction is the silent one — a test that stops testing looks identical to a test that passes.
Worked shape, at 146:
if(manager.diff){constdiffResult=awaitmanager.diff('object','test_object',1,2);expect(diffResult.identical).toBe(false);expect(diffResult.patch!.length).toBeGreaterThan(0);expect(diffResult.summary).toContain('modified');}The whole body of it('should compare versions with diff') is inside the guard.
Why it is worth a card
This package's tests were, until #14342, in no tsc program at all — so the only thing standing behind these methods was vitest at runtime, and vitest's verdict here is conditional on a predicate the test itself controls. #14342 puts the file in front of tsc --noEmit, which is a real improvement and does not touch this: a skipped block type-checks fine.
Note this is NOT the usual "optional capability" pattern that guards exist for. If the guards were written against an interface where the method genuinely is optional, they would be correct. They are written against a class where it is not, so they buy nothing and cost the assertions.
Suggested shape, for triage to weigh
- Delete the eight guards and let the assertions run unconditionally. Cheapest, and it is what the types already say.
- If the intent was to keep the file runnable against a narrower interface than the concrete
MetadataManager, assert the capability instead of branching on it (expect(manager.diff).toBeTypeOf('function') before the call), so a missing method is a red rather than a skip.
Not picking one unasked — it decides whether this file is meant to test the class or a narrower contract, and only (2) preserves the latter reading.
Related: #14342 (the typecheck-script repair on the same package; this file's TS18048 was repaired there, the guard was not).
Observation-class finding, measured while implementing #14342 (repairing that package's hidden test-type errors). Filed unassigned; nothing was changed for it — the #14342 ruling is mechanical repairs only, no assertion moves.
Blocked-by: #14342
Unlock-action: re-check PR #14627
Measured
On
b195676a7(the #14342 branch; the shape is identical onorigin/mainat7085f9053),packages/metadata/src/metadata-history.test.tswraps every assertion body in a truthiness guard over the method under test:if (manager.getHistory) {if (manager.getHistory) {if (manager.rollback) {if (manager.diff) {if (manager.getHistory) {if (manager.getHistory) {if (manager.getHistory) {if (manager.rollback) {All three methods are unconditional members of
MetadataManager—packages/metadata/src/metadata-manager.tsdeclaresasync getHistory(at 3097,async rollback(at 3121,async diff(at 3171, and none of the three is written optional (?) anywhere in the package.So each guard is always true today, and that is exactly the problem: the assertions are structurally optional. Rename, retire or accidentally drop one of the three methods and the guard goes false, the block is skipped, the
it()finishes without asserting, and the suite reports green. The failure direction is the silent one — a test that stops testing looks identical to a test that passes.Worked shape, at 146:
The whole body of
it('should compare versions with diff')is inside the guard.Why it is worth a card
This package's tests were, until #14342, in no tsc program at all — so the only thing standing behind these methods was vitest at runtime, and vitest's verdict here is conditional on a predicate the test itself controls. #14342 puts the file in front of
tsc --noEmit, which is a real improvement and does not touch this: a skipped block type-checks fine.Note this is NOT the usual "optional capability" pattern that guards exist for. If the guards were written against an interface where the method genuinely is optional, they would be correct. They are written against a class where it is not, so they buy nothing and cost the assertions.
Suggested shape, for triage to weigh
MetadataManager, assert the capability instead of branching on it (expect(manager.diff).toBeTypeOf('function')before the call), so a missing method is a red rather than a skip.Not picking one unasked — it decides whether this file is meant to test the class or a narrower contract, and only (2) preserves the latter reading.
Related: #14342 (the typecheck-script repair on the same package; this file's TS18048 was repaired there, the guard was not).