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