Skip to content

Commit ced8c4c

Browse files
committed
send queue: add a test that simulates unreachable network/server
1 parent f45098a commit ced8c4c

File tree

3 files changed

+82
-5
lines changed

3 files changed

+82
-5
lines changed

crates/matrix-sdk/src/http_client/mod.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -278,7 +278,10 @@ impl RetryKind {
278278
}
279279
}
280280

281-
/// Returns whether an API error is transient or permanent,
281+
/// Returns whether an HTTP error response should be qualified as transient or
282+
/// permanent.
283+
///
284+
/// This is what decides whether to retry requests, in the [`native`] module.
282285
pub(crate) fn characterize_retry_kind(err: HttpError) -> RetryKind {
283286
use ruma::api::client::error::{ErrorBody, ErrorKind, RetryAfter};
284287

crates/matrix-sdk/src/send_queue.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -380,9 +380,9 @@ impl RoomSendQueue {
380380
let http_err = retry_kind.error();
381381

382382
if !is_recoverable {
383-
// Also classify raw HTTP-level request errors as recoverable, because
384-
// `characterize_retry_kind` doesn't do that, and bnjbvr isn't clear
385-
// whether it should.
383+
// Also flag all HTTP errors as recoverable: they might indicate that
384+
// the server was unreachable, or that the device is entirely
385+
// disconnected.
386386
is_recoverable =
387387
matches!(http_err, crate::error::HttpError::Reqwest(..));
388388
}

crates/matrix-sdk/tests/integration/send_queue.rs

Lines changed: 75 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ use std::{
99
use assert_matches2::{assert_let, assert_matches};
1010
use matrix_sdk::{
1111
send_queue::{LocalEcho, RoomSendQueueError, RoomSendQueueUpdate},
12-
test_utils::logged_in_client_with_server,
12+
test_utils::{logged_in_client, logged_in_client_with_server},
1313
};
1414
use matrix_sdk_test::{async_test, InvitedRoomBuilder, JoinedRoomBuilder, LeftRoomBuilder};
1515
use ruma::{
@@ -974,3 +974,77 @@ async fn test_unrecoverable_errors() {
974974
assert!(room.send_queue().is_enabled());
975975
assert!(client.send_queue().is_enabled());
976976
}
977+
978+
#[async_test]
979+
async fn test_no_network_access_error_is_recoverable() {
980+
// This is subtle, but for the `drop(server)` below to be effectful, it needs to
981+
// not be a pooled wiremock server (the default), which will keep the dropped
982+
// server in a static. Using the line below will create a "bare" server,
983+
// which is effectively dropped upon `drop()`.
984+
let server = wiremock::MockServer::builder().start().await;
985+
986+
let client = logged_in_client(Some(server.uri().to_string())).await;
987+
988+
// Mark the room as joined.
989+
let room_id = room_id!("!a:b.c");
990+
991+
let room = mock_sync_with_new_room(
992+
|builder| {
993+
builder.add_joined_room(JoinedRoomBuilder::new(room_id));
994+
},
995+
&client,
996+
&server,
997+
room_id,
998+
)
999+
.await;
1000+
1001+
// Dropping the server: any subsequent attempt to connect mimics an unreachable
1002+
// server, which might be caused by missing network.
1003+
drop(server);
1004+
1005+
let mut errors = client.send_queue().subscribe_errors();
1006+
assert!(errors.is_empty());
1007+
1008+
// Start with an enabled sending queue.
1009+
client.send_queue().set_enabled(true);
1010+
assert!(client.send_queue().is_enabled());
1011+
1012+
let q = room.send_queue();
1013+
let (local_echoes, mut watch) = q.subscribe().await;
1014+
1015+
assert!(local_echoes.is_empty());
1016+
assert!(watch.is_empty());
1017+
1018+
// Queue two messages.
1019+
q.send(RoomMessageEventContent::text_plain("is there anyone around here").into())
1020+
.await
1021+
.unwrap();
1022+
1023+
// First message is seen as a local echo.
1024+
assert_let!(
1025+
Ok(Ok(RoomSendQueueUpdate::NewLocalEvent(LocalEcho {
1026+
content: AnyMessageLikeEventContent::RoomMessage(msg),
1027+
transaction_id: txn1,
1028+
..
1029+
}))) = timeout(Duration::from_secs(1), watch.recv()).await
1030+
);
1031+
assert_eq!(msg.body(), format!("is there anyone around here"));
1032+
1033+
// There will be an error report for the first message, indicating that the
1034+
// error is recoverable: because network is unreachable.
1035+
let report = errors.recv().await.unwrap();
1036+
assert_eq!(report.room_id, room.room_id());
1037+
assert!(report.is_recoverable);
1038+
1039+
// The room updates will report the error for the first message as recoverable
1040+
// too.
1041+
assert_let!(
1042+
Ok(Ok(RoomSendQueueUpdate::SendError { is_recoverable: true, transaction_id, .. })) =
1043+
timeout(Duration::from_secs(1), watch.recv()).await
1044+
);
1045+
assert_eq!(transaction_id, txn1);
1046+
1047+
// The room queue is disabled, because the error was recoverable.
1048+
assert!(!room.send_queue().is_enabled());
1049+
assert!(client.send_queue().is_enabled());
1050+
}

0 commit comments

Comments
 (0)