use devflow_core::agent_result::{
self, AgentStatus, agent_pid_path, evaluate_agent_result, exit_code_path, stdout_path,
};
use devflow_core::config::GitFlowConfig;
use devflow_core::mode::Mode;
use devflow_core::monitor::{spawn_monitor, wait_for_agent_pid};
use devflow_core::stage::Stage;
use devflow_core::state::{AgentKind, State};
use devflow_core::workflow::{WorkflowError, load_state};
use std::path::Path;
use std::process::Command;
use std::time::Duration;
fn git(root: &Path, args: &[&str]) {
let output = Command::new("git")
.args(args)
.current_dir(root)
.output()
.expect("spawn git");
assert!(
output.status.success(),
"git {args:?} failed: {}",
String::from_utf8_lossy(&output.stderr)
);
}
fn init_repo(root: &Path, phase: u32) {
git(root, &["init", "-q"]);
git(root, &["config", "user.email", "devflow@example.com"]);
git(root, &["config", "user.name", "DevFlow Tests"]);
git(root, &["config", "commit.gpgsign", "false"]);
git(root, &["config", "core.hooksPath", "/dev/null"]);
git(root, &["checkout", "-q", "-b", "develop"]);
std::fs::write(root.join("README.md"), "base\n").unwrap();
git(root, &["add", "README.md"]);
git(root, &["commit", "-q", "-m", "base"]);
let branch = format!("feature/phase-{phase:02}");
git(root, &["checkout", "-q", "-b", &branch]);
std::fs::write(root.join("work.txt"), "agent work\n").unwrap();
git(root, &["add", "work.txt"]);
git(root, &["commit", "-q", "-m", "agent work"]);
}
#[test]
fn monitor_owns_fake_agent_and_records_devflow_result() {
let dir = tempfile::tempdir().unwrap();
let root = dir.path();
let phase = 7;
init_repo(root, phase);
let mut state = State::new(phase, AgentKind::Claude, Mode::Auto, root.to_path_buf());
state.stage = Stage::Code;
let args = vec![
"-c".to_string(),
"printf 'work done\\nDEVFLOW_RESULT: {\"status\":\"success\",\"commits\":1}\\n'"
.to_string(),
];
spawn_monitor(&state, "sh", &args, &[]).expect("spawn monitor");
let agent_pid = wait_for_agent_pid(root, phase).expect("agent pid recorded");
assert!(agent_pid > 0);
let exit_path = exit_code_path(root, phase);
let mut exit_seen = false;
for _ in 0..200 {
if exit_path.exists() {
exit_seen = true;
break;
}
std::thread::sleep(Duration::from_millis(20));
}
assert!(exit_seen, "monitor never wrote the exit-code file");
let stdout = std::fs::read_to_string(stdout_path(root, phase)).unwrap();
assert!(
stdout.contains("DEVFLOW_RESULT"),
"captured stdout missing marker: {stdout:?}"
);
let exit = std::fs::read_to_string(&exit_path).unwrap();
assert_eq!(exit.trim(), "0");
assert!(agent_pid_path(root, phase).exists());
let result = evaluate_agent_result(root, &state, &GitFlowConfig::default()).unwrap();
assert_eq!(result.status, AgentStatus::Success);
assert_eq!(
stdout_path(root, phase),
agent_result::stdout_path(root, phase)
);
}
#[test]
fn advance_state_loading_fails_cleanly_for_missing_and_corrupt_state() {
let dir = tempfile::tempdir().unwrap();
let root = dir.path();
assert!(matches!(
load_state(root, 7),
Err(WorkflowError::MissingState(_))
));
std::fs::create_dir_all(root.join(".devflow")).unwrap();
std::fs::write(root.join(".devflow/state-07.json"), "{\"stage\":").unwrap();
assert!(matches!(load_state(root, 7), Err(WorkflowError::Json(_))));
std::fs::write(root.join(".devflow/state.json"), "{\"stage\":").unwrap();
assert!(matches!(
load_state(root, 8),
Err(WorkflowError::MissingState(_))
));
}