| 1 | use matrix_sdk::{ |
| 2 | deserialized_responses::TimelineEvent as SdkTimelineEvent, |
| 3 | ruma::{events::AnySyncTimelineEvent, room_id}, |
| 4 | test_utils::mocks::MatrixMockServer, |
| 5 | }; |
| 6 | use matrix_sdk_test::{JoinedRoomBuilder, event_factory::EventFactory}; |
| 7 | use serde_json::json; |
| 8 | |
| 9 | use super::*; |
| 10 | |
| 11 | async fn joined(server: &MatrixMockServer, room_id: &ruma::RoomId) -> Client { |
| 12 | let client = server.client_builder().build().await; |
| 13 | client.event_cache().subscribe().unwrap(); |
| 14 | let own_user = client.user_id().unwrap().to_owned(); |
| 15 | let room_id = room_id.to_owned(); |
| 16 | server |
| 17 | .mock_sync() |
| 18 | .ok_and_run(&client, move |builder| { |
| 19 | let factory = EventFactory::new().room(&room_id); |
| 20 | builder.add_joined_room( |
| 21 | JoinedRoomBuilder::new(&room_id).add_state_event(factory.member(&own_user)), |
| 22 | ); |
| 23 | }) |
| 24 | .await; |
| 25 | client |
| 26 | } |
| 27 | |
| 28 | fn service(client: &Client) -> Service { |
| 29 | let media = Arc::new(MediaService::new(client.clone(), 1 << 20, 64)); |
| 30 | let (events, _) = EventSender::detached(16); |
| 31 | Service::new( |
| 32 | client.clone(), |
| 33 | media, |
| 34 | None, |
| 35 | events, |
| 36 | &EngineConfig::default(), |
| 37 | ) |
| 38 | .1 |
| 39 | } |
| 40 | |
| 41 | fn message(id: &str, ts: u64, body: &str) -> Value { |
| 42 | json!({ |
| 43 | "type": "m.room.message", |
| 44 | "event_id": id, |
| 45 | "sender": "@a:x", |
| 46 | "origin_server_ts": ts, |
| 47 | "content": { "msgtype": "m.text", "body": body }, |
| 48 | }) |
| 49 | } |
| 50 | |
| 51 | fn page(events: &[Value], end: Option<&str>) -> Result<Messages, Error> { |
| 52 | Ok(Messages { |
| 53 | start: "start".to_owned(), |
| 54 | end: end.map(ToOwned::to_owned), |
| 55 | chunk: events |
| 56 | .iter() |
| 57 | .map(|value| { |
| 58 | SdkTimelineEvent::from_plaintext( |
| 59 | Raw::<AnySyncTimelineEvent>::from_json_string(value.to_string()).unwrap(), |
| 60 | ) |
| 61 | }) |
| 62 | .collect(), |
| 63 | state: Vec::new(), |
| 64 | }) |
| 65 | } |
| 66 | |
| 67 | /// live events indexed this run used to count as overlap, so the first head page always |
| 68 | /// ended the pass and an offline gap past one page was never filled |
| 69 | #[tokio::test] |
| 70 | async fn a_head_pass_walks_past_what_this_run_indexed_live() { |
| 71 | let server = MatrixMockServer::new().await; |
| 72 | let room_id = room_id!("!head:x"); |
| 73 | let client = joined(&server, room_id).await; |
| 74 | let mut service = service(&client); |
| 75 | let room = room_id.to_owned(); |
| 76 | service.rooms.insert( |
| 77 | room.to_string(), |
| 78 | RoomProgress { |
| 79 | newest: Some(100), |
| 80 | started: true, |
| 81 | ..RoomProgress::default() |
| 82 | }, |
| 83 | ); |
| 84 | service.baselines.insert(room.to_string(), 100); |
| 85 | service.observe(&room, &message("$old", 100, "old")); |
| 86 | service.observe(&room, &message("$live", 1_000, "live")); |
| 87 | service.backfilled( |
| 88 | room.clone(), |
| 89 | true, |
| 90 | None, |
| 91 | page( |
| 92 | &[message("$live", 1_000, "live"), message("$gap", 500, "gap")], |
| 93 | Some("t1"), |
| 94 | ), |
| 95 | ); |
| 96 | assert_eq!( |
| 97 | service.head_tokens.get(&room).map(String::as_str), |
| 98 | Some("t1") |
| 99 | ); |
| 100 | assert_eq!(service.heads.front(), Some(&room)); |
| 101 | service.backfilled( |
| 102 | room.clone(), |
| 103 | true, |
| 104 | Some("t1".to_owned()), |
| 105 | page(&[message("$old", 100, "old")], Some("t2")), |
| 106 | ); |
| 107 | assert!(!service.head_tokens.contains_key(&room)); |
| 108 | assert!(service.index.get("$gap").is_some()); |
| 109 | } |
| 110 | |
| 111 | /// the generic channel is bounded, and a room whose only update was lost was never followed |
| 112 | #[tokio::test] |
| 113 | async fn a_lagged_generic_channel_follows_every_joined_room() { |
| 114 | let server = MatrixMockServer::new().await; |
| 115 | let room_id = room_id!("!lag:x"); |
| 116 | let client = joined(&server, room_id).await; |
| 117 | let mut service = service(&client); |
| 118 | assert!( |
| 119 | service |
| 120 | .generic(Err(broadcast::error::RecvError::Lagged(3))) |
| 121 | .await |
| 122 | ); |
| 123 | assert!(service.subscriptions.contains_key(room_id)); |
| 124 | } |
| 125 | |
| 126 | /// a closed stream left its entry behind, and the room was never followed again |
| 127 | #[tokio::test] |
| 128 | async fn a_closed_room_stream_can_be_followed_again() { |
| 129 | let server = MatrixMockServer::new().await; |
| 130 | let room_id = room_id!("!closed:x"); |
| 131 | let client = joined(&server, room_id).await; |
| 132 | let mut service = service(&client); |
| 133 | service.subscribe(room_id.to_owned()).await; |
| 134 | assert!(service.subscriptions.contains_key(room_id)); |
| 135 | service |
| 136 | .room_update(room_id.to_owned(), RoomUpdate::Closed) |
| 137 | .await; |
| 138 | assert!(!service.subscriptions.contains_key(room_id)); |
| 139 | assert!(service.generic(Ok(room_id.to_owned())).await); |
| 140 | assert!(service.subscriptions.contains_key(room_id)); |
| 141 | } |