//! Carve-out inventory check — §T4 of the form-a-default-authoring
//! spec. Asserts that exactly the named carve-out files exist
//! under `examples/*.ail.json` after milestone close. The list is
//! hardcoded; any change is a deliberate, brainstorm-level decision.
//!
//! Twelve carve-outs:
//! - §C4 (a) subject-matter: 7 fixtures from form-a.1 milestone
//!   close (canonical-form rejection / unbound / class-def rejection).
//! - §C4 (b) compile-time-embed: retired 2026-05-14 by milestone
//!   prelude-decouple; the prelude is now embedded as `prelude.ail`
//!   in `ailang-surface` and parsed at compile time via
//!   `ailang_surface::parse_prelude`.
//! - loop-recur iter 2: 4 negative typecheck fixtures for the
//!   four `Recur*` diagnostics (recur-outside-loop /
//!   recur-arity-mismatch / recur-type-mismatch /
//!   recur-not-in-tail-position) — the diagnostic code is the
//!   load-bearing assertion, mirroring the §C4(a)
//!   negative-fixture convention.
//! - loop-recur iter loop-recur.tidy: 1 negative typecheck fixture
//!   for the lambda-capture-of-loop-binder rejection
//!   (loop-binder-captured-by-lambda).

use ailang_test_support::examples_dir;
use std::collections::BTreeSet;

const EXPECTED: &[&str] = &[
    // §C4 (a) — subject-matter (form-a.1 milestone close)
    "broken_unbound.ail.json",
    "test_22b2_invalid_superclass_param.ail.json",
    "test_22b2_kind_mismatch.ail.json",
    "test_22b2_unbound_constraint_var.ail.json",
    "test_ct1_bad_qualifier.ail.json",
    "test_ct1_bare_xmod_rejected.ail.json",
    "test_ct1_qualified_class_rejected.ail.json",
    // Lambda-capture-of-loop-binder rejection
    "test_loop_binder_captured_by_lambda.ail.json",
    // loop-recur iter 2 — negative typecheck fixtures
    "test_recur_arity_mismatch.ail.json",
    "test_recur_not_in_tail_position.ail.json",
    "test_recur_outside_loop.ail.json",
    "test_recur_type_mismatch.ail.json",
];

#[test]
fn examples_ail_json_inventory_matches_carve_outs() {
    let expected: BTreeSet<&str> = EXPECTED.iter().copied().collect();
    let mut actual: BTreeSet<String> = BTreeSet::new();
    for entry in std::fs::read_dir(examples_dir()).expect("read examples/") {
        let entry = entry.unwrap();
        let name = entry.file_name().into_string().unwrap();
        if name.ends_with(".ail.json") {
            actual.insert(name);
        }
    }
    let actual_refs: BTreeSet<&str> = actual.iter().map(String::as_str).collect();
    assert_eq!(
        actual_refs, expected,
        "examples/*.ail.json inventory drifted from carve-out list",
    );
}
