use super::tests::{project, tip};
use super::*;
use crate::delivery::Repo;
use std::fs;
#[test]
fn deliver_captures_pending_work_and_squashes_it_onto_integration() {
let (tmp, root, p) = project();
let wt = tmp.path().join("wt");
p.materialize(&wt, "work/bl-x").unwrap();
fs::write(wt.join("feature.txt"), "shipped\n").unwrap();
p.deliver(&wt, "work/bl-x", "main", "Add feature [bl-x]", "[bl-x]").unwrap();
assert_eq!(tip(&root), "Add feature [bl-x]");
assert_eq!(Project::run(&root, &["show", "main:feature.txt"]).unwrap(), "shipped\n");
assert_eq!(Project::run(&root, &["rev-list", "--count", "main"]).unwrap().trim(), "2");
}
#[test]
fn deliver_with_no_pending_work_but_a_committed_branch_still_squashes() {
let (tmp, root, p) = project();
let wt = tmp.path().join("wt");
p.materialize(&wt, "work/bl-x").unwrap();
fs::write(wt.join("f.txt"), "x\n").unwrap();
Project::run(&wt, &["add", "-A"]).unwrap();
Project::run(&wt, &["commit", "-q", "-m", "wip"]).unwrap();
p.deliver(&wt, "work/bl-x", "main", "Land it [bl-x]", "[bl-x]").unwrap();
assert_eq!(tip(&root), "Land it [bl-x]");
}
#[test]
fn a_message_past_the_argv_limit_delivers_via_stdin_and_never_labels_the_capture() {
let (tmp, root, p) = project();
let wt = tmp.path().join("wt");
p.materialize(&wt, "work/bl-x").unwrap();
fs::write(wt.join("feature.txt"), "shipped\n").unwrap(); let message = format!("Add feature [bl-x]\n\n{}", "context line\n".repeat(20_000));
p.deliver(&wt, "work/bl-x", "main", &message, "[bl-x]").unwrap();
let landed = Project::run(&root, &["log", "-1", "--format=%B", "main"]).unwrap();
assert_eq!(landed.trim_end(), message.trim_end());
assert_eq!(tip(&root), "Add feature [bl-x]");
let reflog = Project::run(&root, &["reflog", "show", "--format=%gs", "-1", "main"]).unwrap();
assert_eq!(reflog.trim(), "Add feature [bl-x]");
let capture = Project::run(&root, &["log", "--no-merges", "-1", "--format=%B", "work/bl-x"]).unwrap();
assert_eq!(capture.trim_end(), "Add feature [bl-x]");
}
#[test]
fn deliver_is_a_no_op_for_an_empty_deliverable() {
let (tmp, root, p) = project();
let wt = tmp.path().join("wt");
p.materialize(&wt, "work/bl-x").unwrap();
p.deliver(&wt, "work/bl-x", "main", "nothing [bl-x]", "[bl-x]").unwrap();
assert_eq!(tip(&root), "seed"); }
#[test]
fn deliver_is_a_no_op_when_the_branch_was_never_made() {
let (tmp, root, p) = project();
let wt = tmp.path().join("wt"); p.deliver(&wt, "work/bl-z", "main", "nothing [bl-z]", "[bl-z]").unwrap();
assert_eq!(tip(&root), "seed");
}
#[test]
fn the_fold_consumes_the_pinned_sha_not_the_integration_ref() {
let (tmp, root, p) = project();
let wt = tmp.path().join("wt");
p.materialize(&wt, "work/bl-x").unwrap();
fs::write(wt.join("feature.txt"), "shipped\n").unwrap();
Project::run(&wt, &["add", "-A"]).unwrap();
Project::run(&wt, &["commit", "-qm", "work"]).unwrap();
fs::write(root.join("late.txt"), "landed meanwhile\n").unwrap();
Project::run(&root, &["add", "-A"]).unwrap();
Project::run(&root, &["commit", "-qm", "late main edit"]).unwrap();
let pinned = Project::run(&root, &["rev-parse", "main"]).unwrap().trim().to_string();
p.deliver(&wt, "work/bl-x", "main", "Add feature [bl-x]", "[bl-x]").unwrap();
let fold = Project::run(&root, &["log", "-1", "--format=%s", "work/bl-x"]).unwrap();
assert_eq!(fold.trim(), format!("Merge commit '{pinned}' into work/bl-x"));
assert_eq!(tip(&root), "Add feature [bl-x]");
}
#[test]
fn deliver_surfaces_a_conflict_as_an_error() {
let (tmp, root, p) = project();
let wt = tmp.path().join("wt");
p.materialize(&wt, "work/bl-x").unwrap();
fs::write(wt.join("seed.txt"), "from work\n").unwrap();
Project::run(&wt, &["commit", "-qam", "work edit"]).unwrap();
fs::write(root.join("seed.txt"), "from main\n").unwrap();
Project::run(&root, &["commit", "-qam", "main edit"]).unwrap();
let pinned = Project::run(&root, &["rev-parse", "main"]).unwrap().trim().to_string();
let err = p.deliver(&wt, "work/bl-x", "main", "clash [bl-x]", "[bl-x]").unwrap_err();
let msg = err.to_string();
assert!(msg.starts_with(&format!("delivery conflict merging main (pinned at {pinned}) into the work branch: ")), "{msg}");
assert!(!Project::ok(&wt, &["rev-parse", "--verify", "--quiet", "MERGE_HEAD"]).unwrap());
assert!(Project::ok(&wt, &["diff", "--quiet", "HEAD"]).unwrap());
}
#[test]
fn deliver_skips_when_this_incarnations_delivery_already_landed() {
let (tmp, root, p) = project();
let wt = tmp.path().join("wt");
p.materialize(&wt, "work/bl-x").unwrap();
fs::write(wt.join("feature.txt"), "shipped\n").unwrap();
Project::run(&wt, &["add", "-A"]).unwrap();
Project::run(&wt, &["commit", "-qm", "wip"]).unwrap();
p.deliver(&wt, "work/bl-x", "main", "Add feature [bl-x]", "[bl-x]").unwrap();
fs::write(root.join("other.txt"), "other\n").unwrap();
Project::run(&root, &["add", "-A"]).unwrap();
Project::run(&root, &["commit", "-qm", "concurrent work"]).unwrap();
p.deliver(&wt, "work/bl-x", "main", "Add feature [bl-x]", "[bl-x]").unwrap();
assert_eq!(p.marked("main", "[bl-x]").unwrap().len(), 1); assert_eq!(tip(&root), "concurrent work"); }
#[test]
fn deliver_skips_a_branch_already_fully_merged_into_integration() {
let (tmp, root, p) = project();
let wt = tmp.path().join("wt");
p.materialize(&wt, "work/bl-x").unwrap();
fs::write(wt.join("feature.txt"), "shipped\n").unwrap();
Project::run(&wt, &["add", "-A"]).unwrap();
Project::run(&wt, &["commit", "-qm", "Add feature [bl-x]"]).unwrap();
Project::run(&root, &["merge", "-q", "--ff-only", "work/bl-x"]).unwrap(); fs::write(root.join("other.txt"), "other\n").unwrap();
Project::run(&root, &["add", "-A"]).unwrap();
Project::run(&root, &["commit", "-qm", "concurrent work"]).unwrap();
p.deliver(&wt, "work/bl-x", "main", "Add feature [bl-x]", "[bl-x]").unwrap();
assert_eq!(p.marked("main", "[bl-x]").unwrap().len(), 1);
assert_eq!(tip(&root), "concurrent work");
}
#[test]
fn commit_swap_aborts_when_integration_moved_under_the_delivery() {
let (_tmp, root, _p) = project();
let stale = Project::run(&root, &["rev-parse", "main"]).unwrap().trim().to_string(); fs::write(root.join("other.txt"), "x\n").unwrap();
Project::run(&root, &["add", "-A"]).unwrap();
Project::run(&root, &["commit", "-qm", "concurrent"]).unwrap(); let tip_sha = Project::run(&root, &["rev-parse", "main"]).unwrap().trim().to_string();
let err = super::acts::commit_swap(&root, "main", "late [bl-x]", &tip_sha, &stale).unwrap_err();
assert!(err.to_string().contains("moved under the delivery"));
assert_eq!(tip(&root), "concurrent");
}
#[test]
fn marked_returns_the_marked_commits_newest_first() {
let (tmp, root, p) = project();
let wt = tmp.path().join("wt");
p.materialize(&wt, "work/bl-x").unwrap();
fs::write(wt.join("a.txt"), "1\n").unwrap();
p.deliver(&wt, "work/bl-x", "main", "first [bl-x]", "[bl-x]").unwrap();
p.discard(&wt, "work/bl-x").unwrap();
p.materialize(&wt, "work/bl-x").unwrap();
fs::write(wt.join("b.txt"), "2\n").unwrap();
p.deliver(&wt, "work/bl-x", "main", "second [bl-x]", "[bl-x]").unwrap();
let shas = p.marked("main", "[bl-x]").unwrap();
let subject = |sha: &str| Project::run(&root, &["log", "-1", "--format=%s", sha]).unwrap().trim().to_string();
assert_eq!(shas.iter().map(|s| subject(s)).collect::<Vec<_>>(), ["second [bl-x]", "first [bl-x]"]);
assert!(p.marked("main", "[bl-zzzz]").unwrap().is_empty());
}