//! The crash-recovery harness (Agent D's gate; v0 acceptance criterion 2) and //! the manifest-selection harness (QUICKSTART, Agent F). //! //! Agent D shipped [`epiphany_bundle`] with its own authoritative gates in //! [`epiphany_bundle::fuzz`]; the testkit's job is to be the cross-cutting CI //! entry point that drives them, *and* to exercise the same contract through the //! bundle's fully public API as an external consumer would — which is what //! [`assert_recovers`], [`exhaustive_crash_sweep`], and [`run_crash_recovery`] //! do here. The crash-recovery property (Chapter 8, the most important single //! test in the prototype): //! //! > Kill the process between any two syscalls in the commit protocol; reopen; //! > the bundle must be valid in 100% of runs, and must recover to the previous //! > generation when the crash precedes the durable flush. use epiphany_bundle::{ encode_block, envelope_offsets, pack_operation_blocks, BlockStore, Bundle, BundleError, CommitContext, CrashPoint, DocumentId, ExtensionDeclaration, ExtensionId, FaultStore, FileUuid, IndexedBlock, Manifest, MemStore, OperationIndex, SchemaVersion, SemVer, Slot, StagedChunk, Tear, }; use epiphany_determinism::CanonicalEncode; use epiphany_ops::{peek_operation_id, OperationEnvelope}; /// Stages real operation envelopes into a single op-envelope block, **deriving** /// the block's schema version from its operations: the block major is the max /// over `OperationEnvelope::schema_major` under minimal stamping (a v1 /// `CreateRegion` → major 1; a v2 cross-cutting/staff/metadata value → /// major 2), mapped to a version by [`SchemaVersion::for_major`]. This is the /// writer-side derivation every real-envelope staging path must use so a /// block carrying a versioned payload is never mis-stamped major 0. pub fn stage_operation_block(envelopes: &[OperationEnvelope]) -> StagedChunk { let payloads: Vec> = envelopes.iter().map(|e| e.to_canonical_bytes()).collect(); let major = envelopes .iter() .map(|e| e.schema_major()) .max() .unwrap_or(0); StagedChunk::operation_block_versioned(encode_block(&payloads), SchemaVersion::for_major(major)) } use crate::generators; use crate::rng::Rng; // --- Re-exported authoritative gates from Agent D -------------------------- /// Agent D's exhaustive per-syscall crash sweep for one commit. pub use epiphany_bundle::fuzz::exhaustive_crash_check as bundle_exhaustive_crash_check; /// Agent D's randomized crash-recovery fuzzer (the 10,000-iteration gate). pub use epiphany_bundle::fuzz::run_crash_recovery_fuzz as bundle_crash_recovery_fuzz; /// Agent D's manifest-selection harness over every corruption scenario. pub use epiphany_bundle::fuzz::run_manifest_selection_harness as bundle_manifest_selection; // --- Testkit-authored drivers through the public API ----------------------- /// The commit-context closure: append the commit's new chunks to the previous /// manifest's `operation_roots`. fn append_roots(ctx: &CommitContext) -> Manifest { let mut m = ctx.previous_manifest.clone(); m.operation_roots.extend(ctx.new_chunks.iter().copied()); m } fn staged(payloads: &[Vec]) -> Vec { pack_operation_blocks(payloads) .into_iter() .map(StagedChunk::operation_block) .collect() } /// Builds a fresh bundle image and advances it through `commits` clean commits, /// returning `(image_bytes, generation)`. Pure public API. pub fn build_base(rng: &mut Rng, commits: u64) -> (Vec, u64) { let uuid = FileUuid(rng.array16()); let doc = DocumentId(rng.array16()); let mut bundle = Bundle::create(MemStore::new(), uuid, Manifest::empty(doc)).expect("create bundle"); for _ in 0..commits { let n = rng.range_usize(0, 2); let payloads: Vec> = (0..n).map(|_| rng.byte_vec(1, 40)).collect(); bundle .commit(&staged(&payloads), append_roots) .expect("commit"); } let generation = bundle.generation(); (bundle.into_store().into_bytes(), generation) } /// Drives one commit over a [`FaultStore`] crashing at `crash`, recovers from /// the durable image, and asserts the five crash-safety properties (Chapter 8). /// Panics with context on any violation — the gate's failure condition. pub fn assert_recovers( base_image: &[u8], base_gen: u64, envelope_payloads: &[Vec], crash: CrashPoint, ) { let blocks = staged(envelope_payloads); let mut bundle = Bundle::open(FaultStore::new(base_image.to_vec(), crash)) .expect("the base image must open before the commit"); let committed = bundle.commit(&blocks, append_roots).is_ok(); let store = bundle.into_store(); // Recover from exactly the bytes that survived the crash. let durable = store.durable_image(); let recovered = Bundle::open(MemStore::from_bytes(durable)).unwrap_or_else(|e| { panic!("crash at {crash:?} left an UNOPENABLE bundle (base gen {base_gen}): {e}") }); let g = recovered.generation(); assert!( g == base_gen || g == base_gen + 1, "crash at {crash:?}: recovered to generation {g}, expected {base_gen} or {}", base_gen + 1 ); if committed { assert_eq!( g, base_gen + 1, "a commit that returned Ok must be durable at the new generation" ); } // A clean crash before the durable flush recovers to the previous generation. if !committed && crash.tear == Tear::Clean { assert_eq!( g, base_gen, "clean crash at {crash:?}: an aborted commit must recover to the previous generation" ); } assert!( recovered.anomalies().is_empty(), "crash at {crash:?}: recovery reported anomalies {:?}", recovered.anomalies() ); assert!( !recovered.is_read_only(), "crash at {crash:?}: recovery opened read-only" ); recovered .verify_canonical_chunks() .unwrap_or_else(|e| panic!("crash at {crash:?}: a canonical chunk is corrupt: {e}")); } /// Exhaustively crashes a single commit: every syscall boundary from 0 through /// the commit's total syscall count, crossed with a set of tear points. This is /// the strongest crash-safety guarantee — no syscall boundary untested. Pure /// public API. pub fn exhaustive_crash_sweep(base_image: &[u8], base_gen: u64, envelope_payloads: &[Vec]) { let blocks = staged(envelope_payloads); // Learn the commit's total syscall count via a no-fault run (and confirm the // clean commit reaches G+1). let total = { let mut bundle = Bundle::open(FaultStore::no_fault(base_image.to_vec())).unwrap(); bundle.commit(&blocks, append_roots).unwrap(); let store = bundle.into_store(); let recovered = Bundle::open(store.recover()).unwrap(); assert_eq!(recovered.generation(), base_gen + 1); store.syscalls_issued() }; let tears = || { let mut v = vec![Tear::Clean]; for prefix in [0usize, 1, 100, 128, 251, 252, 253, 255, 256] { v.push(Tear::TornLastWrite { prefix }); } v }; for k in 0..=total { for tear in tears() { assert_recovers( base_image, base_gen, envelope_payloads, CrashPoint { after_syscalls: k, tear, }, ); } } } /// The testkit's randomized crash-recovery driver (acceptance criterion 2): /// `iters` scenarios from `seed`, each a random base generation, random commit /// shape, and random crash point + tear. Deterministic; a failure reproduces /// from its seed. pub fn run_crash_recovery(iters: u64, seed: u64) { let mut rng = Rng::new(seed); for _ in 0..iters { let base_commits = rng.below(4); let (image, generation) = build_base(&mut rng, base_commits); let n = rng.range_usize(0, 3); let payloads: Vec> = (0..n).map(|_| rng.byte_vec(1, 64)).collect(); let tear = if rng.boolean() { Tear::Clean } else { let prefix = match rng.below(6) { 0 => 0, 1 => 1, 2 => 252, 3 => 255, 4 => 256, _ => rng.below(256) as usize, }; Tear::TornLastWrite { prefix } }; let crash = CrashPoint { after_syscalls: rng.below(16) as u32, tear, }; assert_recovers(&image, generation, &payloads, crash); } } /// A testkit-level manifest-selection check through the *normal commit /// protocol*: after several commits the bundle reopens at the highest generation /// and the active slot alternates A/B per commit (Chapter 8 §"Superblock /// Selection"). Complements [`bundle_manifest_selection`], which assembles /// corrupt images directly. pub fn assert_selection_through_commits(seed: u64) { let mut rng = Rng::new(seed); let uuid = FileUuid(rng.array16()); let doc = DocumentId(rng.array16()); let mut bundle = Bundle::create(MemStore::new(), uuid, Manifest::empty(doc)).expect("create bundle"); // Generation 0 lives in slot A; each commit flips the active slot. assert_eq!(bundle.active_slot(), Slot::A); let commits = 5u64; for i in 1..=commits { let payloads = vec![rng.byte_vec(1, 50)]; bundle .commit(&staged(&payloads), append_roots) .expect("commit"); assert_eq!(bundle.generation(), i); let expected = if i % 2 == 1 { Slot::B } else { Slot::A }; assert_eq!(bundle.active_slot(), expected, "active slot must alternate"); } let image = bundle.into_store().into_bytes(); // Reopen: selection picks the highest committed generation, no anomaly. let reopened = Bundle::open(MemStore::from_bytes(image)).expect("reopen"); assert_eq!(reopened.generation(), commits); assert!(reopened.anomalies().is_empty()); assert!(!reopened.is_read_only()); reopened.verify_canonical_chunks().expect("chunks intact"); } /// Runs both the authoritative bundle manifest-selection harness and the /// testkit's commit-protocol selection check. pub fn run_manifest_selection(seed: u64) { bundle_manifest_selection(); assert_selection_through_commits(seed); } // --- The operation-index harness (Chapter 8 §"The Operation Index") -------- // // The C/D seam: `epiphany-ops` vouches that a canonical envelope *leads* with // its 16 operation-id bytes (`peek_operation_id`); the bundle records those // raw bytes against `(block ChunkRef, offset-in-decoded-payload)` coordinates // (`envelope_offsets` + `OperationIndex`) without ever interpreting an // envelope. The index is an acceleration structure, not canonical: absent → // rebuild by scanning blocks; present but stale or corrupt → reject and // rebuild, never bundle corruption. /// Stages real canonical envelope encodings into operation blocks of at most /// `per_block` envelopes each (forcing a multi-block layout regardless of the /// 1 MiB soft target, so index ordinals are actually exercised). fn staged_envelope_blocks(envelopes: &[OperationEnvelope], per_block: usize) -> Vec { // Each block derives its schema version from its own operations, so a group // containing a v1 CreateRegion is stamped major 1 (not V0). envelopes .chunks(per_block) .map(stage_operation_block) .collect() } /// The reader-side rebuild the spec mandates when no usable index exists: /// scan every operation block the manifest references, recover each /// envelope's `(offset, bytes)` with [`envelope_offsets`], peek the leading /// operation-id bytes with ops' [`peek_operation_id`], and build a fresh /// [`OperationIndex`]. The same procedure serves the writer at commit time /// (the spec's SHOULD-rebuild-on-commit), from the committed block refs. pub fn scan_rebuild_operation_index(bundle: &Bundle) -> OperationIndex { let blocks: Vec = bundle .manifest() .operation_roots .iter() .map(|root| { let payload = bundle.read_chunk(root).expect("operation block reads"); let entries = envelope_offsets(&payload) .expect("a committed block payload decodes") .into_iter() .map(|(offset, bytes)| { let id = peek_operation_id(bytes) .expect("a canonical envelope leads with its 16 id bytes"); (id.canonical_bytes(), offset) }) .collect(); (*root, entries) }) .collect(); OperationIndex::build(&blocks).expect("scanned blocks build a valid index") } /// Commits `index` as an operation-index chunk and wires it into the /// manifest's `operation_index_root` — the writer's commit-time update. fn commit_index(bundle: &mut Bundle, index: &OperationIndex) { bundle .commit(&[StagedChunk::operation_index(index.encode())], |ctx| { let mut m = ctx.previous_manifest.clone(); m.operation_index_root = Some(ctx.new_chunks[0]); m }) .expect("commit operation index"); } /// Locates every envelope through the index and verifies each `(block, /// offset)` coordinate addresses exactly that envelope's canonical bytes in /// the block's decoded payload — plus a miss for an id no envelope uses. fn assert_index_locates_all( bundle: &Bundle, index: &OperationIndex, envelopes: &[OperationEnvelope], ) { for env in envelopes { let id = env.id.canonical_bytes(); let (block_ref, offset) = index .locate(&id) .unwrap_or_else(|| panic!("operation {:?} missing from the index", env.id)); let payload = bundle.read_chunk(block_ref).expect("indexed block reads"); let pairs = envelope_offsets(&payload).expect("indexed block payload decodes"); let (_, bytes) = pairs .iter() .find(|(off, _)| *off == offset) .expect("the indexed offset lands on an envelope boundary"); assert_eq!( *bytes, env.to_canonical_bytes().as_slice(), "the (block, offset) coordinate must address exactly this envelope's bytes" ); // The bundle-opaque bytes really lead with this operation's id — the // ops-side half of the seam. assert_eq!(peek_operation_id(bytes), Some(env.id)); // The kind-checked block read path sees the same envelope. let envs = bundle .read_operation_block(block_ref) .expect("kind-checked block read"); assert!(envs.iter().any(|e| e.as_slice() == *bytes)); } assert_eq!( index.locate(&[0xFF; 16]), None, "an id no envelope uses must miss" ); } /// End-to-end: generate real envelopes, pack multi-block, commit, build the /// index at commit time and wire it into `operation_index_root`, reopen, and /// verify the index is usable and locates every operation at its exact bytes. pub fn assert_operation_index_end_to_end(seed: u64) { let mut rng = Rng::new(seed); let envelopes = generators::operation_envelopes(&mut rng, 36, 3, 8, 8); let uuid = FileUuid(rng.array16()); let doc = DocumentId(rng.array16()); let mut bundle = Bundle::create(MemStore::new(), uuid, Manifest::empty(doc)).expect("create bundle"); bundle .commit(&staged_envelope_blocks(&envelopes, 12), append_roots) .expect("commit operation blocks"); // Writer side: rebuild the index at commit time from the committed block // refs (the spec's SHOULD) and reference it from the manifest. let index = scan_rebuild_operation_index(&bundle); assert!( index.blocks().len() >= 2, "the fixture must span multiple blocks to exercise ordinals" ); assert_eq!(index.entries().len(), envelopes.len()); commit_index(&mut bundle, &index); // Reader side: reopen from the durable image; the fresh index is usable // and locates every operation. let image = bundle.into_store().into_bytes(); let reopened = Bundle::open(MemStore::from_bytes(image)).expect("reopen"); let usable = reopened .usable_operation_index() .expect("a fresh, covering index is usable"); assert_eq!(usable, index, "the index round-trips through storage"); assert_index_locates_all(&reopened, &usable, &envelopes); } /// A commit that grows the operation set *without* updating the index leaves /// a stale index: intact as a chunk, but no longer covering the operation /// roots. Readers must reject it (`usable_operation_index` → `None`) and a /// scan-rebuild must produce a fresh valid index (spec §"The Operation /// Index": present but stale → reject and rebuild from blocks). pub fn assert_stale_operation_index_rejected_and_rebuilt(seed: u64) { let mut rng = Rng::new(seed); // One authoring session, so ids are unique across both phases. let envelopes = generators::operation_envelopes(&mut rng, 48, 3, 8, 8); let (first, later) = envelopes.split_at(36); let uuid = FileUuid(rng.array16()); let doc = DocumentId(rng.array16()); let mut bundle = Bundle::create(MemStore::new(), uuid, Manifest::empty(doc)).expect("create bundle"); bundle .commit(&staged_envelope_blocks(first, 12), append_roots) .expect("commit first blocks"); let index = scan_rebuild_operation_index(&bundle); commit_index(&mut bundle, &index); assert!(bundle.usable_operation_index().is_some()); // Grow the operation set WITHOUT updating the index (the closure carries // the old `operation_index_root` forward). bundle .commit(&staged_envelope_blocks(later, 6), append_roots) .expect("commit later blocks"); let root = bundle .manifest() .operation_index_root .expect("the stale index is still referenced"); // The chunk itself is intact — readable and well-formed — let stale = bundle .read_operation_index(&root) .expect("the stale index chunk still reads and decodes"); // — but STALE: its block set no longer equals the operation roots. assert!(!stale.covers(&bundle.manifest().operation_roots)); assert!( bundle.usable_operation_index().is_none(), "a stale index must be rejected" ); // The same verdict from a cold reopen; then rebuild from blocks. let image = bundle.into_store().into_bytes(); let mut reopened = Bundle::open(MemStore::from_bytes(image)).expect("reopen"); assert!(reopened.usable_operation_index().is_none()); let rebuilt = scan_rebuild_operation_index(&reopened); commit_index(&mut reopened, &rebuilt); let usable = reopened .usable_operation_index() .expect("the rebuilt index is usable"); assert_eq!(usable, rebuilt); assert_index_locates_all(&reopened, &usable, &envelopes); } /// A defective operation index — a garbage payload staged as the index chunk, /// or on-disk corruption of a valid index chunk's bytes — must be rejected /// *without* being treated as bundle corruption (Chapter 8 §"Canonical and /// Non-Canonical Manifest Roots"): the bundle still opens cleanly, canonical /// chunks verify, canonical reads work, and only `usable_operation_index` /// says `None` (rebuild from blocks). pub fn assert_corrupt_operation_index_is_not_bundle_corruption(seed: u64) { let mut rng = Rng::new(seed); let envelopes = generators::operation_envelopes(&mut rng, 24, 3, 8, 8); let uuid = FileUuid(rng.array16()); let doc = DocumentId(rng.array16()); let mut bundle = Bundle::create(MemStore::new(), uuid, Manifest::empty(doc)).expect("create bundle"); bundle .commit(&staged_envelope_blocks(&envelopes, 8), append_roots) .expect("commit operation blocks"); // (a) A garbage index chunk, staged and referenced like a real one. The // commit succeeds (the bundle does not interpret non-canonical chunks); // readers must reject it and rebuild. bundle .commit( &[StagedChunk::operation_index(b"not an index".to_vec())], |ctx| { let mut m = ctx.previous_manifest.clone(); m.operation_index_root = Some(ctx.new_chunks[0]); m }, ) .expect("commit garbage index chunk"); let image = bundle.into_store().into_bytes(); let mut reopened = Bundle::open(MemStore::from_bytes(image)) .expect("a defective index must not prevent opening"); assert!(reopened.anomalies().is_empty()); assert!(!reopened.is_read_only()); reopened .verify_canonical_chunks() .expect("canonical chunks are intact"); assert!( reopened.usable_operation_index().is_none(), "a malformed index is rejected: rebuild, not corruption" ); for root in &reopened.manifest().operation_roots.clone() { reopened .read_operation_block(root) .expect("canonical block reads are unaffected"); } // Rebuild-from-blocks restores a usable index. let rebuilt = scan_rebuild_operation_index(&reopened); commit_index(&mut reopened, &rebuilt); let usable = reopened .usable_operation_index() .expect("the rebuilt index is usable"); assert_index_locates_all(&reopened, &usable, &envelopes); // (b) On-disk corruption of the now-valid index chunk's payload region. let valid_image = reopened.into_store().into_bytes(); let probe = Bundle::open(MemStore::from_bytes(valid_image.clone())).expect("reopen"); let root = probe .manifest() .operation_index_root .expect("the index is referenced"); let mut corrupt = valid_image; corrupt[(root.offset + 3) as usize] ^= 0xFF; let reopened = Bundle::open(MemStore::from_bytes(corrupt)) .expect("index-region corruption must not prevent opening"); assert!(reopened.anomalies().is_empty()); reopened .verify_canonical_chunks() .expect("canonical chunks are intact"); // The raw read surfaces the hash defect for diagnostics … assert!( matches!( reopened.read_operation_index(&root), Err(BundleError::ChunkHashMismatch { .. }) ), "the raw index read reports the hash mismatch" ); // … while the reject-and-rebuild gate simply declares it unusable. assert!(reopened.usable_operation_index().is_none()); for root in &reopened.manifest().operation_roots { reopened .read_operation_block(root) .expect("canonical block reads are unaffected"); } } // --- The edit-barrier declaration harness (Chapter 8 §"Forward Compatibility // and Edit Barriers" / §"Behavior Under Unknown Extensions") ------------------ /// The end-to-end edit-barrier round-trip: a manifest [`ExtensionDeclaration`] /// carrying *really-encoded* barrier blobs (the provisional canonical byte /// form owned by `epiphany-layout-ir`) commits, reopens byte-verbatim — the /// bundle preserves the blobs opaquely, exactly as it preserves unknown /// extension chunks — decodes through the owning layer's codec, and /// *evaluates*: the reopened barrier prohibits exactly the edits the authored /// one did. pub fn run_barrier_declaration_roundtrip(seed: u64) { use epiphany_core::{EventId, TypedObjectId}; use epiphany_layout_ir::{ decode_affected_object_kinds, decode_edit_barriers, encode_affected_object_kinds, encode_edit_barriers, AlwaysLiveOracle, BarrierCondition, BarrierScope, EditBarrier, EditContext, ObjectKind, OperationKindTag, }; let mut rng = Rng::new(seed); let protected = TypedObjectId::Event(EventId::from_raw(rng.next_u64() as u128)); // One deterministic barrier the evaluation half asserts against, plus one // generated barrier so arbitrary shapes ride the same declaration. let locked_barrier = EditBarrier { scope: BarrierScope::ObjectSet(vec![protected]), affected_object_kinds: vec![ObjectKind::of(&protected)], prohibited_operation_kinds: vec![OperationKindTag::DeleteEvent], condition: BarrierCondition::Always, }; let authored = vec![ locked_barrier.clone(), crate::layout_stub::gen_edit_barrier(&mut rng), ]; let kinds = vec![ObjectKind::of(&protected)]; let declaration = ExtensionDeclaration { extension_id: ExtensionId(rng.array16()), version: SemVer { major: 1, minor: 0, patch: 0, }, required: false, preserved_chunk_roots: Vec::new(), affected_object_kinds: encode_affected_object_kinds(&kinds), edit_barriers: encode_edit_barriers(&authored), }; let extension_id = declaration.extension_id; let barrier_bytes = declaration.edit_barriers.clone(); let kind_bytes = declaration.affected_object_kinds.clone(); // Commit a manifest carrying the declaration; reopen from the raw image. let uuid = FileUuid(rng.array16()); let doc = DocumentId(rng.array16()); let mut bundle = Bundle::create(MemStore::new(), uuid, Manifest::empty(doc)).expect("create bundle"); bundle .commit(&staged(&[rng.byte_vec(1, 40)]), |ctx| { let mut m = append_roots(ctx); m.extension_declarations.push(declaration.clone()); m }) .expect("commit the declaration"); let image = bundle.into_store().into_bytes(); let reopened = Bundle::open(MemStore::from_bytes(image)).expect("reopen"); // The bundle preserved the opaque blobs verbatim ... let decl = reopened .manifest() .extension_declarations .iter() .find(|d| d.extension_id == extension_id) .expect("the declaration survives the commit"); assert_eq!( decl.edit_barriers, barrier_bytes, "barrier blob is verbatim" ); assert_eq!( decl.affected_object_kinds, kind_bytes, "object-kind blob is verbatim" ); // ... the owning layer's codec decodes them, re-encoding byte-identically ... let decoded = decode_edit_barriers(&decl.edit_barriers).expect("the canonical blob decodes"); assert_eq!( encode_edit_barriers(&decoded), barrier_bytes, "decode → re-encode is byte-identical" ); assert_eq!( decode_affected_object_kinds(&decl.affected_object_kinds).expect("kinds decode"), kinds ); // ... and the reopened barrier evaluates as authored: deleting the // protected event is prohibited; a different operation class and a // different object are not. let reopened_barrier = decoded .iter() .find(|b| **b == locked_barrier) .expect("the locked barrier survives the round-trip"); let ctx = EditContext::default(); assert!(reopened_barrier.prohibits_edit( OperationKindTag::DeleteEvent, &protected, &ctx, &AlwaysLiveOracle )); assert!(!reopened_barrier.prohibits_edit( OperationKindTag::InsertEvent, &protected, &ctx, &AlwaysLiveOracle )); let other = TypedObjectId::Event(EventId::from_raw(u128::MAX)); assert!(!reopened_barrier.prohibits_edit( OperationKindTag::DeleteEvent, &other, &ctx, &AlwaysLiveOracle )); } #[cfg(test)] mod tests { use super::*; #[test] fn barrier_declaration_roundtrip() { for seed in 0..8u64 { run_barrier_declaration_roundtrip(0xBA22_1E20_0000 + seed); } } #[test] fn crash_recovery_smoke() { run_crash_recovery(400, 0xF00D_BEEF_1234_5678); } #[test] fn exhaustive_sweep_representative_commit() { let mut rng = Rng::new(0x00A1_1CE5); let (image, generation) = build_base(&mut rng, 2); exhaustive_crash_sweep(&image, generation, &[b"alpha".to_vec(), b"beta".to_vec()]); } #[test] fn manifest_selection_passes() { run_manifest_selection(7); } #[test] fn operation_index_end_to_end() { assert_operation_index_end_to_end(0x0091_D0EC_5EED_0001); } #[test] fn stale_operation_index_is_rejected_and_rebuilt() { assert_stale_operation_index_rejected_and_rebuilt(0x0091_D0EC_5EED_0002); } #[test] fn corrupt_operation_index_is_not_bundle_corruption() { assert_corrupt_operation_index_is_not_bundle_corruption(0x0091_D0EC_5EED_0003); } }