Skip to content

[finding] every assertion in packages/metadata/src/metadata-history.test.ts sits behind an if (manager.X) guard over a method that is unconditionally declared — 8 sites that go green asserting nothing #14623

Description

@os-musk

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 MetadataManagerpackages/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) {
  const diffResult = await manager.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

  1. Delete the eight guards and let the assertions run unconditionally. Cheapest, and it is what the types already say.
  2. 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).

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions