git.iko.soy/rowtype-yoga/purslint
on v0.1.0 help sign in
rowtype-yoga/purslint/docs/TESTING_PLAN.md
173 lines 4.7 kB history raw
1# Testing Plan
3## Goal
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)
11## Test Structure
13```
14test/
15 Test/
16 Main.purs -- Test runner
17 Rules/
18 FmapIdSpec.purs
19 MapFusionSpec.purs
20 UseTraverseSpec.purs
21 ConcatMapSpec.purs
22 NotEqualSpec.purs
23 UseGuardSpec.purs
24 EtaReduceSpec.purs
25 EtaReduceDeclSpec.purs
26 RedundantBindSpec.purs
27 LetToWhereSpec.purs
28 ImportsSpec.purs -- Test import detection
29 FixSpec.purs -- Test auto-fix application
30```
32## Test Approach
34Use `purescript-spec` or simple assertions. Each rule spec should:
36### 1. Test Detection (Positive Cases)
38```purescript
39-- FmapIdSpec.purs
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)
44```
46### 2. Test Non-Detection (Negative Cases)
48```purescript
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)
58```
60### 3. Test Suggestions
62```purescript
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)
67```
69### 4. Test Auto-Fix
71```purescript
72testAutoFix = do
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
77```
79## Test Cases Per Rule
81### FmapId
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`
90### MapFusion
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
97### UseTraverse
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
104### ConcatMap
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
110### NotEqual
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
116### UseGuard
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
122### EtaReduce
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?)
128### EtaReduceDecl
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`
135### RedundantBind
136- [x] `x >>= pure` → `x`
137- [x] `pure x >>= f` → hint (no auto-fix)
138- [ ] No fire: `x >>= f`
139- [ ] No fire: without imports
141### LetToWhere
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
1525. Add CI integration
154## Running Tests
156```bash
157# Compile and run
158purs compile --codegen corefn 'lib/**/*.purs' 'test/**/*.purs' '.spago/**/*.purs'
159psgo output/*/corefn.json
160# Build test runner
161go build -o test-runner ./output/Test.Main
162./test-runner
163```
165## Dependencies to Add
167```yaml
168# In test package spago.yaml
169dependencies:
170 - spec
171 - spec-discovery
172 - aff
173```