use super::settle_uncorroborated_presence;
use crate::types::{ChainClaim, ChiaQueryError};
#[derive(Debug, PartialEq, Eq)]
struct Answer {
identity: &'static str,
height: u32,
}
impl ChainClaim for Answer {
fn chain_claim(&self) -> String {
format!("{} at {}", self.identity, self.height)
}
}
fn answer(height: u32) -> Answer {
Answer {
identity: "coin-a",
height,
}
}
#[test]
fn a_second_source_making_the_same_claim_makes_it_a_presence() {
let settled = settle_uncorroborated_presence(answer(100), Some(Ok(Some(answer(100)))));
assert_eq!(
settled.expect("two agreeing sources is an answer"),
Some(answer(100))
);
}
#[test]
fn presence_nobody_can_corroborate_is_an_error_not_a_some() {
let settled = settle_uncorroborated_presence(answer(100), None);
assert!(
matches!(settled, Err(ChiaQueryError::UncorroboratedPresence(_))),
"an uncorroborated presence must never surface as Ok(Some): got {settled:?}"
);
}
#[test]
fn the_same_coin_at_a_different_height_is_a_disagreement() {
let settled = settle_uncorroborated_presence(answer(100), Some(Ok(Some(answer(999)))));
assert!(
matches!(settled, Err(ChiaQueryError::SourcesDisagree(_))),
"a fabricated height is caught by comparing claims, not identities: got {settled:?}"
);
}
#[test]
fn a_second_source_that_fails_leaves_it_uncorroborated() {
let settled = settle_uncorroborated_presence(
answer(100),
Some(Err(ChiaQueryError::CoinsetHttp("gateway timeout".into()))),
);
assert!(
matches!(settled, Err(ChiaQueryError::UncorroboratedPresence(_))),
"a failed second opinion is not a confirmed first one: got {settled:?}"
);
}
#[test]
fn a_source_reporting_absent_contradicts_the_presence() {
let settled = settle_uncorroborated_presence(answer(100), Some(Ok(None)));
assert!(
matches!(settled, Err(ChiaQueryError::SourcesDisagree(_))),
"present-then-absent is a disagreement, not a tie to break: got {settled:?}"
);
}
use super::QueryRouter;
use crate::coinset::CoinsetClient;
use crate::peer::{OptAnswer, PeerBackend};
use std::time::Duration;
fn router(coinset_fallback_enabled: bool) -> QueryRouter {
QueryRouter {
peer: std::sync::Arc::new(PeerBackend::for_tests()),
coinset: CoinsetClient::new("http://127.0.0.1:1", Duration::from_millis(1))
.expect("build a client that is never called"),
coinset_fallback_enabled,
}
}
async fn a_coinset_answer_that_must_not_be_used() -> Result<Option<Answer>, ChiaQueryError> {
Ok(Some(answer(999)))
}
#[tokio::test]
async fn an_uncorroborated_presence_does_not_escape_the_settlement() {
let settled = router(false)
.settle_peer_answer(
OptAnswer::UncorroboratedFound(answer(100)),
a_coinset_answer_that_must_not_be_used(),
)
.await;
assert!(
matches!(settled, Err(ChiaQueryError::UncorroboratedPresence(_))),
"the record must not reach the caller as a fact: got {settled:?}"
);
}
#[tokio::test]
async fn an_uncorroborated_presence_is_put_to_the_coinset_tier() {
let settled = router(true)
.settle_peer_answer(OptAnswer::UncorroboratedFound(answer(100)), async {
Ok(Some(answer(999)))
})
.await;
assert!(
matches!(settled, Err(ChiaQueryError::SourcesDisagree(_))),
"the second source was consulted and contradicted the first: got {settled:?}"
);
}
#[tokio::test]
async fn an_uncorroborated_absence_does_not_escape_the_settlement() {
let settled = router(false)
.settle_peer_answer(
OptAnswer::<Answer>::UncorroboratedAbsent,
a_coinset_answer_that_must_not_be_used(),
)
.await;
assert!(
matches!(settled, Err(ChiaQueryError::UncorroboratedAbsence(_))),
"an absence nobody corroborated must not reach the caller as Ok(None): got {settled:?}"
);
}
#[tokio::test]
async fn corroborated_answers_pass_through_in_both_directions() {
let present = router(false)
.settle_peer_answer(
OptAnswer::Found(answer(100)),
a_coinset_answer_that_must_not_be_used(),
)
.await;
assert_eq!(
present.expect("a corroborated presence is an answer"),
Some(answer(100))
);
let absent = router(false)
.settle_peer_answer(
OptAnswer::<Answer>::CorroboratedAbsent,
a_coinset_answer_that_must_not_be_used(),
)
.await;
assert_eq!(absent.expect("a corroborated absence is an answer"), None);
}