crates/gunnar-client/tests/network_ssh.rs
//! Tests that need a network and a key. **All `#[ignore]`d**.
//!
//! The default `cargo test -p gunnar-client` run is hermetic. Nothing in this
//! file runs unless it is asked for by name:
//!
//! ```text
//! cargo test -p gunnar-client --test network_ssh -- --ignored --nocapture
//! ```
//!
//! Each reads its remote from an environment variable and **skips loudly** when
//! it is unset, rather than passing vacuously. A network test that silently
//! succeeds when it has nothing to talk to is the identity-value trap wearing a
//! different hat.
mod common;
use std::path::PathBuf;
use gunnar_client::{MirrorRegistry, PushOptions, RefSpec};
fn ssh_key() -> PathBuf {
std::env::var("GUNNAR_SSH_KEY")
.map(PathBuf::from)
.or_else(|_| {
nornir_git::gitio::nornir_ssh_key_path()
.ok_or("no key")
.map_err(|e| e.to_string())
})
.expect("set GUNNAR_SSH_KEY, or have a key on nornir-git's deploy-key ladder")
}
/// `GUNNAR_TEST_REMOTE=git@codeberg.org:ns/repo.git`
fn remote() -> String {
match std::env::var("GUNNAR_TEST_REMOTE") {
Ok(u) => u,
Err(_) => panic!(
"GUNNAR_TEST_REMOTE is unset, so this test has nothing to talk to and \
would otherwise pass without testing anything"
),
}
}
#[test]
#[ignore = "network: needs GUNNAR_TEST_REMOTE and an ssh key"]
fn ls_remote_against_a_real_forge() {
let refs = gunnar_client::ls_remote(&remote(), &ssh_key()).unwrap();
eprintln!("{} refs advertised", refs.len());
assert!(!refs.is_empty(), "a real repository advertises something");
assert!(
refs.iter().any(|r| r.name == "HEAD"),
"protocol-v2 ls-refs must resolve HEAD: {refs:?}"
);
// Prints the hash kind so a SHA-256 remote is visibly exercised rather than
// assumed: the sha256 path is not inferred from sha1 green.
eprintln!("object format: {:?}", refs[0].oid.kind());
}
/// `gunnar mirror sync` end to end over SSH, including a SHA-256 remote if that
/// is what `GUNNAR_TEST_REMOTE` points at as well.
#[test]
#[ignore = "network: needs GUNNAR_TEST_REMOTE and an ssh key"]
fn mirror_a_real_remote_over_ssh() {
let dir = tempfile::tempdir().unwrap();
let mut reg = MirrorRegistry::open(dir.path()).unwrap();
reg.add("test/mirror", &remote()).unwrap();
let first = reg.sync("test/mirror", &ssh_key()).unwrap();
eprintln!("first sync: {first:?}");
assert!(!first.unchanged, "an unseen remote cannot be unchanged");
assert!(first.changed_refs() > 0);
assert!(first.objects_received > 0);
let local = reg.path_for("test/mirror").unwrap();
assert!(
common::git_try(&local, &["fsck", "--strict", "--no-progress"]).is_ok(),
"the mirrored repository does not pass git fsck"
);
let second = reg.sync("test/mirror", &ssh_key()).unwrap();
assert!(
second.unchanged || second.changed_refs() == 0,
"an immediate re-sync should find nothing to do: {second:?}"
);
}
/// Push over a real SSH connection. **Writes to the remote**, so point it at a
/// scratch repository, never at anything that matters.
///
/// `GUNNAR_TEST_PUSH_REMOTE=git@host:ns/scratch.git`
#[test]
#[ignore = "network: WRITES to GUNNAR_TEST_PUSH_REMOTE"]
fn push_over_real_ssh() {
let url = std::env::var("GUNNAR_TEST_PUSH_REMOTE").expect(
"GUNNAR_TEST_PUSH_REMOTE is unset; this test writes and must be aimed deliberately",
);
let dir = tempfile::tempdir().unwrap();
let src = dir.path().join("src");
common::init_worktree(&src, gix::hash::Kind::Sha1);
let sha = common::commit_file(&src, "a.txt", "gunnar was here\n", "test push");
let repo = common::open(&src);
let branch = format!("refs/heads/gunnar-test-{}", &sha[..8]);
let specs = RefSpec::parse_all(&[format!("refs/heads/main:{branch}")]).unwrap();
let mut opts = PushOptions::new();
opts.ssh.key_path = Some(ssh_key());
let outcome = gunnar_client::push(&repo, &url, &specs, &opts).unwrap();
eprintln!("{outcome:?}");
assert!(
outcome.is_ok(),
"the remote refused: {:?}",
outcome.report.as_ref().and_then(|r| r.failure_summary())
);
// Clean up after ourselves.
let repo = common::open(&src);
let del = RefSpec::parse_all(&[format!(":{branch}")]).unwrap();
let cleanup = gunnar_client::push(&repo, &url, &del, &opts).unwrap();
assert!(
cleanup.is_ok(),
"failed to delete the test branch: {cleanup:?}"
);
}