refactor(tests): add a test helper to send with a delay, use it in timeline/queue
This commit is contained in:
@@ -28,10 +28,6 @@ use ruma::{
|
||||
use serde_json::json;
|
||||
use stream_assert::{assert_next_matches, assert_pending};
|
||||
use tokio::{task::yield_now, time::sleep};
|
||||
use wiremock::{
|
||||
Mock, ResponseTemplate,
|
||||
matchers::{body_string_contains, method, path_regex},
|
||||
};
|
||||
|
||||
#[async_test]
|
||||
async fn test_message_order() {
|
||||
@@ -46,28 +42,20 @@ async fn test_message_order() {
|
||||
timeline.subscribe_filter_map(|item| item.as_event().cloned()).await;
|
||||
|
||||
// Response for first message takes 200ms to respond
|
||||
Mock::given(method("PUT"))
|
||||
.and(path_regex(r"^/_matrix/client/v3/rooms/.*/send/.*"))
|
||||
.and(body_string_contains("First!"))
|
||||
.respond_with(
|
||||
ResponseTemplate::new(200)
|
||||
.set_body_json(json!({ "event_id": "$ev0" }))
|
||||
.set_delay(Duration::from_millis(200)),
|
||||
)
|
||||
.mount(server.server())
|
||||
server
|
||||
.mock_room_send()
|
||||
.body_matches_partial_json(json!({ "body": "First!" }))
|
||||
.ok_with_delay(event_id!("$ev0"), Duration::from_millis(200))
|
||||
.mount()
|
||||
.await;
|
||||
|
||||
// Response for second message only takes 100ms to respond, so should come
|
||||
// back first if we don't serialize requests
|
||||
Mock::given(method("PUT"))
|
||||
.and(path_regex(r"^/_matrix/client/v3/rooms/.*/send/.*"))
|
||||
.and(body_string_contains("Second."))
|
||||
.respond_with(
|
||||
ResponseTemplate::new(200)
|
||||
.set_body_json(json!({ "event_id": "$ev1" }))
|
||||
.set_delay(Duration::from_millis(100)),
|
||||
)
|
||||
.mount(server.server())
|
||||
server
|
||||
.mock_room_send()
|
||||
.body_matches_partial_json(json!({ "body": "Second." }))
|
||||
.ok_with_delay(event_id!("$ev1"), Duration::from_millis(100))
|
||||
.mount()
|
||||
.await;
|
||||
|
||||
timeline.send(RoomMessageEventContent::text_plain("First!").into()).await.unwrap();
|
||||
@@ -132,12 +120,7 @@ async fn test_retry_order() {
|
||||
|
||||
// When trying to send an event, return with a 500 error, which is interpreted
|
||||
// as a transient error.
|
||||
let scoped_faulty_send = server
|
||||
.mock_room_send()
|
||||
.respond_with(ResponseTemplate::new(500))
|
||||
.expect(3)
|
||||
.mount_as_scoped()
|
||||
.await;
|
||||
let scoped_faulty_send = server.mock_room_send().error500().expect(3).mount_as_scoped().await;
|
||||
|
||||
// Send two messages without mocking the server response.
|
||||
// It will respond with a 500, resulting in a failed-to-send state.
|
||||
@@ -167,28 +150,20 @@ async fn test_retry_order() {
|
||||
|
||||
// Response for first message takes 100ms to respond.
|
||||
drop(scoped_faulty_send);
|
||||
Mock::given(method("PUT"))
|
||||
.and(path_regex(r"^/_matrix/client/v3/rooms/.*/send/.*"))
|
||||
.and(body_string_contains("First!"))
|
||||
.respond_with(
|
||||
ResponseTemplate::new(200)
|
||||
.set_body_json(json!({ "event_id": "$ev0" }))
|
||||
.set_delay(Duration::from_millis(100)),
|
||||
)
|
||||
.mount(server.server())
|
||||
server
|
||||
.mock_room_send()
|
||||
.body_matches_partial_json(json!({ "body": "First!" }))
|
||||
.ok_with_delay(event_id!("$ev0"), Duration::from_millis(100))
|
||||
.mount()
|
||||
.await;
|
||||
|
||||
// Response for second message takes 200ms to respond, so should come back
|
||||
// after first if we don't serialize retries.
|
||||
Mock::given(method("PUT"))
|
||||
.and(path_regex(r"^/_matrix/client/v3/rooms/.*/send/.*"))
|
||||
.and(body_string_contains("Second."))
|
||||
.respond_with(
|
||||
ResponseTemplate::new(200)
|
||||
.set_body_json(json!({ "event_id": "$ev1" }))
|
||||
.set_delay(Duration::from_millis(200)),
|
||||
)
|
||||
.mount(server.server())
|
||||
server
|
||||
.mock_room_send()
|
||||
.body_matches_partial_json(json!({ "body": "Second." }))
|
||||
.ok_with_delay(event_id!("$ev1"), Duration::from_millis(200))
|
||||
.mount()
|
||||
.await;
|
||||
|
||||
// Retry the second message first.
|
||||
@@ -244,16 +219,7 @@ async fn test_reloaded_failed_local_echoes_are_marked_as_failed() {
|
||||
|
||||
// When trying to send an event, return with a 413 error, which is interpreted
|
||||
// as a permanent error.
|
||||
server
|
||||
.mock_room_send()
|
||||
.respond_with(ResponseTemplate::new(413).set_body_json(json!({
|
||||
// From https://spec.matrix.org/v1.10/client-server-api/#standard-error-response
|
||||
"errcode": "M_TOO_LARGE",
|
||||
"error": "Sounds like you have a lot to say!"
|
||||
})))
|
||||
.expect(1)
|
||||
.mount()
|
||||
.await;
|
||||
server.mock_room_send().error_too_large().expect(1).mount().await;
|
||||
|
||||
// Sending an event will respond with a 500, resulting in a failed-to-send
|
||||
// state.
|
||||
@@ -301,10 +267,7 @@ async fn test_reloaded_failed_local_echoes_are_marked_as_failed() {
|
||||
assert_matches!(&**error, QueueWedgeError::GenericApiError { msg } => { msg })
|
||||
}
|
||||
);
|
||||
assert_eq!(
|
||||
msg,
|
||||
"the server returned an error: [413 / M_TOO_LARGE] Sounds like you have a lot to say!"
|
||||
);
|
||||
assert_eq!(msg, "the server returned an error: [413 / M_TOO_LARGE] Request body too large");
|
||||
}
|
||||
|
||||
#[async_test]
|
||||
@@ -338,14 +301,10 @@ async fn test_clear_with_echoes() {
|
||||
}
|
||||
|
||||
// Next message will take "forever" to send.
|
||||
Mock::given(method("PUT"))
|
||||
.and(path_regex(r"^/_matrix/client/v3/rooms/.*/send/.*"))
|
||||
.respond_with(
|
||||
ResponseTemplate::new(200)
|
||||
.set_body_json(json!({ "event_id": "$PyHxV5mYzjetBUT3qZq7V95GOzxb02EP" }))
|
||||
.set_delay(Duration::from_secs(3600)),
|
||||
)
|
||||
.mount(server.server())
|
||||
server
|
||||
.mock_room_send()
|
||||
.ok_with_delay(event_id!("$PyHxV5mYzjetBUT3qZq7V95GOzxb02EP"), Duration::from_secs(3600))
|
||||
.mount()
|
||||
.await;
|
||||
|
||||
// (this one)
|
||||
@@ -402,32 +361,20 @@ async fn test_no_duplicate_date_divider() {
|
||||
let (_, mut timeline_stream) = timeline.subscribe().await;
|
||||
|
||||
// Response for first message takes 200ms to respond.
|
||||
Mock::given(method("PUT"))
|
||||
.and(path_regex(r"^/_matrix/client/v3/rooms/.*/send/.*"))
|
||||
.and(body_string_contains("First!"))
|
||||
.respond_with(
|
||||
ResponseTemplate::new(200)
|
||||
.set_body_json(json!({
|
||||
"event_id": "$ev0",
|
||||
}))
|
||||
.set_delay(Duration::from_millis(200)),
|
||||
)
|
||||
.mount(server.server())
|
||||
server
|
||||
.mock_room_send()
|
||||
.body_matches_partial_json(json!({ "body": "First!" }))
|
||||
.ok_with_delay(event_id!("$ev0"), Duration::from_millis(200))
|
||||
.mount()
|
||||
.await;
|
||||
|
||||
// Response for second message only takes 100ms to respond, so should come
|
||||
// back first if we don't serialize requests.
|
||||
Mock::given(method("PUT"))
|
||||
.and(path_regex(r"^/_matrix/client/v3/rooms/.*/send/.*"))
|
||||
.and(body_string_contains("Second."))
|
||||
.respond_with(
|
||||
ResponseTemplate::new(200)
|
||||
.set_body_json(json!({
|
||||
"event_id": "$ev1",
|
||||
}))
|
||||
.set_delay(Duration::from_millis(100)),
|
||||
)
|
||||
.mount(server.server())
|
||||
server
|
||||
.mock_room_send()
|
||||
.body_matches_partial_json(json!({ "body": "Second." }))
|
||||
.ok_with_delay(event_id!("$ev1"), Duration::from_millis(100))
|
||||
.mount()
|
||||
.await;
|
||||
|
||||
timeline.send(RoomMessageEventContent::text_plain("First!").into()).await.unwrap();
|
||||
|
||||
@@ -2068,6 +2068,7 @@ impl<'a, T> MockEndpoint<'a, T> {
|
||||
self.respond_with(ResponseTemplate::new(413).set_body_json(json!({
|
||||
// From https://spec.matrix.org/v1.10/client-server-api/#standard-error-response
|
||||
"errcode": "M_TOO_LARGE",
|
||||
"error": "Request body too large",
|
||||
})))
|
||||
}
|
||||
}
|
||||
@@ -2325,6 +2326,62 @@ impl<'a> MockEndpoint<'a, RoomSendEndpoint> {
|
||||
self.ok_with_event_id(returned_event_id.into())
|
||||
}
|
||||
|
||||
/// Returns a send endpoint that emulates success after a delay, i.e. the
|
||||
/// event has been sent with the given event id, but the response is delayed
|
||||
/// by the given duration.
|
||||
///
|
||||
/// This is useful for testing ordering guarantees when multiple events are
|
||||
/// in-flight simultaneously.
|
||||
///
|
||||
/// # Examples
|
||||
/// ```
|
||||
/// # tokio_test::block_on(async {
|
||||
/// use std::time::Duration;
|
||||
///
|
||||
/// use matrix_sdk::{
|
||||
/// ruma::{event_id, room_id},
|
||||
/// test_utils::mocks::MatrixMockServer,
|
||||
/// };
|
||||
/// use serde_json::json;
|
||||
///
|
||||
/// let mock_server = MatrixMockServer::new().await;
|
||||
/// let client = mock_server.client_builder().build().await;
|
||||
///
|
||||
/// mock_server.mock_room_state_encryption().plain().mount().await;
|
||||
///
|
||||
/// let room = mock_server
|
||||
/// .sync_joined_room(&client, room_id!("!room_id:localhost"))
|
||||
/// .await;
|
||||
///
|
||||
/// mock_server
|
||||
/// .mock_room_send()
|
||||
/// .ok_with_delay(event_id!("$some_id"), Duration::from_millis(100))
|
||||
/// .mock_once()
|
||||
/// .mount()
|
||||
/// .await;
|
||||
///
|
||||
/// let result = room.send_raw("m.room.message", json!({ "body": "Hello world" })).await?;
|
||||
///
|
||||
/// assert_eq!(
|
||||
/// event_id!("$some_id"),
|
||||
/// result.response.event_id,
|
||||
/// "The event ID we mocked should match the one we received when we sent the event"
|
||||
/// );
|
||||
/// # anyhow::Ok(()) });
|
||||
/// ```
|
||||
pub fn ok_with_delay(
|
||||
self,
|
||||
returned_event_id: impl Into<OwnedEventId>,
|
||||
delay: Duration,
|
||||
) -> MatrixMock<'a> {
|
||||
let event_id = returned_event_id.into();
|
||||
self.respond_with(
|
||||
ResponseTemplate::new(200)
|
||||
.set_body_json(json!({ "event_id": event_id }))
|
||||
.set_delay(delay),
|
||||
)
|
||||
}
|
||||
|
||||
/// Returns a send endpoint that emulates success, i.e. the event has been
|
||||
/// sent with the given event id.
|
||||
///
|
||||
|
||||
Reference in New Issue
Block a user