use assert_cmd::Command;
use o7::state::types::{
CallStackEntry, HarnessEvent, HarnessEventKind, HarnessEventStream, PersistedRunState,
RunStatus,
};
use predicates::prelude::*;
use std::fs;
use std::path::PathBuf;
use tempfile::TempDir;
fn write_test_workflow(dir: &TempDir) -> PathBuf {
let file = dir.path().join("test.7");
fs::write(
&file,
"version 1\nworkflow main\n exec\n harness: h\n prompt: \"hi\"\n",
)
.unwrap();
file
}
#[test]
fn test_version_flag() {
Command::cargo_bin("o7")
.unwrap()
.arg("--version")
.assert()
.success()
.stdout(predicate::str::contains("o7"));
}
#[test]
fn test_help_flag() {
Command::cargo_bin("o7")
.unwrap()
.arg("--help")
.assert()
.success()
.stdout(predicate::str::contains("workflow DSL runner"));
}
#[test]
fn test_help_run_subcommand() {
Command::cargo_bin("o7")
.unwrap()
.args(["run", "--help"])
.assert()
.success()
.stdout(predicate::str::contains("Run a workflow"));
}
#[test]
fn test_lint_valid_file() {
let dir = TempDir::new().unwrap();
let file = write_test_workflow(&dir);
Command::cargo_bin("o7")
.unwrap()
.args(["lint", file.to_str().unwrap()])
.assert()
.success()
.stdout(predicate::str::contains("No errors found"));
}
#[test]
fn test_lint_invalid_file() {
let dir = TempDir::new().unwrap();
let file = dir.path().join("test.7");
fs::write(&file, "workflow main\n").unwrap();
Command::cargo_bin("o7")
.unwrap()
.args(["lint", file.to_str().unwrap()])
.assert()
.failure();
}
#[test]
fn test_lint_nonexistent_file() {
Command::cargo_bin("o7")
.unwrap()
.args(["lint", "/nonexistent/test.7"])
.assert()
.failure()
.stderr(predicate::str::contains("file not found"));
}
#[test]
fn test_parse_valid_file() {
let dir = TempDir::new().unwrap();
let file = write_test_workflow(&dir);
Command::cargo_bin("o7")
.unwrap()
.args(["parse", file.to_str().unwrap()])
.assert()
.success()
.stdout(predicate::str::contains("OK"));
}
#[test]
fn test_parse_valid_file_shows_workflow_names() {
let dir = TempDir::new().unwrap();
let file = dir.path().join("test.7");
fs::write(
&file,
"version 1\nworkflow main\n exec\n harness: h\n prompt: \"hi\"\nworkflow deploy\n exec\n harness: h\n prompt: \"go\"\n",
).unwrap();
Command::cargo_bin("o7")
.unwrap()
.args(["parse", file.to_str().unwrap()])
.assert()
.success()
.stdout(predicate::str::contains("2 workflow(s)"))
.stdout(predicate::str::contains("main"))
.stdout(predicate::str::contains("deploy"));
}
#[test]
fn test_parse_invalid_file() {
let dir = TempDir::new().unwrap();
let file = dir.path().join("test.7");
fs::write(&file, "workflow main\n").unwrap();
Command::cargo_bin("o7")
.unwrap()
.args(["parse", file.to_str().unwrap()])
.assert()
.failure()
.stderr(predicate::str::contains("Parse errors"));
}
#[test]
fn test_tui_fails_without_terminal() {
let dir = TempDir::new().unwrap();
let file = write_test_workflow(&dir);
Command::cargo_bin("o7")
.unwrap()
.args(["tui", file.to_str().unwrap()])
.assert()
.failure()
.stderr(predicate::str::contains("TUI error:"));
}
#[test]
fn test_inspect_no_runs() {
let dir = TempDir::new().unwrap();
Command::cargo_bin("o7")
.unwrap()
.args(["inspect", "--project", dir.path().to_str().unwrap()])
.assert()
.success()
.stdout(predicate::str::contains("No runs found"));
}
#[test]
fn test_inspect_nonexistent_run() {
let dir = TempDir::new().unwrap();
Command::cargo_bin("o7")
.unwrap()
.args([
"inspect",
"nonexistent-run",
"--project",
dir.path().to_str().unwrap(),
])
.assert()
.failure()
.stderr(predicate::str::contains("Run not found"));
}
#[test]
fn test_inspect_shows_harness_event_summary() {
let dir = TempDir::new().unwrap();
let state = PersistedRunState {
schema_version: 1,
run_id: "run-harness-inspect".to_string(),
timestamp: "2026-04-10T00:00:00Z".to_string(),
root_workflow: "main".to_string(),
status: RunStatus::Paused,
call_stack: vec![CallStackEntry {
workflow: "main".to_string(),
step_index: 0,
}],
steps: std::collections::HashMap::new(),
branches: None,
collected_outputs: None,
harness_event_log: Some(vec![HarnessEvent {
sequence: 0,
exec_ordinal: 0,
stream: HarnessEventStream::Stdout,
kind: HarnessEventKind::Json,
raw: "{\"type\":\"message\"}".to_string(),
parsed: Some(serde_json::json!({"type": "message"})),
step_path: Some(vec!["main".to_string(), "exec:mock".to_string()]),
boundary_index: Some(0),
timestamp: None,
}]),
safe_boundaries: Vec::new(),
current_boundary_index: 0,
event_log: Some(Vec::new()),
};
o7::state::persistence::save_state(&state, dir.path().to_str().unwrap()).unwrap();
Command::cargo_bin("o7")
.unwrap()
.args([
"inspect",
"run-harness-inspect",
"--project",
dir.path().to_str().unwrap(),
])
.assert()
.success()
.stdout(predicate::str::contains("Harness events: 1"))
.stdout(predicate::str::contains(
"[stdout/json] #0 main > exec:mock message",
));
}
#[test]
fn test_no_args_shows_help() {
Command::cargo_bin("o7")
.unwrap()
.assert()
.success()
.stdout(predicate::str::contains("Usage"));
}
#[test]
fn test_wiz_appears_in_main_help() {
Command::cargo_bin("o7")
.unwrap()
.arg("--help")
.assert()
.success()
.stdout(predicate::str::contains("wiz"));
}
#[test]
fn test_wiz_subcommand_help() {
Command::cargo_bin("o7")
.unwrap()
.args(["wiz", "--help"])
.assert()
.success()
.stdout(predicate::str::contains("Interactively configure"));
}
#[test]
fn test_run_no_args_no_o7md_fails_with_helpful_error() {
let dir = TempDir::new().unwrap();
Command::cargo_bin("o7")
.unwrap()
.current_dir(dir.path())
.arg("run")
.assert()
.failure()
.stderr(predicate::str::contains("o7.md"));
}
#[test]
fn test_run_no_args_finds_o7md_in_cwd() {
let dir = TempDir::new().unwrap();
fs::write(
dir.path().join("o7.md"),
"version 1\nworkflow main\n exec\n harness: h\n prompt: \"hi\"\n",
)
.unwrap();
Command::cargo_bin("o7")
.unwrap()
.current_dir(dir.path())
.arg("run")
.assert()
.failure()
.stderr(predicate::str::contains("no o7.md found").not())
.stderr(predicate::str::contains("no file specified").not());
}
#[test]
fn test_tui_no_args_no_o7md_fails_with_helpful_error() {
let dir = TempDir::new().unwrap();
Command::cargo_bin("o7")
.unwrap()
.current_dir(dir.path())
.arg("tui")
.assert()
.failure()
.stderr(predicate::str::contains("o7.md"));
}