#![allow(clippy::unwrap_used, clippy::expect_used)]
#![allow(clippy::print_stderr)]
#![allow(dead_code)]
mod trace_testing;
use serde::Deserialize;
use std::ffi::OsStr;
use std::path::{Path, PathBuf};
use std::process::Command;
fn clean_environment_command(bin: impl AsRef<OsStr>) -> Command {
let mut cmd = Command::new(bin);
cmd.env_clear()
.env("PATH", std::env::var("PATH").unwrap_or_default())
.env("HOME", std::env::var("HOME").unwrap_or_default())
.env("USER", std::env::var("USER").unwrap_or_default());
cmd
}
#[derive(Debug, Deserialize)]
struct DagExport {
tasks: Vec<DagTask>,
execution_order: Vec<String>,
parallel_groups: Vec<Vec<String>>,
}
#[derive(Debug, Deserialize)]
struct DagTask {
name: String,
dependencies: Vec<String>,
#[serde(default)]
command: Option<String>,
#[serde(default)]
description: Option<String>,
}
fn get_cuenv_bin() -> PathBuf {
let manifest_dir = env!("CARGO_MANIFEST_DIR");
Path::new(manifest_dir)
.parent() .and_then(|p| p.parent()) .expect("Failed to find project root")
.join("target")
.join("debug")
.join("cuenv")
}
fn get_examples_dir() -> PathBuf {
let manifest_dir = env!("CARGO_MANIFEST_DIR");
Path::new(manifest_dir)
.parent() .and_then(|p| p.parent()) .expect("Failed to find project root")
.join("examples")
}
fn binary_available() -> bool {
get_cuenv_bin().exists()
}
macro_rules! skip_if_binary_unavailable {
() => {
if !binary_available() {
eprintln!(
"Skipping test: cuenv binary not found at {:?}. Run `cargo build` first.",
get_cuenv_bin()
);
return;
}
};
}
fn run_dry_run(example: &str, task: &str) -> Result<DagExport, String> {
let bin = get_cuenv_bin();
let examples_dir = get_examples_dir();
let example_path = examples_dir.join(example);
let output = clean_environment_command(&bin)
.args([
"task",
task,
"--dry-run",
"--path",
example_path.to_str().unwrap(),
"--package",
"examples",
])
.output()
.map_err(|e| format!("Failed to run cuenv: {e}"))?;
if !output.status.success() {
let stderr = String::from_utf8_lossy(&output.stderr);
return Err(format!("cuenv --dry-run failed: {stderr}"));
}
let stdout = String::from_utf8_lossy(&output.stdout);
serde_json::from_str(&stdout)
.map_err(|e| format!("Failed to parse DAG JSON: {e}\nOutput: {stdout}"))
}
#[test]
fn test_task_basic_greetall_dag_structure() {
skip_if_binary_unavailable!();
let dag = match run_dry_run("task-basic", "greetAll") {
Ok(d) => d,
Err(e) => {
eprintln!("Skipping test - cuenv execution failed: {e}");
return;
}
};
assert!(!dag.tasks.is_empty(), "DAG should have tasks");
assert!(
!dag.parallel_groups.is_empty(),
"DAG should have parallel groups"
);
for task in &dag.tasks {
assert!(
dag.execution_order.contains(&task.name),
"Task {} should be in execution order",
task.name
);
}
}
#[test]
fn test_task_basic_parallel_groups() {
skip_if_binary_unavailable!();
let dag = match run_dry_run("task-basic", "greetAll") {
Ok(d) => d,
Err(e) => {
eprintln!("Skipping test - cuenv execution failed: {e}");
return;
}
};
assert!(
!dag.parallel_groups.is_empty(),
"Should have at least one parallel group"
);
let first_group = &dag.parallel_groups[0];
for task_name in first_group {
let task = dag
.tasks
.iter()
.find(|t| &t.name == task_name)
.expect("Task in parallel group should exist");
assert!(
task.dependencies.is_empty(),
"Tasks in first parallel group should have no dependencies"
);
}
}
#[test]
fn test_interpolate_task_dag() {
skip_if_binary_unavailable!();
let dag = match run_dry_run("task-basic", "interpolate") {
Ok(d) => d,
Err(e) => {
eprintln!("Skipping test - cuenv execution failed: {e}");
return;
}
};
assert!(!dag.tasks.is_empty(), "DAG should have tasks");
assert!(
!dag.parallel_groups.is_empty(),
"Should have parallel groups"
);
for task in &dag.tasks {
assert!(
dag.execution_order.contains(&task.name),
"Task {} should be in execution order",
task.name
);
}
}
#[test]
fn test_dag_export_includes_command_info() {
skip_if_binary_unavailable!();
let dag = match run_dry_run("task-basic", "interpolate") {
Ok(d) => d,
Err(e) => {
eprintln!("Skipping test - cuenv execution failed: {e}");
return;
}
};
for task in &dag.tasks {
assert!(
task.command.is_some(),
"Task {} should have command information",
task.name
);
}
}
#[test]
fn test_hook_example_verify_env_dag() {
skip_if_binary_unavailable!();
let dag = match run_dry_run("hook", "verify_env") {
Ok(d) => d,
Err(e) => {
eprintln!("Skipping test - cuenv execution failed: {e}");
return;
}
};
assert!(!dag.tasks.is_empty(), "DAG should have tasks");
assert!(
!dag.execution_order.is_empty(),
"Execution order should be populated"
);
}
#[test]
fn test_dagger_task_hello_dag() {
skip_if_binary_unavailable!();
let dag = match run_dry_run("dagger-task", "hello") {
Ok(d) => d,
Err(e) => {
eprintln!("Skipping test - cuenv execution failed: {e}");
return;
}
};
assert!(!dag.tasks.is_empty(), "DAG should have tasks");
for task in &dag.tasks {
assert!(
task.command.is_some(),
"Task {} should have a command",
task.name
);
}
}
#[test]
fn test_ci_pipeline_test_task_dag() {
skip_if_binary_unavailable!();
let dag = match run_dry_run("ci-pipeline", "test") {
Ok(d) => d,
Err(e) => {
eprintln!("Skipping test - cuenv execution failed: {e}");
return;
}
};
assert!(!dag.tasks.is_empty(), "DAG should have tasks");
for task in &dag.tasks {
assert!(
task.command.is_some(),
"Task {} should have a command",
task.name
);
}
}
#[test]
fn test_dry_run_with_nonexistent_task_fails() {
skip_if_binary_unavailable!();
let result = run_dry_run("task-basic", "nonexistent_task_12345");
assert!(result.is_err(), "dry-run with nonexistent task should fail");
}
#[test]
fn test_dry_run_with_nonexistent_example_fails() {
skip_if_binary_unavailable!();
let bin = get_cuenv_bin();
let examples_dir = get_examples_dir();
let example_path = examples_dir.join("nonexistent-example-12345");
let output = clean_environment_command(&bin)
.args([
"task",
"test",
"--dry-run",
"--path",
example_path.to_str().unwrap(),
"--package",
"examples",
])
.output()
.expect("Failed to run cuenv");
assert!(
!output.status.success(),
"dry-run with nonexistent example should fail"
);
}