#![allow(clippy::float_cmp)]
use super::*;
#[test]
fn test_adr019_example() {
let config = DecisionTreeConfig::new("R&D Investment")
.with_root(
Node::decision("Invest in R&D?")
.with_branch(
"invest",
Branch::continuation("tech_outcome").with_cost(2_000_000.0),
)
.with_branch("dont_invest", Branch::terminal(0.0)),
)
.with_node(
"tech_outcome",
Node::chance("Technology works?")
.with_branch(
"success",
Branch::continuation("commercialize").with_probability(0.60),
)
.with_branch(
"failure",
Branch::terminal(-2_000_000.0).with_probability(0.40),
),
)
.with_node(
"commercialize",
Node::decision("How to commercialize?")
.with_branch("license", Branch::terminal(5_000_000.0))
.with_branch(
"manufacture",
Branch::terminal(8_000_000.0).with_cost(3_000_000.0),
),
);
let engine = DecisionTreeEngine::new(config).unwrap();
let result = engine.analyze().unwrap();
let commercialize = result.node_results.get("commercialize").unwrap();
assert!(
(commercialize.expected_value - 5_000_000.0).abs() < 0.01,
"Commercialize EV should be $5M"
);
assert_eq!(
commercialize.optimal_choice,
Some("license".to_string()),
"Should choose license"
);
let tech = result.node_results.get("tech_outcome").unwrap();
assert!(
(tech.expected_value - 2_200_000.0).abs() < 0.01,
"Tech outcome EV should be $2.2M, got {}",
tech.expected_value
);
assert!(
(result.root_expected_value - 200_000.0).abs() < 0.01,
"Root EV should be $0.2M, got {}",
result.root_expected_value
);
assert_eq!(
result.decision_policy.get("root"),
Some(&"invest".to_string()),
"Should recommend investing"
);
}
#[test]
fn test_three_way_decision() {
let config = DecisionTreeConfig::new("Three Options").with_root(
Node::decision("Choose strategy")
.with_branch("conservative", Branch::terminal(100_000.0))
.with_branch("moderate", Branch::terminal(150_000.0))
.with_branch("aggressive", Branch::terminal(200_000.0)),
);
let engine = DecisionTreeEngine::new(config).unwrap();
let result = engine.analyze().unwrap();
assert_eq!(result.root_expected_value, 200_000.0);
assert_eq!(
result.decision_policy.get("root"),
Some(&"aggressive".to_string())
);
}
#[test]
fn test_nested_chance_nodes() {
let config = DecisionTreeConfig::new("Nested Chance")
.with_root(
Node::chance("First flip")
.with_branch(
"heads",
Branch::continuation("second_flip").with_probability(0.5),
)
.with_branch("tails", Branch::terminal(0.0).with_probability(0.5)),
)
.with_node(
"second_flip",
Node::chance("Second flip")
.with_branch("heads", Branch::terminal(100.0).with_probability(0.5))
.with_branch("tails", Branch::terminal(50.0).with_probability(0.5)),
);
let engine = DecisionTreeEngine::new(config).unwrap();
let result = engine.analyze().unwrap();
assert!(
(result.root_expected_value - 37.5).abs() < 0.01,
"Expected 37.5, got {}",
result.root_expected_value
);
}
#[test]
fn test_risk_profile() {
let config = DecisionTreeConfig::new("Risk Test").with_root(
Node::chance("Outcome")
.with_branch("best", Branch::terminal(1000.0).with_probability(0.3))
.with_branch("middle", Branch::terminal(500.0).with_probability(0.5))
.with_branch("worst", Branch::terminal(-200.0).with_probability(0.2)),
);
let engine = DecisionTreeEngine::new(config).unwrap();
let result = engine.analyze().unwrap();
assert_eq!(result.risk_profile.best_case, 1000.0);
assert_eq!(result.risk_profile.worst_case, -200.0);
assert!(
(result.risk_profile.probability_positive - 0.8).abs() < 0.01,
"Expected 0.8 prob positive, got {}",
result.risk_profile.probability_positive
);
}
#[test]
fn test_json_export() {
let config = DecisionTreeConfig::new("Export Test").with_root(
Node::decision("Choose")
.with_branch("a", Branch::terminal(100.0))
.with_branch("b", Branch::terminal(50.0)),
);
let engine = DecisionTreeEngine::new(config).unwrap();
let result = engine.analyze().unwrap();
let json = result.to_json().unwrap();
assert!(json.contains("\"root_expected_value\""));
assert!(json.contains("\"decision_policy\""));
assert!(json.contains("\"risk_profile\""));
}