use super::compiler::{compile_rules, CompiledPlan};
use super::model::{Predicate, Rule, Selector};
use crate::config::GatewardenConfig;
use crate::GatewardenError;
pub fn compile_default_plan(config: &GatewardenConfig) -> Result<CompiledPlan, GatewardenError> {
let mut rules = vec![
Rule {
id: "crypto.signature_verified".to_string(),
selector: Selector::SignaturePresent,
predicate: Predicate::BoolIsTrue,
required: true,
},
Rule {
id: "response.state_valid".to_string(),
selector: Selector::StateValid,
predicate: Predicate::BoolIsTrue,
required: true,
},
];
for (i, ent) in config.required_entitlements.iter().enumerate() {
rules.push(Rule {
id: format!("entitlements.required_{}", i),
selector: Selector::Entitlements,
predicate: Predicate::ContainsString(ent.clone()),
required: true,
});
}
Ok(compile_rules(rules))
}
#[cfg(test)]
mod tests {
use super::*;
use std::time::Duration;
fn test_config() -> GatewardenConfig {
GatewardenConfig {
app_name: "test-app".to_string(),
feature_name: "test".to_string(),
account_id: "test-account".to_string(),
public_key_hex: "d75a980182b10ab7d54bfed3c964073a0ee172f3daa62325af021a68f707511a"
.to_string(),
required_entitlements: vec![],
user_agent_product: "test-product".to_string(),
cache_namespace: "gatewarden-test".to_string(),
offline_grace: Duration::from_secs(86400),
}
}
#[test]
fn test_compile_default_plan_no_entitlements() {
let config = test_config();
let plan = compile_default_plan(&config).unwrap();
assert_eq!(plan.rules.len(), 2);
assert_eq!(plan.required_count, 2);
assert_eq!(plan.rules[0].id, "crypto.signature_verified");
assert_eq!(plan.rules[1].id, "response.state_valid");
assert!(plan.rules.iter().all(|r| r.required));
}
#[test]
fn test_compile_default_plan_with_entitlements() {
let mut config = test_config();
config.required_entitlements = vec![
"PREMIUM".to_string(),
"VISION_ANALYSIS".to_string(),
"API_ACCESS".to_string(),
];
let plan = compile_default_plan(&config).unwrap();
assert_eq!(plan.rules.len(), 5);
assert_eq!(plan.required_count, 5);
assert_eq!(plan.rules[0].id, "crypto.signature_verified");
assert_eq!(plan.rules[1].id, "response.state_valid");
assert_eq!(plan.rules[2].id, "entitlements.required_0");
assert_eq!(plan.rules[3].id, "entitlements.required_1");
assert_eq!(plan.rules[4].id, "entitlements.required_2");
if let Predicate::ContainsString(ent) = &plan.rules[2].predicate {
assert_eq!(ent, "PREMIUM");
} else {
panic!("Expected ContainsString predicate");
}
}
#[test]
fn test_compile_default_plan_selectors_deduplicated() {
let mut config = test_config();
config.required_entitlements = vec!["ENT1".to_string(), "ENT2".to_string()];
let plan = compile_default_plan(&config).unwrap();
assert_eq!(plan.selectors.len(), 3);
assert!(plan.selectors.contains(&Selector::SignaturePresent));
assert!(plan.selectors.contains(&Selector::StateValid));
assert!(plan.selectors.contains(&Selector::Entitlements));
}
#[test]
fn test_compile_default_plan_path_index() {
let mut config = test_config();
config.required_entitlements = vec!["ENT1".to_string(), "ENT2".to_string()];
let plan = compile_default_plan(&config).unwrap();
assert_eq!(plan.path_index[&Selector::SignaturePresent], vec![0]);
assert_eq!(plan.path_index[&Selector::StateValid], vec![1]);
assert_eq!(plan.path_index[&Selector::Entitlements], vec![2, 3]);
}
}