facett-ops/tests/pep503_layout.rs
//! **RED FIRST for row 1b.** The layout inside holger's archive is the contract
//! between what we pack and what `pip install` can find. Get it wrong and the index
//! resolves, returns a link, and 404s — which reads to a user as "the server is
//! broken", not "the wheel is in the wrong folder".
//!
//! The headline case is `normalize_name`. The shell script this replaces did:
//!
//! ```sh
//! normalize() { echo "$1" | tr '[:upper:]' '[:lower:]' | tr '_.' '--'; }
//! ```
//!
//! `tr` maps character-for-character and **cannot collapse a run**, but PEP 503 is
//! `re.sub(r"[-_.]+", "-", name).lower()` — a run of separators becomes ONE dash.
//! For `facet-wrapped-web`, the only name that script was ever run with, the two
//! agree. For anything with a doubled separator they do not, and the wheel lands in
//! a directory `pip` will never ask for.
//!
//! That is the shape worth testing: a function that is correct for the one input
//! someone tried it with.
use std::path::PathBuf;
use facett_ops::holger::{archive_path_for, distribution_of, normalize_name, stage_index};
// ── PEP 503 normalization, including what `tr` could not do ────────────────
#[test]
fn normalization_collapses_runs_which_is_where_the_shell_was_wrong() {
// THE case. `tr '_.' '--'` gives `facet--wrapped`; PEP 503 gives `facet-wrapped`.
assert_eq!(normalize_name("facet__wrapped"), "facet-wrapped");
assert_eq!(normalize_name("a..b"), "a-b");
assert_eq!(normalize_name("a-_.b"), "a-b");
assert_eq!(normalize_name("zope.interface"), "zope-interface");
// ...and a run at the edges is still one dash, not several.
assert_eq!(normalize_name("__lead"), "-lead");
assert_eq!(normalize_name("trail..."), "trail-");
}
#[test]
fn normalization_lowercases() {
assert_eq!(normalize_name("Facet-Wrapped-Web"), "facet-wrapped-web");
assert_eq!(normalize_name("PyYAML"), "pyyaml");
}
#[test]
fn the_name_we_actually_ship_normalizes_to_what_pip_asks_for() {
// The wheel's filename carries `facet_wrapped_web`; a user types
// `pip install facet-wrapped-web`. These must meet.
assert_eq!(normalize_name("facet_wrapped_web"), "facet-wrapped-web");
assert_eq!(normalize_name("facet-wrapped-web"), "facet-wrapped-web");
}
#[test]
fn normalization_is_idempotent() {
// holger normalizes the incoming REQUEST too. If normalizing twice moved the
// name, our directory and its lookup would disagree on the second hop.
for n in ["facet_wrapped_web", "A__b.C", "already-normal", "trail..."] {
let once = normalize_name(n);
assert_eq!(normalize_name(&once), once, "`{n}` is not idempotent");
}
}
// ── reading the distribution out of an artifact filename ───────────────────
#[test]
fn a_wheel_and_its_sdist_land_in_the_same_directory() {
// They MUST: `/simple/facet-wrapped-web/` lists both, and pip picks. Two
// directories would make the sdist invisible.
let whl = archive_path_for("facet_wrapped_web-0.1.19-py3-none-any.whl").unwrap();
let sdist = archive_path_for("facet_wrapped_web-0.1.19.tar.gz").unwrap();
assert_eq!(whl, "packages/facet-wrapped-web/facet_wrapped_web-0.1.19-py3-none-any.whl");
assert_eq!(sdist, "packages/facet-wrapped-web/facet_wrapped_web-0.1.19.tar.gz");
assert_eq!(
whl.rsplit_once('/').unwrap().0,
sdist.rsplit_once('/').unwrap().0,
"the wheel and the sdist must share one directory"
);
}
#[test]
fn the_filename_itself_is_never_normalized() {
// Only the DIRECTORY is normalized. The filename is the artifact's own name and
// pip matches it against the wheel tags — rewriting it would make the download
// link name a file that does not exist.
let p = archive_path_for("facet_wrapped_web-0.1.19-py3-none-any.whl").unwrap();
assert!(p.ends_with("facet_wrapped_web-0.1.19-py3-none-any.whl"), "{p}");
}
#[test]
fn a_file_that_is_not_an_artifact_is_refused_not_mislaid() {
for bad in ["README.md", "robot_results.json", "facet_wrapped_web-0.1.19.zip", ".whl"] {
assert!(
archive_path_for(bad).is_none(),
"`{bad}` must be refused, not packed somewhere pip will never look"
);
}
assert_eq!(distribution_of("facet_wrapped_web-0.1.19.tar.gz").as_deref(), Some("facet_wrapped_web"));
}
// ── the staged tree on disk ────────────────────────────────────────────────
fn scratch(tag: &str) -> PathBuf {
let base = std::env::var("CARGO_TARGET_TMPDIR")
.map(PathBuf::from)
.unwrap_or_else(|_| std::env::temp_dir());
let d = base.join(format!("pep503-{tag}"));
let _ = std::fs::remove_dir_all(&d);
std::fs::create_dir_all(&d).unwrap();
d
}
#[test]
fn staging_writes_the_bytes_at_the_served_path() {
let dir = scratch("stage");
let src = dir.join("in");
std::fs::create_dir_all(&src).unwrap();
let whl = src.join("facet_wrapped_web-0.1.19-py3-none-any.whl");
let sdist = src.join("facet_wrapped_web-0.1.19.tar.gz");
std::fs::write(&whl, b"PK\x03\x04wheel-bytes").unwrap();
std::fs::write(&sdist, b"\x1f\x8bsdist-bytes").unwrap();
let stage = dir.join("stage");
let packed = stage_index(&[whl.clone(), sdist.clone()], &stage).unwrap();
assert_eq!(packed.len(), 2);
// Assert the APPLIED output — the bytes at the path holger will serve — not
// that the function returned a list of strings.
let served = stage.join("packages/facet-wrapped-web/facet_wrapped_web-0.1.19-py3-none-any.whl");
assert!(served.is_file(), "{} was not written", served.display());
assert_eq!(std::fs::read(&served).unwrap(), b"PK\x03\x04wheel-bytes");
assert_eq!(
std::fs::read(stage.join("packages/facet-wrapped-web/facet_wrapped_web-0.1.19.tar.gz"))
.unwrap(),
b"\x1f\x8bsdist-bytes"
);
}
#[test]
fn staging_refuses_a_stray_file_rather_than_packing_it() {
let dir = scratch("stray");
let stray = dir.join("notes.txt");
std::fs::write(&stray, b"hello").unwrap();
let err = stage_index(&[stray], &dir.join("stage")).unwrap_err();
assert!(err.contains("neither a wheel nor an sdist"), "{err}");
assert!(err.contains("404"), "the refusal must say what would go wrong: {err}");
}
#[test]
fn staging_nothing_is_an_error_not_an_empty_index() {
// An empty archive would serve a valid, well-formed index of NO packages, and
// `pip install facet-wrapped-web` against it fails with "no matching
// distribution" — a message that points at the client, not at the empty publish
// that caused it.
let dir = scratch("empty");
let err = stage_index(&[], &dir.join("stage")).unwrap_err();
assert!(err.contains("publish-pip"), "the refusal must name the fix: {err}");
}
#[test]
fn staging_is_a_fresh_tree_not_an_accumulating_one() {
// A previous version's wheel left behind would still be listed and still
// installable, so `pip install facet-wrapped-web` could resolve to bytes nobody
// meant to publish. Re-staging must CLEAR.
let dir = scratch("fresh");
let src = dir.join("in");
std::fs::create_dir_all(&src).unwrap();
let old = src.join("facet_wrapped_web-0.1.15-py3-none-any.whl");
let new = src.join("facet_wrapped_web-0.1.19-py3-none-any.whl");
std::fs::write(&old, b"old").unwrap();
std::fs::write(&new, b"new").unwrap();
let stage = dir.join("stage");
stage_index(&[old], &stage).unwrap();
stage_index(&[new], &stage).unwrap();
let served = stage.join("packages/facet-wrapped-web");
let names: Vec<String> = std::fs::read_dir(&served)
.unwrap()
.map(|e| e.unwrap().file_name().to_string_lossy().into_owned())
.collect();
assert_eq!(names, vec!["facet_wrapped_web-0.1.19-py3-none-any.whl"], "0.1.15 survived: {names:?}");
}