tests/manifests_ingest.rs
//! Out-of-crate proof for the multi-ecosystem manifest ingest seam
//! ([`modgunn::manifests`]).
//!
//! Drives the PUBLIC dispatch ([`parse_manifest`]) once per ecosystem with a
//! small real-shaped fixture and asserts the EXACT components/versions parsed —
//! red-when-broken, one fixture per ecosystem (cargo/pip/npm/maven/go/nuget/gem).
//! The module's in-crate unit tests cover each format variant; this proves the
//! whole seam works through the crate's public API a consumer actually calls.
//!
//! Under `--features testmatrix` it ALSO drains the process-global functional
//! buffer and asserts the ingest surface lit one GREEN row per ecosystem, so
//! `nornir test` SEES the seam. A single test owns the buffer (no parallel race).
#![cfg(feature = "manifests")]
use modgunn::manifests::parse_manifest;
use modgunn::sbom::SbomComponent;
fn rows(comps: &[SbomComponent]) -> Vec<(String, String, String)> {
comps
.iter()
.map(|c| (c.ecosystem.clone(), c.name.clone(), c.version.clone()))
.collect()
}
fn parse(filename: &str, content: &str) -> Vec<SbomComponent> {
parse_manifest(filename, content)
.unwrap_or_else(|| panic!("no ecosystem parser owns {filename}"))
.unwrap_or_else(|e| panic!("{filename} failed to parse: {e}"))
}
fn eco_row(eco: &str, name: &str, ver: &str) -> (String, String, String) {
(eco.into(), name.into(), ver.into())
}
#[test]
fn all_ecosystems_parse_via_public_dispatch() {
#[cfg(feature = "testmatrix")]
let _ = nornir_testmatrix::drain_functional_rows(); // start from a clean buffer
// cargo — Cargo.lock
assert_eq!(
rows(&parse(
"Cargo.lock",
"[[package]]\nname = \"serde\"\nversion = \"1.0.203\"\n"
)),
vec![eco_row("cargo", "serde", "1.0.203")],
);
// pip — requirements.txt (a pin and a range; only the pin resolves)
assert_eq!(
rows(&parse(
"requirements.txt",
"requests==2.31.0\nurllib3>=1.26\n"
)),
vec![eco_row("pip", "requests", "2.31.0")],
);
// npm — package-lock.json (v3 packages map, root skipped)
assert_eq!(
rows(&parse(
"package-lock.json",
r#"{"lockfileVersion":3,"packages":{"":{"name":"r","version":"1.0.0"},"node_modules/left-pad":{"version":"1.3.0"}}}"#,
)),
vec![eco_row("npm", "left-pad", "1.3.0")],
);
// maven — pom.xml (groupId:artifactId name)
assert_eq!(
rows(&parse(
"pom.xml",
"<project><dependencies><dependency><groupId>com.google.guava</groupId><artifactId>guava</artifactId><version>33.0.0-jre</version></dependency></dependencies></project>",
)),
vec![eco_row("maven", "com.google.guava:guava", "33.0.0-jre")],
);
// go — go.mod (single require, version keeps its `v`)
assert_eq!(
rows(&parse(
"go.mod",
"module x\n\nrequire github.com/pkg/errors v0.9.1\n"
)),
vec![eco_row("go", "github.com/pkg/errors", "v0.9.1")],
);
// nuget — packages.lock.json (resolved version)
assert_eq!(
rows(&parse(
"packages.lock.json",
r#"{"dependencies":{"net6.0":{"Newtonsoft.Json":{"resolved":"13.0.3"}}}}"#,
)),
vec![eco_row("nuget", "Newtonsoft.Json", "13.0.3")],
);
// gem — Gemfile.lock (a spec, its constraint children skipped)
assert_eq!(
rows(&parse(
"Gemfile.lock",
"GEM\n specs:\n rake (13.0.6)\n other (>= 1.0)\n",
)),
vec![eco_row("gem", "rake", "13.0.6")],
);
// Unknown filename ⇒ no parser owns it.
assert!(parse_manifest("mystery.lock", "").is_none());
// ── functional_status: one GREEN ingest row per ecosystem ────────────────
#[cfg(feature = "testmatrix")]
{
use nornir_testmatrix::{drain_functional_rows, status};
let rows = drain_functional_rows();
for eco in ["cargo", "pip", "npm", "maven", "go", "nuget", "gem"] {
let suite = format!("modgunn/manifest-ingest-{eco}");
let hit = rows
.iter()
.find(|r| r.suite == suite && r.test_name == "parse_manifest")
.unwrap_or_else(|| panic!("no ingest row for {eco}: {rows:#?}"));
assert!(
status::is_green(&hit.status),
"{eco} parsed OK ⇒ GREEN row: {hit:#?}"
);
assert_eq!(
hit.aspect, "functional",
"ingest markers ride the `functional` aspect"
);
}
}
}