atagen

macltok

public
Star0
Watch0
96fdf90aacd6dc2b8bd3d63773234245b389d28c
3
ssh://brewery@pls.lobotomise.me:33/atagen/macltok.git
tests/send_queue.rsRust251 lines7.8 KB
1
use std::time::Duration;
2
3
use macltok::{
4
    ClientHandle, Command, CommandErrorKind, CommandId, CommandKind, Engine, EngineConfig, Event,
5
    EventStream, LifecycleCommand, MessageContent, RoomCommand, TimelineCommand, TimelineItem,
6
};
7
use matrix_sdk::{
8
    Client,
9
    ruma::{OwnedTransactionId, RoomId, api::client::sync::sync_events::v5, event_id, room_id},
10
    test_utils::mocks::MatrixMockServer,
11
};
12
13
#[tokio::test]
14
async fn cancel_send_uses_transaction_id_from_public_timeline() {
15
    let (server, client, handle, mut events, running) = setup().await;
16
    let room_id = room_id!("!queue-controls:localhost");
17
    client.send_queue().set_enabled(false).await;
18
19
    let transaction_id = queue_message(&handle, &mut events, room_id, "discard me").await;
20
    assert!(
21
        handle
22
            .pending_send_ids(room_id)
23
            .await
24
            .unwrap()
25
            .contains(&transaction_id)
26
    );
27
    let id = handle
28
        .cancel_send(room_id.to_owned(), transaction_id.clone())
29
        .await
30
        .unwrap();
31
    expect_success(&mut events, id, CommandKind::Room).await;
32
33
    let (echoes, _) = client
34
        .get_room(room_id)
35
        .unwrap()
36
        .send_queue()
37
        .subscribe()
38
        .await
39
        .unwrap();
40
    assert!(
41
        !echoes
42
            .iter()
43
            .any(|echo| echo.transaction_id == transaction_id)
44
    );
45
46
    let id = handle
47
        .cancel_send(room_id.to_owned(), transaction_id)
48
        .await
49
        .unwrap();
50
    let failure = receive_until(
51
        &mut events,
52
        |event| matches!(event, Event::CommandFailed(failure) if failure.id == id),
53
    )
54
    .await;
55
    assert!(
56
        matches!(failure, Event::CommandFailed(failure) if failure.kind == CommandErrorKind::Other)
57
    );
58
59
    stop(handle, &mut events, running).await;
60
    drop(server);
61
}
62
63
#[tokio::test]
64
async fn cancel_queued_attachment_by_pending_send_id() {
65
    let (server, client, handle, mut events, running) = setup().await;
66
    let room_id = room_id!("!queue-controls:localhost");
67
    client.send_queue().set_enabled(false).await;
68
69
    let id = handle
70
        .upload_media(
71
            room_id.to_owned().into(),
72
            "note.txt".to_owned(),
73
            "text/plain".to_owned(),
74
            b"attachment".to_vec(),
75
            None,
76
        )
77
        .await
78
        .unwrap();
79
    expect_success(&mut events, id, CommandKind::Media).await;
80
81
    let pending = handle.pending_send_ids(room_id).await.unwrap();
82
    assert_eq!(pending.len(), 1);
83
    let id = handle
84
        .cancel_send(room_id.to_owned(), pending[0].clone())
85
        .await
86
        .unwrap();
87
    expect_success(&mut events, id, CommandKind::Room).await;
88
    assert!(handle.pending_send_ids(room_id).await.unwrap().is_empty());
89
90
    stop(handle, &mut events, running).await;
91
    drop(server);
92
}
93
94
#[tokio::test]
95
async fn retry_send_wakes_paused_queue() {
96
    let (server, client, handle, mut events, running) = setup().await;
97
    let room_id = room_id!("!queue-controls:localhost");
98
    client.send_queue().set_enabled(false).await;
99
    server
100
        .mock_room_send()
101
        .body_matches_partial_json(serde_json::json!({ "body": "retry me" }))
102
        .ok(event_id!("$retried:localhost"))
103
        .expect(1)
104
        .mount()
105
        .await;
106
107
    let transaction_id = queue_message(&handle, &mut events, room_id, "retry me").await;
108
    let id = handle
109
        .retry_send(room_id.to_owned(), transaction_id)
110
        .await
111
        .unwrap();
112
    expect_success(&mut events, id, CommandKind::Room).await;
113
    assert!(client.send_queue().is_enabled());
114
115
    tokio::time::timeout(Duration::from_secs(5), async {
116
        loop {
117
            let (echoes, _) = client
118
                .get_room(room_id)
119
                .unwrap()
120
                .send_queue()
121
                .subscribe()
122
                .await
123
                .unwrap();
124
            if echoes.is_empty() {
125
                break;
126
            }
127
            tokio::task::yield_now().await;
128
        }
129
    })
130
    .await
131
    .expect("retried message was not sent");
132
133
    stop(handle, &mut events, running).await;
134
    drop(server);
135
}
136
137
async fn setup() -> (
138
    MatrixMockServer,
139
    Client,
140
    ClientHandle,
141
    EventStream,
142
    tokio::task::JoinHandle<Result<(), macltok::EngineError>>,
143
) {
144
    let server = MatrixMockServer::new().await;
145
    server
146
        .mock_sliding_sync()
147
        .ok(v5::Response::new("1".to_owned()))
148
        .mount()
149
        .await;
150
    let client = server.client_builder().build().await;
151
    let room_id = room_id!("!queue-controls:localhost");
152
    server.mock_room_state_encryption().plain().mount().await;
153
    server.sync_joined_room(&client, room_id).await;
154
    let (engine, handle, events) = Engine::from_client(
155
        client.clone(),
156
        EngineConfig::default().with_initial_history_limit(0),
157
    )
158
    .await
159
    .unwrap();
160
    let running = tokio::spawn(engine.run());
161
    let mut events = events;
162
    let id = handle
163
        .send(Command::Room(RoomCommand::Open(room_id.to_owned())))
164
        .await
165
        .unwrap();
166
    expect_success(&mut events, id, CommandKind::Room).await;
167
    (server, client, handle, events, running)
168
}
169
170
async fn queue_message(
171
    handle: &ClientHandle,
172
    events: &mut EventStream,
173
    room_id: &RoomId,
174
    body: &str,
175
) -> OwnedTransactionId {
176
    let id = handle
177
        .send(Command::Timeline(TimelineCommand::Send {
178
            timeline_id: room_id.to_owned().into(),
179
            content: MessageContent::text(body),
180
        }))
181
        .await
182
        .unwrap();
183
    let mut succeeded = false;
184
    let mut items = Vec::new();
185
    tokio::time::timeout(Duration::from_secs(5), async {
186
        loop {
187
            match events.recv().await.expect("event stream closed") {
188
                Event::TimelineChanged { diffs, .. } => {
189
                    for diff in diffs {
190
                        diff.try_apply(&mut items).unwrap();
191
                    }
192
                    let transaction = items.iter().find_map(|item| match item {
193
                        TimelineItem::Message(message) if message.body == body => {
194
                            message.transaction_id.clone()
195
                        }
196
                        _ => None,
197
                    });
198
                    if let Some(transaction) = transaction {
199
                        if !succeeded {
200
                            expect_success(events, id, CommandKind::Timeline).await;
201
                        }
202
                        return transaction;
203
                    }
204
                }
205
                Event::CommandSucceeded {
206
                    id: completed,
207
                    command: CommandKind::Timeline,
208
                } if completed == id => succeeded = true,
209
                Event::CommandFailed(failure) if failure.id == id => {
210
                    panic!("send failed {failure:?}")
211
                }
212
                _ => {}
213
            }
214
        }
215
    })
216
    .await
217
    .expect("local echo did not appear")
218
}
219
220
async fn expect_success(events: &mut EventStream, id: CommandId, command: CommandKind) {
221
    receive_until(events, |event| {
222
        matches!(event, Event::CommandSucceeded { id: completed, command: kind } if *completed == id && *kind == command)
223
    })
224
    .await;
225
}
226
227
async fn receive_until(events: &mut EventStream, predicate: impl Fn(&Event) -> bool) -> Event {
228
    tokio::time::timeout(Duration::from_secs(5), async {
229
        loop {
230
            let event = events.recv().await.expect("event stream closed");
231
            if predicate(&event) {
232
                return event;
233
            }
234
        }
235
    })
236
    .await
237
    .expect("expected event did not arrive")
238
}
239
240
async fn stop(
241
    handle: ClientHandle,
242
    events: &mut EventStream,
243
    running: tokio::task::JoinHandle<Result<(), macltok::EngineError>>,
244
) {
245
    let id = handle
246
        .send(Command::Lifecycle(LifecycleCommand::Shutdown))
247
        .await
248
        .unwrap();
249
    expect_success(events, id, CommandKind::Lifecycle).await;
250
    running.await.unwrap().unwrap();
251
}