Establish pass compatibility fixture harness (#1)
This commit is contained in:
236
crates/storage/tests/compatibility_fixtures.rs
Normal file
236
crates/storage/tests/compatibility_fixtures.rs
Normal file
@@ -0,0 +1,236 @@
|
||||
#![forbid(unsafe_code)]
|
||||
|
||||
mod support;
|
||||
|
||||
use std::collections::{BTreeMap, BTreeSet};
|
||||
|
||||
use support::compatibility::{
|
||||
FixtureSet, TestResult, decrypt_entry_with_passphrase, validate_entry_record,
|
||||
validate_key_record, validate_recipient_signature, validate_repository,
|
||||
};
|
||||
|
||||
#[test]
|
||||
fn pins_the_selected_upstream_revisions() -> TestResult {
|
||||
let fixture = FixtureSet::load()?;
|
||||
assert_eq!(fixture.upstream.format, 1);
|
||||
assert_eq!(fixture.upstream.project.len(), 2);
|
||||
|
||||
let projects = fixture
|
||||
.upstream
|
||||
.project
|
||||
.iter()
|
||||
.map(|project| (project.name.as_str(), project))
|
||||
.collect::<BTreeMap<_, _>>();
|
||||
let password_store = projects["password-store"];
|
||||
assert_eq!(password_store.version, "1.7.4");
|
||||
assert_eq!(password_store.reported_version, "1.7.4");
|
||||
assert_eq!(
|
||||
password_store.commit,
|
||||
"1078f2514d579178d5df7042c6a790e9c9b731ad"
|
||||
);
|
||||
let pass_otp = projects["pass-otp"];
|
||||
assert_eq!(pass_otp.version, "1.2.0");
|
||||
assert_eq!(pass_otp.reported_version, "1.1.1");
|
||||
assert_eq!(pass_otp.commit, "1e9d10ca75ae1a8672a7f192809713463657778e");
|
||||
for project in projects.values() {
|
||||
assert_eq!(project.commit.len(), 40);
|
||||
assert!(project.commit.bytes().all(|byte| byte.is_ascii_hexdigit()));
|
||||
assert!(project.source.starts_with("https://"));
|
||||
assert!(!project.behavior_source.is_empty());
|
||||
assert!(project.license.starts_with("GPL-"));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn behavior_catalog_covers_the_milestone_contract() -> TestResult {
|
||||
let fixture = FixtureSet::load()?;
|
||||
assert_eq!(fixture.behavior.format, 1);
|
||||
assert!(fixture.behavior.cases.len() >= 65);
|
||||
|
||||
let mut ids = BTreeSet::new();
|
||||
let mut commands = BTreeSet::new();
|
||||
let mut flags = BTreeSet::new();
|
||||
let mut areas = BTreeSet::new();
|
||||
let mut has_success = false;
|
||||
let mut has_failure = false;
|
||||
|
||||
for case in &fixture.behavior.cases {
|
||||
assert!(
|
||||
ids.insert(case.id.as_str()),
|
||||
"duplicate case ID {}",
|
||||
case.id
|
||||
);
|
||||
assert!(
|
||||
matches!(case.status, 0 | 1),
|
||||
"invalid status in {}",
|
||||
case.id
|
||||
);
|
||||
has_success |= case.status == 0;
|
||||
has_failure |= case.status != 0;
|
||||
areas.insert(case.area.as_str());
|
||||
if let Some(command) = case.argv.first() {
|
||||
commands.insert(command.as_str());
|
||||
}
|
||||
for argument in &case.argv {
|
||||
if argument.starts_with('-') {
|
||||
let flag = argument.split('=').next().unwrap_or(argument);
|
||||
flags.insert(flag);
|
||||
}
|
||||
}
|
||||
if case.status != 0 {
|
||||
assert!(case.recipients.is_empty(), "failed case has recipients");
|
||||
assert!(case.commit.is_empty(), "failed case has a commit");
|
||||
}
|
||||
assert!(!case.outcome.is_empty());
|
||||
assert!(!case.stdin.is_empty());
|
||||
}
|
||||
|
||||
assert!(has_success && has_failure);
|
||||
for area in [
|
||||
"init", "show", "list", "find", "grep", "insert", "edit", "generate", "remove", "move",
|
||||
"copy", "git", "otp", "meta",
|
||||
] {
|
||||
assert!(areas.contains(area), "missing behavior area {area}");
|
||||
}
|
||||
for command in [
|
||||
"init",
|
||||
"show",
|
||||
"ls",
|
||||
"list",
|
||||
"find",
|
||||
"search",
|
||||
"grep",
|
||||
"insert",
|
||||
"add",
|
||||
"edit",
|
||||
"generate",
|
||||
"rm",
|
||||
"remove",
|
||||
"delete",
|
||||
"mv",
|
||||
"rename",
|
||||
"cp",
|
||||
"copy",
|
||||
"git",
|
||||
"otp",
|
||||
"help",
|
||||
"version",
|
||||
"--help",
|
||||
"--version",
|
||||
] {
|
||||
assert!(
|
||||
commands.contains(command),
|
||||
"missing command or alias {command}"
|
||||
);
|
||||
}
|
||||
for flag in [
|
||||
"-p",
|
||||
"--path",
|
||||
"-c",
|
||||
"--clip",
|
||||
"-q2",
|
||||
"--qrcode",
|
||||
"-e",
|
||||
"--echo",
|
||||
"-m",
|
||||
"--multiline",
|
||||
"-f",
|
||||
"--force",
|
||||
"-n",
|
||||
"--no-symbols",
|
||||
"-i",
|
||||
"--in-place",
|
||||
"-r",
|
||||
"--recursive",
|
||||
"-s",
|
||||
"--secret",
|
||||
"-a",
|
||||
"--account",
|
||||
"--issuer",
|
||||
] {
|
||||
assert!(flags.contains(flag), "missing option spelling {flag}");
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn generated_openpgp_fixtures_are_self_consistent() -> TestResult {
|
||||
let fixture = FixtureSet::load()?;
|
||||
assert_eq!(fixture.generated.format, 1);
|
||||
assert_eq!(fixture.generated.keys.len(), 2);
|
||||
assert!(fixture.generated.entries.len() >= 8);
|
||||
for key in &fixture.generated.keys {
|
||||
validate_key_record(&fixture, key)?;
|
||||
}
|
||||
for entry in &fixture.generated.entries {
|
||||
validate_entry_record(&fixture, entry)?;
|
||||
}
|
||||
let bob_entry = fixture
|
||||
.generated
|
||||
.entries
|
||||
.iter()
|
||||
.find(|entry| entry.store == "basic" && entry.path == "team/service.gpg")
|
||||
.expect("Bob-only entry");
|
||||
let bob = fixture.key("bob")?;
|
||||
assert!(
|
||||
decrypt_entry_with_passphrase(&fixture, bob_entry, bob, "fixture-alice-passphrase",)
|
||||
.is_err(),
|
||||
"the encrypted secret-key passphrase must be required"
|
||||
);
|
||||
validate_recipient_signature(&fixture, "basic", "alice")?;
|
||||
validate_recipient_signature(&fixture, "basic/team", "bob")?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn generated_git_repositories_have_valid_objects_and_refs() -> TestResult {
|
||||
let fixture = FixtureSet::load()?;
|
||||
assert_eq!(fixture.generated.repositories.len(), 3);
|
||||
for repository in &fixture.generated.repositories {
|
||||
validate_repository(&fixture, repository)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fixtures_materialize_without_touching_a_user_store() -> TestResult {
|
||||
let fixture = FixtureSet::load()?;
|
||||
let store = fixture.materialize_store("basic")?;
|
||||
assert!(store.path().join(".gpg-id").is_file());
|
||||
assert!(store.path().join("email/personal.gpg").is_file());
|
||||
assert!(store.path().join("team/.gpg-id").is_file());
|
||||
|
||||
let repository = fixture.materialize_repository("basic")?;
|
||||
assert!(repository.path().join("HEAD").is_file());
|
||||
assert!(repository.path().join("objects").is_dir());
|
||||
assert!(repository.path().join("refs/heads/main").is_file());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn hostile_and_interruption_recipes_are_present() -> TestResult {
|
||||
let fixture = FixtureSet::load()?;
|
||||
let nodes = fixture
|
||||
.layouts
|
||||
.get("synthetic_node")
|
||||
.and_then(toml::Value::as_array)
|
||||
.expect("synthetic_node array");
|
||||
assert!(nodes.len() >= 4);
|
||||
let outcomes = nodes
|
||||
.iter()
|
||||
.filter_map(|node| node.get("expected"))
|
||||
.filter_map(toml::Value::as_str)
|
||||
.collect::<BTreeSet<_>>();
|
||||
assert!(outcomes.contains("reject-symlink-escape"));
|
||||
assert!(outcomes.contains("reject-unsupported-file-type"));
|
||||
assert!(outcomes.contains("require-trailing-slash-disambiguation"));
|
||||
|
||||
let interruptions = fixture
|
||||
.layouts
|
||||
.get("interruption")
|
||||
.and_then(toml::Value::as_array)
|
||||
.expect("interruption array");
|
||||
assert_eq!(interruptions.len(), 3);
|
||||
Ok(())
|
||||
}
|
||||
Reference in New Issue
Block a user