use super::compact_engine_config;
use crate::{
merkle::{self, Location},
qmdb::{
self,
sync::{
self, Source as _,
source::tests::{SequenceSource, fetch_compact_state},
},
verify_proof_and_pinned_nodes,
},
};
use commonware_cryptography::{Sha256, sha256};
use commonware_runtime::{Runner as _, Supervisor as _, deterministic};
use commonware_utils::NZU64;
use rand::Rng as _;
use std::{fmt::Debug, future::Future, num::NonZeroU64, sync::Arc};
pub(crate) type CompactOpOf<H> = <<H as CompactSyncTestHarness>::Db as qmdb::sync::Database>::Op;
pub(crate) type CompactConfigOf<H> =
<<H as CompactSyncTestHarness>::Db as qmdb::sync::Database>::Config;
pub(crate) trait CompactSyncTestHarness: Sized + 'static {
type Family: merkle::Family;
type Db: qmdb::sync::Database<
Family = Self::Family,
Context = deterministic::Context,
Digest = sha256::Digest,
Op: Clone,
Config: Clone,
> + sync::Source<
Family = Self::Family,
Digest = sha256::Digest,
Op = CompactOpOf<Self>,
Error = qmdb::Error<Self::Family>,
>;
type Full: sync::Source<
Family = Self::Family,
Digest = sha256::Digest,
Op = CompactOpOf<Self>,
Error = qmdb::Error<Self::Family>,
>;
type Value: Clone + PartialEq + Debug + Send + Sync + 'static;
fn config(suffix: &str) -> CompactConfigOf<Self>;
fn with_witness_items_per_section(
config: CompactConfigOf<Self>,
items_per_section: NonZeroU64,
) -> CompactConfigOf<Self>;
fn value(seed: u8) -> Self::Value;
fn init(
ctx: deterministic::Context,
config: CompactConfigOf<Self>,
max_size: Option<Location<Self::Family>>,
) -> impl Future<Output = Self::Db> + Send;
fn init_full(
ctx: deterministic::Context,
suffix: &str,
) -> impl Future<Output = Self::Full> + Send;
fn import(
ctx: deterministic::Context,
config: &CompactConfigOf<Self>,
last_commit_loc: Location<Self::Family>,
pinned_nodes: Vec<sha256::Digest>,
op: CompactOpOf<Self>,
) -> impl Future<Output = Result<Self::Db, qmdb::Error<Self::Family>>> + Send;
fn destroy(db: Self::Db) -> impl Future<Output = ()> + Send;
fn destroy_full(full: Self::Full) -> impl Future<Output = ()> + Send;
fn apply(
db: Self::Db,
values: &[Self::Value],
metadata: Option<Self::Value>,
floor: Location<Self::Family>,
) -> impl Future<Output = Self::Db> + Send;
fn apply_full(
full: Self::Full,
values: &[Self::Value],
metadata: Option<Self::Value>,
floor: Location<Self::Family>,
) -> impl Future<Output = Self::Full> + Send;
fn sync(db: Self::Db) -> impl Future<Output = Self::Db> + Send;
fn commit_full(full: Self::Full) -> impl Future<Output = Self::Full> + Send;
fn prune(
db: Self::Db,
loc: Location<Self::Family>,
) -> impl Future<Output = Result<Self::Db, qmdb::Error<Self::Family>>> + Send;
fn root(db: &Self::Db) -> sha256::Digest;
fn target(db: &Self::Db) -> sync::CompactTarget<Self::Family, sha256::Digest>;
fn size(db: &Self::Db) -> Location<Self::Family>;
fn inactivity_floor_loc(db: &Self::Db) -> Location<Self::Family>;
fn metadata(db: &Self::Db) -> Option<Self::Value>;
fn full_root(full: &Self::Full) -> sha256::Digest;
fn full_bounds(full: &Self::Full) -> std::ops::Range<Location<Self::Family>>;
fn with_commit_floor(op: CompactOpOf<Self>, floor: Location<Self::Family>)
-> CompactOpOf<Self>;
}
pub(crate) fn test_compact_full_source_missing_reports_missing_source<H: CompactSyncTestHarness>() {
deterministic::Runner::default().start(|_context| async move {
let source: Arc<commonware_utils::sync::AsyncRwLock<Option<H::Full>>> =
Arc::new(commonware_utils::sync::AsyncRwLock::new(None));
let target = sync::CompactTarget {
root: sha256::Digest::from([0; 32]),
size: Location::new(1),
};
assert!(matches!(
fetch_compact_state(&source, target).await,
Err(sync::ServeError::MissingSource)
));
});
}
pub(crate) fn test_compact_sync_roundtrip<H: CompactSyncTestHarness>() {
deterministic::Runner::default().start(|mut context| async move {
let suffix = format!("compact-{}", context.next_u64());
let source = H::init_full(context.child("source"), &suffix).await;
let metadata = H::value(9);
let floor = Location::new(2);
let source = H::apply_full(
source,
&[H::value(1), H::value(4)],
Some(metadata.clone()),
floor,
)
.await;
let source = H::commit_full(source).await;
let bounds = H::full_bounds(&source);
let target = sync::CompactTarget {
root: H::full_root(&source),
size: bounds.end,
};
let source = Arc::new(source);
let client_cfg = H::config(&suffix);
let client: H::Db = sync::sync(compact_engine_config(
context.child("client"),
source.clone(),
target.clone(),
client_cfg.clone(),
))
.await
.unwrap();
assert_eq!(H::root(&client), target.root);
assert_eq!(H::metadata(&client), Some(metadata.clone()));
assert_eq!(H::inactivity_floor_loc(&client), floor);
drop(client);
let reopened = H::init(context.child("reopen"), client_cfg, None).await;
assert_eq!(H::root(&reopened), target.root);
assert_eq!(H::metadata(&reopened), Some(metadata));
assert_eq!(H::inactivity_floor_loc(&reopened), floor);
H::destroy(reopened).await;
let source = Arc::try_unwrap(source).unwrap_or_else(|_| panic!("single source ref"));
H::destroy_full(source).await;
});
}
pub(crate) fn test_compact_sync_recovers_after_invalid_proof<H: CompactSyncTestHarness>() {
deterministic::Runner::default().start(|mut context| async move {
let suffix = format!("compact-bad-proof-{}", context.next_u64());
let source = H::init_full(context.child("source"), &suffix).await;
let source =
H::apply_full(source, &[H::value(7)], Some(H::value(1)), Location::new(1)).await;
let source = H::commit_full(source).await;
let bounds = H::full_bounds(&source);
let target = sync::CompactTarget {
root: H::full_root(&source),
size: bounds.end,
};
let source = Arc::new(source);
let good_state = fetch_compact_state(&source, target.clone()).await.unwrap();
let mut bad_state = good_state.clone();
let sync::Response::Boundary { proof, .. } = &mut bad_state else {
unreachable!("boundary fetch returns a boundary response");
};
proof.digests.push(sha256::Digest::from([0xee; 32]));
let client: H::Db = sync::sync(compact_engine_config(
context.child("client"),
SequenceSource::new(vec![bad_state, good_state]),
target.clone(),
H::config(&suffix),
))
.await
.unwrap();
assert_eq!(H::root(&client), target.root);
H::destroy(client).await;
let source = Arc::try_unwrap(source).unwrap_or_else(|_| panic!("single source ref"));
H::destroy_full(source).await;
});
}
pub(crate) fn test_compact_sync_recovers_after_tampered_commit_floor<H: CompactSyncTestHarness>() {
deterministic::Runner::default().start(|mut context| async move {
let suffix = format!("compact-bad-floor-{}", context.next_u64());
let source = H::init_full(context.child("source"), &suffix).await;
let source =
H::apply_full(source, &[H::value(7)], Some(H::value(1)), Location::new(1)).await;
let source = H::commit_full(source).await;
let bounds = H::full_bounds(&source);
let target = sync::CompactTarget {
root: H::full_root(&source),
size: bounds.end,
};
let source = Arc::new(source);
let good_state = fetch_compact_state(&source, target.clone()).await.unwrap();
let mut bad_state = good_state.clone();
let sync::Response::Boundary { op, .. } = &mut bad_state else {
unreachable!("boundary fetch returns a boundary response");
};
*op = H::with_commit_floor(op.clone(), Location::new(0));
let sequence = SequenceSource::new(vec![bad_state, good_state]);
let client: H::Db = sync::sync(compact_engine_config(
context.child("client"),
sequence.clone(),
target.clone(),
H::config(&suffix),
))
.await
.unwrap();
assert_eq!(sequence.take_verdicts().await, vec![false, true]);
assert_eq!(H::root(&client), target.root);
H::destroy(client).await;
let source = Arc::try_unwrap(source).unwrap_or_else(|_| panic!("single source ref"));
H::destroy_full(source).await;
});
}
pub(crate) fn test_compact_sync_recovers_after_size_mismatch<H: CompactSyncTestHarness>() {
deterministic::Runner::default().start(|mut context| async move {
let suffix = format!("compact-bad-leaf-count-{}", context.next_u64());
let source = H::init_full(context.child("source"), &suffix).await;
let source =
H::apply_full(source, &[H::value(7)], Some(H::value(1)), Location::new(1)).await;
let source = H::commit_full(source).await;
let bounds = H::full_bounds(&source);
let target = sync::CompactTarget {
root: H::full_root(&source),
size: bounds.end,
};
let source = Arc::new(source);
let good_state = fetch_compact_state(&source, target.clone()).await.unwrap();
let mut bad_state = good_state.clone();
let sync::Response::Boundary { proof, .. } = &mut bad_state else {
unreachable!("boundary fetch returns a boundary response");
};
proof.leaves -= 1;
let client: H::Db = sync::sync(compact_engine_config(
context.child("client"),
SequenceSource::new(vec![bad_state, good_state]),
target.clone(),
H::config(&suffix),
))
.await
.unwrap();
assert_eq!(H::root(&client), target.root);
H::destroy(client).await;
let source = Arc::try_unwrap(source).unwrap_or_else(|_| panic!("single source ref"));
H::destroy_full(source).await;
});
}
pub(crate) fn test_compact_sync_recovers_after_tampered_pinned_nodes<H: CompactSyncTestHarness>() {
deterministic::Runner::default().start(|mut context| async move {
let suffix = format!("compact-bad-pinned-nodes-{}", context.next_u64());
let source = H::init_full(context.child("source"), &suffix).await;
let source = H::apply_full(
source,
&[H::value(1), H::value(4)],
Some(H::value(7)),
Location::new(2),
)
.await;
let source = H::commit_full(source).await;
let bounds = H::full_bounds(&source);
let target = sync::CompactTarget {
root: H::full_root(&source),
size: bounds.end,
};
let source = Arc::new(source);
let good_state = fetch_compact_state(&source, target.clone()).await.unwrap();
let mut bad_state = good_state.clone();
let sync::Response::Boundary { pinned_nodes, .. } = &mut bad_state else {
unreachable!("boundary fetch returns a boundary response");
};
pinned_nodes[0] = sha256::Digest::from([0xaa; 32]);
let sequence = SequenceSource::new(vec![bad_state, good_state]);
let client_cfg = H::config(&suffix);
let synced: H::Db = sync::sync(compact_engine_config(
context.child("client"),
sequence.clone(),
target.clone(),
client_cfg.clone(),
))
.await
.unwrap();
assert_eq!(sequence.take_verdicts().await, vec![false, true]);
assert_eq!(H::target(&synced), target);
assert_eq!(H::metadata(&synced), Some(H::value(7)));
drop(synced);
let reopened = H::init(context.child("reopen"), client_cfg, None).await;
assert_eq!(H::target(&reopened), target);
assert_eq!(H::metadata(&reopened), Some(H::value(7)));
H::destroy(reopened).await;
let source = Arc::try_unwrap(source).unwrap_or_else(|_| panic!("single source ref"));
H::destroy_full(source).await;
});
}
pub(crate) fn test_compact_full_source_serves_historical_target<H: CompactSyncTestHarness>() {
deterministic::Runner::default().start(|mut context| async move {
let suffix = format!("compact-stale-full-{}", context.next_u64());
let source = H::init_full(context.child("source"), &suffix).await;
let source =
H::apply_full(source, &[H::value(1)], Some(H::value(1)), Location::new(1)).await;
let source = H::commit_full(source).await;
let stale_target = sync::CompactTarget {
root: H::full_root(&source),
size: H::full_bounds(&source).end,
};
let source =
H::apply_full(source, &[H::value(4)], Some(H::value(2)), Location::new(2)).await;
let source = H::commit_full(source).await;
let current_target = sync::CompactTarget {
root: H::full_root(&source),
size: H::full_bounds(&source).end,
};
assert_ne!(stale_target, current_target);
let source = Arc::new(source);
let client: H::Db = sync::sync(compact_engine_config(
context.child("client"),
source.clone(),
stale_target.clone(),
H::config(&suffix),
))
.await
.unwrap();
assert_eq!(H::root(&client), stale_target.root);
assert_ne!(H::root(&client), current_target.root);
H::destroy(client).await;
let source = Arc::try_unwrap(source).unwrap_or_else(|_| panic!("single source ref"));
H::destroy_full(source).await;
});
}
pub(crate) fn test_compact_source_serves_retained_target<H: CompactSyncTestHarness>() {
deterministic::Runner::default().start(|mut context| async move {
let suffix = format!("compact-retained-{}", context.next_u64());
let source_cfg =
H::with_witness_items_per_section(H::config(&format!("{suffix}-source")), NZU64!(1));
let mut source = H::init(context.child("source"), source_cfg, None).await;
let mut targets = Vec::new();
for i in 1u8..=2 {
let floor = H::inactivity_floor_loc(&source);
source = H::apply(source, &[H::value(i)], Some(H::value(i)), floor).await;
source = H::sync(source).await;
targets.push(H::target(&source));
}
let source = Arc::new(source);
let synced: H::Db = sync::sync(compact_engine_config(
context.child("first"),
source.clone(),
targets[0].clone(),
H::config(&format!("{suffix}-first")),
))
.await
.unwrap();
assert_eq!(H::root(&synced), targets[0].root);
assert_eq!(H::metadata(&synced), Some(H::value(1)));
H::destroy(synced).await;
let source = Arc::try_unwrap(source).unwrap_or_else(|_| panic!("single source ref"));
let source = Arc::new(H::prune(source, targets[1].size).await.unwrap());
let result: Result<H::Db, _> = sync::sync(compact_engine_config(
context.child("pruned"),
source.clone(),
targets[0].clone(),
H::config(&format!("{suffix}-pruned")),
))
.await;
assert!(matches!(
result,
Err(sync::Error::Source(qmdb::Error::Journal(
crate::journal::Error::ItemPruned(_)
)))
));
let source = Arc::try_unwrap(source).unwrap_or_else(|_| panic!("single source ref"));
H::destroy(source).await;
});
}
pub(crate) fn test_compact_source_reopen_bounded_initialization_regrow_and_stale_target<
H: CompactSyncTestHarness,
>() {
deterministic::Runner::default().start(|mut context| async move {
let suffix = format!("compact-unj-source-{}", context.next_u64());
let source_cfg = H::config(&format!("{suffix}-source"));
let source = H::init(context.child("source_init"), source_cfg.clone(), None).await;
let metadata1 = H::value(1);
let floor1 = Location::new(1);
let source = H::apply(source, &[H::value(10)], Some(metadata1.clone()), floor1).await;
let source = H::sync(source).await;
let target1 = H::target(&source);
drop(source);
let source = H::init(context.child("source_reopen"), source_cfg.clone(), None).await;
assert_eq!(H::target(&source), target1);
let serve1_cfg = H::config(&format!("{suffix}-serve1"));
let served1: H::Db = sync::sync(compact_engine_config(
context.child("serve").with_attribute("index", 1),
Arc::new(source),
target1.clone(),
serve1_cfg.clone(),
))
.await
.unwrap();
assert_eq!(H::root(&served1), target1.root);
assert_eq!(H::metadata(&served1), Some(metadata1.clone()));
assert_eq!(H::inactivity_floor_loc(&served1), floor1);
H::destroy(served1).await;
let source = H::init(context.child("source_resume"), source_cfg.clone(), None).await;
let metadata2 = H::value(2);
let floor2 = Location::new(2);
let source = H::apply(source, &[H::value(20)], Some(metadata2.clone()), floor2).await;
let source = H::sync(source).await;
let target2 = H::target(&source);
assert_ne!(target2, target1);
drop(source);
let source = H::init(
context.child("cap_source"),
source_cfg.clone(),
Some(target1.size),
)
.await;
assert_eq!(H::target(&source), target1);
let serve2_cfg = H::config(&format!("{suffix}-serve2"));
let served2: H::Db = sync::sync(compact_engine_config(
context.child("serve").with_attribute("index", 2),
Arc::new(source),
target1.clone(),
serve2_cfg.clone(),
))
.await
.unwrap();
assert_eq!(H::root(&served2), target1.root);
assert_eq!(H::metadata(&served2), Some(metadata1.clone()));
assert_eq!(H::inactivity_floor_loc(&served2), floor1);
H::destroy(served2).await;
let source = H::init(context.child("source_regrow"), source_cfg.clone(), None).await;
assert_eq!(H::target(&source), target1);
let metadata3 = H::value(3);
let floor3 = Location::new(2);
let source = H::apply(source, &[H::value(30)], Some(metadata3.clone()), floor3).await;
let source = H::sync(source).await;
let target3 = H::target(&source);
assert_ne!(target3, target1);
assert_ne!(target3, target2);
let serve3_cfg = H::config(&format!("{suffix}-serve3"));
let served3: H::Db = sync::sync(compact_engine_config(
context.child("serve").with_attribute("index", 3),
Arc::new(source),
target3.clone(),
serve3_cfg.clone(),
))
.await
.unwrap();
assert_eq!(H::root(&served3), target3.root);
assert_eq!(H::metadata(&served3), Some(metadata3.clone()));
assert_eq!(H::inactivity_floor_loc(&served3), floor3);
H::destroy(served3).await;
let source =
Arc::new(H::init(context.child("source_stale"), source_cfg.clone(), None).await);
assert_eq!(H::target(&source), target3);
let divergent_result: Result<H::Db, _> = sync::sync(compact_engine_config(
context.child("divergent_client"),
source.clone(),
target2.clone(),
H::config(&format!("{suffix}-divergent")),
))
.await;
assert!(matches!(
divergent_result,
Err(sync::Error::Engine(sync::EngineError::InvalidResponse))
));
let stale: H::Db = sync::sync(compact_engine_config(
context.child("stale_client"),
source.clone(),
target1.clone(),
H::config(&format!("{suffix}-stale")),
))
.await
.unwrap();
assert_eq!(H::root(&stale), target1.root);
assert_eq!(H::metadata(&stale), Some(metadata1.clone()));
H::destroy(stale).await;
let source = Arc::try_unwrap(source).unwrap_or_else(|_| panic!("single source ref"));
H::destroy(source).await;
});
}
pub(crate) fn test_compact_sync_reuses_pruned_partition<H: CompactSyncTestHarness>() {
deterministic::Runner::default().start(|mut context| async move {
let suffix = format!("compact-pruned-{}", context.next_u64());
let client_cfg = H::with_witness_items_per_section(H::config(&suffix), NZU64!(1));
let mut seeded = H::init(context.child("seed"), client_cfg.clone(), None).await;
for i in 1u8..=3 {
let floor = H::inactivity_floor_loc(&seeded);
seeded = H::apply(seeded, &[H::value(i)], Some(H::value(i)), floor).await;
seeded = H::sync(seeded).await;
}
let boundary = H::size(&seeded);
let seeded = H::prune(seeded, boundary).await.unwrap();
drop(seeded);
let source = H::init_full(context.child("source"), &suffix).await;
let source =
H::apply_full(source, &[H::value(1)], Some(H::value(9)), Location::new(0)).await;
let source = H::commit_full(source).await;
let bounds = H::full_bounds(&source);
let target = sync::CompactTarget {
root: H::full_root(&source),
size: bounds.end,
};
let synced: H::Db = sync::sync(compact_engine_config(
context.child("client"),
Arc::new(source),
target.clone(),
client_cfg.clone(),
))
.await
.unwrap();
assert_eq!(H::root(&synced), target.root);
drop(synced);
let reopened = H::init(context.child("reopen"), client_cfg, None).await;
assert_eq!(H::root(&reopened), target.root);
H::destroy(reopened).await;
});
}
pub(crate) fn test_compact_sync_dropped_import_preserves_existing_state<
H: CompactSyncTestHarness,
>() {
deterministic::Runner::default().start(|mut context| async move {
let suffix = format!("compact-dropped-{}", context.next_u64());
let client_cfg = H::config(&suffix);
let seeded = H::init(context.child("seed"), client_cfg.clone(), None).await;
let seeded = H::apply(seeded, &[H::value(1)], Some(H::value(1)), Location::new(0)).await;
let seeded = H::sync(seeded).await;
let target_a = H::target(&seeded);
drop(seeded);
let source = H::init_full(context.child("source"), &suffix).await;
let source =
H::apply_full(source, &[H::value(9)], Some(H::value(9)), Location::new(0)).await;
let source = H::commit_full(source).await;
let bounds = H::full_bounds(&source);
let target_b = sync::CompactTarget {
root: H::full_root(&source),
size: bounds.end,
};
assert_ne!(target_b, target_a);
let source = Arc::new(source);
let response = fetch_compact_state(&source, target_b.clone())
.await
.unwrap();
let sync::Response::Boundary {
op, pinned_nodes, ..
} = response
else {
unreachable!("boundary fetch returns a boundary response");
};
let imported = H::import(
context.child("import"),
&client_cfg,
target_b.size - 1,
pinned_nodes,
op,
)
.await
.unwrap();
assert_eq!(H::target(&imported), target_b);
drop(imported);
let response = fetch_compact_state(&source, target_b.clone())
.await
.unwrap();
let sync::Response::Boundary {
op, pinned_nodes, ..
} = response
else {
unreachable!("boundary fetch returns a boundary response");
};
let imported = H::import(
context.child("import").with_attribute("index", 2),
&client_cfg,
target_b.size - 1,
pinned_nodes,
op,
)
.await
.unwrap();
assert!(H::prune(imported, target_b.size).await.is_err());
let reopened = H::init(context.child("reopen"), client_cfg, None).await;
assert_eq!(H::target(&reopened), target_a);
H::destroy(reopened).await;
});
}
pub(crate) fn test_compact_serve_refuses_previous_contents_while_import_pending<
H: CompactSyncTestHarness,
>() {
deterministic::Runner::default().start(|mut context| async move {
let suffix = format!("compact-pending-{}", context.next_u64());
let client_cfg = H::config(&suffix);
let seeded = H::init(context.child("seed"), client_cfg.clone(), None).await;
let seeded = H::apply(
seeded,
&[H::value(7), H::value(8)],
Some(H::value(70)),
Location::new(0),
)
.await;
let seeded = H::sync(seeded).await;
let stale = H::target(&seeded);
drop(seeded);
let source = H::init_full(context.child("source"), &suffix).await;
let source =
H::apply_full(source, &[H::value(1)], Some(H::value(11)), Location::new(0)).await;
let source =
H::apply_full(source, &[H::value(2)], Some(H::value(12)), Location::new(0)).await;
let source = H::commit_full(source).await;
let target = sync::CompactTarget {
root: H::full_root(&source),
size: H::full_bounds(&source).end,
};
assert!(stale.size < target.size);
let source = Arc::new(source);
let response = fetch_compact_state(&source, target.clone()).await.unwrap();
let sync::Response::Boundary {
op, pinned_nodes, ..
} = response
else {
unreachable!("boundary fetch returns a boundary response");
};
let imported = H::import(
context.child("import"),
&client_cfg,
target.size - 1,
pinned_nodes,
op,
)
.await
.unwrap();
assert_eq!(H::target(&imported), target);
let tip_request = sync::Request::Boundary {
size: target.size,
start: target.size - 1,
};
let sync::Response::Boundary {
proof,
op,
pinned_nodes,
} = imported.serve(tip_request).await.unwrap().0
else {
unreachable!("boundary request returns a boundary response");
};
assert!(verify_proof_and_pinned_nodes::<Sha256, _, _>(
&proof,
target.size - 1,
std::slice::from_ref(&op),
&pinned_nodes,
&target.root,
));
let stale_request = sync::Request::Boundary {
size: stale.size,
start: stale.size - 1,
};
assert!(matches!(
imported.serve(stale_request).await,
Err(qmdb::Error::Journal(crate::journal::Error::ItemPruned(_)))
));
let db = H::sync(imported).await;
assert!(matches!(
db.serve(stale_request).await,
Err(qmdb::Error::Journal(crate::journal::Error::ItemPruned(_)))
));
assert_eq!(H::target(&db), target);
H::destroy(db).await;
});
}
macro_rules! compact_sync_tests {
($harness:ty, $mod_name:ident $(, $extra:ident)?) => {
mod $mod_name {
use super::*;
use commonware_macros::test_traced;
#[test_traced("WARN")]
fn test_compact_full_source_missing_reports_missing_source() {
crate::qmdb::sync::harness::test_compact_full_source_missing_reports_missing_source::<$harness>();
}
#[test_traced("WARN")]
fn test_compact_sync_roundtrip() {
crate::qmdb::sync::harness::test_compact_sync_roundtrip::<$harness>();
}
#[test_traced("WARN")]
fn test_compact_sync_recovers_after_invalid_proof() {
crate::qmdb::sync::harness::test_compact_sync_recovers_after_invalid_proof::<$harness>();
}
#[test_traced("WARN")]
fn test_compact_sync_recovers_after_tampered_commit_floor() {
crate::qmdb::sync::harness::test_compact_sync_recovers_after_tampered_commit_floor::<$harness>();
}
#[test_traced("WARN")]
fn test_compact_sync_recovers_after_size_mismatch() {
crate::qmdb::sync::harness::test_compact_sync_recovers_after_size_mismatch::<$harness>();
}
#[test_traced("WARN")]
fn test_compact_sync_recovers_after_tampered_pinned_nodes() {
crate::qmdb::sync::harness::test_compact_sync_recovers_after_tampered_pinned_nodes::<$harness>();
}
#[test_traced("WARN")]
fn test_compact_full_source_serves_historical_target() {
crate::qmdb::sync::harness::test_compact_full_source_serves_historical_target::<$harness>();
}
#[test_traced("WARN")]
fn test_compact_source_serves_retained_target() {
crate::qmdb::sync::harness::test_compact_source_serves_retained_target::<$harness>();
}
#[test_traced("WARN")]
fn test_compact_source_reopen_bounded_initialization_regrow_and_stale_target() {
crate::qmdb::sync::harness::test_compact_source_reopen_bounded_initialization_regrow_and_stale_target::<$harness>();
}
#[test_traced("WARN")]
fn test_compact_sync_reuses_pruned_partition() {
crate::qmdb::sync::harness::test_compact_sync_reuses_pruned_partition::<$harness>();
}
#[test_traced("WARN")]
fn test_compact_sync_dropped_import_preserves_existing_state() {
crate::qmdb::sync::harness::test_compact_sync_dropped_import_preserves_existing_state::<$harness>();
}
#[test_traced("WARN")]
fn test_compact_serve_refuses_previous_contents_while_import_pending() {
crate::qmdb::sync::harness::test_compact_serve_refuses_previous_contents_while_import_pending::<$harness>();
}
$( $extra!($harness); )?
}
};
}
pub(crate) use compact_sync_tests;