741 lines
28 KiB
Rust
741 lines
28 KiB
Rust
use std::path::PathBuf;
|
|
|
|
fn repository_root() -> PathBuf {
|
|
PathBuf::from(env!("CARGO_MANIFEST_DIR"))
|
|
.parent()
|
|
.unwrap()
|
|
.parent()
|
|
.unwrap()
|
|
.to_path_buf()
|
|
}
|
|
|
|
#[test]
|
|
fn canonical_small_commit_workload_cannot_be_batch_gamed() {
|
|
let text = std::fs::read_to_string(repository_root().join("bench/workloads/small-commit.toml"))
|
|
.unwrap();
|
|
let workload: toml::Value = toml::from_str(&text).unwrap();
|
|
assert_eq!(workload["schema_version"].as_integer(), Some(1));
|
|
assert_eq!(workload["blob_bytes"].as_integer(), Some(1_024));
|
|
assert_eq!(workload["max_client_batch_commits"].as_integer(), Some(64));
|
|
assert_eq!(
|
|
workload["max_writer_group_transactions"].as_integer(),
|
|
Some(512)
|
|
);
|
|
assert_eq!(
|
|
workload["counting"]["expected_objects_new_per_commit"].as_integer(),
|
|
Some(3)
|
|
);
|
|
for (_, value) in workload["validation"].as_table().unwrap() {
|
|
assert_eq!(value.as_bool(), Some(true));
|
|
}
|
|
let topologies = workload["topology"].as_array().unwrap();
|
|
assert_eq!(topologies.len(), 4);
|
|
// Pin all four required topology/selection combinations by name, not
|
|
// just two of them, and require the exact persistent-client count on
|
|
// every row.
|
|
for (name, repositories, refs_per_repository, selection, zipf_exponent) in [
|
|
("many-ref-uniform", 1, 1_024, "uniform", None),
|
|
("many-ref-zipf", 1, 1_024, "zipf", Some(0.9)),
|
|
("many-repo-uniform", 256, 16, "uniform", None),
|
|
("many-repo-zipf", 256, 16, "zipf", Some(0.9)),
|
|
] {
|
|
let row = topologies
|
|
.iter()
|
|
.find(|value| value["name"].as_str() == Some(name))
|
|
.unwrap_or_else(|| panic!("missing topology row {name}"));
|
|
assert_eq!(
|
|
row["repositories"].as_integer(),
|
|
Some(repositories),
|
|
"{name}"
|
|
);
|
|
assert_eq!(
|
|
row["refs_per_repository"].as_integer(),
|
|
Some(refs_per_repository),
|
|
"{name}"
|
|
);
|
|
assert_eq!(row["persistent_clients"].as_integer(), Some(64), "{name}");
|
|
assert_eq!(row["selection"].as_str(), Some(selection), "{name}");
|
|
assert_eq!(
|
|
row.get("zipf_exponent").and_then(|v| v.as_float()),
|
|
zipf_exponent,
|
|
"{name}"
|
|
);
|
|
}
|
|
|
|
let measurement = &workload["measurement"];
|
|
assert_eq!(measurement["p2_p3_warmup_seconds"].as_integer(), Some(300));
|
|
assert_eq!(
|
|
measurement["p2_p3_measured_seconds"].as_integer(),
|
|
Some(900)
|
|
);
|
|
assert_eq!(measurement["p4_warmup_seconds"].as_integer(), Some(600));
|
|
assert_eq!(measurement["p4_measured_seconds"].as_integer(), Some(1_800));
|
|
assert_eq!(measurement["repetitions"].as_integer(), Some(3));
|
|
assert_eq!(
|
|
measurement["correct_coordinated_omission"].as_bool(),
|
|
Some(true)
|
|
);
|
|
assert_eq!(
|
|
measurement["one_minute_window_target_met_percent_min"].as_integer(),
|
|
Some(95),
|
|
"plan §3: >=95% of one-minute windows must meet the target"
|
|
);
|
|
assert_eq!(
|
|
measurement["one_minute_window_floor_percent"].as_integer(),
|
|
Some(90),
|
|
"plan §3: no window may fall below 90% of the target"
|
|
);
|
|
let latency = &measurement["latency_micros"];
|
|
assert_eq!(latency["p2_p99_max"].as_integer(), Some(50_000));
|
|
assert_eq!(latency["p3_p50_max"].as_integer(), Some(20_000));
|
|
assert_eq!(latency["p3_p95_max"].as_integer(), Some(50_000));
|
|
assert_eq!(latency["p3_p99_max"].as_integer(), Some(100_000));
|
|
assert_eq!(latency["p4_p50_max"].as_integer(), Some(20_000));
|
|
assert_eq!(latency["p4_p95_max"].as_integer(), Some(50_000));
|
|
assert_eq!(latency["p4_p99_max"].as_integer(), Some(100_000));
|
|
|
|
assert_eq!(workload["seed"].as_integer(), Some(126_394_451_485_337));
|
|
assert!(!workload["generator"].as_str().unwrap().is_empty());
|
|
}
|
|
|
|
fn assert_required_names(schema: &serde_json::Value, path: &[&str], expected: &[&str]) {
|
|
let mut node = schema;
|
|
for segment in path {
|
|
node = &node[segment];
|
|
}
|
|
let required: Vec<&str> = node["required"]
|
|
.as_array()
|
|
.unwrap()
|
|
.iter()
|
|
.map(|value| value.as_str().unwrap())
|
|
.collect();
|
|
let mut sorted_required = required.clone();
|
|
sorted_required.sort_unstable();
|
|
let mut sorted_expected = expected.to_vec();
|
|
sorted_expected.sort_unstable();
|
|
assert_eq!(
|
|
sorted_required, sorted_expected,
|
|
"required names at {path:?} do not match the frozen set"
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn result_schema_requires_integrity_durability_and_all_independent_verdicts() {
|
|
let text = std::fs::read_to_string(repository_root().join("bench/result-schema.json")).unwrap();
|
|
let schema: serde_json::Value = serde_json::from_str(&text).unwrap();
|
|
assert_eq!(
|
|
schema["$schema"].as_str(),
|
|
Some("https://json-schema.org/draft/2020-12/schema")
|
|
);
|
|
let required = schema["required"].as_array().unwrap();
|
|
for field in [
|
|
"attestation",
|
|
"source",
|
|
"artifacts",
|
|
"workload",
|
|
"hardware",
|
|
"deployment",
|
|
"measurement",
|
|
"counts",
|
|
"bytes",
|
|
"latency_micros",
|
|
"resources",
|
|
"durability",
|
|
"verification",
|
|
"verdicts",
|
|
] {
|
|
assert!(
|
|
required.iter().any(|value| value == field),
|
|
"missing {field}"
|
|
);
|
|
}
|
|
assert_eq!(
|
|
schema["$defs"]["workload"]["properties"]["client_batch_commits"]["maximum"].as_u64(),
|
|
Some(64)
|
|
);
|
|
assert_eq!(
|
|
schema["$defs"]["workload"]["properties"]["writer_group_limit"]["maximum"].as_u64(),
|
|
Some(512)
|
|
);
|
|
assert_eq!(
|
|
schema["$defs"]["deployment"]["properties"]["tmpfs"]["const"].as_bool(),
|
|
Some(false)
|
|
);
|
|
assert_eq!(
|
|
schema["$defs"]["deployment"]["properties"]["remote_storage"]["const"].as_bool(),
|
|
Some(false)
|
|
);
|
|
assert_required_names(
|
|
&schema,
|
|
&["$defs", "workload", "properties", "validation_flags"],
|
|
&[
|
|
"request_signature",
|
|
"replay",
|
|
"pack_hash_and_framing",
|
|
"outer_embedded_type_match",
|
|
"complete_graph",
|
|
"authority_and_role",
|
|
"instance_policy",
|
|
"repository_policy",
|
|
"typed_ref_cas",
|
|
"fast_forward",
|
|
"durability_fence_before_response",
|
|
],
|
|
);
|
|
assert_eq!(
|
|
schema["$defs"]["durability"]["properties"]["acknowledged_loss"]["const"].as_u64(),
|
|
Some(0)
|
|
);
|
|
assert_eq!(
|
|
schema["$defs"]["durability"]["properties"]["torn_transactions"]["const"].as_u64(),
|
|
Some(0)
|
|
);
|
|
assert_required_names(
|
|
&schema,
|
|
&["properties", "verdicts"],
|
|
&[
|
|
"storage_primitive",
|
|
"in_process_protocol",
|
|
"deployed_30k",
|
|
"deployed_60k",
|
|
"recovery",
|
|
"overload",
|
|
"compaction",
|
|
"federation",
|
|
"release",
|
|
],
|
|
);
|
|
|
|
// Aggregate one-minute-window rule from plan §3: >=95% of windows must
|
|
// meet target and none may fall below 90%.
|
|
//
|
|
// Enforced conditionally on `outcome` since contract review 2026-07-24-B.
|
|
// Unconditionally, the floor made a failed or preliminary run
|
|
// unrepresentable: even a bundle whose verdicts were all `not-applicable`
|
|
// had to claim compliance, so a failure could not be archived as evidence.
|
|
// The rule is unchanged for any run claiming a pass.
|
|
assert!(schema["$defs"]["measurement"]["required"]
|
|
.as_array()
|
|
.unwrap()
|
|
.iter()
|
|
.any(|v| v == "windows_meeting_target_percent"));
|
|
let window_rule = schema["allOf"]
|
|
.as_array()
|
|
.unwrap()
|
|
.iter()
|
|
.find(|rule| {
|
|
// The per-gate latency ceilings became `outcome`-conditional too,
|
|
// so `outcome == "pass"` alone now matches four other rules. The
|
|
// window rule is the one that applies to every gate: it keys on
|
|
// `outcome` and nothing else.
|
|
rule["if"]["properties"]["outcome"]["const"] == "pass"
|
|
&& rule["if"]["properties"]["gate"].is_null()
|
|
})
|
|
.expect("missing the outcome-conditional window rule");
|
|
assert_eq!(
|
|
window_rule["then"]["properties"]["measurement"]["properties"]
|
|
["windows_meeting_target_percent"]["minimum"]
|
|
.as_u64(),
|
|
Some(95)
|
|
);
|
|
assert_eq!(
|
|
window_rule["then"]["properties"]["measurement"]["properties"]["windows_below_floor_count"]
|
|
["const"]
|
|
.as_u64(),
|
|
Some(0)
|
|
);
|
|
assert!(
|
|
schema["required"]
|
|
.as_array()
|
|
.unwrap()
|
|
.iter()
|
|
.any(|v| v == "outcome"),
|
|
"outcome must be required so the window rule always has something to key on"
|
|
);
|
|
|
|
// Per-gate latency ceilings are enforced conditionally on `gate`, not
|
|
// left to the raw p50/p95/p99 fields alone.
|
|
let all_of = schema["allOf"].as_array().unwrap();
|
|
let gate_p99_ceiling = |gate: &str| -> u64 {
|
|
all_of
|
|
.iter()
|
|
.find(|rule| rule["if"]["properties"]["gate"]["const"] == gate)
|
|
.unwrap_or_else(|| panic!("missing latency rule for gate {gate}"))["then"]["properties"]
|
|
["latency_micros"]["properties"]["p99"]["maximum"]
|
|
.as_u64()
|
|
.unwrap()
|
|
};
|
|
assert_eq!(gate_p99_ceiling("storage_primitive"), 50_000);
|
|
assert_eq!(gate_p99_ceiling("in_process_protocol"), 100_000);
|
|
assert_eq!(gate_p99_ceiling("deployed_30k"), 100_000);
|
|
assert_eq!(gate_p99_ceiling("deployed_60k"), 100_000);
|
|
|
|
// Empty strings must not satisfy the descriptive deployment/hardware
|
|
// fields the plan requires be recorded.
|
|
for field in ["systemd", "cgroup", "proxy", "tls"] {
|
|
assert_eq!(
|
|
schema["$defs"]["deployment"]["properties"][field]["minLength"].as_u64(),
|
|
Some(1),
|
|
"{field}"
|
|
);
|
|
}
|
|
assert_eq!(
|
|
schema["$defs"]["hardware"]["properties"]["barriers"]["enum"],
|
|
serde_json::json!(["enabled"])
|
|
);
|
|
}
|
|
|
|
/// Contract review 2026-07-24-B.
|
|
///
|
|
/// A P2 run measures the `levcs-store` API, which plan §5.1 forbids from making
|
|
/// identity-role, merge-policy, or federation decisions and which runs
|
|
/// in-process with no proxy or TLS. Reporting the validation flags as true
|
|
/// would be false; reporting them false failed the original unconditional
|
|
/// schema. They are now pinned per flag, conditionally on `gate`.
|
|
///
|
|
/// The danger in that amendment is un-pinning by accident: relaxing
|
|
/// `const: true` in `$defs.validation_flags` without re-pinning it for every
|
|
/// other gate would let a deployed-node bundle declare `complete_graph: false`
|
|
/// and still validate, which is a strictly worse defect than the one being
|
|
/// fixed. This test asserts both halves.
|
|
#[test]
|
|
fn validation_flags_and_promotability_are_pinned_per_gate() {
|
|
const FLAGS: &[&str] = &[
|
|
"request_signature",
|
|
"replay",
|
|
"pack_hash_and_framing",
|
|
"outer_embedded_type_match",
|
|
"complete_graph",
|
|
"authority_and_role",
|
|
"instance_policy",
|
|
"repository_policy",
|
|
"typed_ref_cas",
|
|
"fast_forward",
|
|
"durability_fence_before_response",
|
|
];
|
|
/// The only two a store-level run genuinely performs: the fence is the
|
|
/// claim under test, and the shard sequencer really does perform the typed
|
|
/// CAS against speculative state. `fast_forward` is not among them — the
|
|
/// sequencer consumes precomputed ancestry facts (plan §7 stage 9) rather
|
|
/// than deriving them, and a validation flag must state what the measured
|
|
/// system performed.
|
|
const TRUE_AT_P2: &[&str] = &["typed_ref_cas", "durability_fence_before_response"];
|
|
|
|
let text = std::fs::read_to_string(repository_root().join("bench/result-schema.json")).unwrap();
|
|
let schema: serde_json::Value = serde_json::from_str(&text).unwrap();
|
|
|
|
// `promotable` is required at top level for every gate, so an evaluator's
|
|
// refusal to promote is a mechanical schema check rather than prose.
|
|
assert!(
|
|
schema["required"]
|
|
.as_array()
|
|
.unwrap()
|
|
.iter()
|
|
.any(|v| v == "promotable"),
|
|
"promotable must be required for every gate, not only storage_primitive"
|
|
);
|
|
|
|
let rule = schema["allOf"]
|
|
.as_array()
|
|
.unwrap()
|
|
.iter()
|
|
.find(|r| {
|
|
r["if"]["properties"]["gate"]["const"] == "storage_primitive" && r.get("else").is_some()
|
|
})
|
|
.expect("missing the per-gate validation-flag rule");
|
|
|
|
let branch_flags = |branch: &str| -> serde_json::Value {
|
|
rule[branch]["properties"]["workload"]["properties"]["validation_flags"]["properties"]
|
|
.clone()
|
|
};
|
|
|
|
// storage_primitive: per-flag, stating exactly what the store performed.
|
|
let p2 = branch_flags("then");
|
|
for flag in FLAGS {
|
|
let expected = TRUE_AT_P2.contains(flag);
|
|
assert_eq!(
|
|
p2[flag]["const"].as_bool(),
|
|
Some(expected),
|
|
"storage_primitive must pin {flag} to {expected}"
|
|
);
|
|
}
|
|
assert_eq!(
|
|
rule["then"]["properties"]["promotable"]["const"].as_bool(),
|
|
Some(false),
|
|
"a storage primitive result can never be promoted to an instance claim"
|
|
);
|
|
|
|
// Every other gate: all eleven re-pinned true. This is the half that
|
|
// fails if the amendment un-pins validation for P3/P4/P5.
|
|
let other = branch_flags("else");
|
|
for flag in FLAGS {
|
|
assert_eq!(
|
|
other[flag]["const"].as_bool(),
|
|
Some(true),
|
|
"every non-storage_primitive gate must re-pin {flag} to true; \
|
|
relaxing the shared definition without re-pinning here would let \
|
|
a deployed-node bundle declare it false and still validate"
|
|
);
|
|
}
|
|
assert_eq!(
|
|
rule["else"]["properties"]["promotable"]["const"].as_bool(),
|
|
Some(true)
|
|
);
|
|
|
|
// The shared definition still requires every flag to be present; only its
|
|
// value moved to the conditional rules.
|
|
assert_required_names(
|
|
&schema,
|
|
&["$defs", "workload", "properties", "validation_flags"],
|
|
FLAGS,
|
|
);
|
|
}
|
|
|
|
/// Contract review 2026-07-24-B, second pass.
|
|
///
|
|
/// The first pass split `workload.validation_flags` per gate but left
|
|
/// `verification.*` blanket `const: true` — the same defect one block over.
|
|
/// `commits_in_recovered_closure` cannot honestly be true at
|
|
/// `storage_primitive` for exactly the reason `complete_graph` is false: plan
|
|
/// §5.1 forbids the store from traversing the graph, so no ref closure exists
|
|
/// below the engine. Fixing one and not the other left the bundle asserting by
|
|
/// a different field precisely what it had just stopped asserting.
|
|
///
|
|
/// The replacement at P2 is the storage-layer analogue —
|
|
/// `acknowledged_sequences_reconciled` — which is required at every gate, so
|
|
/// nothing is weakened: P2 trades a claim it cannot make for one it can prove.
|
|
#[test]
|
|
fn verification_claims_are_pinned_per_gate_like_validation_flags() {
|
|
let text = std::fs::read_to_string(repository_root().join("bench/result-schema.json")).unwrap();
|
|
let schema: serde_json::Value = serde_json::from_str(&text).unwrap();
|
|
|
|
// Only the claims a run at *any* gate can earn are unconditionally
|
|
// required. The five object-graph claims moved to the per-gate rule below
|
|
// (contract review 2026-07-24-B, third amendment).
|
|
assert_required_names(
|
|
&schema,
|
|
&["$defs", "verification"],
|
|
&[
|
|
"setup_traffic_excluded",
|
|
"commits_in_recovered_closure",
|
|
"acknowledged_sequences_reconciled",
|
|
],
|
|
);
|
|
|
|
// The ACK-journal proof is unconditional. It is the only detector for a
|
|
// device that lost a write it acknowledged (scope 3.8), so no gate may
|
|
// opt out of it.
|
|
assert_eq!(
|
|
schema["$defs"]["verification"]["properties"]["acknowledged_sequences_reconciled"]["const"]
|
|
.as_bool(),
|
|
Some(true)
|
|
);
|
|
|
|
let rule = schema["allOf"]
|
|
.as_array()
|
|
.unwrap()
|
|
.iter()
|
|
.find(|r| {
|
|
r["if"]["properties"]["gate"]["const"] == "storage_primitive" && r.get("else").is_some()
|
|
})
|
|
.expect("missing the per-gate rule");
|
|
|
|
assert_eq!(
|
|
rule["then"]["properties"]["verification"]["properties"]["commits_in_recovered_closure"]
|
|
["const"]
|
|
.as_bool(),
|
|
Some(false),
|
|
"the store cannot traverse a commit graph, so P2 must not claim closure"
|
|
);
|
|
assert_eq!(
|
|
rule["else"]["properties"]["verification"]["properties"]["commits_in_recovered_closure"]
|
|
["const"]
|
|
.as_bool(),
|
|
Some(true),
|
|
"every instance gate must still re-pin closure to true"
|
|
);
|
|
|
|
// The five object-graph claims are *forbidden* at storage_primitive, not
|
|
// merely optional. There is no object graph at this layer, so `false`
|
|
// would be its own untrue statement: it claims the check applied and did
|
|
// not pass. Absence is the only honest encoding.
|
|
const GRAPH_CLAIMS: &[&str] = &[
|
|
"unique_blob_tree_commit_ids",
|
|
"objects_new_equals_three_per_commit",
|
|
"blobs_recomputed",
|
|
"operation_receipts_reconciled",
|
|
"metadata_complete",
|
|
];
|
|
let forbidden = rule["then"]["properties"]["verification"]["not"]["anyOf"]
|
|
.as_array()
|
|
.expect("storage_primitive must forbid the object-graph claims outright");
|
|
for claim in GRAPH_CLAIMS {
|
|
assert!(
|
|
forbidden
|
|
.iter()
|
|
.any(|clause| clause["required"][0] == serde_json::json!(claim)),
|
|
"{claim} must be forbidden at storage_primitive, not left optional"
|
|
);
|
|
}
|
|
|
|
// Every one of them is required *and* true again at every other gate.
|
|
// This is the half of the split that is easy to lose: relaxing a claim for
|
|
// one gate must never un-pin it for the rest. It has already been lost
|
|
// once, by the very edit that introduced this assertion.
|
|
let other = &rule["else"]["properties"];
|
|
for claim in GRAPH_CLAIMS {
|
|
assert!(
|
|
other["verification"]["required"]
|
|
.as_array()
|
|
.unwrap()
|
|
.iter()
|
|
.any(|v| v == claim),
|
|
"{claim} must be required again at every non-storage gate"
|
|
);
|
|
assert_eq!(
|
|
other["verification"]["properties"][claim]["const"].as_bool(),
|
|
Some(true),
|
|
"{claim} must be pinned true at every non-storage gate"
|
|
);
|
|
}
|
|
assert_eq!(
|
|
other["promotable"]["const"].as_bool(),
|
|
Some(true),
|
|
"the else branch must keep re-pinning promotability"
|
|
);
|
|
let flags = other["workload"]["properties"]["validation_flags"]["properties"]
|
|
.as_object()
|
|
.expect("the else branch must keep re-pinning every validation flag");
|
|
assert_eq!(flags.len(), 11, "all eleven flags, not a subset");
|
|
for (name, spec) in flags {
|
|
assert_eq!(spec["const"].as_bool(), Some(true), "{name}");
|
|
}
|
|
|
|
// Coordinated omission is the other shape: applicable at every gate, but a
|
|
// closed-loop driver does not correct for it. So the base permits either
|
|
// value and the else pins true — the honest `false` is representable
|
|
// exactly where it is the truth, and nowhere else.
|
|
assert_eq!(
|
|
schema["$defs"]["measurement"]["properties"]["coordinated_omission_corrected"]["type"]
|
|
.as_str(),
|
|
Some("boolean"),
|
|
"a closed-loop storage run must be able to report false"
|
|
);
|
|
assert_eq!(
|
|
other["measurement"]["properties"]["coordinated_omission_corrected"]["const"].as_bool(),
|
|
Some(true),
|
|
"every instance gate must still require CO correction"
|
|
);
|
|
}
|
|
|
|
/// Plan §13 names index bytes/object and checkpoint lookup fan-out as stop
|
|
/// conditions and requires result bundles to report them; §5.2 requires P2 to
|
|
/// measure signing cost separately. Every schema object is
|
|
/// `additionalProperties: false`, so before 2026-07-24-B there was nowhere to
|
|
/// put any of them and they would have been smuggled into a free-form map.
|
|
#[test]
|
|
fn the_bundle_has_a_home_for_every_figure_phase_one_must_report() {
|
|
let text = std::fs::read_to_string(repository_root().join("bench/result-schema.json")).unwrap();
|
|
let schema: serde_json::Value = serde_json::from_str(&text).unwrap();
|
|
|
|
assert!(
|
|
schema["required"]
|
|
.as_array()
|
|
.unwrap()
|
|
.iter()
|
|
.any(|v| v == "storage"),
|
|
"the storage block must be required, not optional"
|
|
);
|
|
assert_required_names(
|
|
&schema,
|
|
&["$defs", "storage"],
|
|
&[
|
|
"index_bytes_per_object",
|
|
"checkpoint_lookup_fanout",
|
|
"evidence_signing_micros_p50",
|
|
"fences",
|
|
"transactions",
|
|
],
|
|
);
|
|
// Fences and transactions together are the mechanical form of the Phase 1
|
|
// exit criterion "no per-object fsync" — a reader can divide.
|
|
for field in ["fences", "transactions"] {
|
|
assert_eq!(
|
|
schema["$defs"]["storage"]["properties"][field]["type"].as_str(),
|
|
Some("integer"),
|
|
"{field}"
|
|
);
|
|
}
|
|
}
|
|
|
|
/// The generator string is pinned so a bundle can be reproduced rather than
|
|
/// believed: an evaluator recomputes every deterministic 1,024-byte blob from
|
|
/// `workload.seed` and `workload.generator` and checks them against the
|
|
/// recovered store (scope 9.5).
|
|
#[test]
|
|
fn the_bundle_records_the_frozen_workload_generator_and_seed() {
|
|
let schema_text =
|
|
std::fs::read_to_string(repository_root().join("bench/result-schema.json")).unwrap();
|
|
let schema: serde_json::Value = serde_json::from_str(&schema_text).unwrap();
|
|
for field in ["seed", "generator"] {
|
|
assert!(
|
|
schema["$defs"]["workload"]["required"]
|
|
.as_array()
|
|
.unwrap()
|
|
.iter()
|
|
.any(|v| v == field),
|
|
"workload.{field} must be required in the result bundle"
|
|
);
|
|
}
|
|
assert_eq!(
|
|
schema["$defs"]["workload"]["properties"]["generator"]["minLength"].as_u64(),
|
|
Some(1)
|
|
);
|
|
|
|
// And the frozen workload still carries both, so the bundle has something
|
|
// to equal.
|
|
let workload_text =
|
|
std::fs::read_to_string(repository_root().join("bench/workloads/small-commit.toml"))
|
|
.unwrap();
|
|
let workload: toml::Value = toml::from_str(&workload_text).unwrap();
|
|
assert_eq!(workload["seed"].as_integer(), Some(126_394_451_485_337));
|
|
assert_eq!(
|
|
workload["generator"].as_str(),
|
|
Some("blake3-xof(seed || repo_ordinal_le || ref_ordinal_le || commit_ordinal_le)")
|
|
);
|
|
}
|
|
|
|
/// The store-directory attributes are part of the frozen profile, not
|
|
/// per-bundle metadata.
|
|
///
|
|
/// A profile that silently permits two on-disk configurations for the files
|
|
/// carrying the throughput is not frozen: a P2 number measured nodatacow is
|
|
/// not comparable to one measured copy-on-write. The bundle additionally
|
|
/// records what it verified at startup (scope 9.2).
|
|
#[test]
|
|
fn store_directory_attributes_are_frozen_in_the_profile_and_recorded_in_the_bundle() {
|
|
let text =
|
|
std::fs::read_to_string(repository_root().join("bench/reference-hardware.toml")).unwrap();
|
|
let hardware: toml::Value = toml::from_str(&text).unwrap();
|
|
let profiles = hardware["profile"].as_array().unwrap();
|
|
assert_eq!(profiles.len(), 2);
|
|
for profile in profiles {
|
|
let fs = &profile["filesystem"];
|
|
assert_eq!(
|
|
fs["store_directory_attributes"].as_str(),
|
|
Some("nodatacow"),
|
|
"profile {:?}",
|
|
profile["name"].as_str()
|
|
);
|
|
let dirs = fs["store_directories"].as_array().unwrap();
|
|
assert!(
|
|
dirs.iter().any(|d| d.as_str() == Some("shards/*/active")),
|
|
"the journal directory must be covered"
|
|
);
|
|
assert!(
|
|
dirs.iter().any(|d| d.as_str() == Some("shards/*/segments")),
|
|
"the segment directory must be covered"
|
|
);
|
|
}
|
|
|
|
let schema_text =
|
|
std::fs::read_to_string(repository_root().join("bench/result-schema.json")).unwrap();
|
|
let schema: serde_json::Value = serde_json::from_str(&schema_text).unwrap();
|
|
assert!(
|
|
schema["$defs"]["deployment"]["required"]
|
|
.as_array()
|
|
.unwrap()
|
|
.iter()
|
|
.any(|v| v == "store_directory_attributes"),
|
|
"every bundle must record the attributes it verified"
|
|
);
|
|
assert_eq!(
|
|
schema["$defs"]["deployment"]["properties"]["store_directory_attributes"]["minLength"]
|
|
.as_u64(),
|
|
Some(1)
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn reference_profiles_freeze_every_required_hardware_and_stack_field() {
|
|
let text =
|
|
std::fs::read_to_string(repository_root().join("bench/reference-hardware.toml")).unwrap();
|
|
let hardware: toml::Value = toml::from_str(&text).unwrap();
|
|
let profiles = hardware["profile"].as_array().unwrap();
|
|
assert_eq!(profiles.len(), 2);
|
|
assert_eq!(profiles[0]["name"].as_str(), Some("minimum-30k"));
|
|
assert_eq!(profiles[1]["name"].as_str(), Some("release-60k"));
|
|
for profile in profiles {
|
|
for table in ["cpu", "memory", "nvme", "filesystem", "network", "software"] {
|
|
assert!(profile[table].is_table(), "{table} must be frozen");
|
|
}
|
|
assert_eq!(profile["memory"]["swap_enabled"].as_bool(), Some(false));
|
|
assert_eq!(profile["filesystem"]["barriers"].as_str(), Some("enabled"));
|
|
assert!(profile["network"]["link_mbps"].as_integer().unwrap() >= 1_000);
|
|
assert!(!profile["software"]["kernel"].as_str().unwrap().is_empty());
|
|
assert!(!profile["software"]["proxy"].as_str().unwrap().is_empty());
|
|
}
|
|
assert_eq!(
|
|
profiles[1]["network"]["link_mbps"].as_integer(),
|
|
Some(10_000),
|
|
"release profile must have deployed-network headroom"
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn federation_workload_freezes_projection_rtt_partition_and_digest_gates() {
|
|
let text =
|
|
std::fs::read_to_string(repository_root().join("bench/workloads/federation.toml")).unwrap();
|
|
let workload: toml::Value = toml::from_str(&text).unwrap();
|
|
let projections: Vec<&str> = workload["projections"]
|
|
.as_array()
|
|
.unwrap()
|
|
.iter()
|
|
.map(|v| v.as_str().unwrap())
|
|
.collect();
|
|
assert_eq!(projections, vec!["full", "release", "metadata"]);
|
|
let rtts: Vec<i64> = workload["round_trip_milliseconds"]
|
|
.as_array()
|
|
.unwrap()
|
|
.iter()
|
|
.map(|v| v.as_integer().unwrap())
|
|
.collect();
|
|
assert_eq!(rtts, vec![1, 20, 80], "plan §8: 1ms/20ms/80ms RTT");
|
|
// Plan §8 requires testing across "RTT, loss, a 10-minute partition"; the
|
|
// loss dimension must actually be present, not only RTT/partition.
|
|
assert!(
|
|
!workload["packet_loss_percent"]
|
|
.as_array()
|
|
.unwrap()
|
|
.is_empty(),
|
|
"federation workload must exercise packet loss, not just RTT"
|
|
);
|
|
assert_eq!(workload["partition_seconds"].as_integer(), Some(600));
|
|
assert_eq!(
|
|
workload["same_rack_ref_lag_p99_seconds"].as_integer(),
|
|
Some(5)
|
|
);
|
|
assert_eq!(workload["wan_ref_lag_p99_seconds"].as_integer(), Some(30));
|
|
assert_eq!(
|
|
workload["partition_catchup_seconds"].as_integer(),
|
|
Some(120)
|
|
);
|
|
assert_eq!(
|
|
workload["maximum_source_degradation_percent"].as_integer(),
|
|
Some(10)
|
|
);
|
|
assert_eq!(workload["require_source_restart"].as_bool(), Some(true));
|
|
assert_eq!(
|
|
workload["require_mirror_restart_during_apply"].as_bool(),
|
|
Some(true)
|
|
);
|
|
assert_eq!(
|
|
workload["require_exact_projection_digest"].as_bool(),
|
|
Some(true)
|
|
);
|
|
assert_eq!(
|
|
workload["require_atomic_destination_generation"].as_bool(),
|
|
Some(true)
|
|
);
|
|
}
|