waddling-errors-macros 0.7.3

Procedural macros for structured error codes with compile-time validation and taxonomy enforcement
Documentation
# 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
   |
28 |     E.Auth.Token.NONEXISTENT: {
   |                  ^^^^^^^^^^^ not found in `crate::sequences`
```

### Invalid Primary
```
error[E0425]: cannot find value `NonExistent` in module `crate::primaries`
  --> tests/compile-fail/invalid_primary.rs:45:12
   |
45 |     E.Auth.NonExistent.MISSING: {
   |            ^^^^^^^^^^^ not found in `crate::primaries`
```

### Invalid Component
```
error[E0425]: cannot find value `Database` in module `crate::components`
  --> tests/compile-fail/invalid_component.rs:59:7
   |
59 |     E.Database.Token.MISSING: {
   |       ^^^^^^^^ not found in `crate::components`
```

---

## 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)