use super::*;
use crate::tracker::fixtures::{
binding, checkout, commit, empty_remote, env_at, legacy_remote, local_unpushed,
remote_with_branch, store_clone, tip, BRANCH,
};
use tempfile::TempDir;
#[test]
fn push_keeps_work_local_when_the_remote_tip_is_not_a_store() {
let tmp = TempDir::new().unwrap();
let remote = legacy_remote(tmp.path());
let store = local_unpushed(tmp.path());
let before = tip(&remote, BRANCH);
push(&binding(Some(&remote), &store), &env_at(1)).unwrap();
assert_eq!(tip(&remote, BRANCH), before); }
#[test]
fn push_to_an_unreachable_remote_stays_the_original_error() {
let tmp = TempDir::new().unwrap();
let store = local_unpushed(tmp.path());
let gone = tmp.path().join("no-such-remote.git");
let mut b = binding(Some(&gone), &store);
b.remote = Some(gone.to_string_lossy().into_owned());
let err = push(&b, &env_at(1)).unwrap_err().to_string();
assert!(err.contains("git push"), "{err}");
assert!(!err.contains("bl sync"), "{err}");
}
#[test]
fn push_publishes_the_local_balls_branch_to_the_remote() {
let tmp = TempDir::new().unwrap();
let remote = remote_with_branch(tmp.path());
let store = store_clone(tmp.path(), &remote);
let landed = commit(&store, "landed.txt", "landed");
push(&binding(Some(&remote), &store), &env_at(1)).unwrap();
assert_eq!(tip(&remote, BRANCH), landed);
}
#[test]
fn push_from_a_nested_op_publishes_nothing() {
let tmp = TempDir::new().unwrap();
let remote = remote_with_branch(tmp.path());
let store = store_clone(tmp.path(), &remote);
let before = tip(&remote, BRANCH);
let landed = commit(&store, "landed.txt", "landed");
assert_ne!(landed, before, "the fixture must leave something worth publishing");
push(&binding(Some(&remote), &store), &env_at(2)).unwrap();
assert_eq!(tip(&remote, BRANCH), before, "a nested op must not publish");
}
#[test]
fn push_in_stealth_is_a_no_op() {
let tmp = TempDir::new().unwrap();
let remote = empty_remote(tmp.path());
let store = local_unpushed(tmp.path());
push(&binding(None, &store), &env_at(1)).unwrap();
assert!(git(&remote, &["rev-parse", BRANCH]).is_err());
}
#[test]
fn push_fails_when_the_remote_rejects_a_non_fast_forward() {
let tmp = TempDir::new().unwrap();
let remote = remote_with_branch(tmp.path());
let store = store_clone(tmp.path(), &remote);
let other = checkout(tmp.path(), &remote, "other");
commit(&other, "remote.txt", "remote");
git(&other, &["push", "-q", "origin", BRANCH]).unwrap();
commit(&store, "local.txt", "local");
let err = push(&binding(Some(&remote), &store), &env_at(1)).unwrap_err().to_string();
assert!(err.contains("push rejected: the remote store moved ahead"), "{err}");
assert!(err.contains("run `bl sync`"), "{err}");
assert!(err.contains("or refuses and names what this store holds"), "{err}");
assert!(err.contains("then re-run the command"), "{err}");
}
#[test]
fn the_recovery_e5_advertises_exits_a_sealed_but_unpublished_store() {
let tmp = TempDir::new().unwrap();
let remote = remote_with_branch(tmp.path());
let store = store_clone(tmp.path(), &remote);
let stranded = commit(&store, "sealed.txt", "sealed, never published");
let other = checkout(tmp.path(), &remote, "other");
commit(&other, "remote.txt", "remote");
git(&other, &["push", "-q", "origin", BRANCH]).unwrap();
let e5 = push(&binding(Some(&remote), &store), &env_at(1)).unwrap_err().to_string();
assert!(e5.contains("or refuses and names what this store holds"), "{e5}");
let refusal = sync(&binding(Some(&remote), &store)).unwrap_err().to_string();
let listing = format!("git -C {} log FETCH_HEAD..{BRANCH}", store.display());
assert!(refusal.contains(&listing), "{refusal}");
assert!(refusal.contains("rebase them onto FETCH_HEAD and push"), "{refusal}");
assert!(refusal.contains("reset --hard"), "{refusal}");
let unpublished = git(&store, &["log", "--format=%H", &format!("FETCH_HEAD..{BRANCH}")]).unwrap();
assert_eq!(unpublished, stranded);
git(&store, &["rebase", "FETCH_HEAD"]).unwrap();
push(&binding(Some(&remote), &store), &env_at(1)).unwrap();
sync(&binding(Some(&remote), &store)).unwrap(); }