git-ents.gitmain
⌘K
foforge
mod.rs112 lines · 4.3 KB · rusthistorycomment on this file
1//! Shared test fixtures for `git-ents`'s integration suite: a real,
2//! on-disk repository plus a deterministic signing key — the counterpart
3//! to `ents-testutil`'s in-memory fixtures, needed here because this
4//! crate's composition roots ([`git_ents::root::LocalRoot`],
5//! [`git_ents::root::HostedRoot`]) wire a real `LooseRefStore` and a real
6//! odb, not the in-memory `MemRefStore`/`ObjectStore` pair every library
7//! crate's own tests use.
8
9#![allow(dead_code, reason = "not every test file uses every helper")]
10#![allow(clippy::expect_used, reason = "integration test")]
11
12use std::path::{Path, PathBuf};
13use std::process::Command;
14
15use ssh_key::private::{Ed25519Keypair, KeypairData};
16use ssh_key::{LineEnding, PrivateKey};
17use tempfile::TempDir;
18
19/// A real, empty git repository plus a deterministic signing key, ready
20/// for [`git_ents::root::LocalRoot::open`] or [`git_ents::root::HostedRoot::open`].
21pub struct Fixture {
22 pub dir: TempDir,
23 pub key_path: PathBuf,
24}
25
26impl Fixture {
27 /// Initialize a fresh, non-bare repository with a deterministic
28 /// ed25519 signing key at `<repo>/../id_ed25519`, seeded by `seed`.
29 pub fn new(seed: u8) -> Self {
30 let dir = tempfile::tempdir().expect("tempdir");
31 gix::init(dir.path()).expect("init");
32 let key_path = dir.path().join(".id_ed25519");
33 write_key(&key_path, seed);
34 Self { dir, key_path }
35 }
36
37 /// Initialize a fresh *bare* repository (the single-node hosted root's
38 /// shape) with a deterministic signing key.
39 pub fn new_bare(seed: u8) -> Self {
40 let dir = tempfile::tempdir().expect("tempdir");
41 gix::init_bare(dir.path()).expect("init bare");
42 let key_path = dir.path().join(".id_ed25519");
43 write_key(&key_path, seed);
44 Self { dir, key_path }
45 }
46
47 pub fn path(&self) -> &Path {
48 self.dir.path()
49 }
50}
51
52/// Write an executable script at `path` that overwrites its one argument
53/// (the scratch file a composing command opens) with `contents` — a
54/// stand-in for a real `$EDITOR`, exercising the same spawn-and-read-back
55/// path a real editor would.
56pub fn write_fake_editor(path: &Path, contents: &str) {
57 let script = format!("#!/bin/sh\ncat > \"$1\" <<'EOF'\n{contents}\nEOF\n");
58 std::fs::write(path, script).expect("write fake editor");
59 #[cfg(unix)]
60 {
61 use std::os::unix::fs::PermissionsExt as _;
62 let mut perms = std::fs::metadata(path).expect("metadata").permissions();
63 perms.set_mode(0o755);
64 std::fs::set_permissions(path, perms).expect("chmod");
65 }
66}
67
68/// Write a deterministic key inside `dir` (as `.id_ed25519`) and return its
69/// path — for tests that need a key living alongside a specific working
70/// directory (a clone) rather than a [`Fixture`]'s own repo directory.
71pub fn write_key_in(dir: &Path, seed: u8) -> PathBuf {
72 let path = dir.join(".id_ed25519");
73 write_key(&path, seed);
74 path
75}
76
77/// Write a deterministic, unencrypted ed25519 key at `path` — the fixture
78/// counterpart to `ents_testutil::Keypair::from_seed`, but a real file a
79/// [`git_ents::sign::Signer`] can load.
80pub fn write_key(path: &Path, seed: u8) {
81 let pair = Ed25519Keypair::from_seed(&[seed; 32]);
82 let key = PrivateKey::new(KeypairData::from(pair), "git-ents-test").expect("well-formed");
83 key.write_openssh_file(path, LineEnding::LF)
84 .expect("write key");
85}
86
87/// Configure `dir`'s own local git config to sign with `key`
88/// (`user.signingkey` + `gpg.format=ssh`) — what a real operator's
89/// `git ents setup` does for a clone, needed here so `git push
90/// --signed=if-asked` against the hosted root (which now always
91/// advertises `push-cert`) actually produces a certificate instead of
92/// silently pushing unsigned.
93pub fn configure_signing(dir: &Path, key: &Path) {
94 for (name, value) in [
95 ("user.signingkey", key.to_str().expect("utf8 path")),
96 ("gpg.format", "ssh"),
97 ] {
98 let output = Command::new("git")
99 .arg("-C")
100 .arg(dir)
101 .args(["config", "--local", name, value])
102 .output()
103 .expect("git runs");
104 assert!(output.status.success(), "{output:?}");
105 }
106}
107
108/// The path to the built `git-ents` binary under test — `cargo test`
109/// exposes this via `CARGO_BIN_EXE_<name>`.
110pub fn bin_path() -> PathBuf {
111 PathBuf::from(env!("CARGO_BIN_EXE_git-ents"))
112}