# Compile-Time Validation Tests
This directory contains comprehensive tests for the `waddling-errors-macros` compile-time validation system.
## Overview
The `diag!` macro supports **7 different validation modes** through the `strict()` mode:
```rust
diag! {
strict(sequence, primary, component, naming, duplicates, sequence_values, string_values),
E.Auth.Token.EXPIRED: {
message: "Token expired",
}
}
```
These tests ensure that validation **actually catches errors at compile time**.
---
## Test Structure
### `compile-fail/` - Tests That Should Fail Compilation
These tests verify that invalid references are caught at compile time:
- **`invalid_sequence.rs`** - References non-existent sequence constant
- **`invalid_primary.rs`** - References non-existent primary constant
- **`invalid_component.rs`** - References non-existent component enum variant
- **`strict_all_invalid_sequence.rs`** - Tests `strict(sequence, primary, component)` catches sequence errors
- **`invalid_naming_conventions.rs`** - Catches lowercase sequence names
- **`invalid_naming_primary_lowercase.rs`** - Catches lowercase primary names
- **`invalid_naming_component_lowercase.rs`** - Catches lowercase component names
- **`duplicate_diagnostic_codes.rs`** - Catches duplicate diagnostic codes
Each test **must fail to compile** with a specific error message indicating the validation failure.
### `compile-pass/` - Tests That Should Compile Successfully
These tests verify that valid code compiles correctly:
- **`relaxed_mode_allows_invalid.rs`** - Relaxed mode allows undefined references
- **`strict_mode_with_valid_refs.rs`** - Strict mode passes when all references are valid
- **`valid_naming_conventions.rs`** - Correct naming conventions pass validation
- **`no_duplicate_codes.rs`** - Unique codes pass duplicate detection
- **`validates_sequence_values.rs`** - Sequence type checking passes
- **`validates_string_values.rs`** - String type checking passes
Each test **must compile successfully** without errors.
---
## Running Tests
### Run all compile tests:
```bash
cargo test -p waddling-errors-macros --test compile_fail --features metadata
```
### Run only fail tests:
```bash
cargo test -p waddling-errors-macros compile_fail_tests --features metadata
```
### Run only pass tests:
```bash
cargo test -p waddling-errors-macros compile_pass_tests --features metadata
```
---
## Test Framework
We use [`trybuild`](https://github.com/dtolnay/trybuild) for compile-time testing:
- **Compile-fail tests**: Verify that code fails to compile with expected error messages
- **Compile-pass tests**: Verify that valid code compiles successfully
### How trybuild Works
1. **Compile-fail tests** compare actual compiler errors against `.stderr` files
2. If errors don't match, the test fails
3. Run with `TRYBUILD=overwrite` to update expected error files
### Updating Expected Errors
If you modify the macros and error messages change:
```bash
TRYBUILD=overwrite cargo test -p waddling-errors-macros --test compile_fail --features metadata
```
This regenerates the `.stderr` files with new expected errors.
---
## What Is Validated
### 1. Sequence Validation (`strict(sequence)`)
```rust
diag! {
strict(sequence),
E.Auth.Token.EXPIRED: { ... }
}
```
- ✅ Checks that `crate::sequences::EXPIRED` constant exists
- ✅ Fails at compile time if sequence is missing or misspelled
### 2. Primary Validation (`strict(primary)`)
```rust
diag! {
strict(primary),
E.Auth.Token.EXPIRED: { ... }
}
```
- ✅ Checks that `crate::primaries::Token` constant exists
- ✅ Fails at compile time if primary is missing or misspelled
### 3. Component Validation (`strict(component)`)
```rust
diag! {
strict(component),
E.Auth.Token.EXPIRED: { ... }
}
```
- ✅ Checks that `crate::components::Auth` enum variant exists
- ✅ Fails at compile time if component is missing or misspelled
### 4. Naming Convention Validation (`strict(naming)`)
```rust
diag! {
strict(naming),
E.Auth.Token.EXPIRED: { ... }
}
```
- ✅ Validates UPPER_SNAKE_CASE for sequences (e.g., `EXPIRED`, `MISSING`)
- ✅ Validates PascalCase for primaries (e.g., `Token`, `Permission`)
- ✅ Validates PascalCase for components (e.g., `Auth`, `Database`)
- ✅ Fails at compile time with helpful suggestions
### 5. Duplicate Detection (`strict(duplicates)`)
```rust
diag! {
strict(duplicates),
E.Auth.Token.MISSING: { ... },
E.Auth.Token.MISSING: { ... }, // ❌ Duplicate!
}
```
- ✅ Detects duplicate diagnostic codes within the same block
- ✅ Shows both occurrences in error message
- ✅ Ensures each code is unique
### 6. Sequence Value Validation (`strict(sequence_values)`)
```rust
diag! {
strict(sequence_values),
E.Auth.Token.EXPIRED: { ... }
}
```
- ✅ Type-checks that sequence constants are `u16`
- ✅ Verifies sequence constants have correct type
- ✅ Numeric values are enforced by `sequence!` macro
### 7. String Value Validation (`strict(string_values)`)
```rust
diag! {
strict(string_values),
E.Auth.Token.EXPIRED: { ... }
}
```
- ✅ Type-checks that primary constants are `&str`
- ✅ Verifies component enum variants exist
- ✅ String values are enforced by `primary!`/`component!` macros
### Combined Validation
```rust
diag! {
strict(sequence, primary, component, naming, duplicates, sequence_values, string_values),
E.Auth.Token.EXPIRED: { ... }
}
```
- ✅ Validates everything at once
- ✅ Maximum compile-time safety
- ✅ Catches all possible errors
### Relaxed Mode
```rust
diag! {
relaxed, // or omit strict() entirely
E.Any.Thing.WORKS: { ... }
}
```
- ✅ No validation - allows any identifiers
- ✅ Useful for prototyping or dynamic code
---
## Expected Error Messages
### Invalid Sequence
```
error[E0425]: cannot find value `NONEXISTENT` in module `crate::sequences`
--> tests/compile-fail/invalid_sequence.rs:28:18
|
```
### Invalid Primary
```
error[E0425]: cannot find value `NonExistent` in module `crate::primaries`
--> tests/compile-fail/invalid_primary.rs:45:12
|
```
### Invalid Component
```
error[E0425]: cannot find value `Database` in module `crate::components`
--> tests/compile-fail/invalid_component.rs:59:7
|
```
---
## Adding New Tests
### 1. Create a new test file:
```bash
# For tests that should fail:
touch tests/compile-fail/my_new_test.rs
# For tests that should pass:
touch tests/compile-pass/my_new_test.rs
```
### 2. Write the test:
```rust
//! Test: Brief description of what this test validates
use waddling_errors_macros::{diag, sequence, primary, component};
// Setup: define sequences/primaries/components
// Test: write diag! macro invocation that should fail/pass
fn main() {}
```
### 3. Run tests to generate expected errors:
```bash
TRYBUILD=overwrite cargo test -p waddling-errors-macros --test compile_fail --features metadata
```
### 4. Verify the test:
```bash
cargo test -p waddling-errors-macros --test compile_fail --features metadata
```
---
## Why These Tests Matter
1. **Catch Typos Early** - Misspelled sequences/primaries/components fail at compile time, not runtime
2. **Prevent Regressions** - Ensure validation keeps working as macros evolve
3. **Document Behavior** - Tests serve as examples of what should/shouldn't compile
4. **Build Confidence** - Users can trust that strict mode actually validates
---
## CI Integration
Include these tests in your CI pipeline:
```yaml
- name: Run compile-time validation tests
run: cargo test -p waddling-errors-macros --test compile_fail --features metadata
```
This ensures that:
- Invalid code still fails to compile (tests catch regressions)
- Valid code still compiles (no false positives)
- Error messages remain clear and helpful
---
## Test Coverage
✅ **Currently Covered:**
- ✅ Invalid sequence names fail compilation
- ✅ Invalid primary names fail compilation
- ✅ Invalid component names fail compilation
- ✅ Combined validation modes work correctly
- ✅ Relaxed mode allows undefined references
- ✅ Valid strict mode code compiles successfully
- ✅ Naming convention validation (UPPER_SNAKE_CASE, PascalCase)
- ✅ Duplicate diagnostic code detection
- ✅ Sequence value type checking (u16)
- ✅ String value type checking (&str, enum variants)
❌ **Not Yet Covered:**
- Role-gated field validation
- Cross-module duplicate detection
- Numeric range validation for sequences
These could be added in future test expansions if needed.
---
## Troubleshooting
### Test fails with "stderr mismatch"
The actual error doesn't match the expected `.stderr` file. Run with `TRYBUILD=overwrite` to update.
### Test passes but shouldn't compile
Check that the test file is in `compile-fail/` and uses invalid references.
### Test fails but should compile
Check that the test file is in `compile-pass/` and all references are valid.
### "successfully created new stderr files" error
Run the test with `TRYBUILD=overwrite` to accept the new error messages as correct.
---
## Further Reading
- [trybuild documentation](https://docs.rs/trybuild/)
- [Rust compile-fail testing](https://doc.rust-lang.org/rustc-dev-guide/tests/ui.html)
- [waddling-errors-macros validation docs](../README.md#compile-time-validation)