303 lines
12 KiB
Rust
303 lines
12 KiB
Rust
// file: crates/ksp-job-backfill-lib/unit_tests/request.rs
|
|
// version: 3
|
|
|
|
fn signature(character: char) -> std::option::Option<crate::BackfillSignature> {
|
|
return match crate::BackfillSignature::new(character.to_string().repeat(crate::MIN_BACKFILL_SIGNATURE_TEXT_BYTES)) {
|
|
std::result::Result::Ok(value) => std::option::Option::Some(value),
|
|
std::result::Result::Err(_) => std::option::Option::None,
|
|
};
|
|
}
|
|
|
|
fn network(value: &str) -> std::option::Option<ksp_store_lib::RawNetworkId> {
|
|
return match ksp_store_lib::RawNetworkId::new(value) {
|
|
std::result::Result::Ok(value) => std::option::Option::Some(value),
|
|
std::result::Result::Err(_) => std::option::Option::None,
|
|
};
|
|
}
|
|
|
|
fn job_id(value: &str) -> std::option::Option<ksp_job_api::JobId> {
|
|
return match ksp_job_api::JobId::new(value) {
|
|
std::result::Result::Ok(value) => std::option::Option::Some(value),
|
|
std::result::Result::Err(_) => std::option::Option::None,
|
|
};
|
|
}
|
|
|
|
#[test]
|
|
fn pre_005_signature_shape_is_bounded_and_debug_redacted() {
|
|
let minimum = crate::BackfillSignature::new("1".repeat(crate::MIN_BACKFILL_SIGNATURE_TEXT_BYTES));
|
|
assert!(minimum.is_ok());
|
|
let maximum = crate::BackfillSignature::new("z".repeat(crate::MAX_BACKFILL_SIGNATURE_TEXT_BYTES));
|
|
assert!(maximum.is_ok());
|
|
assert!(crate::BackfillSignature::new("1".repeat(crate::MIN_BACKFILL_SIGNATURE_TEXT_BYTES - 1)).is_err());
|
|
assert!(crate::BackfillSignature::new("1".repeat(crate::MAX_BACKFILL_SIGNATURE_TEXT_BYTES + 1)).is_err());
|
|
assert!(crate::BackfillSignature::new(format!("{}0", "1".repeat(crate::MIN_BACKFILL_SIGNATURE_TEXT_BYTES - 1))).is_err());
|
|
let minimum = match minimum {
|
|
std::result::Result::Ok(value) => value,
|
|
std::result::Result::Err(_) => return,
|
|
};
|
|
let debug = format!("{minimum:?}");
|
|
assert_eq!(debug, "BackfillSignature(..)");
|
|
assert!(!debug.contains(minimum.as_str()));
|
|
return;
|
|
}
|
|
|
|
#[test]
|
|
fn pre_005_explicit_scope_deduplicates_at_first_occurrence_without_reordering() {
|
|
let first = match signature('1') {
|
|
std::option::Option::Some(value) => value,
|
|
std::option::Option::None => return,
|
|
};
|
|
let second = match signature('2') {
|
|
std::option::Option::Some(value) => value,
|
|
std::option::Option::None => return,
|
|
};
|
|
let scope_result = crate::BackfillScope::explicit_signatures(std::vec![first.clone(), second.clone(), first.clone(), second.clone()]);
|
|
assert!(scope_result.is_ok());
|
|
let scope = match scope_result {
|
|
std::result::Result::Ok(value) => value,
|
|
std::result::Result::Err(_) => return,
|
|
};
|
|
let signatures = match scope.signatures() {
|
|
std::option::Option::Some(value) => value,
|
|
std::option::Option::None => return,
|
|
};
|
|
assert_eq!(signatures, &[first, second]);
|
|
let debug = format!("{scope:?}");
|
|
assert!(debug.contains("signature_count"));
|
|
assert!(!debug.contains(signatures[0].as_str()));
|
|
return;
|
|
}
|
|
|
|
#[test]
|
|
fn pre_005_request_bounds_are_exact_and_explicit_context_is_rejected() {
|
|
let network = match network("devnet") {
|
|
std::option::Option::Some(value) => value,
|
|
std::option::Option::None => return,
|
|
};
|
|
let job_id = match job_id("backfill:test") {
|
|
std::option::Option::Some(value) => value,
|
|
std::option::Option::None => return,
|
|
};
|
|
let role = ksp_onchain_transport_lib::HttpRoleName::new("history");
|
|
let address = ksp_core_lib::Pubkey::new_from_array([7_u8; 32]);
|
|
let scope = crate::BackfillScope::latest_address(address);
|
|
let valid = crate::BackfillRequest::new(
|
|
job_id.clone(),
|
|
network.clone(),
|
|
role.clone(),
|
|
crate::BackfillCommitment::Confirmed,
|
|
scope.clone(),
|
|
crate::MAX_BACKFILL_PAGE_SIZE,
|
|
crate::MAX_BACKFILL_PAGES,
|
|
crate::MAX_BACKFILL_CANDIDATES,
|
|
crate::MAX_BACKFILL_HYDRATION_CONCURRENCY,
|
|
std::option::Option::Some(1),
|
|
);
|
|
assert!(valid.is_ok());
|
|
for (page_size, max_pages, max_candidates, concurrency) in [
|
|
(0, 1, 1, 1),
|
|
(crate::MAX_BACKFILL_PAGE_SIZE + 1, 1, 1, 1),
|
|
(1, 0, 1, 1),
|
|
(1, crate::MAX_BACKFILL_PAGES + 1, 1, 1),
|
|
(1, 1, 0, 1),
|
|
(1, 1, crate::MAX_BACKFILL_CANDIDATES + 1, 1),
|
|
(1, 1, 1, 0),
|
|
(1, 1, 1, crate::MAX_BACKFILL_HYDRATION_CONCURRENCY + 1),
|
|
] {
|
|
let result = crate::BackfillRequest::new(
|
|
job_id.clone(),
|
|
network.clone(),
|
|
role.clone(),
|
|
crate::BackfillCommitment::Finalized,
|
|
scope.clone(),
|
|
page_size,
|
|
max_pages,
|
|
max_candidates,
|
|
concurrency,
|
|
std::option::Option::None,
|
|
);
|
|
assert!(result.is_err());
|
|
}
|
|
let explicit_signature = match signature('3') {
|
|
std::option::Option::Some(value) => value,
|
|
std::option::Option::None => return,
|
|
};
|
|
let explicit_scope = crate::BackfillScope::explicit_signatures(std::vec![explicit_signature]);
|
|
let explicit_scope = match explicit_scope {
|
|
std::result::Result::Ok(value) => value,
|
|
std::result::Result::Err(_) => return,
|
|
};
|
|
let explicit_with_context =
|
|
crate::BackfillRequest::new(job_id, network, role, crate::BackfillCommitment::Confirmed, explicit_scope, 1, 1, 1, 1, std::option::Option::Some(10));
|
|
assert!(explicit_with_context.is_err());
|
|
return;
|
|
}
|
|
|
|
#[test]
|
|
fn pre_005_scope_fingerprint_is_network_semantic_and_transport_source_independent() {
|
|
let signature = match signature('4') {
|
|
std::option::Option::Some(value) => value,
|
|
std::option::Option::None => return,
|
|
};
|
|
let scope_result = crate::BackfillScope::explicit_signatures(std::vec![signature]);
|
|
let scope = match scope_result {
|
|
std::result::Result::Ok(value) => value,
|
|
std::result::Result::Err(_) => return,
|
|
};
|
|
let mainnet = match network("mainnet") {
|
|
std::option::Option::Some(value) => value,
|
|
std::option::Option::None => return,
|
|
};
|
|
let devnet = match network("devnet") {
|
|
std::option::Option::Some(value) => value,
|
|
std::option::Option::None => return,
|
|
};
|
|
let first_job = match job_id("backfill:first") {
|
|
std::option::Option::Some(value) => value,
|
|
std::option::Option::None => return,
|
|
};
|
|
let second_job = match job_id("backfill:second") {
|
|
std::option::Option::Some(value) => value,
|
|
std::option::Option::None => return,
|
|
};
|
|
let first = crate::BackfillRequest::new(
|
|
first_job,
|
|
devnet.clone(),
|
|
ksp_onchain_transport_lib::HttpRoleName::new("primary-http"),
|
|
crate::BackfillCommitment::Confirmed,
|
|
scope.clone(),
|
|
100,
|
|
5,
|
|
10,
|
|
1,
|
|
std::option::Option::None,
|
|
);
|
|
let second = crate::BackfillRequest::new(
|
|
second_job,
|
|
devnet,
|
|
ksp_onchain_transport_lib::HttpRoleName::new("fallback-http"),
|
|
crate::BackfillCommitment::Confirmed,
|
|
scope.clone(),
|
|
100,
|
|
5,
|
|
10,
|
|
64,
|
|
std::option::Option::None,
|
|
);
|
|
let other_network = crate::BackfillRequest::new(
|
|
match job_id("backfill:third") {
|
|
std::option::Option::Some(value) => value,
|
|
std::option::Option::None => return,
|
|
},
|
|
mainnet,
|
|
ksp_onchain_transport_lib::HttpRoleName::new("primary-http"),
|
|
crate::BackfillCommitment::Confirmed,
|
|
scope,
|
|
100,
|
|
5,
|
|
10,
|
|
1,
|
|
std::option::Option::None,
|
|
);
|
|
let first = match first {
|
|
std::result::Result::Ok(value) => value,
|
|
std::result::Result::Err(_) => return,
|
|
};
|
|
let second = match second {
|
|
std::result::Result::Ok(value) => value,
|
|
std::result::Result::Err(_) => return,
|
|
};
|
|
let other_network = match other_network {
|
|
std::result::Result::Ok(value) => value,
|
|
std::result::Result::Err(_) => return,
|
|
};
|
|
assert_eq!(first.scope_fingerprint(), second.scope_fingerprint());
|
|
assert_ne!(first.scope_fingerprint(), other_network.scope_fingerprint());
|
|
return;
|
|
}
|
|
|
|
#[test]
|
|
fn pre_005_scope_kind_and_anchor_are_distinct_semantics() {
|
|
let anchor = match signature('5') {
|
|
std::option::Option::Some(value) => value,
|
|
std::option::Option::None => return,
|
|
};
|
|
let address = ksp_core_lib::Pubkey::new_from_array([9_u8; 32]);
|
|
let before = crate::BackfillScope::before_address(address, anchor.clone());
|
|
let after = crate::BackfillScope::after_address(address, anchor);
|
|
assert_eq!(before.kind(), crate::BackfillScopeKind::BeforeAddress);
|
|
assert_eq!(after.kind(), crate::BackfillScopeKind::AfterAddress);
|
|
assert!(before.anchor().is_some());
|
|
assert!(after.anchor().is_some());
|
|
assert_ne!(before, after);
|
|
return;
|
|
}
|
|
|
|
#[test]
|
|
fn pre_011_resume_reissues_checkpoint_for_new_job_without_changing_scope_semantics() {
|
|
let network = match network("mainnet") {
|
|
std::option::Option::Some(value) => value,
|
|
std::option::Option::None => return,
|
|
};
|
|
let original_job = match job_id("backfill:resume-original") {
|
|
std::option::Option::Some(value) => value,
|
|
std::option::Option::None => return,
|
|
};
|
|
let resumed_job = match job_id("backfill:resume-next") {
|
|
std::option::Option::Some(value) => value,
|
|
std::option::Option::None => return,
|
|
};
|
|
let request = crate::BackfillRequest::new(
|
|
original_job.clone(),
|
|
network,
|
|
ksp_onchain_transport_lib::HttpRoleName::new("backfill_pool"),
|
|
crate::BackfillCommitment::Finalized,
|
|
crate::BackfillScope::before_address(
|
|
ksp_core_lib::Pubkey::new_from_array([11_u8; 32]),
|
|
match signature('8') {
|
|
std::option::Option::Some(value) => value,
|
|
std::option::Option::None => return,
|
|
},
|
|
),
|
|
100,
|
|
10,
|
|
1000,
|
|
4,
|
|
std::option::Option::Some(42),
|
|
);
|
|
let request = match request {
|
|
std::result::Result::Ok(value) => value,
|
|
std::result::Result::Err(_) => return,
|
|
};
|
|
let resume_before = match signature('9') {
|
|
std::option::Option::Some(value) => value,
|
|
std::option::Option::None => return,
|
|
};
|
|
let checkpoint = crate::BackfillCheckpoint::new(original_job, request.scope_fingerprint(), 7, std::option::Option::Some(resume_before.clone()));
|
|
let resumed = request.resume_for_job(resumed_job.clone(), checkpoint);
|
|
assert!(resumed.is_ok());
|
|
let resumed = match resumed {
|
|
std::result::Result::Ok(value) => value,
|
|
std::result::Result::Err(_) => return,
|
|
};
|
|
assert_eq!(resumed.job_id(), &resumed_job);
|
|
assert_eq!(resumed.scope_fingerprint(), request.scope_fingerprint());
|
|
assert_eq!(resumed.network(), request.network());
|
|
assert_eq!(resumed.role(), request.role());
|
|
assert_eq!(resumed.commitment(), request.commitment());
|
|
assert_eq!(resumed.scope(), request.scope());
|
|
assert_eq!(resumed.page_size(), request.page_size());
|
|
assert_eq!(resumed.max_pages(), request.max_pages());
|
|
assert_eq!(resumed.max_candidates(), request.max_candidates());
|
|
assert_eq!(resumed.hydration_concurrency(), request.hydration_concurrency());
|
|
assert_eq!(resumed.min_context_slot(), request.min_context_slot());
|
|
let checkpoint = resumed.checkpoint();
|
|
assert!(checkpoint.is_some());
|
|
if let std::option::Option::Some(checkpoint) = checkpoint {
|
|
assert_eq!(checkpoint.job_id(), &resumed_job);
|
|
assert_eq!(checkpoint.scope_fingerprint(), request.scope_fingerprint());
|
|
assert_eq!(checkpoint.completed_prefix(), 7);
|
|
assert_eq!(checkpoint.resume_before(), std::option::Option::Some(&resume_before));
|
|
}
|
|
}
|