#![allow(
clippy::expect_used,
clippy::unwrap_used,
clippy::tests_outside_test_module,
reason = "integration test: fail-fast unwrap/expect are idiomatic, and test fns live at crate root by construction"
)]
use std::path::Path;
use std::process::{Command, Output};
const BIN: &str = env!("CARGO_BIN_EXE_doctrine");
const COORD_BRANCH: &str = "dispatch/064";
fn git(dir: &Path, args: &[&str]) -> String {
let out = Command::new("git")
.arg("-C")
.arg(dir)
.args(args)
.output()
.expect("spawn git");
assert!(
out.status.success(),
"git {args:?} failed: {}",
String::from_utf8_lossy(&out.stderr)
);
String::from_utf8_lossy(&out.stdout).trim().to_string()
}
fn ref_exists(dir: &Path, refname: &str) -> bool {
Command::new("git")
.arg("-C")
.arg(dir)
.args(["rev-parse", "--verify", "--quiet", refname])
.output()
.expect("spawn git")
.status
.success()
}
fn run(cwd: &Path, args: &[&str]) -> Output {
Command::new(BIN)
.args(args)
.current_dir(cwd)
.env_remove("DOCTRINE_WORKER")
.output()
.expect("spawn doctrine")
}
fn stderr(out: &Output) -> String {
String::from_utf8_lossy(&out.stderr).into_owned()
}
fn commit(dir: &Path, path: &str, content: &str, msg: &str) -> String {
let full = dir.join(path);
std::fs::create_dir_all(full.parent().unwrap()).unwrap();
std::fs::write(&full, content).unwrap();
git(dir, &["add", path]);
git(dir, &["commit", "-q", "-m", msg]);
git(dir, &["rev-parse", "HEAD"])
}
fn init_repo(dir: &Path) {
std::fs::create_dir_all(dir).unwrap();
git(dir, &["init", "-q", "-b", "main"]);
git(dir, &["config", "user.email", "t@example.com"]);
git(dir, &["config", "user.name", "Test"]);
std::fs::write(dir.join("root-sentinel.txt"), "untouched").unwrap();
let plan = ".doctrine/slice/064/plan.toml";
let full = dir.join(plan);
std::fs::create_dir_all(full.parent().unwrap()).unwrap();
std::fs::write(
&full,
"schema = \"doctrine.plan.overview\"\n\n[[phase]]\nid = \"PHASE-01\"\nname = \"fixture\"\nobjective = \"fixture\"\n",
)
.unwrap();
git(dir, &["add", "-A"]);
git(dir, &["commit", "-q", "-m", "base + plan fixture"]);
}
#[test]
fn full_lifecycle_coordinate_to_integrate_preserves_main_and_deliverables() {
let src = tempfile::tempdir().unwrap();
let root = src.path();
init_repo(root);
let trunk = git(root, &["rev-parse", "HEAD"]);
let holder = tempfile::tempdir().unwrap();
let coord = holder.path().join("coord");
let out = run(
root,
&[
"worktree",
"coordinate",
"--slice",
"64",
"--dir",
coord.to_str().unwrap(),
],
);
assert!(out.status.success(), "coordinate; stderr: {}", stderr(&out));
assert_eq!(
git(&coord, &["rev-parse", "HEAD"]),
trunk,
"coord worktree forks off the resolved trunk"
);
let code_tip = commit(&coord, "src/feature.rs", "fn f() {}\n", "PHASE-01 code");
let out = run(
&coord,
&[
"dispatch",
"record-boundary",
"--slice",
"64",
"--phase",
"PHASE-01",
"--code-start",
&trunk,
"--code-end",
&code_tip,
"-p",
coord.to_str().unwrap(),
],
);
assert!(
out.status.success(),
"record-boundary; stderr: {}",
stderr(&out)
);
git(&coord, &["add", ".doctrine/dispatch/064"]);
git(&coord, &["commit", "-q", "-m", "PHASE-01 boundary ledger"]);
let out = run(
&coord,
&[
"dispatch",
"sync",
"--prepare-review",
"--slice",
"64",
"-p",
coord.to_str().unwrap(),
],
);
assert!(
out.status.success(),
"prepare-review; stderr: {}",
stderr(&out)
);
assert!(
ref_exists(root, "review/064"),
"review/064 left for audit (visible from the shared common dir)"
);
assert!(
ref_exists(root, "phase/064-01"),
"phase/064-01 synthesized cut left for audit"
);
assert_eq!(
git(root, &["rev-parse", "main"]),
trunk,
"trunk ref unmoved by the run (advance is integrate's act, not the run's)"
);
assert_eq!(
git(root, &["status", "--porcelain"]),
"",
"session `main` working tree is byte-clean — orchestrator wrote the coord tree, not root"
);
assert_eq!(
std::fs::read_to_string(root.join("root-sentinel.txt")).unwrap(),
"untouched",
"root working file untouched"
);
let phase_tip = git(root, &["rev-parse", "phase/064-01"]);
let integrate = |extra: &[&str]| {
let mut args = vec![
"dispatch",
"sync",
"--integrate",
"--slice",
"64",
"-p",
coord.to_str().unwrap(),
];
args.extend_from_slice(extra);
run(&coord, &args)
};
let out = integrate(&["--trunk", "refs/heads/main"]);
assert!(out.status.success(), "integrate; stderr: {}", stderr(&out));
assert_eq!(
git(root, &["rev-parse", "main"]),
phase_tip,
"trunk fast-forwards to the cumulative code tip at integration"
);
let out = integrate(&["--trunk", "refs/heads/main"]);
assert!(
out.status.success(),
"idempotent re-integrate; stderr: {}",
stderr(&out)
);
assert_eq!(
git(root, &["rev-parse", "main"]),
phase_tip,
"no second advance — replay no-ops when current == planned"
);
git(
root,
&["worktree", "remove", "--force", coord.to_str().unwrap()],
);
assert!(
!coord.exists(),
"coordination worktree directory removed at conclude"
);
assert!(
ref_exists(root, COORD_BRANCH),
"dispatch/064 deliverable preserved past worktree removal"
);
assert!(
ref_exists(root, "phase/064-01"),
"phase/064-01 deliverable preserved past worktree removal"
);
assert!(
ref_exists(root, "review/064"),
"review/064 stays reviewable after the worktree is gone"
);
}