Lines
100 %
Functions
Branches
//! Contract tests for the artifact layout.
//!
//! These run on every branch and PR via `cargo test -p xtask`, which matters
//! because the S3 transport they guard is master-only: without them the rules
//! master depends on would first execute after a merge.
use super::*;
const DIGEST: &str = "@sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
fn identity(run_id: &str, attempt: &str) -> Identity {
Identity {
commit: "abc123".into(),
run_id: run_id.into(),
run_attempt: attempt.into(),
builder: format!("registry.example/nomisync-builder:1.96.1{DIGEST}"),
}
fn meta(kind: Kind, sha: &str, identity: Identity) -> Meta {
Meta {
kind,
sha256: sha.into(),
bytes: 10,
identity,
#[test]
fn prefix_is_commit_then_run_id() {
assert_eq!(
identity("77", "1").prefix("buck"),
"s3://buck/abc123/77",
"the prefix is what makes cross-run and cross-commit mixing unreachable"
);
fn different_bytes_give_a_different_object_key() {
let a = meta(Kind::Coverage, "deadbeef", identity("77", "1")).object_key();
let b = meta(Kind::Coverage, "cafebabe", identity("77", "1")).object_key();
assert_ne!(
a, b,
"content addressing is what makes an overwrite-with-different-bytes impossible"
fn meta_round_trips() {
let original = meta(Kind::Release, "beef", identity("77", "2"));
let parsed = Meta::from_text(&original.to_text()).expect("meta should parse");
assert_eq!(parsed, original);
fn meta_is_parsed_not_evaluated() {
// A meta is fetched from object storage. The shell version had to avoid
// sourcing it; here the equivalent mistake is not expressible, and this
// pins that a shell-ish payload survives as inert text.
let text = "kind=coverage\nobject=$(touch /tmp/pwned).tar\nsha256=dead\nbytes=1\n\
commit=abc123\nrun_id=77\nrun_attempt=1\nbuilder=x@sha256:y\n";
let parsed = Meta::from_text(text).expect("meta should parse");
assert_eq!(parsed.sha256, "dead");
fn meta_missing_a_field_is_an_error() {
let text = "kind=coverage\nsha256=dead\nbytes=1\n";
assert!(
Meta::from_text(text).is_err(),
"an incomplete meta must not parse"
fn failed_only_rerun_is_a_valid_pair() {
// Coverage passed in attempt 1, release failed and was re-run as attempt 2.
// Same commit, same run, same builder — usable. Refusing this would force a
// ~35 minute coverage rebuild because an unrelated job flaked.
let coverage = meta(Kind::Coverage, "dead", identity("77", "1"));
let release = meta(Kind::Release, "beef", identity("77", "2"));
assert_eq!(check_pair(&coverage, &release), Ok(()));
fn artifacts_from_different_runs_are_refused() {
let release = meta(Kind::Release, "beef", identity("78", "1"));
assert!(matches!(
check_pair(&coverage, &release),
Err(PairError::Run { .. })
));
fn artifacts_from_different_commits_are_refused() {
let mut other = identity("77", "1");
other.commit = "def456".into();
let release = meta(Kind::Release, "beef", other);
Err(PairError::Commit { .. })
fn artifacts_from_different_builder_images_are_refused() {
// A mid-run image change must not produce a mixed-toolchain pair.
other.builder = format!("registry.example/nomisync-builder:1.96.2{DIGEST}");
Err(PairError::Builder { .. })
/// Environment as a map, so these never touch the real process environment.
fn env(pairs: &[(&str, &str)]) -> impl Fn(&str) -> Option<String> + use<> {
let owned: Vec<(String, String)> = pairs
.iter()
.map(|(k, v)| ((*k).to_owned(), (*v).to_owned()))
.collect();
move |key| owned.iter().find(|(k, _)| k == key).map(|(_, v)| v.clone())
fn identity_requires_a_digest_pinned_builder() {
// Guards the reason the digest pin exists: a tag can be moved, so it cannot
// prove two producers ran the same image.
let err = Identity::from_source(env(&[
("SHA", "abc123"),
("GITHUB_RUN_ID", "77"),
("RUST_BUILDER_IMAGE_REF", "registry.example/builder:1.96.1"),
]))
.expect_err("a tag-only builder ref must be refused");
assert!(format!("{err}").contains("not digest-pinned"), "{err}");
fn identity_refuses_a_missing_run_id() {
(
"RUST_BUILDER_IMAGE_REF",
"registry.example/builder:1.96.1@sha256:aa",
),
.expect_err("a missing run number must not default");
assert!(format!("{err}").contains("GITHUB_RUN_ID"), "{err}");
fn identity_refuses_an_empty_run_id() {
// ci-build forwards this undefaulted, so empty is the shape a missing value
// actually arrives in.
("GITHUB_RUN_ID", ""),
.expect_err("an empty run number must not be accepted");
fn identity_accepts_a_complete_environment() {
let identity = Identity::from_source(env(&[
("GITHUB_RUN_ATTEMPT", "2"),
.expect("complete identity should be accepted");
assert_eq!(identity.run_attempt, "2");
fn a_missing_attempt_degrades_rather_than_failing() {
// The attempt is provenance detail, not a path component, so a platform
// that does not supply it must still be able to publish.
.expect("a missing attempt must not be fatal");
assert_eq!(identity.run_attempt, "unknown");