atagen

macltok

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

Blame: tests/mirror.rs

a319bae745d8atagen2026-09-26 09:59 UTC1
#![cfg(feature = "mirror")]
a319bae745d8atagen2026-09-26 09:59 UTC2
a319bae745d8atagen2026-09-26 09:59 UTC3
use std::time::Duration;
a319bae745d8atagen2026-09-26 09:59 UTC4
a319bae745d8atagen2026-09-26 09:59 UTC5
use futures_util::StreamExt;
a319bae745d8atagen2026-09-26 09:59 UTC6
use macltok::{
a319bae745d8atagen2026-09-26 09:59 UTC7
    ClientHandle, Command, Engine, EngineConfig, Event, Mirror, MirrorHandle, StateChange,
a319bae745d8atagen2026-09-26 09:59 UTC8
    Subscription, TimelineChange, TimelineCommand, TimelineDiff, TimelineId, TimelineItem, Update,
a319bae745d8atagen2026-09-26 09:59 UTC9
    UploadOutcome,
a319bae745d8atagen2026-09-26 09:59 UTC10
};
a319bae745d8atagen2026-09-26 09:59 UTC11
use matrix_sdk::{
a319bae745d8atagen2026-09-26 09:59 UTC12
    ruma::{OwnedRoomId, TransactionId, api::client::sync::sync_events::v5, event_id, room_id},
a319bae745d8atagen2026-09-26 09:59 UTC13
    test_utils::mocks::MatrixMockServer,
a319bae745d8atagen2026-09-26 09:59 UTC14
};
a319bae745d8atagen2026-09-26 09:59 UTC15
use tokio::sync::mpsc;
a319bae745d8atagen2026-09-26 09:59 UTC16
a319bae745d8atagen2026-09-26 09:59 UTC17
struct Harness {
a319bae745d8atagen2026-09-26 09:59 UTC18
    handle: ClientHandle,
a319bae745d8atagen2026-09-26 09:59 UTC19
    mirror: MirrorHandle,
a319bae745d8atagen2026-09-26 09:59 UTC20
    inject: mpsc::UnboundedSender<Event>,
a319bae745d8atagen2026-09-26 09:59 UTC21
    room_id: OwnedRoomId,
a319bae745d8atagen2026-09-26 09:59 UTC22
    _server: MatrixMockServer,
a319bae745d8atagen2026-09-26 09:59 UTC23
}
a319bae745d8atagen2026-09-26 09:59 UTC24
a319bae745d8atagen2026-09-26 09:59 UTC25
/// the mirror reads the real engine's stream with synthetic events mixed in, so a test can
a319bae745d8atagen2026-09-26 09:59 UTC26
/// hand it a diff the engine never would
a319bae745d8atagen2026-09-26 09:59 UTC27
async fn start(config: EngineConfig, capacity: usize) -> Harness {
a319bae745d8atagen2026-09-26 09:59 UTC28
    let server = MatrixMockServer::new().await;
a319bae745d8atagen2026-09-26 09:59 UTC29
    server
a319bae745d8atagen2026-09-26 09:59 UTC30
        .mock_sliding_sync()
a319bae745d8atagen2026-09-26 09:59 UTC31
        .ok(v5::Response::new("1".to_owned()))
a319bae745d8atagen2026-09-26 09:59 UTC32
        .mount()
a319bae745d8atagen2026-09-26 09:59 UTC33
        .await;
a319bae745d8atagen2026-09-26 09:59 UTC34
    let client = server.client_builder().build().await;
a319bae745d8atagen2026-09-26 09:59 UTC35
    let room_id = room_id!("!mirror:localhost");
a319bae745d8atagen2026-09-26 09:59 UTC36
    server.mock_room_state_encryption().plain().mount().await;
a319bae745d8atagen2026-09-26 09:59 UTC37
    server.sync_joined_room(&client, room_id).await;
a319bae745d8atagen2026-09-26 09:59 UTC38
    let (engine, handle, events) =
a319bae745d8atagen2026-09-26 09:59 UTC39
        Engine::from_client(client, config.with_initial_history_limit(0))
a319bae745d8atagen2026-09-26 09:59 UTC40
            .await
a319bae745d8atagen2026-09-26 09:59 UTC41
            .unwrap();
a319bae745d8atagen2026-09-26 09:59 UTC42
    tokio::spawn(engine.run());
a319bae745d8atagen2026-09-26 09:59 UTC43
    let (inject, mut injected) = mpsc::unbounded_channel();
a319bae745d8atagen2026-09-26 09:59 UTC44
    let injected = futures_util::stream::poll_fn(move |context| injected.poll_recv(context));
a319bae745d8atagen2026-09-26 09:59 UTC45
    let (mirror, mirror_handle) = Mirror::with_capacity(
a319bae745d8atagen2026-09-26 09:59 UTC46
        handle.clone(),
a319bae745d8atagen2026-09-26 09:59 UTC47
        futures_util::stream::select(events, injected),
a319bae745d8atagen2026-09-26 09:59 UTC48
        capacity,
a319bae745d8atagen2026-09-26 09:59 UTC49
    );
a319bae745d8atagen2026-09-26 09:59 UTC50
    tokio::spawn(mirror.run());
a319bae745d8atagen2026-09-26 09:59 UTC51
    Harness {
a319bae745d8atagen2026-09-26 09:59 UTC52
        handle,
a319bae745d8atagen2026-09-26 09:59 UTC53
        mirror: mirror_handle,
a319bae745d8atagen2026-09-26 09:59 UTC54
        inject,
a319bae745d8atagen2026-09-26 09:59 UTC55
        room_id: room_id.to_owned(),
a319bae745d8atagen2026-09-26 09:59 UTC56
        _server: server,
a319bae745d8atagen2026-09-26 09:59 UTC57
    }
a319bae745d8atagen2026-09-26 09:59 UTC58
}
a319bae745d8atagen2026-09-26 09:59 UTC59
a319bae745d8atagen2026-09-26 09:59 UTC60
async fn next_matching(
a319bae745d8atagen2026-09-26 09:59 UTC61
    subscription: &mut Subscription,
a319bae745d8atagen2026-09-26 09:59 UTC62
    predicate: impl Fn(&StateChange) -> bool,
a319bae745d8atagen2026-09-26 09:59 UTC63
) -> Update {
a319bae745d8atagen2026-09-26 09:59 UTC64
    tokio::time::timeout(Duration::from_secs(5), async {
a319bae745d8atagen2026-09-26 09:59 UTC65
        loop {
a319bae745d8atagen2026-09-26 09:59 UTC66
            let update = subscription.next().await.expect("the mirror stopped");
a319bae745d8atagen2026-09-26 09:59 UTC67
            if predicate(&update.change) {
a319bae745d8atagen2026-09-26 09:59 UTC68
                return update;
a319bae745d8atagen2026-09-26 09:59 UTC69
            }
a319bae745d8atagen2026-09-26 09:59 UTC70
        }
a319bae745d8atagen2026-09-26 09:59 UTC71
    })
a319bae745d8atagen2026-09-26 09:59 UTC72
    .await
a319bae745d8atagen2026-09-26 09:59 UTC73
    .expect("expected update did not arrive")
a319bae745d8atagen2026-09-26 09:59 UTC74
}
a319bae745d8atagen2026-09-26 09:59 UTC75
a319bae745d8atagen2026-09-26 09:59 UTC76
fn timeline_change(
a319bae745d8atagen2026-09-26 09:59 UTC77
    change: &StateChange,
a319bae745d8atagen2026-09-26 09:59 UTC78
    id: &TimelineId,
a319bae745d8atagen2026-09-26 09:59 UTC79
    predicate: impl Fn(&TimelineChange) -> bool,
a319bae745d8atagen2026-09-26 09:59 UTC80
) -> bool {
a319bae745d8atagen2026-09-26 09:59 UTC81
    matches!(change, StateChange::Timeline { timeline_id, change } if timeline_id == id && predicate(change))
a319bae745d8atagen2026-09-26 09:59 UTC82
}
a319bae745d8atagen2026-09-26 09:59 UTC83
a319bae745d8atagen2026-09-26 09:59 UTC84
fn item(timestamp_ms: u64) -> TimelineItem {
a319bae745d8atagen2026-09-26 09:59 UTC85
    TimelineItem::DayDivider { timestamp_ms }
a319bae745d8atagen2026-09-26 09:59 UTC86
}
a319bae745d8atagen2026-09-26 09:59 UTC87
a319bae745d8atagen2026-09-26 09:59 UTC88
fn reset(timeline_id: &TimelineId, items: Vec<TimelineItem>) -> Event {
a319bae745d8atagen2026-09-26 09:59 UTC89
    Event::TimelineReset {
a319bae745d8atagen2026-09-26 09:59 UTC90
        timeline_id: timeline_id.clone(),
a319bae745d8atagen2026-09-26 09:59 UTC91
        items: items.into(),
a319bae745d8atagen2026-09-26 09:59 UTC92
    }
a319bae745d8atagen2026-09-26 09:59 UTC93
}
a319bae745d8atagen2026-09-26 09:59 UTC94
a319bae745d8atagen2026-09-26 09:59 UTC95
/// the room never opens, so every resync is refused and the divergence holds still to be looked at
a319bae745d8atagen2026-09-26 09:59 UTC96
#[tokio::test]
a319bae745d8atagen2026-09-26 09:59 UTC97
async fn diverged_batch_keeps_last_good_items_until_a_reset() {
a319bae745d8atagen2026-09-26 09:59 UTC98
    let harness = start(EngineConfig::default(), 256).await;
a319bae745d8atagen2026-09-26 09:59 UTC99
    let timeline_id = TimelineId::from(harness.room_id.clone());
a319bae745d8atagen2026-09-26 09:59 UTC100
    let mut updates = harness.mirror.subscribe();
a319bae745d8atagen2026-09-26 09:59 UTC101
    harness
a319bae745d8atagen2026-09-26 09:59 UTC102
        .inject
a319bae745d8atagen2026-09-26 09:59 UTC103
        .send(reset(&timeline_id, vec![item(1), item(2)]))
a319bae745d8atagen2026-09-26 09:59 UTC104
        .unwrap();
a319bae745d8atagen2026-09-26 09:59 UTC105
    for diffs in [
a319bae745d8atagen2026-09-26 09:59 UTC106
        vec![
a319bae745d8atagen2026-09-26 09:59 UTC107
            TimelineDiff::PushBack { value: item(3) },
a319bae745d8atagen2026-09-26 09:59 UTC108
            TimelineDiff::Remove { index: 7 },
a319bae745d8atagen2026-09-26 09:59 UTC109
        ],
a319bae745d8atagen2026-09-26 09:59 UTC110
        vec![TimelineDiff::PushBack { value: item(4) }],
a319bae745d8atagen2026-09-26 09:59 UTC111
    ] {
a319bae745d8atagen2026-09-26 09:59 UTC112
        harness
a319bae745d8atagen2026-09-26 09:59 UTC113
            .inject
a319bae745d8atagen2026-09-26 09:59 UTC114
            .send(Event::TimelineChanged {
a319bae745d8atagen2026-09-26 09:59 UTC115
                timeline_id: timeline_id.clone(),
a319bae745d8atagen2026-09-26 09:59 UTC116
                diffs,
a319bae745d8atagen2026-09-26 09:59 UTC117
            })
a319bae745d8atagen2026-09-26 09:59 UTC118
            .unwrap();
a319bae745d8atagen2026-09-26 09:59 UTC119
    }
a319bae745d8atagen2026-09-26 09:59 UTC120
    next_matching(&mut updates, |change| {
a319bae745d8atagen2026-09-26 09:59 UTC121
        timeline_change(change, &timeline_id, |change| {
a319bae745d8atagen2026-09-26 09:59 UTC122
            matches!(change, TimelineChange::Diverged)
a319bae745d8atagen2026-09-26 09:59 UTC123
        })
a319bae745d8atagen2026-09-26 09:59 UTC124
    })
a319bae745d8atagen2026-09-26 09:59 UTC125
    .await;
a319bae745d8atagen2026-09-26 09:59 UTC126
    let snapshot = harness.mirror.snapshot();
a319bae745d8atagen2026-09-26 09:59 UTC127
    let timeline = &snapshot.timelines[&timeline_id];
a319bae745d8atagen2026-09-26 09:59 UTC128
    assert!(timeline.diverged);
a319bae745d8atagen2026-09-26 09:59 UTC129
    assert_eq!(timeline.items, [item(1), item(2)].into_iter().collect());
a319bae745d8atagen2026-09-26 09:59 UTC130
a319bae745d8atagen2026-09-26 09:59 UTC131
    harness
a319bae745d8atagen2026-09-26 09:59 UTC132
        .inject
a319bae745d8atagen2026-09-26 09:59 UTC133
        .send(reset(&timeline_id, vec![item(5)]))
a319bae745d8atagen2026-09-26 09:59 UTC134
        .unwrap();
a319bae745d8atagen2026-09-26 09:59 UTC135
    let update = next_matching(&mut updates, |change| {
a319bae745d8atagen2026-09-26 09:59 UTC136
        timeline_change(change, &timeline_id, |_| true)
a319bae745d8atagen2026-09-26 09:59 UTC137
    })
a319bae745d8atagen2026-09-26 09:59 UTC138
    .await;
a319bae745d8atagen2026-09-26 09:59 UTC139
    assert!(
a319bae745d8atagen2026-09-26 09:59 UTC140
        timeline_change(&update.change, &timeline_id, |change| {
a319bae745d8atagen2026-09-26 09:59 UTC141
            matches!(change, TimelineChange::Reset { .. })
a319bae745d8atagen2026-09-26 09:59 UTC142
        }),
a319bae745d8atagen2026-09-26 09:59 UTC143
        "a diverged timeline took {update:?}"
a319bae745d8atagen2026-09-26 09:59 UTC144
    );
a319bae745d8atagen2026-09-26 09:59 UTC145
    let snapshot = harness.mirror.snapshot();
a319bae745d8atagen2026-09-26 09:59 UTC146
    let timeline = &snapshot.timelines[&timeline_id];
a319bae745d8atagen2026-09-26 09:59 UTC147
    assert!(!timeline.diverged);
a319bae745d8atagen2026-09-26 09:59 UTC148
    assert_eq!(timeline.items, [item(5)].into_iter().collect());
a319bae745d8atagen2026-09-26 09:59 UTC149
}
a319bae745d8atagen2026-09-26 09:59 UTC150
a319bae745d8atagen2026-09-26 09:59 UTC151
#[tokio::test]
a319bae745d8atagen2026-09-26 09:59 UTC152
async fn room_and_thread_timelines_stay_apart() {
a319bae745d8atagen2026-09-26 09:59 UTC153
    let harness = start(EngineConfig::default(), 256).await;
a319bae745d8atagen2026-09-26 09:59 UTC154
    let room = TimelineId::from(harness.room_id.clone());
a319bae745d8atagen2026-09-26 09:59 UTC155
    let thread = TimelineId::thread(
a319bae745d8atagen2026-09-26 09:59 UTC156
        harness.room_id.clone(),
a319bae745d8atagen2026-09-26 09:59 UTC157
        event_id!("$root:localhost").to_owned(),
a319bae745d8atagen2026-09-26 09:59 UTC158
    );
a319bae745d8atagen2026-09-26 09:59 UTC159
    let mut updates = harness.mirror.subscribe();
a319bae745d8atagen2026-09-26 09:59 UTC160
    harness.inject.send(reset(&room, vec![item(1)])).unwrap();
a319bae745d8atagen2026-09-26 09:59 UTC161
    harness.inject.send(reset(&thread, vec![item(2)])).unwrap();
a319bae745d8atagen2026-09-26 09:59 UTC162
    harness
a319bae745d8atagen2026-09-26 09:59 UTC163
        .inject
a319bae745d8atagen2026-09-26 09:59 UTC164
        .send(Event::TimelineChanged {
a319bae745d8atagen2026-09-26 09:59 UTC165
            timeline_id: thread.clone(),
a319bae745d8atagen2026-09-26 09:59 UTC166
            diffs: vec![TimelineDiff::PushBack { value: item(3) }],
a319bae745d8atagen2026-09-26 09:59 UTC167
        })
a319bae745d8atagen2026-09-26 09:59 UTC168
        .unwrap();
a319bae745d8atagen2026-09-26 09:59 UTC169
    next_matching(&mut updates, |change| {
a319bae745d8atagen2026-09-26 09:59 UTC170
        timeline_change(change, &thread, |change| {
a319bae745d8atagen2026-09-26 09:59 UTC171
            matches!(change, TimelineChange::Changed { .. })
a319bae745d8atagen2026-09-26 09:59 UTC172
        })
a319bae745d8atagen2026-09-26 09:59 UTC173
    })
a319bae745d8atagen2026-09-26 09:59 UTC174
    .await;
a319bae745d8atagen2026-09-26 09:59 UTC175
    let snapshot = harness.mirror.snapshot();
a319bae745d8atagen2026-09-26 09:59 UTC176
    assert_eq!(
a319bae745d8atagen2026-09-26 09:59 UTC177
        snapshot.timelines[&room].items,
a319bae745d8atagen2026-09-26 09:59 UTC178
        [item(1)].into_iter().collect()
a319bae745d8atagen2026-09-26 09:59 UTC179
    );
a319bae745d8atagen2026-09-26 09:59 UTC180
    assert_eq!(
a319bae745d8atagen2026-09-26 09:59 UTC181
        snapshot.timelines[&thread].items,
a319bae745d8atagen2026-09-26 09:59 UTC182
        [item(2), item(3)].into_iter().collect()
a319bae745d8atagen2026-09-26 09:59 UTC183
    );
a319bae745d8atagen2026-09-26 09:59 UTC184
}
a319bae745d8atagen2026-09-26 09:59 UTC185
a319bae745d8atagen2026-09-26 09:59 UTC186
#[tokio::test]
a319bae745d8atagen2026-09-26 09:59 UTC187
async fn late_subscriber_gets_state_then_every_update_after_it() {
a319bae745d8atagen2026-09-26 09:59 UTC188
    let harness = start(EngineConfig::default(), 1024).await;
a319bae745d8atagen2026-09-26 09:59 UTC189
    let transaction_id = TransactionId::new();
a319bae745d8atagen2026-09-26 09:59 UTC190
    let progress = |uploaded_bytes| Event::MediaUploadProgress {
a319bae745d8atagen2026-09-26 09:59 UTC191
        room_id: harness.room_id.clone(),
a319bae745d8atagen2026-09-26 09:59 UTC192
        transaction_id: transaction_id.clone(),
a319bae745d8atagen2026-09-26 09:59 UTC193
        uploaded_bytes,
a319bae745d8atagen2026-09-26 09:59 UTC194
        total_bytes: 100,
a319bae745d8atagen2026-09-26 09:59 UTC195
        complete: false,
a319bae745d8atagen2026-09-26 09:59 UTC196
    };
a319bae745d8atagen2026-09-26 09:59 UTC197
    for uploaded_bytes in 1..=50 {
a319bae745d8atagen2026-09-26 09:59 UTC198
        harness.inject.send(progress(uploaded_bytes)).unwrap();
a319bae745d8atagen2026-09-26 09:59 UTC199
    }
a319bae745d8atagen2026-09-26 09:59 UTC200
    let mut updates = harness.mirror.subscribe();
a319bae745d8atagen2026-09-26 09:59 UTC201
    for uploaded_bytes in 51..=100 {
a319bae745d8atagen2026-09-26 09:59 UTC202
        harness.inject.send(progress(uploaded_bytes)).unwrap();
a319bae745d8atagen2026-09-26 09:59 UTC203
    }
a319bae745d8atagen2026-09-26 09:59 UTC204
a319bae745d8atagen2026-09-26 09:59 UTC205
    let first = updates.next().await.unwrap();
a319bae745d8atagen2026-09-26 09:59 UTC206
    let StateChange::State(state) = &first.change else {
a319bae745d8atagen2026-09-26 09:59 UTC207
        panic!("a subscription opened with {first:?}");
a319bae745d8atagen2026-09-26 09:59 UTC208
    };
a319bae745d8atagen2026-09-26 09:59 UTC209
    let mut seen = state
a319bae745d8atagen2026-09-26 09:59 UTC210
        .uploads
a319bae745d8atagen2026-09-26 09:59 UTC211
        .get(&transaction_id)
a319bae745d8atagen2026-09-26 09:59 UTC212
        .map_or(0, |upload| upload.uploaded_bytes);
a319bae745d8atagen2026-09-26 09:59 UTC213
    let mut sequence = first.sequence;
a319bae745d8atagen2026-09-26 09:59 UTC214
    while seen < 100 {
a319bae745d8atagen2026-09-26 09:59 UTC215
        let update = next_matching(&mut updates, |_| true).await;
a319bae745d8atagen2026-09-26 09:59 UTC216
        assert_eq!(update.sequence, sequence + 1);
a319bae745d8atagen2026-09-26 09:59 UTC217
        sequence = update.sequence;
a319bae745d8atagen2026-09-26 09:59 UTC218
        if let StateChange::Upload { upload, .. } = update.change {
a319bae745d8atagen2026-09-26 09:59 UTC219
            assert_eq!(upload.uploaded_bytes, seen + 1);
a319bae745d8atagen2026-09-26 09:59 UTC220
            seen += 1;
a319bae745d8atagen2026-09-26 09:59 UTC221
        }
a319bae745d8atagen2026-09-26 09:59 UTC222
    }
a319bae745d8atagen2026-09-26 09:59 UTC223
}
a319bae745d8atagen2026-09-26 09:59 UTC224
a319bae745d8atagen2026-09-26 09:59 UTC225
#[tokio::test]
a319bae745d8atagen2026-09-26 09:59 UTC226
async fn lagging_subscriber_starts_over_from_a_fresh_state() {
a319bae745d8atagen2026-09-26 09:59 UTC227
    let harness = start(EngineConfig::default(), 4).await;
a319bae745d8atagen2026-09-26 09:59 UTC228
    let transaction_id = TransactionId::new();
a319bae745d8atagen2026-09-26 09:59 UTC229
    let mut updates = harness.mirror.subscribe();
a319bae745d8atagen2026-09-26 09:59 UTC230
    assert!(matches!(
a319bae745d8atagen2026-09-26 09:59 UTC231
        updates.next().await.unwrap().change,
a319bae745d8atagen2026-09-26 09:59 UTC232
        StateChange::State(_)
a319bae745d8atagen2026-09-26 09:59 UTC233
    ));
a319bae745d8atagen2026-09-26 09:59 UTC234
    for uploaded_bytes in 1..=20 {
a319bae745d8atagen2026-09-26 09:59 UTC235
        harness
a319bae745d8atagen2026-09-26 09:59 UTC236
            .inject
a319bae745d8atagen2026-09-26 09:59 UTC237
            .send(Event::MediaUploadProgress {
a319bae745d8atagen2026-09-26 09:59 UTC238
                room_id: harness.room_id.clone(),
a319bae745d8atagen2026-09-26 09:59 UTC239
                transaction_id: transaction_id.clone(),
a319bae745d8atagen2026-09-26 09:59 UTC240
                uploaded_bytes,
a319bae745d8atagen2026-09-26 09:59 UTC241
                total_bytes: 20,
a319bae745d8atagen2026-09-26 09:59 UTC242
                complete: uploaded_bytes == 20,
a319bae745d8atagen2026-09-26 09:59 UTC243
            })
a319bae745d8atagen2026-09-26 09:59 UTC244
            .unwrap();
a319bae745d8atagen2026-09-26 09:59 UTC245
    }
a319bae745d8atagen2026-09-26 09:59 UTC246
    tokio::time::timeout(Duration::from_secs(5), async {
a319bae745d8atagen2026-09-26 09:59 UTC247
        while !harness
a319bae745d8atagen2026-09-26 09:59 UTC248
            .mirror
a319bae745d8atagen2026-09-26 09:59 UTC249
            .snapshot()
a319bae745d8atagen2026-09-26 09:59 UTC250
            .uploads
a319bae745d8atagen2026-09-26 09:59 UTC251
            .get(&transaction_id)
a319bae745d8atagen2026-09-26 09:59 UTC252
            .is_some_and(|upload| upload.complete)
a319bae745d8atagen2026-09-26 09:59 UTC253
        {
a319bae745d8atagen2026-09-26 09:59 UTC254
            tokio::task::yield_now().await;
a319bae745d8atagen2026-09-26 09:59 UTC255
        }
a319bae745d8atagen2026-09-26 09:59 UTC256
    })
a319bae745d8atagen2026-09-26 09:59 UTC257
    .await
a319bae745d8atagen2026-09-26 09:59 UTC258
    .expect("the mirror never caught up");
a319bae745d8atagen2026-09-26 09:59 UTC259
a319bae745d8atagen2026-09-26 09:59 UTC260
    let update = updates.next().await.unwrap();
a319bae745d8atagen2026-09-26 09:59 UTC261
    let StateChange::State(state) = &update.change else {
a319bae745d8atagen2026-09-26 09:59 UTC262
        panic!("a lagged subscription carried on with {update:?}");
a319bae745d8atagen2026-09-26 09:59 UTC263
    };
a319bae745d8atagen2026-09-26 09:59 UTC264
    assert!(state.uploads[&transaction_id].complete);
a319bae745d8atagen2026-09-26 09:59 UTC265
}
a319bae745d8atagen2026-09-26 09:59 UTC266
a319bae745d8atagen2026-09-26 09:59 UTC267
#[tokio::test]
a319bae745d8atagen2026-09-26 09:59 UTC268
async fn resync_does_not_deadlock_on_full_queues() {
a319bae745d8atagen2026-09-26 09:59 UTC269
    let config = EngineConfig::default()
a319bae745d8atagen2026-09-26 09:59 UTC270
        .with_command_capacity(1)
a319bae745d8atagen2026-09-26 09:59 UTC271
        .with_event_capacity(1);
a319bae745d8atagen2026-09-26 09:59 UTC272
    let harness = start(config, 1024).await;
a319bae745d8atagen2026-09-26 09:59 UTC273
    let timeline_id = TimelineId::from(harness.room_id.clone());
a319bae745d8atagen2026-09-26 09:59 UTC274
    let mut updates = harness.mirror.subscribe();
a319bae745d8atagen2026-09-26 09:59 UTC275
    harness
a319bae745d8atagen2026-09-26 09:59 UTC276
        .handle
a319bae745d8atagen2026-09-26 09:59 UTC277
        .open_room(harness.room_id.clone())
a319bae745d8atagen2026-09-26 09:59 UTC278
        .await
a319bae745d8atagen2026-09-26 09:59 UTC279
        .unwrap();
a319bae745d8atagen2026-09-26 09:59 UTC280
    next_matching(&mut updates, |change| {
a319bae745d8atagen2026-09-26 09:59 UTC281
        timeline_change(change, &timeline_id, |change| {
a319bae745d8atagen2026-09-26 09:59 UTC282
            matches!(change, TimelineChange::Reset { .. })
a319bae745d8atagen2026-09-26 09:59 UTC283
        })
a319bae745d8atagen2026-09-26 09:59 UTC284
    })
a319bae745d8atagen2026-09-26 09:59 UTC285
    .await;
a319bae745d8atagen2026-09-26 09:59 UTC286
a319bae745d8atagen2026-09-26 09:59 UTC287
    let flood = tokio::spawn({
a319bae745d8atagen2026-09-26 09:59 UTC288
        let handle = harness.handle.clone();
a319bae745d8atagen2026-09-26 09:59 UTC289
        let timeline_id = timeline_id.clone();
a319bae745d8atagen2026-09-26 09:59 UTC290
        async move {
a319bae745d8atagen2026-09-26 09:59 UTC291
            loop {
a319bae745d8atagen2026-09-26 09:59 UTC292
                let command = Command::Timeline(TimelineCommand::Paginate {
a319bae745d8atagen2026-09-26 09:59 UTC293
                    timeline_id: timeline_id.clone(),
a319bae745d8atagen2026-09-26 09:59 UTC294
                    limit: 1,
a319bae745d8atagen2026-09-26 09:59 UTC295
                });
a319bae745d8atagen2026-09-26 09:59 UTC296
                if handle.send(command).await.is_err() {
a319bae745d8atagen2026-09-26 09:59 UTC297
                    return;
a319bae745d8atagen2026-09-26 09:59 UTC298
                }
a319bae745d8atagen2026-09-26 09:59 UTC299
            }
a319bae745d8atagen2026-09-26 09:59 UTC300
        }
a319bae745d8atagen2026-09-26 09:59 UTC301
    });
a319bae745d8atagen2026-09-26 09:59 UTC302
    for _ in 0..10 {
a319bae745d8atagen2026-09-26 09:59 UTC303
        harness
a319bae745d8atagen2026-09-26 09:59 UTC304
            .inject
a319bae745d8atagen2026-09-26 09:59 UTC305
            .send(Event::TimelineChanged {
a319bae745d8atagen2026-09-26 09:59 UTC306
                timeline_id: timeline_id.clone(),
a319bae745d8atagen2026-09-26 09:59 UTC307
                diffs: vec![TimelineDiff::Remove { index: 1000 }],
a319bae745d8atagen2026-09-26 09:59 UTC308
            })
a319bae745d8atagen2026-09-26 09:59 UTC309
            .unwrap();
a319bae745d8atagen2026-09-26 09:59 UTC310
        next_matching(&mut updates, |change| {
a319bae745d8atagen2026-09-26 09:59 UTC311
            timeline_change(change, &timeline_id, |change| {
a319bae745d8atagen2026-09-26 09:59 UTC312
                matches!(change, TimelineChange::Diverged)
a319bae745d8atagen2026-09-26 09:59 UTC313
            })
a319bae745d8atagen2026-09-26 09:59 UTC314
        })
a319bae745d8atagen2026-09-26 09:59 UTC315
        .await;
a319bae745d8atagen2026-09-26 09:59 UTC316
        next_matching(&mut updates, |change| {
a319bae745d8atagen2026-09-26 09:59 UTC317
            timeline_change(change, &timeline_id, |change| {
a319bae745d8atagen2026-09-26 09:59 UTC318
                matches!(change, TimelineChange::Reset { .. })
a319bae745d8atagen2026-09-26 09:59 UTC319
            })
a319bae745d8atagen2026-09-26 09:59 UTC320
        })
a319bae745d8atagen2026-09-26 09:59 UTC321
        .await;
a319bae745d8atagen2026-09-26 09:59 UTC322
    }
a319bae745d8atagen2026-09-26 09:59 UTC323
    flood.abort();
a319bae745d8atagen2026-09-26 09:59 UTC324
}
a319bae745d8atagen2026-09-26 09:59 UTC325
a319bae745d8atagen2026-09-26 09:59 UTC326
#[tokio::test]
a319bae745d8atagen2026-09-26 09:59 UTC327
async fn closing_a_timeline_evicts_it() {
a319bae745d8atagen2026-09-26 09:59 UTC328
    let harness = start(EngineConfig::default(), 256).await;
a319bae745d8atagen2026-09-26 09:59 UTC329
    let timeline_id = TimelineId::from(harness.room_id.clone());
a319bae745d8atagen2026-09-26 09:59 UTC330
    let mut updates = harness.mirror.subscribe();
a319bae745d8atagen2026-09-26 09:59 UTC331
    harness
a319bae745d8atagen2026-09-26 09:59 UTC332
        .handle
a319bae745d8atagen2026-09-26 09:59 UTC333
        .open_room(harness.room_id.clone())
a319bae745d8atagen2026-09-26 09:59 UTC334
        .await
a319bae745d8atagen2026-09-26 09:59 UTC335
        .unwrap();
a319bae745d8atagen2026-09-26 09:59 UTC336
    harness
a319bae745d8atagen2026-09-26 09:59 UTC337
        .handle
a319bae745d8atagen2026-09-26 09:59 UTC338
        .close_room(harness.room_id.clone())
a319bae745d8atagen2026-09-26 09:59 UTC339
        .await
a319bae745d8atagen2026-09-26 09:59 UTC340
        .unwrap();
a319bae745d8atagen2026-09-26 09:59 UTC341
    next_matching(&mut updates, |change| {
a319bae745d8atagen2026-09-26 09:59 UTC342
        timeline_change(change, &timeline_id, |change| {
a319bae745d8atagen2026-09-26 09:59 UTC343
            matches!(change, TimelineChange::Closed)
a319bae745d8atagen2026-09-26 09:59 UTC344
        })
a319bae745d8atagen2026-09-26 09:59 UTC345
    })
a319bae745d8atagen2026-09-26 09:59 UTC346
    .await;
a319bae745d8atagen2026-09-26 09:59 UTC347
    assert!(
a319bae745d8atagen2026-09-26 09:59 UTC348
        !harness
a319bae745d8atagen2026-09-26 09:59 UTC349
            .mirror
a319bae745d8atagen2026-09-26 09:59 UTC350
            .snapshot()
a319bae745d8atagen2026-09-26 09:59 UTC351
            .timelines
a319bae745d8atagen2026-09-26 09:59 UTC352
            .contains_key(&timeline_id)
a319bae745d8atagen2026-09-26 09:59 UTC353
    );
a319bae745d8atagen2026-09-26 09:59 UTC354
}
a319bae745d8atagen2026-09-26 09:59 UTC355
a319bae745d8atagen2026-09-26 09:59 UTC356
#[tokio::test]
a319bae745d8atagen2026-09-26 09:59 UTC357
async fn an_upload_ends_on_finished() {
a319bae745d8atagen2026-09-26 09:59 UTC358
    let harness = start(EngineConfig::default(), 256).await;
a319bae745d8atagen2026-09-26 09:59 UTC359
    let transaction_id = TransactionId::new();
a319bae745d8atagen2026-09-26 09:59 UTC360
    let mut updates = harness.mirror.subscribe();
a319bae745d8atagen2026-09-26 09:59 UTC361
    harness
a319bae745d8atagen2026-09-26 09:59 UTC362
        .inject
a319bae745d8atagen2026-09-26 09:59 UTC363
        .send(Event::MediaUploadProgress {
a319bae745d8atagen2026-09-26 09:59 UTC364
            room_id: harness.room_id.clone(),
a319bae745d8atagen2026-09-26 09:59 UTC365
            transaction_id: transaction_id.clone(),
a319bae745d8atagen2026-09-26 09:59 UTC366
            uploaded_bytes: 5,
a319bae745d8atagen2026-09-26 09:59 UTC367
            total_bytes: 10,
a319bae745d8atagen2026-09-26 09:59 UTC368
            complete: false,
a319bae745d8atagen2026-09-26 09:59 UTC369
        })
a319bae745d8atagen2026-09-26 09:59 UTC370
        .unwrap();
a319bae745d8atagen2026-09-26 09:59 UTC371
    next_matching(&mut updates, |change| {
a319bae745d8atagen2026-09-26 09:59 UTC372
        matches!(change, StateChange::Upload { .. })
a319bae745d8atagen2026-09-26 09:59 UTC373
    })
a319bae745d8atagen2026-09-26 09:59 UTC374
    .await;
a319bae745d8atagen2026-09-26 09:59 UTC375
    assert!(
a319bae745d8atagen2026-09-26 09:59 UTC376
        harness
a319bae745d8atagen2026-09-26 09:59 UTC377
            .mirror
a319bae745d8atagen2026-09-26 09:59 UTC378
            .snapshot()
a319bae745d8atagen2026-09-26 09:59 UTC379
            .uploads
a319bae745d8atagen2026-09-26 09:59 UTC380
            .contains_key(&transaction_id)
a319bae745d8atagen2026-09-26 09:59 UTC381
    );
a319bae745d8atagen2026-09-26 09:59 UTC382
    harness
a319bae745d8atagen2026-09-26 09:59 UTC383
        .inject
a319bae745d8atagen2026-09-26 09:59 UTC384
        .send(Event::MediaUploadFinished {
a319bae745d8atagen2026-09-26 09:59 UTC385
            room_id: harness.room_id.clone(),
a319bae745d8atagen2026-09-26 09:59 UTC386
            transaction_id: transaction_id.clone(),
a319bae745d8atagen2026-09-26 09:59 UTC387
            outcome: UploadOutcome::Cancelled,
a319bae745d8atagen2026-09-26 09:59 UTC388
        })
a319bae745d8atagen2026-09-26 09:59 UTC389
        .unwrap();
a319bae745d8atagen2026-09-26 09:59 UTC390
    next_matching(&mut updates, |change| {
a319bae745d8atagen2026-09-26 09:59 UTC391
        matches!(change, StateChange::UploadFinished { .. })
a319bae745d8atagen2026-09-26 09:59 UTC392
    })
a319bae745d8atagen2026-09-26 09:59 UTC393
    .await;
a319bae745d8atagen2026-09-26 09:59 UTC394
    assert!(harness.mirror.snapshot().uploads.is_empty());
a319bae745d8atagen2026-09-26 09:59 UTC395
}