axioval-engine 0.1.17

Trusted capability compiler and deterministic source-neutral validation runtime
Documentation
//! Strict compiler contract tests.
#![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(&registry, &[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(&registry, &[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(&registry, &[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(&registry, &[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(&registry, &[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(&registry, &[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(&registry, &[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");
}