use super::*;
use crate::delivery::{Repo, Spec};
use crate::delivery_repo::tests::{project, tip};
use crate::delivery_repo::Project;
use std::fs;
#[test]
fn compose_is_the_bare_tagged_subject_with_no_narration() {
assert_eq!(compose(None, &[], "Title [bl-x]"), "Title [bl-x]");
let blank = vec![" \n ".to_string()];
assert_eq!(compose(None, &blank, "Title [bl-x]"), "Title [bl-x]");
}
#[test]
fn compose_puts_work_messages_in_the_body_under_the_tagged_subject() {
let work = vec!["first\n\nbody".to_string(), "second".to_string()];
assert_eq!(compose(None, &work, "Title [bl-x]"), "Title [bl-x]\n\nfirst\n\nbody\n\nsecond");
}
#[test]
fn compose_leads_the_body_with_the_m_narration_then_keeps_the_work_context() {
let work = vec!["work rationale".to_string()];
assert_eq!(compose(Some("Do it"), &work, "T [bl-x]"), "T [bl-x]\n\nDo it\n\nwork rationale");
assert_eq!(compose(Some(" "), &work, "T [bl-x]"), "T [bl-x]\n\nwork rationale");
}
#[test]
fn compose_never_lets_narration_displace_or_duplicate_the_subject_tag() {
assert_eq!(compose(Some("A para\n\nsecond para"), &[], "T [bl-x]"), "T [bl-x]\n\nA para\n\nsecond para");
assert_eq!(compose(Some("Done [bl-x] already"), &[], "T [bl-x]"), "T [bl-x]\n\nDone [bl-x] already");
}
#[test]
fn compose_drops_a_part_that_is_just_the_subject_so_retries_do_not_compound() {
let work = vec!["Title [bl-x]".to_string(), "real work".to_string(), "Title [bl-x]".to_string()];
assert_eq!(compose(None, &work, "Title [bl-x]"), "Title [bl-x]\n\nreal work");
assert_eq!(compose(None, &work[..1], "Title [bl-x]"), "Title [bl-x]");
}
#[test]
fn compose_caps_the_body_and_says_how_many_it_dropped() {
let big = "x".repeat(BODY_CAP / 2 - 2); let work = vec![big.clone(), big.clone(), big.clone()];
let out = compose(None, &work, "T [bl-x]");
assert_eq!(out, format!("T [bl-x]\n\n{big}\n\n{big}\n\nand 1 more work commit message(s), over the {BODY_CAP}-byte body budget"));
let huge = vec!["y".repeat(BODY_CAP + 1)];
assert_eq!(
compose(None, &huge, "T [bl-x]"),
format!("T [bl-x]\n\nand 1 more work commit message(s), over the {BODY_CAP}-byte body budget")
);
}
#[test]
fn subject_line_is_the_first_line_of_any_message() {
assert_eq!(subject_line("T [bl-x]\n\nbody\nmore"), "T [bl-x]");
assert_eq!(subject_line("T [bl-x]"), "T [bl-x]");
assert_eq!(subject_line(""), ""); }
#[test]
fn work_messages_is_empty_for_a_branch_never_made() {
let (_tmp, _root, p) = project();
assert!(p.work_messages("work/bl-absent", "main").unwrap().is_empty());
}
#[test]
fn work_messages_lists_authored_commits_oldest_first_skipping_merges() {
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();
Project::run(&wt, &["add", "-A"]).unwrap();
Project::run(&wt, &["commit", "-q", "-m", "first commit\n\nbody one"]).unwrap();
fs::write(wt.join("b.txt"), "2\n").unwrap();
Project::run(&wt, &["add", "-A"]).unwrap();
Project::run(&wt, &["commit", "-q", "-m", "second commit"]).unwrap();
fs::write(root.join("c.txt"), "3\n").unwrap();
Project::run(&root, &["add", "-A"]).unwrap();
Project::run(&root, &["commit", "-q", "-m", "main moved"]).unwrap();
Project::run(&wt, &["merge", "--no-edit", "main"]).unwrap();
let got: Vec<String> =
p.work_messages("work/bl-x", "main").unwrap().iter().map(|m| m.trim().to_string()).filter(|m| !m.is_empty()).collect();
assert_eq!(got, ["first commit\n\nbody one", "second commit"]);
}
#[test]
fn deliver_close_carries_the_authors_rich_work_body_to_main() {
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();
let rich = "Fix the squash message\n\nThe delivery commit used to be only the ball\ntitle; now it carries the body.";
Project::run(&wt, &["commit", "-q", "-m", rich]).unwrap();
let spec = Spec {
worktree: &wt,
branch: "work/bl-x",
subject: "ball title [bl-x]",
override_msg: None,
marker: "[bl-x]",
target: None,
};
deliver_close(&p, &spec).unwrap();
let body = Project::run(&root, &["log", "-1", "--format=%B", "main"]).unwrap();
assert_eq!(body.trim(), "ball title [bl-x]\n\nFix the squash message\n\nThe delivery commit used to be only the ball\ntitle; now it carries the body.");
assert_eq!(tip(&root), "ball title [bl-x]");
}
#[test]
fn deliver_close_builds_the_body_from_branch_own_commits_across_a_folded_merge() {
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", "authored one"]).unwrap();
fs::write(root.join("g.txt"), "y\n").unwrap();
Project::run(&root, &["add", "-A"]).unwrap();
Project::run(&root, &["commit", "-q", "-m", "upstream noise"]).unwrap();
Project::run(&wt, &["merge", "--no-edit", "main"]).unwrap();
fs::write(wt.join("f.txt"), "xx\n").unwrap();
Project::run(&wt, &["commit", "-qam", "authored two"]).unwrap();
let spec = Spec {
worktree: &wt,
branch: "work/bl-x",
subject: "ball title [bl-x]",
override_msg: None,
marker: "[bl-x]",
target: None,
};
deliver_close(&p, &spec).unwrap();
let body = Project::run(&root, &["log", "-1", "--format=%B", "main"]).unwrap();
assert_eq!(body.trim(), "ball title [bl-x]\n\nauthored one\n\nauthored two");
}
#[test]
fn deliver_close_keeps_the_m_narration_and_the_work_body_under_the_tagged_subject() {
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", "work body kept"]).unwrap();
let spec = Spec {
worktree: &wt,
branch: "work/bl-x",
subject: "ball title [bl-x]",
override_msg: Some("Close note\n\nthe full narration"),
marker: "[bl-x]",
target: None,
};
deliver_close(&p, &spec).unwrap();
let body = Project::run(&root, &["log", "-1", "--format=%B", "main"]).unwrap();
assert_eq!(body.trim(), "ball title [bl-x]\n\nClose note\n\nthe full narration\n\nwork body kept");
assert_eq!(tip(&root), "ball title [bl-x]");
}