huskies: merge 675_bug_mergemaster_silently_exits_when_feature_branch_has_zero_commits_ahead_of_master

This commit is contained in:
dave
2026-04-27 14:38:53 +00:00
parent ea872fa01c
commit 6a582d73b6
2 changed files with 206 additions and 8 deletions
+161
View File
@@ -614,4 +614,165 @@ mod tests {
MergeJobStatus::Running => panic!("should not still be running"),
}
}
// ── bug 675: zero commits ahead must fail with "no commits to merge" ─────
/// Regression test for bug 675: when the feature branch has zero commits
/// ahead of master the pipeline must fail with a clear "no commits to merge"
/// error and the story must remain in `4_merge` (not advance to `5_done`).
#[tokio::test]
async fn merge_agent_work_zero_commits_ahead_stays_in_merge_stage() {
use std::fs;
use tempfile::tempdir;
let tmp = tempdir().unwrap();
let repo = tmp.path();
init_git_repo(repo);
// Feature branch is created at the same commit as master — zero commits ahead.
Command::new("git")
.args(["checkout", "-b", "feature/story-675_zero_commits"])
.current_dir(repo)
.output()
.unwrap();
Command::new("git")
.args(["checkout", "master"])
.current_dir(repo)
.output()
.unwrap();
// Place the story file in 4_merge so we can verify it stays there.
let merge_dir = repo.join(".huskies/work/4_merge");
fs::create_dir_all(&merge_dir).unwrap();
fs::write(
merge_dir.join("675_zero_commits.md"),
"---\nname: Zero commits test\n---\n",
)
.unwrap();
Command::new("git")
.args(["add", "."])
.current_dir(repo)
.output()
.unwrap();
Command::new("git")
.args(["commit", "-m", "place story in 4_merge"])
.current_dir(repo)
.output()
.unwrap();
let pool = Arc::new(AgentPool::new_test(3001));
let job = run_merge_to_completion(&pool, repo, "675_zero_commits").await;
// The job must have failed with a "no commits to merge" error.
match &job.status {
MergeJobStatus::Failed(e) => {
assert!(
e.contains("no commits to merge"),
"error must contain 'no commits to merge', got: {e}"
);
assert!(
e.contains("675_zero_commits"),
"error must name the story_id, got: {e}"
);
}
MergeJobStatus::Completed(report) => {
panic!(
"expected Failed status, got Completed with success={}: {}",
report.success, report.gate_output
);
}
MergeJobStatus::Running => panic!("should not still be running"),
}
// Story file must still be in 4_merge — NOT advanced to 5_done.
assert!(
merge_dir.join("675_zero_commits.md").exists(),
"story file must remain in 4_merge when merge fails"
);
assert!(
!repo
.join(".huskies/work/5_done/675_zero_commits.md")
.exists(),
"story must NOT advance to 5_done when merge fails with no commits"
);
}
/// Non-regression test for bug 675: a feature branch with exactly one commit
/// ahead of master must continue to merge successfully (happy path).
#[tokio::test]
async fn merge_agent_work_one_commit_ahead_merges_successfully() {
use std::fs;
use tempfile::tempdir;
let tmp = tempdir().unwrap();
let repo = tmp.path();
init_git_repo(repo);
// Feature branch: one commit ahead of master.
Command::new("git")
.args(["checkout", "-b", "feature/story-675_one_commit"])
.current_dir(repo)
.output()
.unwrap();
fs::write(repo.join("feature_675.txt"), "feature content\n").unwrap();
Command::new("git")
.args(["add", "."])
.current_dir(repo)
.output()
.unwrap();
Command::new("git")
.args(["commit", "-m", "add feature file"])
.current_dir(repo)
.output()
.unwrap();
Command::new("git")
.args(["checkout", "master"])
.current_dir(repo)
.output()
.unwrap();
// Place the story file in 4_merge.
let merge_dir = repo.join(".huskies/work/4_merge");
fs::create_dir_all(&merge_dir).unwrap();
fs::write(
merge_dir.join("675_one_commit.md"),
"---\nname: One commit test\n---\n",
)
.unwrap();
Command::new("git")
.args(["add", "."])
.current_dir(repo)
.output()
.unwrap();
Command::new("git")
.args(["commit", "-m", "place story in 4_merge"])
.current_dir(repo)
.output()
.unwrap();
let pool = Arc::new(AgentPool::new_test(3001));
let job = run_merge_to_completion(&pool, repo, "675_one_commit").await;
// The merge must not fail with "no commits to merge".
match &job.status {
MergeJobStatus::Failed(e) => {
assert!(
!e.contains("no commits to merge"),
"one-commit-ahead branch must NOT fail with 'no commits to merge': {e}"
);
// Gate failures (no script/test) are acceptable in test env.
}
MergeJobStatus::Completed(report) => {
// Success or gate failure — both acceptable; the key invariant is
// that we didn't fail with the zero-commits early-exit.
assert!(
report.success || !report.gates_passed,
"unexpected state: success={} gates_passed={}",
report.success,
report.gates_passed
);
}
MergeJobStatus::Running => panic!("should not still be running"),
}
}
}