---
name: type-checker-tests
description: Add integration tests for type checker inference and checking functions
allowed-tools: Bash(mkdir:*)
---
# Type Checker Integration Tests
Use this skill when adding new type checker functions or expanding behavior.
**Language:** Test fixtures use PureScript syntax, not Haskell.
## Quick Reference
| Action | Command |
|--------|---------|
| Find next test number | `ls tests-integration/fixtures/checking/ \| tail -5` |
| Run a test or multiple tests | `just tc NNN` or `just tc 101 102` |
| Run with tracing enabled | `just tc --debug NNN` |
| Run all checking tests | `just tc` |
| Accept all pending snapshots | `cargo insta accept` |
Use `just tc --help` for all options.
## Creating a Test
### 1. Create fixture directory
```bash
mkdir tests-integration/fixtures/checking/{NNN_descriptive_name}
```
Tests are auto-discovered by `build.rs` - no manual registration needed.
### 2. Write Main.purs
**Standard pattern** - pair typed (checking) and untyped (inference) variants:
```purescript
module Main where
-- Checking mode: explicit signature constrains type checker
test :: Array Int -> Int
test [x] = x
-- Inference mode: type checker infers unconstrained
test' [x] = x
```
**Guidelines:**
- Test ONE specific behavior per fixture
- Name tests descriptively: `test`, `test'`, `test2`, `test2'`, etc.
- Include edge cases relevant to the behavior being tested
### 3. Generate and review snapshot
```bash
just tc NNN
```
This outputs:
- `CREATED path` (green) with numbered lines showing full content
- `UPDATED path` (yellow) with chunked diff (2 lines context, line numbers)
## Multi-File Tests
For testing imports, re-exports, or cross-module behavior, add multiple `.purs` files
to the same fixture directory. The type checker loads all `.purs` files in the folder.
**Example structure:**
```
tests-integration/fixtures/checking/NNN_import_test/
├── Main.purs # The test file (snapshot generated for Main)
├── Lib.purs # Supporting module
└── Main.snap # Generated snapshot
```
**Lib.purs:**
```purescript
module Lib where
life :: Int
life = 42
data Maybe a = Just a | Nothing
```
**Main.purs:**
```purescript
module Main where
import Lib (life, Maybe(..))
test :: Maybe Int
test = Just life
```
**Key points:**
- Module name must match filename (`Lib.purs` -> `module Lib where`)
- Only `Main.purs` generates a snapshot (the test runs against `Main`)
- Use standard PureScript import syntax
## Reviewing Snapshots
Snapshots have this structure:
```
Terms
functionName :: InferredOrCheckedType
...
Types
TypeName :: Kind
...
Errors
ErrorKind { details } at [location]
```
### Acceptance Criteria
**Before accepting, verify:**
1. **Types are correct** - Check that inferred types match expectations
- `test :: Array Int -> Int` - explicit signature preserved
- `test' :: forall t. Array t -> t` - polymorphism inferred correctly
2. **No unexpected `???`** - This indicates inference failure
- `test :: ???` - STOP: the term failed to type check
- `CannotUnify { ??? -> ???, Int }` - OK in error tests, shows unresolved unification variables
3. **Errors appear where expected** - For tests validating error behavior
- Confirm error kind matches expectations (e.g., `NoInstanceFound`, `CannotUnify`)
- Verify error location points to the correct declaration
4. **Polymorphism is appropriate**
- Check type variable names (`t6`, `a`, etc.) are scoped correctly
- Verify constraints propagate as expected
### Common Issues
| Symptom | Likely Cause |
|---------|--------------|
| `test :: ???` | Test code has syntax error or uses undefined names |
| Unexpected monomorphism | Missing polymorphic context or over-constrained signature |
| Wrong error location | Check binder/expression placement in source |
| Missing types in snapshot | Module header or imports incorrect |
## Accept and Verify
```bash
# Accept only after thorough review
cargo insta accept
# Verify all checking tests pass
just tc
```
## Debugging
When investigating a potential compiler bug:
```bash
# Focus on single test to reduce noise
just tc NNN
# Enable tracing to see type checker behaviour
just tc --debug NNN
```
### Trace Files
The `--debug` flag emits detailed type checker traces to `target/compiler-tracing/`.
**Trace file naming:** `{test_id}_{module_name}.jsonl`
- Example: `200_int_compare_transitive_Main.jsonl`
**Output format:** JSON Lines (one JSON object per line), containing:
- `timestamp` - when the event occurred
- `level` - DEBUG, INFO, or TRACE
- `fields` - trace data (e.g., types being unified)
- `target` - the module emitting the trace (e.g., `checking::algorithm::unification`)
- `span`/`spans` - current span and span stack
**Example trace line:**
```json
{"timestamp":"...","level":"DEBUG","fields":{"t1":"?0","t2":"Int"},"target":"checking::algorithm::unification","span":{"name":"unify"}}
```
When `--debug` is used, the trace file path is shown alongside pending snapshots:
```
UPDATED tests-integration/fixtures/checking/200_int_compare_transitive/Main.snap
TRACE target/compiler-tracing/200_int_compare_transitive_Main.jsonl
```
### Analysing Traces
Trace files can be large for complex tests. Use sampling and filtering:
```bash
# Check file size and line count
wc -l target/compiler-tracing/NNN_*.jsonl
# Sample random lines to get an overview
shuf -n 20 target/compiler-tracing/NNN_*.jsonl | jq .
# Filter by level
jq 'select(.level == "DEBUG")' target/compiler-tracing/NNN_*.jsonl
# Filter by target module
jq 'select(.target | contains("unification"))' target/compiler-tracing/NNN_*.jsonl
# Extract specific fields
jq '{level, target, fields}' target/compiler-tracing/NNN_*.jsonl
```
You should run `just tc` to check for regressions.