use std::path::PathBuf;
fn fixture_dir() -> PathBuf {
PathBuf::from(env!("CARGO_MANIFEST_DIR"))
.join("tests")
.join("reliability_fixtures")
}
#[test]
fn external_artifact_resolves_into_fault_tree() {
let base = fixture_dir();
let doc = etdl_parser::parse_document_from_file(&base.join("external.etdl"))
.expect("document parses");
let registry = etdl_parser::load_asyncapi_imports(&doc, &base).expect("imports load");
let compiler = etdl_compiler::Compiler::new();
let result = compiler.compile_with_base(&doc, ®istry, &base);
let errors: Vec<_> = result.diagnostics.iter().filter(|d| d.is_error()).collect();
assert!(
errors.is_empty(),
"expected no errors, got {:?}",
errors
.iter()
.map(|d| (&d.code, &d.message))
.collect::<Vec<_>>()
);
let _rust = result.rust_output.expect("rust output present");
let manifest = result
.build_manifest
.expect("build manifest present for reliability build");
let resolved = manifest["resolved_probabilities"].as_array().unwrap();
assert_eq!(resolved.len(), 2);
let values: Vec<f64> = resolved
.iter()
.map(|r| r["value"].as_f64().unwrap())
.collect();
assert!(values.contains(&0.0027));
assert!(values.contains(&0.0012));
let mut diags = Vec::new();
let probs = etdl_compiler::fault_tree::resolve_fault_trees_with_overrides(
&doc,
&etdl_compiler::fault_tree::BasicEventOverrides::from([
("GatewayTimeout".to_string(), 0.0027),
("GatewayUnreachable".to_string(), 0.0012),
]),
&mut diags,
);
let expected = 1.0 - (1.0 - 0.0027) * (1.0 - 0.0012);
let actual = probs["PaymentGatewayFailure"];
assert!(
(actual - expected).abs() < 1e-9,
"expected {expected}, got {actual}"
);
}
#[test]
fn backward_compat_no_supplement_unchanged() {
let base = fixture_dir();
let doc = etdl_parser::parse_document_from_file(&base.join("core-only.etdl")).expect("parses");
let registry = etdl_parser::load_asyncapi_imports(&doc, &base).expect("imports load");
let compiler = etdl_compiler::Compiler::new();
let result = compiler.compile(&doc, ®istry);
let errors: Vec<_> = result.diagnostics.iter().filter(|d| d.is_error()).collect();
assert!(errors.is_empty(), "core-only should validate: {:?}", errors);
assert!(
result.build_manifest.is_none(),
"no reliability supplement => no manifest"
);
let mut diags = Vec::new();
let probs = etdl_compiler::fault_tree::resolve_fault_trees(&doc, &mut diags);
let expected = 1.0 - (1.0 - 0.008) * (1.0 - 0.005027);
let actual = probs["PaymentGatewayFailure"];
assert!(
(actual - expected).abs() < 0.0001,
"expected ~{expected}, got {actual}"
);
}
#[test]
fn supplement_declared_but_optional_and_unsupported_warns() {
let yaml = r#"
etdl: "1.0.0"
info:
title: "T"
version: "1.0.0"
domain: "D"
asyncapi_imports: {}
supplements:
- id: etdl.performance
version: "1.0"
eventTrees:
T:
initiatingEvent: { id: I, message: "a#/m", next: C }
nodes:
C: { type: consequence, operation: terminate }
"#;
let doc: etdl_parser::ast::EtlDocument = serde_yaml::from_str(yaml).unwrap();
let registry = etdl_parser::asyncapi::AsyncApiRegistry::new();
let compiler = etdl_compiler::Compiler::new();
let result = compiler.validate(&doc, ®istry);
assert!(
result.iter().any(|d| d.code == "W-407"),
"optional unsupported supplement should warn W-407, got {:?}",
result
);
}
#[test]
fn golden_worked_example_probability_is_exact() {
let base = fixture_dir();
let doc = etdl_parser::parse_document_from_file(&base.join("core-only.etdl")).unwrap();
let mut diags = Vec::new();
let probs = etdl_compiler::fault_tree::resolve_fault_trees(&doc, &mut diags);
let p = probs["PaymentGatewayFailure"];
let expected = 1.0 - (1.0 - 0.008) * (1.0 - 0.005027);
assert!(
(p - expected).abs() < 0.0001,
"worked example probability drifted: expected ~{expected}, got {p}"
);
assert!((p - 0.012987).abs() < 0.00001, "expected 0.012987, got {p}");
}
#[test]
fn required_unsupported_supplement_errors() {
let yaml = r#"
etdl: "1.0.0"
info:
title: "T"
version: "1.0.0"
domain: "D"
asyncapi_imports: {}
supplements:
- id: etdl.safety
version: "1.0"
required: true
eventTrees:
T:
initiatingEvent: { id: I, message: "a#/m", next: C }
nodes:
C: { type: consequence, operation: terminate }
"#;
let doc: etdl_parser::ast::EtlDocument = serde_yaml::from_str(yaml).unwrap();
let registry = etdl_parser::asyncapi::AsyncApiRegistry::new();
let compiler = etdl_compiler::Compiler::new();
let result = compiler.validate(&doc, ®istry);
assert!(
result.iter().any(|d| d.code == "E-108"),
"required unsupported supplement should error E-108, got {:?}",
result
);
}