use super::commit::remove_settled;
use super::lfs_pre_push::{World, commit_directly, ref_of, world};
use super::support::{Fixture, Observed, fixture, git, observe_raw, stderr_of, wrapped};
use git_policy_cli::event_hints::{engine_failure_hint, manual_push_failure_hint};
use git_policy_cli::manual_push_cause::ManualPushCause;
fn wrapped_with_hints(fixture: &Fixture, directory: &std::path::Path, arguments: &[&str]) -> Observed {
return observe_raw(wrapped(fixture).current_dir(directory).args(arguments), b"");
}
fn policy_world(fixture: &Fixture) -> World {
let world: World = world(fixture, false, None);
commit_directly(fixture, world.repo.as_path(), "cli-git.config.jsonc", "{ \"policies\": { \"final-newline\": \"error\" } }\n");
git(fixture, world.repo.as_path(), &["push", "--quiet", "--no-verify", "origin", "main"]);
return world;
}
fn hint_field(cause: ManualPushCause) -> Option<String> {
return manual_push_failure_hint(cause).map(|hint| { return format!("\"hint\":\"{hint}\""); });
}
fn assert_failure_of(pushed: &Observed, cause: ManualPushCause, message_part: &str) {
let stderr: String = stderr_of(pushed);
assert_eq!(pushed.code, Some(2), "{stderr}");
assert!(stderr.contains("\"code\":\"content-unavailable\""), "{stderr}");
assert!(stderr.contains("\"trigger\":\"manual-push\""), "{stderr}");
assert!(stderr.contains(message_part), "the message must say {message_part:?}: {stderr}");
match hint_field(cause) {
Some(field) => assert!(stderr.contains(field.as_str()), "the hint of {cause:?} is missing: {stderr}"),
None => assert!(!stderr.contains("\"hint\""), "{cause:?} takes no hint: {stderr}"),
}
}
#[test]
fn a_tag_of_a_tree_gets_the_hint_about_commits() {
let fixture: Fixture = fixture("scope-tree-tag");
let world: World = policy_world(&fixture);
git(&fixture, world.repo.as_path(), &["tag", "tree-tag", "HEAD^{tree}"]);
let pushed: Observed = wrapped_with_hints(&fixture, world.repo.as_path(), &["push", "origin", "tree-tag"]);
assert_failure_of(&pushed, ManualPushCause::UnsupportedObject, "is not a commit");
let by_code: &str = engine_failure_hint("content-unavailable").expect("the code's hint");
assert!(!stderr_of(&pushed).contains(by_code), "a tag of a tree is not missing content: {}", stderr_of(&pushed));
remove_settled(&fixture);
}
#[test]
fn a_commit_whose_parent_object_is_gone_keeps_the_hint_about_missing_content() {
let fixture: Fixture = fixture("scope-missing-parent");
let world: World = policy_world(&fixture);
commit_directly(&fixture, world.repo.as_path(), "one.txt", "one\n");
let parent: String = ref_of(&fixture, world.repo.as_path(), "HEAD");
commit_directly(&fixture, world.repo.as_path(), "two.txt", "two\n");
let object: std::path::PathBuf = world.repo.join(".git/objects").join(&parent[..2]).join(&parent[2..]);
assert!(object.exists(), "the parent is a loose object: {}", object.display());
std::fs::remove_file(&object).expect("remove the parent object");
let pushed: Observed = wrapped_with_hints(&fixture, world.repo.as_path(), &["push", "origin", "HEAD:refs/heads/unpublished"]);
assert_failure_of(&pushed, ManualPushCause::ContentMissing, "failed");
remove_settled(&fixture);
}