#![allow(unused_imports)]
use super::dag::{build_execution_order, compute_parallel_waves};
use super::tests_helpers::{dag_config, make_base_resource};
use super::*;
use std::collections::HashMap;
#[test]
fn test_fj003_topo_linear() {
let config = dag_config(&["a", "b", "c"], &[("a", "b"), ("b", "c")]);
let order = build_execution_order(&config).unwrap();
assert_eq!(order, vec!["a", "b", "c"]);
}
#[test]
fn test_fj003_topo_parallel() {
let config = dag_config(&["alpha", "beta"], &[]);
let order = build_execution_order(&config).unwrap();
assert_eq!(order, vec!["alpha", "beta"]);
}
#[test]
fn test_fj003_topo_diamond() {
let config = dag_config(
&["top", "left", "right", "bottom"],
&[
("top", "left"),
("top", "right"),
("left", "bottom"),
("right", "bottom"),
],
);
let order = build_execution_order(&config).unwrap();
assert_eq!(order[0], "top");
assert_eq!(order[3], "bottom");
assert_eq!(order[1], "left");
assert_eq!(order[2], "right");
}
#[test]
fn test_fj003_topo_cycle() {
let config = dag_config(&["a", "b"], &[("a", "b"), ("b", "a")]);
let result = build_execution_order(&config);
assert!(result.is_err());
assert!(result.unwrap_err().contains("cycle"));
}
#[test]
fn test_fj003_self_dependency_is_cycle() {
let config = dag_config(&["self-ref"], &[("self-ref", "self-ref")]);
let result = build_execution_order(&config);
assert!(result.is_err(), "self-dependency must be detected as cycle");
assert!(result.unwrap_err().contains("cycle"));
}
#[test]
fn test_fj003_transitive_3_level_chain() {
let config = dag_config(
&["database", "schema", "app"],
&[("database", "schema"), ("schema", "app")],
);
let order = build_execution_order(&config).unwrap();
let pos_db = order.iter().position(|x| x == "database").unwrap();
let pos_schema = order.iter().position(|x| x == "schema").unwrap();
let pos_app = order.iter().position(|x| x == "app").unwrap();
assert!(pos_db < pos_schema, "database before schema");
assert!(pos_schema < pos_app, "schema before app");
}
#[test]
fn test_fj003_empty_depends_on_vs_missing() {
let config = dag_config(&["explicit-empty", "implicit-empty"], &[]);
let order = build_execution_order(&config).unwrap();
assert_eq!(order.len(), 2);
assert_eq!(order[0], "explicit-empty");
assert_eq!(order[1], "implicit-empty");
}
#[test]
fn test_fj003_single_resource_no_deps() {
let config = dag_config(&["solo"], &[]);
let order = build_execution_order(&config).unwrap();
assert_eq!(order, vec!["solo"]);
}
#[test]
fn test_fj003_wide_fan_out_dag() {
let config = dag_config(
&["root", "a", "b", "c", "d"],
&[("root", "a"), ("root", "b"), ("root", "c"), ("root", "d")],
);
let order = build_execution_order(&config).unwrap();
assert_eq!(order[0], "root");
assert_eq!(order[1], "a");
assert_eq!(order[2], "b");
assert_eq!(order[3], "c");
assert_eq!(order[4], "d");
}
#[test]
fn test_fj003_wide_fan_in_dag() {
let config = dag_config(
&["a", "b", "c", "leaf"],
&[("a", "leaf"), ("b", "leaf"), ("c", "leaf")],
);
let order = build_execution_order(&config).unwrap();
assert_eq!(order[0], "a");
assert_eq!(order[1], "b");
assert_eq!(order[2], "c");
assert_eq!(order[3], "leaf");
}
#[test]
fn test_fj003_dag_unknown_dependency() {
let config = dag_config(&["a"], &[("nonexistent", "a")]);
let result = build_execution_order(&config);
assert!(result.is_err());
assert!(result.unwrap_err().contains("unknown"));
}
#[test]
fn test_fj132_build_dag_unknown_dependency() {
let mut resources = indexmap::IndexMap::new();
let mut r = make_base_resource();
r.depends_on = vec!["nonexistent".to_string()];
resources.insert("my-file".to_string(), r);
let config = ForjarConfig {
version: "1.0".to_string(),
name: "test".to_string(),
description: None,
params: HashMap::new(),
machines: indexmap::IndexMap::new(),
resources,
policy: Policy::default(),
outputs: indexmap::IndexMap::new(),
policies: vec![],
data: indexmap::IndexMap::new(),
includes: vec![],
include_provenance: HashMap::new(),
checks: indexmap::IndexMap::new(),
moved: vec![],
secrets: Default::default(),
environments: indexmap::IndexMap::new(),
dist: None,
};
let result = build_execution_order(&config);
assert!(result.is_err());
assert!(result.unwrap_err().contains("unknown"));
}
#[test]
fn test_fj132_kahn_sort_diamond_dependency() {
let mut resources = indexmap::IndexMap::new();
let mut r_a = make_base_resource();
r_a.resource_type = ResourceType::Package;
resources.insert("a".to_string(), r_a);
let mut r_b = make_base_resource();
r_b.resource_type = ResourceType::Package;
r_b.depends_on = vec!["a".to_string()];
resources.insert("b".to_string(), r_b);
let mut r_c = make_base_resource();
r_c.resource_type = ResourceType::Package;
r_c.depends_on = vec!["a".to_string()];
resources.insert("c".to_string(), r_c);
let mut r_d = make_base_resource();
r_d.resource_type = ResourceType::Package;
r_d.depends_on = vec!["b".to_string(), "c".to_string()];
resources.insert("d".to_string(), r_d);
let config = ForjarConfig {
version: "1.0".to_string(),
name: "test".to_string(),
description: None,
params: HashMap::new(),
machines: indexmap::IndexMap::new(),
resources,
policy: Policy::default(),
outputs: indexmap::IndexMap::new(),
policies: vec![],
data: indexmap::IndexMap::new(),
includes: vec![],
include_provenance: HashMap::new(),
checks: indexmap::IndexMap::new(),
moved: vec![],
secrets: Default::default(),
environments: indexmap::IndexMap::new(),
dist: None,
};
let order = build_execution_order(&config).unwrap();
assert_eq!(order, vec!["a", "b", "c", "d"]);
}
#[test]
fn test_fj132_build_execution_order_empty() {
let config = ForjarConfig {
version: "1.0".to_string(),
name: "test".to_string(),
description: None,
params: HashMap::new(),
machines: indexmap::IndexMap::new(),
resources: indexmap::IndexMap::new(),
policy: Policy::default(),
outputs: indexmap::IndexMap::new(),
policies: vec![],
data: indexmap::IndexMap::new(),
includes: vec![],
include_provenance: HashMap::new(),
checks: indexmap::IndexMap::new(),
moved: vec![],
secrets: Default::default(),
environments: indexmap::IndexMap::new(),
dist: None,
};
let order = build_execution_order(&config).unwrap();
assert!(order.is_empty());
}
#[test]
fn test_fj132_unclosed_template() {
let params = HashMap::new();
let machines = indexmap::IndexMap::new();
let result = resolve_template("hello {{params.name", ¶ms, &machines);
assert!(result.is_err());
assert!(result.unwrap_err().contains("unclosed template"));
}
#[test]
fn test_fj132_resolve_template_secret_missing_error() {
let params = HashMap::new();
let machines = indexmap::IndexMap::new();
let result = resolve_template("token={{secrets.zzz-missing-99}}", ¶ms, &machines);
assert!(result.is_err());
assert!(result.unwrap_err().contains("FORJAR_SECRET_ZZZ_MISSING_99"));
}
#[test]
fn test_resolve_template_nested_braces() {
let mut params = HashMap::new();
params.insert(
"x".to_string(),
serde_yaml_ng::Value::String("value".to_string()),
);
let machines = indexmap::IndexMap::new();
let result = resolve_template("{{params.x}}_suffix", ¶ms, &machines).unwrap();
assert_eq!(result, "value_suffix");
}
#[test]
fn test_resolve_template_multiple() {
let mut params = HashMap::new();
params.insert(
"a".to_string(),
serde_yaml_ng::Value::String("hello".to_string()),
);
params.insert(
"b".to_string(),
serde_yaml_ng::Value::String("world".to_string()),
);
let machines = indexmap::IndexMap::new();
let result = resolve_template("{{params.a}}-{{params.b}}", ¶ms, &machines).unwrap();
assert_eq!(result, "hello-world");
}
#[test]
fn test_build_execution_order_empty() {
let config = dag_config(&[], &[]);
let order = build_execution_order(&config).unwrap();
assert!(order.is_empty());
}
#[test]
fn test_build_execution_order_no_deps() {
let config = dag_config(&["alpha", "beta", "gamma"], &[]);
let order = build_execution_order(&config).unwrap();
assert_eq!(order.len(), 3);
assert_eq!(order, vec!["alpha", "beta", "gamma"]);
}