#![expect(
clippy::unwrap_used,
reason = "Tests unwrap Results to assert success and shape; a failed unwrap is the test failing."
)]
use garde as _;
use serde as _;
use serde_json as _;
use shakrs_json_parser::{ConfigParseError, PolicyOverride, canonicalize, parse, scaffold_default};
#[test]
fn parse_valid() {
let json = br#"{
"version": 1,
"policies": {
"defaultEnabled": false,
"overrides": {
"baseline": true,
"astro": { "enabled": true, "params": { "strict": true } }
}
},
"waivers": [
{ "key": "[workspace.lints.clippy].unwrap_used", "subject": "Cargo.toml", "selector": null, "reason": "legacy" }
]
}"#;
let config = parse(json).unwrap();
assert_eq!(config.version, 1, "version parses");
assert!(
!config.policies.default_enabled,
"defaultEnabled camelCase maps onto default_enabled"
);
let baseline = config.policies.overrides.get("baseline").unwrap();
assert!(
matches!(baseline, &PolicyOverride::Enabled(true)),
"a bare boolean override deserializes to Enabled"
);
let astro = config.policies.overrides.get("astro").unwrap();
assert!(
matches!(astro, &PolicyOverride::Detailed { enabled: true, .. }),
"an object override deserializes to Detailed"
);
assert_eq!(config.waivers.len(), 1, "one waiver parses");
let waiver = config.waivers.first().unwrap();
assert_eq!(
waiver.key, "[workspace.lints.clippy].unwrap_used",
"waiver key parses"
);
}
fn assert_rejected(json: &[u8], is_kind: impl Fn(&ConfigParseError) -> bool, why: &str) {
let err = parse(json).unwrap_err();
assert!(is_kind(&err), "{why}: got {err}");
}
#[test]
fn rejects_unknown_field() {
assert_rejected(
br#"{ "version": 1, "bogus": true }"#,
|err| matches!(err, ConfigParseError::Schema(_)),
"an unknown field is a schema error",
);
}
#[test]
fn rejects_empty_reason() {
assert_rejected(
br#"{ "version": 1, "waivers": [ { "key": "[workspace.lints.clippy].unwrap_used", "subject": "Cargo.toml", "selector": null, "reason": "" } ] }"#,
|err| matches!(err, ConfigParseError::Semantic(_)),
"an empty waiver reason fails the semantic pass",
);
}
#[test]
fn rejects_bad_version() {
assert_rejected(
br#"{ "version": 2 }"#,
|err| matches!(err, ConfigParseError::Semantic(_)),
"an unsupported version fails the garde range rule",
);
}
#[test]
fn canonicalize_sorted_and_idempotent() {
let json = br#"{ "version": 1, "policies": { "defaultEnabled": true, "overrides": {} }, "waivers": [] }"#;
let config = parse(json).unwrap();
let bytes = canonicalize(&config).unwrap();
let text = std::str::from_utf8(&bytes).unwrap();
let policies_at = text.find("policies").unwrap();
let version_at = text.find("version").unwrap();
let waivers_at = text.find("waivers").unwrap();
assert!(
policies_at < version_at && version_at < waivers_at,
"top-level keys are sorted in canonical output:\n{text}"
);
let reparsed = parse(&bytes).unwrap();
let again = canonicalize(&reparsed).unwrap();
assert_eq!(bytes, again, "canonicalize is idempotent");
}
#[test]
fn scaffold_round_trips() {
let bytes = scaffold_default().unwrap();
let config = parse(&bytes).unwrap();
assert_eq!(config.version, 1, "scaffold is version 1");
assert!(
config.policies.default_enabled,
"scaffold defaults to the opt-out model"
);
assert!(config.waivers.is_empty(), "scaffold has no waivers");
}