use assert_matches::assert_matches;
use linera_base::{crypto::CryptoHash, data_types::Timestamp, identifiers::ApplicationId};
use linera_execution::{Message, MessageKind};
use super::*;
use crate::test::MessageTestExt as _;
fn make_bundle(
certificate_hash: CryptoHash,
height: u64,
index: u32,
message: impl Into<Vec<u8>>,
) -> MessageBundle {
let message = Message::User {
application_id: ApplicationId::default(),
bytes: message.into(),
};
MessageBundle {
certificate_hash,
height: BlockHeight::from(height),
timestamp: Timestamp::default(),
transaction_index: index,
messages: vec![message.to_posted(index, MessageKind::Simple)],
}
}
fn make_unskippable_bundle(
certificate_hash: CryptoHash,
height: u64,
index: u32,
message: impl Into<Vec<u8>>,
) -> MessageBundle {
let mut bundle = make_bundle(certificate_hash, height, index, message);
bundle.messages[0].kind = MessageKind::Protected;
bundle
}
#[tokio::test]
async fn test_inbox_add_then_remove_skippable() {
let hash = CryptoHash::test_hash("1");
let mut view = InboxStateView::new().await;
assert!(view.add_bundle(make_bundle(hash, 0, 0, [0])).await.unwrap());
assert!(view
.remove_bundle(&make_bundle(hash, 0, 0, [0]))
.await
.unwrap());
assert_matches!(
view.add_bundle(make_bundle(hash, 0, 0, [0])).await,
Err(InboxError::IncorrectOrder { .. })
);
assert_matches!(
view.remove_bundle(&make_bundle(hash, 0, 0, [0])).await,
Err(InboxError::IncorrectOrder { .. })
);
assert!(view.add_bundle(make_bundle(hash, 0, 1, [1])).await.unwrap());
assert!(view.add_bundle(make_bundle(hash, 1, 0, [2])).await.unwrap());
assert_matches!(
view.remove_bundle(&make_bundle(hash, 0, 1, [0])).await,
Err(InboxError::UnexpectedBundle { .. })
);
assert_matches!(
view.remove_bundle(&make_bundle(CryptoHash::test_hash("2"), 0, 1, [1]))
.await,
Err(InboxError::UnexpectedBundle { .. })
);
assert!(view
.remove_bundle(&make_bundle(hash, 1, 0, [2]))
.await
.unwrap());
assert_eq!(view.added_bundles.count(), 0);
assert_eq!(view.removed_bundles.count(), 0);
}
#[tokio::test]
async fn test_inbox_remove_then_add_skippable() {
let hash = CryptoHash::test_hash("1");
let mut view = InboxStateView::new().await;
assert!(!view
.remove_bundle(&make_bundle(hash, 0, 0, [0]))
.await
.unwrap());
assert!(!view.add_bundle(make_bundle(hash, 0, 0, [0])).await.unwrap());
assert_matches!(
view.remove_bundle(&make_bundle(hash, 0, 0, [0])).await,
Err(InboxError::IncorrectOrder { .. })
);
assert_matches!(
view.add_bundle(make_bundle(hash, 0, 0, [0])).await,
Err(InboxError::IncorrectOrder { .. })
);
assert!(!view
.remove_bundle(&make_bundle(hash, 0, 1, [1]))
.await
.unwrap());
assert!(!view
.remove_bundle(&make_bundle(hash, 1, 1, [3]))
.await
.unwrap());
assert_matches!(
view.add_bundle(make_bundle(hash, 0, 1, [0])).await,
Err(InboxError::UnexpectedBundle { .. })
);
assert_matches!(
view.add_bundle(make_bundle(CryptoHash::test_hash("2"), 0, 1, [1]))
.await,
Err(InboxError::UnexpectedBundle { .. })
);
assert_matches!(
view.add_bundle(make_bundle(hash, 1, 0, [2])).await,
Err(InboxError::UnexpectedBundle { .. })
);
assert!(!view.add_bundle(make_bundle(hash, 0, 1, [1])).await.unwrap());
assert_matches!(
view.add_bundle(make_unskippable_bundle(hash, 1, 0, [2]))
.await,
Err(InboxError::UnexpectedBundle { .. })
);
assert!(!view.add_bundle(make_bundle(hash, 1, 0, [2])).await.unwrap());
assert!(!view.add_bundle(make_bundle(hash, 1, 1, [3])).await.unwrap());
assert_eq!(view.added_bundles.count(), 0);
assert_eq!(view.removed_bundles.count(), 0);
}
#[tokio::test]
async fn test_inbox_add_then_remove_unskippable() {
let hash = CryptoHash::test_hash("1");
let mut view = InboxStateView::new().await;
assert!(view
.add_bundle(make_unskippable_bundle(hash, 0, 0, [0]))
.await
.unwrap());
assert!(view
.remove_bundle(&make_unskippable_bundle(hash, 0, 0, [0]))
.await
.unwrap());
assert_matches!(
view.add_bundle(make_unskippable_bundle(hash, 0, 0, [0]))
.await,
Err(InboxError::IncorrectOrder { .. })
);
assert_matches!(
view.remove_bundle(&make_unskippable_bundle(hash, 0, 0, [0]))
.await,
Err(InboxError::IncorrectOrder { .. })
);
assert!(view
.add_bundle(make_unskippable_bundle(hash, 0, 1, [1]))
.await
.unwrap());
assert!(view
.add_bundle(make_unskippable_bundle(hash, 1, 0, [2]))
.await
.unwrap());
assert_matches!(
view.remove_bundle(&make_unskippable_bundle(hash, 0, 1, [0]))
.await,
Err(InboxError::UnexpectedBundle { .. })
);
assert_matches!(
view.remove_bundle(&make_unskippable_bundle(
CryptoHash::test_hash("2"),
0,
1,
[1]
))
.await,
Err(InboxError::UnexpectedBundle { .. })
);
assert_matches!(
view.remove_bundle(&make_unskippable_bundle(hash, 1, 0, [2])).await,
Err(InboxError::UnskippableBundle { bundle })
if bundle == make_unskippable_bundle(hash, 0, 1, [1])
);
assert!(view
.remove_bundle(&make_unskippable_bundle(hash, 0, 1, [1]))
.await
.unwrap());
assert!(view
.remove_bundle(&make_unskippable_bundle(hash, 1, 0, [2]))
.await
.unwrap());
assert_eq!(view.added_bundles.count(), 0);
assert_eq!(view.removed_bundles.count(), 0);
}
#[tokio::test]
async fn test_inbox_remove_then_add_unskippable() {
let hash = CryptoHash::test_hash("1");
let mut view = InboxStateView::new().await;
assert!(!view
.remove_bundle(&make_unskippable_bundle(hash, 0, 0, [0]))
.await
.unwrap());
assert!(!view
.add_bundle(make_unskippable_bundle(hash, 0, 0, [0]))
.await
.unwrap());
assert_matches!(
view.remove_bundle(&make_unskippable_bundle(hash, 0, 0, [0]))
.await,
Err(InboxError::IncorrectOrder { .. })
);
assert_matches!(
view.add_bundle(make_unskippable_bundle(hash, 0, 0, [0]))
.await,
Err(InboxError::IncorrectOrder { .. })
);
assert!(!view
.remove_bundle(&make_unskippable_bundle(hash, 0, 1, [1]))
.await
.unwrap());
assert!(!view
.remove_bundle(&make_unskippable_bundle(hash, 1, 1, [3]))
.await
.unwrap());
assert_matches!(
view.add_bundle(make_unskippable_bundle(hash, 0, 1, [0]))
.await,
Err(InboxError::UnexpectedBundle { .. })
);
assert_matches!(
view.add_bundle(make_unskippable_bundle(
CryptoHash::test_hash("2"),
0,
1,
[1]
))
.await,
Err(InboxError::UnexpectedBundle { .. })
);
assert_matches!(
view.add_bundle(make_unskippable_bundle(hash, 1, 1, [3]))
.await,
Err(InboxError::UnexpectedBundle { .. })
);
assert!(!view
.add_bundle(make_unskippable_bundle(hash, 0, 1, [1]))
.await
.unwrap());
assert_matches!(
view.add_bundle(make_unskippable_bundle(hash, 1, 0, [2]))
.await,
Err(InboxError::UnexpectedBundle { .. })
);
assert!(!view
.add_bundle(make_unskippable_bundle(hash, 1, 1, [3]))
.await
.unwrap());
assert_eq!(view.added_bundles.count(), 0);
assert_eq!(view.removed_bundles.count(), 0);
}
#[tokio::test]
async fn test_inbox_add_then_remove_mixed() {
let hash = CryptoHash::test_hash("1");
let mut view = InboxStateView::new().await;
assert!(view
.add_bundle(make_unskippable_bundle(hash, 0, 1, [1]))
.await
.unwrap());
assert!(view.add_bundle(make_bundle(hash, 1, 0, [2])).await.unwrap());
assert_matches!(
view.remove_bundle(&make_bundle(hash, 0, 1, [1])).await,
Err(InboxError::UnexpectedBundle { .. })
);
assert_matches!(
view.remove_bundle(&make_unskippable_bundle(
CryptoHash::test_hash("2"),
0,
1,
[1]
))
.await,
Err(InboxError::UnexpectedBundle { .. })
);
assert_matches!(
view.remove_bundle(&make_bundle(hash, 1, 0, [2])).await,
Err(InboxError::UnskippableBundle { bundle })
if bundle == make_unskippable_bundle(hash, 0, 1, [1])
);
assert!(view
.remove_bundle(&make_unskippable_bundle(hash, 0, 1, [1]))
.await
.unwrap());
assert!(view
.remove_bundle(&make_bundle(hash, 1, 0, [2]))
.await
.unwrap());
assert_eq!(view.added_bundles.count(), 0);
assert_eq!(view.removed_bundles.count(), 0);
}