@mintwork/honest-tests 0.0.0-stage → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (75) hide show
  1. package/.claude-plugin/marketplace.json +22 -0
  2. package/.claude-plugin/plugin.json +20 -0
  3. package/CHANGELOG.md +56 -0
  4. package/LICENSE +21 -0
  5. package/README.md +280 -2
  6. package/commands/audit.md +17 -0
  7. package/commands/mutate.md +10 -0
  8. package/dist/adapters/index.d.ts +3 -0
  9. package/dist/adapters/index.js +8 -0
  10. package/dist/adapters/js/ast.d.ts +24 -0
  11. package/dist/adapters/js/ast.js +199 -0
  12. package/dist/adapters/js/index.d.ts +25 -0
  13. package/dist/adapters/js/index.js +122 -0
  14. package/dist/adapters/js/mutants.d.ts +8 -0
  15. package/dist/adapters/js/mutants.js +190 -0
  16. package/dist/adapters/js/parse-tests.d.ts +5 -0
  17. package/dist/adapters/js/parse-tests.js +1101 -0
  18. package/dist/adapters/python/index.d.ts +17 -0
  19. package/dist/adapters/python/index.js +26 -0
  20. package/dist/cli.d.ts +2 -0
  21. package/dist/cli.js +21 -0
  22. package/dist/core/engine.d.ts +44 -0
  23. package/dist/core/engine.js +205 -0
  24. package/dist/core/types.d.ts +195 -0
  25. package/dist/core/types.js +7 -0
  26. package/dist/index.d.ts +6 -0
  27. package/dist/index.js +5 -0
  28. package/dist/mutate/runner.d.ts +59 -0
  29. package/dist/mutate/runner.js +379 -0
  30. package/dist/report/colors.d.ts +11 -0
  31. package/dist/report/colors.js +13 -0
  32. package/dist/report/human.d.ts +6 -0
  33. package/dist/report/human.js +79 -0
  34. package/dist/report/json.d.ts +4 -0
  35. package/dist/report/json.js +8 -0
  36. package/dist/rules/asserts-on-mock.d.ts +2 -0
  37. package/dist/rules/asserts-on-mock.js +53 -0
  38. package/dist/rules/asserts-on-own-literal.d.ts +2 -0
  39. package/dist/rules/asserts-on-own-literal.js +36 -0
  40. package/dist/rules/catch-only-assertion.d.ts +2 -0
  41. package/dist/rules/catch-only-assertion.js +22 -0
  42. package/dist/rules/conditional-assertion.d.ts +2 -0
  43. package/dist/rules/conditional-assertion.js +30 -0
  44. package/dist/rules/excessive-mocking.d.ts +3 -0
  45. package/dist/rules/excessive-mocking.js +22 -0
  46. package/dist/rules/focused-test.d.ts +2 -0
  47. package/dist/rules/focused-test.js +15 -0
  48. package/dist/rules/helpers.d.ts +5 -0
  49. package/dist/rules/helpers.js +18 -0
  50. package/dist/rules/index.d.ts +3 -0
  51. package/dist/rules/index.js +31 -0
  52. package/dist/rules/mocked-unit-under-test.d.ts +2 -0
  53. package/dist/rules/mocked-unit-under-test.js +32 -0
  54. package/dist/rules/never-failing-assertion.d.ts +2 -0
  55. package/dist/rules/never-failing-assertion.js +43 -0
  56. package/dist/rules/no-assertions.d.ts +2 -0
  57. package/dist/rules/no-assertions.js +16 -0
  58. package/dist/rules/skipped-test.d.ts +2 -0
  59. package/dist/rules/skipped-test.js +15 -0
  60. package/dist/rules/snapshot-only.d.ts +2 -0
  61. package/dist/rules/snapshot-only.js +18 -0
  62. package/dist/rules/swallowed-assertion.d.ts +2 -0
  63. package/dist/rules/swallowed-assertion.js +18 -0
  64. package/dist/rules/tautological-assertion.d.ts +2 -0
  65. package/dist/rules/tautological-assertion.js +42 -0
  66. package/dist/run.d.ts +12 -0
  67. package/dist/run.js +204 -0
  68. package/integrations/codex/AGENTS.md +20 -0
  69. package/integrations/cursor/honest-tests.mdc +24 -0
  70. package/package.json +73 -4
  71. package/skills/honest-tests/SKILL.md +141 -0
  72. package/skills/honest-tests/references/audit-procedure.md +128 -0
  73. package/skills/honest-tests/references/mutation-testing.md +68 -0
  74. package/skills/honest-tests/references/test-smells.md +268 -0
  75. package/skills/honest-tests/references/writing-honest-tests.md +96 -0
@@ -0,0 +1,96 @@
1
+ # Writing honest tests
2
+
3
+ ## The litmus test
4
+
5
+ For every test, you should be able to complete this sentence:
6
+
7
+ > "If someone changes **\_\_\_** in the implementation, this test goes red."
8
+
9
+ If the blank is "nothing" or "it throws", the test is decoration.
10
+
11
+ A cheap mental mutation check: imagine flipping `>=` to `>`, `+` to `-`, `true` to `false`, or
12
+ replacing a return value with `undefined`. Which of your tests would fail? `npx @mintwork/honest-tests mutate
13
+ <file>` does exactly this for real.
14
+
15
+ ## Choosing expected values (the oracle)
16
+
17
+ The expected value must be known **independently** of the code under test:
18
+
19
+ | Source | OK? |
20
+ | --- | --- |
21
+ | Worked out by hand in a comment (`// 40 * 0.9 = 36, + 5 shipping`) | yes |
22
+ | Taken from a spec, ticket, RFC, or domain rule | yes |
23
+ | A fixture file reviewed by a human | yes |
24
+ | Copied from what the code printed when you ran it | **no** — unless you then verified it |
25
+ | Computed with the same formula as the implementation | **no** |
26
+ | `fn(x)` compared with `fn(x)` | **never** |
27
+
28
+ Show your arithmetic in a short comment when it isn't obvious; it makes review trivial.
29
+
30
+ ## Cover the behaviors that bugs live in
31
+
32
+ 1. **Boundaries** — every comparison is a potential off-by-one. For `total >= 50`, test 49.99 and 50.
33
+ For a range `0..100`, test -1, 0, 100, 101.
34
+ 2. **Empty and identity cases** — empty list, 0, empty string, single element.
35
+ 3. **Error paths** — invalid input must throw / reject / return an error *of the right type*:
36
+ `expect(() => applyDiscount(1, 101)).toThrow(RangeError)`.
37
+ 4. **Each branch** — every `if` should have a test where it's true and one where it's false.
38
+ 5. **Side effects that matter** — calls to collaborators *with arguments*:
39
+ `expect(mailer.send).toHaveBeenCalledWith('ada@example.com', expect.stringContaining('Receipt'))`.
40
+
41
+ ## Mock at the boundary, not in the middle
42
+
43
+ Mock: network, database, filesystem, clock (`vi.useFakeTimers()`), randomness, third-party SDKs,
44
+ other services.
45
+
46
+ Don't mock: the unit under test, pure helpers it uses, value objects, or anything cheap and
47
+ deterministic. Over-mocked tests pass while the real wiring is broken.
48
+
49
+ ```ts
50
+ // Dishonest: tests the mock
51
+ vi.mock('../src/pricing');
52
+ vi.mocked(subtotal).mockReturnValue(35);
53
+ expect(subtotal(items)).toBe(35);
54
+
55
+ // Honest: the real unit, a mocked boundary
56
+ const rates = { fetchTaxRate: vi.fn().mockResolvedValue(0.2) };
57
+ expect(await priceWithTax(items, rates)).toBe(42); // 35 * 1.2
58
+ expect(rates.fetchTaxRate).toHaveBeenCalledWith('DE');
59
+ ```
60
+
61
+ ## Prefer precise matchers
62
+
63
+ | Instead of | Use |
64
+ | --- | --- |
65
+ | `toBeDefined()` / `toBeTruthy()` | `toBe(value)`, `toEqual({...})`, `toMatchObject({...})` |
66
+ | `toHaveBeenCalled()` | `toHaveBeenCalledWith(args)` / `toHaveBeenCalledTimes(n)` |
67
+ | `toBeGreaterThan(0)` | `toBe(5)` when the value is known |
68
+ | `toThrow()` | `toThrow(RangeError)` or `toThrow('percent must be between 0 and 100')` |
69
+ | `toMatchSnapshot()` alone | an explicit assertion, optionally plus the snapshot |
70
+
71
+ ## Structure
72
+
73
+ - One behavior per test; name it after the behavior: `'ships free once the discounted total reaches 50'`.
74
+ - Arrange / act / assert, with the act being a call to the real unit.
75
+ - No logic in tests: no `if`, no loops computing expected values, no `try/catch` around assertions.
76
+ Use `it.each` tables for many cases.
77
+ - Async: always `await` (or `return`) promises and `.resolves`/`.rejects` matchers.
78
+ - Errors: `expect(() => f()).toThrow(...)`, `await expect(p).rejects.toThrow(...)`.
79
+
80
+ ## When a test fails
81
+
82
+ 1. Read the failure. Decide whether the **code** or the **test** is wrong — with evidence.
83
+ 2. If the code is wrong: fix the code (or report the bug), keep the test.
84
+ 3. If the test is wrong: fix the expectation *and* write down why the new value is correct.
85
+ 4. Never delete, skip, loosen (`toBe` → `toBeDefined`), or wrap the assertion in `try/catch` to get green.
86
+ 5. Never run snapshot update (`-u`) without reviewing each changed snapshot.
87
+
88
+ ## Before you say "done"
89
+
90
+ ```bash
91
+ npx @mintwork/honest-tests audit <tests> # zero errors
92
+ npx @mintwork/honest-tests mutate <source file> # report the score; explain each survivor
93
+ ```
94
+
95
+ Report: tests added, behaviors covered, mutation score, and any surviving mutants you judged
96
+ equivalent (with a one-line reason each).