# Rule Regression Testing

Rules change independently from application binaries, so their expected behavior needs a repeatable test contract. MoonRule regression suites store named JSON inputs and expected decisions in version control and evaluate them against one compiled `RuleSet`.

## Case format

```json
[
  {
    "name": "complete valid request",
    "input": { "request": { "email": "dev@example.com" } },
    "expected_passed": true
  },
  {
    "name": "invalid email",
    "input": { "request": { "email": "invalid" } },
    "expected_passed": false,
    "expected_failed_rules": ["contact-email"]
  }
]
```

`expected_failed_rules` is optional. When present, MoonRule compares it as an order-independent set against rules that returned `false`. Evaluation errors are reported separately and always make a case mismatch, so a missing field or type error cannot accidentally satisfy a negative test.

## Library API

```moonbit nocheck
let suite = RuleTestSuite::from_json(case_config).unwrap()
let report = rules.run_test_suite(suite)
assert_true(report.passed)
```

`RuleTestSuiteReport` contains aggregate matched and mismatched counts. Every `RuleTestCaseReport` includes expected and actual decisions, failed-rule names, evaluation-error rule names, and the complete `RuleReport`.

External suites must contain at least one case and default to at most 1,024 cases and 128 characters per case name. `RuleTestSuiteLimits` can tighten or explicitly raise those bounds. Stable configuration diagnostics use `C040` through `C047`.

## Command line and CI

```text
moon run cmd/main -- test-rules \
  examples/api-validation-rules.json \
  examples/api-validation-cases.json
```

Exit codes:

- `0`: every case matched its expectation;
- `1`: at least one expectation mismatch;
- `2`: file, JSON, rule compilation, or suite configuration error.

The output is deterministic JSON and can be retained as a CI artifact. The repository example covers valid input, all-invalid input, missing optional fields, and a partial client-only failure.
