# Testing Strategy
See [standards.md §7](standards.md#7-testing) for naming conventions, test location rules, and fixture patterns.
## Test Suites
### Run Commands
```bash
cargo test --workspace # All tests (excl. Docker)
cargo test -p lab-lib # lab-lib crate only
cargo test -p natmap # natmap crate only
cargo test -p auto-discover # auto-discover crate only
cargo test --features docker-tests -- --test-threads=1 # Docker integration tests
```
### Unit Tests (Inline)
Located in `#[cfg(test)] mod tests { }` blocks within source files.
| `src/cmd/cf2ansible.rs` | 20 | DNS zone parsing, record extraction, YAML output |
| `src/cmd/dockernet.rs` | 8 | IP formatting and port bind parsing |
| `crates/auto-discover/src/config.rs` | 18 | YAML config parsing, resolution, forwarding |
| `crates/auto-discover/src/consul.rs` | 5 | Consul service registration, metadata encoding |
| `crates/auto-discover/src/port.rs` | 6 | Port allocation, persistence, free-port checks |
**Total: 83 inline unit tests** (+ 0 in `lab-lib`, currently only re-exports)
### Integration Tests (External)
Located in `tests/` or `crates/*/tests/` directories.
| `tests/integration.rs` | 6 | cf2ansible end-to-end (zone file → YAML output) |
| `crates/natmap/tests/cli.rs` | 11 | Port mapping string parsing |
| `crates/natmap/tests/model.rs` | 10 | Model serialization, rule comment generation |
**Total: 25 integration tests**
### Docker Integration Tests
Located in `tests/natmap_docker.rs` and `tests/auto_discover.rs`, behind `#[cfg(feature = "docker-tests")]`.
**auto-discover Docker tests** (`tests/auto_discover.rs`, 49 tests):
Spins up a privileged Docker container (Ubuntu 24.04 + Consul + iptables) running Consul, natmap, and auto-discover daemons. Verifies Consul registration, port binding, nginx config generation, forwarding metadata, crash recovery, config change handling, nginx config pipeline, registration metadata, forwarding sync (DNAT rules), nginx daemon (file/symlink operations), concurrency, and large configs.
| NAT rule operations (DNAT, SNAT, hairpin, forward) | 5 |
| Rule cleanup (clear, container flush) | 6 |
| Startup flush (natmap chains, postrouting, output) | 6 |
| Port management (freebind, release, conflict) | 3 |
| Graceful shutdown | 3 |
| Other | 5 |
**Total: 28 natmap Docker tests + 49 auto-discover Docker tests = 77 Docker tests**
The Docker image is built once via `Once` from `ubuntu:24.04` with `iptables` installed.
## How Tests Run
`./dev.sh test` executes `cargo test --workspace --all-targets --all-features`. Note that `--all-features` enables `docker-tests` in the root crate and the auto-discover crate, so the Docker tests listed below are included in `./dev.sh test`.
### Docker Test Requirements
Docker tests must run single-threaded:
```bash
cargo test --features docker-tests -- --test-threads=1
```
Each test creates a fresh Docker container. Parallel execution causes race conditions with image builds.
## Common Pitfalls
1. **Docker tests hang**: Always use `--test-threads=1`.
2. **Daemon connection refused**: Docker tests must start the daemon inside the container with `&` and `sleep 2` before CLI commands.
3. **Port binding fails in Docker**: Use `--privileged`. Port allocation requires `CAP_NET_BIND_SERVICE` or root.
4. **State file conflicts**: Each test uses a unique `--state-dir` path.
## Adding New Tests
### Inline Unit Test
```rust
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn function_name_scenario() {
let result = thing_under_test(input);
assert!(result.is_ok());
}
}
```
### Docker Integration Test
1. Add a `#[test]` function to `tests/natmap_docker.rs`
2. Use `run_in_docker(&[...])` for shell commands
3. Pattern: start daemon → wait → run command → verify output