Your browser sucks, JavaScript disabled! Gunnar's console needs JavaScript and WebAssembly. Git over SSH on port 2222 does not.

nordisk/holger

server/lib/tests/pull_through_npm_maven_crates.rs

//! **npm, Maven and cargo pull through the intake chain** (lane HOLGER-PT2,
//! `server/lib/src/pull_intake/{npm,maven,crates,ecosystems}.rs`).
//!
//! Each test runs one real Holger (four `raw` intake repositories, static-token
//! principals with duties, the job queue) in front of a local FAKE upstream that
//! serves REAL bytes fetched once from the internet and kept under
//! `tests/fixtures/pull_through/` (fetched 2026-10-24):
//!
//! * `npm/`: `sigstore 5.0.0` from registry.npmjs.org: its packument (trimmed to
//!   that one version; the version document is as published, with
//!   `dist.integrity`, `dist.signatures` and `dist.attestations`), its tarball,
//!   its attestations (`/-/npm/v1/attestations/sigstore@5.0.0`: npm's SLSA
//!   provenance, keyless, and its publish attestation, key-signed) and the
//!   registry's keys (`/-/npm/v1/keys`);
//! * `maven/`: `org.hamcrest:hamcrest-core:1.3` from Maven Central (POM, its `.sha1`
//!   and `.md5`, `maven-metadata.xml`) and its parent POM. The JAR and its checksums
//!   are MADE here ([`hamcrest_jar`], since 2026-10-30): a committed `.jar` is a binary
//!   artifact to OpenSSF Scorecard, and nothing here reads the classes inside it;
//!   `org.slf4j:slf4j-api:2.0.16`'s POM with its `.sha1` and `.asc` (T9, fetched
//!   2026-10-24; the signer's key is in `provenance/tests/fixtures/maven/`);
//! * `crates/`: `cfg-if` from index.crates.io (the sparse index file,
//!   `config.json`) and `cfg-if-1.0.5.crate` from static.crates.io;
//! * Go (Holger v2.0 H6): a GOPROXY (`@v/<v>.info`, `.mod`, `.zip`) over modules made
//!   here: `example.com/hello`, one whose zip carries a file outside its module, and one
//!   whose `.mod` is not its zip's `go.mod`.
//!
//! What the tests measure: the client's first request is its ecosystem's "not
//! yet" (`503` + `Retry-After`); the package lands INGESTED as
//! `pull-through:<source>`, is scanned (and its provenance verified) at SCANNED
//! and waits at POLICY_CHECKED for a person; the indexes (packument,
//! `maven-metadata.xml`, the sparse index file) list only AVAILABLE versions;
//! after the approval the bytes come from the SEALED store.

// A server's test, never a gateway build's (lane HOL-CLEANUP): a gateway build is a
// downstream, and a pull-through fetches from its sources, which a downstream refuses (holger-downstream-1).
#![cfg(not(feature = "gateway"))]

use std::collections::BTreeMap;
use std::io::{Read, Write};
use std::net::{TcpListener, TcpStream};
use std::path::Path;
use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::{Arc, Mutex};
use std::time::{Duration, Instant};

use holger_server_lib::promote::intake::{
    ArtifactState, EvidenceKind, EvidenceVerdict, IntakeChain, TransitionRequest,
};
use holger_server_lib::pull_intake::provenance::{
    Demand, HolgerProvenance, Material, Outcome, Verdict, Verifier,
};
use holger_server_lib::pull_intake::{crates, maven, npm, Want};
use holger_server_lib::{read_ron_config, wire_holger, Holger};
use sha2::Digest as _;
use traits::test_support::hold_port;

const FX: &str = concat!(env!("CARGO_MANIFEST_DIR"), "/tests/fixtures/pull_through");
const NPM_KEY_ID: &str = "SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U";
const NPM_KEY: &str = "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEY6Ya7W++7aUPzvMTrezH6Ycx3c+HOKYCcNGybJZSCJq/fd7Qa8uuAKtdIkUQtQiEKERhAmE5lMMJhP8OkDOa2g==";
/// npm's key before 2025-01-29 (`/-/npm/v1/keys` still lists it, with that expiry):
/// packages published before then, `is-number 7.0.0` among them, carry its signature.
const NPM_OLD_KEY_ID: &str = "SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA";
const NPM_OLD_KEY: &str = "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE1Olb3zMAFFxXKHiIkQO5cJ3Yhl5i6UPp+IhuteBJbuHcA5UogKo0EWtlWwW6KSaKoTNEYL7JlCQiVnkhBktUgg==";
const GITHUB: &str = "https://token.actions.githubusercontent.com";
const SIGSTORE_JS: &str =
    "https://github.com/sigstore/sigstore-js/.github/workflows/release.yml@refs/heads/main";
const HAMCREST: &str = "org/hamcrest/hamcrest-core/1.3";
const CG_PULL_TOKEN: &str = "a-chainguard-pull-token-for-the-fake";

/// `hamcrest-core-1.3.jar` as the fake Central serves it: a JAR (a zip) with a manifest and
/// a source file, the same bytes every call. The tests follow its bytes through the chain
/// (digest, seal, checksums, the mirror on disk); none of them opens a class.
fn hamcrest_jar() -> Vec<u8> {
    static JAR: std::sync::OnceLock<Vec<u8>> = std::sync::OnceLock::new();
    JAR.get_or_init(|| {
        zip_entry::fixture::zip(&[
            (
                "META-INF/MANIFEST.MF",
                b"Manifest-Version: 1.0\r\nImplementation-Title: hamcrest-core\r\nImplementation-Version: 1.3\r\nCreated-By: holger tests\r\n\r\n",
            ),
            (
                "org/hamcrest/Matcher.java",
                b"package org.hamcrest;\n\npublic interface Matcher<T> extends SelfDescribing {\n    boolean matches(Object item);\n}\n",
            ),
            ("LICENSE.txt", b"BSD License: a fixture made by holger's tests, not Maven Central's bytes.\n"),
        ])
    })
    .clone()
}

/// The fixtures made here instead of read: the JAR and its Central checksums.
fn made(p: &str) -> Option<Vec<u8>> {
    let jar = format!("maven/{HAMCREST}/hamcrest-core-1.3.jar");
    if p == jar {
        return Some(hamcrest_jar());
    }
    let algo = p.strip_prefix(&jar)?.strip_prefix('.')?;
    maven::digest(algo, &hamcrest_jar())
        .filter(|_| matches!(algo, "sha1" | "md5"))
        .map(String::into_bytes)
}

fn fixture(p: &str) -> Vec<u8> {
    made(p).unwrap_or_else(|| {
        std::fs::read(Path::new(FX).join(p)).unwrap_or_else(|e| panic!("{p}: {e}"))
    })
}

fn fixture_opt(p: &str) -> Option<Vec<u8>> {
    made(p).or_else(|| std::fs::read(Path::new(FX).join(p)).ok())
}

fn sha256_hex(b: &[u8]) -> String {
    hex::encode(sha2::Sha256::digest(b))
}

fn token(who: &str) -> String {
    format!("{who}-token-of-sufficient-length-for-holger")
}

// ── the fake upstream ───────────────────────────────────────────────────────

struct Upstream {
    base: String,
    hits: Arc<Mutex<BTreeMap<String, usize>>>,
}

impl Upstream {
    fn hits(&self, path: &str) -> usize {
        self.hits.lock().unwrap().get(path).copied().unwrap_or(0)
    }
}

struct Reply {
    status: u16,
    ctype: String,
    body: Vec<u8>,
}

fn reply(status: u16, ctype: &str, body: Vec<u8>) -> Reply {
    Reply {
        status,
        ctype: ctype.into(),
        body,
    }
}

fn not_found() -> Reply {
    reply(404, "text/plain", b"not found".to_vec())
}

/// The packument as the registry publishes it, its links on the fake. `signed`
/// false strips the signatures and the attestations (an unsigned package).
fn packument(base: &str, signed: bool) -> Vec<u8> {
    let mut v: serde_json::Value =
        serde_json::from_slice(&fixture("npm/sigstore.packument.json")).unwrap();
    let dist = v.pointer_mut("/versions/5.0.0/dist").unwrap();
    dist["tarball"] = format!("{base}/npm/sigstore/-/sigstore-5.0.0.tgz").into();
    if signed {
        dist["attestations"]["url"] =
            format!("{base}/npm/-/npm/v1/attestations/sigstore@5.0.0").into();
    } else {
        let d = dist.as_object_mut().unwrap();
        d.remove("attestations");
        d.remove("signatures");
    }
    serde_json::to_vec(&v).unwrap()
}

/// A Chainguard-style `.slsa-attestation.json` beside the JAR. Chainguard does not
/// document the file's format and the fixture is not a fetched one: this is an
/// in-toto statement naming the JAR, so the test can see it reach the verifier.
fn cg_attestation() -> Vec<u8> {
    let jar = fixture(&format!("maven/{HAMCREST}/hamcrest-core-1.3.jar"));
    serde_json::to_vec(&serde_json::json!({
        "_type": "https://in-toto.io/Statement/v1",
        "subject": [{ "name": "hamcrest-core-1.3.jar", "digest": { "sha256": sha256_hex(&jar) } }],
        "predicateType": "https://slsa.dev/provenance/v1",
        "predicate": {}
    }))
    .unwrap()
}

/// A two-version npm package of the fake registry (`leftpad-field` 1.0.0 and 2.0.0, `latest`
/// 2.0.0): each tarball made here, the same bytes every time.
fn leftpad_tgz(version: &str) -> Vec<u8> {
    let manifest =
        serde_json::json!({ "name": "leftpad-field", "version": version, "main": "index.js" });
    let files: [(&str, Vec<u8>); 2] = [
        (
            "package/package.json",
            serde_json::to_vec(&manifest).unwrap(),
        ),
        (
            "package/index.js",
            format!("module.exports = {version:?};\n").into_bytes(),
        ),
    ];
    let mut tar = tar::Builder::new(Vec::new());
    for (name, body) in &files {
        let mut h = tar::Header::new_gnu();
        h.set_size(body.len() as u64);
        h.set_mode(0o644);
        h.set_mtime(0);
        h.set_cksum();
        tar.append_data(&mut h, name, body.as_slice()).unwrap();
    }
    let mut gz = flate2::write::GzEncoder::new(Vec::new(), flate2::Compression::default());
    gz.write_all(&tar.into_inner().unwrap()).unwrap();
    gz.finish().unwrap()
}

fn leftpad_packument(base: &str) -> Vec<u8> {
    let version = |v: &str| {
        serde_json::json!({
            "name": "leftpad-field", "version": v, "main": "index.js",
            "dist": {
                "tarball": format!("{base}/npm/leftpad-field/-/leftpad-field-{v}.tgz"),
                "integrity": npm::integrity_of(&leftpad_tgz(v)),
            }
        })
    };
    serde_json::to_vec(&serde_json::json!({
        "name": "leftpad-field",
        "dist-tags": { "latest": "2.0.0" },
        "versions": { "1.0.0": version("1.0.0"), "2.0.0": version("2.0.0") },
    }))
    .unwrap()
}

fn route(base: &str, path: &str, auth: Option<&str>) -> Reply {
    if let Some(p) = path.strip_prefix("/npm/") {
        if p == "leftpad-field" {
            return reply(200, "application/json", leftpad_packument(base));
        }
        if let Some(v) = p
            .strip_prefix("leftpad-field/-/leftpad-field-")
            .and_then(|f| f.strip_suffix(".tgz"))
        {
            return reply(200, "application/octet-stream", leftpad_tgz(v));
        }
        return match p {
            "sigstore" => reply(200, "application/json", packument(base, true)),
            "sigstore/-/sigstore-5.0.0.tgz" => reply(
                200,
                "application/octet-stream",
                fixture("npm/sigstore-5.0.0.tgz"),
            ),
            "-/npm/v1/attestations/sigstore@5.0.0" => reply(
                200,
                "application/json",
                fixture("npm/sigstore-5.0.0.attestations.json"),
            ),
            _ => not_found(),
        };
    }
    if path == "/bare/sigstore" {
        return reply(200, "application/json", packument(base, false));
    }
    if let Some(p) = path.strip_prefix("/maven2/") {
        return fixture_opt(&format!("maven/{p}"))
            .map(|b| reply(200, "application/octet-stream", b))
            .unwrap_or_else(not_found);
    }
    if let Some(p) = path.strip_prefix("/java/") {
        use base64::Engine as _;
        let want = format!(
            "Basic {}",
            base64::engine::general_purpose::STANDARD
                .encode(format!("cg-identity:{CG_PULL_TOKEN}"))
        );
        if auth != Some(want.as_str()) {
            return reply(401, "text/plain", b"pull token required".to_vec());
        }
        if p == format!("{HAMCREST}/hamcrest-core-1.3.slsa-attestation.json") {
            return reply(200, "application/json", cg_attestation());
        }
        return fixture_opt(&format!("maven/{p}"))
            .map(|b| reply(200, "application/octet-stream", b))
            .unwrap_or_else(not_found);
    }
    if let Some(p) = path.strip_prefix("/goproxy/") {
        return go_route(p).unwrap_or_else(not_found);
    }
    if path == "/index/config.json" {
        return reply(
            200,
            "application/json",
            serde_json::json!({ "dl": format!("{base}/crates"), "api": base })
                .to_string()
                .into_bytes(),
        );
    }
    if path == "/index/cf/g-/cfg-if" {
        return reply(200, "text/plain", fixture("crates/cfg-if.index"));
    }
    if path == "/crates/cfg-if/1.0.0/download" {
        return reply(
            200,
            "application/x-tar",
            fixture("crates/cfg-if-1.0.0.crate"),
        );
    }
    if path == "/crates/cfg-if/1.0.5/download" {
        return reply(
            200,
            "application/x-tar",
            fixture("crates/cfg-if-1.0.5.crate"),
        );
    }
    not_found()
}

fn serve_one(mut s: TcpStream, base: &str, hits: &Mutex<BTreeMap<String, usize>>) {
    let _ = s.set_read_timeout(Some(Duration::from_secs(10)));
    let mut buf = Vec::new();
    let mut chunk = [0u8; 4096];
    while !buf.windows(4).any(|w| w == b"\r\n\r\n") {
        match s.read(&mut chunk) {
            Ok(0) | Err(_) => return,
            Ok(n) => buf.extend_from_slice(&chunk[..n]),
        }
    }
    let head = String::from_utf8_lossy(&buf).to_string();
    let mut lines = head.lines();
    let path = lines
        .next()
        .unwrap_or("")
        .split_whitespace()
        .nth(1)
        .unwrap_or("/")
        .to_string();
    let auth = lines
        .find_map(|l| {
            l.strip_prefix("authorization: ")
                .or_else(|| l.strip_prefix("Authorization: "))
        })
        .map(str::to_string);
    let bare = path.split('?').next().unwrap_or("").to_string();
    *hits.lock().unwrap().entry(bare.clone()).or_default() += 1;
    let r = route(base, &bare, auth.as_deref());
    let out = format!(
        "HTTP/1.1 {} X\r\nContent-Length: {}\r\nContent-Type: {}\r\nConnection: close\r\n\r\n",
        r.status,
        r.body.len(),
        r.ctype
    );
    let _ = s.write_all(out.as_bytes());
    let _ = s.write_all(&r.body);
}

fn upstream() -> Upstream {
    let l = TcpListener::bind("127.0.0.1:0").unwrap();
    let base = format!("http://{}", l.local_addr().unwrap());
    let hits: Arc<Mutex<BTreeMap<String, usize>>> = Arc::default();
    let (b2, h2) = (base.clone(), Arc::clone(&hits));
    std::thread::spawn(move || {
        for s in l.incoming().flatten() {
            let (b3, h3) = (b2.clone(), Arc::clone(&h2));
            std::thread::spawn(move || serve_one(s, &b3, &h3));
        }
    });
    Upstream { base, hits }
}

// ── one Holger ──────────────────────────────────────────────────────────────

struct Running {
    holger: Holger,
    _rt: tokio::runtime::Runtime,
    _data: tempfile::TempDir,
    _cfg: tempfile::NamedTempFile,
    http: String,
}

impl Running {
    fn chain(&self) -> Arc<IntakeChain> {
        self.holger.intake().expect("the intake chain")
    }
}

fn start(pull_through: &str) -> Running {
    start_as(pull_through, false)
}

/// A Holger whose production repository is NOT public and whose reads are gated (an estate's
/// private repository): every read needs a credential.
fn start_gated(pull_through: &str) -> Running {
    start_as(pull_through, true)
}

fn start_as(pull_through: &str, gated: bool) -> Running {
    let mut last = String::new();
    for _ in 0..8 {
        let (grpc, http) = (hold_port(), hold_port());
        match try_start(grpc, http, pull_through, gated) {
            Ok(r) => return r,
            Err(e) => last = e,
        }
    }
    panic!("server never started; last error: {last}");
}

fn try_start(grpc: u16, http: u16, pull_through: &str, gated: bool) -> Result<Running, String> {
    let data = tempfile::tempdir().unwrap();
    let d = data.path().display().to_string();
    let repo = |name: &str| {
        format!(
            r#"(ron_name: "{name}", ron_repo_type: "raw", ron_upstreams: [], ron_in: None,
         ron_out: Some((ron_storage_endpoint: "storage", ron_exposed_endpoint: "door")),
         ron_data_dir: Some("{d}/{name}")),"#
        )
    };
    let methods: Vec<String> = ["ida", "bob"]
        .iter()
        .map(|p| {
            format!(
                r#"StaticToken(subject: "{p}", token: Some("{}"))"#,
                token(p)
            )
        })
        .collect();
    let pull_through = pull_through.replace("{data}", &d);
    let ron = format!(
        r#"(
    repositories: [ {incoming} {quarantine} {approved} {production} ],
    exposed_endpoints: [ (ron_name: "door", ron_url: "127.0.0.1:{grpc}", ron_http_url: Some("127.0.0.1:{http}")) ],
    storage_endpoints: [ (ron_name: "storage", ron_storage_type: "znippy", ron_path: "{d}") ],
    audit: (dir: "{d}/audit", enabled: true),
    auth: Some((
        methods: [ {methods} ],
        duties: {{ "ida": [Importer], "bob": [SecurityApprover] }},
        require_auth_for_reads: {gated},
    )),
    promotion: Some((
        ladder_name: "intake",
        stages: [
            (name: "incoming", repo: "incoming"), (name: "quarantine", repo: "quarantine"),
            (name: "approved", repo: "approved"), (name: "production", repo: "production"),
        ],
        intake: Some((
            incoming: "incoming", quarantine: "quarantine", approved: "approved", production: "production",
            rules: (sandbox: off),
            approvals: 1,
            scan: None,
        )),
    )),
    pull_through: Some(({pull_through})),
)"#,
        incoming = repo("incoming"),
        quarantine = repo("quarantine"),
        approved = repo("approved"),
        // HCON-5: an estate build gates reads; the repository clients read is public (as the
        // ONE box feeds an estate's `public_repositories`), the intake stages stay gated.
        production = if gated {
            repo("production")
        } else {
            repo("production").replacen("ron_in: None,", "ron_in: None, visibility: Public,", 1)
        },
        methods = methods.join(", "),
    );
    let mut cfg = tempfile::NamedTempFile::new().unwrap();
    cfg.write_all(ron.as_bytes()).unwrap();
    let mut holger: Holger = read_ron_config(cfg.path()).map_err(|e| format!("config: {e}"))?;
    holger
        .instantiate_backends()
        .map_err(|e| format!("backends: {e}"))?;
    wire_holger(&mut holger).map_err(|e| format!("wire: {e:#}"))?;
    let rt = tokio::runtime::Builder::new_multi_thread()
        .worker_threads(4)
        .enable_all()
        .build()
        .unwrap();
    {
        let _in = rt.enter();
        holger.start().map_err(|e| format!("start: {e:#}"))?;
    }
    let r = Running {
        holger,
        _rt: rt,
        _data: data,
        _cfg: cfg,
        http: format!("127.0.0.1:{http}"),
    };
    for _ in 0..200 {
        if get(&r.http, "/healthz", &[]).status == 200 {
            return Ok(r);
        }
        std::thread::sleep(Duration::from_millis(20));
    }
    Err("never healthy".into())
}

struct Got {
    status: u16,
    headers: BTreeMap<String, String>,
    body: Vec<u8>,
}

impl Got {
    fn text(&self) -> String {
        String::from_utf8_lossy(&self.body).to_string()
    }
    fn json(&self) -> serde_json::Value {
        serde_json::from_slice(&self.body).unwrap_or_else(|e| panic!("{e}: {}", self.text()))
    }
}

fn get(addr: &str, path: &str, headers: &[(&str, &str)]) -> Got {
    let Ok(mut s) = TcpStream::connect(addr) else {
        return Got {
            status: 0,
            headers: BTreeMap::new(),
            body: Vec::new(),
        };
    };
    let mut req = format!("GET {path} HTTP/1.1\r\nHost: {addr}\r\nConnection: close\r\n");
    for (k, v) in headers {
        req.push_str(&format!("{k}: {v}\r\n"));
    }
    req.push_str("\r\n");
    s.write_all(req.as_bytes()).unwrap();
    let mut all = Vec::new();
    let _ = s.read_to_end(&mut all);
    let split = all
        .windows(4)
        .position(|w| w == b"\r\n\r\n")
        .map(|i| i + 4)
        .unwrap_or(all.len());
    let head = String::from_utf8_lossy(&all[..split]).to_string();
    let status = head
        .split_whitespace()
        .nth(1)
        .and_then(|s| s.parse().ok())
        .unwrap_or(0);
    let headers = head
        .lines()
        .skip(1)
        .filter_map(|l| {
            l.split_once(':')
                .map(|(k, v)| (k.trim().to_ascii_lowercase(), v.trim().to_string()))
        })
        .collect();
    Got {
        status,
        headers,
        body: all[split..].to_vec(),
    }
}

fn wait_state(chain: &IntakeChain, id: &traits::ArtifactId, want: ArtifactState) {
    let t = Instant::now();
    while t.elapsed() < Duration::from_secs(60) {
        if chain.state(id) == want {
            return;
        }
        std::thread::sleep(Duration::from_millis(50));
    }
    let jobs = chain.pull().map(|p| p.debug_records()).unwrap_or_default();
    panic!(
        "{} never reached {want}; it stands at {} ({:#?}); pull records: {jobs}",
        id.name,
        chain.state(id),
        chain.history(id)
    );
}

/// A GET once the pull job is done: while the job still runs, the answer is 503.
fn get_settled(addr: &str, path: &str) -> Got {
    let t = Instant::now();
    loop {
        let g = get(addr, path, &[]);
        if g.status != 503 || t.elapsed() > Duration::from_secs(60) {
            return g;
        }
        std::thread::sleep(Duration::from_millis(50));
    }
}

fn approve(chain: &IntakeChain, id: &traits::ArtifactId) {
    chain
        .advance(
            TransitionRequest::new(id.clone(), ArtifactState::Approved, "bob")
                .with_reason("reviewed"),
        )
        .unwrap_or_else(|e| panic!("bob approves {}: {e}", id.name));
}

/// The walk a pull takes before a person: INGESTED, SCANNED, POLICY_CHECKED, all as
/// `pull-through:<source>`.
fn assert_walked_as(chain: &IntakeChain, id: &traits::ArtifactId, source: &str) {
    let h = chain.history(id);
    let states: Vec<ArtifactState> = h.iter().map(|e| e.to).collect();
    assert_eq!(
        states,
        [
            ArtifactState::Ingested,
            ArtifactState::Scanned,
            ArtifactState::PolicyChecked
        ],
        "{h:#?}"
    );
    assert!(
        h.iter()
            .all(|e| e.actor == format!("pull-through:{source}")),
        "{h:#?}"
    );
    assert!(
        h[0].evidence
            .iter()
            .any(|e| e.kind == EvidenceKind::Malware && e.source == "holger-inspect"),
        "{:?}",
        h[0].evidence
    );
}

/// The real verifier (`holger-provenance`), counted, with Sigstore's public-good
/// trust root and the floor's keys.
struct Real {
    calls: AtomicUsize,
    names: Mutex<Vec<String>>,
    inner: HolgerProvenance,
}

impl Real {
    fn new(keys: Vec<holger_provenance::TrustedKey>) -> Self {
        let root = std::fs::read(concat!(
            env!("CARGO_MANIFEST_DIR"),
            "/../../provenance/tests/fixtures/sigstore/trusted_root.json"
        ))
        .unwrap();
        let trust = holger_provenance::TrustRoot::from_trusted_root_json(&root).unwrap();
        let mut inner = HolgerProvenance::new(Some(Arc::new(trust)));
        inner.keys = keys;
        Self {
            calls: AtomicUsize::new(0),
            names: Mutex::default(),
            inner,
        }
    }
}

impl Verifier for Real {
    fn verify(&self, subject: &[u8], materials: &[Material], demand: &Demand) -> Outcome {
        self.calls.fetch_add(1, Ordering::SeqCst);
        self.names
            .lock()
            .unwrap()
            .extend(materials.iter().map(|m| m.name.clone()));
        self.inner.verify(subject, materials, demand)
    }
}

fn npm_key() -> holger_provenance::TrustedKey {
    holger_provenance::TrustedKey {
        id: NPM_KEY_ID.into(),
        alg: holger_provenance::KeyAlg::EcdsaP256,
        public_key: NPM_KEY.into(),
    }
}

// ── npm ─────────────────────────────────────────────────────────────────────

fn npm_sources(up: &Upstream) -> String {
    format!(
        r#"sources: [ (name: "npmjs", kind: npm, url: "{base}/npm/",
                       gates: (provenance: Some(require(identity: Some((issuer: "{GITHUB}", subject: Some("{SIGSTORE_JS}"))))))),
                     (name: "bare", kind: npm, url: "{base}/bare/") ],
           rules: (require_signed: true, keys: [(id: "{NPM_KEY_ID}", alg: ecdsa_p256, public_key: "{NPM_KEY}")]),
           retry_after_secs: Some(1),"#,
        base = up.base
    )
}

#[test]
fn an_npm_package_with_provenance_is_pulled_verified_held_and_served_from_the_sealed_store() {
    let up = upstream();
    let r = start(&npm_sources(&up));
    let chain = r.chain();
    let real = Arc::new(Real::new(vec![npm_key()]));
    chain.pull().unwrap().replace_verifier(real.clone());

    // 1. npm's first request: the packument is not yet there; a pull is on its way.
    let first = get(
        &r.http,
        "/production/npmjs/sigstore",
        &[("Accept", "application/json")],
    );
    assert_eq!(first.status, 503, "{}", first.text());
    assert!(
        first.headers.contains_key("retry-after"),
        "{:?}",
        first.headers
    );
    assert!(
        first.json()["error"]
            .as_str()
            .unwrap()
            .contains("pulling it into its intake chain"),
        "{}",
        first.text()
    );

    // 2. `latest` (5.0.0) lands INGESTED as pull-through:npmjs, its provenance and
    //    the registry's signature verified OFFLINE at SCANNED, and waits for a person.
    let id = npm::id("npmjs", "sigstore", "5.0.0");
    wait_state(&chain, &id, ArtifactState::PolicyChecked);
    assert_walked_as(&chain, &id, "npmjs");
    let tgz = fixture("npm/sigstore-5.0.0.tgz");
    assert_eq!(chain.history(&id)[0].sha256, sha256_hex(&tgz));
    let prov = chain.history(&id)[1]
        .evidence
        .iter()
        .find(|e| e.kind == EvidenceKind::Provenance)
        .cloned()
        .expect("a provenance slot");
    assert_eq!(
        (prov.source.as_str(), prov.verdict),
        ("holger-provenance", EvidenceVerdict::Pass),
        "{prov:?}"
    );
    assert!(
        prov.reference.contains("3 verified"),
        "the SLSA provenance, the publish attestation and the registry signature: {}",
        prov.reference
    );
    let names = real.names.lock().unwrap().clone();
    assert!(
        names
            .iter()
            .any(|n| n == "sigstore-5.0.0.tgz.registry-signatures.json"),
        "{names:?}"
    );
    assert!(
        names
            .iter()
            .any(|n| n == "sigstore-5.0.0.tgz.attestations.json"),
        "{names:?}"
    );

    // 3. While it waits, the packument and the tarball say so, and serve nothing.
    // The packument lists it DEPRECATED with its state (npm installs no range onto it).
    let held = get_settled(&r.http, "/production/npmjs/sigstore");
    assert_eq!(held.status, 200, "{}", held.text());
    assert!(
        held.json()["versions"]["5.0.0"]["deprecated"]
            .as_str()
            .unwrap()
            .contains("waiting for approval"),
        "{}",
        held.text()
    );
    assert_eq!(
        get(
            &r.http,
            "/production/npmjs/sigstore/-/sigstore-5.0.0.tgz",
            &[]
        )
        .status,
        404
    );

    // 4. Approved: the packument lists the AVAILABLE version, its integrity the sealed
    //    bytes', its tarball under Holger; the tarball is the sealed bytes.
    approve(&chain, &id);
    let doc = get(&r.http, "/production/npmjs/sigstore", &[]);
    assert_eq!(doc.status, 200, "{}", doc.text());
    let doc = doc.json();
    assert_eq!(doc["dist-tags"]["latest"], "5.0.0", "{doc}");
    let versions: Vec<&String> = doc["versions"].as_object().unwrap().keys().collect();
    assert_eq!(versions, ["5.0.0"]);
    let dist = &doc["versions"]["5.0.0"]["dist"];
    assert_eq!(dist["integrity"], npm::integrity_of(&tgz));
    assert_eq!(
        dist["tarball"],
        format!(
            "http://{}/production/npmjs/sigstore/-/sigstore-5.0.0.tgz",
            r.http
        )
    );
    assert_eq!(
        doc["versions"]["5.0.0"]["dependencies"]["@sigstore/core"], "^4.0.0",
        "the manifest is the tarball's own"
    );
    let t = get(
        &r.http,
        "/production/npmjs/sigstore/-/sigstore-5.0.0.tgz",
        &[],
    );
    assert_eq!(t.status, 200, "{}", t.text());
    assert_eq!(
        t.headers.get("x-holger-intake-state").map(String::as_str),
        Some("AVAILABLE")
    );
    assert_eq!(t.body, tgz);
    assert_eq!(
        up.hits("/npm/sigstore/-/sigstore-5.0.0.tgz"),
        1,
        "the upstream gave the tarball once"
    );
    assert_eq!(up.hits("/npm/-/npm/v1/attestations/sigstore@5.0.0"), 1);
}

#[test]
fn an_npm_package_without_signatures_is_refused_by_the_floor() {
    let up = upstream();
    let r = start(&npm_sources(&up));
    let chain = r.chain();
    let real = Arc::new(Real::new(vec![npm_key()]));
    chain.pull().unwrap().replace_verifier(real.clone());
    assert_eq!(get(&r.http, "/production/bare/sigstore", &[]).status, 503);
    let id = npm::id("bare", "sigstore", "5.0.0");
    wait_state(&chain, &id, ArtifactState::Blocked);
    let last = chain.history(&id).last().cloned().unwrap();
    assert!(
        last.reason
            .contains("no signature or attestation came with it"),
        "{}",
        last.reason
    );
    assert_eq!(
        real.calls.load(Ordering::SeqCst),
        0,
        "refused before the verifier"
    );
    let denied = get_settled(&r.http, "/production/bare/sigstore");
    assert_eq!(denied.status, 403, "{}", denied.text());
    assert!(
        denied.json()["error"].as_str().unwrap().contains("refused"),
        "{}",
        denied.text()
    );
}

// ── Maven ───────────────────────────────────────────────────────────────────

fn maven_sources(up: &Upstream) -> String {
    format!(
        r#"sources: [ (name: "central", kind: maven, url: "{base}/maven2/"),
                     (name: "cg-java", kind: maven, url: "{base}/java/", gates: (provenance: Some(prefer)), wait_secs: Some(0),
                      credential: Some((username: "cg-identity", password_file: Some("{{data}}/cg-token")))) ],
           rules: (require_signed: false),
           retry_after_secs: Some(1),"#,
        base = up.base
    )
}

#[test]
fn a_maven_jar_and_its_pom_are_pulled_against_their_checksums_and_served_after_approval() {
    let up = upstream();
    let r = start_with_token(&maven_sources(&up));
    let chain = r.chain();
    let pom_path = format!("{HAMCREST}/hamcrest-core-1.3.pom");
    let jar_path = format!("{HAMCREST}/hamcrest-core-1.3.jar");

    // 1. Maven asks for the POM first. Maven before 3.9.5 does not retry a 503 (3.9.5 and
    //    later retry it three times; measured, HOL-DOCFIND i), so Holger holds
    //    the request while the POM and the JAR are pulled together, then says where
    //    they stand: waiting for a person (404, the state named).
    let first = get(&r.http, &format!("/production/central/{pom_path}"), &[]);
    assert_eq!(first.status, 404, "{}", first.text());
    assert!(
        first.text().contains("waiting for approval"),
        "{}",
        first.text()
    );
    let (pom, jar) = (
        maven::id("central", &pom_path),
        maven::id("central", &jar_path),
    );
    wait_state(&chain, &pom, ArtifactState::PolicyChecked);
    wait_state(&chain, &jar, ArtifactState::PolicyChecked);
    assert_walked_as(&chain, &jar, "central");
    assert_eq!(
        chain.history(&jar)[0].sha256,
        sha256_hex(&fixture(&format!("maven/{jar_path}")))
    );
    assert!(
        up.hits(&format!("/maven2/{jar_path}.sha1")) >= 1,
        "the JAR was held against its .sha1"
    );

    // 2. Held: 404, the state named; metadata lists nothing yet.
    let held = get_settled(&r.http, &format!("/production/central/{jar_path}"));
    assert_eq!(held.status, 404, "{}", held.text());
    assert!(
        held.text().contains("waiting for approval"),
        "{}",
        held.text()
    );

    // 3. Approved: the files, their checksums from the sealed bytes, and metadata
    //    over the AVAILABLE versions only (Central's lists 1.1 to 3.0).
    approve(&chain, &pom);
    approve(&chain, &jar);
    let j = get(&r.http, &format!("/production/central/{jar_path}"), &[]);
    assert_eq!(j.status, 200, "{}", j.text());
    assert_eq!(j.body, fixture(&format!("maven/{jar_path}")));
    assert_eq!(
        j.headers.get("content-type").map(String::as_str),
        Some("application/java-archive")
    );
    let sha1 = get(
        &r.http,
        &format!("/production/central/{jar_path}.sha1"),
        &[],
    );
    assert_eq!(sha1.status, 200, "{}", sha1.text());
    assert_eq!(
        sha1.text(),
        String::from_utf8(fixture(&format!("maven/{jar_path}.sha1")))
            .unwrap()
            .trim()
    );
    let md5 = get(&r.http, &format!("/production/central/{pom_path}.md5"), &[]);
    assert_eq!(
        md5.text(),
        String::from_utf8(fixture(&format!("maven/{pom_path}.md5")))
            .unwrap()
            .trim()
    );
    let meta = get(
        &r.http,
        "/production/central/org/hamcrest/hamcrest-core/maven-metadata.xml",
        &[],
    );
    assert_eq!(meta.status, 200, "{}", meta.text());
    assert!(
        meta.text().contains("<version>1.3</version>")
            && meta.text().contains("<release>1.3</release>"),
        "{}",
        meta.text()
    );
    assert!(
        !meta.text().contains("<version>3.0</version>"),
        "only AVAILABLE versions: {}",
        meta.text()
    );
}

/// Chainguard Libraries for Java: the pull token goes to the source, and the
/// `.slsa-attestation.json` beside the JAR reaches the provenance verifier.
#[test]
fn a_chainguard_java_jar_carries_its_slsa_attestation_to_the_verifier() {
    let up = upstream();
    let r = start_with_token(&maven_sources(&up));
    let chain = r.chain();
    #[derive(Default)]
    struct Seen(Mutex<Vec<(String, Vec<String>)>>);
    impl Verifier for Seen {
        fn verify(&self, subject: &[u8], materials: &[Material], _: &Demand) -> Outcome {
            self.0.lock().unwrap().push((
                sha256_hex(subject),
                materials.iter().map(|m| m.name.clone()).collect(),
            ));
            Outcome {
                verdict: Verdict::Pass,
                detail: "seen".into(),
                code: None,
                verified: materials.len(),
            }
        }
    }
    let seen = Arc::new(Seen::default());
    chain.pull().unwrap().replace_verifier(seen.clone());
    let jar_path = format!("{HAMCREST}/hamcrest-core-1.3.jar");
    assert_eq!(
        get(&r.http, &format!("/production/cg-java/{jar_path}"), &[]).status,
        503
    );
    let jar = maven::id("cg-java", &jar_path);
    wait_state(&chain, &jar, ArtifactState::PolicyChecked);
    assert_walked_as(&chain, &jar, "cg-java");
    let jar_sha = sha256_hex(&fixture(&format!("maven/{jar_path}")));
    let seen = seen.0.lock().unwrap().clone();
    let (_, names) = seen
        .iter()
        .find(|(sha, _)| *sha == jar_sha)
        .expect("the verifier saw the JAR");
    assert_eq!(
        names,
        &["hamcrest-core-1.3.slsa-attestation.json"],
        "{seen:?}"
    );
    assert_eq!(
        up.hits(&format!(
            "/java/{HAMCREST}/hamcrest-core-1.3.slsa-attestation.json"
        )),
        1
    );
}

/// T9: Maven Central's `.asc` beside a POM is fetched with it and verified
/// OFFLINE against the OpenPGP keys the trust root pins, narrowed per source by
/// `require(pgp_keys: [...])`. An unpinned signer is unverified: refused, never AVAILABLE.
#[test]
fn a_maven_central_pom_is_held_to_its_asc_and_the_pinned_pgp_key_per_source() {
    const CEKI: &str = "ceki-60200AC4AE761F1614D6C46766D68DAA073BE985.asc";
    const POVIRK: &str = "povirk-BDB5FA4FE719D787FB3D3197F6D4A1D411E9D1AE.asc";
    let up = upstream();
    let cfg = format!(
        r#"sources: [ (name: "central", kind: maven, url: "{base}/maven2/", gates: (provenance: Some(require(pgp_keys: ["{CEKI}"])))),
                     (name: "apache-only", kind: maven, url: "{base}/maven2/", gates: (provenance: Some(require(pgp_keys: ["{POVIRK}"])))) ],
           rules: (require_signed: false),
           retry_after_secs: Some(1),"#,
        base = up.base
    );
    if !holger_provenance::pgp::BUILT {
        // T9b: a server built without `maven-pgp` refuses the pinned sources at boot, by
        // name, and never pulls through them unpinned.
        let e = try_start(hold_port(), hold_port(), &cfg, false)
            .err()
            .expect("a pgp pin without the feature refuses the boot");
        assert!(
            e.contains("[provenance-pgp-not-built]")
                && e.contains("'central'")
                && e.contains("`maven-pgp`"),
            "{e}"
        );
        assert_eq!(
            up.hits("/maven2/org/slf4j/slf4j-api/2.0.16/slf4j-api-2.0.16.pom"),
            0,
            "nothing was fetched"
        );
        return;
    }
    let r = start(&cfg);
    let chain = r.chain();
    let key = |n: &str| {
        (
            n.to_string(),
            std::fs::read(format!(
                "{}/../../provenance/tests/fixtures/maven/{n}",
                env!("CARGO_MANIFEST_DIR")
            ))
            .unwrap(),
        )
    };
    let trust = holger_provenance::TrustRoot::default()
        .with_pgp_keys(&[key(CEKI), key(POVIRK)])
        .unwrap();
    let real = Arc::new(Real {
        calls: AtomicUsize::new(0),
        names: Mutex::default(),
        inner: HolgerProvenance::new(Some(Arc::new(trust))),
    });
    chain.pull().unwrap().replace_verifier(real.clone());
    let pom_path = "org/slf4j/slf4j-api/2.0.16/slf4j-api-2.0.16.pom";

    // 1. Signed by Ceki's key, which this source pins: verified, waits for a person.
    assert_eq!(
        get(&r.http, &format!("/production/central/{pom_path}"), &[]).status,
        404
    );
    let pom = maven::id("central", pom_path);
    wait_state(&chain, &pom, ArtifactState::PolicyChecked);
    let prov = chain.history(&pom)[1]
        .evidence
        .iter()
        .find(|e| e.kind == EvidenceKind::Provenance)
        .cloned()
        .expect("a provenance slot");
    assert_eq!(
        (prov.source.as_str(), prov.verdict),
        ("holger-provenance", EvidenceVerdict::Pass),
        "{prov:?}"
    );
    assert!(prov.reference.contains("1 verified"), "{}", prov.reference);
    assert!(
        real.names
            .lock()
            .unwrap()
            .iter()
            .any(|n| n == "slf4j-api-2.0.16.pom.asc"),
        "{:?}",
        real.names.lock().unwrap()
    );
    assert!(
        up.hits(&format!("/maven2/{pom_path}.asc")) >= 1,
        "the .asc was fetched beside the POM"
    );

    // 2. The same bytes from a source that pins only another key: unverified, refused.
    assert_ne!(
        get(&r.http, &format!("/production/apache-only/{pom_path}"), &[]).status,
        200
    );
    let other = maven::id("apache-only", pom_path);
    wait_state(&chain, &other, ArtifactState::Blocked);
    let last = chain.history(&other).last().cloned().unwrap();
    assert!(
        last.reason.contains("holger-provenance-4"),
        "{}",
        last.reason
    );
}

fn start_with_token(pt: &str) -> Running {
    // The pull token is read from a file at boot; the config names `{data}/cg-token`.
    let dir = tempfile::tempdir().unwrap();
    let file = dir.path().join("cg-token");
    std::fs::write(&file, CG_PULL_TOKEN).unwrap();
    let pt = pt.replace("{data}/cg-token", &file.display().to_string());
    let r = start(&pt);
    std::mem::forget(dir);
    r
}

// ── cargo ───────────────────────────────────────────────────────────────────

fn crates_sources(up: &Upstream, signed: bool) -> String {
    format!(
        r#"sources: [ (name: "crates-io", kind: crates, url: "sparse+{}/index/") ], rules: (require_signed: {signed}), retry_after_secs: Some(1),"#,
        up.base
    )
}

#[test]
fn a_crate_is_pulled_against_its_index_checksum_and_the_index_lists_only_available_versions() {
    let up = upstream();
    let r = start(&crates_sources(&up, false));
    let chain = r.chain();

    // cargo reads config.json first: downloads come from Holger.
    let cfg = get(&r.http, "/production/crates-io/config.json", &[]);
    assert_eq!(cfg.status, 200, "{}", cfg.text());
    assert_eq!(
        cfg.json()["dl"],
        format!(
            "http://{}/production/crates-io/{{crate}}/{{crate}}-{{version}}.crate",
            r.http
        )
    );

    // The index file: not yet; the newest live release (1.0.5) is pulled.
    let first = get(&r.http, "/production/crates-io/cf/g-/cfg-if", &[]);
    assert_eq!(first.status, 503, "{}", first.text());
    assert!(first.headers.contains_key("retry-after"));
    let id = crates::id("crates-io", "cfg-if", "1.0.5");
    wait_state(&chain, &id, ArtifactState::PolicyChecked);
    assert_walked_as(&chain, &id, "crates-io");
    let crate_bytes = fixture("crates/cfg-if-1.0.5.crate");
    assert_eq!(chain.history(&id)[0].sha256, sha256_hex(&crate_bytes));
    // Held: nothing AVAILABLE, so every line is yanked (cargo resolves nothing new onto it).
    let held = get_settled(&r.http, "/production/crates-io/cf/g-/cfg-if");
    assert_eq!(held.status, 200, "{}", held.text());
    assert!(
        held.text().lines().all(|l| l.contains("\"yanked\":true")),
        "held: nothing AVAILABLE is installable: {}",
        held.text()
    );

    approve(&chain, &id);
    let index = get(&r.http, "/production/crates-io/cf/g-/cfg-if", &[]);
    assert_eq!(index.status, 200, "{}", index.text());
    // The AVAILABLE version is the one installable line; the registry's others ride yanked.
    let lines: Vec<serde_json::Value> = index
        .text()
        .lines()
        .map(|l| serde_json::from_str::<serde_json::Value>(l).unwrap())
        .filter(|l| l["yanked"] == false)
        .collect();
    assert_eq!(
        lines.len(),
        1,
        "only the AVAILABLE version is installable (crates.io lists eleven): {}",
        index.text()
    );
    assert_eq!(lines[0]["vers"], "1.0.5");
    assert_eq!(lines[0]["cksum"], sha256_hex(&crate_bytes));
    assert_eq!(
        lines[0]["deps"][0]["package"], "rustc-std-workspace-core",
        "the registry's own line"
    );
    let c = get(
        &r.http,
        "/production/crates-io/cfg-if/cfg-if-1.0.5.crate",
        &[],
    );
    assert_eq!(c.status, 200, "{}", c.text());
    assert_eq!(c.body, crate_bytes);
    assert_eq!(up.hits("/crates/cfg-if/1.0.5/download"), 1);
}

/// ★ **A gated crates source says `auth-required`** (HOL-DOCFIND b): on a Holger whose reads
/// are gated (an estate), cargo reads `config.json` with no token, is refused 401 and asks
/// again with its token; the answer must say `"auth-required": true`, or cargo never sends the
/// token for the index files and the `.crate`s, and every read after `config.json` is a 401.
/// A public source says nothing of it (cargo then sends no token at all).
#[test]
fn a_gated_crates_source_says_auth_required_and_a_public_one_does_not() {
    let up = upstream();
    let r = start_gated(&crates_sources(&up, false));
    let anon = get(&r.http, "/production/crates-io/config.json", &[]);
    assert_eq!(anon.status, 401, "a gated read: {}", anon.text());
    assert!(
        anon.headers.contains_key("www-authenticate"),
        "cargo is told to ask again with its token"
    );
    // cargo's spelling: the token verbatim, no scheme.
    let tok = token("bob");
    let cfg = get(
        &r.http,
        "/production/crates-io/config.json",
        &[("Authorization", tok.as_str())],
    );
    assert_eq!(cfg.status, 200, "{}", cfg.text());
    assert_eq!(
        cfg.json()["auth-required"],
        serde_json::json!(true),
        "a gated source tells cargo to send its token: {}",
        cfg.text()
    );
    let index = get(&r.http, "/production/crates-io/cf/g-/cfg-if", &[]);
    assert_eq!(
        index.status,
        401,
        "the index is gated too: {}",
        index.text()
    );
    drop(r);

    let open = start(&crates_sources(&up, false));
    let cfg = get(&open.http, "/production/crates-io/config.json", &[]);
    assert_eq!(cfg.status, 200, "{}", cfg.text());
    assert!(
        cfg.json().get("auth-required").is_none(),
        "a public source asks no token: {}",
        cfg.text()
    );
}

/// A verifier that holds each caller until a second one arrives (or 3 s pass), so two pulls
/// of one coordinate meet inside the INGESTED step: the interleave the field hit when cargo
/// asked for a crate's index and its download at once (lane HOL-FIELD-2).
#[derive(Default)]
struct Rendezvous {
    arrived: Mutex<usize>,
    cv: std::sync::Condvar,
}

impl Verifier for Rendezvous {
    fn verify(&self, _: &[u8], _: &[Material], _: &Demand) -> Outcome {
        let mut n = self.arrived.lock().unwrap();
        *n += 1;
        self.cv.notify_all();
        let t = Instant::now();
        while *n < 2 && t.elapsed() < Duration::from_secs(3) {
            n = self
                .cv
                .wait_timeout(n, Duration::from_millis(100))
                .unwrap()
                .0;
        }
        Outcome {
            verdict: Verdict::Pass,
            detail: "met the other pull".into(),
            code: None,
            verified: 0,
        }
    }
}

/// Every file called `name` under `dir`, read.
fn files_named(dir: &Path, name: &str) -> Vec<String> {
    let mut out = Vec::new();
    let Ok(rd) = std::fs::read_dir(dir) else {
        return out;
    };
    for e in rd.flatten() {
        let p = e.path();
        if p.is_dir() {
            out.extend(files_named(&p, name));
        } else if p.file_name().is_some_and(|f| f == name) {
            out.push(std::fs::read_to_string(&p).unwrap_or_default());
        }
    }
    out
}

/// ★ **HOL-DOGFOOD-8: every pull and every closure walk reads ONE shared index.** On the
/// oden dogfood Holger, `cargo fetch` of gunnar drove ~1000 crate pulls at once; each walk
/// read every dependency's sparse-index file from the upstream again, held its own copy (all
/// versions, raw JSON, cloned per edge) and appended the whole listing to
/// `crates-listings.jsonl` again, which every index request then read and parsed whole: 40 GB
/// in 30 s. Now three pulls of one crate read its index from the upstream ONCE, and its
/// listing is recorded once.
#[test]
fn three_pulls_of_one_crate_read_its_upstream_index_once() {
    let up = upstream();
    let r = start(&crates_sources(&up, false));
    let chain = r.chain();
    let pull = chain.pull().expect("the pull-through").clone();
    for w in [
        Want::CrateName {
            name: "cfg-if".into(),
        },
        Want::CrateVersion {
            name: "cfg-if".into(),
            version: "1.0.5".into(),
        },
        Want::CrateName {
            name: "cfg-if".into(),
        },
    ] {
        pull.run(&chain, "crates-io", &w)
            .unwrap_or_else(|e| panic!("{w:?}: {e:#}"));
    }
    assert_eq!(
        up.hits("/index/cf/g-/cfg-if"),
        1,
        "the upstream index file is read once and shared by every pull"
    );
    let listings = files_named(r._data.path(), "crates-listings.jsonl").join("");
    assert_eq!(
        listings
            .lines()
            .filter(|l| l.contains("\"cfg-if\""))
            .count(),
        1,
        "the listing is recorded once per upstream read, not once per pull: {listings}"
    );
}

/// **Two pulls of one crate at once never block it** (lane HOL-FIELD-2): cargo asks for the
/// index file (the newest release) and the download (that version) together, two jobs drive
/// the same coordinate, and the one that lost the race used to BLOCK it for good with
/// "'SCANNED' → 'SCANNED' does not move forward" (ryu 1.0.23 in the field). The loser walks
/// on from where the winner left it.
#[test]
fn two_pulls_of_one_crate_at_once_never_block_it() {
    let up = upstream();
    let r = start(&format!(
        r#"sources: [ (name: "crates-io", kind: crates, url: "sparse+{}/index/", gates: (provenance: Some(prefer))) ], retry_after_secs: Some(1),"#,
        up.base
    ));
    let chain = r.chain();
    let pull = chain.pull().expect("the pull-through").clone();
    pull.replace_verifier(Arc::new(Rendezvous::default()));
    let wants = [
        Want::CrateName {
            name: "cfg-if".into(),
        },
        Want::CrateVersion {
            name: "cfg-if".into(),
            version: "1.0.5".into(),
        },
    ];
    let lines: Vec<String> = std::thread::scope(|s| {
        let runs: Vec<_> = wants
            .iter()
            .map(|w| {
                let (chain, pull) = (chain.clone(), pull.clone());
                s.spawn(move || {
                    pull.run(&chain, "crates-io", w)
                        .unwrap_or_else(|e| format!("{e:#}"))
                })
            })
            .collect();
        runs.into_iter().map(|h| h.join().unwrap()).collect()
    });
    let id = crates::id("crates-io", "cfg-if", "1.0.5");
    assert_eq!(
        chain.state(&id),
        ArtifactState::PolicyChecked,
        "both pulls end at POLICY_CHECKED, waiting for a person: {lines:?} {:#?}",
        chain.history(&id)
    );
}

#[test]
fn an_unsigned_crate_is_refused_where_the_floor_requires_a_signature() {
    let up = upstream();
    let r = start(&crates_sources(&up, true));
    let chain = r.chain();
    assert_eq!(
        get(&r.http, "/production/crates-io/cf/g-/cfg-if", &[]).status,
        503
    );
    let id = crates::id("crates-io", "cfg-if", "1.0.5");
    wait_state(&chain, &id, ArtifactState::Blocked);
    // The refused version is never on the index; its `.crate` says why.
    let index = get_settled(&r.http, "/production/crates-io/cf/g-/cfg-if");
    assert!(
        !index.text().contains("\"vers\":\"1.0.5\""),
        "a refused version is never listed: {}",
        index.text()
    );
    let denied = get_settled(&r.http, "/production/crates-io/cfg-if/cfg-if-1.0.5.crate");
    assert_eq!(denied.status, 403, "{}", denied.text());
    assert!(
        denied
            .text()
            .contains("no signature or attestation came with it"),
        "{}",
        denied.text()
    );
}

// ── an invalidation from the upstream (H4) ───────────────────────────────────
//
// `IntakeChain::invalidate` is what an applied sync manifest does to each package it
// invalidates (`server_lib::sync`): the package goes to QUARANTINED at once, its
// bytes stay in the sealed store, every index drops it, a read is refused with the
// reason, and no release brings it back.

const INVALIDATED: &str =
    "invalidated by upstream 'dmz' (generation 2): malware: found after its approval";

fn invalidate(chain: &IntakeChain, id: &traits::ArtifactId) {
    use holger_server_lib::promote::intake::InvalidateOutcome;
    match chain
        .invalidate(id, "holger-sync:dmz", "sync", INVALIDATED)
        .unwrap_or_else(|e| panic!("invalidate {}: {e}", id.name))
    {
        InvalidateOutcome::Held(e) => assert_eq!(e.to, ArtifactState::Quarantined, "{e:?}"),
        other => panic!("{}: {other:?}", id.name),
    }
    assert_eq!(chain.state(id), ArtifactState::Quarantined);
    // Again: nothing more happens.
    assert!(matches!(
        chain
            .invalidate(id, "holger-sync:dmz", "sync", INVALIDATED)
            .unwrap(),
        InvalidateOutcome::AlreadyHeld(ArtifactState::Quarantined)
    ));
    // No release brings it back; only a Block moves it on.
    let back = chain.advance(
        TransitionRequest::new(id.clone(), ArtifactState::Available, "bob").with_reason("release"),
    );
    assert!(
        back.is_err(),
        "a release of an invalidated package is refused"
    );
    assert!(
        back.unwrap_err().to_string().contains("invalidated"),
        "the refusal names the invalidation"
    );
}

#[test]
fn an_invalidated_maven_version_drops_from_its_metadata_and_is_refused() {
    let up = upstream();
    let r = start_with_token(&maven_sources(&up));
    let chain = r.chain();
    let pom_path = format!("{HAMCREST}/hamcrest-core-1.3.pom");
    let jar_path = format!("{HAMCREST}/hamcrest-core-1.3.jar");
    get(&r.http, &format!("/production/central/{pom_path}"), &[]);
    let (pom, jar) = (
        maven::id("central", &pom_path),
        maven::id("central", &jar_path),
    );
    wait_state(&chain, &pom, ArtifactState::PolicyChecked);
    wait_state(&chain, &jar, ArtifactState::PolicyChecked);
    approve(&chain, &pom);
    approve(&chain, &jar);
    assert_eq!(
        get(&r.http, &format!("/production/central/{jar_path}"), &[]).status,
        200
    );
    let meta = "/production/central/org/hamcrest/hamcrest-core/maven-metadata.xml";
    assert!(get(&r.http, meta, &[])
        .text()
        .contains("<version>1.3</version>"));

    invalidate(&chain, &pom);
    invalidate(&chain, &jar);
    let j = get_settled(&r.http, &format!("/production/central/{jar_path}"));
    assert_eq!(j.status, 403, "{}", j.text());
    assert!(
        j.text().contains("invalidated by upstream 'dmz'"),
        "the refusal names the reason: {}",
        j.text()
    );
    let m = get_settled(&r.http, meta);
    assert!(
        !m.text().contains("<version>1.3</version>"),
        "maven-metadata.xml drops it: {} {}",
        m.status,
        m.text()
    );
    // Quarantine, not delete: the record and the sealed bytes stay.
    let h = chain.history(&jar);
    assert_eq!(h.last().unwrap().reason, INVALIDATED);
    assert_eq!(h.last().unwrap().from, ArtifactState::Available);
    // Audited on the artifact's trail.
    let audit = std::fs::read_dir(r._data.path().join("audit"))
        .unwrap()
        .flatten()
        .map(|e| std::fs::read_to_string(e.path()).unwrap_or_default())
        .collect::<String>();
    assert!(
        audit.contains("\"invalidated\"") && audit.contains(&h[0].sha256),
        "the trail carries the invalidated step"
    );
}

#[test]
fn an_invalidated_crate_drops_from_the_sparse_index_and_is_refused() {
    let up = upstream();
    let r = start(&crates_sources(&up, false));
    let chain = r.chain();
    get(&r.http, "/production/crates-io/cf/g-/cfg-if", &[]);
    let id = crates::id("crates-io", "cfg-if", "1.0.5");
    wait_state(&chain, &id, ArtifactState::PolicyChecked);
    approve(&chain, &id);
    assert_eq!(
        get(&r.http, "/production/crates-io/cf/g-/cfg-if", &[]).status,
        200
    );

    invalidate(&chain, &id);
    let index = get_settled(&r.http, "/production/crates-io/cf/g-/cfg-if");
    assert!(
        !index.text().contains("\"yanked\":false") && !index.text().contains("\"vers\":\"1.0.5\""),
        "the sparse index lists no AVAILABLE version and never the invalidated one: {}",
        index.text()
    );
    assert!(
        !index.text().contains("\"vers\":\"1.0.5\""),
        "{}",
        index.text()
    );
    let c = get_settled(&r.http, "/production/crates-io/cfg-if/cfg-if-1.0.5.crate");
    assert_eq!(c.status, 403, "{}", c.text());
    assert!(c.text().contains("invalidated"), "{}", c.text());
}

#[test]
fn an_invalidated_npm_version_drops_from_the_packument_and_is_refused() {
    let up = upstream();
    let pt = format!(
        r#"sources: [ (name: "bare", kind: npm, url: "{}/bare/") ], rules: (require_signed: false), retry_after_secs: Some(1),"#,
        up.base
    );
    let r = start(&pt);
    let chain = r.chain();
    get(
        &r.http,
        "/production/bare/sigstore",
        &[("Accept", "application/json")],
    );
    let id = npm::id("bare", "sigstore", "5.0.0");
    wait_state(&chain, &id, ArtifactState::PolicyChecked);
    approve(&chain, &id);
    let p = get_settled(&r.http, "/production/bare/sigstore");
    assert_eq!(p.status, 200, "{}", p.text());
    assert!(p.json()["versions"]["5.0.0"].is_object());

    invalidate(&chain, &id);
    let p = get_settled(&r.http, "/production/bare/sigstore");
    assert_ne!(p.status, 200, "{}", p.text());
    assert!(
        !p.text().contains("\"5.0.0\":{"),
        "the packument drops the version: {}",
        p.text()
    );
    let t = get_settled(&r.http, "/production/bare/sigstore/-/sigstore-5.0.0.tgz");
    assert_eq!(t.status, 403, "{}", t.text());
}

// ── real clients against the real registries (`--ignored`; online) ─────────────
//
// `cargo test --features jobs,online,supply-chain --test pull_through_npm_maven_crates -- --ignored`
// (T9's `.asc` test needs `maven-pgp` too: `--features jobs,online,supply-chain,maven-pgp`; without it,
// it checks the boot's refusal by name)
// in a container that has `npm`, `mvn` and `cargo` on its PATH and reaches
// registry.npmjs.org, repo1.maven.org and index.crates.io. A person (bob) approves
// every pulled coordinate as it reaches POLICY_CHECKED, as a reviewer would.

/// Approve every pull-through coordinate at POLICY_CHECKED until dropped.
struct Approver {
    stop: Arc<std::sync::atomic::AtomicBool>,
    approved: Arc<Mutex<Vec<String>>>,
    join: Option<std::thread::JoinHandle<()>>,
}

impl Approver {
    fn start(chain: Arc<IntakeChain>) -> Self {
        let stop = Arc::new(std::sync::atomic::AtomicBool::new(false));
        let approved: Arc<Mutex<Vec<String>>> = Arc::default();
        let (s2, a2) = (Arc::clone(&stop), Arc::clone(&approved));
        let join = std::thread::spawn(move || {
            while !s2.load(Ordering::SeqCst) {
                for (id, st) in chain.all() {
                    if st.state == ArtifactState::PolicyChecked
                        && chain
                            .advance(
                                TransitionRequest::new(id.clone(), ArtifactState::Approved, "bob")
                                    .with_reason("reviewed"),
                            )
                            .is_ok()
                    {
                        a2.lock().unwrap().push(id.name.clone());
                    }
                }
                std::thread::sleep(Duration::from_millis(50));
            }
        });
        Self {
            stop,
            approved,
            join: Some(join),
        }
    }
    fn approved(&self) -> Vec<String> {
        self.approved.lock().unwrap().clone()
    }
}

impl Drop for Approver {
    fn drop(&mut self) {
        self.stop.store(true, Ordering::SeqCst);
        if let Some(j) = self.join.take() {
            let _ = j.join();
        }
    }
}

fn run(cmd: &mut std::process::Command) -> std::process::Output {
    let out = cmd.output().unwrap_or_else(|e| panic!("{cmd:?}: {e}"));
    eprintln!(
        "{cmd:?}\n--- stdout\n{}\n--- stderr\n{}",
        String::from_utf8_lossy(&out.stdout),
        String::from_utf8_lossy(&out.stderr)
    );
    out
}

#[test]
#[ignore = "a real npm against registry.npmjs.org through Holger"]
fn real_client_npm_install_through_holger() {
    let pt = format!(
        r#"sources: [ (name: "npmjs", kind: npm, url: "https://registry.npmjs.org/") ],
           rules: (require_signed: true, keys: [
               (id: "{NPM_KEY_ID}", alg: ecdsa_p256, public_key: "{NPM_KEY}"),
               (id: "{NPM_OLD_KEY_ID}", alg: ecdsa_p256, public_key: "{NPM_OLD_KEY}"),
           ]),
           retry_after_secs: Some(1),"#
    );
    let r = start(&pt);
    let approver = Approver::start(r.chain());
    let dir = tempfile::tempdir().unwrap();
    std::fs::write(
        dir.path().join("package.json"),
        r#"{"name":"pt2-demo","version":"1.0.0","dependencies":{"is-number":"7.0.0"}}"#,
    )
    .unwrap();
    std::fs::write(
        dir.path().join(".npmrc"),
        format!("registry=http://{}/production/npmjs/\n", r.http),
    )
    .unwrap();
    let out = run(std::process::Command::new("npm")
        .args([
            "install",
            "--no-audit",
            "--no-fund",
            "--ignore-scripts",
            "--fetch-retries=6",
            "--fetch-retry-mintimeout=1000",
            "--fetch-retry-maxtimeout=3000",
            "--cache",
        ])
        .arg(dir.path().join("cache"))
        .current_dir(dir.path()));
    assert!(out.status.success(), "npm install failed");
    let installed = std::fs::read(dir.path().join("node_modules/is-number/package.json")).unwrap();
    assert!(String::from_utf8_lossy(&installed).contains("\"version\": \"7.0.0\""));
    assert!(
        approver
            .approved()
            .iter()
            .any(|n| n == "npmjs/is-number/-/is-number-7.0.0.tgz"),
        "{:?}",
        approver.approved()
    );
    // It came through the chain: the registry's signature verified at SCANNED.
    let id = npm::id("npmjs", "is-number", "7.0.0");
    let h = r.chain().history(&id);
    let prov = h
        .iter()
        .flat_map(|e| e.evidence.iter())
        .find(|e| e.kind == EvidenceKind::Provenance)
        .cloned()
        .expect("a provenance slot");
    assert_eq!(prov.verdict, EvidenceVerdict::Pass, "{prov:?}");
}

#[test]
#[ignore = "a real mvn against Maven Central through Holger"]
fn real_client_mvn_resolves_through_holger() {
    let pt = r#"sources: [ (name: "central", kind: maven, url: "https://repo1.maven.org/maven2/") ],
           rules: (require_signed: false), retry_after_secs: Some(1),"#;
    let r = start(pt);
    let approver = Approver::start(r.chain());
    let dir = tempfile::tempdir().unwrap();
    let holger = format!("http://{}/production/central/", r.http);
    std::fs::write(
        dir.path().join("pom.xml"),
        format!(
            r#"<project xmlns="http://maven.apache.org/POM/4.0.0"><modelVersion>4.0.0</modelVersion>
  <groupId>pt2</groupId><artifactId>demo</artifactId><version>1.0</version>
  <repositories><repository><id>central</id><url>{holger}</url></repository></repositories>
  <pluginRepositories><pluginRepository><id>central</id><url>https://repo1.maven.org/maven2/</url></pluginRepository></pluginRepositories>
  <dependencies><dependency><groupId>org.hamcrest</groupId><artifactId>hamcrest-core</artifactId><version>1.3</version></dependency></dependencies>
</project>"#
        ),
    )
    .unwrap();
    let repo = dir.path().join("m2");
    // Maven before 3.9.5 does not retry a 503 (3.9.5 and later retry it three times, honouring
    // Retry-After; measured, HOL-DOCFIND i); Holger holds a Maven request (bounded) until its
    // pull has walked as far as policy lets it, and a version that waits for a person
    // is a 404 Maven caches. So each level of the tree (the POM, its parent) takes one
    // run after the approval, as it would for a developer: `mvn -U` again.
    let mut ok = false;
    for _ in 0..6 {
        let out = run(std::process::Command::new("mvn")
            .args(["-B", "-q", "-U", "dependency:resolve"])
            .arg(format!("-Dmaven.repo.local={}", repo.display()))
            .current_dir(dir.path()));
        if out.status.success() {
            ok = true;
            break;
        }
        std::thread::sleep(Duration::from_millis(500));
    }
    assert!(ok, "mvn never resolved through Holger");
    let jar = repo.join("org/hamcrest/hamcrest-core/1.3/hamcrest-core-1.3.jar");
    assert_eq!(
        std::fs::read(&jar).unwrap(),
        fixture(&format!("maven/{HAMCREST}/hamcrest-core-1.3.jar"))
    );
    let names = approver.approved();
    for want in [
        "central/org/hamcrest/hamcrest-core/1.3/hamcrest-core-1.3.jar",
        "central/org/hamcrest/hamcrest-parent/1.3/hamcrest-parent-1.3.pom",
    ] {
        assert!(
            names.iter().any(|n| n == want),
            "{want} not pulled through the chain: {names:?}"
        );
    }
}

#[test]
#[ignore = "a real cargo against index.crates.io through Holger"]
fn real_client_cargo_fetch_through_holger() {
    let pt = r#"sources: [ (name: "crates-io", kind: crates, url: "sparse+https://index.crates.io/") ],
           rules: (require_signed: false), retry_after_secs: Some(1),"#;
    let r = start(pt);
    let approver = Approver::start(r.chain());
    let dir = tempfile::tempdir().unwrap();
    let home = dir.path().join("cargo-home");
    std::fs::create_dir_all(home.join("..").join("demo/src")).unwrap();
    let demo = dir.path().join("demo");
    std::fs::write(demo.join("Cargo.toml"), "[package]\nname = \"demo\"\nversion = \"0.1.0\"\nedition = \"2021\"\n\n[dependencies]\ncfg-if = \"1\"\n").unwrap();
    std::fs::write(demo.join("src/lib.rs"), "").unwrap();
    std::fs::create_dir_all(&home).unwrap();
    std::fs::write(
        home.join("config.toml"),
        format!(
            "[source.crates-io]\nreplace-with = \"holger\"\n[source.holger]\nregistry = \"sparse+http://{}/production/crates-io/\"\n[net]\nretry = 10\n",
            r.http
        ),
    )
    .unwrap();
    let cargo = std::env::var("CARGO").unwrap_or_else(|_| "cargo".into());
    let out = run(std::process::Command::new(cargo)
        .args(["fetch"])
        .env("CARGO_HOME", &home)
        .current_dir(&demo));
    assert!(out.status.success(), "cargo fetch failed");
    let names = approver.approved();
    assert!(
        names
            .iter()
            .any(|n| n.starts_with("crates-io/cfg-if/cfg-if-1.")),
        "{names:?}"
    );
    let lock = std::fs::read_to_string(demo.join("Cargo.lock")).unwrap();
    assert!(lock.contains("name = \"cfg-if\""), "{lock}");
}

// ── Go modules from a GOPROXY (Holger v2.0 H6) ──────────────────────────────

const GO_MOD: &str = "module example.com/hello\n\ngo 1.21\n";

fn go_module_zip(module: &str, version: &str, extra: Option<&str>) -> Vec<u8> {
    let prefix = format!("{module}@{version}");
    let go_mod = format!("module {module}\n\ngo 1.21\n");
    let (m, src) = (format!("{prefix}/go.mod"), format!("{prefix}/hello.go"));
    let mut entries: Vec<(&str, &[u8])> = vec![
        (&m, go_mod.as_bytes()),
        (
            &src,
            b"package hello\n\nfunc Hello() string { return \"hi\" }\n",
        ),
    ];
    if let Some(e) = extra {
        entries.push((e, b"package other\n"));
    }
    zip_entry::fixture::zip(&entries)
}

/// The fake GOPROXY: `hello` is honest, `evil`'s zip carries a file outside its module,
/// `liar`'s `.mod` is not its zip's `go.mod`.
fn go_route(p: &str) -> Option<Reply> {
    let (module, file) = p.split_once("/@v/")?;
    let name = module.strip_prefix("example.com/")?;
    let (version, ext) = file.rsplit_once('.')?;
    if version != "v1.0.0" || !["hello", "evil", "liar"].contains(&name) {
        return None;
    }
    let extra = (name == "evil").then_some("example.com/other@v1.0.0/x.go");
    Some(match ext {
        "info" => reply(
            200,
            "application/json",
            br#"{"Version":"v1.0.0","Time":"2026-10-01T00:00:00Z"}"#.to_vec(),
        ),
        "mod" if name == "liar" => reply(
            200,
            "text/plain",
            b"module example.com/liar\n\ngo 1.21\n\nrequire example.com/x v9.9.9\n".to_vec(),
        ),
        "mod" => reply(
            200,
            "text/plain",
            format!("module {module}\n\ngo 1.21\n").into_bytes(),
        ),
        "zip" => reply(
            200,
            "application/zip",
            go_module_zip(module, version, extra),
        ),
        _ => return None,
    })
}

fn go_sources(up: &Upstream) -> String {
    format!(
        r#"sources: [ (name: "go", kind: go, url: "{}/goproxy/") ], rules: (require_signed: false), retry_after_secs: Some(1),"#,
        up.base
    )
}

fn go_id(module: &str) -> traits::ArtifactId {
    traits::ArtifactId {
        namespace: None,
        name: format!("go/{module}/@v/v1.0.0.zip"),
        version: String::new(),
    }
}

#[test]
fn a_go_module_is_pulled_from_a_goproxy_held_for_a_person_and_served_from_the_sealed_store() {
    let up = upstream();
    let r = start(&go_sources(&up));
    let chain = r.chain();
    let zip = "/production/go/example.com/hello/@v/v1.0.0.zip";

    // The first ask is "not yet": the module version is pulled into the chain.
    let first = get(&r.http, zip, &[]);
    assert_eq!(first.status, 503, "{}", first.text());
    assert!(first.headers.contains_key("retry-after"));
    let id = go_id("example.com/hello");
    wait_state(&chain, &id, ArtifactState::PolicyChecked);
    assert_walked_as(&chain, &id, "go");
    let bytes = go_module_zip("example.com/hello", "v1.0.0", None);
    assert_eq!(
        chain.history(&id)[0].sha256,
        sha256_hex(&bytes),
        "the zip as the proxy served it"
    );
    assert_eq!(
        get_settled(&r.http, zip).status,
        404,
        "held: it waits for a person"
    );
    assert_eq!(
        get(&r.http, "/production/go/example.com/hello/@v/list", &[]).text(),
        "",
        "nothing AVAILABLE to list"
    );

    // After the approval every GOPROXY read is answered from the sealed zip.
    approve(&chain, &id);
    let z = get(&r.http, zip, &[]);
    assert_eq!((z.status, z.body == bytes), (200, true), "{}", z.text());
    assert_eq!(
        get(
            &r.http,
            "/production/go/example.com/hello/@v/v1.0.0.mod",
            &[]
        )
        .text(),
        GO_MOD
    );
    assert_eq!(
        get(
            &r.http,
            "/production/go/example.com/hello/@v/v1.0.0.info",
            &[]
        )
        .json()["Version"],
        "v1.0.0"
    );
    assert_eq!(
        get(&r.http, "/production/go/example.com/hello/@v/list", &[]).text(),
        "v1.0.0\n"
    );
    assert_eq!(
        get(&r.http, "/production/go/example.com/hello/@latest", &[]).json()["Version"],
        "v1.0.0"
    );
    assert_eq!(
        (
            up.hits("/goproxy/example.com/hello/@v/v1.0.0.zip"),
            up.hits("/goproxy/example.com/hello/@v/v1.0.0.mod")
        ),
        (1, 1),
        "pulled once"
    );
}

/// H4 + H6: an invalidation from the upstream reaches the Go index too. The version drops
/// from `@v/list` and `@latest`, and its `.zip`, `.mod` and `.info` are refused with the
/// reason; the sealed bytes stay.
#[test]
fn an_invalidated_go_module_version_drops_from_list_and_latest_and_is_refused() {
    let up = upstream();
    let r = start(&go_sources(&up));
    let chain = r.chain();
    let base = "/production/go/example.com/hello/@v";
    assert_eq!(get(&r.http, &format!("{base}/v1.0.0.zip"), &[]).status, 503);
    let id = go_id("example.com/hello");
    wait_state(&chain, &id, ArtifactState::PolicyChecked);
    approve(&chain, &id);
    assert_eq!(get(&r.http, &format!("{base}/v1.0.0.zip"), &[]).status, 200);
    assert_eq!(
        get(&r.http, &format!("{base}/list"), &[]).text(),
        "v1.0.0\n"
    );

    invalidate(&chain, &id);
    let list = get_settled(&r.http, &format!("{base}/list"));
    assert!(
        !list.text().contains("v1.0.0"),
        "@v/list drops it: {} {}",
        list.status,
        list.text()
    );
    let latest = get_settled(&r.http, "/production/go/example.com/hello/@latest");
    assert_ne!(
        latest.status,
        200,
        "@latest names no invalidated version: {}",
        latest.text()
    );
    for f in ["v1.0.0.zip", "v1.0.0.mod", "v1.0.0.info"] {
        let a = get_settled(&r.http, &format!("{base}/{f}"));
        assert_eq!(a.status, 403, "{f}: {}", a.text());
        assert!(
            a.text().contains("invalidated by upstream 'dmz'"),
            "{f}: the refusal names the reason: {}",
            a.text()
        );
    }
    assert_eq!(
        chain.history(&id).last().unwrap().reason,
        INVALIDATED,
        "quarantined, not deleted"
    );
}

#[test]
fn a_go_module_zip_with_a_file_outside_its_module_is_refused_and_never_stored() {
    let up = upstream();
    let r = start(&go_sources(&up));
    let path = "/production/go/example.com/evil/@v/v1.0.0.zip";
    assert_eq!(get(&r.http, path, &[]).status, 503);
    let denied = get_settled(&r.http, path);
    assert_eq!(denied.status, 403, "{}", denied.text());
    assert!(
        denied.text().contains("example.com/other@v1.0.0/x.go")
            && denied.text().contains("outside"),
        "{}",
        denied.text()
    );
    assert!(
        r.chain().status(&go_id("example.com/evil")).is_none(),
        "never ingested"
    );
}

#[test]
fn a_go_module_whose_mod_is_not_its_zips_go_mod_is_refused() {
    let up = upstream();
    let r = start(&go_sources(&up));
    let path = "/production/go/example.com/liar/@v/v1.0.0.mod";
    assert_eq!(get(&r.http, path, &[]).status, 503);
    let denied = get_settled(&r.http, path);
    assert_eq!(denied.status, 403, "{}", denied.text());
    assert!(denied.text().contains("go.mod"), "{}", denied.text());
    assert!(
        r.chain().status(&go_id("example.com/liar")).is_none(),
        "never ingested"
    );
}

/// **A pinned npm version that is not `latest` pulls through** (lane HOL-FIELD-TEST, npm half of
/// P2). In the field `npm install lodash@4.17.15` ended in "No matching version": the packument
/// pull took only `latest`, and the packument lists only AVAILABLE versions, so npm never asked
/// for the version it was told to install. Now every version the registry lists that is not
/// AVAILABLE is in the packument DEPRECATED with the reason (npm prefers a non-deprecated version
/// for a range, so an unpinned install keeps to what is approved), its tarball on Holger's door;
/// an exact pin asks for that tarball, which pulls exactly that version into the chain.
#[test]
fn a_pinned_npm_version_that_is_not_latest_is_listed_deprecated_and_pulled_when_asked() {
    let up = upstream();
    let r = start(&format!(
        r#"sources: [ (name: "npmjs", kind: npm, url: "{}/npm/") ], rules: (require_signed: false), retry_after_secs: Some(1),"#,
        up.base
    ));
    let chain = r.chain();
    let accept = [("Accept", "application/json")];

    // 1. The first ask pulls `latest`; then the packument lists every version.
    assert_eq!(
        get(&r.http, "/production/npmjs/leftpad-field", &accept).status,
        503
    );
    let started = Instant::now();
    let doc = loop {
        let g = get(&r.http, "/production/npmjs/leftpad-field", &accept);
        if g.status == 200 {
            break g.json();
        }
        assert!(
            started.elapsed() < Duration::from_secs(30),
            "never listed: {}",
            g.text()
        );
        std::thread::sleep(Duration::from_millis(100));
    };
    let old = &doc["versions"]["1.0.0"];
    assert!(
        old["deprecated"]
            .as_str()
            .is_some_and(|d| d.contains("not in Holger yet")),
        "a version Holger has not pulled is deprecated, with the reason: {doc}"
    );
    assert_eq!(
        old["dist"]["tarball"],
        format!(
            "http://{}/production/npmjs/leftpad-field/-/leftpad-field-1.0.0.tgz",
            r.http
        ),
        "{doc}"
    );

    // 2. Asking for its tarball pulls exactly that version; it waits for a person.
    let tgz = "/production/npmjs/leftpad-field/-/leftpad-field-1.0.0.tgz";
    assert_eq!(get(&r.http, tgz, &[]).status, 503);
    let id = npm::id("npmjs", "leftpad-field", "1.0.0");
    wait_state(&chain, &id, ArtifactState::PolicyChecked);
    approve(&chain, &id);
    let got = get_settled(&r.http, tgz);
    assert_eq!(got.status, 200, "{}", got.text());
    assert_eq!(got.body, leftpad_tgz("1.0.0"));

    // 3. AVAILABLE now: listed, no longer deprecated.
    let doc = get(&r.http, "/production/npmjs/leftpad-field", &accept).json();
    assert!(
        doc["versions"]["1.0.0"].get("deprecated").is_none(),
        "{doc}"
    );
}

/// **A plugin group's metadata names its prefixes** (lane HOL-FIELD-TEST P13). In the field
/// `mvn dependency:get` through Holger said "No plugin found for prefix 'dependency'": Maven
/// resolves a prefix from the GROUP's `org/apache/maven/plugins/maven-metadata.xml`
/// (`<plugins><plugin><prefix>`), and Holger read that path as the metadata of an artifact
/// `plugins` and answered nothing. The group's metadata is names only (prefix to artifactId):
/// Holger serves the plugin list the source publishes, and every plugin's bytes still come
/// through the chain. The fixture is the real document of Maven Central.
#[test]
fn a_plugin_groups_metadata_names_its_prefixes_for_maven() {
    let up = upstream();
    let r = start_with_token(&maven_sources(&up));
    let got = get_settled(
        &r.http,
        "/production/central/org/apache/maven/plugins/maven-metadata.xml",
    );
    assert_eq!(got.status, 200, "{}", got.text());
    let t = got.text();
    assert!(
        t.contains("<prefix>dependency</prefix>")
            && t.contains("<artifactId>maven-dependency-plugin</artifactId>"),
        "{t}"
    );
    assert!(
        !t.contains("<versions>"),
        "a group's metadata names no versions: {t}"
    );
}

/// **A crate version that is not the newest pulls through from a lock file** (lane
/// HOL-FIELD-TEST, the cargo half of P2). In the field `cfg-if = "=1.0.0"` through Holger said
/// "candidate versions found which didn't match: 1.0.5": the index pull took only the newest,
/// and the index lists only AVAILABLE versions. Now every version the registry lists that is
/// not AVAILABLE is on Holger's index line YANKED: cargo resolves no new requirement onto it,
/// a `Cargo.lock` (or `cargo update --precise`) that names it takes it, and asking for its
/// `.crate` pulls exactly that version into the chain. `cfg-if-1.0.0.crate` is the real one.
#[test]
fn a_locked_crate_version_that_is_not_newest_is_listed_yanked_and_pulled_when_its_crate_is_asked() {
    let up = upstream();
    let r = start(&crates_sources(&up, false));
    let chain = r.chain();
    let index = "/production/crates-io/cf/g-/cfg-if";
    let started = Instant::now();
    let text = loop {
        let g = get(&r.http, index, &[]);
        if g.status == 200 {
            break g.text();
        }
        assert!(
            started.elapsed() < Duration::from_secs(30),
            "never listed: {}",
            g.text()
        );
        std::thread::sleep(Duration::from_millis(100));
    };
    let line = |v: &str, text: &str| -> serde_json::Value {
        text.lines()
            .map(|l| serde_json::from_str::<serde_json::Value>(l).unwrap())
            .find(|l| l["vers"] == v)
            .unwrap_or_else(|| panic!("{v} is on the index: {text}"))
    };
    assert_eq!(line("1.0.0", &text)["yanked"], true, "{text}");

    let crate_path = "/production/crates-io/cfg-if/cfg-if-1.0.0.crate";
    assert_eq!(get(&r.http, crate_path, &[]).status, 503);
    let id = crates::id("crates-io", "cfg-if", "1.0.0");
    wait_state(&chain, &id, ArtifactState::PolicyChecked);
    approve(&chain, &id);
    let got = get_settled(&r.http, crate_path);
    assert_eq!(got.status, 200, "{}", got.text());
    assert_eq!(got.body, fixture("crates/cfg-if-1.0.0.crate"));
    let text = get(&r.http, index, &[]).text();
    assert_eq!(
        line("1.0.0", &text)["yanked"],
        false,
        "AVAILABLE now: {text}"
    );
}