TypeScript: a test that imports its module as '.' or '..' is linked, not copied, so its mutants are reported SURVIVED

Open Beginner friendly
#29 0 comments 0 reactions 0 assignees View on GitHub

Maintainers usually reply within 2 days

Nobody has claimed this yet.

Assessment

Difficulty
2/5
Estimated time
1-3 hours
Newbie friendliness
84/100
Issue type
Bug
Clarity
Clearly specified
Activity status
Active
Tech stack
python, typescript
Domain
testing-qa

Research direction

Start in workers.py and inspect the import-specifier pattern and _candidates, which the issue says already resolves '.' and '..' to an index file. Run the provided Vitest reproduction before and after the change, then run mutator's own suite. Done means the reproduction reports the mutant as KILLED and the suite still passes.

Written by the indexing model from the issue text.

Description

Thanks for mutator. It found real gaps in our TypeScript suite. While using it I hit one case where it reports mutants as surviving when the tests do kill them.

workers.py copies a module into the worker when it imports the mutated file through a relative path, so Node loads the mutant. The specifier pattern needs at least one character after the dot:

r"""(?:from|import|require)\s*\(?\s*['"](\.[^'"]+)['"]"""

So '.' and '..' (a directory's index) never match. A test that imports its module that way stays a symlink, Node resolves it to the project, and the test runs against the original file.

Repro (mutator a5a0cb8, macOS 27.0.1, Node 22.23.4, Vitest 3.2.4, no Vitest config)

src/calc/index.ts:

export function big(x: number): boolean {
  return x > 10;
}

src/calc/index.test.ts:

import { expect, it } from 'vitest';
import { big } from '.';

it('eleven is big', () => expect(big(11)).toBe(true));
it('ten is not big', () => expect(big(10)).toBe(false));
$ ./node_modules/.bin/vitest run
      Tests  2 passed (2)
$ mutator --no-coverage --test-command "./node_modules/.bin/vitest run" src/calc/index.ts
SURVIVED  src/calc/index.ts:2 > -> >=
big      calc.index       0         1          0      1   0.0%

Applying the same mutant by hand (x >= 10) gives 1 failed | 1 passed, so it should be KILLED.

Possible fix

Let the dot stand alone. _candidates already resolves . and .. to index.<ext>:

-    r"""(?:from|import|require)\s*\(?\s*['"](\.[^'"]+)['"]""",
+    r"""(?:from|import|require)\s*\(?\s*['"](\.[^'"]*)['"]""",

With that change the repro reports KILLED (100%), and mutator's own suite still passes (76 passed). In our project, one file went from 0 of 25 mutants killed to 19 of 25, and a hand-applied spot check agreed.

Related: #19 (same cause, reached through a linked directory or a path alias).

Dominant language
Python
Stars
112
Forks
53
PR merge metrics
No merged PRs in 30d

Getting set up

This project ships no dev container, Dockerfile or contributing guide, so setting up is up to you: start from its README, and see our first-contribution guide for the general steps.

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

More from unclebob/mutator

All issues in unclebob/mutator

Similar issues

More Python issues

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.