5Create a comprehensive test suite that verifies:
61. Each rule detects the patterns it should
72. Each rule does NOT fire on code it shouldn't
83. Auto-fix produces correct output
94. Import checking works (rules only fire when relevant imports exist)
16 Main.purs -- Test runner
25 EtaReduceDeclSpec.purs
26 RedundantBindSpec.purs
28 ImportsSpec.purs -- Test import detection
29 FixSpec.purs -- Test auto-fix application
34Use `purescript-spec` or simple assertions. Each rule spec should:
36### 1. Test Detection (Positive Cases)
40testDetectsMapIdentity = do
41 let result = runRules [fmapIdRule] (SourceCode "module T where\nimport Prelude\nx = map identity [1]")
42 assertEqual 1 (length result.warnings)
43 assertEqual "FmapId" (unwrap (head result.warnings).ruleId)
46### 2. Test Non-Detection (Negative Cases)
49-- Should NOT fire without Prelude import
50testNoFireWithoutImport = do
51 let result = runRules [fmapIdRule] (SourceCode "module T where\nx = map identity [1]")
52 assertEqual 0 (length result.warnings)
54-- Should NOT fire on non-matching code
55testNoFireOnOtherCode = do
56 let result = runRules [fmapIdRule] (SourceCode "module T where\nimport Prelude\nx = map show [1]")
57 assertEqual 0 (length result.warnings)
60### 3. Test Suggestions
63testSuggestionCorrect = do
64 let result = runRules [fmapIdRule] (SourceCode "module T where\nimport Prelude\nx = map identity [1]")
65 let suggestion = (head result.warnings).suggestion
66 assertEqual (Just "[1]") (map (unwrap <<< _.replacement) suggestion)
73 let input = "module T where\nimport Prelude\nx = map identity [1]"
74 let result = runRules [fmapIdRule] (SourceCode input)
75 let fixed = applyAllFixes input result
76 assertEqual "module T where\nimport Prelude\nx = [1]" fixed
82- [x] `map identity x` → `x`
83- [x] `fmap identity x` → `x`
84- [x] `identity <$> x` → `x`
85- [x] `x <#> identity` → `x`
86- [ ] No fire: `map show x`
87- [ ] No fire: without Prelude import
88- [ ] No fire: user-defined `identity`
91- [x] `map f (map g x)` → `map (f <<< g) x`
92- [x] `f <$> (g <$> x)` → `(f <<< g) <$> x`
93- [x] `x <#> g <#> f` → `x <#> (g >>> f)`
94- [ ] No fire: single map
95- [ ] No fire: without Prelude import
98- [x] `sequenceA (map f x)` → `traverse f x`
99- [x] `sequenceA (f <$> x)` → `traverse f x`
100- [x] `sequence (map f x)` → `mapM f x`
101- [ ] No fire: `sequenceA x` (no map)
102- [ ] No fire: without imports
105- [x] `concat (map f x)` → `concatMap f x`
106- [x] `join (map f x)` → `(=<<) f x`
107- [ ] No fire: `concat x` (no map)
108- [ ] No fire: without imports
111- [x] `not (a == b)` → `a /= b`
112- [x] `not (a /= b)` → `a == b`
113- [ ] No fire: `not (a > b)`
114- [ ] No fire: without imports
117- [x] `if cond then x else pure unit` → `when cond x`
118- [x] `if cond then pure unit else x` → `unless cond x`
119- [ ] No fire: `if cond then x else y` (neither is pure unit)
120- [ ] No fire: without imports
123- [x] `\x -> f x` → `f`
124- [ ] No fire: `\x -> f x x` (x used twice)
125- [ ] No fire: `\x -> x` (no function application)
126- [ ] No fire: `\x y -> f x y` (multiple params - only reduce one at a time?)
129- [x] `foo x = bar x` → `foo = bar`
130- [x] `foo a b = bar a b` → `foo a = bar a`
131- [x] Fix includes removing param from LHS
132- [ ] No fire: `foo x = bar x x`
133- [ ] No fire: `foo x = x`
136- [x] `x >>= pure` → `x`
137- [x] `pure x >>= f` → hint (no auto-fix)
138- [ ] No fire: `x >>= f`
139- [ ] No fire: without imports
142- [x] `let x = 1 in x + 1` → `x + 1 where x = 1`
143- [x] Multiple bindings
144- [ ] Nested let (should it fire on inner?)
146## Implementation Steps
1481. Add `purescript-spec` to dependencies
1492. Create test infrastructure in `Test.Main`
1503. Implement specs for each rule (start with FmapId as template)
1514. Run tests: `spago test` or via Go runner
158purs compile --codegen corefn 'lib/**/*.purs' 'test/**/*.purs' '.spago/**/*.purs'
159psgo output/*/corefn.json
161go build -o test-runner ./output/Test.Main
165## Dependencies to Add
168# In test package spago.yaml
keysesc to close
- j k
- move down, up
- enter
- open
- esc
- back, or up a level
- 1 2 3 4
- readme, files, commits, refs
- /
- filter
- y
- copy the clone command
- c
- compare the chosen commit with another (or shift-click both)
- ?
- show or hide this list