atagen

macltok

public
Star0
Watch0
13da982fe7bb952b0fb1ae3efb8bd3ca4f39b204
3
ssh://brewery@pls.lobotomise.me:33/atagen/macltok.git

Blame: tests/mentions_late_decryption.rs

cbb715e11963atagen2026-10-24 23:24 UTC1
mod support;
cbb715e11963atagen2026-10-24 23:24 UTC2
cbb715e11963atagen2026-10-24 23:24 UTC3
use std::sync::{
cbb715e11963atagen2026-10-24 23:24 UTC4
    Arc,
cbb715e11963atagen2026-10-24 23:24 UTC5
    atomic::{AtomicUsize, Ordering},
cbb715e11963atagen2026-10-24 23:24 UTC6
};
cbb715e11963atagen2026-10-24 23:24 UTC7
use std::time::Duration;
cbb715e11963atagen2026-10-24 23:24 UTC8
cbb715e11963atagen2026-10-24 23:24 UTC9
use macltok::{Engine, EngineConfig, Event, EventStream, MentionsPage};
cbb715e11963atagen2026-10-24 23:24 UTC10
use matrix_sdk::{
cbb715e11963atagen2026-10-24 23:24 UTC11
    ruma::{RoomVersionId, device_id, event_id, room_id, user_id},
cbb715e11963atagen2026-10-24 23:24 UTC12
    test_utils::mocks::MatrixMockServer,
cbb715e11963atagen2026-10-24 23:24 UTC13
};
cbb715e11963atagen2026-10-24 23:24 UTC14
use matrix_sdk_test::{JoinedRoomBuilder, event_factory::EventFactory};
cbb715e11963atagen2026-10-24 23:24 UTC15
use serde_json::{Value, json};
cbb715e11963atagen2026-10-24 23:24 UTC16
use support::shut_down;
cbb715e11963atagen2026-10-24 23:24 UTC17
use wiremock::{
cbb715e11963atagen2026-10-24 23:24 UTC18
    Mock, Request, Respond, ResponseTemplate,
cbb715e11963atagen2026-10-24 23:24 UTC19
    matchers::{method, path},
cbb715e11963atagen2026-10-24 23:24 UTC20
};
cbb715e11963atagen2026-10-24 23:24 UTC21
cbb715e11963atagen2026-10-24 23:24 UTC22
const SLIDING_SYNC_PATH: &str = "/_matrix/client/unstable/org.matrix.simplified_msc3575/sync";
cbb715e11963atagen2026-10-24 23:24 UTC23
cbb715e11963atagen2026-10-24 23:24 UTC24
struct EncryptedSlidingFeed {
cbb715e11963atagen2026-10-24 23:24 UTC25
    room_id: String,
cbb715e11963atagen2026-10-24 23:24 UTC26
    event: Value,
cbb715e11963atagen2026-10-24 23:24 UTC27
    requests: Arc<AtomicUsize>,
cbb715e11963atagen2026-10-24 23:24 UTC28
}
cbb715e11963atagen2026-10-24 23:24 UTC29
cbb715e11963atagen2026-10-24 23:24 UTC30
impl Clone for EncryptedSlidingFeed {
cbb715e11963atagen2026-10-24 23:24 UTC31
    fn clone(&self) -> Self {
cbb715e11963atagen2026-10-24 23:24 UTC32
        Self {
cbb715e11963atagen2026-10-24 23:24 UTC33
            room_id: self.room_id.clone(),
cbb715e11963atagen2026-10-24 23:24 UTC34
            event: self.event.clone(),
cbb715e11963atagen2026-10-24 23:24 UTC35
            requests: self.requests.clone(),
cbb715e11963atagen2026-10-24 23:24 UTC36
        }
cbb715e11963atagen2026-10-24 23:24 UTC37
    }
cbb715e11963atagen2026-10-24 23:24 UTC38
}
cbb715e11963atagen2026-10-24 23:24 UTC39
cbb715e11963atagen2026-10-24 23:24 UTC40
impl EncryptedSlidingFeed {
cbb715e11963atagen2026-10-24 23:24 UTC41
    async fn mount(&self, server: &MatrixMockServer) {
cbb715e11963atagen2026-10-24 23:24 UTC42
        Mock::given(method("POST"))
cbb715e11963atagen2026-10-24 23:24 UTC43
            .and(path(SLIDING_SYNC_PATH))
cbb715e11963atagen2026-10-24 23:24 UTC44
            .respond_with(self.clone())
cbb715e11963atagen2026-10-24 23:24 UTC45
            .mount(server.server())
cbb715e11963atagen2026-10-24 23:24 UTC46
            .await;
cbb715e11963atagen2026-10-24 23:24 UTC47
    }
cbb715e11963atagen2026-10-24 23:24 UTC48
}
cbb715e11963atagen2026-10-24 23:24 UTC49
cbb715e11963atagen2026-10-24 23:24 UTC50
impl Respond for EncryptedSlidingFeed {
cbb715e11963atagen2026-10-24 23:24 UTC51
    fn respond(&self, _request: &Request) -> ResponseTemplate {
cbb715e11963atagen2026-10-24 23:24 UTC52
        let request = self.requests.fetch_add(1, Ordering::AcqRel);
cbb715e11963atagen2026-10-24 23:24 UTC53
        ResponseTemplate::new(200)
cbb715e11963atagen2026-10-24 23:24 UTC54
            .set_delay(Duration::from_millis(20))
cbb715e11963atagen2026-10-24 23:24 UTC55
            .set_body_json(json!({
cbb715e11963atagen2026-10-24 23:24 UTC56
                "txn_id": null,
cbb715e11963atagen2026-10-24 23:24 UTC57
                "pos": format!("late-decryption-{request}"),
cbb715e11963atagen2026-10-24 23:24 UTC58
                "lists": {},
cbb715e11963atagen2026-10-24 23:24 UTC59
                "rooms": {
cbb715e11963atagen2026-10-24 23:24 UTC60
                    (self.room_id.clone()): {
cbb715e11963atagen2026-10-24 23:24 UTC61
                        "initial": request == 0,
cbb715e11963atagen2026-10-24 23:24 UTC62
                        "required_state": [],
cbb715e11963atagen2026-10-24 23:24 UTC63
                        "timeline": [self.event.clone()],
cbb715e11963atagen2026-10-24 23:24 UTC64
                    }
cbb715e11963atagen2026-10-24 23:24 UTC65
                },
cbb715e11963atagen2026-10-24 23:24 UTC66
                "extensions": {},
cbb715e11963atagen2026-10-24 23:24 UTC67
            }))
cbb715e11963atagen2026-10-24 23:24 UTC68
    }
cbb715e11963atagen2026-10-24 23:24 UTC69
}
cbb715e11963atagen2026-10-24 23:24 UTC70
cbb715e11963atagen2026-10-24 23:24 UTC71
async fn mentions_page(
cbb715e11963atagen2026-10-24 23:24 UTC72
    events: &mut EventStream,
cbb715e11963atagen2026-10-24 23:24 UTC73
    room_id: &ruma::RoomId,
cbb715e11963atagen2026-10-24 23:24 UTC74
    predicate: impl Fn(&MentionsPage) -> bool,
cbb715e11963atagen2026-10-24 23:24 UTC75
) -> MentionsPage {
cbb715e11963atagen2026-10-24 23:24 UTC76
    tokio::time::timeout(Duration::from_secs(5), async {
cbb715e11963atagen2026-10-24 23:24 UTC77
        while let Some(event) = events.recv().await {
cbb715e11963atagen2026-10-24 23:24 UTC78
            match event {
cbb715e11963atagen2026-10-24 23:24 UTC79
                Event::TimelineReset { timeline_id, .. } if timeline_id.room_id() == room_id => {
cbb715e11963atagen2026-10-24 23:24 UTC80
                    panic!("the unopened room produced a timeline reset");
cbb715e11963atagen2026-10-24 23:24 UTC81
                }
cbb715e11963atagen2026-10-24 23:24 UTC82
                Event::MentionsChanged(update) if predicate(&update.snapshot) => {
cbb715e11963atagen2026-10-24 23:24 UTC83
                    return update.snapshot;
cbb715e11963atagen2026-10-24 23:24 UTC84
                }
cbb715e11963atagen2026-10-24 23:24 UTC85
                _ => {}
cbb715e11963atagen2026-10-24 23:24 UTC86
            }
cbb715e11963atagen2026-10-24 23:24 UTC87
        }
cbb715e11963atagen2026-10-24 23:24 UTC88
        panic!("event stream closed before the expected mentions update");
cbb715e11963atagen2026-10-24 23:24 UTC89
    })
cbb715e11963atagen2026-10-24 23:24 UTC90
    .await
cbb715e11963atagen2026-10-24 23:24 UTC91
    .expect("expected mentions update did not arrive")
cbb715e11963atagen2026-10-24 23:24 UTC92
}
cbb715e11963atagen2026-10-24 23:24 UTC93
cbb715e11963atagen2026-10-24 23:24 UTC94
async fn wait_for_requests(feed: &EncryptedSlidingFeed, minimum: usize) {
cbb715e11963atagen2026-10-24 23:24 UTC95
    tokio::time::timeout(Duration::from_secs(5), async {
cbb715e11963atagen2026-10-24 23:24 UTC96
        while feed.requests.load(Ordering::Acquire) < minimum {
cbb715e11963atagen2026-10-24 23:24 UTC97
            tokio::task::yield_now().await;
cbb715e11963atagen2026-10-24 23:24 UTC98
        }
cbb715e11963atagen2026-10-24 23:24 UTC99
    })
cbb715e11963atagen2026-10-24 23:24 UTC100
    .await
cbb715e11963atagen2026-10-24 23:24 UTC101
    .expect("the sliding sync feed did not receive another request");
cbb715e11963atagen2026-10-24 23:24 UTC102
}
cbb715e11963atagen2026-10-24 23:24 UTC103
cbb715e11963atagen2026-10-24 23:24 UTC104
#[tokio::test]
cbb715e11963atagen2026-10-24 23:24 UTC105
async fn late_room_key_updates_the_production_mentions_inbox_once() {
cbb715e11963atagen2026-10-24 23:24 UTC106
    let server = MatrixMockServer::new().await;
cbb715e11963atagen2026-10-24 23:24 UTC107
    server.mock_crypto_endpoints_preset().await;
cbb715e11963atagen2026-10-24 23:24 UTC108
    let alice_id = user_id!("@alice:localhost");
cbb715e11963atagen2026-10-24 23:24 UTC109
    let bob_id = user_id!("@bob:localhost");
cbb715e11963atagen2026-10-24 23:24 UTC110
    let room_id = room_id!("!mentions-late-decryption:localhost");
cbb715e11963atagen2026-10-24 23:24 UTC111
    let event_id = event_id!("$late-production-mention");
cbb715e11963atagen2026-10-24 23:24 UTC112
    let alice = server
cbb715e11963atagen2026-10-24 23:24 UTC113
        .client_builder_for_crypto_end_to_end(alice_id, device_id!("ALICEDEVICE"))
cbb715e11963atagen2026-10-24 23:24 UTC114
        .build()
cbb715e11963atagen2026-10-24 23:24 UTC115
        .await;
cbb715e11963atagen2026-10-24 23:24 UTC116
    let bob = server
cbb715e11963atagen2026-10-24 23:24 UTC117
        .client_builder_for_crypto_end_to_end(bob_id, device_id!("BOBDEVICE"))
cbb715e11963atagen2026-10-24 23:24 UTC118
        .build()
cbb715e11963atagen2026-10-24 23:24 UTC119
        .await;
cbb715e11963atagen2026-10-24 23:24 UTC120
    bob.event_cache().subscribe().unwrap();
cbb715e11963atagen2026-10-24 23:24 UTC121
    server.exchange_e2ee_identities(&alice, &bob).await;
cbb715e11963atagen2026-10-24 23:24 UTC122
cbb715e11963atagen2026-10-24 23:24 UTC123
    let factory = EventFactory::new().room(room_id).sender(alice_id);
cbb715e11963atagen2026-10-24 23:24 UTC124
    let joined = JoinedRoomBuilder::new(room_id)
cbb715e11963atagen2026-10-24 23:24 UTC125
        .add_state_event(factory.create(alice_id, RoomVersionId::V1))
cbb715e11963atagen2026-10-24 23:24 UTC126
        .add_state_event(factory.room_encryption())
cbb715e11963atagen2026-10-24 23:24 UTC127
        .add_state_event(factory.member(alice_id))
cbb715e11963atagen2026-10-24 23:24 UTC128
        .add_state_event(factory.member(bob_id));
cbb715e11963atagen2026-10-24 23:24 UTC129
    server.sync_room(&alice, joined.clone()).await;
cbb715e11963atagen2026-10-24 23:24 UTC130
    server.sync_room(&bob, joined).await;
cbb715e11963atagen2026-10-24 23:24 UTC131
cbb715e11963atagen2026-10-24 23:24 UTC132
    let room = alice.get_room(room_id).unwrap();
cbb715e11963atagen2026-10-24 23:24 UTC133
    let (encrypted_event, send) = server.mock_room_send().ok_with_capture(event_id, alice_id);
cbb715e11963atagen2026-10-24 23:24 UTC134
    let _send = send.mock_once().mount_as_scoped().await;
cbb715e11963atagen2026-10-24 23:24 UTC135
    let (_keys, room_key) = server.mock_capture_put_to_device(alice_id).await;
cbb715e11963atagen2026-10-24 23:24 UTC136
    server
cbb715e11963atagen2026-10-24 23:24 UTC137
        .mock_get_members()
cbb715e11963atagen2026-10-24 23:24 UTC138
        .ok(vec![
cbb715e11963atagen2026-10-24 23:24 UTC139
            factory.member(alice_id).into_raw(),
cbb715e11963atagen2026-10-24 23:24 UTC140
            factory.member(bob_id).into_raw(),
cbb715e11963atagen2026-10-24 23:24 UTC141
        ])
cbb715e11963atagen2026-10-24 23:24 UTC142
        .mock_once()
cbb715e11963atagen2026-10-24 23:24 UTC143
        .mount()
cbb715e11963atagen2026-10-24 23:24 UTC144
        .await;
cbb715e11963atagen2026-10-24 23:24 UTC145
    room.send_raw(
cbb715e11963atagen2026-10-24 23:24 UTC146
        "m.room.message",
cbb715e11963atagen2026-10-24 23:24 UTC147
        json!({
cbb715e11963atagen2026-10-24 23:24 UTC148
            "msgtype": "m.text",
cbb715e11963atagen2026-10-24 23:24 UTC149
            "body": "a private mention",
cbb715e11963atagen2026-10-24 23:24 UTC150
            "m.mentions": { "user_ids": [bob_id] }
cbb715e11963atagen2026-10-24 23:24 UTC151
        }),
cbb715e11963atagen2026-10-24 23:24 UTC152
    )
cbb715e11963atagen2026-10-24 23:24 UTC153
    .await
cbb715e11963atagen2026-10-24 23:24 UTC154
    .unwrap();
cbb715e11963atagen2026-10-24 23:24 UTC155
    let encrypted_event = encrypted_event.await.unwrap();
cbb715e11963atagen2026-10-24 23:24 UTC156
    let room_key = room_key.await;
cbb715e11963atagen2026-10-24 23:24 UTC157
    let encrypted_event: Value = serde_json::from_str(encrypted_event.json().get()).unwrap();
cbb715e11963atagen2026-10-24 23:24 UTC158
cbb715e11963atagen2026-10-24 23:24 UTC159
    let feed = EncryptedSlidingFeed {
cbb715e11963atagen2026-10-24 23:24 UTC160
        room_id: room_id.to_string(),
cbb715e11963atagen2026-10-24 23:24 UTC161
        event: encrypted_event,
cbb715e11963atagen2026-10-24 23:24 UTC162
        requests: Arc::new(AtomicUsize::new(0)),
cbb715e11963atagen2026-10-24 23:24 UTC163
    };
cbb715e11963atagen2026-10-24 23:24 UTC164
    feed.mount(&server).await;
cbb715e11963atagen2026-10-24 23:24 UTC165
cbb715e11963atagen2026-10-24 23:24 UTC166
    let (engine, handle, mut events) = Engine::from_client(
cbb715e11963atagen2026-10-24 23:24 UTC167
        bob.clone(),
cbb715e11963atagen2026-10-24 23:24 UTC168
        EngineConfig::default().with_initial_history_limit(0),
cbb715e11963atagen2026-10-24 23:24 UTC169
    )
cbb715e11963atagen2026-10-24 23:24 UTC170
    .await
cbb715e11963atagen2026-10-24 23:24 UTC171
    .unwrap();
cbb715e11963atagen2026-10-24 23:24 UTC172
    let running = tokio::spawn(engine.run());
cbb715e11963atagen2026-10-24 23:24 UTC173
cbb715e11963atagen2026-10-24 23:24 UTC174
    let unresolved = mentions_page(&mut events, room_id, |page| {
cbb715e11963atagen2026-10-24 23:24 UTC175
        page.coverage.unresolved_candidates == 1
cbb715e11963atagen2026-10-24 23:24 UTC176
    })
cbb715e11963atagen2026-10-24 23:24 UTC177
    .await;
cbb715e11963atagen2026-10-24 23:24 UTC178
    assert!(unresolved.entries.is_empty());
cbb715e11963atagen2026-10-24 23:24 UTC179
cbb715e11963atagen2026-10-24 23:24 UTC180
    server
cbb715e11963atagen2026-10-24 23:24 UTC181
        .mock_sync()
cbb715e11963atagen2026-10-24 23:24 UTC182
        .ok_and_run(&bob, |sync| {
cbb715e11963atagen2026-10-24 23:24 UTC183
            sync.add_to_device_event(room_key.deserialize_as().unwrap());
cbb715e11963atagen2026-10-24 23:24 UTC184
        })
cbb715e11963atagen2026-10-24 23:24 UTC185
        .await;
cbb715e11963atagen2026-10-24 23:24 UTC186
cbb715e11963atagen2026-10-24 23:24 UTC187
    let decrypted = mentions_page(&mut events, room_id, |page| {
cbb715e11963atagen2026-10-24 23:24 UTC188
        page.entries.len() == 1 && page.entries[0].key.event_id == event_id
cbb715e11963atagen2026-10-24 23:24 UTC189
    })
cbb715e11963atagen2026-10-24 23:24 UTC190
    .await;
cbb715e11963atagen2026-10-24 23:24 UTC191
    assert_eq!(decrypted.entries.len(), 1);
cbb715e11963atagen2026-10-24 23:24 UTC192
    assert_eq!(decrypted.entries[0].key.event_id, event_id);
cbb715e11963atagen2026-10-24 23:24 UTC193
    assert_eq!(decrypted.coverage.unresolved_candidates, 0);
cbb715e11963atagen2026-10-24 23:24 UTC194
cbb715e11963atagen2026-10-24 23:24 UTC195
    let replay_start = feed.requests.load(Ordering::Acquire);
cbb715e11963atagen2026-10-24 23:24 UTC196
    wait_for_requests(&feed, replay_start + 3).await;
cbb715e11963atagen2026-10-24 23:24 UTC197
    let replayed = handle.mentions_page(None, 50).await.unwrap();
cbb715e11963atagen2026-10-24 23:24 UTC198
    assert_eq!(replayed.entries.len(), 1);
cbb715e11963atagen2026-10-24 23:24 UTC199
    assert_eq!(replayed.entries[0].key.event_id, event_id);
cbb715e11963atagen2026-10-24 23:24 UTC200
    while let Ok(event) = events.try_recv() {
cbb715e11963atagen2026-10-24 23:24 UTC201
        if let Event::TimelineReset { timeline_id, .. } = event {
cbb715e11963atagen2026-10-24 23:24 UTC202
            assert_ne!(timeline_id.room_id(), room_id);
cbb715e11963atagen2026-10-24 23:24 UTC203
        }
cbb715e11963atagen2026-10-24 23:24 UTC204
    }
cbb715e11963atagen2026-10-24 23:24 UTC205
cbb715e11963atagen2026-10-24 23:24 UTC206
    shut_down(&handle, &mut events, running).await;
cbb715e11963atagen2026-10-24 23:24 UTC207
}