Add 87 unit tests for uncovered pure logic in arc-workflows

Cover format_cost, format_tokens_human, compute_stage_cost, all
format_event_summary variants, validate_command, context_diff, and
parse_label_filters with fast unit tests.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Bryan Helmkamp 2026-03-03 23:42:34 -05:00
parent fe9f6f6882
commit 7b39b42fda
4 changed files with 1136 additions and 0 deletions

File diff suppressed because it is too large Load diff

View file

@ -620,4 +620,41 @@ mod tests {
prune_from(&args, base).unwrap();
assert!(!orphan_dir.exists(), "orphan directory should be deleted");
}
#[test]
fn parse_label_filters_basic() {
let args = vec!["env=prod".to_string()];
let result = parse_label_filters(&args);
assert_eq!(result, vec![("env".to_string(), "prod".to_string())]);
}
#[test]
fn parse_label_filters_multiple() {
let args = vec!["a=1".to_string(), "b=2".to_string()];
let result = parse_label_filters(&args);
assert_eq!(result.len(), 2);
assert!(result.contains(&("a".to_string(), "1".to_string())));
assert!(result.contains(&("b".to_string(), "2".to_string())));
}
#[test]
fn parse_label_filters_value_with_equals() {
let args = vec!["key=a=b".to_string()];
let result = parse_label_filters(&args);
assert_eq!(result, vec![("key".to_string(), "a=b".to_string())]);
}
#[test]
fn parse_label_filters_skips_no_equals() {
let args = vec!["nope".to_string(), "a=1".to_string()];
let result = parse_label_filters(&args);
assert_eq!(result, vec![("a".to_string(), "1".to_string())]);
}
#[test]
fn parse_label_filters_empty() {
let args: Vec<String> = vec![];
let result = parse_label_filters(&args);
assert!(result.is_empty());
}
}

View file

@ -31,3 +31,61 @@ pub fn validate_command(args: &ValidateArgs, styles: &Styles) -> anyhow::Result<
eprintln!("Validation: {}", styles.green.apply_to("OK"));
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
use std::io::Write;
use std::path::PathBuf;
#[test]
fn validate_valid_workflow() {
let mut tmp = tempfile::NamedTempFile::new().unwrap();
write!(
tmp,
r#"digraph Simple {{
graph [goal="Run tests and report results"]
rankdir=LR
start [shape=Mdiamond, label="Start"]
exit [shape=Msquare, label="Exit"]
run_tests [label="Run Tests", prompt="Run the test suite and report results"]
report [label="Report", prompt="Summarize the test results"]
start -> run_tests -> report -> exit
}}"#
)
.unwrap();
let args = ValidateArgs {
workflow: tmp.path().to_path_buf(),
};
let styles = Styles::new(false);
let result = validate_command(&args, &styles);
assert!(result.is_ok(), "expected Ok but got: {result:?}");
}
#[test]
fn validate_invalid_syntax() {
let mut tmp = tempfile::NamedTempFile::new().unwrap();
write!(tmp, "not a valid dot file").unwrap();
let args = ValidateArgs {
workflow: tmp.path().to_path_buf(),
};
let styles = Styles::new(false);
let result = validate_command(&args, &styles);
assert!(result.is_err(), "expected Err for invalid syntax");
}
#[test]
fn validate_missing_file() {
let args = ValidateArgs {
workflow: PathBuf::from("/tmp/nonexistent_workflow_12345.dot"),
};
let styles = Styles::new(false);
let result = validate_command(&args, &styles);
assert!(result.is_err(), "expected Err for missing file");
}
}

View file

@ -595,4 +595,44 @@ mod tests {
fn parse_duration_str_invalid_fallback() {
assert_eq!(parse_duration_str("bad"), Duration::from_secs(45));
}
#[test]
fn context_diff_detects_additions() {
let before = HashMap::new();
let mut after = HashMap::new();
after.insert("key".to_string(), serde_json::json!("value"));
let diff = context_diff(&before, &after);
assert_eq!(diff.len(), 1);
assert_eq!(diff.get("key"), Some(&serde_json::json!("value")));
}
#[test]
fn context_diff_detects_changes() {
let mut before = HashMap::new();
before.insert("key".to_string(), serde_json::json!("old"));
let mut after = HashMap::new();
after.insert("key".to_string(), serde_json::json!("new"));
let diff = context_diff(&before, &after);
assert_eq!(diff.len(), 1);
assert_eq!(diff.get("key"), Some(&serde_json::json!("new")));
}
#[test]
fn context_diff_ignores_unchanged() {
let mut before = HashMap::new();
before.insert("key".to_string(), serde_json::json!("same"));
let mut after = HashMap::new();
after.insert("key".to_string(), serde_json::json!("same"));
let diff = context_diff(&before, &after);
assert!(diff.is_empty());
}
#[test]
fn context_diff_ignores_deletions() {
let mut before = HashMap::new();
before.insert("removed".to_string(), serde_json::json!("gone"));
let after = HashMap::new();
let diff = context_diff(&before, &after);
assert!(diff.is_empty());
}
}