use std::path::{Path, PathBuf};
use super::helpers::TestEnv;
fn signer_with_pushed_commit() -> (TestEnv, PathBuf) {
let alice = TestEnv::new();
alice.init_identity();
let origin = alice.home.path().join("origin.git");
let init_bare = std::process::Command::new("git")
.args(["init", "-q", "--bare"])
.arg(&origin)
.output()
.unwrap();
assert!(init_bare.status.success(), "git init --bare failed");
std::fs::write(alice.repo_path.join("main.rs"), "fn main() {}").unwrap();
let bundle = alice.repo_path.join(".auths").join("ci-bundle.json");
let export = alice
.cmd("auths")
.args([
"id",
"export-bundle",
"--alias",
"main",
"--output",
bundle.to_str().unwrap(),
"--max-age-secs",
"3600",
])
.output()
.unwrap();
assert!(
export.status.success(),
"id export-bundle failed: {}",
String::from_utf8_lossy(&export.stderr)
);
let add = alice
.git_cmd()
.args(["add", "main.rs", ".auths/ci-bundle.json"])
.output()
.unwrap();
assert!(add.status.success(), "git add failed");
let commit = alice
.git_cmd()
.args(["commit", "-m", "feat: add main"])
.output()
.unwrap();
assert!(
commit.status.success(),
"git commit failed: {}",
String::from_utf8_lossy(&commit.stderr)
);
let branch = alice
.git_cmd()
.args(["rev-parse", "--abbrev-ref", "HEAD"])
.output()
.unwrap();
let branch = String::from_utf8_lossy(&branch.stdout).trim().to_string();
let push = alice
.git_cmd()
.arg("push")
.arg(&origin)
.arg(&branch)
.output()
.unwrap();
assert!(
push.status.success(),
"git push failed: {}",
String::from_utf8_lossy(&push.stderr)
);
let head = std::process::Command::new("git")
.arg("--git-dir")
.arg(&origin)
.args(["symbolic-ref", "HEAD"])
.arg(format!("refs/heads/{branch}"))
.output()
.unwrap();
assert!(head.status.success(), "could not set origin HEAD");
(alice, origin)
}
fn observer_cloning(origin: &Path) -> TestEnv {
let mut bob = TestEnv::new();
let clone = bob.home.path().join("clone");
let out = std::process::Command::new("git")
.arg("clone")
.arg("-q")
.arg(origin)
.arg(&clone)
.output()
.unwrap();
assert!(
out.status.success(),
"git clone failed: {}",
String::from_utf8_lossy(&out.stderr)
);
bob.repo_path = clone;
bob
}
#[test]
fn a_second_person_verifies_a_plain_clone_with_no_identity_and_no_flags() {
let (_alice, origin) = signer_with_pushed_commit();
let bob = observer_cloning(&origin);
let out = bob.cmd("auths").args(["verify", "HEAD"]).output().unwrap();
assert!(
out.status.success(),
"a second person must be able to verify a clone with no flags.\n\
stdout: {}\nstderr: {}",
String::from_utf8_lossy(&out.stdout),
String::from_utf8_lossy(&out.stderr)
);
assert!(
String::from_utf8_lossy(&out.stdout).contains("verified"),
"expected a verified verdict, got: {}",
String::from_utf8_lossy(&out.stdout)
);
}
#[test]
fn the_trust_pin_travels_with_the_repository() {
let (_alice, origin) = signer_with_pushed_commit();
let bob = observer_cloning(&origin);
let pin = bob.repo_path.join(".auths").join("roots");
assert!(
pin.is_file(),
".auths/roots must be committed, or a cloner has nothing to anchor trust to"
);
let content = std::fs::read_to_string(&pin).unwrap();
assert!(
content.contains("did:keri:"),
"pin must name a did:keri root, got: {content:?}"
);
}
#[test]
fn the_committed_bundle_carries_the_kel_in_the_clone() {
let (_alice, origin) = signer_with_pushed_commit();
let bob = observer_cloning(&origin);
let bundle = bob.repo_path.join(".auths").join("ci-bundle.json");
assert!(
bundle.is_file(),
".auths/ci-bundle.json must be committed, or a cloner has no KEL to verify against"
);
let content = std::fs::read_to_string(&bundle).unwrap();
assert!(
content.contains("kel_attachments"),
"the bundle must carry signature attachments, got: {}",
&content[..content.len().min(200)]
);
}
#[test]
fn an_unpinned_root_still_fails_closed() {
let (_alice, origin) = signer_with_pushed_commit();
let bob = observer_cloning(&origin);
std::fs::remove_file(bob.repo_path.join(".auths").join("roots")).unwrap();
let out = bob.cmd("auths").args(["verify", "HEAD"]).output().unwrap();
assert!(
!out.status.success(),
"an unpinned root must not verify — this is the TOFU-on-the-root guard.\n\
stdout: {}",
String::from_utf8_lossy(&out.stdout)
);
}