Skip to main content

onevcs_testing/
boundary.rs

1// llmlint: ignore[new_code_lands_in_a_project] AGENTS.md assigns onevcs-testing
2// to onevcs's workspace targets through crateSource (crates/**/*), as its existing
3// sources are owned; a second crate project would duplicate those workspace checks.
4//! The public boundary's budget workload: one host the release-binary journeys time
5//! the publication term check and an export over.
6//!
7//! Twenty registered private repositories, each committing a manifest and a term
8//! declaration that derive exactly `TERMS_PER_IDENTITY` terms, and one registered
9//! public repository with a branch adding `PATHS` files of neutral UTF-8 text,
10//! `BYTES` in all. One of the private repositories carries the same files on a
11//! branch, confined to one directory, for an export. Everything is built with real
12//! git — `fast-import` for the large commits — and the registry is written through
13//! onevcs's own test-support bridge. Nothing here matches, diffs, derives or exports:
14//! the journeys drive the release binary for all of that.
15//!
16//! Every owner, name and term is synthetic.
17
18use std::collections::BTreeMap;
19use std::io::Write;
20use std::path::{Path, PathBuf};
21use std::process::{Command, Stdio};
22
23use onevcs::registry::{Checkout, Identity, Registry};
24use onevcs::{testing, Error, Result};
25
26/// Registered private repositories in the check's scope.
27pub const IDENTITIES: usize = 20;
28/// Terms each of them derives: its `owner/name`, its owner, its name, its package,
29/// and 96 declared terms.
30pub const TERMS_PER_IDENTITY: usize = 100;
31/// Paths the publication adds, and the export copies.
32pub const PATHS: usize = 1000;
33/// Bytes of UTF-8 text across those paths.
34pub const BYTES: usize = 10 * 1024 * 1024;
35
36/// The public repository every write lands in.
37pub const DESTINATION: &str = "github.com/sample-owner/openwidget";
38/// The branch of the public repository that carries the published work.
39pub const PUBLICATION_BRANCH: &str = "generic-workload";
40/// The branch of the export's source that carries the directory to export.
41pub const EXPORT_BRANCH: &str = "generic-examples";
42/// The directory the export copies.
43pub const EXPORT_DIRECTORY: &str = "examples";
44
45/// A generated host.
46#[derive(Debug)]
47pub struct Workload {
48    /// The empty root the caller handed over, now holding everything.
49    pub root: PathBuf,
50    /// The state root to pass as `ONEVCS_HOME`.
51    pub home: PathBuf,
52    /// The public repository's registered checkout.
53    pub destination: PathBuf,
54    /// Its bare origin.
55    pub destination_origin: PathBuf,
56    /// The identity of the private repository the export reads from.
57    pub source: String,
58    /// The private identities, in order.
59    pub identities: Vec<String>,
60    /// Bytes of text the publication adds.
61    pub bytes: usize,
62}
63
64/// The identity key of private repository `index`.
65pub fn identity(index: usize) -> String {
66    format!("github.com/shelterco{index:02}/quietvault{index:02}")
67}
68
69/// Build the workload under `root`, which must be empty or absent.
70pub fn build(root: &Path) -> Result<Workload> {
71    if root.exists() && std::fs::read_dir(root).map_err(io)?.next().is_some() {
72        return Err(invalid(format!(
73            "workload root {} must be empty",
74            root.display()
75        )));
76    }
77    std::fs::create_dir_all(root).map_err(io)?;
78    let root = std::fs::canonicalize(root).map_err(io)?;
79    let home = root.join("home");
80    std::fs::create_dir_all(&home).map_err(io)?;
81    std::fs::write(
82        root.join(".gitconfig"),
83        "[user]\nname=Fixture\nemail=fixture@example.invalid\n[commit]\ngpgsign=false\n\
84         [maintenance]\nauto=false\n[init]\ndefaultBranch=main\n",
85    )
86    .map_err(io)?;
87    let rules = home.join("rules.yml");
88    std::fs::write(
89        &rules,
90        "version: 4\nrules:\n  - match: {host: github.com, owner: \"shelterco*\"}\n    \
91         visibility: private\n  - match: {host: github.com, owner: sample-owner, name: \
92         openwidget}\n    visibility: public\ndefault: {publication: local-direct, approvals: \
93         none}\n",
94    )
95    .map_err(io)?;
96
97    let files = workload_files();
98    let bytes = files.iter().map(|(_, body)| body.len()).sum();
99    let mut registry = Registry {
100        version: testing::registry_version(),
101        identities: BTreeMap::new(),
102        checkouts: BTreeMap::new(),
103        rules: Some(rules),
104    };
105    let mut identities = Vec::new();
106    for index in 0..IDENTITIES {
107        let key = identity(index);
108        let checkout = private_repository(&root, index, &files)?;
109        registry
110            .identities
111            .insert(key.clone(), Identity::new(key.clone(), "<no-op>"));
112        registry.checkouts.insert(
113            format!("quietvault{index:02}"),
114            Checkout {
115                path: checkout,
116                identity: key.clone(),
117            },
118        );
119        identities.push(key);
120    }
121    let (destination, destination_origin) = public_repository(&root, &files)?;
122    registry.identities.insert(
123        DESTINATION.to_owned(),
124        Identity::new(DESTINATION, "<no-op>"),
125    );
126    registry.checkouts.insert(
127        "openwidget".to_owned(),
128        Checkout {
129            path: destination.clone(),
130            identity: DESTINATION.to_owned(),
131        },
132    );
133    testing::write_registry(&home, &registry)?;
134    Ok(Workload {
135        root,
136        home,
137        destination,
138        destination_origin,
139        source: identity(0),
140        identities,
141        bytes,
142    })
143}
144
145/// Words no term is made of, so the generated text is neutral by construction.
146const WORDS: [&str; 32] = [
147    "amber", "basin", "cedar", "delta", "ember", "fable", "grove", "hazel", "inlet", "jasper",
148    "kettle", "lumen", "maple", "nectar", "orbit", "pebble", "quartz", "ripple", "sable",
149    "thistle", "umber", "velvet", "willow", "xenon", "yarrow", "zephyr", "anchor", "bramble",
150    "canyon", "drift", "estuary", "fern",
151];
152
153/// The files the publication adds and the export copies: `PATHS` of them, whose
154/// sizes sum to [`BYTES`], each a deterministic run of neutral words.
155fn workload_files() -> Vec<(String, String)> {
156    let mut state: u64 = 0x5eed_b0a7;
157    let per_file = BYTES / PATHS;
158    let mut remainder = BYTES % PATHS;
159    (0..PATHS)
160        .map(|index| {
161            let mut size = per_file;
162            if remainder > 0 {
163                size += 1;
164                remainder -= 1;
165            }
166            let mut text = String::with_capacity(size + 64);
167            let mut line = 0;
168            while text.len() < size {
169                line += 1;
170                text.push_str(&format!("line {line}:"));
171                for _ in 0..9 {
172                    state = state
173                        .wrapping_mul(6_364_136_223_846_793_005)
174                        .wrapping_add(1_442_695_040_888_963_407);
175                    text.push(' ');
176                    text.push_str(WORDS[(state >> 59) as usize]);
177                }
178                text.push('\n');
179            }
180            text.truncate(size - 1);
181            text.push('\n');
182            (format!("part-{:02}/file-{index:04}.txt", index % 25), text)
183        })
184        .collect()
185}
186
187/// Private repository `index`: a manifest and a declaration committed on `main`, and
188/// for the first one a bare origin and the export's branch.
189fn private_repository(root: &Path, index: usize, files: &[(String, String)]) -> Result<PathBuf> {
190    let name = format!("quietvault{index:02}");
191    let repo = root.join("checkouts").join(&name);
192    std::fs::create_dir_all(&repo).map_err(io)?;
193    git(&repo, &["init", "-q", "-b", "main"])?;
194    let declared: Vec<String> = (0..TERMS_PER_IDENTITY - 4)
195        .map(|term| format!("\"{name}x{term:03}\""))
196        .collect();
197    let base = vec![
198        ("README.md".to_owned(), "# notes\n".to_owned()),
199        (
200            "Cargo.toml".to_owned(),
201            format!("[package]\nname = \"{name}-core\"\n"),
202        ),
203        (
204            "private-terms.toml".to_owned(),
205            format!("schema_version = 1\nterms = [{}]\n", declared.join(", ")),
206        ),
207    ];
208    let mut import = Import::default();
209    let main = import.commit("main", None, "chore: the repository", &base);
210    if index == 0 {
211        let examples: Vec<(String, String)> = files
212            .iter()
213            .map(|(path, body)| (format!("{EXPORT_DIRECTORY}/{path}"), body.clone()))
214            .collect();
215        import.commit(
216            EXPORT_BRANCH,
217            Some(main),
218            "docs: add generic examples",
219            &examples,
220        );
221    }
222    import.run(&repo)?;
223    git(&repo, &["checkout", "-q", "main"])?;
224    if index == 0 {
225        let origin = root.join("origins").join(format!("{name}.git"));
226        git(
227            root,
228            &[
229                "init",
230                "-q",
231                "--bare",
232                "-b",
233                "main",
234                &origin.to_string_lossy(),
235            ],
236        )?;
237        git(
238            &repo,
239            &["remote", "add", "origin", &origin.to_string_lossy()],
240        )?;
241        git(&repo, &["push", "-q", "origin", "main"])?;
242        git(&repo, &["fetch", "-q", "origin"])?;
243        git(&repo, &["remote", "set-head", "origin", "main"])?;
244    }
245    Ok(repo)
246}
247
248/// The public repository: a bare origin, and a registered checkout of it carrying the
249/// publication's branch.
250fn public_repository(root: &Path, files: &[(String, String)]) -> Result<(PathBuf, PathBuf)> {
251    let origin = root.join("origins/openwidget.git");
252    let repo = root.join("checkouts/openwidget");
253    std::fs::create_dir_all(&repo).map_err(io)?;
254    git(
255        root,
256        &[
257            "init",
258            "-q",
259            "--bare",
260            "-b",
261            "main",
262            &origin.to_string_lossy(),
263        ],
264    )?;
265    git(&repo, &["init", "-q", "-b", "main"])?;
266    let mut import = Import::default();
267    let main = import.commit(
268        "main",
269        None,
270        "chore: seed the repository",
271        &[("README.md".to_owned(), "# openwidget\n".to_owned())],
272    );
273    let workload: Vec<(String, String)> = files
274        .iter()
275        .map(|(path, body)| (format!("workload/{path}"), body.clone()))
276        .collect();
277    import.commit(
278        PUBLICATION_BRANCH,
279        Some(main),
280        "docs: add generic workload fixtures",
281        &workload,
282    );
283    import.run(&repo)?;
284    git(&repo, &["checkout", "-q", "main"])?;
285    git(
286        &repo,
287        &["remote", "add", "origin", &origin.to_string_lossy()],
288    )?;
289    git(&repo, &["push", "-q", "origin", "main"])?;
290    git(&repo, &["fetch", "-q", "origin"])?;
291    git(&repo, &["remote", "set-head", "origin", "main"])?;
292    git(
293        &repo,
294        &["branch", "-q", "--set-upstream-to=origin/main", "main"],
295    )?;
296    Ok((repo, origin))
297}
298
299/// A `git fast-import` stream, built in memory.
300#[derive(Default)]
301struct Import {
302    bytes: Vec<u8>,
303    next: usize,
304}
305
306impl Import {
307    fn mark(&mut self) -> usize {
308        self.next += 1;
309        self.next
310    }
311
312    fn commit(
313        &mut self,
314        branch: &str,
315        parent: Option<usize>,
316        subject: &str,
317        files: &[(String, String)],
318    ) -> usize {
319        let mut blobs = Vec::new();
320        for (path, body) in files {
321            let mark = self.mark();
322            write!(self.bytes, "blob\nmark :{mark}\ndata {}\n", body.len()).expect("memory");
323            self.bytes.extend_from_slice(body.as_bytes());
324            self.bytes.push(b'\n');
325            blobs.push((path, mark));
326        }
327        let mark = self.mark();
328        write!(
329            self.bytes,
330            "commit refs/heads/{branch}\nmark :{mark}\ncommitter Fixture \
331             <fixture@example.invalid> 1700000000 +0000\ndata {}\n{subject}\n",
332            subject.len()
333        )
334        .expect("memory");
335        if let Some(parent) = parent {
336            writeln!(self.bytes, "from :{parent}").expect("memory");
337        }
338        for (path, blob) in blobs {
339            writeln!(self.bytes, "M 100644 :{blob} {path}").expect("memory");
340        }
341        self.bytes.push(b'\n');
342        mark
343    }
344
345    fn run(self, repo: &Path) -> Result<()> {
346        git_input(repo, &["fast-import", "--quiet"], &self.bytes).map(|_| ())
347    }
348}
349
350fn invalid(reason: impl Into<String>) -> Error {
351    Error::Invalid {
352        reason: reason.into(),
353    }
354}
355
356fn io(error: std::io::Error) -> Error {
357    invalid(error.to_string())
358}
359
360fn git(repo: &Path, args: &[&str]) -> Result<String> {
361    git_input(repo, args, &[])
362}
363
364fn git_input(repo: &Path, args: &[&str], input: &[u8]) -> Result<String> {
365    let home = repo
366        .ancestors()
367        .find(|ancestor| ancestor.join(".gitconfig").is_file())
368        .unwrap_or(repo);
369    let mut child = Command::new("git")
370        .current_dir(repo)
371        .args(args)
372        .env_clear()
373        .env("PATH", std::env::var_os("PATH").unwrap_or_default())
374        .env("HOME", home)
375        .env("GIT_CONFIG_NOSYSTEM", "1")
376        .stdin(Stdio::piped())
377        .stdout(Stdio::piped())
378        .stderr(Stdio::piped())
379        .spawn()
380        .map_err(io)?;
381    child
382        .stdin
383        .take()
384        .expect("piped stdin")
385        .write_all(input)
386        .map_err(io)?;
387    let output = child.wait_with_output().map_err(io)?;
388    if !output.status.success() {
389        return Err(invalid(format!(
390            "workload git {} in {}: {}",
391            args.join(" "),
392            repo.display(),
393            String::from_utf8_lossy(&output.stderr)
394        )));
395    }
396    String::from_utf8(output.stdout).map_err(|error| invalid(error.to_string()))
397}