158 lines
5.7 KiB
Rust
158 lines
5.7 KiB
Rust
// Copyright 2023 The Matrix.org Foundation C.I.C.
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
use std::{io, sync::Arc};
|
|
|
|
use assert_matches::assert_matches;
|
|
use eyeball_im::VectorDiff;
|
|
use matrix_sdk::Error;
|
|
use matrix_sdk_test::async_test;
|
|
use ruma::{
|
|
event_id,
|
|
events::{room::message::RoomMessageEventContent, AnyMessageLikeEventContent},
|
|
};
|
|
use serde_json::json;
|
|
use stream_assert::assert_next_matches;
|
|
|
|
use super::{TestTimeline, ALICE, BOB};
|
|
use crate::timeline::event_item::EventSendState;
|
|
|
|
#[async_test]
|
|
async fn remote_echo_full_trip() {
|
|
let timeline = TestTimeline::new();
|
|
let mut stream = timeline.subscribe().await;
|
|
|
|
// Given a local event…
|
|
let txn_id = timeline
|
|
.handle_local_event(AnyMessageLikeEventContent::RoomMessage(
|
|
RoomMessageEventContent::text_plain("echo"),
|
|
))
|
|
.await;
|
|
|
|
let _day_divider = assert_next_matches!(stream, VectorDiff::PushBack { value } => value);
|
|
|
|
// Scenario 1: The local event has not been sent yet to the server.
|
|
{
|
|
let item = assert_next_matches!(stream, VectorDiff::PushBack { value } => value);
|
|
let event = item.as_event().unwrap();
|
|
assert_matches!(event.send_state(), Some(EventSendState::NotSentYet));
|
|
}
|
|
|
|
// Scenario 2: The local event has not been sent to the server successfully, it
|
|
// has failed. In this case, there is no event ID.
|
|
{
|
|
let some_io_error = Error::Io(io::Error::new(io::ErrorKind::Other, "this is a test"));
|
|
timeline
|
|
.inner
|
|
.update_event_send_state(
|
|
&txn_id,
|
|
EventSendState::SendingFailed { error: Arc::new(some_io_error) },
|
|
)
|
|
.await;
|
|
|
|
let item = assert_next_matches!(stream, VectorDiff::Set { value, index: 1 } => value);
|
|
let event = item.as_event().unwrap();
|
|
assert_matches!(event.send_state(), Some(EventSendState::SendingFailed { .. }));
|
|
}
|
|
|
|
// Scenario 3: The local event has been sent successfully to the server and an
|
|
// event ID has been received as part of the server's response.
|
|
let event_id = event_id!("$W6mZSLWMmfuQQ9jhZWeTxFIM");
|
|
let timestamp = {
|
|
timeline
|
|
.inner
|
|
.update_event_send_state(
|
|
&txn_id,
|
|
EventSendState::Sent { event_id: event_id.to_owned() },
|
|
)
|
|
.await;
|
|
|
|
let item = assert_next_matches!(stream, VectorDiff::Set { value, index: 1 } => value);
|
|
let event_item = item.as_event().unwrap();
|
|
assert_matches!(event_item.send_state(), Some(EventSendState::Sent { .. }));
|
|
|
|
event_item.timestamp()
|
|
};
|
|
|
|
// Now, a sync has been run against the server, and an event with the same ID
|
|
// comes in.
|
|
timeline
|
|
.handle_live_custom_event(json!({
|
|
"content": {
|
|
"body": "echo",
|
|
"msgtype": "m.text",
|
|
},
|
|
"sender": &*ALICE,
|
|
"event_id": event_id,
|
|
"origin_server_ts": timestamp,
|
|
"type": "m.room.message",
|
|
}))
|
|
.await;
|
|
|
|
// The local echo is replaced with the remote echo
|
|
let item = assert_next_matches!(stream, VectorDiff::Set { index: 1, value } => value);
|
|
assert!(!item.as_event().unwrap().is_local_echo());
|
|
}
|
|
|
|
#[async_test]
|
|
async fn remote_echo_new_position() {
|
|
let timeline = TestTimeline::new();
|
|
let mut stream = timeline.subscribe().await;
|
|
|
|
// Given a local event…
|
|
let txn_id = timeline
|
|
.handle_local_event(AnyMessageLikeEventContent::RoomMessage(
|
|
RoomMessageEventContent::text_plain("echo"),
|
|
))
|
|
.await;
|
|
|
|
let _day_divider = assert_next_matches!(stream, VectorDiff::PushBack { value } => value);
|
|
|
|
let item = assert_next_matches!(stream, VectorDiff::PushBack { value } => value);
|
|
let txn_id_from_event = item.as_event().unwrap();
|
|
assert_eq!(txn_id, txn_id_from_event.transaction_id().unwrap());
|
|
|
|
// … and another event that comes back before the remote echo
|
|
timeline.handle_live_message_event(&BOB, RoomMessageEventContent::text_plain("test")).await;
|
|
let _day_divider = assert_next_matches!(stream, VectorDiff::PushBack { value } => value);
|
|
let _bob_message = assert_next_matches!(stream, VectorDiff::PushBack { value } => value);
|
|
|
|
// When the remote echo comes in…
|
|
timeline
|
|
.handle_live_custom_event(json!({
|
|
"content": {
|
|
"body": "echo",
|
|
"msgtype": "m.text",
|
|
},
|
|
"sender": &*ALICE,
|
|
"event_id": "$eeG0HA0FAZ37wP8kXlNkxx3I",
|
|
"origin_server_ts": 6,
|
|
"type": "m.room.message",
|
|
"unsigned": {
|
|
"transaction_id": txn_id,
|
|
},
|
|
}))
|
|
.await;
|
|
|
|
// … the local echo should be removed
|
|
assert_next_matches!(stream, VectorDiff::Remove { index: 1 });
|
|
// … along with its day divider
|
|
assert_next_matches!(stream, VectorDiff::Remove { index: 0 });
|
|
|
|
// … and the remote echo added (no new day divider because both bob's and
|
|
// alice's message are from the same day according to server timestamps)
|
|
let item = assert_next_matches!(stream, VectorDiff::PushBack { value } => value);
|
|
assert!(!item.as_event().unwrap().is_local_echo());
|
|
}
|