godmode-cli 0.8.1

CLI for godmode: task graph management, parallel agent dispatch, session handoff, and release automation.
//! Tests task metadata persistence, defaults, help text, and priority validation.

use std::process::Command;

fn godmode_bin() -> std::path::PathBuf {
    if let Ok(path) = std::env::var("CARGO_BIN_EXE_godmode") {
        return std::path::PathBuf::from(path);
    }
    let manifest = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR"));
    manifest
        .parent()
        .expect("crate parent")
        .parent()
        .expect("workspace root")
        .join("target/debug/godmode")
}

fn run_godmode(dir: &tempfile::TempDir, args: &[&str]) -> std::process::Output {
    Command::new(godmode_bin())
        .args(args)
        .current_dir(dir.path())
        .output()
        .expect("run godmode")
}

#[test]
fn task_add_persists_metadata_and_tag_order_in_yaml_and_json() {
    let dir = tempfile::TempDir::new().expect("tempdir");
    let output = run_godmode(
        &dir,
        &[
            "task",
            "add",
            "Ship metadata",
            "--id",
            "t7",
            "--notes",
            "Preserve this note",
            "--run",
            "cargo nextest run -p godmode-cli",
            "--priority",
            "high",
            "--tag",
            "cli",
            "--tag",
            "metadata",
        ],
    );
    assert!(
        output.status.success(),
        "task add failed: {}",
        String::from_utf8_lossy(&output.stderr)
    );

    let yaml = std::fs::read_to_string(dir.path().join(".ctx/godmode/tasks.yaml"))
        .expect("persisted task graph");
    assert!(yaml.contains("notes: Preserve this note"), "yaml:\n{yaml}");
    assert!(
        yaml.contains("run: cargo nextest run -p godmode-cli"),
        "yaml:\n{yaml}"
    );
    assert!(yaml.contains("priority: high"), "yaml:\n{yaml}");
    let cli_tag = yaml.find("- cli").expect("cli tag in yaml");
    let metadata_tag = yaml.find("- metadata").expect("metadata tag in yaml");
    assert!(cli_tag < metadata_tag, "tag order changed in yaml:\n{yaml}");

    let list = run_godmode(&dir, &["--json", "task", "list"]);
    assert!(list.status.success(), "task list failed");
    let tasks: serde_json::Value = serde_json::from_slice(&list.stdout).expect("task list json");
    let task = &tasks[0];
    assert_eq!(task["notes"], "Preserve this note");
    assert_eq!(task["run"], "cargo nextest run -p godmode-cli");
    assert_eq!(task["priority"], "high");
    assert_eq!(task["tags"], serde_json::json!(["cli", "metadata"]));
}

#[test]
fn task_add_without_metadata_retains_existing_defaults() {
    let dir = tempfile::TempDir::new().expect("tempdir");
    let output = run_godmode(&dir, &["task", "add", "Minimal task", "--id", "t1"]);
    assert!(output.status.success());

    let list = run_godmode(&dir, &["--json", "task", "list"]);
    let tasks: serde_json::Value = serde_json::from_slice(&list.stdout).expect("task list json");
    let task = &tasks[0];
    assert_eq!(task["notes"], "");
    assert!(task.get("run").is_none());
    assert!(task.get("priority").is_none());
    assert!(task.get("tags").is_none());
}

#[test]
fn task_add_help_lists_metadata_options_and_priority_values() {
    let dir = tempfile::TempDir::new().expect("tempdir");
    let output = run_godmode(&dir, &["task", "add", "--help"]);
    assert!(output.status.success());
    let help = String::from_utf8_lossy(&output.stdout);
    for option in ["--notes", "--run", "--priority", "--tag"] {
        assert!(help.contains(option), "missing {option} in help:\n{help}");
    }
    assert!(help.contains("--tag <TAG>"), "help: {help}");
    assert!(help.contains("high"), "help:\n{help}");
    assert!(help.contains("normal"), "help:\n{help}");
    assert!(help.contains("low"), "help:\n{help}");
}

#[test]
fn task_add_rejects_unsupported_priority() {
    let dir = tempfile::TempDir::new().expect("tempdir");
    let output = run_godmode(
        &dir,
        &["task", "add", "Invalid priority", "--priority", "urgent"],
    );
    assert!(!output.status.success());
    let stderr = String::from_utf8_lossy(&output.stderr);
    assert!(stderr.contains("urgent"), "stderr: {stderr}");
}