use lemma::{DateGranularity, DateTimeValue, Engine, SourceType};
use std::collections::HashMap;
use std::sync::Arc;
fn path_source(path: &str) -> SourceType {
SourceType::Path(Arc::new(std::path::PathBuf::from(path)))
}
fn wasm_style_instants() -> [DateTimeValue; 4] {
[
DateTimeValue::now(),
DateTimeValue {
year: 2026,
month: 1,
day: 1,
hour: 0,
minute: 0,
second: 0,
microsecond: 0,
timezone: None,
granularity: DateGranularity::Full,
},
DateTimeValue {
year: 2024,
month: 1,
day: 1,
hour: 0,
minute: 0,
second: 0,
microsecond: 0,
timezone: None,
granularity: DateGranularity::Full,
},
DateTimeValue {
year: 2020,
month: 6,
day: 15,
hour: 12,
minute: 0,
second: 0,
microsecond: 0,
timezone: None,
granularity: DateGranularity::DateTime,
},
]
}
#[test]
fn dependency_batch_get_plan_and_schema_via_repo_qualifier() {
let mut engine = Engine::new();
let bundle = r#"
spec alpha2
data code: text
-> option "NL"
spec cashier
uses C: alpha2
with C.code: "NL"
rule country: C.code
"#;
engine
.load_batch(
HashMap::from([(SourceType::Volatile, bundle.to_string())]),
Some("@iso/countries"),
)
.expect("dependency-tag load_batch should parse and plan");
for instant in wasm_style_instants() {
let plan = engine
.get_plan(Some("@iso/countries"), "cashier", Some(&instant))
.expect("get_plan(Some(dep), cashier) mirrors wasm.schema with repository set");
let _ = plan.schema(&lemma::DataOverlay::default());
engine
.schema(Some("@iso/countries"), "cashier", Some(&instant))
.expect("Engine::schema same path as WasmEngine.schema");
engine
.schema(Some("@iso/countries"), "alpha2", Some(&instant))
.expect("alpha2 exposes schema inside dependency-qualified repository");
}
}
#[test]
fn workspace_consumer_after_dependency_batch_resolves_country_type() {
let mut engine = Engine::new();
engine
.load_batch(
HashMap::from([(
path_source("deps/countries.lemma"),
r#"spec alpha2
data code: text
-> option "NL"
-> option "BE"
"#
.to_string(),
)]),
Some("@iso/countries"),
)
.expect("registry dependency bundle loads under @iso/countries");
engine
.load(
r#"spec kiosk
uses @iso/countries alpha2
data country: alpha2.code
rule tally: country
"#,
path_source("kiosk.lemma"),
)
.expect("workspace kiosk uses registry-style qualifier line like examples/12_registry_references");
for instant in wasm_style_instants() {
let plan = engine.get_plan(None, "kiosk", Some(&instant));
match plan {
Ok(p) => {
let _ = p.schema(&lemma::DataOverlay::default());
let _ = engine.schema(None, "kiosk", Some(&instant));
}
Err(e) => {
panic!(
"kiosk should plan across instants once dependency types resolve — error: {}",
e
);
}
}
}
}
#[test]
fn duplicate_named_finance_across_named_repositories_plan_without_panic() {
let mut engine = Engine::new();
engine
.load(
r#"repo tier_a
spec finance
data x: number
rule gross: x
"#,
path_source("a.lemma"),
)
.expect("tier_a repository loads");
engine
.load_batch(
HashMap::from([(
path_source("tier_b/bundle.lemma"),
r#"repo tier_b
spec finance
data y: number
rule gross: y
"#
.to_string(),
)]),
Some("tier_b_dep"),
)
.expect("tier_b dependency bundle loads with duplicate bare spec name `finance`");
let counts: Vec<(Option<String>, usize)> = engine
.list()
.iter()
.map(|entry| (entry.repository.name.clone(), entry.specs.len()))
.collect();
assert!(
counts
.iter()
.any(|(name, _)| name.as_deref() == Some("tier_a")),
"expected tier_a repository in {:?}",
counts
);
assert!(
counts
.iter()
.any(|(name, _)| name.as_deref() == Some("tier_b")),
"expected tier_b repository in {:?}",
counts
);
let now = DateTimeValue::now();
engine
.get_plan(Some("tier_a"), "finance", Some(&now))
.expect("finance in tier_a");
engine
.get_plan(Some("tier_b"), "finance", Some(&now))
.expect("finance in tier_b");
engine
.schema(Some("tier_a"), "finance", Some(&now))
.expect("schema tier_a finance");
engine
.schema(Some("tier_b"), "finance", Some(&now))
.expect("schema tier_b finance");
}