crates/gunnar-ssh/tests/lfs_authenticate.rs
//! **`git-lfs-authenticate` is ANSWERED, with the words that say what to do.**
//!
//! # What this is a regression test for
//!
//! MEASURED 2026-10-25 against gunnar.rs with git-lfs 3.8.0: when a pure-SSH
//! `git-lfs-transfer` session fails or is refused, git-lfs falls back to
//! `ssh … git-lfs-authenticate <path> <operation>`. gunnar answered that exec
//! with a channel failure, so the push ended in
//!
//! ```text
//! batch request: exec request failed on channel 0: exit status 255
//! error: failed to fetch some objects from 'https://git.gunnar.rs/nordisk/gunnar.git/info/lfs'
//! ```
//!
//! — which CI lanes read as "the LFS server is unreachable" and pushed past
//! with `--no-verify`. Gunnar serves LFS over pure SSH only (git-lfs ≥ 3.0),
//! so the verb is answered on stderr with exit 1 and a sentence: pure SSH only,
//! retry. git-lfs prints that sentence after `batch request:`.
//!
//! The words must NOT contain `not found`, `no such file` or `command not
//! found`: git-lfs reads those as "this server has no git-lfs-authenticate" and
//! goes on to a guessed HTTPS endpoint, which Gunnar does not serve.
mod common;
use common::*;
use gunnar_ssh::Access;
#[tokio::test]
async fn git_lfs_authenticate_is_answered_with_the_pure_ssh_sentence() {
let key = mint_key();
let fixture = start_with_key(&key, Access::Admin).await;
let session = authenticate(fixture.server.local_addr(), &key, "git")
.await
.expect("connect")
.expect("authenticate");
for line in [
"git-lfs-authenticate 'nordisk/osm-katana.git' upload",
"git-lfs-authenticate nordisk/osm-katana.git download",
] {
match exec(&session, line).await.expect("exec") {
ExecOutcome::Accepted {
stdout,
exit_status,
} => {
assert_eq!(
exit_status,
Some(1),
"{line}: a refusal git-lfs reports, not a crash: {stdout:?}"
);
assert!(
stdout.contains("pure SSH"),
"{line}: the sentence names the transport: {stdout:?}"
);
assert!(stdout.contains("git-lfs-transfer"), "{line}: {stdout:?}");
assert!(
stdout.contains("again"),
"{line}: it says to retry: {stdout:?}"
);
let lower = stdout.to_ascii_lowercase();
for fallback in ["not found", "no such file", "command not found"] {
assert!(
!lower.contains(fallback),
"{line}: `{fallback}` sends git-lfs to a guessed HTTPS endpoint Gunnar does not serve: {stdout:?}"
);
}
}
ExecOutcome::Refused => panic!(
"{line}: refused the channel — git-lfs prints `exec request failed on channel 0`, \
which says nothing about what to do"
),
}
}
assert!(
fixture.transport.served().is_empty(),
"nothing is served by this verb"
);
}