Skip to main content

gen_gomod/
testkit.rs

1//! Test support — a deterministic, filesystem-free [`GoBuildEnv`].
2//!
3//! The Environment trait ([`crate::interp::GoBuildEnv`]) IS the
4//! testability contract: [`MockGoBuildEnv`] satisfies it from in-memory
5//! maps so the entire encoder runs with ZERO `go` / filesystem
6//! dependency (integration tests + downstream consumers alike). Shipped
7//! in the crate (not `#[cfg(test)]`) exactly because integration-test
8//! crates and downstream verifiers need it — mirrors gen-cargo shipping
9//! its `MockResolver`.
10
11use std::collections::HashMap;
12use std::path::Path;
13
14use crate::error::GomodError;
15use crate::interp::GoBuildEnv;
16use crate::build_spec::TargetTuple;
17
18/// A [`GoBuildEnv`] backed by in-memory maps.
19#[derive(Clone, Debug, Default)]
20pub struct MockGoBuildEnv {
21    /// Canned `go list` JSON keyed by [`TargetTuple::suffix`], so a
22    /// multi-tuple mock (linux vs darwin build-tag differences) works.
23    pub list_by_tuple: HashMap<String, String>,
24    /// Canned file contents keyed by absolute path string (go.mod,
25    /// go.sum, each source/embed file).
26    pub files: HashMap<String, Vec<u8>>,
27}
28
29impl MockGoBuildEnv {
30    #[must_use]
31    pub fn new() -> Self {
32        Self::default()
33    }
34
35    /// Register the `go list` output for a tuple (builder style).
36    #[must_use]
37    pub fn with_list(mut self, tuple: &TargetTuple, json: impl Into<String>) -> Self {
38        self.list_by_tuple.insert(tuple.suffix(), json.into());
39        self
40    }
41
42    /// Register a file's bytes at an absolute path (builder style).
43    #[must_use]
44    pub fn with_file(mut self, path: impl Into<String>, bytes: impl Into<Vec<u8>>) -> Self {
45        self.files.insert(path.into(), bytes.into());
46        self
47    }
48}
49
50impl GoBuildEnv for MockGoBuildEnv {
51    fn go_list(&self, _root: &Path, tuple: &TargetTuple) -> Result<String, GomodError> {
52        self.list_by_tuple
53            .get(&tuple.suffix())
54            .cloned()
55            .ok_or_else(|| GomodError::GoList(format!("mock: no go list registered for {}", tuple.suffix())))
56    }
57
58    fn read_file(&self, path: &Path) -> Result<Vec<u8>, GomodError> {
59        let key = path.to_string_lossy().to_string();
60        self.files.get(&key).cloned().ok_or_else(|| GomodError::Io {
61            path: path.to_path_buf(),
62            source: std::io::Error::new(std::io::ErrorKind::NotFound, "mock: no file registered"),
63        })
64    }
65}