//! 0570 — `CanonicalId::const_validate` (used by `from_static`) and
//! `validate()` (used by `new`) agree for every curated input. Same
//! type, same name, same acceptance rules — no surprises when copying
//! a literal between contexts.
//!
//! `from_static` is only callable on a `&'static str`, so this test
//! exercises runtime acceptance for the same literals via `new`. The
//! const path's strictness is verified by the existing compile-time
//! tests in `plugin_id.rs` (anything in the invalid list below would
//! produce `error[E0080]` if passed to `from_static`).

use maat_sdk_types::plugin_id::CanonicalId;

// 5571 — exercise the const path too. Each `const _` declaration
// invokes `from_static` at compile time on the same literal the
// runtime test asserts is valid; a compile failure here means
// `const_validate` rejected a literal `validate()` accepted (an
// agreement break). The invalid side stays runtime-only since
// `compile_fail` doc-tests would cost more than the value of
// per-case coverage.
const _: CanonicalId = CanonicalId::from_static("github.com/example/maat-plugin");
const _: CanonicalId = CanonicalId::from_static("gitlab.example.org/group/sub/proj");
const _: CanonicalId = CanonicalId::from_static("gitlab.example.org/group/sub/sub2/proj");
const _: CanonicalId = CanonicalId::from_static("local:dev-plugin");
const _: CanonicalId = CanonicalId::from_static("system:host");
const _: CanonicalId = CanonicalId::from_static("local:foo.bar");
// 5572: empty-label-in-URL-form — currently accepted by both validators.
const _: CanonicalId = CanonicalId::from_static("github.com//bar");

#[test]
fn agrees_on_curated_valid_inputs() {
    // Every literal here must also be accepted by `const_validate` —
    // i.e. `CanonicalId::from_static(s)` would compile.
    let valid = [
        "github.com/example/maat-plugin",
        "gitlab.example.org/group/sub/proj",
        // 5572: deeper path with multiple slashes after the dot-bearing prefix.
        "gitlab.example.org/group/sub/sub2/proj",
        "local:dev-plugin",
        "system:host",
        "local:foo.bar",
        // 5572: empty label in URL form — currently ACCEPTED by both
        // validators because no rule forbids consecutive slashes. Pinning
        // current behavior so a future tightening surfaces here. If the
        // empty label should be rejected, both validators need an explicit
        // "no consecutive slashes" rule and this entry moves to `invalid`.
        "github.com//bar",
    ];
    for s in valid {
        assert!(
            CanonicalId::new(s.to_string()).is_ok(),
            "validate() should accept {s:?}"
        );
    }
}

#[test]
fn agrees_on_curated_invalid_inputs() {
    // Every literal here must also be rejected by `const_validate` —
    // i.e. `CanonicalId::from_static(s)` would fail const-eval with
    // E0080. validate() must produce the same verdict (an Err).
    let invalid = [
        "",
        "github.com/foo bar",       // ASCII space
        "github.com/foo\tbar",      // tab
        "github.com/foo\u{00A0}",   // NBSP — char-set rejects (Invalid)
        // 5572: U+2028 + U+2029 — line/paragraph separators. char-set rejects.
        "github.com/foo\u{2028}",
        "github.com/foo\u{2029}",
        // 5572: trailing tab — exercises the per-char check at the boundary
        // (mid-string tab already covered above).
        "github.com/foo/bar\t",
        // 5572: non-reserved `:` in body — char-set must reject.
        "foo:bar",
        // 5572: reserved prefix WITH invalid body char — char-set must
        // reject the post-prefix `:`.
        "local:foo:bar",
        "com.example.plugin",       // reverse-DNS without slash
        "org.foundation.bar",       // reverse-DNS without slash
        "github.com/../etc",        // path traversal
        "github.com\\plugin",       // backslash
        "no_dot_at_all",            // missing `.` (and missing `/`)
        "noslash.example",          // has `.`, no `/`
        "foo/bar",                  // missing `.` before first `/`
        "local:",                   // reserved prefix without body
        "system:",                  // reserved prefix without body
    ];
    for s in invalid {
        assert!(
            CanonicalId::new(s.to_string()).is_err(),
            "validate() should reject {s:?}"
        );
    }
}
