use std::ffi::OsString;
use std::path::{Path, PathBuf};
use std::process::Command;
use std::sync::atomic::{AtomicU64, Ordering};
use testing_conventions::e2e::{
attest, verify, verify_extra_scoped, verify_scoped, verify_since, Verification,
};
use testing_conventions::run;
struct TempRepo(PathBuf);
impl TempRepo {
fn new() -> Self {
static COUNTER: AtomicU64 = AtomicU64::new(0);
let root = std::env::temp_dir().join(format!(
"tc-e2e-verify-{}-{}",
std::process::id(),
COUNTER.fetch_add(1, Ordering::Relaxed),
));
std::fs::create_dir_all(&root).unwrap();
git(&root, &["init", "-q"]);
git(&root, &["config", "user.email", "test@example.com"]);
git(&root, &["config", "user.name", "Test"]);
git(&root, &["config", "commit.gpgsign", "false"]);
std::fs::write(root.join("README.md"), "seed\n").unwrap();
git(&root, &["add", "."]);
git(
&root,
&["-c", "commit.gpgsign=false", "commit", "-q", "-m", "seed"],
);
TempRepo(root)
}
fn commit_code(&self, name: &str, contents: &str) {
std::fs::write(self.0.join(name), contents).unwrap();
git(&self.0, &["add", name]);
git(
&self.0,
&["-c", "commit.gpgsign=false", "commit", "-q", "-m", "code"],
);
}
}
impl Drop for TempRepo {
fn drop(&mut self) {
let _ = std::fs::remove_dir_all(&self.0);
}
}
fn git(dir: &Path, args: &[&str]) {
let status = Command::new("git")
.args(args)
.current_dir(dir)
.status()
.expect("git should run");
assert!(status.success(), "git {args:?} failed");
}
fn rev_parse(dir: &Path, rev: &str) -> String {
let out = Command::new("git")
.args(["rev-parse", rev])
.current_dir(dir)
.output()
.expect("git rev-parse should run");
assert!(out.status.success(), "git rev-parse {rev} failed");
String::from_utf8(out.stdout).unwrap().trim().to_string()
}
#[test]
fn verify_passes_when_the_attestation_names_the_latest_code_commit() {
let repo = TempRepo::new();
attest(&repo.0, "true").expect("attest should succeed");
assert_eq!(
verify(&repo.0).expect("verify should succeed"),
Verification::Fresh,
);
}
#[test]
fn verify_fails_when_no_attestation_is_present() {
let repo = TempRepo::new();
assert_eq!(
verify(&repo.0).expect("verify should succeed"),
Verification::Missing,
);
}
#[test]
fn verify_fails_when_code_changed_since_the_attestation() {
let repo = TempRepo::new();
attest(&repo.0, "true").expect("attest should succeed");
let attested = rev_parse(&repo.0, "HEAD^");
repo.commit_code("widget.rs", "pub fn widget() {}\n");
let latest = rev_parse(&repo.0, "HEAD");
assert_eq!(
verify(&repo.0).expect("verify should succeed"),
Verification::Stale { attested, latest },
);
}
#[test]
fn verify_scopes_fresh_to_a_package_subdirectory() {
let repo = TempRepo::new();
let package = repo.0.join("packages/widget");
std::fs::create_dir_all(&package).unwrap();
repo.commit_code("packages/widget/widget.rs", "pub fn widget() {}\n");
attest(&package, "true").expect("attest should succeed");
assert_eq!(
verify(&package).expect("verify should succeed"),
Verification::Fresh,
);
assert_eq!(
verify(&repo.0).expect("verify should succeed"),
Verification::Missing,
);
}
#[test]
fn verify_scopes_stale_to_a_package_subdirectory() {
let repo = TempRepo::new();
let package = repo.0.join("packages/widget");
std::fs::create_dir_all(&package).unwrap();
repo.commit_code("packages/widget/widget.rs", "pub fn widget() {}\n");
attest(&package, "true").expect("attest should succeed");
let attested = rev_parse(&repo.0, "HEAD^");
repo.commit_code("packages/widget/widget2.rs", "pub fn widget2() {}\n");
let latest = rev_parse(&repo.0, "HEAD");
assert_eq!(
verify(&package).expect("verify should succeed"),
Verification::Stale { attested, latest },
);
}
#[test]
fn verify_scopes_missing_to_a_package_subdirectory() {
let repo = TempRepo::new();
let package = repo.0.join("packages/widget");
std::fs::create_dir_all(&package).unwrap();
assert_eq!(
verify(&package).expect("verify should succeed"),
Verification::Missing,
);
}
fn e2e_verify_run(path: &Path) -> anyhow::Result<i32> {
let argv: Vec<OsString> = vec![
"testing-conventions".into(),
"e2e".into(),
"verify".into(),
path.as_os_str().to_owned(),
];
run(argv)
}
#[test]
fn cli_verify_with_path_argument_passes_when_fresh() {
let repo = TempRepo::new();
let package = repo.0.join("packages/widget");
std::fs::create_dir_all(&package).unwrap();
repo.commit_code("packages/widget/widget.rs", "pub fn widget() {}\n");
attest(&package, "true").expect("attest should succeed");
assert_eq!(
e2e_verify_run(&package).expect("dispatch should succeed"),
0,
"a fresh attestation at the given path should pass",
);
}
#[test]
fn cli_verify_with_path_argument_fails_when_missing() {
let repo = TempRepo::new();
let package = repo.0.join("packages/widget");
std::fs::create_dir_all(&package).unwrap();
assert_eq!(
e2e_verify_run(&package).expect("dispatch should succeed"),
1,
"no attestation at the given path should fail",
);
}
#[test]
fn cli_verify_with_path_argument_fails_when_stale() {
let repo = TempRepo::new();
let package = repo.0.join("packages/widget");
std::fs::create_dir_all(&package).unwrap();
repo.commit_code("packages/widget/widget.rs", "pub fn widget() {}\n");
attest(&package, "true").expect("attest should succeed");
repo.commit_code("packages/widget/widget2.rs", "pub fn widget2() {}\n");
assert_eq!(
e2e_verify_run(&package).expect("dispatch should succeed"),
1,
"a stale attestation at the given path should fail",
);
}
#[test]
fn verify_scoped_ignores_a_commit_outside_the_scope_but_inside_the_repo() {
let repo = TempRepo::new();
let package = repo.0.join("packages/widget");
std::fs::create_dir_all(package.join("src")).unwrap();
std::fs::create_dir_all(package.join("tests")).unwrap();
repo.commit_code("packages/widget/src/widget.rs", "pub fn widget() {}\n");
attest(&package, "true").expect("attest should succeed");
repo.commit_code("packages/widget/tests/widget_test.rs", "// test\n");
assert_eq!(
verify_scoped(&package, &package.join("src")).expect("verify should succeed"),
Verification::Fresh,
"a commit outside the scoped directory must not count as code",
);
}
#[test]
fn verify_scoped_still_flags_a_commit_inside_the_scope() {
let repo = TempRepo::new();
let package = repo.0.join("packages/widget");
std::fs::create_dir_all(package.join("src")).unwrap();
repo.commit_code("packages/widget/src/widget.rs", "pub fn widget() {}\n");
attest(&package, "true").expect("attest should succeed");
let attested = rev_parse(&repo.0, "HEAD^");
repo.commit_code("packages/widget/src/widget2.rs", "pub fn widget2() {}\n");
let latest = rev_parse(&repo.0, "HEAD");
assert_eq!(
verify_scoped(&package, &package.join("src")).expect("verify should succeed"),
Verification::Stale { attested, latest },
);
}
#[test]
fn verify_scoped_with_scope_equal_to_repo_matches_verify() {
let repo = TempRepo::new();
attest(&repo.0, "true").expect("attest should succeed");
assert_eq!(
verify_scoped(&repo.0, &repo.0).expect("verify should succeed"),
verify(&repo.0).expect("verify should succeed"),
);
}
fn e2e_verify_run_scoped(path: &Path, scope: Option<&Path>) -> anyhow::Result<i32> {
let mut argv: Vec<OsString> = vec![
"testing-conventions".into(),
"e2e".into(),
"verify".into(),
path.as_os_str().to_owned(),
];
if let Some(scope) = scope {
argv.push("--scope".into());
argv.push(scope.as_os_str().to_owned());
}
run(argv)
}
#[test]
fn cli_verify_with_scope_ignores_a_commit_outside_it() {
let repo = TempRepo::new();
let package = repo.0.join("packages/widget");
std::fs::create_dir_all(package.join("src")).unwrap();
std::fs::create_dir_all(package.join("tests")).unwrap();
repo.commit_code("packages/widget/src/widget.rs", "pub fn widget() {}\n");
attest(&package, "true").expect("attest should succeed");
repo.commit_code("packages/widget/tests/widget_test.rs", "// test\n");
assert_eq!(
e2e_verify_run_scoped(&package, Some(&package.join("src")))
.expect("dispatch should succeed"),
0,
"a fresh attestation should pass when the only new commit is outside --scope",
);
}
#[test]
fn cli_verify_with_no_scope_defaults_to_path_unchanged() {
let repo = TempRepo::new();
let package = repo.0.join("packages/widget");
std::fs::create_dir_all(package.join("src")).unwrap();
repo.commit_code("packages/widget/src/widget.rs", "pub fn widget() {}\n");
attest(&package, "true").expect("attest should succeed");
repo.commit_code("packages/widget/other.rs", "pub fn other() {}\n");
assert_eq!(
e2e_verify_run_scoped(&package, None).expect("dispatch should succeed"),
1,
"with no --scope, a commit anywhere under path should still count as code",
);
}
#[test]
fn cli_verify_with_no_argument_defaults_to_the_current_directory() {
let argv: Vec<OsString> = vec!["testing-conventions".into(), "e2e".into(), "verify".into()];
let code = run(argv).expect("`e2e verify` with no argument should still dispatch");
assert_eq!(code, 1);
}
#[test]
fn verify_since_passes_when_the_branch_introduced_no_scoped_commit() {
let repo = TempRepo::new();
let package = repo.0.join("packages/widget");
std::fs::create_dir_all(package.join("src")).unwrap();
std::fs::create_dir_all(repo.0.join("packages/other")).unwrap();
repo.commit_code("packages/widget/src/widget.rs", "pub fn widget() {}\n");
attest(&package, "true").expect("attest should succeed");
repo.commit_code(
"packages/widget/src/widget.rs",
"pub fn widget() { /* v2 */ }\n",
);
let base = rev_parse(&repo.0, "HEAD");
repo.commit_code("packages/other/thing.rs", "pub fn thing() {}\n");
assert!(
matches!(
verify_scoped(&package, &package.join("src")).unwrap(),
Verification::Stale { .. },
),
"history-absolute freshness should still see this attestation as stale",
);
assert_eq!(
verify_since(&package, &package.join("src"), Some(&base)).unwrap(),
Verification::Fresh,
"a PR that didn't touch the scoped source must pass despite a stale-on-base attestation",
);
}
#[test]
fn verify_since_flags_a_scoped_commit_the_branch_did_not_reattest() {
let repo = TempRepo::new();
let package = repo.0.join("packages/widget");
std::fs::create_dir_all(package.join("src")).unwrap();
repo.commit_code("packages/widget/src/widget.rs", "pub fn widget() {}\n");
attest(&package, "true").expect("attest should succeed");
let attested = rev_parse(&repo.0, "HEAD^");
let base = rev_parse(&repo.0, "HEAD");
repo.commit_code(
"packages/widget/src/widget.rs",
"pub fn widget() { /* v2 */ }\n",
);
let latest = rev_parse(&repo.0, "HEAD");
assert_eq!(
verify_since(&package, &package.join("src"), Some(&base)).unwrap(),
Verification::Stale { attested, latest },
"a scoped change on the branch without a re-attest must still fail",
);
}
#[test]
fn verify_since_passes_when_the_branch_reattested_its_scoped_change() {
let repo = TempRepo::new();
let package = repo.0.join("packages/widget");
std::fs::create_dir_all(package.join("src")).unwrap();
repo.commit_code("packages/widget/src/widget.rs", "pub fn widget() {}\n");
attest(&package, "true").expect("attest should succeed");
let base = rev_parse(&repo.0, "HEAD");
repo.commit_code(
"packages/widget/src/widget.rs",
"pub fn widget() { /* v2 */ }\n",
);
attest(&package, "true").expect("re-attest should succeed");
assert_eq!(
verify_since(&package, &package.join("src"), Some(&base)).unwrap(),
Verification::Fresh,
"a scoped change the branch re-attested must pass",
);
}
#[test]
fn cli_verify_with_base_passes_on_an_unrelated_branch() {
let repo = TempRepo::new();
let package = repo.0.join("packages/widget");
std::fs::create_dir_all(package.join("src")).unwrap();
std::fs::create_dir_all(repo.0.join("packages/other")).unwrap();
repo.commit_code("packages/widget/src/widget.rs", "pub fn widget() {}\n");
attest(&package, "true").expect("attest should succeed");
repo.commit_code(
"packages/widget/src/widget.rs",
"pub fn widget() { /* v2 */ }\n",
);
let base = rev_parse(&repo.0, "HEAD");
repo.commit_code("packages/other/thing.rs", "pub fn thing() {}\n");
let argv: Vec<OsString> = vec![
"testing-conventions".into(),
"e2e".into(),
"verify".into(),
package.as_os_str().to_owned(),
"--scope".into(),
package.join("src").as_os_str().to_owned(),
"--base".into(),
base.into(),
];
assert_eq!(
run(argv).expect("dispatch should succeed"),
0,
"--base must make an unrelated PR pass despite a stale-on-base attestation",
);
}
#[test]
fn verify_extra_scoped_flags_a_commit_under_an_extra_root() {
let repo = TempRepo::new();
let package = repo.0.join("packages/python");
std::fs::create_dir_all(package.join("src")).unwrap();
std::fs::create_dir_all(repo.0.join("packages/rust/src")).unwrap();
repo.commit_code("packages/python/src/lib.rs", "pub fn binding() {}\n");
attest(&package, "true").expect("attest should succeed");
let attested = rev_parse(&repo.0, "HEAD^");
let base = rev_parse(&repo.0, "HEAD");
repo.commit_code("packages/rust/src/core.rs", "pub fn core() {}\n");
let latest = rev_parse(&repo.0, "HEAD");
assert_eq!(
verify_since(&package, &package, Some(&base)).unwrap(),
Verification::Fresh,
"scope-only --base can't see a sibling-tree change",
);
let extra = [PathBuf::from("packages/rust/src")];
let exclude = [PathBuf::from("packages/rust/src/cli")];
assert_eq!(
verify_extra_scoped(&package, &package, Some(&base), &extra, &exclude).unwrap(),
Verification::Stale { attested, latest },
"a non-excluded change under an extra root must stale the attestation",
);
}
#[test]
fn verify_extra_scoped_passes_once_the_extra_root_change_is_reattested() {
let repo = TempRepo::new();
let package = repo.0.join("packages/python");
std::fs::create_dir_all(package.join("src")).unwrap();
std::fs::create_dir_all(repo.0.join("packages/rust/src")).unwrap();
repo.commit_code("packages/python/src/lib.rs", "pub fn binding() {}\n");
attest(&package, "true").expect("attest should succeed");
let base = rev_parse(&repo.0, "HEAD");
repo.commit_code("packages/rust/src/core.rs", "pub fn core() {}\n");
attest(&package, "true").expect("re-attest should succeed");
let extra = [PathBuf::from("packages/rust/src")];
let exclude = [PathBuf::from("packages/rust/src/cli")];
assert_eq!(
verify_extra_scoped(&package, &package, Some(&base), &extra, &exclude).unwrap(),
Verification::Fresh,
"re-attesting after the extra-root change must pass",
);
}
#[test]
fn verify_extra_scoped_ignores_a_commit_under_an_excluded_subtree() {
let repo = TempRepo::new();
let package = repo.0.join("packages/python");
std::fs::create_dir_all(package.join("src")).unwrap();
std::fs::create_dir_all(repo.0.join("packages/rust/src/cli")).unwrap();
repo.commit_code("packages/python/src/lib.rs", "pub fn binding() {}\n");
attest(&package, "true").expect("attest should succeed");
let base = rev_parse(&repo.0, "HEAD");
repo.commit_code("packages/rust/src/cli/main.rs", "pub fn cli() {}\n");
let extra = [PathBuf::from("packages/rust/src")];
let exclude = [PathBuf::from("packages/rust/src/cli")];
assert_eq!(
verify_extra_scoped(&package, &package, Some(&base), &extra, &exclude).unwrap(),
Verification::Fresh,
"a change only under an excluded subtree must not stale the attestation",
);
}
#[test]
fn verify_extra_scoped_with_no_extra_roots_matches_verify_since() {
let repo = TempRepo::new();
let package = repo.0.join("packages/widget");
std::fs::create_dir_all(package.join("src")).unwrap();
repo.commit_code("packages/widget/src/widget.rs", "pub fn widget() {}\n");
attest(&package, "true").expect("attest should succeed");
let base = rev_parse(&repo.0, "HEAD");
repo.commit_code(
"packages/widget/src/widget.rs",
"pub fn widget() { /* v2 */ }\n",
);
assert_eq!(
verify_extra_scoped(&package, &package.join("src"), Some(&base), &[], &[]).unwrap(),
verify_since(&package, &package.join("src"), Some(&base)).unwrap(),
"no extra roots must be byte-identical to verify_since",
);
}
fn e2e_verify_run_extra(
path: &Path,
base: &str,
extra_scopes: &[&str],
excludes: &[&str],
) -> anyhow::Result<i32> {
let mut argv: Vec<OsString> = vec![
"testing-conventions".into(),
"e2e".into(),
"verify".into(),
path.as_os_str().to_owned(),
"--base".into(),
base.into(),
];
for extra in extra_scopes {
argv.push("--extra-scope".into());
argv.push((*extra).into());
}
for exclude in excludes {
argv.push("--exclude".into());
argv.push((*exclude).into());
}
run(argv)
}
#[test]
fn cli_verify_with_extra_scope_fails_on_a_non_excluded_core_change() {
let repo = TempRepo::new();
let package = repo.0.join("packages/python");
std::fs::create_dir_all(package.join("src")).unwrap();
std::fs::create_dir_all(repo.0.join("packages/rust/src")).unwrap();
repo.commit_code("packages/python/src/lib.rs", "pub fn binding() {}\n");
attest(&package, "true").expect("attest should succeed");
let base = rev_parse(&repo.0, "HEAD");
repo.commit_code("packages/rust/src/core.rs", "pub fn core() {}\n");
assert_eq!(
e2e_verify_run_extra(
&package,
&base,
&["packages/rust/src"],
&["packages/rust/src/cli"],
)
.expect("dispatch should succeed"),
1,
"a non-excluded change under --extra-scope should fail verify",
);
}
#[test]
fn cli_verify_with_extra_scope_passes_on_an_excluded_change() {
let repo = TempRepo::new();
let package = repo.0.join("packages/python");
std::fs::create_dir_all(package.join("src")).unwrap();
std::fs::create_dir_all(repo.0.join("packages/rust/src/cli")).unwrap();
repo.commit_code("packages/python/src/lib.rs", "pub fn binding() {}\n");
attest(&package, "true").expect("attest should succeed");
let base = rev_parse(&repo.0, "HEAD");
repo.commit_code("packages/rust/src/cli/main.rs", "pub fn cli() {}\n");
assert_eq!(
e2e_verify_run_extra(
&package,
&base,
&["packages/rust/src"],
&["packages/rust/src/cli"],
)
.expect("dispatch should succeed"),
0,
"a change only under --exclude should pass verify",
);
}