137 lines
3.7 KiB
Rust
137 lines
3.7 KiB
Rust
|
|
use remora_domain::{
|
||
|
|
BucketName, FailureDisposition, JobPhase, MemberPath, RuntimeFailureClass, StorageScope,
|
||
|
|
Target, TargetPrefix, TargetStoreId, fingerprint,
|
||
|
|
};
|
||
|
|
use serde::Deserialize;
|
||
|
|
use serde_json::Value;
|
||
|
|
|
||
|
|
#[derive(Deserialize)]
|
||
|
|
struct Corpus {
|
||
|
|
job_phases: Vec<JobPhase>,
|
||
|
|
runtime_failures: Vec<FailureCase>,
|
||
|
|
buckets: ValidationCases<String>,
|
||
|
|
target_prefixes: ValidationCases<CanonicalCase>,
|
||
|
|
reserved_target_prefixes: Vec<ReservedPrefixCase>,
|
||
|
|
member_paths: ValidationCases<CanonicalCase>,
|
||
|
|
scope_coverage: Vec<ScopeCase>,
|
||
|
|
fingerprints: Vec<FingerprintCase>,
|
||
|
|
}
|
||
|
|
|
||
|
|
#[derive(Deserialize)]
|
||
|
|
struct FailureCase {
|
||
|
|
class: RuntimeFailureClass,
|
||
|
|
disposition: FailureDisposition,
|
||
|
|
}
|
||
|
|
|
||
|
|
#[derive(Deserialize)]
|
||
|
|
struct ValidationCases<T> {
|
||
|
|
valid: Vec<T>,
|
||
|
|
invalid: Vec<String>,
|
||
|
|
}
|
||
|
|
|
||
|
|
#[derive(Deserialize)]
|
||
|
|
struct CanonicalCase {
|
||
|
|
input: String,
|
||
|
|
canonical: String,
|
||
|
|
}
|
||
|
|
|
||
|
|
#[derive(Deserialize)]
|
||
|
|
struct FingerprintCase {
|
||
|
|
value: Value,
|
||
|
|
canonical: String,
|
||
|
|
sha256: String,
|
||
|
|
}
|
||
|
|
|
||
|
|
#[derive(Deserialize)]
|
||
|
|
struct ScopeCase {
|
||
|
|
scope: [String; 3],
|
||
|
|
target: [String; 3],
|
||
|
|
covers: bool,
|
||
|
|
}
|
||
|
|
|
||
|
|
#[derive(Deserialize)]
|
||
|
|
struct ReservedPrefixCase {
|
||
|
|
input: String,
|
||
|
|
reserved: Vec<String>,
|
||
|
|
}
|
||
|
|
|
||
|
|
fn corpus() -> Corpus {
|
||
|
|
serde_json::from_str(include_str!("../../../contracts/golden/domain.json")).unwrap()
|
||
|
|
}
|
||
|
|
|
||
|
|
#[test]
|
||
|
|
fn protocol_phase_vocabulary_is_exact() {
|
||
|
|
let phases = corpus().job_phases;
|
||
|
|
assert_eq!(phases.len(), 7);
|
||
|
|
assert!(phases[..4].iter().all(|phase| !phase.is_terminal()));
|
||
|
|
assert!(phases[4..].iter().all(|phase| phase.is_terminal()));
|
||
|
|
}
|
||
|
|
|
||
|
|
#[test]
|
||
|
|
fn runtime_failure_dispositions_match_the_architecture() {
|
||
|
|
for case in corpus().runtime_failures {
|
||
|
|
assert_eq!(case.class.disposition(), case.disposition);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
#[test]
|
||
|
|
fn domain_values_validate_and_normalize() {
|
||
|
|
let corpus = corpus();
|
||
|
|
for value in corpus.buckets.valid {
|
||
|
|
assert_eq!(BucketName::parse(&value).unwrap().as_str(), value);
|
||
|
|
}
|
||
|
|
for value in corpus.buckets.invalid {
|
||
|
|
assert!(BucketName::parse(value).is_err());
|
||
|
|
}
|
||
|
|
for case in corpus.target_prefixes.valid {
|
||
|
|
assert_eq!(
|
||
|
|
TargetPrefix::parse(&case.input, []).unwrap().canonical(),
|
||
|
|
case.canonical
|
||
|
|
);
|
||
|
|
}
|
||
|
|
for value in corpus.target_prefixes.invalid {
|
||
|
|
assert!(TargetPrefix::parse(&value, []).is_err());
|
||
|
|
}
|
||
|
|
for case in corpus.reserved_target_prefixes {
|
||
|
|
assert!(
|
||
|
|
TargetPrefix::parse(&case.input, case.reserved.iter().map(String::as_str)).is_err()
|
||
|
|
);
|
||
|
|
}
|
||
|
|
for case in corpus.member_paths.valid {
|
||
|
|
assert_eq!(
|
||
|
|
MemberPath::parse(&case.input).unwrap().canonical(),
|
||
|
|
case.canonical
|
||
|
|
);
|
||
|
|
}
|
||
|
|
for value in corpus.member_paths.invalid {
|
||
|
|
assert!(MemberPath::parse(&value).is_err());
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
#[test]
|
||
|
|
fn request_fingerprints_match_the_shared_corpus() {
|
||
|
|
for case in corpus().fingerprints {
|
||
|
|
assert_eq!(
|
||
|
|
serde_json_canonicalizer::to_string(&case.value).unwrap(),
|
||
|
|
case.canonical
|
||
|
|
);
|
||
|
|
assert_eq!(fingerprint(&case.value).unwrap().to_hex(), case.sha256);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
#[test]
|
||
|
|
fn storage_scope_coverage_uses_exact_segment_tuples() {
|
||
|
|
for case in corpus().scope_coverage {
|
||
|
|
let scope = StorageScope {
|
||
|
|
store: TargetStoreId::parse(&case.scope[0]).unwrap(),
|
||
|
|
bucket: BucketName::parse(&case.scope[1]).unwrap(),
|
||
|
|
prefix: TargetPrefix::parse(&case.scope[2], []).unwrap(),
|
||
|
|
};
|
||
|
|
let target = Target {
|
||
|
|
store: TargetStoreId::parse(&case.target[0]).unwrap(),
|
||
|
|
bucket: BucketName::parse(&case.target[1]).unwrap(),
|
||
|
|
prefix: TargetPrefix::parse(&case.target[2], []).unwrap(),
|
||
|
|
};
|
||
|
|
assert_eq!(scope.covers(&target), case.covers);
|
||
|
|
}
|
||
|
|
}
|