server/lib/tests/every_backend_enumerates_on_the_wire.rs
// stinky 1.4 (2026-09-21): the seventeen backends are optional (`fmt-*`); this
// census needs all of them, and `fmt-all` is in server-lib's `default` (the dev set).
//! **Every shipped backend says it enumerates — ON THE WIRE.**
//!
//! `a_zero_nobody_counted.rs` proves by source that each backend crate that
//! overrides `list` also overrides `supports_listing`. This file proves the
//! other half a consumer actually sees: each of the 17 shipped backends,
//! constructed for real and mapped through the ONE mapping the catalog RPC uses
//! (`holger_server_lib::capacity::repository_info`), carries
//! `RepositoryInfo.supports_listing = true`. Plus the two wrappers holger puts
//! in front of them: a proxy over a backend inherits its answer, and a group
//! over enumerating members answers `true` and names them.
//!
//! Before this field existed, monetize had to carry a `CANNOT_ENUMERATE` list
//! of formats by name. The list is now on the wire, per repository.
use std::sync::Arc;
use holger_server_lib::capacity::repository_info;
use holger_server_lib::group::GroupBackend;
use holger_server_lib::proxy::ProxyBackend;
use traits::RepositoryBackendTrait;
/// The 17 shipped backends, one of each, with no archive loaded — the answer
/// must be a property of the FORMAT, not of whether bytes are present.
fn estate() -> Vec<Arc<dyn RepositoryBackendTrait>> {
let oci_dir = tempfile::tempdir().unwrap();
let rust_dir = tempfile::tempdir().unwrap();
let n = |s: &str| s.to_string();
let all: Vec<Arc<dyn RepositoryBackendTrait>> = vec![
Arc::new(
repository_file_oci::OciRepoFile::new(n("file-oci"), oci_dir.path().to_path_buf())
.unwrap(),
),
Arc::new(repository_file_rust::RustRepoFile::new(
n("file-rust"),
rust_dir.path().to_path_buf(),
"http://127.0.0.1:0".into(),
)),
Arc::new(repository_znippy_build_thing::BuildThingRepoZnippy::new(n(
"build-thing",
))),
Arc::new(repository_znippy_composer::ComposerRepoZnippy::new(n(
"composer",
))),
Arc::new(repository_znippy_conda::CondaRepoZnippy::new(n("conda"))),
Arc::new(repository_znippy_deb::DebRepoZnippy::new(n("deb"))),
Arc::new(repository_znippy_docker::DockerRepoZnippy::new(n("docker"))),
Arc::new(repository_znippy_gem::GemRepoZnippy::new(n("gem"))),
Arc::new(repository_znippy_go::GoRepoZnippy::new(n("go"))),
Arc::new(repository_znippy_helm::HelmRepoZnippy::new(n("helm"))),
Arc::new(repository_znippy_maven::MavenRepoZnippy::new(n("maven"))),
Arc::new(repository_znippy_npm::NpmRepoZnippy::new(n("npm"))),
Arc::new(repository_znippy_nuget::NugetRepoZnippy::new(n("nuget"))),
Arc::new(repository_znippy_package::ZnippyRepoZnippy::new(n(
"package",
))),
Arc::new(repository_znippy_python::PipRepoZnippy::new(n("python"))),
Arc::new(repository_znippy_rpm::RpmRepoZnippy::new(n("rpm"))),
Arc::new(repository_znippy_rust::RustRepoZnippy::new(n("rust"))),
];
// The tempdirs may go: the file backends only need the path to exist at
// construction, and this test never writes.
std::mem::forget(oci_dir);
std::mem::forget(rust_dir);
all
}
#[test]
fn all_seventeen_shipped_backends_carry_supports_listing_on_the_wire() {
let all = estate();
assert_eq!(all.len(), 17, "the shipped estate is 17 backends");
let refusing: Vec<String> = all
.iter()
.map(|b| repository_info(b.name(), b.as_ref()))
.filter(|info| !info.supports_listing)
.map(|info| format!("{} ({})", info.name, info.repo_type))
.collect();
assert!(
refusing.is_empty(),
"backends still refusing to enumerate on the wire: {refusing:?}"
);
for b in &all {
let info = repository_info(b.name(), b.as_ref());
assert!(!info.is_group, "{} owns its storage", info.name);
assert!(info.members.is_empty(), "{} has no members", info.name);
}
}
#[test]
fn a_proxy_inherits_its_primary_answer_and_a_group_names_its_members() {
let all = estate();
let primary = all[10].clone(); // maven
let proxy = ProxyBackend::new(primary, Vec::new());
let info = repository_info("maven-proxied", &proxy);
assert!(
info.supports_listing,
"a proxy over an enumerating primary enumerates"
);
assert!(!info.is_group);
let group = GroupBackend::new("maven-all".into(), vec![all[10].clone(), all[11].clone()]);
let info = repository_info("maven-all", &group);
assert!(info.is_group);
assert!(
info.supports_listing,
"a group over enumerating members enumerates"
);
assert_eq!(info.members, vec!["maven", "npm"]);
let empty = GroupBackend::new("nothing".into(), Vec::new());
let info = repository_info("nothing", &empty);
assert!(info.is_group);
assert!(
!info.supports_listing,
"an empty group has no member to enumerate"
);
assert!(info.members.is_empty());
}