use super::visitor::{BasicHclValidator, validate_with_hcl};
use crate::validation::types::ValidationResult;
#[cfg(test)]
mod flow_validation_tests {
use super::*;
#[test]
fn test_flow_input_undefined_in_all_flows() {
let combined_content = r#"
flow "super1" {
api_url = "https://api1.com"
}
flow "super2" {
api_url = "https://api2.com"
}
action "deploy" "evm::deploy_contract" {
constructor_args = [flow.chain_id]
}
"#;
let mut result = ValidationResult::new();
let _refs = validate_with_hcl(combined_content, &mut result, "runbook.tx").unwrap();
assert!(result.has_errors(), "Expected error for undefined flow input");
let error = result.errors.iter()
.find(|e| e.message.contains("chain_id"))
.expect("Should have error mentioning chain_id");
assert_eq!(error.file.as_deref(), Some("runbook.tx"));
assert_eq!(error.related_locations.len(), 2,
"Should show both flows missing the input");
assert!(error.related_locations.iter()
.any(|loc| loc.message.contains("super1") && loc.message.contains("chain_id")));
assert!(error.related_locations.iter()
.any(|loc| loc.message.contains("super2") && loc.message.contains("chain_id")));
}
#[test]
fn test_flow_input_missing_in_some_flows() {
let combined_content = r#"
flow "super1" {
chain_id = "1"
}
flow "super2" {
api_url = "https://api.com"
}
action "deploy" "evm::deploy_contract" {
constructor_args = [flow.chain_id]
}
"#;
let mut result = ValidationResult::new();
let _refs = validate_with_hcl(combined_content, &mut result, "runbook.tx").unwrap();
assert!(result.has_errors(), "Expected error for partially defined flow input");
let ref_errors: Vec<_> = result.errors.iter()
.filter(|e| e.message.contains("chain_id") && e.message.contains("not defined in all flows"))
.collect();
assert!(!ref_errors.is_empty(), "Should have error at reference site");
let flow_errors: Vec<_> = result.errors.iter()
.filter(|e| e.message.contains("super2") && e.message.contains("missing input"))
.collect();
assert!(!flow_errors.is_empty(), "Should have error at incomplete flow definition");
let ref_error = &ref_errors[0];
assert!(ref_error.related_locations.iter()
.any(|loc| loc.message.contains("super2")),
"Reference error should point to flow missing the input");
}
#[test]
fn test_flow_input_defined_in_all_flows() {
let combined_content = r#"
flow "super1" {
chain_id = "1"
}
flow "super2" {
chain_id = "11155111"
}
variable "chain_config" {
value = flow.chain_id
}
"#;
let mut result = ValidationResult::new();
let _refs = validate_with_hcl(combined_content, &mut result, "runbook.tx").unwrap();
if result.has_errors() {
eprintln!("Errors found:");
for error in &result.errors {
eprintln!(" - {}", error.message);
}
}
assert!(!result.has_errors(), "Should not have errors when all flows define the input");
}
#[test]
fn test_flow_input_in_variable() {
let combined_content = r#"
flow "prod" {
env_name = "production"
}
variable "deployment_target" {
value = flow.region
}
"#;
let mut result = ValidationResult::new();
let _refs = validate_with_hcl(combined_content, &mut result, "runbook.tx").unwrap();
assert!(result.has_errors(), "Should have error for undefined flow input in variable");
let error = result.errors.iter()
.find(|e| e.message.contains("region"))
.expect("Should have error mentioning region");
assert!(error.related_locations.iter()
.any(|loc| loc.message.contains("region")));
}
#[test]
fn test_flow_input_in_output() {
let combined_content = r#"
flow "default" {
chain_id = "1"
}
output "contract_address" {
value = action.deploy.address
network = flow.network_name
}
"#;
let mut result = ValidationResult::new();
let _refs = validate_with_hcl(combined_content, &mut result, "runbook.tx").unwrap();
assert!(result.has_errors(), "Should have error for undefined flow input in output");
let error = result.errors.iter()
.find(|e| e.message.contains("network_name"))
.expect("Should have error mentioning network_name");
assert_eq!(error.related_locations.len(), 1, "Should reference the one flow");
}
#[test]
fn test_multiple_references_to_same_flow_input() {
let combined_content = r#"
flow "main" {
api_key = "secret"
}
action "deploy" "evm::deploy_contract" {
constructor_args = [flow.chain_id]
}
output "api_used" {
value = input.api_url
chain_id = flow.chain_id
}
"#;
let mut result = ValidationResult::new();
let _refs = validate_with_hcl(combined_content, &mut result, "runbook.tx").unwrap();
assert!(result.has_errors(), "Should have errors for undefined flow input");
let errors: Vec<_> = result.errors.iter()
.filter(|e| e.message.contains("chain_id"))
.collect();
assert_eq!(errors.len(), 2, "Should have error at both reference sites");
}
#[test]
fn test_no_flows_defined() {
let combined_content = r#"
variable "chain_config" {
value = flow.chain_id
}
"#;
let mut result = ValidationResult::new();
let _refs = validate_with_hcl(combined_content, &mut result, "runbook.tx").unwrap();
if result.has_errors() {
eprintln!("Errors found:");
for error in &result.errors {
eprintln!(" - {}", error.message);
}
}
assert!(!result.has_errors(), "Should not error when no flows are defined (might be runtime flow)");
}
}