1018 lines
42 KiB
Rust
1018 lines
42 KiB
Rust
//! Macro of integration tests for StateStore implementations.
|
|
|
|
/// Macro building to allow your StateStore implementation to run the entire
|
|
/// tests suite locally.
|
|
///
|
|
/// You need to provide a `async fn get_store() -> StoreResult<impl StateStore>`
|
|
/// providing a fresh store on the same level you invoke the macro.
|
|
///
|
|
/// ## Usage Example:
|
|
/// ```no_run
|
|
/// # use matrix_sdk_base::store::{
|
|
/// # StateStore,
|
|
/// # MemoryStore as MyStore,
|
|
/// # Result as StoreResult,
|
|
/// # };
|
|
///
|
|
/// #[cfg(test)]
|
|
/// mod tests {
|
|
/// use super::{MyStore, StateStore, StoreResult};
|
|
///
|
|
/// async fn get_store() -> StoreResult<impl StateStore> {
|
|
/// Ok(MyStore::new())
|
|
/// }
|
|
///
|
|
/// statestore_integration_tests!();
|
|
/// }
|
|
/// ```
|
|
#[allow(unused_macros, unused_extern_crates)]
|
|
#[macro_export]
|
|
macro_rules! statestore_integration_tests {
|
|
() => {
|
|
mod statestore_integration_tests {
|
|
use std::{
|
|
collections::{BTreeMap, BTreeSet},
|
|
sync::Arc,
|
|
};
|
|
|
|
#[cfg(feature = "experimental-timeline")]
|
|
use futures_util::StreamExt;
|
|
use matrix_sdk_test::{async_test, test_json};
|
|
#[cfg(feature = "experimental-timeline")]
|
|
use ruma::api::{
|
|
client::{
|
|
message::get_message_events::v3::Response as MessageResponse,
|
|
sync::sync_events::v3::Response as SyncResponse,
|
|
},
|
|
IncomingResponse,
|
|
};
|
|
use ruma::{
|
|
api::client::media::get_content_thumbnail::v3::Method,
|
|
event_id,
|
|
events::{
|
|
presence::PresenceEvent,
|
|
receipt::ReceiptType,
|
|
room::{
|
|
member::{
|
|
MembershipState, OriginalSyncRoomMemberEvent, RoomMemberEventContent,
|
|
RoomMemberUnsigned, StrippedRoomMemberEvent, SyncRoomMemberEvent,
|
|
},
|
|
power_levels::RoomPowerLevelsEventContent,
|
|
MediaSource,
|
|
topic::{RoomTopicEventContent, OriginalRoomTopicEvent, RedactedRoomTopicEvent},
|
|
},
|
|
AnyEphemeralRoomEventContent, AnyGlobalAccountDataEvent,
|
|
AnyRoomAccountDataEvent, AnyStrippedStateEvent, AnySyncEphemeralRoomEvent,
|
|
AnySyncStateEvent, GlobalAccountDataEventType, RoomAccountDataEventType,
|
|
StateEventType,
|
|
},
|
|
mxc_uri, room_id,
|
|
serde::Raw,
|
|
uint, user_id, EventId, MilliSecondsSinceUnixEpoch, OwnedEventId, RoomId, UserId,
|
|
};
|
|
use serde_json::{json, Value as JsonValue};
|
|
|
|
#[cfg(feature = "experimental-timeline")]
|
|
use $crate::{
|
|
deserialized_responses::{SyncTimelineEvent, TimelineEvent, TimelineSlice},
|
|
http::Response,
|
|
};
|
|
use $crate::{
|
|
media::{MediaFormat, MediaRequest, MediaThumbnailSize},
|
|
store::{Result as StoreResult, StateChanges, StateStore, StateStoreExt},
|
|
RoomInfo, RoomType,
|
|
};
|
|
|
|
use super::get_store;
|
|
|
|
fn user_id() -> &'static UserId {
|
|
user_id!("@example:localhost")
|
|
}
|
|
pub(crate) fn invited_user_id() -> &'static UserId {
|
|
user_id!("@invited:localhost")
|
|
}
|
|
|
|
pub(crate) fn room_id() -> &'static RoomId {
|
|
room_id!("!test:localhost")
|
|
}
|
|
|
|
pub(crate) fn stripped_room_id() -> &'static RoomId {
|
|
room_id!("!stripped:localhost")
|
|
}
|
|
|
|
pub(crate) fn first_receipt_event_id() -> &'static EventId {
|
|
event_id!("$example")
|
|
}
|
|
|
|
/// Populate the given `StateStore`.
|
|
pub async fn populate_store(store: Arc<dyn StateStore>) -> StoreResult<()> {
|
|
let mut changes = StateChanges::default();
|
|
|
|
let user_id = user_id();
|
|
let invited_user_id = invited_user_id();
|
|
let room_id = room_id();
|
|
let stripped_room_id = stripped_room_id();
|
|
|
|
changes.sync_token = Some("t392-516_47314_0_7_1_1_1_11444_1".to_owned());
|
|
|
|
let presence_json: &JsonValue = &test_json::PRESENCE;
|
|
let presence_raw =
|
|
serde_json::from_value::<Raw<PresenceEvent>>(presence_json.clone()).unwrap();
|
|
let presence_event = presence_raw.deserialize().unwrap();
|
|
changes.add_presence_event(presence_event, presence_raw);
|
|
|
|
let pushrules_json: &JsonValue = &test_json::PUSH_RULES;
|
|
let pushrules_raw = serde_json::from_value::<Raw<AnyGlobalAccountDataEvent>>(
|
|
pushrules_json.clone(),
|
|
)
|
|
.unwrap();
|
|
let pushrules_event = pushrules_raw.deserialize().unwrap();
|
|
changes.add_account_data(pushrules_event, pushrules_raw);
|
|
|
|
let mut room = RoomInfo::new(room_id, RoomType::Joined);
|
|
room.mark_as_left();
|
|
|
|
let tag_json: &JsonValue = &test_json::TAG;
|
|
let tag_raw =
|
|
serde_json::from_value::<Raw<AnyRoomAccountDataEvent>>(tag_json.clone())
|
|
.unwrap();
|
|
let tag_event = tag_raw.deserialize().unwrap();
|
|
changes.add_room_account_data(room_id, tag_event, tag_raw);
|
|
|
|
let name_json: &JsonValue = &test_json::NAME;
|
|
let name_raw =
|
|
serde_json::from_value::<Raw<AnySyncStateEvent>>(name_json.clone()).unwrap();
|
|
let name_event = name_raw.deserialize().unwrap();
|
|
room.handle_state_event(&name_event);
|
|
changes.add_state_event(room_id, name_event, name_raw);
|
|
|
|
let topic_json: &JsonValue = &test_json::TOPIC;
|
|
let topic_raw =
|
|
serde_json::from_value::<Raw<AnySyncStateEvent>>(topic_json.clone()).expect("can create sync-state-event for topic");
|
|
let topic_event = topic_raw.deserialize().expect("can deserialize raw topic");
|
|
room.handle_state_event(&topic_event);
|
|
changes.add_state_event(room_id, topic_event, topic_raw);
|
|
|
|
let mut room_ambiguity_map = BTreeMap::new();
|
|
let mut room_profiles = BTreeMap::new();
|
|
let mut room_members = BTreeMap::new();
|
|
|
|
let member_json: &JsonValue = &test_json::MEMBER;
|
|
let member_event: SyncRoomMemberEvent =
|
|
serde_json::from_value(member_json.clone()).unwrap();
|
|
let displayname =
|
|
member_event.as_original().unwrap().content.displayname.clone().unwrap();
|
|
room_ambiguity_map
|
|
.insert(displayname.clone(), BTreeSet::from([user_id.to_owned()]));
|
|
room_profiles.insert(user_id.to_owned(), (&member_event).into());
|
|
room_members.insert(user_id.to_owned(), member_event);
|
|
|
|
let member_state_raw =
|
|
serde_json::from_value::<Raw<AnySyncStateEvent>>(member_json.clone()).unwrap();
|
|
let member_state_event = member_state_raw.deserialize().unwrap();
|
|
changes.add_state_event(room_id, member_state_event, member_state_raw);
|
|
|
|
let invited_member_json: &JsonValue = &test_json::MEMBER_INVITE;
|
|
// FIXME: Should be stripped room member event
|
|
let invited_member_event: SyncRoomMemberEvent =
|
|
serde_json::from_value(invited_member_json.clone()).unwrap();
|
|
room_ambiguity_map
|
|
.entry(displayname)
|
|
.or_default()
|
|
.insert(invited_user_id.to_owned());
|
|
room_profiles.insert(invited_user_id.to_owned(), (&invited_member_event).into());
|
|
room_members.insert(invited_user_id.to_owned(), invited_member_event);
|
|
|
|
let invited_member_state_raw =
|
|
serde_json::from_value::<Raw<AnySyncStateEvent>>(invited_member_json.clone())
|
|
.unwrap();
|
|
let invited_member_state_event = invited_member_state_raw.deserialize().unwrap();
|
|
changes.add_state_event(
|
|
room_id,
|
|
invited_member_state_event,
|
|
invited_member_state_raw,
|
|
);
|
|
|
|
let receipt_json: &JsonValue = &test_json::READ_RECEIPT;
|
|
let receipt_event =
|
|
serde_json::from_value::<AnySyncEphemeralRoomEvent>(receipt_json.clone())
|
|
.unwrap();
|
|
let receipt_content = match receipt_event.content() {
|
|
AnyEphemeralRoomEventContent::Receipt(content) => content,
|
|
_ => panic!(),
|
|
};
|
|
changes.add_receipts(room_id, receipt_content);
|
|
|
|
changes.ambiguity_maps.insert(room_id.to_owned(), room_ambiguity_map);
|
|
changes.profiles.insert(room_id.to_owned(), room_profiles);
|
|
changes.members.insert(room_id.to_owned(), room_members);
|
|
changes.add_room(room);
|
|
|
|
let mut stripped_room = RoomInfo::new(stripped_room_id, RoomType::Invited);
|
|
|
|
let stripped_name_json: &JsonValue = &test_json::NAME_STRIPPED;
|
|
let stripped_name_raw = serde_json::from_value::<Raw<AnyStrippedStateEvent>>(
|
|
stripped_name_json.clone(),
|
|
)
|
|
.unwrap();
|
|
let stripped_name_event = stripped_name_raw.deserialize().unwrap();
|
|
stripped_room.handle_stripped_state_event(&stripped_name_event);
|
|
changes.stripped_state.insert(
|
|
stripped_room_id.to_owned(),
|
|
BTreeMap::from([(
|
|
stripped_name_event.event_type(),
|
|
BTreeMap::from([(
|
|
stripped_name_event.state_key().to_owned(),
|
|
stripped_name_raw.clone(),
|
|
)]),
|
|
)]),
|
|
);
|
|
|
|
changes.add_stripped_room(stripped_room);
|
|
|
|
let stripped_member_json: &JsonValue = &test_json::MEMBER_STRIPPED;
|
|
let stripped_member_event =
|
|
serde_json::from_value::<StrippedRoomMemberEvent>(stripped_member_json.clone())
|
|
.unwrap();
|
|
changes.add_stripped_member(stripped_room_id, stripped_member_event);
|
|
|
|
store.save_changes(&changes).await?;
|
|
Ok(())
|
|
}
|
|
|
|
fn power_level_event() -> Raw<AnySyncStateEvent> {
|
|
let content = RoomPowerLevelsEventContent::default();
|
|
|
|
let event = json!({
|
|
"event_id": "$h29iv0s8:example.com",
|
|
"content": content,
|
|
"sender": user_id(),
|
|
"type": "m.room.power_levels",
|
|
"origin_server_ts": 0u64,
|
|
"state_key": "",
|
|
});
|
|
|
|
serde_json::from_value(event).unwrap()
|
|
}
|
|
|
|
fn stripped_membership_event() -> StrippedRoomMemberEvent {
|
|
custom_stripped_membership_event(user_id())
|
|
}
|
|
|
|
fn custom_stripped_membership_event(user_id: &UserId) -> StrippedRoomMemberEvent {
|
|
StrippedRoomMemberEvent {
|
|
content: RoomMemberEventContent::new(MembershipState::Join),
|
|
sender: user_id.to_owned(),
|
|
state_key: user_id.to_owned(),
|
|
}
|
|
}
|
|
|
|
fn membership_event() -> SyncRoomMemberEvent {
|
|
custom_membership_event(user_id(), event_id!("$h29iv0s8:example.com").to_owned())
|
|
}
|
|
|
|
fn custom_membership_event(
|
|
user_id: &UserId,
|
|
event_id: OwnedEventId,
|
|
) -> SyncRoomMemberEvent {
|
|
SyncRoomMemberEvent::Original(OriginalSyncRoomMemberEvent {
|
|
event_id,
|
|
content: RoomMemberEventContent::new(MembershipState::Join),
|
|
sender: user_id.to_owned(),
|
|
origin_server_ts: MilliSecondsSinceUnixEpoch(198u32.into()),
|
|
state_key: user_id.to_owned(),
|
|
unsigned: RoomMemberUnsigned::default(),
|
|
})
|
|
}
|
|
|
|
#[async_test]
|
|
async fn test_topic_redaction() -> StoreResult<()> {
|
|
let room_id = room_id();
|
|
let inner_store = get_store().await?;
|
|
|
|
let store = Arc::new(inner_store);
|
|
populate_store(store.clone()).await?;
|
|
|
|
assert!(store.get_sync_token().await?.is_some());
|
|
assert_eq!(
|
|
store
|
|
.get_state_event_static::<RoomTopicEventContent>(room_id)
|
|
.await?
|
|
.expect("room topic found before redaction")
|
|
.deserialize_as::<OriginalRoomTopicEvent>()
|
|
.expect("can deserialize room topic before redaction")
|
|
.content
|
|
.topic,
|
|
"😀"
|
|
);
|
|
|
|
let mut changes = StateChanges::default();
|
|
|
|
let redaction_json: &JsonValue = &test_json::TOPIC_REDACTION;
|
|
let redaction_evt = serde_json::from_value(redaction_json.clone()).expect("topic redaction event making works");
|
|
|
|
changes.add_redaction(room_id, redaction_evt);
|
|
store.save_changes(&changes).await?;
|
|
|
|
match store
|
|
.get_state_event_static::<RoomTopicEventContent>(room_id)
|
|
.await?
|
|
.expect("room topic found before redaction")
|
|
.deserialize_as::<OriginalRoomTopicEvent>()
|
|
{
|
|
Err(_) => { } // as expected
|
|
Ok(_) => panic!("Topic has not been redacted")
|
|
}
|
|
|
|
let _ = store
|
|
.get_state_event_static::<RoomTopicEventContent>(room_id)
|
|
.await?
|
|
.expect("room topic found after redaction")
|
|
.deserialize_as::<RedactedRoomTopicEvent>()
|
|
.expect("can deserialize room topic after redaction");
|
|
|
|
Ok(())
|
|
}
|
|
|
|
#[async_test]
|
|
async fn test_populate_store() -> StoreResult<()> {
|
|
let room_id = room_id();
|
|
let user_id = user_id();
|
|
let inner_store = get_store().await?;
|
|
|
|
let store = Arc::new(inner_store);
|
|
populate_store(store.clone()).await?;
|
|
|
|
assert!(store.get_sync_token().await?.is_some());
|
|
assert!(store.get_presence_event(user_id).await?.is_some());
|
|
assert_eq!(store.get_room_infos().await?.len(), 1, "Expected to find 1 room info");
|
|
assert_eq!(
|
|
store.get_stripped_room_infos().await?.len(),
|
|
1,
|
|
"Expected to find 1 stripped room info"
|
|
);
|
|
assert!(store
|
|
.get_account_data_event(GlobalAccountDataEventType::PushRules)
|
|
.await?
|
|
.is_some());
|
|
|
|
assert!(store
|
|
.get_state_event(room_id, StateEventType::RoomName, "")
|
|
.await?
|
|
.is_some());
|
|
assert_eq!(
|
|
store.get_state_events(room_id, StateEventType::RoomTopic).await?.len(),
|
|
1,
|
|
"Expected to find 1 room topic"
|
|
);
|
|
assert!(store.get_profile(room_id, user_id).await?.is_some());
|
|
assert!(store.get_member_event(room_id, user_id).await?.is_some());
|
|
assert_eq!(
|
|
store.get_user_ids(room_id).await?.len(),
|
|
2,
|
|
"Expected to find 2 members for room"
|
|
);
|
|
assert_eq!(
|
|
store.get_invited_user_ids(room_id).await?.len(),
|
|
1,
|
|
"Expected to find 1 invited user ids"
|
|
);
|
|
assert_eq!(
|
|
store.get_joined_user_ids(room_id).await?.len(),
|
|
1,
|
|
"Expected to find 1 joined user ids"
|
|
);
|
|
assert_eq!(
|
|
store.get_users_with_display_name(room_id, "example").await?.len(),
|
|
2,
|
|
"Expected to find 2 display names for room"
|
|
);
|
|
assert!(store
|
|
.get_room_account_data_event(room_id, RoomAccountDataEventType::Tag)
|
|
.await?
|
|
.is_some());
|
|
assert!(store
|
|
.get_user_room_receipt_event(room_id, ReceiptType::Read, user_id)
|
|
.await?
|
|
.is_some());
|
|
assert_eq!(
|
|
store
|
|
.get_event_room_receipt_events(
|
|
room_id,
|
|
ReceiptType::Read,
|
|
first_receipt_event_id()
|
|
)
|
|
.await?
|
|
.len(),
|
|
1,
|
|
"Expected to find 1 read receipt"
|
|
);
|
|
Ok(())
|
|
}
|
|
|
|
#[async_test]
|
|
async fn test_member_saving() {
|
|
let store = get_store().await.unwrap();
|
|
let room_id = room_id!("!test_member_saving:localhost");
|
|
let user_id = user_id();
|
|
|
|
assert!(store.get_member_event(room_id, user_id).await.unwrap().is_none());
|
|
let mut changes = StateChanges::default();
|
|
changes
|
|
.members
|
|
.entry(room_id.to_owned())
|
|
.or_default()
|
|
.insert(user_id.to_owned(), membership_event());
|
|
|
|
store.save_changes(&changes).await.unwrap();
|
|
assert!(store.get_member_event(room_id, user_id).await.unwrap().is_some());
|
|
|
|
let members = store.get_user_ids(room_id).await.unwrap();
|
|
assert!(!members.is_empty(), "We expected to find members for the room")
|
|
}
|
|
|
|
#[async_test]
|
|
async fn test_filter_saving() {
|
|
let store = get_store().await.unwrap();
|
|
let test_name = "filter_name";
|
|
let filter_id = "filter_id_1234";
|
|
assert_eq!(store.get_filter(test_name).await.unwrap(), None);
|
|
store.save_filter(test_name, filter_id).await.unwrap();
|
|
assert_eq!(store.get_filter(test_name).await.unwrap(), Some(filter_id.to_owned()));
|
|
}
|
|
|
|
#[async_test]
|
|
async fn test_sync_token_saving() {
|
|
let mut changes = StateChanges::default();
|
|
let store = get_store().await.unwrap();
|
|
let sync_token = "t392-516_47314_0_7_1".to_owned();
|
|
|
|
changes.sync_token = Some(sync_token.clone());
|
|
assert_eq!(store.get_sync_token().await.unwrap(), None);
|
|
store.save_changes(&changes).await.unwrap();
|
|
assert_eq!(store.get_sync_token().await.unwrap(), Some(sync_token));
|
|
}
|
|
|
|
#[async_test]
|
|
async fn test_stripped_member_saving() {
|
|
let store = get_store().await.unwrap();
|
|
let room_id = room_id!("!test_stripped_member_saving:localhost");
|
|
let user_id = user_id();
|
|
|
|
assert!(store.get_member_event(room_id, user_id).await.unwrap().is_none());
|
|
let mut changes = StateChanges::default();
|
|
changes
|
|
.stripped_members
|
|
.entry(room_id.to_owned())
|
|
.or_default()
|
|
.insert(user_id.to_owned(), stripped_membership_event());
|
|
|
|
store.save_changes(&changes).await.unwrap();
|
|
assert!(store.get_member_event(room_id, user_id).await.unwrap().is_some());
|
|
|
|
let members = store.get_user_ids(room_id).await.unwrap();
|
|
assert!(!members.is_empty(), "We expected to find members for the room")
|
|
}
|
|
|
|
#[async_test]
|
|
async fn test_power_level_saving() {
|
|
let store = get_store().await.unwrap();
|
|
let room_id = room_id!("!test_power_level_saving:localhost");
|
|
|
|
let raw_event = power_level_event();
|
|
let event = raw_event.deserialize().unwrap();
|
|
|
|
assert!(store
|
|
.get_state_event(room_id, StateEventType::RoomPowerLevels, "")
|
|
.await
|
|
.unwrap()
|
|
.is_none());
|
|
let mut changes = StateChanges::default();
|
|
changes.add_state_event(room_id, event, raw_event);
|
|
|
|
store.save_changes(&changes).await.unwrap();
|
|
assert!(store
|
|
.get_state_event(room_id, StateEventType::RoomPowerLevels, "")
|
|
.await
|
|
.unwrap()
|
|
.is_some());
|
|
}
|
|
|
|
#[async_test]
|
|
async fn test_receipts_saving() {
|
|
let store = get_store().await.expect("creating store failed");
|
|
|
|
let room_id = room_id!("!test_receipts_saving:localhost");
|
|
|
|
let first_event_id = event_id!("$1435641916114394fHBLK:matrix.org");
|
|
let second_event_id = event_id!("$fHBLK1435641916114394:matrix.org");
|
|
|
|
let first_receipt_event = serde_json::from_value(json!({
|
|
first_event_id: {
|
|
"m.read": {
|
|
user_id(): {
|
|
"ts": 1436451550453u64
|
|
}
|
|
}
|
|
}
|
|
}))
|
|
.expect("json creation failed");
|
|
|
|
let second_receipt_event = serde_json::from_value(json!({
|
|
second_event_id: {
|
|
"m.read": {
|
|
user_id(): {
|
|
"ts": 1436451551453u64
|
|
}
|
|
}
|
|
}
|
|
}))
|
|
.expect("json creation failed");
|
|
|
|
assert!(store
|
|
.get_user_room_receipt_event(room_id, ReceiptType::Read, user_id())
|
|
.await
|
|
.expect("failed to read user room receipt")
|
|
.is_none());
|
|
assert!(store
|
|
.get_event_room_receipt_events(room_id, ReceiptType::Read, &first_event_id)
|
|
.await
|
|
.expect("failed to read user room receipt for 1")
|
|
.is_empty());
|
|
assert!(store
|
|
.get_event_room_receipt_events(room_id, ReceiptType::Read, &second_event_id)
|
|
.await
|
|
.expect("failed to read user room receipt for 2")
|
|
.is_empty());
|
|
|
|
let mut changes = StateChanges::default();
|
|
changes.add_receipts(room_id, first_receipt_event);
|
|
|
|
store.save_changes(&changes).await.expect("writing changes fauked");
|
|
assert!(store
|
|
.get_user_room_receipt_event(room_id, ReceiptType::Read, user_id())
|
|
.await
|
|
.expect("failed to read user room receipt after save")
|
|
.is_some());
|
|
assert_eq!(
|
|
store
|
|
.get_event_room_receipt_events(room_id, ReceiptType::Read, &first_event_id)
|
|
.await
|
|
.expect("failed to read user room receipt for 1 after save")
|
|
.len(),
|
|
1,
|
|
"Found a wrong number of receipts for 1 after save"
|
|
);
|
|
assert!(store
|
|
.get_event_room_receipt_events(room_id, ReceiptType::Read, &second_event_id)
|
|
.await
|
|
.expect("failed to read user room receipt for 2 after save")
|
|
.is_empty());
|
|
|
|
let mut changes = StateChanges::default();
|
|
changes.add_receipts(room_id, second_receipt_event);
|
|
|
|
store.save_changes(&changes).await.expect("Saving works");
|
|
assert!(store
|
|
.get_user_room_receipt_event(room_id, ReceiptType::Read, user_id())
|
|
.await
|
|
.expect("Getting user room receipts failed")
|
|
.is_some());
|
|
assert!(store
|
|
.get_event_room_receipt_events(room_id, ReceiptType::Read, &first_event_id)
|
|
.await
|
|
.expect("Getting event room receipt events for first event failed")
|
|
.is_empty());
|
|
assert_eq!(
|
|
store
|
|
.get_event_room_receipt_events(room_id, ReceiptType::Read, &second_event_id)
|
|
.await
|
|
.expect("Getting event room receipt events for second event failed")
|
|
.len(),
|
|
1,
|
|
"Found a wrong number of receipts for second event after save"
|
|
);
|
|
}
|
|
|
|
#[async_test]
|
|
async fn test_media_content() {
|
|
let store = get_store().await.unwrap();
|
|
|
|
let uri = mxc_uri!("mxc://localhost/media");
|
|
let content: Vec<u8> = "somebinarydata".into();
|
|
|
|
let request_file = MediaRequest {
|
|
source: MediaSource::Plain(uri.to_owned()),
|
|
format: MediaFormat::File,
|
|
};
|
|
|
|
let request_thumbnail = MediaRequest {
|
|
source: MediaSource::Plain(uri.to_owned()),
|
|
format: MediaFormat::Thumbnail(MediaThumbnailSize {
|
|
method: Method::Crop,
|
|
width: uint!(100),
|
|
height: uint!(100),
|
|
}),
|
|
};
|
|
|
|
assert!(
|
|
store.get_media_content(&request_file).await.unwrap().is_none(),
|
|
"unexpected media found"
|
|
);
|
|
assert!(
|
|
store.get_media_content(&request_thumbnail).await.unwrap().is_none(),
|
|
"media not found"
|
|
);
|
|
|
|
store
|
|
.add_media_content(&request_file, content.clone())
|
|
.await
|
|
.expect("adding media failed");
|
|
assert!(
|
|
store.get_media_content(&request_file).await.unwrap().is_some(),
|
|
"media not found though added"
|
|
);
|
|
|
|
store.remove_media_content(&request_file).await.expect("removing media failed");
|
|
assert!(
|
|
store.get_media_content(&request_file).await.unwrap().is_none(),
|
|
"media still there after removing"
|
|
);
|
|
|
|
store
|
|
.add_media_content(&request_file, content.clone())
|
|
.await
|
|
.expect("adding media again failed");
|
|
assert!(
|
|
store.get_media_content(&request_file).await.unwrap().is_some(),
|
|
"media not found after adding again"
|
|
);
|
|
|
|
store
|
|
.add_media_content(&request_thumbnail, content.clone())
|
|
.await
|
|
.expect("adding thumbnail failed");
|
|
assert!(
|
|
store.get_media_content(&request_thumbnail).await.unwrap().is_some(),
|
|
"thumbnail not found"
|
|
);
|
|
|
|
store
|
|
.remove_media_content_for_uri(uri)
|
|
.await
|
|
.expect("removing all media for uri failed");
|
|
assert!(
|
|
store.get_media_content(&request_file).await.unwrap().is_none(),
|
|
"media wasn't removed"
|
|
);
|
|
assert!(
|
|
store.get_media_content(&request_thumbnail).await.unwrap().is_none(),
|
|
"thumbnail wasn't removed"
|
|
);
|
|
}
|
|
|
|
#[async_test]
|
|
async fn test_custom_storage() -> StoreResult<()> {
|
|
let key = "my_key";
|
|
let value = &[0, 1, 2, 3];
|
|
let store = get_store().await?;
|
|
|
|
store.set_custom_value(key.as_bytes(), value.to_vec()).await?;
|
|
|
|
let read = store.get_custom_value(key.as_bytes()).await?;
|
|
|
|
assert_eq!(Some(value.as_ref()), read.as_deref());
|
|
|
|
Ok(())
|
|
}
|
|
|
|
#[async_test]
|
|
async fn test_persist_invited_room() -> StoreResult<()> {
|
|
let inner_store = get_store().await?;
|
|
let store = Arc::new(inner_store);
|
|
populate_store(store.clone()).await?;
|
|
|
|
assert_eq!(store.get_stripped_room_infos().await?.len(), 1);
|
|
|
|
Ok(())
|
|
}
|
|
|
|
#[async_test]
|
|
async fn test_stripped_non_stripped() -> StoreResult<()> {
|
|
let store = get_store().await.unwrap();
|
|
let room_id = room_id!("!test_stripped_non_stripped:localhost");
|
|
let user_id = user_id();
|
|
|
|
assert!(store.get_member_event(room_id, user_id).await.unwrap().is_none());
|
|
assert_eq!(store.get_room_infos().await.unwrap().len(), 0);
|
|
assert_eq!(store.get_stripped_room_infos().await.unwrap().len(), 0);
|
|
|
|
let mut changes = StateChanges::default();
|
|
changes
|
|
.members
|
|
.entry(room_id.to_owned())
|
|
.or_default()
|
|
.insert(user_id.to_owned(), membership_event());
|
|
changes.add_room(RoomInfo::new(room_id, RoomType::Left));
|
|
store.save_changes(&changes).await.unwrap();
|
|
|
|
assert!(matches!(
|
|
store.get_member_event(room_id, user_id).await.unwrap(),
|
|
Some(matrix_sdk_common::deserialized_responses::MemberEvent::Sync(_))
|
|
));
|
|
assert_eq!(store.get_room_infos().await.unwrap().len(), 1);
|
|
assert_eq!(store.get_stripped_room_infos().await.unwrap().len(), 0);
|
|
|
|
let members = store.get_user_ids(room_id).await.unwrap();
|
|
assert_eq!(members, vec![user_id.to_owned()]);
|
|
|
|
let mut changes = StateChanges::default();
|
|
changes.add_stripped_member(room_id, custom_stripped_membership_event(user_id));
|
|
changes.add_stripped_room(RoomInfo::new(room_id, RoomType::Invited));
|
|
store.save_changes(&changes).await.unwrap();
|
|
|
|
assert!(matches!(
|
|
store.get_member_event(room_id, user_id).await.unwrap(),
|
|
Some(matrix_sdk_common::deserialized_responses::MemberEvent::Stripped(_))
|
|
));
|
|
assert_eq!(store.get_room_infos().await.unwrap().len(), 0);
|
|
assert_eq!(store.get_stripped_room_infos().await.unwrap().len(), 1);
|
|
|
|
let members = store.get_user_ids(room_id).await.unwrap();
|
|
assert_eq!(members, vec![user_id.to_owned()]);
|
|
|
|
Ok(())
|
|
}
|
|
|
|
#[async_test]
|
|
async fn test_room_removal() -> StoreResult<()> {
|
|
let room_id = room_id();
|
|
let user_id = user_id();
|
|
let inner_store = get_store().await?;
|
|
let stripped_room_id = stripped_room_id();
|
|
|
|
let store = Arc::new(inner_store);
|
|
populate_store(store.clone()).await?;
|
|
|
|
store.remove_room(room_id).await?;
|
|
|
|
assert!(store.get_room_infos().await?.is_empty(), "room is still there");
|
|
assert_eq!(store.get_stripped_room_infos().await?.len(), 1);
|
|
|
|
assert!(store
|
|
.get_state_event(room_id, StateEventType::RoomName, "")
|
|
.await?
|
|
.is_none());
|
|
assert!(
|
|
store.get_state_events(room_id, StateEventType::RoomTopic).await?.is_empty(),
|
|
"still state events found"
|
|
);
|
|
assert!(store.get_profile(room_id, user_id).await?.is_none());
|
|
assert!(store.get_member_event(room_id, user_id).await?.is_none());
|
|
assert!(store.get_user_ids(room_id).await?.is_empty(), "still user ids found");
|
|
assert!(
|
|
store.get_invited_user_ids(room_id).await?.is_empty(),
|
|
"still invited user ids found"
|
|
);
|
|
assert!(
|
|
store.get_joined_user_ids(room_id).await?.is_empty(),
|
|
"still joined users found"
|
|
);
|
|
assert!(
|
|
store.get_users_with_display_name(room_id, "example").await?.is_empty(),
|
|
"still display names found"
|
|
);
|
|
assert!(store
|
|
.get_room_account_data_event(room_id, RoomAccountDataEventType::Tag)
|
|
.await?
|
|
.is_none());
|
|
assert!(store
|
|
.get_user_room_receipt_event(room_id, ReceiptType::Read, user_id)
|
|
.await?
|
|
.is_none());
|
|
assert!(
|
|
store
|
|
.get_event_room_receipt_events(
|
|
room_id,
|
|
ReceiptType::Read,
|
|
first_receipt_event_id()
|
|
)
|
|
.await?
|
|
.is_empty(),
|
|
"still event recepts in the store"
|
|
);
|
|
|
|
store.remove_room(stripped_room_id).await?;
|
|
|
|
assert!(store.get_room_infos().await?.is_empty(), "still room info found");
|
|
assert!(
|
|
store.get_stripped_room_infos().await?.is_empty(),
|
|
"still stripped room info found"
|
|
);
|
|
Ok(())
|
|
}
|
|
|
|
#[async_test]
|
|
#[cfg(feature = "experimental-timeline")]
|
|
async fn test_room_timeline() {
|
|
let store = get_store().await.unwrap();
|
|
let mut stored_events = Vec::new();
|
|
let room_id = *test_json::DEFAULT_SYNC_ROOM_ID;
|
|
|
|
// Before the first sync the timeline should be empty
|
|
assert!(
|
|
store.room_timeline(room_id).await.expect("failed to read timeline").is_none(),
|
|
"TL wasn't empty"
|
|
);
|
|
|
|
// Add sync response
|
|
let sync = SyncResponse::try_from_http_response(
|
|
Response::builder()
|
|
.body(
|
|
serde_json::to_vec(&*test_json::MORE_SYNC)
|
|
.expect("Parsing MORE_SYNC failed"),
|
|
)
|
|
.unwrap(),
|
|
)
|
|
.unwrap();
|
|
|
|
let timeline = &sync.rooms.join[room_id].timeline;
|
|
let events: Vec<SyncTimelineEvent> =
|
|
timeline.events.iter().cloned().map(Into::into).collect();
|
|
|
|
stored_events.extend(events.iter().rev().cloned());
|
|
|
|
let timeline_slice = TimelineSlice::new(
|
|
events,
|
|
sync.next_batch.clone(),
|
|
timeline.prev_batch.clone(),
|
|
false,
|
|
true,
|
|
);
|
|
let mut changes = StateChanges::new(sync.next_batch.clone());
|
|
changes.add_timeline(room_id, timeline_slice);
|
|
store.save_changes(&changes).await.expect("Saving room timeline failed");
|
|
|
|
check_timeline_events(
|
|
room_id,
|
|
&store,
|
|
&stored_events,
|
|
timeline.prev_batch.as_deref(),
|
|
)
|
|
.await;
|
|
|
|
// Add message response
|
|
let messages = MessageResponse::try_from_http_response(
|
|
Response::builder()
|
|
.body(
|
|
serde_json::to_vec(&*test_json::ROOM_MESSAGES_BATCH_1)
|
|
.expect("Parsing ROOM_MESSAGES_BATCH_1 failed"),
|
|
)
|
|
.unwrap(),
|
|
)
|
|
.unwrap();
|
|
|
|
let events: Vec<SyncTimelineEvent> = messages
|
|
.chunk
|
|
.iter()
|
|
.cloned()
|
|
.map(|event| TimelineEvent { event, encryption_info: None }.into())
|
|
.collect();
|
|
|
|
stored_events.append(&mut events.clone());
|
|
|
|
let timeline_slice = TimelineSlice::new(
|
|
events,
|
|
messages.start.clone(),
|
|
messages.end.clone(),
|
|
false,
|
|
false,
|
|
);
|
|
let mut changes = StateChanges::default();
|
|
changes.add_timeline(room_id, timeline_slice);
|
|
store.save_changes(&changes).await.expect("Saving room update timeline failed");
|
|
|
|
check_timeline_events(room_id, &store, &stored_events, messages.end.as_deref())
|
|
.await;
|
|
|
|
// Add second message response
|
|
let messages = MessageResponse::try_from_http_response(
|
|
Response::builder()
|
|
.body(
|
|
serde_json::to_vec(&*test_json::ROOM_MESSAGES_BATCH_2)
|
|
.expect("Parsing ROOM_MESSAGES_BATCH_2 failed"),
|
|
)
|
|
.unwrap(),
|
|
)
|
|
.unwrap();
|
|
|
|
let events: Vec<SyncTimelineEvent> = messages
|
|
.chunk
|
|
.iter()
|
|
.cloned()
|
|
.map(|event| TimelineEvent { event, encryption_info: None }.into())
|
|
.collect();
|
|
|
|
stored_events.append(&mut events.clone());
|
|
|
|
let timeline_slice = TimelineSlice::new(
|
|
events,
|
|
messages.start.clone(),
|
|
messages.end.clone(),
|
|
false,
|
|
false,
|
|
);
|
|
let mut changes = StateChanges::default();
|
|
changes.add_timeline(room_id, timeline_slice);
|
|
store.save_changes(&changes).await.expect("Saving room update timeline 2 failed");
|
|
|
|
check_timeline_events(room_id, &store, &stored_events, messages.end.as_deref())
|
|
.await;
|
|
|
|
// Add second sync response
|
|
let sync = SyncResponse::try_from_http_response(
|
|
Response::builder()
|
|
.body(serde_json::to_vec(&*test_json::MORE_SYNC_2).unwrap())
|
|
.unwrap(),
|
|
)
|
|
.unwrap();
|
|
|
|
let timeline = &sync.rooms.join[room_id].timeline;
|
|
let events: Vec<SyncTimelineEvent> =
|
|
timeline.events.iter().cloned().map(Into::into).collect();
|
|
|
|
let prev_stored_events = stored_events;
|
|
stored_events = events.iter().rev().cloned().collect();
|
|
stored_events.extend(prev_stored_events);
|
|
|
|
let timeline_slice = TimelineSlice::new(
|
|
events,
|
|
sync.next_batch.clone(),
|
|
timeline.prev_batch.clone(),
|
|
false,
|
|
true,
|
|
);
|
|
let mut changes = StateChanges::new(sync.next_batch.clone());
|
|
changes.add_timeline(room_id, timeline_slice);
|
|
store.save_changes(&changes).await.expect("Saving room update timeline 3 failed");
|
|
|
|
check_timeline_events(room_id, &store, &stored_events, messages.end.as_deref())
|
|
.await;
|
|
|
|
// Check if limited sync removes the stored timeline
|
|
let end_token = Some("end token".to_owned());
|
|
let timeline_slice = TimelineSlice::new(
|
|
Vec::new(),
|
|
"start token".to_owned(),
|
|
end_token.clone(),
|
|
true,
|
|
true,
|
|
);
|
|
let mut changes = StateChanges::default();
|
|
changes.add_timeline(room_id, timeline_slice);
|
|
store.save_changes(&changes).await.expect("Saving room update timeline 4 failed");
|
|
|
|
check_timeline_events(room_id, &store, &Vec::new(), end_token.as_deref()).await;
|
|
}
|
|
|
|
#[cfg(feature = "experimental-timeline")]
|
|
async fn check_timeline_events(
|
|
room_id: &RoomId,
|
|
store: &dyn StateStore,
|
|
stored_events: &[SyncTimelineEvent],
|
|
expected_end_token: Option<&str>,
|
|
) {
|
|
let (timeline_iter, end_token) =
|
|
store.room_timeline(room_id).await.unwrap().unwrap();
|
|
|
|
assert_eq!(end_token.as_deref(), expected_end_token);
|
|
|
|
let timeline = timeline_iter.collect::<Vec<StoreResult<SyncTimelineEvent>>>().await;
|
|
|
|
let expected: Vec<OwnedEventId> = stored_events
|
|
.iter()
|
|
.map(|a| a.event_id().expect("event id doesn't exist"))
|
|
.collect();
|
|
let found: Vec<OwnedEventId> = timeline
|
|
.iter()
|
|
.map(|a| {
|
|
a.as_ref()
|
|
.expect("object missing")
|
|
.event_id()
|
|
.clone()
|
|
.expect("event id missing")
|
|
})
|
|
.collect();
|
|
|
|
for (idx, (a, b)) in timeline.into_iter().zip(stored_events.iter()).enumerate() {
|
|
assert_eq!(
|
|
a.expect("not a value").event_id(),
|
|
b.event_id(),
|
|
"pos {idx} not equal - expected: {expected:#?}, but found {found:#?}",
|
|
);
|
|
}
|
|
}
|
|
}
|
|
};
|
|
}
|