Skip to content

Commit

Permalink
sdk-ui: add and fix tests
Browse files Browse the repository at this point in the history
  • Loading branch information
jmartinesp committed Aug 2, 2024
1 parent 6889430 commit ab12f2f
Show file tree
Hide file tree
Showing 4 changed files with 293 additions and 1 deletion.
6 changes: 6 additions & 0 deletions crates/matrix-sdk-ui/src/timeline/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ use matrix_sdk::{
};
use thiserror::Error;

use crate::timeline::pinned_events_loader::PinnedEventsLoaderError;

/// Errors specific to the timeline.
#[derive(Error, Debug)]
#[non_exhaustive]
Expand Down Expand Up @@ -60,6 +62,10 @@ pub enum Error {
#[error("An error happened during pagination.")]
PaginationError(#[from] PaginationError),

/// An error happened during pagination.
#[error("An error happened when loading pinned events.")]
PinnedEventsError(#[from] PinnedEventsLoaderError),

/// An error happened while operating the room's send queue.
#[error(transparent)]
SendQueueError(#[from] RoomSendQueueError),
Expand Down
16 changes: 16 additions & 0 deletions crates/matrix-sdk-ui/tests/integration/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,22 @@ async fn mock_context(
.await;
}

/// Mocks the /event endpoint
#[allow(clippy::too_many_arguments)] // clippy you've got such a fixed mindset
async fn mock_event(
server: &MockServer,
room_id: &RoomId,
event_id: &EventId,
event: TimelineEvent,
) {
Mock::given(method("GET"))
.and(path(format!("/_matrix/client/r0/rooms/{room_id}/event/{event_id}")))
.and(header("authorization", "Bearer 1234"))
.respond_with(ResponseTemplate::new(200).set_body_json(event.event.json()))
.mount(server)
.await;
}

/// Mocks the /messages endpoint.
///
/// Note: pass `chunk` in the correct order: topological for forward pagination,
Expand Down
270 changes: 270 additions & 0 deletions crates/matrix-sdk-ui/tests/integration/timeline/pinned_event.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,270 @@
use std::time::Duration;

use assert_matches::assert_matches;
use eyeball_im::VectorDiff;
use futures_util::StreamExt;
use matrix_sdk::{
config::SyncSettings,
sync::SyncResponse,
test_utils::{events::EventFactory, logged_in_client_with_server},
Client,
};
use matrix_sdk_test::{async_test, JoinedRoomBuilder, StateTestEvent, SyncResponseBuilder, BOB};
use matrix_sdk_ui::{timeline::TimelineFocus, Timeline};
use ruma::{event_id, owned_room_id, OwnedEventId, OwnedRoomId};
use serde_json::json;
use stream_assert::assert_pending;
use wiremock::MockServer;

use crate::{mock_event, mock_sync};

#[async_test]
async fn test_new_pinned_events_are_added_on_sync() {
let mut test_helper = TestHelper::new().await;
let room_id = test_helper.room_id.clone();

// Join the room
let _ = test_helper.setup_initial_sync_response().await;
test_helper.server.reset().await;

// Load initial timeline items: a text message and a `m.room.pinned_events` with
// events $1 and $2 pinned
let _ = test_helper
.setup_sync_response(vec![("$1", "in the end", false)], Some(vec!["$1", "$2"]))
.await;

let room = test_helper.client.get_room(&room_id).unwrap();
let timeline = Timeline::builder(&room)
.with_focus(TimelineFocus::PinnedEvents { max_events_to_load: 100 })
.build()
.await
.unwrap();
test_helper.server.reset().await;

assert!(
timeline.live_back_pagination_status().await.is_none(),
"there should be no live back-pagination status for a focused timeline"
);

// Load timeline items
let (items, mut timeline_stream) = timeline.subscribe().await;

assert_eq!(items.len(), 1 + 1); // event item + a day divider
assert!(items[0].is_day_divider());
assert_eq!(items[1].as_event().unwrap().content().as_message().unwrap().body(), "in the end");
assert_pending!(timeline_stream);
test_helper.server.reset().await;

// Load new pinned event contents from sync, $2 was pinned but wasn't available
// before
let _ = test_helper
.setup_sync_response(
vec![("$2", "pinned message!", true), ("$3", "normal message", true)],
None,
)
.await;

// The list is reloaded, so it's reset
assert_matches!(timeline_stream.next().await.unwrap(), VectorDiff::Clear);
assert_matches!(timeline_stream.next().await.unwrap(), VectorDiff::PushBack { value } => {
assert_eq!(value.as_event().unwrap().event_id().unwrap(), event_id!("$1"));
});
assert_matches!(timeline_stream.next().await.unwrap(), VectorDiff::PushBack { value } => {
assert_eq!(value.as_event().unwrap().event_id().unwrap(), event_id!("$2"));
});
assert_matches!(timeline_stream.next().await.unwrap(), VectorDiff::PushFront { value } => {
assert!(value.is_day_divider());
});
assert_pending!(timeline_stream);
test_helper.server.reset().await;
}

#[async_test]
async fn test_new_pinned_event_ids_reload_the_timeline() {
let mut test_helper = TestHelper::new().await;
let room_id = test_helper.room_id.clone();

// Join the room
let _ = test_helper.setup_initial_sync_response().await;
test_helper.server.reset().await;

// Load initial timeline items: 2 text messages and a `m.room.pinned_events`
// with event $1 and $2 pinned
let _ = test_helper
.setup_sync_response(
vec![("$1", "in the end", false), ("$2", "it doesn't even matter", true)],
Some(vec!["$1"]),
)
.await;

let room = test_helper.client.get_room(&room_id).unwrap();
let timeline = Timeline::builder(&room)
.with_focus(TimelineFocus::PinnedEvents { max_events_to_load: 100 })
.build()
.await
.unwrap();

assert!(
timeline.live_back_pagination_status().await.is_none(),
"there should be no live back-pagination status for a focused timeline"
);

let (items, mut timeline_stream) = timeline.subscribe().await;

assert_eq!(items.len(), 1 + 1); // event item + a day divider
assert!(items[0].is_day_divider());
assert_eq!(items[1].as_event().unwrap().content().as_message().unwrap().body(), "in the end");
assert_pending!(timeline_stream);
test_helper.server.reset().await;

// Reload timeline with new pinned event ids
let _ = test_helper
.setup_sync_response(
vec![("$1", "in the end", false), ("$2", "it doesn't even matter", false)],
Some(vec!["$1", "$2"]),
)
.await;

assert_matches!(timeline_stream.next().await.unwrap(), VectorDiff::Clear);
assert_matches!(timeline_stream.next().await.unwrap(), VectorDiff::PushBack { value } => {
assert_eq!(value.as_event().unwrap().event_id().unwrap(), event_id!("$1"));
});
assert_matches!(timeline_stream.next().await.unwrap(), VectorDiff::PushBack { value } => {
assert_eq!(value.as_event().unwrap().event_id().unwrap(), event_id!("$2"));
});
assert_matches!(timeline_stream.next().await.unwrap(), VectorDiff::PushFront { value } => {
assert!(value.is_day_divider());
});
assert_pending!(timeline_stream);
test_helper.server.reset().await;

// Reload timeline with no pinned event ids
let _ = test_helper
.setup_sync_response(
vec![("$1", "in the end", false), ("$2", "it doesn't even matter", false)],
Some(Vec::new()),
)
.await;

assert_matches!(timeline_stream.next().await.unwrap(), VectorDiff::Clear);
assert_pending!(timeline_stream);
test_helper.server.reset().await;
}

#[async_test]
async fn test_max_events_to_load_is_honored() {
let mut test_helper = TestHelper::new().await;
let room_id = test_helper.room_id.clone();

// Join the room
let _ = test_helper.setup_initial_sync_response().await;
test_helper.server.reset().await;

// Load initial timeline items: a text message and a `m.room.pinned_events`
// with event $1 and $2 pinned
let _ = test_helper
.setup_sync_response(vec![("$1", "in the end", false)], Some(vec!["$1", "$2"]))
.await;

let room = test_helper.client.get_room(&room_id).unwrap();
let timeline = Timeline::builder(&room)
.with_focus(TimelineFocus::PinnedEvents { max_events_to_load: 1 })
.build()
.await
.unwrap();

assert!(
timeline.live_back_pagination_status().await.is_none(),
"there should be no live back-pagination status for a focused timeline"
);

let (items, mut timeline_stream) = timeline.subscribe().await;

assert!(items.is_empty()); // We're only taking the last event id, `$2`, and it's not available
assert_pending!(timeline_stream);
test_helper.server.reset().await;
}

struct TestHelper {
pub client: Client,
pub server: MockServer,
pub room_id: OwnedRoomId,
pub sync_settings: SyncSettings,
pub sync_response_builder: SyncResponseBuilder,
}

impl TestHelper {
async fn new() -> Self {
let (client, server) = logged_in_client_with_server().await;
Self {
client,
server,
room_id: owned_room_id!("!a98sd12bjh:example.org"),
sync_settings: SyncSettings::new().timeout(Duration::from_millis(3000)),
sync_response_builder: SyncResponseBuilder::new(),
}
}

async fn setup_initial_sync_response(&mut self) -> Result<SyncResponse, matrix_sdk::Error> {
let joined_room_builder = JoinedRoomBuilder::new(&self.room_id)
// Set up encryption
.add_state_event(StateTestEvent::Encryption);

// Mark the room as joined.
let json_response = self
.sync_response_builder
.add_joined_room(joined_room_builder)
.build_json_sync_response();
mock_sync(&self.server, json_response, None).await;
self.client.sync_once(self.sync_settings.clone()).await
}

async fn setup_sync_response(
&mut self,
text_messages: Vec<(&str, &str, bool)>,
pinned_event_ids: Option<Vec<&str>>,
) -> Result<SyncResponse, matrix_sdk::Error> {
let mut joined_room_builder = JoinedRoomBuilder::new(&self.room_id);
for (id, txt, add_to_timeline) in text_messages {
let event_id: OwnedEventId = id.try_into().unwrap();
let f = EventFactory::new().room(&self.room_id);
let event_builder = f.text_msg(txt).event_id(&event_id).sender(*BOB);
mock_event(&self.server, &self.room_id, &event_id, event_builder.into_timeline()).await;

if add_to_timeline {
let event_builder = f.text_msg(txt).event_id(&event_id).sender(*BOB);
joined_room_builder = joined_room_builder
.add_timeline_event(event_builder.into_raw_timeline().cast());
}
}

if let Some(pinned_event_ids) = pinned_event_ids {
let pinned_event_ids: Vec<String> =
pinned_event_ids.into_iter().map(|id| id.to_owned()).collect();
joined_room_builder =
joined_room_builder.add_state_event(StateTestEvent::Custom(json!(
{
"content": {
"pinned": pinned_event_ids
},
"event_id": "$15139375513VdeRF:localhost",
"origin_server_ts": 151393755,
"sender": "@example:localhost",
"state_key": "",
"type": "m.room.pinned_events",
"unsigned": {
"age": 703422
}
}
)))
}

// Mark the room as joined.
let json_response = self
.sync_response_builder
.add_joined_room(joined_room_builder)
.build_json_sync_response();
mock_sync(&self.server, json_response, None).await;
self.client.sync_once(self.sync_settings.clone()).await
}
}
2 changes: 1 addition & 1 deletion testing/matrix-sdk-test/src/test_json/sync_events.rs
Original file line number Diff line number Diff line change
Expand Up @@ -320,7 +320,7 @@ pub static NAME_STRIPPED: Lazy<JsonValue> = Lazy::new(|| {
pub static PINNED_EVENTS: Lazy<JsonValue> = Lazy::new(|| {
json!({
"content": {
"pinned": [ "$a" ]
"pinned": [ "$a", "$b" ]
},
"event_id": "$15139375513VdeRF:localhost",
"origin_server_ts": 151393755,
Expand Down

0 comments on commit ab12f2f

Please sign in to comment.