mod common;
use std::io::Write as _;
use std::path::Path;
use std::process::Stdio;
use common::{assert_success, git, git_command};
const OWNER: &str = "did:web:owner.example";
const MASTER_KEY_ENV: &str = "KNOT_STATIC_LFS_MASTER_KEY";
const MASTER_KEY: [u8; 32] = [7; 32];
const POINTER: &str = "version https://git-lfs.github.com/spec/v1\noid sha256:";
fn blob(repo: &Path, content: &[u8]) -> String {
let mut child = git_command(repo)
.args(["hash-object", "-w", "--stdin"])
.stdin(Stdio::piped())
.stdout(Stdio::piped())
.spawn()
.unwrap();
child.stdin.take().unwrap().write_all(content).unwrap();
let output = child.wait_with_output().unwrap();
assert_success(&output, "hash-object");
String::from_utf8(output.stdout).unwrap().trim().to_string()
}
#[test]
fn import_finds_lfs_pointers_in_a_bare_repo_with_one_batch() {
let root = tempfile::tempdir().unwrap();
let source = root.path().join("source.git");
let config = root.path().join("config.toml");
std::fs::create_dir_all(&source).unwrap();
git(&source, ["init", "-q", "--bare", "-b", "main"]);
let mut pointers = [
blob(
&source,
format!("{POINTER}{}\nsize 1\n", "a".repeat(64)).as_bytes(),
),
blob(
&source,
format!("{POINTER}{}\nsize 2\n", "b".repeat(64)).as_bytes(),
),
];
pointers.sort();
for n in 0..40 {
blob(&source, format!("plain {n}\n").as_bytes());
}
blob(&source, format!("{POINTER}{}", "c".repeat(5000)).as_bytes());
blob(&source, b"");
std::fs::write(
&config,
format!(
r#"[knot]
hostname = "knot.lfs.test"
owner = "{OWNER}"
[repo]
scan_path = "{}"
default_branch = "main"
object_format = "sha1"
[secrets]
sealed_key_file = "{}"
master_key_env = "{MASTER_KEY_ENV}"
[atproto]
plc_directory = "https://127.0.0.1:9"
[output]
root = "{}"
"#,
root.path().join("state").display(),
root.path().join("keys.sealed").display(),
root.path().join("output").display(),
),
)
.unwrap();
let knot_static = |args: &[&str], env: &[(&str, &str)]| {
common::knot_static_raw(&config, MASTER_KEY_ENV, &MASTER_KEY, args, env)
};
assert_success(&knot_static(&["init"], &[]), "init");
let trace = root.path().join("trace2.json");
let imported = knot_static(
&[
"repo",
"import",
"--owner",
OWNER,
"--rkey",
"lfs",
source.to_str().unwrap(),
],
&[("GIT_TRACE2_EVENT", trace.to_str().unwrap())],
);
assert!(!imported.status.success(), "pointers stop the import");
let stderr = String::from_utf8_lossy(&imported.stderr);
assert!(
stderr.contains(&format!(
"detected 2 Git LFS pointer file(s) (e.g. {})",
pointers[0]
)),
"{stderr}"
);
let cat_files = std::fs::read_to_string(&trace)
.unwrap()
.lines()
.filter(|line| line.contains(r#""event":"cmd_name""#))
.filter(|line| line.contains(r#""name":"cat-file""#))
.count();
assert_eq!(cat_files, 2, "one listing and one batch read");
}