#![allow(missing_docs)]
use axioval_engine::{
CapabilityEvaluation, CapabilityRegistry, CompiledRule, EngineError, EvidenceSession,
NotEvaluatedReason, ParameterDescriptor, ParameterType, RuleCapability, RuleContext, Runtime,
ServiceRegistry, SnapshotBoundService, SourceSnapshot, compile,
};
use axioval_ir::{DefinitionPackage, Object, ObjectId, Project, RuleSetPackage, SourceId};
struct Stub;
impl RuleCapability for Stub {
fn id(&self) -> &'static str {
"axioval:capability.property-exists"
}
fn parameters(&self) -> Vec<ParameterDescriptor> {
vec![ParameterDescriptor::required(
"property",
ParameterType::PropertyReference,
)]
}
fn evaluate(&self, _: &RuleContext<'_>, _: &CompiledRule) -> CapabilityEvaluation {
CapabilityEvaluation::evaluated(vec![])
}
}
fn packages() -> (DefinitionPackage, RuleSetPackage) {
(
serde_json::from_str(include_str!(
"../../../fixtures/schema-v0.1.0/definitions.json"
))
.unwrap(),
serde_json::from_str(include_str!("../../../fixtures/schema-v0.1.0/ruleset.json")).unwrap(),
)
}
#[test]
fn canonical_packages_compile() {
let (definitions, rules) = packages();
let registry = CapabilityRegistry::new().register(Stub).unwrap();
assert_eq!(
compile(®istry, &[definitions], &rules)
.unwrap()
.rules()
.len(),
1
);
}
#[test]
fn compiler_fails_closed_for_missing_required_parameter() {
let (definitions, mut rules) = packages();
rules.root.rules[0].parameters.clear();
let registry = CapabilityRegistry::new().register(Stub).unwrap();
assert!(compile(®istry, &[definitions], &rules).is_err());
}
#[test]
fn compiler_rejects_unsupported_definition_schema_version() {
let (mut definitions, rules) = packages();
definitions.schema_version = "999.0.0".into();
let registry = CapabilityRegistry::new().register(Stub).unwrap();
assert!(compile(®istry, &[definitions], &rules).is_err());
}
#[test]
fn compiler_rejects_unsupported_ruleset_schema_version() {
let (definitions, mut rules) = packages();
rules.schema_version = "999.0.0".into();
let registry = CapabilityRegistry::new().register(Stub).unwrap();
assert!(compile(®istry, &[definitions], &rules).is_err());
}
#[test]
fn runtime_rejects_capability_registry_drift() {
let (definitions, rules) = packages();
let compiler_registry = CapabilityRegistry::new().register(Stub).unwrap();
let plan = compile(&compiler_registry, &[definitions], &rules).unwrap();
let error = Runtime::new(CapabilityRegistry::new())
.run(&Project::new(vec![]).unwrap(), plan)
.unwrap_err();
assert!(matches!(error, EngineError::UnknownCapability(_)));
}
#[test]
fn compiler_rejects_duplicate_definition_package_ids() {
let (definitions, rules) = packages();
let duplicate = definitions.clone();
let registry = CapabilityRegistry::new().register(Stub).unwrap();
let error = compile(®istry, &[definitions, duplicate], &rules).unwrap_err();
assert!(matches!(error, EngineError::DuplicateDefinitionPackage(_)));
}
struct Unavailable;
impl RuleCapability for Unavailable {
fn id(&self) -> &'static str {
"axioval:capability.property-exists"
}
fn parameters(&self) -> Vec<ParameterDescriptor> {
vec![ParameterDescriptor::required(
"property",
ParameterType::PropertyReference,
)]
}
fn evaluate(&self, _: &RuleContext<'_>, _: &CompiledRule) -> CapabilityEvaluation {
let mut evaluation = CapabilityEvaluation::default();
evaluation.push_not_evaluated(NotEvaluatedReason::MissingService, "z diagnostic");
evaluation.push_not_evaluated(NotEvaluatedReason::MissingService, "a diagnostic");
evaluation
}
}
struct SessionMarker {
label: &'static str,
snapshots: Vec<SourceSnapshot>,
}
impl SnapshotBoundService for SessionMarker {
fn source_snapshots(&self) -> &[SourceSnapshot] {
&self.snapshots
}
}
struct SessionServiceCapability;
impl RuleCapability for SessionServiceCapability {
fn id(&self) -> &'static str {
"axioval:capability.property-exists"
}
fn parameters(&self) -> Vec<ParameterDescriptor> {
vec![ParameterDescriptor::required(
"property",
ParameterType::PropertyReference,
)]
}
fn evaluate(&self, context: &RuleContext<'_>, _: &CompiledRule) -> CapabilityEvaluation {
let mut evaluation = CapabilityEvaluation::default();
let marker = context.services.get::<SessionMarker>().unwrap();
evaluation.push_not_evaluated(NotEvaluatedReason::MissingService, marker.label);
evaluation
}
}
#[test]
fn runtime_reports_capability_unavailability_without_false_pass() {
let (definitions, rules) = packages();
let registry = CapabilityRegistry::new().register(Unavailable).unwrap();
let plan = compile(®istry, &[definitions], &rules).unwrap();
let report = Runtime::new(registry)
.run(&Project::new(vec![]).unwrap(), plan)
.unwrap();
assert!(report.findings().is_empty());
assert_eq!(report.not_evaluated().len(), 2);
assert_eq!(report.not_evaluated()[0].message, "a diagnostic");
assert_eq!(report.not_evaluated()[1].message, "z diagnostic");
assert_eq!(
report.not_evaluated()[0].reason,
NotEvaluatedReason::MissingService
);
assert_eq!(
report.not_evaluated()[0].rule_id.to_string(),
"wall-reference-required"
);
}
#[test]
fn session_services_are_authoritative_for_session_runs() {
let (definitions, rules) = packages();
let registry = CapabilityRegistry::new()
.register(SessionServiceCapability)
.unwrap();
let plan = compile(®istry, &[definitions], &rules).unwrap();
let source = SourceId::new("test", "runtime-session").unwrap();
let snapshot =
SourceSnapshot::try_new(source.clone(), "revision-1", "sha256:runtime-session").unwrap();
let project = Project::new(vec![Object::new(
ObjectId::new(source, "object-1").unwrap(),
"wall",
)])
.unwrap();
let mut runtime_services = ServiceRegistry::new();
runtime_services
.register(SessionMarker {
label: "runtime",
snapshots: vec![snapshot.clone()],
})
.unwrap();
let session = EvidenceSession::try_new(project, [snapshot.clone()])
.unwrap()
.with_service(SessionMarker {
label: "session",
snapshots: vec![snapshot],
})
.unwrap();
let report = Runtime::new(registry)
.with_services(runtime_services)
.run_session(&session, plan)
.unwrap();
assert_eq!(report.not_evaluated()[0].message, "session");
}