use std::sync::Arc;
use fd_policy::airlock::config::SchemaDriftConfig;
use fd_policy::airlock::SchemaDriftGuard;
use fd_policy::ViolationType;
use serde_json::json;
fn strict_schema() -> serde_json::Value {
json!({
"type": "object",
"required": ["path"],
"additionalProperties": false,
"properties": { "path": { "type": "string" } }
})
}
#[test]
fn shared_guard_enforces_a_version_registered_after_boot() {
let guard: Arc<SchemaDriftGuard> = Arc::new(SchemaDriftGuard::empty());
let cfg = SchemaDriftConfig::default();
let tv_id = fd_core::ToolVersionId::new();
assert!(guard
.check(&tv_id, &json!({ "path": "/etc/passwd", "rm": "-rf" }), &cfg)
.is_none());
assert!(!guard.contains(&tv_id));
let registered = guard.upsert(tv_id, &strict_schema());
assert!(registered, "a valid schema must register");
assert!(guard.contains(&tv_id));
let violation = guard
.check(&tv_id, &json!({ "path": "/etc/passwd", "rm": "-rf" }), &cfg)
.expect("a version registered after boot must be drift-checked");
assert_eq!(violation.violation_type, ViolationType::SchemaDrift);
assert!(guard
.check(&tv_id, &json!({ "path": "/tmp/ok" }), &cfg)
.is_none());
}
#[test]
fn fail_closed_is_opt_in_and_blocks_unknown_versions() {
let guard = SchemaDriftGuard::empty();
let tv_id = fd_core::ToolVersionId::new();
let mut cfg = SchemaDriftConfig::default();
assert!(
!cfg.fail_closed_on_unregistered,
"default must remain fail-open"
);
assert!(guard
.check(&tv_id, &json!({ "any": "thing" }), &cfg)
.is_none());
cfg.fail_closed_on_unregistered = true;
let violation = guard
.check(&tv_id, &json!({ "any": "thing" }), &cfg)
.expect("fail-closed blocks an unregistered version");
assert_eq!(violation.violation_type, ViolationType::SchemaDrift);
}