atagen

macltok

public
Star0
Watch0
fa9114fcf75f7bdf1a7cec3b3367d604cf7de472
3
ssh://brewery@pls.lobotomise.me:33/atagen/macltok.git
tests/send_queue.rsRust255 lines7.9 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
99
        .get_room(room_id)
100
        .unwrap()
101
        .send_queue()
102
        .set_enabled(false);
103
    server
104
        .mock_room_send()
105
        .body_matches_partial_json(serde_json::json!({ "body": "retry me" }))
106
        .ok(event_id!("$retried:localhost"))
107
        .expect(1)
108
        .mount()
109
        .await;
110
111
    let transaction_id = queue_message(&handle, &mut events, room_id, "retry me").await;
112
    let id = handle
113
        .retry_send(room_id.to_owned(), transaction_id)
114
        .await
115
        .unwrap();
116
    expect_success(&mut events, id, CommandKind::Room).await;
117
    assert!(client.get_room(room_id).unwrap().send_queue().is_enabled());
118
119
    tokio::time::timeout(Duration::from_secs(5), async {
120
        loop {
121
            let (echoes, _) = client
122
                .get_room(room_id)
123
                .unwrap()
124
                .send_queue()
125
                .subscribe()
126
                .await
127
                .unwrap();
128
            if echoes.is_empty() {
129
                break;
130
            }
131
            tokio::task::yield_now().await;
132
        }
133
    })
134
    .await
135
    .expect("retried message was not sent");
136
137
    stop(handle, &mut events, running).await;
138
    drop(server);
139
}
140
141
async fn setup() -> (
142
    MatrixMockServer,
143
    Client,
144
    ClientHandle,
145
    EventStream,
146
    tokio::task::JoinHandle<Result<(), macltok::EngineError>>,
147
) {
148
    let server = MatrixMockServer::new().await;
149
    server
150
        .mock_sliding_sync()
151
        .ok(v5::Response::new("1".to_owned()))
152
        .mount()
153
        .await;
154
    let client = server.client_builder().build().await;
155
    let room_id = room_id!("!queue-controls:localhost");
156
    server.mock_room_state_encryption().plain().mount().await;
157
    server.sync_joined_room(&client, room_id).await;
158
    let (engine, handle, events) = Engine::from_client(
159
        client.clone(),
160
        EngineConfig::default().with_initial_history_limit(0),
161
    )
162
    .await
163
    .unwrap();
164
    let running = tokio::spawn(engine.run());
165
    let mut events = events;
166
    let id = handle
167
        .send(Command::Room(RoomCommand::Open(room_id.to_owned())))
168
        .await
169
        .unwrap();
170
    expect_success(&mut events, id, CommandKind::Room).await;
171
    (server, client, handle, events, running)
172
}
173
174
async fn queue_message(
175
    handle: &ClientHandle,
176
    events: &mut EventStream,
177
    room_id: &RoomId,
178
    body: &str,
179
) -> OwnedTransactionId {
180
    let id = handle
181
        .send(Command::Timeline(TimelineCommand::Send {
182
            timeline_id: room_id.to_owned().into(),
183
            content: MessageContent::text(body),
184
        }))
185
        .await
186
        .unwrap();
187
    let mut succeeded = false;
188
    let mut items = Vec::new();
189
    tokio::time::timeout(Duration::from_secs(5), async {
190
        loop {
191
            match events.recv().await.expect("event stream closed") {
192
                Event::TimelineChanged { diffs, .. } => {
193
                    for diff in diffs {
194
                        diff.try_apply(&mut items).unwrap();
195
                    }
196
                    let transaction = items.iter().find_map(|item| match item {
197
                        TimelineItem::Message(message) if message.body == body => {
198
                            message.transaction_id.clone()
199
                        }
200
                        _ => None,
201
                    });
202
                    if let Some(transaction) = transaction {
203
                        if !succeeded {
204
                            expect_success(events, id, CommandKind::Timeline).await;
205
                        }
206
                        return transaction;
207
                    }
208
                }
209
                Event::CommandSucceeded {
210
                    id: completed,
211
                    command: CommandKind::Timeline,
212
                } if completed == id => succeeded = true,
213
                Event::CommandFailed(failure) if failure.id == id => {
214
                    panic!("send failed {failure:?}")
215
                }
216
                _ => {}
217
            }
218
        }
219
    })
220
    .await
221
    .expect("local echo did not appear")
222
}
223
224
async fn expect_success(events: &mut EventStream, id: CommandId, command: CommandKind) {
225
    receive_until(events, |event| {
226
        matches!(event, Event::CommandSucceeded { id: completed, command: kind } if *completed == id && *kind == command)
227
    })
228
    .await;
229
}
230
231
async fn receive_until(events: &mut EventStream, predicate: impl Fn(&Event) -> bool) -> Event {
232
    tokio::time::timeout(Duration::from_secs(5), async {
233
        loop {
234
            let event = events.recv().await.expect("event stream closed");
235
            if predicate(&event) {
236
                return event;
237
            }
238
        }
239
    })
240
    .await
241
    .expect("expected event did not arrive")
242
}
243
244
async fn stop(
245
    handle: ClientHandle,
246
    events: &mut EventStream,
247
    running: tokio::task::JoinHandle<Result<(), macltok::EngineError>>,
248
) {
249
    let id = handle
250
        .send(Command::Lifecycle(LifecycleCommand::Shutdown))
251
        .await
252
        .unwrap();
253
    expect_success(events, id, CommandKind::Lifecycle).await;
254
    running.await.unwrap().unwrap();
255
}