matrix_sdk/test_utils/mocks/mod.rs
1// Copyright 2024 The Matrix.org Foundation C.I.C.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15//! Helpers to mock a server and have a client automatically connected to that
16//! server, for the purpose of integration tests.
17
18#![allow(missing_debug_implementations)]
19
20use std::{
21 collections::BTreeMap,
22 sync::{Arc, Mutex, atomic::AtomicU32},
23};
24
25use js_int::UInt;
26use matrix_sdk_base::deserialized_responses::TimelineEvent;
27#[cfg(feature = "experimental-element-recent-emojis")]
28use matrix_sdk_base::recent_emojis::RecentEmojisContent;
29use matrix_sdk_test::{
30 InvitedRoomBuilder, JoinedRoomBuilder, KnockedRoomBuilder, LeftRoomBuilder,
31 SyncResponseBuilder, test_json,
32};
33use percent_encoding::{AsciiSet, CONTROLS};
34use ruma::{
35 DeviceId, EventId, MilliSecondsSinceUnixEpoch, MxcUri, OwnedDeviceId, OwnedEventId,
36 OwnedOneTimeKeyId, OwnedRoomId, OwnedUserId, RoomId, ServerName, UserId,
37 api::client::{
38 receipt::create_receipt::v3::ReceiptType,
39 room::Visibility,
40 sync::sync_events::v5,
41 threads::get_thread_subscriptions_changes::unstable::{
42 ThreadSubscription, ThreadUnsubscription,
43 },
44 },
45 device_id,
46 directory::PublicRoomsChunk,
47 encryption::{CrossSigningKey, DeviceKeys, OneTimeKey},
48 events::{
49 AnyStateEvent, AnySyncTimelineEvent, AnyTimelineEvent, GlobalAccountDataEventType,
50 MessageLikeEventType, RoomAccountDataEventType, StateEventType, receipt::ReceiptThread,
51 room::member::RoomMemberEvent,
52 },
53 media::Method,
54 push::RuleKind,
55 serde::Raw,
56 time::Duration,
57};
58use serde::{Deserialize, Serialize};
59use serde_json::{Value, from_value, json};
60use tokio::sync::oneshot::{self, Receiver};
61use wiremock::{
62 Mock, MockBuilder, MockGuard, MockServer, Request, Respond, ResponseTemplate, Times,
63 matchers::{
64 body_json, body_partial_json, header, method, path, path_regex, query_param,
65 query_param_is_missing,
66 },
67};
68
69#[cfg(feature = "e2e-encryption")]
70pub mod encryption;
71pub mod oauth;
72
73use super::client::MockClientBuilder;
74use crate::{Client, OwnedServerName, Room, SlidingSyncBuilder, room::IncludeRelations};
75
76/// Structure used to store the crypto keys uploaded to the server.
77/// They will be served back to clients when requested.
78#[derive(Debug, Default)]
79struct Keys {
80 device: BTreeMap<OwnedUserId, BTreeMap<String, Raw<DeviceKeys>>>,
81 master: BTreeMap<OwnedUserId, Raw<CrossSigningKey>>,
82 self_signing: BTreeMap<OwnedUserId, Raw<CrossSigningKey>>,
83 user_signing: BTreeMap<OwnedUserId, Raw<CrossSigningKey>>,
84 one_time_keys: BTreeMap<
85 OwnedUserId,
86 BTreeMap<OwnedDeviceId, BTreeMap<OwnedOneTimeKeyId, Raw<OneTimeKey>>>,
87 >,
88}
89
90/// A [`wiremock`] [`MockServer`] along with useful methods to help mocking
91/// Matrix client-server API endpoints easily.
92///
93/// It implements mock endpoints, limiting the shared code as much as possible,
94/// so the mocks are still flexible to use as scoped/unscoped mounts, named, and
95/// so on.
96///
97/// It works like this:
98///
99/// * start by saying which endpoint you'd like to mock, e.g.
100/// [`Self::mock_room_send()`]. This returns a specialized [`MockEndpoint`]
101/// data structure, with its own impl. For this example, it's
102/// `MockEndpoint<RoomSendEndpoint>`.
103/// * configure the response on the endpoint-specific mock data structure. For
104/// instance, if you want the sending to result in a transient failure, call
105/// [`MockEndpoint::error500`]; if you want it to succeed and return the event
106/// `$42`, call [`MockEndpoint::ok()`]. It's still possible to call
107/// [`MockEndpoint::respond_with()`], as we do with wiremock MockBuilder, for
108/// maximum flexibility when the helpers aren't sufficient.
109/// * once the endpoint's response is configured, for any mock builder, you get
110/// a [`MatrixMock`]; this is a plain [`wiremock::Mock`] with the server
111/// curried, so one doesn't have to pass it around when calling
112/// [`MatrixMock::mount()`] or [`MatrixMock::mount_as_scoped()`]. As such, it
113/// mostly defers its implementations to [`wiremock::Mock`] under the hood.
114///
115/// # Examples
116///
117/// ```
118/// # tokio_test::block_on(async {
119/// use matrix_sdk::{ruma::{room_id, event_id}, test_utils::mocks::MatrixMockServer};
120/// use serde_json::json;
121///
122/// // First create the mock server and client pair.
123/// let mock_server = MatrixMockServer::new().await;
124/// let client = mock_server.client_builder().build().await;
125///
126/// // Let's say that our rooms are not encrypted.
127/// mock_server.mock_room_state_encryption().plain().mount().await;
128///
129/// // Let us get a room where we will send an event.
130/// let room = mock_server
131/// .sync_joined_room(&client, room_id!("!room_id:localhost"))
132/// .await;
133///
134/// // Now we mock the endpoint so we can actually send the event.
135/// let event_id = event_id!("$some_id");
136/// let send_guard = mock_server
137/// .mock_room_send()
138/// .ok(event_id)
139/// .expect(1)
140/// .mount_as_scoped()
141/// .await;
142///
143/// // And we send it out.
144/// let response = room.send_raw("m.room.message", json!({ "body": "Hello world" })).await?;
145///
146/// assert_eq!(
147/// event_id,
148/// response.event_id,
149/// "The event ID we mocked should match the one we received when we sent the event"
150/// );
151/// # anyhow::Ok(()) });
152/// ```
153pub struct MatrixMockServer {
154 server: MockServer,
155
156 /// Make the sync response builder stateful, to keep in memory the batch
157 /// token and avoid the client ignoring subsequent responses after the first
158 /// one.
159 sync_response_builder: Arc<Mutex<SyncResponseBuilder>>,
160
161 /// Make this mock server capable of mocking real end to end communications
162 keys: Arc<Mutex<Keys>>,
163
164 /// For crypto API end-points to work we need to be able to recognise
165 /// what client is doing the request by mapping the token to the user_id
166 token_to_user_id_map: Arc<Mutex<BTreeMap<String, OwnedUserId>>>,
167 token_counter: AtomicU32,
168}
169
170impl MatrixMockServer {
171 /// Create a new [`wiremock`] server specialized for Matrix usage.
172 pub async fn new() -> Self {
173 let server = MockServer::start().await;
174 let keys: Arc<Mutex<Keys>> = Default::default();
175 Self {
176 server,
177 sync_response_builder: Default::default(),
178 keys,
179 token_to_user_id_map: Default::default(),
180 token_counter: AtomicU32::new(0),
181 }
182 }
183
184 /// Creates a new [`MatrixMockServer`] from a [`wiremock`] server.
185 pub fn from_server(server: MockServer) -> Self {
186 let keys: Arc<Mutex<Keys>> = Default::default();
187 Self {
188 server,
189 sync_response_builder: Default::default(),
190 keys,
191 token_to_user_id_map: Default::default(),
192 token_counter: AtomicU32::new(0),
193 }
194 }
195
196 /// Creates a new [`MockClientBuilder`] configured to use this server,
197 /// preconfigured with a session expected by the server endpoints.
198 pub fn client_builder(&self) -> MockClientBuilder {
199 MockClientBuilder::new(Some(&self.server.uri()))
200 }
201
202 /// Return the underlying [`wiremock`] server.
203 pub fn server(&self) -> &MockServer {
204 &self.server
205 }
206
207 /// Return the URI of this server.
208 pub fn uri(&self) -> String {
209 self.server.uri()
210 }
211
212 /// Get an `OAuthMockServer` that uses the same mock server as this one.
213 pub fn oauth(&self) -> oauth::OAuthMockServer<'_> {
214 oauth::OAuthMockServer::new(self)
215 }
216
217 /// Mock the given endpoint.
218 fn mock_endpoint<T>(&self, mock: MockBuilder, endpoint: T) -> MockEndpoint<'_, T> {
219 MockEndpoint::new(&self.server, mock, endpoint)
220 }
221
222 /// Overrides the sync/ endpoint with knowledge that the given
223 /// invited/joined/knocked/left room exists, runs a sync and returns the
224 /// given room.
225 ///
226 /// # Examples
227 ///
228 /// ```
229 /// # tokio_test::block_on(async {
230 /// use matrix_sdk::{ruma::{room_id, event_id}, test_utils::mocks::MatrixMockServer};
231 /// use matrix_sdk_test::LeftRoomBuilder;
232 ///
233 /// let mock_server = MatrixMockServer::new().await;
234 /// let client = mock_server.client_builder().build().await;
235 ///
236 /// let left_room = mock_server
237 /// .sync_room(&client, LeftRoomBuilder::new(room_id!("!room_id:localhost")))
238 /// .await;
239 /// # anyhow::Ok(()) });
240 pub async fn sync_room(&self, client: &Client, room_data: impl Into<AnyRoomBuilder>) -> Room {
241 let any_room = room_data.into();
242 let room_id = any_room.room_id().to_owned();
243
244 self.mock_sync()
245 .ok_and_run(client, move |builder| match any_room {
246 AnyRoomBuilder::Invited(invited) => {
247 builder.add_invited_room(invited);
248 }
249 AnyRoomBuilder::Joined(joined) => {
250 builder.add_joined_room(joined);
251 }
252 AnyRoomBuilder::Left(left) => {
253 builder.add_left_room(left);
254 }
255 AnyRoomBuilder::Knocked(knocked) => {
256 builder.add_knocked_room(knocked);
257 }
258 })
259 .await;
260
261 client.get_room(&room_id).expect("look at me, the room is known now")
262 }
263
264 /// Overrides the sync/ endpoint with knowledge that the given room exists
265 /// in the joined state, runs a sync and returns the given room.
266 ///
267 /// # Examples
268 ///
269 /// ```
270 /// # tokio_test::block_on(async {
271 /// use matrix_sdk::{ruma::room_id, test_utils::mocks::MatrixMockServer};
272 ///
273 /// let mock_server = MatrixMockServer::new().await;
274 /// let client = mock_server.client_builder().build().await;
275 ///
276 /// let room = mock_server
277 /// .sync_joined_room(&client, room_id!("!room_id:localhost"))
278 /// .await;
279 /// # anyhow::Ok(()) });
280 pub async fn sync_joined_room(&self, client: &Client, room_id: &RoomId) -> Room {
281 self.sync_room(client, JoinedRoomBuilder::new(room_id)).await
282 }
283
284 /// Verify that the previous mocks expected number of requests match
285 /// reality, and then cancels all active mocks.
286 ///
287 /// # Examples
288 ///
289 /// ```
290 /// # tokio_test::block_on(async {
291 /// use matrix_sdk::{ruma::{room_id, event_id}, test_utils::mocks::MatrixMockServer};
292 /// use serde_json::json;
293 ///
294 /// let mock_server = MatrixMockServer::new().await;
295 /// let client = mock_server.client_builder().build().await;
296 ///
297 /// mock_server.mock_room_state_encryption().plain().mount().await;
298 /// let room = mock_server
299 /// .sync_joined_room(&client, room_id!("!room_id:localhost"))
300 /// .await;
301 /// mock_server.mock_room_send().ok(event_id!("$some_id")).mount().await;
302 ///
303 /// // This will succeed.
304 /// let response = room.send_raw("m.room.message", json!({ "body": "Hello world" })).await?;
305 ///
306 /// // Now we reset the mocks.
307 /// mock_server.verify_and_reset().await;
308 ///
309 /// // And we can't send anymore.
310 /// let response = room
311 /// .send_raw("m.room.message", json!({ "body": "Hello world" }))
312 /// .await
313 /// .expect_err("We removed the mock so sending should now fail");
314 /// # anyhow::Ok(()) });
315 /// ```
316 pub async fn verify_and_reset(&self) {
317 self.server.verify().await;
318 self.server.reset().await;
319 }
320}
321
322// Specific mount endpoints.
323impl MatrixMockServer {
324 /// Mocks a sync endpoint.
325 ///
326 /// # Examples
327 ///
328 /// ```
329 /// # tokio_test::block_on(async {
330 /// use matrix_sdk::{ruma::room_id, test_utils::mocks::MatrixMockServer};
331 /// use matrix_sdk_test::JoinedRoomBuilder;
332 ///
333 /// // First create the mock server and client pair.
334 /// let mock_server = MatrixMockServer::new().await;
335 /// let client = mock_server.client_builder().build().await;
336 /// let room_id = room_id!("!room_id:localhost");
337 ///
338 /// // Let's emulate what `MatrixMockServer::sync_joined_room()` does.
339 /// mock_server
340 /// .mock_sync()
341 /// .ok_and_run(&client, |builder| {
342 /// builder.add_joined_room(JoinedRoomBuilder::new(room_id));
343 /// })
344 /// .await;
345 ///
346 /// let room = client
347 /// .get_room(room_id)
348 /// .expect("The room should be available after we mocked the sync");
349 /// # anyhow::Ok(()) });
350 /// ```
351 pub fn mock_sync(&self) -> MockEndpoint<'_, SyncEndpoint> {
352 let mock = Mock::given(method("GET")).and(path("/_matrix/client/v3/sync"));
353 self.mock_endpoint(
354 mock,
355 SyncEndpoint { sync_response_builder: self.sync_response_builder.clone() },
356 )
357 }
358
359 /// Mocks the sliding sync endpoint.
360 pub fn mock_sliding_sync(&self) -> MockEndpoint<'_, SlidingSyncEndpoint> {
361 let mock = Mock::given(method("POST"))
362 .and(path("/_matrix/client/unstable/org.matrix.simplified_msc3575/sync"));
363 self.mock_endpoint(mock, SlidingSyncEndpoint)
364 }
365
366 /// Creates a prebuilt mock for joining a room.
367 ///
368 /// # Examples
369 ///
370 /// ```
371 /// # tokio_test::block_on(async {
372 /// use matrix_sdk::{ruma::{room_id, event_id}, test_utils::mocks::MatrixMockServer};
373 /// use serde_json::json;
374 ///
375 /// let mock_server = MatrixMockServer::new().await;
376 /// let client = mock_server.client_builder().build().await;
377 /// let room_id = room_id!("!test:localhost");
378 ///
379 /// mock_server.mock_room_join(room_id).ok().mount();
380 ///
381 /// let room = client.join_room_by_id(room_id).await?;
382 ///
383 /// assert_eq!(
384 /// room_id,
385 /// room.room_id(),
386 /// "The room ID we mocked should match the one we received when we joined the room"
387 /// );
388 /// # anyhow::Ok(()) });
389 /// ```
390 pub fn mock_room_join(&self, room_id: &RoomId) -> MockEndpoint<'_, JoinRoomEndpoint> {
391 let mock = Mock::given(method("POST"))
392 .and(path_regex(format!("^/_matrix/client/v3/rooms/{room_id}/join")));
393 self.mock_endpoint(mock, JoinRoomEndpoint { room_id: room_id.to_owned() })
394 }
395
396 /// Creates a prebuilt mock for sending an event in a room.
397 ///
398 /// Note: works with *any* room.
399 ///
400 /// # Examples
401 ///
402 /// ```
403 /// # tokio_test::block_on(async {
404 /// use matrix_sdk::{ruma::{room_id, event_id}, test_utils::mocks::MatrixMockServer};
405 /// use serde_json::json;
406 ///
407 /// let mock_server = MatrixMockServer::new().await;
408 /// let client = mock_server.client_builder().build().await;
409 ///
410 /// mock_server.mock_room_state_encryption().plain().mount().await;
411 ///
412 /// let room = mock_server
413 /// .sync_joined_room(&client, room_id!("!room_id:localhost"))
414 /// .await;
415 ///
416 /// let event_id = event_id!("$some_id");
417 /// mock_server
418 /// .mock_room_send()
419 /// .ok(event_id)
420 /// .expect(1)
421 /// .mount()
422 /// .await;
423 ///
424 /// let response = room.send_raw("m.room.message", json!({ "body": "Hello world" })).await?;
425 ///
426 /// assert_eq!(
427 /// event_id,
428 /// response.event_id,
429 /// "The event ID we mocked should match the one we received when we sent the event"
430 /// );
431 /// # anyhow::Ok(()) });
432 /// ```
433 pub fn mock_room_send(&self) -> MockEndpoint<'_, RoomSendEndpoint> {
434 let mock = Mock::given(method("PUT"))
435 .and(path_regex(r"^/_matrix/client/v3/rooms/.*/send/.*".to_owned()));
436 self.mock_endpoint(mock, RoomSendEndpoint)
437 }
438
439 /// Creates a prebuilt mock for sending a state event in a room.
440 ///
441 /// Similar to: [`MatrixMockServer::mock_room_send`]
442 ///
443 /// Note: works with *any* room.
444 /// Note: works with *any* event type.
445 ///
446 /// ```
447 /// # tokio_test::block_on(async {
448 /// use matrix_sdk::{ruma::{room_id, event_id}, test_utils::mocks::MatrixMockServer};
449 /// use serde_json::json;
450 ///
451 /// let mock_server = MatrixMockServer::new().await;
452 /// let client = mock_server.client_builder().build().await;
453 ///
454 /// mock_server.mock_room_state_encryption().plain().mount().await;
455 ///
456 /// let room = mock_server
457 /// .sync_joined_room(&client, room_id!("!room_id:localhost"))
458 /// .await;
459 ///
460 /// let event_id = event_id!("$some_id");
461 /// mock_server
462 /// .mock_room_send_state()
463 /// .ok(event_id)
464 /// .expect(1)
465 /// .mount()
466 /// .await;
467 ///
468 /// let response_not_mocked = room.send_raw("m.room.create", json!({ "body": "Hello world" })).await;
469 /// // The `/send` endpoint should not be mocked by the server.
470 /// assert!(response_not_mocked.is_err());
471 ///
472 ///
473 /// let response = room.send_state_event_raw("m.room.message", "my_key", json!({ "body": "Hello world" })).await?;
474 /// // The `/state` endpoint should be mocked by the server.
475 /// assert_eq!(
476 /// event_id,
477 /// response.event_id,
478 /// "The event ID we mocked should match the one we received when we sent the event"
479 /// );
480 /// # anyhow::Ok(()) });
481 /// ```
482 pub fn mock_room_send_state(&self) -> MockEndpoint<'_, RoomSendStateEndpoint> {
483 let mock =
484 Mock::given(method("PUT")).and(path_regex(r"^/_matrix/client/v3/rooms/.*/state/.*/.*"));
485 self.mock_endpoint(mock, RoomSendStateEndpoint::default()).expect_default_access_token()
486 }
487
488 /// Creates a prebuilt mock for asking whether *a* room is encrypted or not.
489 ///
490 /// Note: Applies to all rooms.
491 ///
492 /// # Examples
493 ///
494 /// ```
495 /// # tokio_test::block_on(async {
496 /// use matrix_sdk::{ruma::room_id, test_utils::mocks::MatrixMockServer};
497 ///
498 /// let mock_server = MatrixMockServer::new().await;
499 /// let client = mock_server.client_builder().build().await;
500 ///
501 /// mock_server.mock_room_state_encryption().encrypted().mount().await;
502 ///
503 /// let room = mock_server
504 /// .sync_joined_room(&client, room_id!("!room_id:localhost"))
505 /// .await;
506 ///
507 /// assert!(
508 /// room.latest_encryption_state().await?.is_encrypted(),
509 /// "The room should be marked as encrypted."
510 /// );
511 /// # anyhow::Ok(()) });
512 /// ```
513 pub fn mock_room_state_encryption(&self) -> MockEndpoint<'_, EncryptionStateEndpoint> {
514 let mock = Mock::given(method("GET"))
515 .and(path_regex(r"^/_matrix/client/v3/rooms/.*/state/m.*room.*encryption.?"));
516 self.mock_endpoint(mock, EncryptionStateEndpoint).expect_default_access_token()
517 }
518
519 /// Creates a prebuilt mock for setting the room encryption state.
520 ///
521 /// Note: Applies to all rooms.
522 ///
523 /// # Examples
524 ///
525 /// ```
526 /// # tokio_test::block_on(async {
527 /// use matrix_sdk::{
528 /// ruma::{event_id, room_id},
529 /// test_utils::mocks::MatrixMockServer,
530 /// };
531 ///
532 /// let mock_server = MatrixMockServer::new().await;
533 /// let client = mock_server.client_builder().build().await;
534 ///
535 /// mock_server.mock_room_state_encryption().plain().mount().await;
536 /// mock_server
537 /// .mock_set_room_state_encryption()
538 /// .ok(event_id!("$id"))
539 /// .mock_once()
540 /// .mount()
541 /// .await;
542 ///
543 /// let room = mock_server
544 /// .sync_joined_room(&client, room_id!("!room_id:localhost"))
545 /// .await;
546 ///
547 /// room.enable_encryption()
548 /// .await
549 /// .expect("We should be able to enable encryption in the room");
550 /// # anyhow::Ok(()) });
551 /// ```
552 pub fn mock_set_room_state_encryption(&self) -> MockEndpoint<'_, SetEncryptionStateEndpoint> {
553 let mock = Mock::given(method("PUT"))
554 .and(path_regex(r"^/_matrix/client/v3/rooms/.*/state/m.*room.*encryption.?"));
555 self.mock_endpoint(mock, SetEncryptionStateEndpoint).expect_default_access_token()
556 }
557
558 /// Creates a prebuilt mock for the room redact endpoint.
559 ///
560 /// # Examples
561 ///
562 /// ```
563 /// # tokio_test::block_on(async {
564 /// use matrix_sdk::{
565 /// ruma::{event_id, room_id},
566 /// test_utils::mocks::MatrixMockServer,
567 /// };
568 ///
569 /// let mock_server = MatrixMockServer::new().await;
570 /// let client = mock_server.client_builder().build().await;
571 /// let event_id = event_id!("$id");
572 ///
573 /// mock_server.mock_room_redact().ok(event_id).mock_once().mount().await;
574 ///
575 /// let room = mock_server
576 /// .sync_joined_room(&client, room_id!("!room_id:localhost"))
577 /// .await;
578 ///
579 /// room.redact(event_id, None, None)
580 /// .await
581 /// .expect("We should be able to redact events in the room");
582 /// # anyhow::Ok(()) });
583 /// ```
584 pub fn mock_room_redact(&self) -> MockEndpoint<'_, RoomRedactEndpoint> {
585 let mock = Mock::given(method("PUT"))
586 .and(path_regex(r"^/_matrix/client/v3/rooms/.*/redact/.*?/.*?"));
587 self.mock_endpoint(mock, RoomRedactEndpoint).expect_default_access_token()
588 }
589
590 /// Creates a prebuilt mock for retrieving an event with /room/.../event.
591 pub fn mock_room_event(&self) -> MockEndpoint<'_, RoomEventEndpoint> {
592 let mock = Mock::given(method("GET"));
593 self.mock_endpoint(mock, RoomEventEndpoint { room: None, match_event_id: false })
594 .expect_default_access_token()
595 }
596
597 /// Creates a prebuilt mock for retrieving an event with /room/.../context.
598 pub fn mock_room_event_context(&self) -> MockEndpoint<'_, RoomEventContextEndpoint> {
599 let mock = Mock::given(method("GET"));
600 self.mock_endpoint(mock, RoomEventContextEndpoint { room: None, match_event_id: false })
601 .expect_default_access_token()
602 }
603
604 /// Create a prebuild mock for paginating room message with the `/messages`
605 /// endpoint.
606 pub fn mock_room_messages(&self) -> MockEndpoint<'_, RoomMessagesEndpoint> {
607 let mock =
608 Mock::given(method("GET")).and(path_regex(r"^/_matrix/client/v3/rooms/.*/messages$"));
609 self.mock_endpoint(mock, RoomMessagesEndpoint).expect_default_access_token()
610 }
611
612 /// Create a prebuilt mock for uploading media.
613 pub fn mock_upload(&self) -> MockEndpoint<'_, UploadEndpoint> {
614 let mock = Mock::given(method("POST")).and(path("/_matrix/media/v3/upload"));
615 self.mock_endpoint(mock, UploadEndpoint)
616 }
617
618 /// Create a prebuilt mock for resolving room aliases.
619 ///
620 /// # Examples
621 ///
622 /// ```
623 /// # tokio_test::block_on(async {
624 /// use matrix_sdk::{
625 /// ruma::{owned_room_id, room_alias_id},
626 /// test_utils::mocks::MatrixMockServer,
627 /// };
628 /// let mock_server = MatrixMockServer::new().await;
629 /// let client = mock_server.client_builder().build().await;
630 ///
631 /// mock_server
632 /// .mock_room_directory_resolve_alias()
633 /// .ok("!a:b.c", Vec::new())
634 /// .mock_once()
635 /// .mount()
636 /// .await;
637 ///
638 /// let res = client
639 /// .resolve_room_alias(room_alias_id!("#a:b.c"))
640 /// .await
641 /// .expect("We should be able to resolve the room alias");
642 /// assert_eq!(res.room_id, owned_room_id!("!a:b.c"));
643 /// # anyhow::Ok(()) });
644 /// ```
645 pub fn mock_room_directory_resolve_alias(&self) -> MockEndpoint<'_, ResolveRoomAliasEndpoint> {
646 let mock =
647 Mock::given(method("GET")).and(path_regex(r"/_matrix/client/v3/directory/room/.*"));
648 self.mock_endpoint(mock, ResolveRoomAliasEndpoint)
649 }
650
651 /// Create a prebuilt mock for publishing room aliases in the room
652 /// directory.
653 ///
654 /// # Examples
655 ///
656 /// ```
657 /// # tokio_test::block_on(async {
658 /// use matrix_sdk::{
659 /// ruma::{room_alias_id, room_id},
660 /// test_utils::mocks::MatrixMockServer,
661 /// };
662 ///
663 /// let mock_server = MatrixMockServer::new().await;
664 /// let client = mock_server.client_builder().build().await;
665 ///
666 /// mock_server
667 /// .mock_room_directory_create_room_alias()
668 /// .ok()
669 /// .mock_once()
670 /// .mount()
671 /// .await;
672 ///
673 /// client
674 /// .create_room_alias(room_alias_id!("#a:b.c"), room_id!("!a:b.c"))
675 /// .await
676 /// .expect("We should be able to create a room alias");
677 /// # anyhow::Ok(()) });
678 /// ```
679 pub fn mock_room_directory_create_room_alias(
680 &self,
681 ) -> MockEndpoint<'_, CreateRoomAliasEndpoint> {
682 let mock =
683 Mock::given(method("PUT")).and(path_regex(r"/_matrix/client/v3/directory/room/.*"));
684 self.mock_endpoint(mock, CreateRoomAliasEndpoint)
685 }
686
687 /// Create a prebuilt mock for removing room aliases from the room
688 /// directory.
689 ///
690 /// # Examples
691 ///
692 /// ```
693 /// # tokio_test::block_on(async {
694 /// use matrix_sdk::{
695 /// ruma::room_alias_id, test_utils::mocks::MatrixMockServer,
696 /// };
697 ///
698 /// let mock_server = MatrixMockServer::new().await;
699 /// let client = mock_server.client_builder().build().await;
700 ///
701 /// mock_server
702 /// .mock_room_directory_remove_room_alias()
703 /// .ok()
704 /// .mock_once()
705 /// .mount()
706 /// .await;
707 ///
708 /// client
709 /// .remove_room_alias(room_alias_id!("#a:b.c"))
710 /// .await
711 /// .expect("We should be able to remove the room alias");
712 /// # anyhow::Ok(()) });
713 /// ```
714 pub fn mock_room_directory_remove_room_alias(
715 &self,
716 ) -> MockEndpoint<'_, RemoveRoomAliasEndpoint> {
717 let mock =
718 Mock::given(method("DELETE")).and(path_regex(r"/_matrix/client/v3/directory/room/.*"));
719 self.mock_endpoint(mock, RemoveRoomAliasEndpoint)
720 }
721
722 /// Create a prebuilt mock for listing public rooms.
723 ///
724 /// # Examples
725 ///
726 /// ```
727 /// #
728 /// tokio_test::block_on(async {
729 /// use js_int::uint;
730 /// use ruma::directory::PublicRoomsChunkInit;
731 /// use matrix_sdk::room_directory_search::RoomDirectorySearch;
732 /// use matrix_sdk::{
733 /// ruma::{event_id, room_id},
734 /// test_utils::mocks::MatrixMockServer,
735 /// };
736 /// let mock_server = MatrixMockServer::new().await;
737 /// let client = mock_server.client_builder().build().await;
738 /// let event_id = event_id!("$id");
739 /// let room_id = room_id!("!room_id:localhost");
740 ///
741 /// let chunk = vec![PublicRoomsChunkInit {
742 /// num_joined_members: uint!(0),
743 /// room_id: room_id.to_owned(),
744 /// world_readable: true,
745 /// guest_can_join: true,
746 /// }.into()];
747 ///
748 /// mock_server.mock_public_rooms().ok(chunk, None, None, Some(20)).mock_once().mount().await;
749 /// let mut room_directory_search = RoomDirectorySearch::new(client);
750 ///
751 /// room_directory_search.search(Some("some-alias".to_owned()), 100, None)
752 /// .await
753 /// .expect("Room directory search failed");
754 ///
755 /// let (results, _) = room_directory_search.results();
756 /// assert_eq!(results.len(), 1);
757 /// assert_eq!(results.get(0).unwrap().room_id, room_id.to_owned());
758 /// # });
759 /// ```
760 pub fn mock_public_rooms(&self) -> MockEndpoint<'_, PublicRoomsEndpoint> {
761 let mock = Mock::given(method("POST")).and(path_regex(r"/_matrix/client/v3/publicRooms"));
762 self.mock_endpoint(mock, PublicRoomsEndpoint)
763 }
764
765 /// Create a prebuilt mock for setting a room's visibility in the room
766 /// directory.
767 ///
768 /// # Examples
769 ///
770 /// ```
771 /// # tokio_test::block_on(async {
772 /// use matrix_sdk::{ruma::room_id, test_utils::mocks::MatrixMockServer};
773 /// use ruma::api::client::room::Visibility;
774 ///
775 /// let mock_server = MatrixMockServer::new().await;
776 /// let client = mock_server.client_builder().build().await;
777 ///
778 /// mock_server
779 /// .mock_room_directory_set_room_visibility()
780 /// .ok()
781 /// .mock_once()
782 /// .mount()
783 /// .await;
784 ///
785 /// let room = mock_server
786 /// .sync_joined_room(&client, room_id!("!room_id:localhost"))
787 /// .await;
788 ///
789 /// room.privacy_settings()
790 /// .update_room_visibility(Visibility::Private)
791 /// .await
792 /// .expect("We should be able to update the room's visibility");
793 /// # anyhow::Ok(()) });
794 /// ```
795 pub fn mock_room_directory_set_room_visibility(
796 &self,
797 ) -> MockEndpoint<'_, SetRoomVisibilityEndpoint> {
798 let mock = Mock::given(method("PUT"))
799 .and(path_regex(r"^/_matrix/client/v3/directory/list/room/.*$"));
800 self.mock_endpoint(mock, SetRoomVisibilityEndpoint)
801 }
802
803 /// Create a prebuilt mock for getting a room's visibility in the room
804 /// directory.
805 ///
806 /// # Examples
807 ///
808 /// ```
809 /// # tokio_test::block_on(async {
810 /// use matrix_sdk::{ruma::room_id, test_utils::mocks::MatrixMockServer};
811 /// use ruma::api::client::room::Visibility;
812 ///
813 /// let mock_server = MatrixMockServer::new().await;
814 /// let client = mock_server.client_builder().build().await;
815 ///
816 /// mock_server
817 /// .mock_room_directory_get_room_visibility()
818 /// .ok(Visibility::Public)
819 /// .mock_once()
820 /// .mount()
821 /// .await;
822 ///
823 /// let room = mock_server
824 /// .sync_joined_room(&client, room_id!("!room_id:localhost"))
825 /// .await;
826 ///
827 /// let visibility = room
828 /// .privacy_settings()
829 /// .get_room_visibility()
830 /// .await
831 /// .expect("We should be able to get the room's visibility");
832 /// assert_eq!(visibility, Visibility::Public);
833 /// # anyhow::Ok(()) });
834 /// ```
835 pub fn mock_room_directory_get_room_visibility(
836 &self,
837 ) -> MockEndpoint<'_, GetRoomVisibilityEndpoint> {
838 let mock = Mock::given(method("GET"))
839 .and(path_regex(r"^/_matrix/client/v3/directory/list/room/.*$"));
840 self.mock_endpoint(mock, GetRoomVisibilityEndpoint)
841 }
842
843 /// Create a prebuilt mock for fetching information about key storage
844 /// backups.
845 ///
846 /// # Examples
847 ///
848 /// ```
849 /// # #[cfg(feature = "e2e-encryption")]
850 /// # {
851 /// # tokio_test::block_on(async {
852 /// use matrix_sdk::test_utils::mocks::MatrixMockServer;
853 ///
854 /// let mock_server = MatrixMockServer::new().await;
855 /// let client = mock_server.client_builder().build().await;
856 ///
857 /// mock_server.mock_room_keys_version().exists().expect(1).mount().await;
858 ///
859 /// let exists =
860 /// client.encryption().backups().fetch_exists_on_server().await.unwrap();
861 ///
862 /// assert!(exists);
863 /// # });
864 /// # }
865 /// ```
866 pub fn mock_room_keys_version(&self) -> MockEndpoint<'_, RoomKeysVersionEndpoint> {
867 let mock =
868 Mock::given(method("GET")).and(path_regex(r"_matrix/client/v3/room_keys/version"));
869 self.mock_endpoint(mock, RoomKeysVersionEndpoint).expect_default_access_token()
870 }
871
872 /// Create a prebuilt mock for adding key storage backups via POST
873 pub fn mock_add_room_keys_version(&self) -> MockEndpoint<'_, AddRoomKeysVersionEndpoint> {
874 let mock =
875 Mock::given(method("POST")).and(path_regex(r"_matrix/client/v3/room_keys/version"));
876 self.mock_endpoint(mock, AddRoomKeysVersionEndpoint).expect_default_access_token()
877 }
878
879 /// Create a prebuilt mock for adding key storage backups via POST
880 pub fn mock_delete_room_keys_version(&self) -> MockEndpoint<'_, DeleteRoomKeysVersionEndpoint> {
881 let mock = Mock::given(method("DELETE"))
882 .and(path_regex(r"_matrix/client/v3/room_keys/version/[^/]*"));
883 self.mock_endpoint(mock, DeleteRoomKeysVersionEndpoint).expect_default_access_token()
884 }
885
886 /// Creates a prebuilt mock for the `/sendToDevice` endpoint.
887 ///
888 /// This mock can be used to simulate sending to-device messages in tests.
889 /// # Examples
890 ///
891 /// ```
892 /// # #[cfg(feature = "e2e-encryption")]
893 /// # {
894 /// # tokio_test::block_on(async {
895 /// use std::collections::BTreeMap;
896 /// use matrix_sdk::{
897 /// ruma::{
898 /// serde::Raw,
899 /// api::client::to_device::send_event_to_device::v3::Request as ToDeviceRequest,
900 /// to_device::DeviceIdOrAllDevices,
901 /// user_id,owned_device_id
902 /// },
903 /// test_utils::mocks::MatrixMockServer,
904 /// };
905 /// use serde_json::json;
906 ///
907 /// let mock_server = MatrixMockServer::new().await;
908 /// let client = mock_server.client_builder().build().await;
909 ///
910 /// mock_server.mock_send_to_device().ok().mock_once().mount().await;
911 ///
912 /// let request = ToDeviceRequest::new_raw(
913 /// "m.custom.event".into(),
914 /// "txn_id".into(),
915 /// BTreeMap::from([
916 /// (user_id!("@alice:localhost").to_owned(), BTreeMap::from([(
917 /// DeviceIdOrAllDevices::AllDevices,
918 /// Raw::new(&ruma::events::AnyToDeviceEventContent::Dummy(ruma::events::dummy::ToDeviceDummyEventContent {})).unwrap(),
919 /// )])),
920 /// ])
921 /// );
922 ///
923 /// client
924 /// .send(request)
925 /// .await
926 /// .expect("We should be able to send a to-device message");
927 /// # anyhow::Ok(()) });
928 /// # }
929 /// ```
930 pub fn mock_send_to_device(&self) -> MockEndpoint<'_, SendToDeviceEndpoint> {
931 let mock =
932 Mock::given(method("PUT")).and(path_regex(r"^/_matrix/client/v3/sendToDevice/.*/.*"));
933 self.mock_endpoint(mock, SendToDeviceEndpoint).expect_default_access_token()
934 }
935
936 /// Create a prebuilt mock for getting the room members in a room.
937 ///
938 /// # Examples
939 ///
940 /// ```
941 /// # tokio_test::block_on(async {
942 /// use matrix_sdk::{
943 /// ruma::{event_id, room_id},
944 /// test_utils::mocks::MatrixMockServer,
945 /// };
946 /// use matrix_sdk_base::RoomMemberships;
947 /// use matrix_sdk_test::event_factory::EventFactory;
948 /// use ruma::{
949 /// events::room::member::{MembershipState, RoomMemberEventContent},
950 /// user_id,
951 /// };
952 /// let mock_server = MatrixMockServer::new().await;
953 /// let client = mock_server.client_builder().build().await;
954 /// let event_id = event_id!("$id");
955 /// let room_id = room_id!("!room_id:localhost");
956 ///
957 /// let f = EventFactory::new().room(room_id);
958 /// let alice_user_id = user_id!("@alice:b.c");
959 /// let alice_knock_event = f
960 /// .event(RoomMemberEventContent::new(MembershipState::Knock))
961 /// .event_id(event_id)
962 /// .sender(alice_user_id)
963 /// .state_key(alice_user_id)
964 /// .into_raw();
965 ///
966 /// mock_server
967 /// .mock_get_members()
968 /// .ok(vec![alice_knock_event])
969 /// .mock_once()
970 /// .mount()
971 /// .await;
972 /// let room = mock_server.sync_joined_room(&client, room_id).await;
973 ///
974 /// let members = room.members(RoomMemberships::all()).await.unwrap();
975 /// assert_eq!(members.len(), 1);
976 /// # });
977 /// ```
978 pub fn mock_get_members(&self) -> MockEndpoint<'_, GetRoomMembersEndpoint> {
979 let mock =
980 Mock::given(method("GET")).and(path_regex(r"^/_matrix/client/v3/rooms/.*/members$"));
981 self.mock_endpoint(mock, GetRoomMembersEndpoint)
982 }
983
984 /// Creates a prebuilt mock for inviting a user to a room by its id.
985 ///
986 /// # Examples
987 ///
988 /// ```
989 /// # use ruma::user_id;
990 /// tokio_test::block_on(async {
991 /// use matrix_sdk::{
992 /// ruma::room_id,
993 /// test_utils::mocks::MatrixMockServer,
994 /// };
995 ///
996 /// let mock_server = MatrixMockServer::new().await;
997 /// let client = mock_server.client_builder().build().await;
998 ///
999 /// mock_server.mock_invite_user_by_id().ok().mock_once().mount().await;
1000 ///
1001 /// let room = mock_server
1002 /// .sync_joined_room(&client, room_id!("!room_id:localhost"))
1003 /// .await;
1004 ///
1005 /// room.invite_user_by_id(user_id!("@alice:localhost")).await.unwrap();
1006 /// # anyhow::Ok(()) });
1007 /// ```
1008 pub fn mock_invite_user_by_id(&self) -> MockEndpoint<'_, InviteUserByIdEndpoint> {
1009 let mock =
1010 Mock::given(method("POST")).and(path_regex(r"^/_matrix/client/v3/rooms/.*/invite$"));
1011 self.mock_endpoint(mock, InviteUserByIdEndpoint)
1012 }
1013
1014 /// Creates a prebuilt mock for kicking a user from a room.
1015 ///
1016 /// # Examples
1017 ///
1018 /// ```
1019 /// # use ruma::user_id;
1020 /// tokio_test::block_on(async {
1021 /// use matrix_sdk::{
1022 /// ruma::room_id,
1023 /// test_utils::mocks::MatrixMockServer,
1024 /// };
1025 ///
1026 /// let mock_server = MatrixMockServer::new().await;
1027 /// let client = mock_server.client_builder().build().await;
1028 ///
1029 /// mock_server.mock_kick_user().ok().mock_once().mount().await;
1030 ///
1031 /// let room = mock_server
1032 /// .sync_joined_room(&client, room_id!("!room_id:localhost"))
1033 /// .await;
1034 ///
1035 /// room.kick_user(user_id!("@alice:localhost"), None).await.unwrap();
1036 /// # anyhow::Ok(()) });
1037 /// ```
1038 pub fn mock_kick_user(&self) -> MockEndpoint<'_, KickUserEndpoint> {
1039 let mock =
1040 Mock::given(method("POST")).and(path_regex(r"^/_matrix/client/v3/rooms/.*/kick"));
1041 self.mock_endpoint(mock, KickUserEndpoint)
1042 }
1043
1044 /// Creates a prebuilt mock for banning a user from a room.
1045 ///
1046 /// # Examples
1047 ///
1048 /// ```
1049 /// # use ruma::user_id;
1050 /// tokio_test::block_on(async {
1051 /// use matrix_sdk::{
1052 /// ruma::room_id,
1053 /// test_utils::mocks::MatrixMockServer,
1054 /// };
1055 ///
1056 /// let mock_server = MatrixMockServer::new().await;
1057 /// let client = mock_server.client_builder().build().await;
1058 ///
1059 /// mock_server.mock_ban_user().ok().mock_once().mount().await;
1060 ///
1061 /// let room = mock_server
1062 /// .sync_joined_room(&client, room_id!("!room_id:localhost"))
1063 /// .await;
1064 ///
1065 /// room.ban_user(user_id!("@alice:localhost"), None).await.unwrap();
1066 /// # anyhow::Ok(()) });
1067 /// ```
1068 pub fn mock_ban_user(&self) -> MockEndpoint<'_, BanUserEndpoint> {
1069 let mock = Mock::given(method("POST")).and(path_regex(r"^/_matrix/client/v3/rooms/.*/ban"));
1070 self.mock_endpoint(mock, BanUserEndpoint)
1071 }
1072
1073 /// Creates a prebuilt mock for the `/_matrix/client/versions` endpoint.
1074 pub fn mock_versions(&self) -> MockEndpoint<'_, VersionsEndpoint> {
1075 let mock = Mock::given(method("GET")).and(path_regex(r"^/_matrix/client/versions"));
1076 self.mock_endpoint(mock, VersionsEndpoint)
1077 }
1078
1079 /// Creates a prebuilt mock for the room summary endpoint [MSC3266](https://github.com/matrix-org/matrix-spec-proposals/pull/3266).
1080 pub fn mock_room_summary(&self) -> MockEndpoint<'_, RoomSummaryEndpoint> {
1081 let mock = Mock::given(method("GET"))
1082 .and(path_regex(r"^/_matrix/client/unstable/im.nheko.summary/rooms/.*/summary"));
1083 self.mock_endpoint(mock, RoomSummaryEndpoint)
1084 }
1085
1086 /// Creates a prebuilt mock for the endpoint used to set a room's pinned
1087 /// events.
1088 pub fn mock_set_room_pinned_events(&self) -> MockEndpoint<'_, SetRoomPinnedEventsEndpoint> {
1089 let mock = Mock::given(method("PUT"))
1090 .and(path_regex(r"^/_matrix/client/v3/rooms/.*/state/m.room.pinned_events/.*?"));
1091 self.mock_endpoint(mock, SetRoomPinnedEventsEndpoint).expect_default_access_token()
1092 }
1093
1094 /// Creates a prebuilt mock for the endpoint used to get information about
1095 /// the owner of the given access token.
1096 ///
1097 /// If no access token is provided, the access token to match is `"1234"`,
1098 /// which matches the default value in the mock data.
1099 pub fn mock_who_am_i(&self) -> MockEndpoint<'_, WhoAmIEndpoint> {
1100 let mock =
1101 Mock::given(method("GET")).and(path_regex(r"^/_matrix/client/v3/account/whoami"));
1102 self.mock_endpoint(mock, WhoAmIEndpoint).expect_default_access_token()
1103 }
1104
1105 /// Creates a prebuilt mock for the endpoint used to publish end-to-end
1106 /// encryption keys.
1107 pub fn mock_upload_keys(&self) -> MockEndpoint<'_, UploadKeysEndpoint> {
1108 let mock = Mock::given(method("POST")).and(path_regex(r"^/_matrix/client/v3/keys/upload"));
1109 self.mock_endpoint(mock, UploadKeysEndpoint).expect_default_access_token()
1110 }
1111
1112 /// Creates a prebuilt mock for the endpoint used to query end-to-end
1113 /// encryption keys.
1114 pub fn mock_query_keys(&self) -> MockEndpoint<'_, QueryKeysEndpoint> {
1115 let mock = Mock::given(method("POST")).and(path_regex(r"^/_matrix/client/v3/keys/query"));
1116 self.mock_endpoint(mock, QueryKeysEndpoint).expect_default_access_token()
1117 }
1118
1119 /// Creates a prebuilt mock for the endpoint used to discover the URL of a
1120 /// homeserver.
1121 pub fn mock_well_known(&self) -> MockEndpoint<'_, WellKnownEndpoint> {
1122 let mock = Mock::given(method("GET")).and(path_regex(r"^/.well-known/matrix/client"));
1123 self.mock_endpoint(mock, WellKnownEndpoint)
1124 }
1125
1126 /// Creates a prebuilt mock for the endpoint used to publish cross-signing
1127 /// keys.
1128 pub fn mock_upload_cross_signing_keys(
1129 &self,
1130 ) -> MockEndpoint<'_, UploadCrossSigningKeysEndpoint> {
1131 let mock = Mock::given(method("POST"))
1132 .and(path_regex(r"^/_matrix/client/v3/keys/device_signing/upload"));
1133 self.mock_endpoint(mock, UploadCrossSigningKeysEndpoint).expect_default_access_token()
1134 }
1135
1136 /// Creates a prebuilt mock for the endpoint used to publish cross-signing
1137 /// signatures.
1138 pub fn mock_upload_cross_signing_signatures(
1139 &self,
1140 ) -> MockEndpoint<'_, UploadCrossSigningSignaturesEndpoint> {
1141 let mock = Mock::given(method("POST"))
1142 .and(path_regex(r"^/_matrix/client/v3/keys/signatures/upload"));
1143 self.mock_endpoint(mock, UploadCrossSigningSignaturesEndpoint).expect_default_access_token()
1144 }
1145
1146 /// Creates a prebuilt mock for the endpoint used to leave a room.
1147 pub fn mock_room_leave(&self) -> MockEndpoint<'_, RoomLeaveEndpoint> {
1148 let mock =
1149 Mock::given(method("POST")).and(path_regex(r"^/_matrix/client/v3/rooms/.*/leave"));
1150 self.mock_endpoint(mock, RoomLeaveEndpoint).expect_default_access_token()
1151 }
1152
1153 /// Creates a prebuilt mock for the endpoint used to forget a room.
1154 pub fn mock_room_forget(&self) -> MockEndpoint<'_, RoomForgetEndpoint> {
1155 let mock =
1156 Mock::given(method("POST")).and(path_regex(r"^/_matrix/client/v3/rooms/.*/forget"));
1157 self.mock_endpoint(mock, RoomForgetEndpoint).expect_default_access_token()
1158 }
1159
1160 /// Create a prebuilt mock for the endpoint use to log out a session.
1161 pub fn mock_logout(&self) -> MockEndpoint<'_, LogoutEndpoint> {
1162 let mock = Mock::given(method("POST")).and(path("/_matrix/client/v3/logout"));
1163 self.mock_endpoint(mock, LogoutEndpoint).expect_default_access_token()
1164 }
1165
1166 /// Create a prebuilt mock for the endpoint used to get the list of thread
1167 /// roots.
1168 pub fn mock_room_threads(&self) -> MockEndpoint<'_, RoomThreadsEndpoint> {
1169 let mock =
1170 Mock::given(method("GET")).and(path_regex(r"^/_matrix/client/v1/rooms/.*/threads$"));
1171 self.mock_endpoint(mock, RoomThreadsEndpoint).expect_default_access_token()
1172 }
1173
1174 /// Create a prebuilt mock for the endpoint used to get the related events.
1175 pub fn mock_room_relations(&self) -> MockEndpoint<'_, RoomRelationsEndpoint> {
1176 // Routing happens in the final method ok(), since it can get complicated.
1177 let mock = Mock::given(method("GET"));
1178 self.mock_endpoint(mock, RoomRelationsEndpoint::default()).expect_default_access_token()
1179 }
1180
1181 /// Create a prebuilt mock for the endpoint used to get the global account
1182 /// data.
1183 ///
1184 /// # Examples
1185 ///
1186 /// ```
1187 /// tokio_test::block_on(async {
1188 /// use js_int::uint;
1189 /// use matrix_sdk::test_utils::mocks::MatrixMockServer;
1190 ///
1191 /// let mock_server = MatrixMockServer::new().await;
1192 /// let client = mock_server.client_builder().build().await;
1193 ///
1194 /// mock_server.mock_get_recent_emojis().ok(
1195 /// client.user_id().unwrap(),
1196 /// vec![(":)".to_string(), uint!(1))]
1197 /// )
1198 /// .mock_once()
1199 /// .mount()
1200 /// .await;
1201 ///
1202 /// client.account().fetch_media_preview_config_event_content().await.unwrap();
1203 ///
1204 /// # anyhow::Ok(()) });
1205 /// ```
1206 #[cfg(feature = "experimental-element-recent-emojis")]
1207 pub fn mock_get_recent_emojis(&self) -> MockEndpoint<'_, GetRecentEmojisEndpoint> {
1208 let mock = Mock::given(method("GET"));
1209 self.mock_endpoint(mock, GetRecentEmojisEndpoint).expect_default_access_token()
1210 }
1211
1212 /// Create a prebuilt mock for the endpoint that updates the global account
1213 /// data.
1214 ///
1215 /// # Examples
1216 ///
1217 /// ```
1218 /// tokio_test::block_on(async {
1219 /// use js_int::uint;
1220 /// use matrix_sdk::test_utils::mocks::MatrixMockServer;
1221 /// use ruma::user_id;
1222 ///
1223 /// let mock_server = MatrixMockServer::new().await;
1224 /// let client = mock_server.client_builder().build().await;
1225 /// let user_id = client.user_id().unwrap();
1226 ///
1227 /// mock.server.mock_get_recent_emojis()
1228 /// .ok(user_id, vec![(":D".to_string(), uint!(1))])
1229 /// .mock_once()
1230 /// .mount()
1231 /// .await;
1232 /// mock_server.mock_add_recent_emojis()
1233 /// .ok(user_id)
1234 /// .mock_once()
1235 /// .mount()
1236 /// .await;
1237 /// // Calls both get and update recent emoji endpoints, with an update value
1238 /// client.account().add_recent_emoji(":D").await.unwrap();
1239 ///
1240 /// # anyhow::Ok(()) });
1241 /// ```
1242 #[cfg(feature = "experimental-element-recent-emojis")]
1243 pub fn mock_add_recent_emojis(&self) -> MockEndpoint<'_, UpdateRecentEmojisEndpoint> {
1244 let mock = Mock::given(method("PUT"));
1245 self.mock_endpoint(mock, UpdateRecentEmojisEndpoint::new()).expect_default_access_token()
1246 }
1247
1248 /// Create a prebuilt mock for the endpoint used to get the default secret
1249 /// storage key.
1250 ///
1251 /// # Examples
1252 ///
1253 /// ```
1254 /// tokio_test::block_on(async {
1255 /// use js_int::uint;
1256 /// use matrix_sdk::{
1257 /// encryption::secret_storage::SecretStorage,
1258 /// test_utils::mocks::MatrixMockServer,
1259 /// };
1260 ///
1261 /// let mock_server = MatrixMockServer::new().await;
1262 /// let client = mock_server.client_builder().build().await;
1263 ///
1264 /// mock_server.mock_get_default_secret_storage_key().ok(
1265 /// client.user_id().unwrap(),
1266 /// "abc", // key ID of default secret storage key
1267 /// )
1268 /// .mount()
1269 /// .await;
1270 ///
1271 /// client.encryption()
1272 /// .secret_storage()
1273 /// .fetch_default_key_id()
1274 /// .await
1275 /// .unwrap();
1276 ///
1277 /// # anyhow::Ok(()) });
1278 /// ```
1279 #[cfg(feature = "e2e-encryption")]
1280 pub fn mock_get_default_secret_storage_key(
1281 &self,
1282 ) -> MockEndpoint<'_, GetDefaultSecretStorageKeyEndpoint> {
1283 let mock = Mock::given(method("GET"));
1284 self.mock_endpoint(mock, GetDefaultSecretStorageKeyEndpoint).expect_default_access_token()
1285 }
1286
1287 /// Create a prebuilt mock for the endpoint used to get a secret storage
1288 /// key.
1289 ///
1290 /// # Examples
1291 ///
1292 /// ```
1293 /// tokio_test::block_on(async {
1294 /// use js_int::uint;
1295 /// use ruma::events::secret_storage::key;
1296 /// use ruma::serde::Base64;
1297 /// use matrix_sdk::{
1298 /// encryption::secret_storage::SecretStorage,
1299 /// test_utils::mocks::MatrixMockServer,
1300 /// };
1301 ///
1302 /// let mock_server = MatrixMockServer::new().await;
1303 /// let client = mock_server.client_builder().build().await;
1304 ///
1305 /// mock_server.mock_get_default_secret_storage_key().ok(
1306 /// client.user_id().unwrap(),
1307 /// "abc",
1308 /// )
1309 /// .mount()
1310 /// .await;
1311 /// mock_server.mock_get_secret_storage_key().ok(
1312 /// client.user_id().unwrap(),
1313 /// &key::SecretStorageKeyEventContent::new(
1314 /// "abc".into(),
1315 /// key::SecretStorageEncryptionAlgorithm::V1AesHmacSha2(key::SecretStorageV1AesHmacSha2Properties::new(
1316 /// Some(Base64::parse("xv5b6/p3ExEw++wTyfSHEg==").unwrap()),
1317 /// Some(Base64::parse("ujBBbXahnTAMkmPUX2/0+VTfUh63pGyVRuBcDMgmJC8=").unwrap()),
1318 /// )),
1319 /// ),
1320 /// )
1321 /// .mount()
1322 /// .await;
1323 ///
1324 /// client.encryption()
1325 /// .secret_storage()
1326 /// .open_secret_store("EsTj 3yST y93F SLpB jJsz eAXc 2XzA ygD3 w69H fGaN TKBj jXEd")
1327 /// .await
1328 /// .unwrap();
1329 ///
1330 /// # anyhow::Ok(()) });
1331 /// ```
1332 #[cfg(feature = "e2e-encryption")]
1333 pub fn mock_get_secret_storage_key(&self) -> MockEndpoint<'_, GetSecretStorageKeyEndpoint> {
1334 let mock = Mock::given(method("GET"));
1335 self.mock_endpoint(mock, GetSecretStorageKeyEndpoint).expect_default_access_token()
1336 }
1337
1338 /// Create a prebuilt mock for the endpoint used to get the default secret
1339 /// storage key.
1340 ///
1341 /// # Examples
1342 ///
1343 /// ```
1344 /// tokio_test::block_on(async {
1345 /// use js_int::uint;
1346 /// use serde_json::json;
1347 /// use ruma::events::GlobalAccountDataEventType;
1348 /// use matrix_sdk::test_utils::mocks::MatrixMockServer;
1349 ///
1350 /// let mock_server = MatrixMockServer::new().await;
1351 /// let client = mock_server.client_builder().build().await;
1352 ///
1353 /// mock_server.mock_get_master_signing_key().ok(
1354 /// client.user_id().unwrap(),
1355 /// json!({})
1356 /// )
1357 /// .mount()
1358 /// .await;
1359 ///
1360 /// client.account()
1361 /// .fetch_account_data(GlobalAccountDataEventType::from("m.cross_signing.master".to_owned()))
1362 /// .await
1363 /// .unwrap();
1364 ///
1365 /// # anyhow::Ok(()) });
1366 /// ```
1367 #[cfg(feature = "e2e-encryption")]
1368 pub fn mock_get_master_signing_key(&self) -> MockEndpoint<'_, GetMasterSigningKeyEndpoint> {
1369 let mock = Mock::given(method("GET"));
1370 self.mock_endpoint(mock, GetMasterSigningKeyEndpoint).expect_default_access_token()
1371 }
1372
1373 /// Create a prebuilt mock for the endpoint used to send a single receipt.
1374 pub fn mock_send_receipt(
1375 &self,
1376 receipt_type: ReceiptType,
1377 ) -> MockEndpoint<'_, ReceiptEndpoint> {
1378 let mock = Mock::given(method("POST"))
1379 .and(path_regex(format!("^/_matrix/client/v3/rooms/.*/receipt/{receipt_type}/")));
1380 self.mock_endpoint(mock, ReceiptEndpoint).expect_default_access_token()
1381 }
1382
1383 /// Create a prebuilt mock for the endpoint used to send multiple receipts.
1384 pub fn mock_send_read_markers(&self) -> MockEndpoint<'_, ReadMarkersEndpoint> {
1385 let mock = Mock::given(method("POST"))
1386 .and(path_regex(r"^/_matrix/client/v3/rooms/.*/read_markers"));
1387 self.mock_endpoint(mock, ReadMarkersEndpoint).expect_default_access_token()
1388 }
1389
1390 /// Create a prebuilt mock for the endpoint used to set room account data.
1391 pub fn mock_set_room_account_data(
1392 &self,
1393 data_type: RoomAccountDataEventType,
1394 ) -> MockEndpoint<'_, RoomAccountDataEndpoint> {
1395 let mock = Mock::given(method("PUT")).and(path_regex(format!(
1396 "^/_matrix/client/v3/user/.*/rooms/.*/account_data/{data_type}"
1397 )));
1398 self.mock_endpoint(mock, RoomAccountDataEndpoint).expect_default_access_token()
1399 }
1400
1401 /// Create a prebuilt mock for the endpoint used to get the media config of
1402 /// the homeserver that requires authentication.
1403 pub fn mock_authenticated_media_config(
1404 &self,
1405 ) -> MockEndpoint<'_, AuthenticatedMediaConfigEndpoint> {
1406 let mock = Mock::given(method("GET")).and(path("/_matrix/client/v1/media/config"));
1407 self.mock_endpoint(mock, AuthenticatedMediaConfigEndpoint)
1408 }
1409
1410 /// Create a prebuilt mock for the endpoint used to get the media config of
1411 /// the homeserver without requiring authentication.
1412 pub fn mock_media_config(&self) -> MockEndpoint<'_, MediaConfigEndpoint> {
1413 let mock = Mock::given(method("GET")).and(path("/_matrix/media/v3/config"));
1414 self.mock_endpoint(mock, MediaConfigEndpoint)
1415 }
1416
1417 /// Create a prebuilt mock for the endpoint used to log into a session.
1418 pub fn mock_login(&self) -> MockEndpoint<'_, LoginEndpoint> {
1419 let mock = Mock::given(method("POST")).and(path("/_matrix/client/v3/login"));
1420 self.mock_endpoint(mock, LoginEndpoint)
1421 }
1422
1423 /// Create a prebuilt mock for the endpoint used to list the devices of a
1424 /// user.
1425 pub fn mock_devices(&self) -> MockEndpoint<'_, DevicesEndpoint> {
1426 let mock = Mock::given(method("GET")).and(path("/_matrix/client/v3/devices"));
1427 self.mock_endpoint(mock, DevicesEndpoint).expect_default_access_token()
1428 }
1429
1430 /// Create a prebuilt mock for the endpoint used to search in the user
1431 /// directory.
1432 pub fn mock_user_directory(&self) -> MockEndpoint<'_, UserDirectoryEndpoint> {
1433 let mock = Mock::given(method("POST"))
1434 .and(path("/_matrix/client/v3/user_directory/search"))
1435 .and(body_json(&*test_json::search_users::SEARCH_USERS_REQUEST));
1436 self.mock_endpoint(mock, UserDirectoryEndpoint).expect_default_access_token()
1437 }
1438
1439 /// Create a prebuilt mock for the endpoint used to create a new room.
1440 pub fn mock_create_room(&self) -> MockEndpoint<'_, CreateRoomEndpoint> {
1441 let mock = Mock::given(method("POST")).and(path("/_matrix/client/v3/createRoom"));
1442 self.mock_endpoint(mock, CreateRoomEndpoint).expect_default_access_token()
1443 }
1444
1445 /// Create a prebuilt mock for the endpoint used to upgrade a room.
1446 pub fn mock_upgrade_room(&self) -> MockEndpoint<'_, UpgradeRoomEndpoint> {
1447 let mock =
1448 Mock::given(method("POST")).and(path_regex("/_matrix/client/v3/rooms/.*/upgrade"));
1449 self.mock_endpoint(mock, UpgradeRoomEndpoint).expect_default_access_token()
1450 }
1451
1452 /// Create a prebuilt mock for the endpoint used to pre-allocate a MXC URI
1453 /// for a media file.
1454 pub fn mock_media_allocate(&self) -> MockEndpoint<'_, MediaAllocateEndpoint> {
1455 let mock = Mock::given(method("POST")).and(path("/_matrix/media/v1/create"));
1456 self.mock_endpoint(mock, MediaAllocateEndpoint)
1457 }
1458
1459 /// Create a prebuilt mock for the endpoint used to upload a media file with
1460 /// a pre-allocated MXC URI.
1461 pub fn mock_media_allocated_upload(
1462 &self,
1463 server_name: &str,
1464 media_id: &str,
1465 ) -> MockEndpoint<'_, MediaAllocatedUploadEndpoint> {
1466 let mock = Mock::given(method("PUT"))
1467 .and(path(format!("/_matrix/media/v3/upload/{server_name}/{media_id}")));
1468 self.mock_endpoint(mock, MediaAllocatedUploadEndpoint)
1469 }
1470
1471 /// Create a prebuilt mock for the endpoint used to download a media file
1472 /// without requiring authentication.
1473 pub fn mock_media_download(&self) -> MockEndpoint<'_, MediaDownloadEndpoint> {
1474 let mock = Mock::given(method("GET")).and(path_regex("^/_matrix/media/v3/download/"));
1475 self.mock_endpoint(mock, MediaDownloadEndpoint)
1476 }
1477
1478 /// Create a prebuilt mock for the endpoint used to download a thumbnail of
1479 /// a media file without requiring authentication.
1480 pub fn mock_media_thumbnail(
1481 &self,
1482 resize_method: Method,
1483 width: u16,
1484 height: u16,
1485 animated: bool,
1486 ) -> MockEndpoint<'_, MediaThumbnailEndpoint> {
1487 let mock = Mock::given(method("GET"))
1488 .and(path_regex("^/_matrix/media/v3/thumbnail/"))
1489 .and(query_param("method", resize_method.as_str()))
1490 .and(query_param("width", width.to_string()))
1491 .and(query_param("height", height.to_string()))
1492 .and(query_param("animated", animated.to_string()));
1493 self.mock_endpoint(mock, MediaThumbnailEndpoint)
1494 }
1495
1496 /// Create a prebuilt mock for the endpoint used to download a media file
1497 /// that requires authentication.
1498 pub fn mock_authed_media_download(&self) -> MockEndpoint<'_, AuthedMediaDownloadEndpoint> {
1499 let mock =
1500 Mock::given(method("GET")).and(path_regex("^/_matrix/client/v1/media/download/"));
1501 self.mock_endpoint(mock, AuthedMediaDownloadEndpoint).expect_default_access_token()
1502 }
1503
1504 /// Create a prebuilt mock for the endpoint used to download a thumbnail of
1505 /// a media file that requires authentication.
1506 pub fn mock_authed_media_thumbnail(
1507 &self,
1508 resize_method: Method,
1509 width: u16,
1510 height: u16,
1511 animated: bool,
1512 ) -> MockEndpoint<'_, AuthedMediaThumbnailEndpoint> {
1513 let mock = Mock::given(method("GET"))
1514 .and(path_regex("^/_matrix/client/v1/media/thumbnail/"))
1515 .and(query_param("method", resize_method.as_str()))
1516 .and(query_param("width", width.to_string()))
1517 .and(query_param("height", height.to_string()))
1518 .and(query_param("animated", animated.to_string()));
1519 self.mock_endpoint(mock, AuthedMediaThumbnailEndpoint).expect_default_access_token()
1520 }
1521
1522 /// Create a prebuilt mock for the endpoint used to get a single thread
1523 /// subscription status in a given room.
1524 pub fn mock_room_get_thread_subscription(
1525 &self,
1526 ) -> MockEndpoint<'_, RoomGetThreadSubscriptionEndpoint> {
1527 let mock = Mock::given(method("GET"));
1528 self.mock_endpoint(mock, RoomGetThreadSubscriptionEndpoint::default())
1529 .expect_default_access_token()
1530 }
1531
1532 /// Create a prebuilt mock for the endpoint used to define a thread
1533 /// subscription in a given room.
1534 pub fn mock_room_put_thread_subscription(
1535 &self,
1536 ) -> MockEndpoint<'_, RoomPutThreadSubscriptionEndpoint> {
1537 let mock = Mock::given(method("PUT"));
1538 self.mock_endpoint(mock, RoomPutThreadSubscriptionEndpoint::default())
1539 .expect_default_access_token()
1540 }
1541
1542 /// Create a prebuilt mock for the endpoint used to delete a thread
1543 /// subscription in a given room.
1544 pub fn mock_room_delete_thread_subscription(
1545 &self,
1546 ) -> MockEndpoint<'_, RoomDeleteThreadSubscriptionEndpoint> {
1547 let mock = Mock::given(method("DELETE"));
1548 self.mock_endpoint(mock, RoomDeleteThreadSubscriptionEndpoint::default())
1549 .expect_default_access_token()
1550 }
1551
1552 /// Create a prebuilt mock for the endpoint used to enable a push rule.
1553 pub fn mock_enable_push_rule(
1554 &self,
1555 kind: RuleKind,
1556 rule_id: impl AsRef<str>,
1557 ) -> MockEndpoint<'_, EnablePushRuleEndpoint> {
1558 let rule_id = rule_id.as_ref();
1559 let mock = Mock::given(method("PUT")).and(path_regex(format!(
1560 "^/_matrix/client/v3/pushrules/global/{kind}/{rule_id}/enabled",
1561 )));
1562 self.mock_endpoint(mock, EnablePushRuleEndpoint).expect_default_access_token()
1563 }
1564
1565 /// Create a prebuilt mock for the endpoint used to set push rules actions.
1566 pub fn mock_set_push_rules_actions(
1567 &self,
1568 kind: RuleKind,
1569 rule_id: PushRuleIdSpec<'_>,
1570 ) -> MockEndpoint<'_, SetPushRulesActionsEndpoint> {
1571 let rule_id = rule_id.to_path();
1572 let mock = Mock::given(method("PUT")).and(path_regex(format!(
1573 "^/_matrix/client/v3/pushrules/global/{kind}/{rule_id}/actions",
1574 )));
1575 self.mock_endpoint(mock, SetPushRulesActionsEndpoint).expect_default_access_token()
1576 }
1577
1578 /// Create a prebuilt mock for the endpoint used to set push rules.
1579 pub fn mock_set_push_rules(
1580 &self,
1581 kind: RuleKind,
1582 rule_id: PushRuleIdSpec<'_>,
1583 ) -> MockEndpoint<'_, SetPushRulesEndpoint> {
1584 let rule_id = rule_id.to_path();
1585 let mock = Mock::given(method("PUT"))
1586 .and(path_regex(format!("^/_matrix/client/v3/pushrules/global/{kind}/{rule_id}$",)));
1587 self.mock_endpoint(mock, SetPushRulesEndpoint).expect_default_access_token()
1588 }
1589
1590 /// Create a prebuilt mock for the endpoint used to delete push rules.
1591 pub fn mock_delete_push_rules(
1592 &self,
1593 kind: RuleKind,
1594 rule_id: PushRuleIdSpec<'_>,
1595 ) -> MockEndpoint<'_, DeletePushRulesEndpoint> {
1596 let rule_id = rule_id.to_path();
1597 let mock = Mock::given(method("DELETE"))
1598 .and(path_regex(format!("^/_matrix/client/v3/pushrules/global/{kind}/{rule_id}$",)));
1599 self.mock_endpoint(mock, DeletePushRulesEndpoint).expect_default_access_token()
1600 }
1601
1602 /// Create a prebuilt mock for the federation version endpoint.
1603 pub fn mock_federation_version(&self) -> MockEndpoint<'_, FederationVersionEndpoint> {
1604 let mock = Mock::given(method("GET")).and(path("/_matrix/federation/v1/version"));
1605 self.mock_endpoint(mock, FederationVersionEndpoint)
1606 }
1607
1608 /// Create a prebuilt mock for the endpoint used to get all thread
1609 /// subscriptions across all rooms.
1610 pub fn mock_get_thread_subscriptions(
1611 &self,
1612 ) -> MockEndpoint<'_, GetThreadSubscriptionsEndpoint> {
1613 let mock = Mock::given(method("GET"))
1614 .and(path_regex(r"^/_matrix/client/unstable/io.element.msc4308/thread_subscriptions$"));
1615 self.mock_endpoint(mock, GetThreadSubscriptionsEndpoint::default())
1616 .expect_default_access_token()
1617 }
1618
1619 /// Create a prebuilt mock for the endpoint used to retrieve a space tree
1620 pub fn mock_get_hierarchy(&self) -> MockEndpoint<'_, GetHierarchyEndpoint> {
1621 let mock =
1622 Mock::given(method("GET")).and(path_regex(r"^/_matrix/client/v1/rooms/.*/hierarchy"));
1623 self.mock_endpoint(mock, GetHierarchyEndpoint).expect_default_access_token()
1624 }
1625}
1626
1627/// A specification for a push rule ID.
1628pub enum PushRuleIdSpec<'a> {
1629 /// A precise rule ID.
1630 Some(&'a str),
1631 /// Any rule ID should match.
1632 Any,
1633}
1634
1635impl<'a> PushRuleIdSpec<'a> {
1636 /// Convert this [`PushRuleIdSpec`] to a path.
1637 pub fn to_path(&self) -> &str {
1638 match self {
1639 PushRuleIdSpec::Some(id) => id,
1640 PushRuleIdSpec::Any => "[^/]*",
1641 }
1642 }
1643}
1644
1645/// Parameter to [`MatrixMockServer::sync_room`].
1646pub enum AnyRoomBuilder {
1647 /// A room we've been invited to.
1648 Invited(InvitedRoomBuilder),
1649 /// A room we've joined.
1650 Joined(JoinedRoomBuilder),
1651 /// A room we've left.
1652 Left(LeftRoomBuilder),
1653 /// A room we've knocked to.
1654 Knocked(KnockedRoomBuilder),
1655}
1656
1657impl AnyRoomBuilder {
1658 /// Get the [`RoomId`] of the room this [`AnyRoomBuilder`] will create.
1659 fn room_id(&self) -> &RoomId {
1660 match self {
1661 AnyRoomBuilder::Invited(r) => r.room_id(),
1662 AnyRoomBuilder::Joined(r) => r.room_id(),
1663 AnyRoomBuilder::Left(r) => r.room_id(),
1664 AnyRoomBuilder::Knocked(r) => r.room_id(),
1665 }
1666 }
1667}
1668
1669impl From<InvitedRoomBuilder> for AnyRoomBuilder {
1670 fn from(val: InvitedRoomBuilder) -> AnyRoomBuilder {
1671 AnyRoomBuilder::Invited(val)
1672 }
1673}
1674
1675impl From<JoinedRoomBuilder> for AnyRoomBuilder {
1676 fn from(val: JoinedRoomBuilder) -> AnyRoomBuilder {
1677 AnyRoomBuilder::Joined(val)
1678 }
1679}
1680
1681impl From<LeftRoomBuilder> for AnyRoomBuilder {
1682 fn from(val: LeftRoomBuilder) -> AnyRoomBuilder {
1683 AnyRoomBuilder::Left(val)
1684 }
1685}
1686
1687impl From<KnockedRoomBuilder> for AnyRoomBuilder {
1688 fn from(val: KnockedRoomBuilder) -> AnyRoomBuilder {
1689 AnyRoomBuilder::Knocked(val)
1690 }
1691}
1692
1693/// The [path percent-encode set] as defined in the WHATWG URL standard + `/`
1694/// since we always encode single segments of the path.
1695///
1696/// [path percent-encode set]: https://url.spec.whatwg.org/#path-percent-encode-set
1697///
1698/// Copied from Ruma:
1699/// https://github.com/ruma/ruma/blob/e4cb409ff3aaa16f31a7fe1e61fee43b2d144f7b/crates/ruma-common/src/percent_encode.rs#L7
1700const PATH_PERCENT_ENCODE_SET: &AsciiSet = &CONTROLS
1701 .add(b' ')
1702 .add(b'"')
1703 .add(b'#')
1704 .add(b'<')
1705 .add(b'>')
1706 .add(b'?')
1707 .add(b'`')
1708 .add(b'{')
1709 .add(b'}')
1710 .add(b'/');
1711
1712fn percent_encoded_path(path: &str) -> String {
1713 percent_encoding::utf8_percent_encode(path, PATH_PERCENT_ENCODE_SET).to_string()
1714}
1715
1716/// A wrapper for a [`Mock`] as well as a [`MockServer`], allowing us to call
1717/// [`Mock::mount`] or [`Mock::mount_as_scoped`] without having to pass the
1718/// [`MockServer`] reference (i.e. call `mount()` instead of `mount(&server)`).
1719pub struct MatrixMock<'a> {
1720 pub(super) mock: Mock,
1721 pub(super) server: &'a MockServer,
1722}
1723
1724impl MatrixMock<'_> {
1725 /// Set an expectation on the number of times this [`MatrixMock`] should
1726 /// match in the current test case.
1727 ///
1728 /// Expectations are verified when the server is shutting down: if
1729 /// the expectation is not satisfied, the [`MatrixMockServer`] will panic
1730 /// and the `error_message` is shown.
1731 ///
1732 /// By default, no expectation is set for [`MatrixMock`]s.
1733 pub fn expect<T: Into<Times>>(self, num_calls: T) -> Self {
1734 Self { mock: self.mock.expect(num_calls), ..self }
1735 }
1736
1737 /// Assign a name to your mock.
1738 ///
1739 /// The mock name will be used in error messages (e.g. if the mock
1740 /// expectation is not satisfied) and debug logs to help you identify
1741 /// what failed.
1742 pub fn named(self, name: impl Into<String>) -> Self {
1743 Self { mock: self.mock.named(name), ..self }
1744 }
1745
1746 /// Respond to a response of this endpoint exactly once.
1747 ///
1748 /// After it's been called, subsequent responses will hit the next handler
1749 /// or a 404.
1750 ///
1751 /// Also verifies that it's been called once.
1752 pub fn mock_once(self) -> Self {
1753 Self { mock: self.mock.up_to_n_times(1).expect(1), ..self }
1754 }
1755
1756 /// Makes sure the endpoint is never reached.
1757 pub fn never(self) -> Self {
1758 Self { mock: self.mock.expect(0), ..self }
1759 }
1760
1761 /// Specify an upper limit to the number of times you would like this
1762 /// [`MatrixMock`] to respond to incoming requests that satisfy the
1763 /// conditions imposed by your matchers.
1764 pub fn up_to_n_times(self, num: u64) -> Self {
1765 Self { mock: self.mock.up_to_n_times(num), ..self }
1766 }
1767
1768 /// Mount a [`MatrixMock`] on the attached server.
1769 ///
1770 /// The [`MatrixMock`] will remain active until the [`MatrixMockServer`] is
1771 /// shut down. If you want to control or limit how long your
1772 /// [`MatrixMock`] stays active, check out [`Self::mount_as_scoped`].
1773 pub async fn mount(self) {
1774 self.mock.mount(self.server).await;
1775 }
1776
1777 /// Mount a [`MatrixMock`] as **scoped** on the attached server.
1778 ///
1779 /// When using [`Self::mount`], your [`MatrixMock`]s will be active until
1780 /// the [`MatrixMockServer`] is shut down.
1781 ///
1782 /// When using `mount_as_scoped`, your [`MatrixMock`]s will be active as
1783 /// long as the returned [`MockGuard`] is not dropped.
1784 ///
1785 /// When the returned [`MockGuard`] is dropped, [`MatrixMockServer`] will
1786 /// verify that the expectations set on the scoped [`MatrixMock`] were
1787 /// verified - if not, it will panic.
1788 pub async fn mount_as_scoped(self) -> MockGuard {
1789 self.mock.mount_as_scoped(self.server).await
1790 }
1791}
1792
1793/// Generic mocked endpoint, with useful common helpers.
1794pub struct MockEndpoint<'a, T> {
1795 server: &'a MockServer,
1796 mock: MockBuilder,
1797 endpoint: T,
1798 expected_access_token: ExpectedAccessToken,
1799}
1800
1801impl<'a, T> MockEndpoint<'a, T> {
1802 fn new(server: &'a MockServer, mock: MockBuilder, endpoint: T) -> Self {
1803 Self { server, mock, endpoint, expected_access_token: ExpectedAccessToken::None }
1804 }
1805
1806 /// Expect authentication with the default access token on this endpoint.
1807 pub fn expect_default_access_token(mut self) -> Self {
1808 self.expected_access_token = ExpectedAccessToken::Default;
1809 self
1810 }
1811
1812 /// Expect authentication with the given access token on this endpoint.
1813 pub fn expect_access_token(mut self, access_token: &'static str) -> Self {
1814 self.expected_access_token = ExpectedAccessToken::Custom(access_token);
1815 self
1816 }
1817
1818 /// Don't expect authentication with an access token on this endpoint.
1819 ///
1820 /// This should be used to override the default behavior of the endpoint,
1821 /// when the access token is unknown for example.
1822 pub fn do_not_expect_access_token(mut self) -> Self {
1823 self.expected_access_token = ExpectedAccessToken::None;
1824 self
1825 }
1826
1827 /// Specify how to respond to a query (viz., like
1828 /// [`MockBuilder::respond_with`] does), when other predefined responses
1829 /// aren't sufficient.
1830 ///
1831 /// # Examples
1832 ///
1833 /// ```
1834 /// # tokio_test::block_on(async {
1835 /// use matrix_sdk::{ruma::{room_id, event_id}, test_utils::mocks::MatrixMockServer};
1836 /// use serde_json::json;
1837 /// use wiremock::ResponseTemplate;
1838 ///
1839 /// let mock_server = MatrixMockServer::new().await;
1840 /// let client = mock_server.client_builder().build().await;
1841 ///
1842 /// mock_server.mock_room_state_encryption().plain().mount().await;
1843 ///
1844 /// let room = mock_server
1845 /// .sync_joined_room(&client, room_id!("!room_id:localhost"))
1846 /// .await;
1847 ///
1848 /// let event_id = event_id!("$some_id");
1849 /// mock_server
1850 /// .mock_room_send()
1851 /// .respond_with(
1852 /// ResponseTemplate::new(429)
1853 /// .insert_header("Retry-After", "100")
1854 /// .set_body_json(json!({
1855 /// "errcode": "M_LIMIT_EXCEEDED",
1856 /// "custom_field": "with custom data",
1857 /// })))
1858 /// .expect(1)
1859 /// .mount()
1860 /// .await;
1861 ///
1862 /// room
1863 /// .send_raw("m.room.message", json!({ "body": "Hello world" }))
1864 /// .await
1865 /// .expect_err("The sending of the event should fail");
1866 /// # anyhow::Ok(()) });
1867 /// ```
1868 pub fn respond_with<R: Respond + 'static>(self, func: R) -> MatrixMock<'a> {
1869 let mock = self
1870 .expected_access_token
1871 .maybe_match_authorization_header(self.mock)
1872 .respond_with(func);
1873 MatrixMock { mock, server: self.server }
1874 }
1875
1876 /// Returns a send endpoint that emulates a transient failure, i.e responds
1877 /// with error 500.
1878 ///
1879 /// # Examples
1880 /// ```
1881 /// # tokio_test::block_on(async {
1882 /// use matrix_sdk::{ruma::{room_id, event_id}, test_utils::mocks::MatrixMockServer};
1883 /// use serde_json::json;
1884 ///
1885 /// let mock_server = MatrixMockServer::new().await;
1886 /// let client = mock_server.client_builder().build().await;
1887 ///
1888 /// mock_server.mock_room_state_encryption().plain().mount().await;
1889 ///
1890 /// let room = mock_server
1891 /// .sync_joined_room(&client, room_id!("!room_id:localhost"))
1892 /// .await;
1893 ///
1894 /// mock_server
1895 /// .mock_room_send()
1896 /// .error500()
1897 /// .expect(1)
1898 /// .mount()
1899 /// .await;
1900 ///
1901 /// room
1902 /// .send_raw("m.room.message", json!({ "body": "Hello world" }))
1903 /// .await.expect_err("The sending of the event should have failed");
1904 /// # anyhow::Ok(()) });
1905 /// ```
1906 pub fn error500(self) -> MatrixMock<'a> {
1907 self.respond_with(ResponseTemplate::new(500))
1908 }
1909
1910 /// Internal helper to return an `{ event_id }` JSON struct along with a 200
1911 /// ok response.
1912 fn ok_with_event_id(self, event_id: OwnedEventId) -> MatrixMock<'a> {
1913 self.respond_with(ResponseTemplate::new(200).set_body_json(json!({ "event_id": event_id })))
1914 }
1915
1916 /// Internal helper to return a 200 OK response with an empty JSON object in
1917 /// the body.
1918 fn ok_empty_json(self) -> MatrixMock<'a> {
1919 self.respond_with(ResponseTemplate::new(200).set_body_json(json!({})))
1920 }
1921
1922 /// Returns an endpoint that emulates a permanent failure error (e.g. event
1923 /// is too large).
1924 ///
1925 /// # Examples
1926 /// ```
1927 /// # tokio_test::block_on(async {
1928 /// use matrix_sdk::{ruma::{room_id, event_id}, test_utils::mocks::MatrixMockServer};
1929 /// use serde_json::json;
1930 ///
1931 /// let mock_server = MatrixMockServer::new().await;
1932 /// let client = mock_server.client_builder().build().await;
1933 ///
1934 /// mock_server.mock_room_state_encryption().plain().mount().await;
1935 ///
1936 /// let room = mock_server
1937 /// .sync_joined_room(&client, room_id!("!room_id:localhost"))
1938 /// .await;
1939 ///
1940 /// mock_server
1941 /// .mock_room_send()
1942 /// .error_too_large()
1943 /// .expect(1)
1944 /// .mount()
1945 /// .await;
1946 ///
1947 /// room
1948 /// .send_raw("m.room.message", json!({ "body": "Hello world" }))
1949 /// .await.expect_err("The sending of the event should have failed");
1950 /// # anyhow::Ok(()) });
1951 /// ```
1952 pub fn error_too_large(self) -> MatrixMock<'a> {
1953 self.respond_with(ResponseTemplate::new(413).set_body_json(json!({
1954 // From https://spec.matrix.org/v1.10/client-server-api/#standard-error-response
1955 "errcode": "M_TOO_LARGE",
1956 })))
1957 }
1958}
1959
1960/// The access token to expect on an endpoint.
1961enum ExpectedAccessToken {
1962 /// We don't expect an access token.
1963 None,
1964
1965 /// We expect the default access token.
1966 Default,
1967
1968 /// We expect the given access token.
1969 Custom(&'static str),
1970}
1971
1972impl ExpectedAccessToken {
1973 /// Match an `Authorization` header on the given mock if one is expected.
1974 fn maybe_match_authorization_header(&self, mock: MockBuilder) -> MockBuilder {
1975 let token = match self {
1976 Self::None => return mock,
1977 Self::Default => "1234",
1978 Self::Custom(token) => token,
1979 };
1980 mock.and(header(http::header::AUTHORIZATION, format!("Bearer {token}")))
1981 }
1982}
1983
1984/// A prebuilt mock for sending a message like event in a room.
1985pub struct RoomSendEndpoint;
1986
1987impl<'a> MockEndpoint<'a, RoomSendEndpoint> {
1988 /// Ensures that the body of the request is a superset of the provided
1989 /// `body` parameter.
1990 ///
1991 /// # Examples
1992 /// ```
1993 /// # tokio_test::block_on(async {
1994 /// use matrix_sdk::{
1995 /// ruma::{room_id, event_id, events::room::message::RoomMessageEventContent},
1996 /// test_utils::mocks::MatrixMockServer
1997 /// };
1998 /// use serde_json::json;
1999 ///
2000 /// let mock_server = MatrixMockServer::new().await;
2001 /// let client = mock_server.client_builder().build().await;
2002 ///
2003 /// mock_server.mock_room_state_encryption().plain().mount().await;
2004 ///
2005 /// let room = mock_server
2006 /// .sync_joined_room(&client, room_id!("!room_id:localhost"))
2007 /// .await;
2008 ///
2009 /// let event_id = event_id!("$some_id");
2010 /// mock_server
2011 /// .mock_room_send()
2012 /// .body_matches_partial_json(json!({
2013 /// "body": "Hello world",
2014 /// }))
2015 /// .ok(event_id)
2016 /// .expect(1)
2017 /// .mount()
2018 /// .await;
2019 ///
2020 /// let content = RoomMessageEventContent::text_plain("Hello world");
2021 /// let response = room.send(content).await?;
2022 ///
2023 /// assert_eq!(
2024 /// event_id,
2025 /// response.event_id,
2026 /// "The event ID we mocked should match the one we received when we sent the event"
2027 /// );
2028 /// # anyhow::Ok(()) });
2029 /// ```
2030 pub fn body_matches_partial_json(self, body: Value) -> Self {
2031 Self { mock: self.mock.and(body_partial_json(body)), ..self }
2032 }
2033
2034 /// Ensures that the send endpoint request uses a specific event type.
2035 ///
2036 /// # Examples
2037 ///
2038 /// see also [`MatrixMockServer::mock_room_send`] for more context.
2039 ///
2040 /// ```
2041 /// # tokio_test::block_on(async {
2042 /// use matrix_sdk::{ruma::{room_id, event_id}, test_utils::mocks::MatrixMockServer};
2043 /// use serde_json::json;
2044 ///
2045 /// let mock_server = MatrixMockServer::new().await;
2046 /// let client = mock_server.client_builder().build().await;
2047 ///
2048 /// mock_server.mock_room_state_encryption().plain().mount().await;
2049 ///
2050 /// let room = mock_server
2051 /// .sync_joined_room(&client, room_id!("!room_id:localhost"))
2052 /// .await;
2053 ///
2054 /// let event_id = event_id!("$some_id");
2055 /// mock_server
2056 /// .mock_room_send()
2057 /// .for_type("m.room.message".into())
2058 /// .ok(event_id)
2059 /// .expect(1)
2060 /// .mount()
2061 /// .await;
2062 ///
2063 /// let response_not_mocked = room.send_raw("m.room.reaction", json!({ "body": "Hello world" })).await;
2064 /// // The `m.room.reaction` event type should not be mocked by the server.
2065 /// assert!(response_not_mocked.is_err());
2066 ///
2067 /// let response = room.send_raw("m.room.message", json!({ "body": "Hello world" })).await?;
2068 /// // The `m.room.message` event type should be mocked by the server.
2069 /// assert_eq!(
2070 /// event_id,
2071 /// response.event_id,
2072 /// "The event ID we mocked should match the one we received when we sent the event"
2073 /// );
2074 /// # anyhow::Ok(()) });
2075 /// ```
2076 pub fn for_type(self, event_type: MessageLikeEventType) -> Self {
2077 Self {
2078 // Note: we already defined a path when constructing the mock builder, but this one
2079 // ought to be more specialized.
2080 mock: self
2081 .mock
2082 .and(path_regex(format!(r"^/_matrix/client/v3/rooms/.*/send/{event_type}",))),
2083 ..self
2084 }
2085 }
2086
2087 /// Ensures the event was sent as a delayed event.
2088 ///
2089 /// See also [the MSC](https://github.com/matrix-org/matrix-spec-proposals/pull/4140).
2090 ///
2091 /// Note: works with *any* room.
2092 ///
2093 /// # Examples
2094 ///
2095 /// see also [`MatrixMockServer::mock_room_send`] for more context.
2096 ///
2097 /// ```
2098 /// # tokio_test::block_on(async {
2099 /// use matrix_sdk::{
2100 /// ruma::{
2101 /// api::client::delayed_events::{delayed_message_event, DelayParameters},
2102 /// events::{message::MessageEventContent, AnyMessageLikeEventContent},
2103 /// room_id,
2104 /// time::Duration,
2105 /// TransactionId,
2106 /// },
2107 /// test_utils::mocks::MatrixMockServer,
2108 /// };
2109 /// use serde_json::json;
2110 /// use wiremock::ResponseTemplate;
2111 ///
2112 /// let mock_server = MatrixMockServer::new().await;
2113 /// let client = mock_server.client_builder().build().await;
2114 ///
2115 /// mock_server.mock_room_state_encryption().plain().mount().await;
2116 ///
2117 /// let room = mock_server.sync_joined_room(&client, room_id!("!room_id:localhost")).await;
2118 ///
2119 /// mock_server
2120 /// .mock_room_send()
2121 /// .match_delayed_event(Duration::from_millis(500))
2122 /// .respond_with(ResponseTemplate::new(200).set_body_json(json!({"delay_id":"$some_id"})))
2123 /// .mock_once()
2124 /// .mount()
2125 /// .await;
2126 ///
2127 /// let response_not_mocked =
2128 /// room.send_raw("m.room.message", json!({ "body": "Hello world" })).await;
2129 ///
2130 /// // A non delayed event should not be mocked by the server.
2131 /// assert!(response_not_mocked.is_err());
2132 ///
2133 /// let r = delayed_message_event::unstable::Request::new(
2134 /// room.room_id().to_owned(),
2135 /// TransactionId::new(),
2136 /// DelayParameters::Timeout { timeout: Duration::from_millis(500) },
2137 /// &AnyMessageLikeEventContent::Message(MessageEventContent::plain("hello world")),
2138 /// )
2139 /// .unwrap();
2140 ///
2141 /// let response = room.client().send(r).await.unwrap();
2142 /// // The delayed `m.room.message` event type should be mocked by the server.
2143 /// assert_eq!("$some_id", response.delay_id);
2144 /// # anyhow::Ok(()) });
2145 /// ```
2146 pub fn match_delayed_event(self, delay: Duration) -> Self {
2147 Self {
2148 mock: self
2149 .mock
2150 .and(query_param("org.matrix.msc4140.delay", delay.as_millis().to_string())),
2151 ..self
2152 }
2153 }
2154
2155 /// Returns a send endpoint that emulates success, i.e. the event has been
2156 /// sent with the given event id.
2157 ///
2158 /// # Examples
2159 /// ```
2160 /// # tokio_test::block_on(async {
2161 /// use matrix_sdk::{ruma::{room_id, event_id}, test_utils::mocks::MatrixMockServer};
2162 /// use serde_json::json;
2163 ///
2164 /// let mock_server = MatrixMockServer::new().await;
2165 /// let client = mock_server.client_builder().build().await;
2166 ///
2167 /// mock_server.mock_room_state_encryption().plain().mount().await;
2168 ///
2169 /// let room = mock_server
2170 /// .sync_joined_room(&client, room_id!("!room_id:localhost"))
2171 /// .await;
2172 ///
2173 /// let event_id = event_id!("$some_id");
2174 /// let send_guard = mock_server
2175 /// .mock_room_send()
2176 /// .ok(event_id)
2177 /// .expect(1)
2178 /// .mount_as_scoped()
2179 /// .await;
2180 ///
2181 /// let response = room.send_raw("m.room.message", json!({ "body": "Hello world" })).await?;
2182 ///
2183 /// assert_eq!(
2184 /// event_id,
2185 /// response.event_id,
2186 /// "The event ID we mocked should match the one we received when we sent the event"
2187 /// );
2188 /// # anyhow::Ok(()) });
2189 /// ```
2190 pub fn ok(self, returned_event_id: impl Into<OwnedEventId>) -> MatrixMock<'a> {
2191 self.ok_with_event_id(returned_event_id.into())
2192 }
2193
2194 /// Returns a send endpoint that emulates success, i.e. the event has been
2195 /// sent with the given event id.
2196 ///
2197 /// The sent event is captured and can be accessed using the returned
2198 /// [`Receiver`]. The [`Receiver`] is valid only for a send call. The given
2199 /// `event_sender` are added to the event JSON.
2200 ///
2201 /// # Examples
2202 ///
2203 /// ```no_run
2204 /// # tokio_test::block_on(async {
2205 /// use matrix_sdk::{
2206 /// ruma::{
2207 /// event_id, events::room::message::RoomMessageEventContent, room_id,
2208 /// },
2209 /// test_utils::mocks::MatrixMockServer,
2210 /// };
2211 /// use matrix_sdk_test::JoinedRoomBuilder;
2212 ///
2213 /// let room_id = room_id!("!room_id:localhost");
2214 /// let event_id = event_id!("$some_id");
2215 ///
2216 /// let server = MatrixMockServer::new().await;
2217 /// let client = server.client_builder().build().await;
2218 ///
2219 /// let user_id = client.user_id().expect("We should have a user ID by now");
2220 ///
2221 /// let (receiver, mock) =
2222 /// server.mock_room_send().ok_with_capture(event_id, user_id);
2223 ///
2224 /// server
2225 /// .mock_sync()
2226 /// .ok_and_run(&client, |builder| {
2227 /// builder.add_joined_room(JoinedRoomBuilder::new(room_id));
2228 /// })
2229 /// .await;
2230 ///
2231 /// // Mock any additional endpoints that might be needed to send the message.
2232 ///
2233 /// let room = client
2234 /// .get_room(room_id)
2235 /// .expect("We should have access to our room now");
2236 ///
2237 /// let event_id = room
2238 /// .send(RoomMessageEventContent::text_plain("It's a secret to everybody"))
2239 /// .await
2240 /// .expect("We should be able to send an initial message")
2241 /// .event_id;
2242 ///
2243 /// let event = receiver.await?;
2244 /// # anyhow::Ok(()) });
2245 /// ```
2246 pub fn ok_with_capture(
2247 self,
2248 returned_event_id: impl Into<OwnedEventId>,
2249 event_sender: impl Into<OwnedUserId>,
2250 ) -> (Receiver<Raw<AnySyncTimelineEvent>>, MatrixMock<'a>) {
2251 let event_id = returned_event_id.into();
2252 let event_sender = event_sender.into();
2253
2254 let (sender, receiver) = oneshot::channel();
2255 let sender = Arc::new(Mutex::new(Some(sender)));
2256
2257 let ret = self.respond_with(move |request: &Request| {
2258 if let Some(sender) = sender.lock().unwrap().take() {
2259 let uri = &request.url;
2260 let path_segments = uri.path_segments();
2261 let maybe_event_type = path_segments.and_then(|mut s| s.nth_back(1));
2262 let event_type = maybe_event_type
2263 .as_ref()
2264 .map(|&e| e.to_owned())
2265 .unwrap_or("m.room.message".to_owned());
2266
2267 let body: Value =
2268 request.body_json().expect("The received body should be valid JSON");
2269
2270 let event = json!({
2271 "event_id": event_id.clone(),
2272 "sender": event_sender,
2273 "type": event_type,
2274 "origin_server_ts": MilliSecondsSinceUnixEpoch::now(),
2275 "content": body,
2276 });
2277
2278 let event: Raw<AnySyncTimelineEvent> = from_value(event)
2279 .expect("We should be able to create a raw event from the content");
2280
2281 sender.send(event).expect("We should be able to send the event to the receiver");
2282 }
2283
2284 ResponseTemplate::new(200).set_body_json(json!({ "event_id": event_id.clone() }))
2285 });
2286
2287 (receiver, ret)
2288 }
2289}
2290
2291/// A prebuilt mock for sending a state event in a room.
2292#[derive(Default)]
2293pub struct RoomSendStateEndpoint {
2294 state_key: Option<String>,
2295 event_type: Option<StateEventType>,
2296}
2297
2298impl<'a> MockEndpoint<'a, RoomSendStateEndpoint> {
2299 fn generate_path_regexp(endpoint: &RoomSendStateEndpoint) -> String {
2300 format!(
2301 r"^/_matrix/client/v3/rooms/.*/state/{}/{}",
2302 endpoint.event_type.as_ref().map_or_else(|| ".*".to_owned(), |t| t.to_string()),
2303 endpoint.state_key.as_ref().map_or_else(|| ".*".to_owned(), |k| k.to_string())
2304 )
2305 }
2306
2307 /// Ensures that the body of the request is a superset of the provided
2308 /// `body` parameter.
2309 ///
2310 /// # Examples
2311 /// ```
2312 /// # tokio_test::block_on(async {
2313 /// use matrix_sdk::{
2314 /// ruma::{
2315 /// room_id, event_id,
2316 /// events::room::power_levels::RoomPowerLevelsEventContent,
2317 /// room_version_rules::AuthorizationRules
2318 /// },
2319 /// test_utils::mocks::MatrixMockServer
2320 /// };
2321 /// use serde_json::json;
2322 ///
2323 /// let mock_server = MatrixMockServer::new().await;
2324 /// let client = mock_server.client_builder().build().await;
2325 ///
2326 /// mock_server.mock_room_state_encryption().plain().mount().await;
2327 ///
2328 /// let room = mock_server
2329 /// .sync_joined_room(&client, room_id!("!room_id:localhost"))
2330 /// .await;
2331 ///
2332 /// let event_id = event_id!("$some_id");
2333 /// mock_server
2334 /// .mock_room_send_state()
2335 /// .body_matches_partial_json(json!({
2336 /// "redact": 51,
2337 /// }))
2338 /// .ok(event_id)
2339 /// .expect(1)
2340 /// .mount()
2341 /// .await;
2342 ///
2343 /// let mut content = RoomPowerLevelsEventContent::new(&AuthorizationRules::V1);
2344 /// // Update the power level to a non default value.
2345 /// // Otherwise it will be skipped from serialization.
2346 /// content.redact = 51.into();
2347 ///
2348 /// let response = room.send_state_event(content).await?;
2349 ///
2350 /// assert_eq!(
2351 /// event_id,
2352 /// response.event_id,
2353 /// "The event ID we mocked should match the one we received when we sent the event"
2354 /// );
2355 /// # anyhow::Ok(()) });
2356 /// ```
2357 pub fn body_matches_partial_json(self, body: Value) -> Self {
2358 Self { mock: self.mock.and(body_partial_json(body)), ..self }
2359 }
2360
2361 /// Ensures that the send endpoint request uses a specific event type.
2362 ///
2363 /// Note: works with *any* room.
2364 ///
2365 /// # Examples
2366 ///
2367 /// see also [`MatrixMockServer::mock_room_send`] for more context.
2368 ///
2369 /// ```
2370 /// # tokio_test::block_on(async {
2371 /// use matrix_sdk::{
2372 /// ruma::{
2373 /// event_id,
2374 /// events::room::{
2375 /// create::RoomCreateEventContent, power_levels::RoomPowerLevelsEventContent,
2376 /// },
2377 /// events::StateEventType,
2378 /// room_id,
2379 /// room_version_rules::AuthorizationRules,
2380 /// },
2381 /// test_utils::mocks::MatrixMockServer,
2382 /// };
2383 ///
2384 /// let mock_server = MatrixMockServer::new().await;
2385 /// let client = mock_server.client_builder().build().await;
2386 ///
2387 /// mock_server.mock_room_state_encryption().plain().mount().await;
2388 ///
2389 /// let room = mock_server.sync_joined_room(&client, room_id!("!room_id:localhost")).await;
2390 ///
2391 /// let event_id = event_id!("$some_id");
2392 ///
2393 /// mock_server
2394 /// .mock_room_send_state()
2395 /// .for_type(StateEventType::RoomPowerLevels)
2396 /// .ok(event_id)
2397 /// .expect(1)
2398 /// .mount()
2399 /// .await;
2400 ///
2401 /// let response_not_mocked = room.send_state_event(RoomCreateEventContent::new_v11()).await;
2402 /// // The `m.room.reaction` event type should not be mocked by the server.
2403 /// assert!(response_not_mocked.is_err());
2404 ///
2405 /// let response = room.send_state_event(RoomPowerLevelsEventContent::new(&AuthorizationRules::V1)).await?;
2406 /// // The `m.room.message` event type should be mocked by the server.
2407 /// assert_eq!(
2408 /// event_id, response.event_id,
2409 /// "The event ID we mocked should match the one we received when we sent the event"
2410 /// );
2411 ///
2412 /// # anyhow::Ok(()) });
2413 /// ```
2414 pub fn for_type(mut self, event_type: StateEventType) -> Self {
2415 self.endpoint.event_type = Some(event_type);
2416 // Note: we may have already defined a path, but this one ought to be more
2417 // specialized (unless for_key/for_type were called multiple times).
2418 Self { mock: self.mock.and(path_regex(Self::generate_path_regexp(&self.endpoint))), ..self }
2419 }
2420
2421 /// Ensures the event was sent as a delayed event.
2422 ///
2423 /// See also [the MSC](https://github.com/matrix-org/matrix-spec-proposals/pull/4140).
2424 ///
2425 /// Note: works with *any* room.
2426 ///
2427 /// # Examples
2428 ///
2429 /// see also [`MatrixMockServer::mock_room_send`] for more context.
2430 ///
2431 /// ```
2432 /// # tokio_test::block_on(async {
2433 /// use matrix_sdk::{
2434 /// ruma::{
2435 /// api::client::delayed_events::{delayed_state_event, DelayParameters},
2436 /// events::{room::create::RoomCreateEventContent, AnyStateEventContent},
2437 /// room_id,
2438 /// time::Duration,
2439 /// },
2440 /// test_utils::mocks::MatrixMockServer,
2441 /// };
2442 /// use wiremock::ResponseTemplate;
2443 /// use serde_json::json;
2444 ///
2445 /// let mock_server = MatrixMockServer::new().await;
2446 /// let client = mock_server.client_builder().build().await;
2447 ///
2448 /// mock_server.mock_room_state_encryption().plain().mount().await;
2449 ///
2450 /// let room = mock_server.sync_joined_room(&client, room_id!("!room_id:localhost")).await;
2451 ///
2452 /// mock_server
2453 /// .mock_room_send_state()
2454 /// .match_delayed_event(Duration::from_millis(500))
2455 /// .respond_with(ResponseTemplate::new(200).set_body_json(json!({"delay_id":"$some_id"})))
2456 /// .mock_once()
2457 /// .mount()
2458 /// .await;
2459 ///
2460 /// let response_not_mocked = room.send_state_event(RoomCreateEventContent::new_v11()).await;
2461 /// // A non delayed event should not be mocked by the server.
2462 /// assert!(response_not_mocked.is_err());
2463 ///
2464 /// let r = delayed_state_event::unstable::Request::new(
2465 /// room.room_id().to_owned(),
2466 /// "".to_owned(),
2467 /// DelayParameters::Timeout { timeout: Duration::from_millis(500) },
2468 /// &AnyStateEventContent::RoomCreate(RoomCreateEventContent::new_v11()),
2469 /// )
2470 /// .unwrap();
2471 /// let response = room.client().send(r).await.unwrap();
2472 /// // The delayed `m.room.message` event type should be mocked by the server.
2473 /// assert_eq!("$some_id", response.delay_id);
2474 ///
2475 /// # anyhow::Ok(()) });
2476 /// ```
2477 pub fn match_delayed_event(self, delay: Duration) -> Self {
2478 Self {
2479 mock: self
2480 .mock
2481 .and(query_param("org.matrix.msc4140.delay", delay.as_millis().to_string())),
2482 ..self
2483 }
2484 }
2485
2486 ///
2487 /// ```
2488 /// # tokio_test::block_on(async {
2489 /// use matrix_sdk::{
2490 /// ruma::{
2491 /// event_id,
2492 /// events::{call::member::CallMemberEventContent, AnyStateEventContent},
2493 /// room_id,
2494 /// },
2495 /// test_utils::mocks::MatrixMockServer,
2496 /// };
2497 ///
2498 /// let mock_server = MatrixMockServer::new().await;
2499 /// let client = mock_server.client_builder().build().await;
2500 ///
2501 /// mock_server.mock_room_state_encryption().plain().mount().await;
2502 ///
2503 /// let room = mock_server.sync_joined_room(&client, room_id!("!room_id:localhost")).await;
2504 ///
2505 /// let event_id = event_id!("$some_id");
2506 ///
2507 /// mock_server
2508 /// .mock_room_send_state()
2509 /// .for_key("my_key".to_owned())
2510 /// .ok(event_id)
2511 /// .expect(1)
2512 /// .mount()
2513 /// .await;
2514 ///
2515 /// let response_not_mocked = room
2516 /// .send_state_event_for_key(
2517 /// "",
2518 /// AnyStateEventContent::CallMember(CallMemberEventContent::new_empty(None)),
2519 /// )
2520 /// .await;
2521 /// // The `m.room.reaction` event type should not be mocked by the server.
2522 /// assert!(response_not_mocked.is_err());
2523 ///
2524 /// let response = room
2525 /// .send_state_event_for_key(
2526 /// "my_key",
2527 /// AnyStateEventContent::CallMember(CallMemberEventContent::new_empty(None)),
2528 /// )
2529 /// .await
2530 /// .unwrap();
2531 ///
2532 /// // The `m.room.message` event type should be mocked by the server.
2533 /// assert_eq!(
2534 /// event_id, response.event_id,
2535 /// "The event ID we mocked should match the one we received when we sent the event"
2536 /// );
2537 /// # anyhow::Ok(()) });
2538 /// ```
2539 pub fn for_key(mut self, state_key: String) -> Self {
2540 self.endpoint.state_key = Some(state_key);
2541 // Note: we may have already defined a path, but this one ought to be more
2542 // specialized (unless for_key/for_type were called multiple times).
2543 Self { mock: self.mock.and(path_regex(Self::generate_path_regexp(&self.endpoint))), ..self }
2544 }
2545
2546 /// Returns a send endpoint that emulates success, i.e. the event has been
2547 /// sent with the given event id.
2548 ///
2549 /// # Examples
2550 /// ```
2551 /// # tokio_test::block_on(async {
2552 /// use matrix_sdk::{ruma::{room_id, event_id}, test_utils::mocks::MatrixMockServer};
2553 /// use serde_json::json;
2554 ///
2555 /// let mock_server = MatrixMockServer::new().await;
2556 /// let client = mock_server.client_builder().build().await;
2557 ///
2558 /// mock_server.mock_room_state_encryption().plain().mount().await;
2559 ///
2560 /// let room = mock_server
2561 /// .sync_joined_room(&client, room_id!("!room_id:localhost"))
2562 /// .await;
2563 ///
2564 /// let event_id = event_id!("$some_id");
2565 /// let send_guard = mock_server
2566 /// .mock_room_send_state()
2567 /// .ok(event_id)
2568 /// .expect(1)
2569 /// .mount_as_scoped()
2570 /// .await;
2571 ///
2572 /// let response = room.send_state_event_raw("m.room.message", "my_key", json!({ "body": "Hello world" })).await?;
2573 ///
2574 /// assert_eq!(
2575 /// event_id,
2576 /// response.event_id,
2577 /// "The event ID we mocked should match the one we received when we sent the event"
2578 /// );
2579 /// # anyhow::Ok(()) });
2580 /// ```
2581 pub fn ok(self, returned_event_id: impl Into<OwnedEventId>) -> MatrixMock<'a> {
2582 self.ok_with_event_id(returned_event_id.into())
2583 }
2584}
2585
2586/// A prebuilt mock for running sync v2.
2587pub struct SyncEndpoint {
2588 sync_response_builder: Arc<Mutex<SyncResponseBuilder>>,
2589}
2590
2591impl<'a> MockEndpoint<'a, SyncEndpoint> {
2592 /// Expect the given timeout, or lack thereof, in the request.
2593 pub fn timeout(mut self, timeout: Option<Duration>) -> Self {
2594 if let Some(timeout) = timeout {
2595 self.mock = self.mock.and(query_param("timeout", timeout.as_millis().to_string()));
2596 } else {
2597 self.mock = self.mock.and(query_param_is_missing("timeout"));
2598 }
2599
2600 self
2601 }
2602
2603 /// Mocks the sync endpoint, using the given function to generate the
2604 /// response.
2605 pub fn ok<F: FnOnce(&mut SyncResponseBuilder)>(self, func: F) -> MatrixMock<'a> {
2606 let json_response = {
2607 let mut builder = self.endpoint.sync_response_builder.lock().unwrap();
2608 func(&mut builder);
2609 builder.build_json_sync_response()
2610 };
2611
2612 self.respond_with(ResponseTemplate::new(200).set_body_json(json_response))
2613 }
2614
2615 /// Temporarily mocks the sync with the given endpoint and runs a client
2616 /// sync with it.
2617 ///
2618 /// After calling this function, the sync endpoint isn't mocked anymore.
2619 ///
2620 /// # Examples
2621 ///
2622 /// ```
2623 /// # tokio_test::block_on(async {
2624 /// use matrix_sdk::{ruma::room_id, test_utils::mocks::MatrixMockServer};
2625 /// use matrix_sdk_test::JoinedRoomBuilder;
2626 ///
2627 /// // First create the mock server and client pair.
2628 /// let mock_server = MatrixMockServer::new().await;
2629 /// let client = mock_server.client_builder().build().await;
2630 /// let room_id = room_id!("!room_id:localhost");
2631 ///
2632 /// // Let's emulate what `MatrixMockServer::sync_joined_room()` does.
2633 /// mock_server
2634 /// .mock_sync()
2635 /// .ok_and_run(&client, |builder| {
2636 /// builder.add_joined_room(JoinedRoomBuilder::new(room_id));
2637 /// })
2638 /// .await;
2639 ///
2640 /// let room = client
2641 /// .get_room(room_id)
2642 /// .expect("The room should be available after we mocked the sync");
2643 /// # anyhow::Ok(()) });
2644 /// ```
2645 pub async fn ok_and_run<F: FnOnce(&mut SyncResponseBuilder)>(self, client: &Client, func: F) {
2646 let _scope = self.ok(func).mount_as_scoped().await;
2647
2648 let _response = client.sync_once(Default::default()).await.unwrap();
2649 }
2650}
2651
2652/// A prebuilt mock for reading the encryption state of a room.
2653pub struct EncryptionStateEndpoint;
2654
2655impl<'a> MockEndpoint<'a, EncryptionStateEndpoint> {
2656 /// Marks the room as encrypted.
2657 ///
2658 /// # Examples
2659 ///
2660 /// ```
2661 /// # tokio_test::block_on(async {
2662 /// use matrix_sdk::{ruma::room_id, test_utils::mocks::MatrixMockServer};
2663 ///
2664 /// let mock_server = MatrixMockServer::new().await;
2665 /// let client = mock_server.client_builder().build().await;
2666 ///
2667 /// mock_server.mock_room_state_encryption().encrypted().mount().await;
2668 ///
2669 /// let room = mock_server
2670 /// .sync_joined_room(&client, room_id!("!room_id:localhost"))
2671 /// .await;
2672 ///
2673 /// assert!(
2674 /// room.latest_encryption_state().await?.is_encrypted(),
2675 /// "The room should be marked as encrypted."
2676 /// );
2677 /// # anyhow::Ok(()) });
2678 /// ```
2679 pub fn encrypted(self) -> MatrixMock<'a> {
2680 self.respond_with(
2681 ResponseTemplate::new(200).set_body_json(&*test_json::sync_events::ENCRYPTION_CONTENT),
2682 )
2683 }
2684
2685 /// Marks the room as encrypted, opting into experimental state event
2686 /// encryption.
2687 ///
2688 /// # Examples
2689 ///
2690 /// ```
2691 /// # tokio_test::block_on(async {
2692 /// use matrix_sdk::{ruma::room_id, test_utils::mocks::MatrixMockServer};
2693 ///
2694 /// let mock_server = MatrixMockServer::new().await;
2695 /// let client = mock_server.client_builder().build().await;
2696 ///
2697 /// mock_server.mock_room_state_encryption().state_encrypted().mount().await;
2698 ///
2699 /// let room = mock_server
2700 /// .sync_joined_room(&client, room_id!("!room_id:localhost"))
2701 /// .await;
2702 ///
2703 /// assert!(
2704 /// room.latest_encryption_state().await?.is_state_encrypted(),
2705 /// "The room should be marked as state encrypted."
2706 /// );
2707 /// # anyhow::Ok(()) });
2708 #[cfg(feature = "experimental-encrypted-state-events")]
2709 pub fn state_encrypted(self) -> MatrixMock<'a> {
2710 self.respond_with(ResponseTemplate::new(200).set_body_json(
2711 &*test_json::sync_events::ENCRYPTION_WITH_ENCRYPTED_STATE_EVENTS_CONTENT,
2712 ))
2713 }
2714
2715 /// Marks the room as not encrypted.
2716 ///
2717 /// # Examples
2718 ///
2719 /// ```
2720 /// # tokio_test::block_on(async {
2721 /// use matrix_sdk::{ruma::room_id, test_utils::mocks::MatrixMockServer};
2722 ///
2723 /// let mock_server = MatrixMockServer::new().await;
2724 /// let client = mock_server.client_builder().build().await;
2725 ///
2726 /// mock_server.mock_room_state_encryption().plain().mount().await;
2727 ///
2728 /// let room = mock_server
2729 /// .sync_joined_room(&client, room_id!("!room_id:localhost"))
2730 /// .await;
2731 ///
2732 /// assert!(
2733 /// !room.latest_encryption_state().await?.is_encrypted(),
2734 /// "The room should not be marked as encrypted."
2735 /// );
2736 /// # anyhow::Ok(()) });
2737 /// ```
2738 pub fn plain(self) -> MatrixMock<'a> {
2739 self.respond_with(ResponseTemplate::new(404).set_body_json(&*test_json::NOT_FOUND))
2740 }
2741}
2742
2743/// A prebuilt mock for setting the encryption state of a room.
2744pub struct SetEncryptionStateEndpoint;
2745
2746impl<'a> MockEndpoint<'a, SetEncryptionStateEndpoint> {
2747 /// Returns a mock for a successful setting of the encryption state event.
2748 pub fn ok(self, returned_event_id: impl Into<OwnedEventId>) -> MatrixMock<'a> {
2749 self.ok_with_event_id(returned_event_id.into())
2750 }
2751}
2752
2753/// A prebuilt mock for redacting an event in a room.
2754pub struct RoomRedactEndpoint;
2755
2756impl<'a> MockEndpoint<'a, RoomRedactEndpoint> {
2757 /// Returns a redact endpoint that emulates success, i.e. the redaction
2758 /// event has been sent with the given event id.
2759 pub fn ok(self, returned_event_id: impl Into<OwnedEventId>) -> MatrixMock<'a> {
2760 self.ok_with_event_id(returned_event_id.into())
2761 }
2762}
2763
2764/// A prebuilt mock for getting a single event in a room.
2765pub struct RoomEventEndpoint {
2766 room: Option<OwnedRoomId>,
2767 match_event_id: bool,
2768}
2769
2770impl<'a> MockEndpoint<'a, RoomEventEndpoint> {
2771 /// Limits the scope of this mock to a specific room.
2772 pub fn room(mut self, room: impl Into<OwnedRoomId>) -> Self {
2773 self.endpoint.room = Some(room.into());
2774 self
2775 }
2776
2777 /// Whether the mock checks for the event id from the event.
2778 pub fn match_event_id(mut self) -> Self {
2779 self.endpoint.match_event_id = true;
2780 self
2781 }
2782
2783 /// Returns a redact endpoint that emulates success, i.e. the redaction
2784 /// event has been sent with the given event id.
2785 pub fn ok(self, event: TimelineEvent) -> MatrixMock<'a> {
2786 let event_path = if self.endpoint.match_event_id {
2787 let event_id = event.kind.event_id().expect("an event id is required");
2788 // The event id should begin with `$`, which would be taken as the end of the
2789 // regex so we need to escape it
2790 event_id.as_str().replace("$", "\\$")
2791 } else {
2792 // Event is at the end, so no need to add anything.
2793 "".to_owned()
2794 };
2795
2796 let room_path = self.endpoint.room.map_or_else(|| ".*".to_owned(), |room| room.to_string());
2797
2798 let mock = self
2799 .mock
2800 .and(path_regex(format!(r"^/_matrix/client/v3/rooms/{room_path}/event/{event_path}")))
2801 .respond_with(ResponseTemplate::new(200).set_body_json(event.into_raw().json()));
2802 MatrixMock { server: self.server, mock }
2803 }
2804}
2805
2806/// A builder pattern for the response to a [`RoomEventContextEndpoint`]
2807/// request.
2808pub struct RoomContextResponseTemplate {
2809 event: TimelineEvent,
2810 events_before: Vec<TimelineEvent>,
2811 events_after: Vec<TimelineEvent>,
2812 start: Option<String>,
2813 end: Option<String>,
2814 state_events: Vec<Raw<AnyStateEvent>>,
2815}
2816
2817impl RoomContextResponseTemplate {
2818 /// Creates a new context response with the given focused event.
2819 pub fn new(event: TimelineEvent) -> Self {
2820 Self {
2821 event,
2822 events_before: Vec::new(),
2823 events_after: Vec::new(),
2824 start: None,
2825 end: None,
2826 state_events: Vec::new(),
2827 }
2828 }
2829
2830 /// Add some events before the target event.
2831 pub fn events_before(mut self, events: Vec<TimelineEvent>) -> Self {
2832 self.events_before = events;
2833 self
2834 }
2835
2836 /// Add some events after the target event.
2837 pub fn events_after(mut self, events: Vec<TimelineEvent>) -> Self {
2838 self.events_after = events;
2839 self
2840 }
2841
2842 /// Set the start token that could be used for paginating backwards.
2843 pub fn start(mut self, start: impl Into<String>) -> Self {
2844 self.start = Some(start.into());
2845 self
2846 }
2847
2848 /// Set the end token that could be used for paginating forwards.
2849 pub fn end(mut self, end: impl Into<String>) -> Self {
2850 self.end = Some(end.into());
2851 self
2852 }
2853
2854 /// Pass some extra state events to this response.
2855 pub fn state_events(mut self, state_events: Vec<Raw<AnyStateEvent>>) -> Self {
2856 self.state_events = state_events;
2857 self
2858 }
2859}
2860
2861/// A prebuilt mock for getting a single event with its context in a room.
2862pub struct RoomEventContextEndpoint {
2863 room: Option<OwnedRoomId>,
2864 match_event_id: bool,
2865}
2866
2867impl<'a> MockEndpoint<'a, RoomEventContextEndpoint> {
2868 /// Limits the scope of this mock to a specific room.
2869 pub fn room(mut self, room: impl Into<OwnedRoomId>) -> Self {
2870 self.endpoint.room = Some(room.into());
2871 self
2872 }
2873
2874 /// Whether the mock checks for the event id from the event.
2875 pub fn match_event_id(mut self) -> Self {
2876 self.endpoint.match_event_id = true;
2877 self
2878 }
2879
2880 /// Returns an endpoint that emulates a successful response.
2881 pub fn ok(self, response: RoomContextResponseTemplate) -> MatrixMock<'a> {
2882 let event_path = if self.endpoint.match_event_id {
2883 let event_id = response.event.event_id().expect("an event id is required");
2884 // The event id should begin with `$`, which would be taken as the end of the
2885 // regex so we need to escape it
2886 event_id.as_str().replace("$", "\\$")
2887 } else {
2888 // Event is at the end, so no need to add anything.
2889 "".to_owned()
2890 };
2891
2892 let room_path = self.endpoint.room.map_or_else(|| ".*".to_owned(), |room| room.to_string());
2893
2894 let mock = self
2895 .mock
2896 .and(path_regex(format!(r"^/_matrix/client/v3/rooms/{room_path}/context/{event_path}")))
2897 .respond_with(ResponseTemplate::new(200).set_body_json(json!({
2898 "event": response.event.into_raw().json(),
2899 "events_before": response.events_before.into_iter().map(|event| event.into_raw().json().to_owned()).collect::<Vec<_>>(),
2900 "events_after": response.events_after.into_iter().map(|event| event.into_raw().json().to_owned()).collect::<Vec<_>>(),
2901 "end": response.end,
2902 "start": response.start,
2903 "state": response.state_events,
2904 })));
2905 MatrixMock { server: self.server, mock }
2906 }
2907}
2908
2909/// A prebuilt mock for the `/messages` endpoint.
2910pub struct RoomMessagesEndpoint;
2911
2912/// A prebuilt mock for getting a room messages in a room.
2913impl<'a> MockEndpoint<'a, RoomMessagesEndpoint> {
2914 /// Expects an optional limit to be set on the request.
2915 pub fn match_limit(self, limit: u32) -> Self {
2916 Self { mock: self.mock.and(query_param("limit", limit.to_string())), ..self }
2917 }
2918
2919 /// Expects an optional `from` to be set on the request.
2920 pub fn match_from(self, from: &str) -> Self {
2921 Self { mock: self.mock.and(query_param("from", from)), ..self }
2922 }
2923
2924 /// Returns a messages endpoint that emulates success, i.e. the messages
2925 /// provided as `response` could be retrieved.
2926 ///
2927 /// Note: pass `chunk` in the correct order: topological for forward
2928 /// pagination, reverse topological for backwards pagination.
2929 pub fn ok(self, response: RoomMessagesResponseTemplate) -> MatrixMock<'a> {
2930 let mut template = ResponseTemplate::new(200).set_body_json(json!({
2931 "start": response.start,
2932 "end": response.end,
2933 "chunk": response.chunk,
2934 "state": response.state,
2935 }));
2936
2937 if let Some(delay) = response.delay {
2938 template = template.set_delay(delay);
2939 }
2940
2941 self.respond_with(template)
2942 }
2943}
2944
2945/// A response to a [`RoomMessagesEndpoint`] query.
2946pub struct RoomMessagesResponseTemplate {
2947 /// The start token for this /messages query.
2948 pub start: String,
2949 /// The end token for this /messages query (previous batch for back
2950 /// paginations, next batch for forward paginations).
2951 pub end: Option<String>,
2952 /// The set of timeline events returned by this query.
2953 pub chunk: Vec<Raw<AnyTimelineEvent>>,
2954 /// The set of state events returned by this query.
2955 pub state: Vec<Raw<AnyStateEvent>>,
2956 /// Optional delay to respond to the query.
2957 pub delay: Option<Duration>,
2958}
2959
2960impl RoomMessagesResponseTemplate {
2961 /// Fill the events returned as part of this response.
2962 pub fn events(mut self, chunk: Vec<impl Into<Raw<AnyTimelineEvent>>>) -> Self {
2963 self.chunk = chunk.into_iter().map(Into::into).collect();
2964 self
2965 }
2966
2967 /// Fill the end token.
2968 pub fn end_token(mut self, token: impl Into<String>) -> Self {
2969 self.end = Some(token.into());
2970 self
2971 }
2972
2973 /// Respond with a given delay to the query.
2974 pub fn with_delay(mut self, delay: Duration) -> Self {
2975 self.delay = Some(delay);
2976 self
2977 }
2978}
2979
2980impl Default for RoomMessagesResponseTemplate {
2981 fn default() -> Self {
2982 Self {
2983 start: "start-token-unused".to_owned(),
2984 end: Default::default(),
2985 chunk: Default::default(),
2986 state: Default::default(),
2987 delay: None,
2988 }
2989 }
2990}
2991
2992/// A prebuilt mock for uploading media.
2993pub struct UploadEndpoint;
2994
2995impl<'a> MockEndpoint<'a, UploadEndpoint> {
2996 /// Expect that the content type matches what's given here.
2997 pub fn expect_mime_type(self, content_type: &str) -> Self {
2998 Self { mock: self.mock.and(header("content-type", content_type)), ..self }
2999 }
3000
3001 /// Returns a upload endpoint that emulates success, i.e. the media has been
3002 /// uploaded to the media server and can be accessed using the given
3003 /// event has been sent with the given [`MxcUri`].
3004 ///
3005 /// The uploaded content is captured and can be accessed using the returned
3006 /// [`Receiver`]. The [`Receiver`] is valid only for a single media
3007 /// upload.
3008 ///
3009 /// # Examples
3010 ///
3011 /// ```no_run
3012 /// # tokio_test::block_on(async {
3013 /// use matrix_sdk::{
3014 /// ruma::{event_id, mxc_uri, room_id},
3015 /// test_utils::mocks::MatrixMockServer,
3016 /// };
3017 ///
3018 /// let mxid = mxc_uri!("mxc://localhost/12345");
3019 ///
3020 /// let server = MatrixMockServer::new().await;
3021 /// let (receiver, upload_mock) = server.mock_upload().ok_with_capture(mxid);
3022 /// let client = server.client_builder().build().await;
3023 ///
3024 /// client.media().upload(&mime::TEXT_PLAIN, vec![1, 2, 3, 4, 5], None).await?;
3025 ///
3026 /// let uploaded = receiver.await?;
3027 ///
3028 /// assert_eq!(uploaded, vec![1, 2, 3, 4, 5]);
3029 /// # anyhow::Ok(()) });
3030 /// ```
3031 pub fn ok_with_capture(self, mxc_id: &MxcUri) -> (Receiver<Vec<u8>>, MatrixMock<'a>) {
3032 let (sender, receiver) = oneshot::channel();
3033 let sender = Arc::new(Mutex::new(Some(sender)));
3034 let response_body = json!({"content_uri": mxc_id});
3035
3036 let ret = self.respond_with(move |request: &Request| {
3037 let maybe_sender = sender.lock().unwrap().take();
3038
3039 if let Some(sender) = maybe_sender {
3040 let body = request.body.clone();
3041 let _ = sender.send(body);
3042 }
3043
3044 ResponseTemplate::new(200).set_body_json(response_body.clone())
3045 });
3046
3047 (receiver, ret)
3048 }
3049
3050 /// Returns a upload endpoint that emulates success, i.e. the media has been
3051 /// uploaded to the media server and can be accessed using the given
3052 /// event has been sent with the given [`MxcUri`].
3053 pub fn ok(self, mxc_id: &MxcUri) -> MatrixMock<'a> {
3054 self.respond_with(ResponseTemplate::new(200).set_body_json(json!({
3055 "content_uri": mxc_id
3056 })))
3057 }
3058}
3059
3060/// A prebuilt mock for resolving a room alias.
3061pub struct ResolveRoomAliasEndpoint;
3062
3063impl<'a> MockEndpoint<'a, ResolveRoomAliasEndpoint> {
3064 /// Sets up the endpoint to only intercept requests for the given room
3065 /// alias.
3066 pub fn for_alias(self, alias: impl Into<String>) -> Self {
3067 let alias = alias.into();
3068 Self {
3069 mock: self.mock.and(path_regex(format!(
3070 r"^/_matrix/client/v3/directory/room/{}",
3071 percent_encoded_path(&alias)
3072 ))),
3073 ..self
3074 }
3075 }
3076
3077 /// Returns a data endpoint with a resolved room alias.
3078 pub fn ok(self, room_id: &str, servers: Vec<String>) -> MatrixMock<'a> {
3079 self.respond_with(ResponseTemplate::new(200).set_body_json(json!({
3080 "room_id": room_id,
3081 "servers": servers,
3082 })))
3083 }
3084
3085 /// Returns a data endpoint for a room alias that does not exit.
3086 pub fn not_found(self) -> MatrixMock<'a> {
3087 self.respond_with(ResponseTemplate::new(404).set_body_json(json!({
3088 "errcode": "M_NOT_FOUND",
3089 "error": "Room alias not found."
3090 })))
3091 }
3092}
3093
3094/// A prebuilt mock for creating a room alias.
3095pub struct CreateRoomAliasEndpoint;
3096
3097impl<'a> MockEndpoint<'a, CreateRoomAliasEndpoint> {
3098 /// Returns a data endpoint for creating a room alias.
3099 pub fn ok(self) -> MatrixMock<'a> {
3100 self.respond_with(ResponseTemplate::new(200).set_body_json(json!({})))
3101 }
3102}
3103
3104/// A prebuilt mock for removing a room alias.
3105pub struct RemoveRoomAliasEndpoint;
3106
3107impl<'a> MockEndpoint<'a, RemoveRoomAliasEndpoint> {
3108 /// Returns a data endpoint for removing a room alias.
3109 pub fn ok(self) -> MatrixMock<'a> {
3110 self.respond_with(ResponseTemplate::new(200).set_body_json(json!({})))
3111 }
3112}
3113
3114/// A prebuilt mock for paginating the public room list.
3115pub struct PublicRoomsEndpoint;
3116
3117impl<'a> MockEndpoint<'a, PublicRoomsEndpoint> {
3118 /// Returns a data endpoint for paginating the public room list.
3119 pub fn ok(
3120 self,
3121 chunk: Vec<PublicRoomsChunk>,
3122 next_batch: Option<String>,
3123 prev_batch: Option<String>,
3124 total_room_count_estimate: Option<u64>,
3125 ) -> MatrixMock<'a> {
3126 self.respond_with(ResponseTemplate::new(200).set_body_json(json!({
3127 "chunk": chunk,
3128 "next_batch": next_batch,
3129 "prev_batch": prev_batch,
3130 "total_room_count_estimate": total_room_count_estimate,
3131 })))
3132 }
3133
3134 /// Returns a data endpoint for paginating the public room list with several
3135 /// `via` params.
3136 ///
3137 /// Each `via` param must be in the `server_map` parameter, otherwise it'll
3138 /// fail.
3139 pub fn ok_with_via_params(
3140 self,
3141 server_map: BTreeMap<OwnedServerName, Vec<PublicRoomsChunk>>,
3142 ) -> MatrixMock<'a> {
3143 self.respond_with(move |req: &Request| {
3144 #[derive(Deserialize)]
3145 struct PartialRequest {
3146 server: Option<OwnedServerName>,
3147 }
3148
3149 let (_, server) = req
3150 .url
3151 .query_pairs()
3152 .into_iter()
3153 .find(|(key, _)| key == "server")
3154 .expect("Server param not found in request URL");
3155 let server = ServerName::parse(server).expect("Couldn't parse server name");
3156 let chunk = server_map.get(&server).expect("Chunk for the server param not found");
3157 ResponseTemplate::new(200).set_body_json(json!({
3158 "chunk": chunk,
3159 "total_room_count_estimate": chunk.len(),
3160 }))
3161 })
3162 }
3163}
3164
3165/// A prebuilt mock for getting the room's visibility in the room directory.
3166pub struct GetRoomVisibilityEndpoint;
3167
3168impl<'a> MockEndpoint<'a, GetRoomVisibilityEndpoint> {
3169 /// Returns an endpoint that get the room's public visibility.
3170 pub fn ok(self, visibility: Visibility) -> MatrixMock<'a> {
3171 self.respond_with(ResponseTemplate::new(200).set_body_json(json!({
3172 "visibility": visibility,
3173 })))
3174 }
3175}
3176
3177/// A prebuilt mock for setting the room's visibility in the room directory.
3178pub struct SetRoomVisibilityEndpoint;
3179
3180impl<'a> MockEndpoint<'a, SetRoomVisibilityEndpoint> {
3181 /// Returns an endpoint that updates the room's visibility.
3182 pub fn ok(self) -> MatrixMock<'a> {
3183 self.respond_with(ResponseTemplate::new(200).set_body_json(json!({})))
3184 }
3185}
3186
3187/// A prebuilt mock for `GET room_keys/version`: storage ("backup") of room
3188/// keys.
3189pub struct RoomKeysVersionEndpoint;
3190
3191impl<'a> MockEndpoint<'a, RoomKeysVersionEndpoint> {
3192 /// Returns an endpoint that says there is a single room keys backup
3193 pub fn exists(self) -> MatrixMock<'a> {
3194 self.respond_with(ResponseTemplate::new(200).set_body_json(json!({
3195 "algorithm": "m.megolm_backup.v1.curve25519-aes-sha2",
3196 "auth_data": {
3197 "public_key": "abcdefg",
3198 "signatures": {},
3199 },
3200 "count": 42,
3201 "etag": "anopaquestring",
3202 "version": "1",
3203 })))
3204 }
3205
3206 /// Returns an endpoint that says there is no room keys backup
3207 pub fn none(self) -> MatrixMock<'a> {
3208 self.respond_with(ResponseTemplate::new(404).set_body_json(json!({
3209 "errcode": "M_NOT_FOUND",
3210 "error": "No current backup version"
3211 })))
3212 }
3213
3214 /// Returns an endpoint that 429 errors when we get it
3215 pub fn error429(self) -> MatrixMock<'a> {
3216 self.respond_with(ResponseTemplate::new(429).set_body_json(json!({
3217 "errcode": "M_LIMIT_EXCEEDED",
3218 "error": "Too many requests",
3219 "retry_after_ms": 2000
3220 })))
3221 }
3222
3223 /// Returns an endpoint that 404 errors when we get it
3224 pub fn error404(self) -> MatrixMock<'a> {
3225 self.respond_with(ResponseTemplate::new(404))
3226 }
3227}
3228
3229/// A prebuilt mock for `POST room_keys/version`: adding room key backups.
3230pub struct AddRoomKeysVersionEndpoint;
3231
3232impl<'a> MockEndpoint<'a, AddRoomKeysVersionEndpoint> {
3233 /// Returns an endpoint that may be used to add room key backups
3234 pub fn ok(self) -> MatrixMock<'a> {
3235 self.respond_with(ResponseTemplate::new(200).set_body_json(json!({
3236 "version": "1"
3237 })))
3238 .named("POST for the backup creation")
3239 }
3240}
3241
3242/// A prebuilt mock for `DELETE room_keys/version/xxx`: deleting room key
3243/// backups.
3244pub struct DeleteRoomKeysVersionEndpoint;
3245
3246impl<'a> MockEndpoint<'a, DeleteRoomKeysVersionEndpoint> {
3247 /// Returns an endpoint that allows deleting room key backups
3248 pub fn ok(self) -> MatrixMock<'a> {
3249 self.respond_with(ResponseTemplate::new(200).set_body_json(json!({})))
3250 .named("DELETE for the backup deletion")
3251 }
3252}
3253
3254/// A prebuilt mock for the `/sendToDevice` endpoint.
3255///
3256/// This mock can be used to simulate sending to-device messages in tests.
3257pub struct SendToDeviceEndpoint;
3258impl<'a> MockEndpoint<'a, SendToDeviceEndpoint> {
3259 /// Returns a successful response with default data.
3260 pub fn ok(self) -> MatrixMock<'a> {
3261 self.respond_with(ResponseTemplate::new(200).set_body_json(json!({})))
3262 }
3263}
3264
3265/// A prebuilt mock for `GET /members` request.
3266pub struct GetRoomMembersEndpoint;
3267
3268impl<'a> MockEndpoint<'a, GetRoomMembersEndpoint> {
3269 /// Returns a successful get members request with a list of members.
3270 pub fn ok(self, members: Vec<Raw<RoomMemberEvent>>) -> MatrixMock<'a> {
3271 self.respond_with(ResponseTemplate::new(200).set_body_json(json!({
3272 "chunk": members,
3273 })))
3274 }
3275}
3276
3277/// A prebuilt mock for `POST /invite` request.
3278pub struct InviteUserByIdEndpoint;
3279
3280impl<'a> MockEndpoint<'a, InviteUserByIdEndpoint> {
3281 /// Returns a successful invite user by id request.
3282 pub fn ok(self) -> MatrixMock<'a> {
3283 self.respond_with(ResponseTemplate::new(200).set_body_json(json!({})))
3284 }
3285}
3286
3287/// A prebuilt mock for `POST /kick` request.
3288pub struct KickUserEndpoint;
3289
3290impl<'a> MockEndpoint<'a, KickUserEndpoint> {
3291 /// Returns a successful kick user request.
3292 pub fn ok(self) -> MatrixMock<'a> {
3293 self.respond_with(ResponseTemplate::new(200).set_body_json(json!({})))
3294 }
3295}
3296
3297/// A prebuilt mock for `POST /ban` request.
3298pub struct BanUserEndpoint;
3299
3300impl<'a> MockEndpoint<'a, BanUserEndpoint> {
3301 /// Returns a successful ban user request.
3302 pub fn ok(self) -> MatrixMock<'a> {
3303 self.respond_with(ResponseTemplate::new(200).set_body_json(json!({})))
3304 }
3305}
3306
3307/// A prebuilt mock for `GET /versions` request.
3308pub struct VersionsEndpoint;
3309
3310impl<'a> MockEndpoint<'a, VersionsEndpoint> {
3311 // Get a JSON array of commonly supported versions.
3312 fn versions() -> Value {
3313 json!([
3314 "r0.0.1", "r0.2.0", "r0.3.0", "r0.4.0", "r0.5.0", "r0.6.0", "r0.6.1", "v1.1", "v1.2",
3315 "v1.3", "v1.4", "v1.5", "v1.6", "v1.7", "v1.8", "v1.9", "v1.10", "v1.11"
3316 ])
3317 }
3318
3319 /// Returns a successful `/_matrix/client/versions` request.
3320 ///
3321 /// The response will return some commonly supported versions.
3322 pub fn ok(self) -> MatrixMock<'a> {
3323 self.respond_with(ResponseTemplate::new(200).set_body_json(json!({
3324 "unstable_features": {},
3325 "versions": Self::versions()
3326 })))
3327 }
3328
3329 /// Returns a successful `/_matrix/client/versions` request.
3330 ///
3331 /// The response will return some commonly supported versions and unstable
3332 /// features supported by the SDK.
3333 pub fn ok_with_unstable_features(self) -> MatrixMock<'a> {
3334 self.respond_with(ResponseTemplate::new(200).set_body_json(json!({
3335 "unstable_features": {
3336 "org.matrix.label_based_filtering": true,
3337 "org.matrix.e2e_cross_signing": true,
3338 "org.matrix.msc4028": true,
3339 "org.matrix.simplified_msc3575": true,
3340 },
3341 "versions": Self::versions()
3342 })))
3343 }
3344
3345 /// Returns a successful `/_matrix/client/versions` request with the given
3346 /// versions and unstable features.
3347 pub fn ok_custom(
3348 self,
3349 versions: &[&str],
3350 unstable_features: &BTreeMap<&str, bool>,
3351 ) -> MatrixMock<'a> {
3352 self.respond_with(ResponseTemplate::new(200).set_body_json(json!({
3353 "unstable_features": unstable_features,
3354 "versions": versions,
3355 })))
3356 }
3357}
3358
3359/// A prebuilt mock for the room summary endpoint.
3360pub struct RoomSummaryEndpoint;
3361
3362impl<'a> MockEndpoint<'a, RoomSummaryEndpoint> {
3363 /// Returns a successful response with some default data for the given room
3364 /// id.
3365 pub fn ok(self, room_id: &RoomId) -> MatrixMock<'a> {
3366 self.respond_with(ResponseTemplate::new(200).set_body_json(json!({
3367 "room_id": room_id,
3368 "guest_can_join": true,
3369 "num_joined_members": 1,
3370 "world_readable": true,
3371 "join_rule": "public",
3372 })))
3373 }
3374}
3375
3376/// A prebuilt mock to set a room's pinned events.
3377pub struct SetRoomPinnedEventsEndpoint;
3378
3379impl<'a> MockEndpoint<'a, SetRoomPinnedEventsEndpoint> {
3380 /// Returns a successful response with a given event id.
3381 /// id.
3382 pub fn ok(self, event_id: OwnedEventId) -> MatrixMock<'a> {
3383 self.ok_with_event_id(event_id)
3384 }
3385
3386 /// Returns an error response with a generic error code indicating the
3387 /// client is not authorized to set pinned events.
3388 pub fn unauthorized(self) -> MatrixMock<'a> {
3389 self.respond_with(ResponseTemplate::new(400))
3390 }
3391}
3392
3393/// A prebuilt mock for `GET /account/whoami` request.
3394pub struct WhoAmIEndpoint;
3395
3396impl<'a> MockEndpoint<'a, WhoAmIEndpoint> {
3397 /// Returns a successful response with the default device ID.
3398 pub fn ok(self) -> MatrixMock<'a> {
3399 self.ok_with_device_id(device_id!("D3V1C31D"))
3400 }
3401
3402 /// Returns a successful response with the given device ID.
3403 pub fn ok_with_device_id(self, device_id: &DeviceId) -> MatrixMock<'a> {
3404 self.respond_with(ResponseTemplate::new(200).set_body_json(json!({
3405 "user_id": "@joe:example.org",
3406 "device_id": device_id,
3407 })))
3408 }
3409
3410 /// Returns an error response with an `M_UNKNOWN_TOKEN`.
3411 pub fn err_unknown_token(self) -> MatrixMock<'a> {
3412 self.respond_with(ResponseTemplate::new(401).set_body_json(json!({
3413 "errcode": "M_UNKNOWN_TOKEN",
3414 "error": "Invalid token"
3415 })))
3416 }
3417}
3418
3419/// A prebuilt mock for `POST /keys/upload` request.
3420pub struct UploadKeysEndpoint;
3421
3422impl<'a> MockEndpoint<'a, UploadKeysEndpoint> {
3423 /// Returns a successful response with counts of 10 curve25519 keys and 20
3424 /// signed curve25519 keys.
3425 pub fn ok(self) -> MatrixMock<'a> {
3426 self.respond_with(ResponseTemplate::new(200).set_body_json(json!({
3427 "one_time_key_counts": {
3428 "curve25519": 10,
3429 "signed_curve25519": 20,
3430 },
3431 })))
3432 }
3433}
3434
3435/// A prebuilt mock for `POST /keys/query` request.
3436pub struct QueryKeysEndpoint;
3437
3438impl<'a> MockEndpoint<'a, QueryKeysEndpoint> {
3439 /// Returns a successful empty response.
3440 pub fn ok(self) -> MatrixMock<'a> {
3441 self.respond_with(ResponseTemplate::new(200).set_body_json(json!({})))
3442 }
3443}
3444
3445/// A prebuilt mock for `GET /.well-known/matrix/client` request.
3446pub struct WellKnownEndpoint;
3447
3448impl<'a> MockEndpoint<'a, WellKnownEndpoint> {
3449 /// Returns a successful response with the URL for this homeserver.
3450 pub fn ok(self) -> MatrixMock<'a> {
3451 let server_uri = self.server.uri();
3452 self.ok_with_homeserver_url(&server_uri)
3453 }
3454
3455 /// Returns a successful response with the given homeserver URL.
3456 pub fn ok_with_homeserver_url(self, homeserver_url: &str) -> MatrixMock<'a> {
3457 self.respond_with(ResponseTemplate::new(200).set_body_json(json!({
3458 "m.homeserver": {
3459 "base_url": homeserver_url,
3460 },
3461 "m.rtc_foci": [
3462 {
3463 "type": "livekit",
3464 "livekit_service_url": "https://livekit.example.com",
3465 },
3466 ],
3467 })))
3468 }
3469
3470 /// Returns a 404 error response.
3471 pub fn error404(self) -> MatrixMock<'a> {
3472 self.respond_with(ResponseTemplate::new(404))
3473 }
3474}
3475
3476/// A prebuilt mock for `POST /keys/device_signing/upload` request.
3477pub struct UploadCrossSigningKeysEndpoint;
3478
3479impl<'a> MockEndpoint<'a, UploadCrossSigningKeysEndpoint> {
3480 /// Returns a successful empty response.
3481 pub fn ok(self) -> MatrixMock<'a> {
3482 self.respond_with(ResponseTemplate::new(200).set_body_json(json!({})))
3483 }
3484
3485 /// Returns an error response with a UIAA stage that failed to authenticate
3486 /// because of an invalid password.
3487 pub fn uiaa_invalid_password(self) -> MatrixMock<'a> {
3488 self.respond_with(ResponseTemplate::new(401).set_body_json(json!({
3489 "errcode": "M_FORBIDDEN",
3490 "error": "Invalid password",
3491 "flows": [
3492 {
3493 "stages": [
3494 "m.login.password"
3495 ]
3496 }
3497 ],
3498 "params": {},
3499 "session": "oFIJVvtEOCKmRUTYKTYIIPHL"
3500 })))
3501 }
3502
3503 /// Returns an error response with a UIAA stage.
3504 pub fn uiaa(self) -> MatrixMock<'a> {
3505 self.respond_with(ResponseTemplate::new(401).set_body_json(json!({
3506 "flows": [
3507 {
3508 "stages": [
3509 "m.login.password"
3510 ]
3511 }
3512 ],
3513 "params": {},
3514 "session": "oFIJVvtEOCKmRUTYKTYIIPHL"
3515 })))
3516 }
3517
3518 /// Returns an error response with an OAuth 2.0 UIAA stage.
3519 pub fn uiaa_oauth(self) -> MatrixMock<'a> {
3520 let server_uri = self.server.uri();
3521 self.respond_with(ResponseTemplate::new(401).set_body_json(json!({
3522 "session": "dummy",
3523 "flows": [{
3524 "stages": [ "org.matrix.cross_signing_reset" ]
3525 }],
3526 "params": {
3527 "org.matrix.cross_signing_reset": {
3528 "url": format!("{server_uri}/account/?action=org.matrix.cross_signing_reset"),
3529 }
3530 },
3531 "msg": "To reset your end-to-end encryption cross-signing identity, you first need to approve it and then try again."
3532 })))
3533 }
3534}
3535
3536/// A prebuilt mock for `POST /keys/signatures/upload` request.
3537pub struct UploadCrossSigningSignaturesEndpoint;
3538
3539impl<'a> MockEndpoint<'a, UploadCrossSigningSignaturesEndpoint> {
3540 /// Returns a successful empty response.
3541 pub fn ok(self) -> MatrixMock<'a> {
3542 self.respond_with(ResponseTemplate::new(200).set_body_json(json!({})))
3543 }
3544}
3545
3546/// A prebuilt mock for the room leave endpoint.
3547pub struct RoomLeaveEndpoint;
3548
3549impl<'a> MockEndpoint<'a, RoomLeaveEndpoint> {
3550 /// Returns a successful response with some default data for the given room
3551 /// id.
3552 pub fn ok(self, room_id: &RoomId) -> MatrixMock<'a> {
3553 self.respond_with(ResponseTemplate::new(200).set_body_json(json!({
3554 "room_id": room_id,
3555 })))
3556 }
3557
3558 /// Returns a `M_FORBIDDEN` response.
3559 pub fn forbidden(self) -> MatrixMock<'a> {
3560 self.respond_with(ResponseTemplate::new(403).set_body_json(json!({
3561 "errcode": "M_FORBIDDEN",
3562 "error": "sowwy",
3563 })))
3564 }
3565}
3566
3567/// A prebuilt mock for the room forget endpoint.
3568pub struct RoomForgetEndpoint;
3569
3570impl<'a> MockEndpoint<'a, RoomForgetEndpoint> {
3571 /// Returns a successful response with some default data for the given room
3572 /// id.
3573 pub fn ok(self) -> MatrixMock<'a> {
3574 self.respond_with(ResponseTemplate::new(200).set_body_json(json!({})))
3575 }
3576}
3577
3578/// A prebuilt mock for `POST /logout` request.
3579pub struct LogoutEndpoint;
3580
3581impl<'a> MockEndpoint<'a, LogoutEndpoint> {
3582 /// Returns a successful empty response.
3583 pub fn ok(self) -> MatrixMock<'a> {
3584 self.respond_with(ResponseTemplate::new(200).set_body_json(json!({})))
3585 }
3586}
3587
3588/// A prebuilt mock for a `GET /rooms/{roomId}/threads` request.
3589pub struct RoomThreadsEndpoint;
3590
3591impl<'a> MockEndpoint<'a, RoomThreadsEndpoint> {
3592 /// Expects an optional `from` to be set on the request.
3593 pub fn match_from(self, from: &str) -> Self {
3594 Self { mock: self.mock.and(query_param("from", from)), ..self }
3595 }
3596
3597 /// Returns a successful response with some optional events and previous
3598 /// batch token.
3599 pub fn ok(
3600 self,
3601 chunk: Vec<Raw<AnyTimelineEvent>>,
3602 next_batch: Option<String>,
3603 ) -> MatrixMock<'a> {
3604 self.respond_with(ResponseTemplate::new(200).set_body_json(json!({
3605 "chunk": chunk,
3606 "next_batch": next_batch
3607 })))
3608 }
3609}
3610
3611/// A prebuilt mock for a `GET /rooms/{roomId}/relations/{eventId}` family of
3612/// requests.
3613#[derive(Default)]
3614pub struct RoomRelationsEndpoint {
3615 event_id: Option<OwnedEventId>,
3616 spec: Option<IncludeRelations>,
3617}
3618
3619impl<'a> MockEndpoint<'a, RoomRelationsEndpoint> {
3620 /// Expects an optional `from` to be set on the request.
3621 pub fn match_from(self, from: &str) -> Self {
3622 Self { mock: self.mock.and(query_param("from", from)), ..self }
3623 }
3624
3625 /// Expects an optional `limit` to be set on the request.
3626 pub fn match_limit(self, limit: u32) -> Self {
3627 Self { mock: self.mock.and(query_param("limit", limit.to_string())), ..self }
3628 }
3629
3630 /// Match the given subrequest, according to the given specification.
3631 pub fn match_subrequest(mut self, spec: IncludeRelations) -> Self {
3632 self.endpoint.spec = Some(spec);
3633 self
3634 }
3635
3636 /// Expects the request to match a specific event id.
3637 pub fn match_target_event(mut self, event_id: OwnedEventId) -> Self {
3638 self.endpoint.event_id = Some(event_id);
3639 self
3640 }
3641
3642 /// Returns a successful response with some optional events and pagination
3643 /// tokens.
3644 pub fn ok(mut self, response: RoomRelationsResponseTemplate) -> MatrixMock<'a> {
3645 // Escape the leading $ to not confuse the regular expression engine.
3646 let event_spec = self
3647 .endpoint
3648 .event_id
3649 .take()
3650 .map(|event_id| event_id.as_str().replace("$", "\\$"))
3651 .unwrap_or_else(|| ".*".to_owned());
3652
3653 match self.endpoint.spec.take() {
3654 Some(IncludeRelations::RelationsOfType(rel_type)) => {
3655 self.mock = self.mock.and(path_regex(format!(
3656 r"^/_matrix/client/v1/rooms/.*/relations/{event_spec}/{rel_type}$"
3657 )));
3658 }
3659 Some(IncludeRelations::RelationsOfTypeAndEventType(rel_type, event_type)) => {
3660 self.mock = self.mock.and(path_regex(format!(
3661 r"^/_matrix/client/v1/rooms/.*/relations/{event_spec}/{rel_type}/{event_type}$"
3662 )));
3663 }
3664 _ => {
3665 self.mock = self.mock.and(path_regex(format!(
3666 r"^/_matrix/client/v1/rooms/.*/relations/{event_spec}",
3667 )));
3668 }
3669 }
3670
3671 self.respond_with(ResponseTemplate::new(200).set_body_json(json!({
3672 "chunk": response.chunk,
3673 "next_batch": response.next_batch,
3674 "prev_batch": response.prev_batch,
3675 "recursion_depth": response.recursion_depth,
3676 })))
3677 }
3678}
3679
3680/// Helper function to set up a [`MockBuilder`] so it intercepts the account
3681/// data URLs.
3682fn global_account_data_mock_builder(
3683 builder: MockBuilder,
3684 user_id: &UserId,
3685 event_type: GlobalAccountDataEventType,
3686) -> MockBuilder {
3687 builder
3688 .and(path_regex(format!(r"^/_matrix/client/v3/user/{user_id}/account_data/{event_type}",)))
3689}
3690
3691/// A prebuilt mock for a `GET
3692/// /_matrix/client/v3/user/{userId}/account_data/io.element.recent_emoji`
3693/// request, which fetches the recently used emojis in the account data.
3694#[cfg(feature = "experimental-element-recent-emojis")]
3695pub struct GetRecentEmojisEndpoint;
3696
3697#[cfg(feature = "experimental-element-recent-emojis")]
3698impl<'a> MockEndpoint<'a, GetRecentEmojisEndpoint> {
3699 /// Returns a mock for a successful fetch of the recently used emojis in the
3700 /// account data.
3701 pub fn ok(self, user_id: &UserId, emojis: Vec<(String, UInt)>) -> MatrixMock<'a> {
3702 let mock =
3703 global_account_data_mock_builder(self.mock, user_id, "io.element.recent_emoji".into())
3704 .respond_with(
3705 ResponseTemplate::new(200).set_body_json(json!({ "recent_emoji": emojis })),
3706 );
3707 MatrixMock { server: self.server, mock }
3708 }
3709}
3710
3711/// A prebuilt mock for a `PUT
3712/// /_matrix/client/v3/user/{userId}/account_data/io.element.recent_emoji`
3713/// request, which updates the recently used emojis in the account data.
3714#[cfg(feature = "experimental-element-recent-emojis")]
3715pub struct UpdateRecentEmojisEndpoint {
3716 pub(crate) request_body: Option<Vec<(String, UInt)>>,
3717}
3718
3719#[cfg(feature = "experimental-element-recent-emojis")]
3720impl UpdateRecentEmojisEndpoint {
3721 /// Creates a new instance of the recent update recent emojis mock endpoint.
3722 fn new() -> Self {
3723 Self { request_body: None }
3724 }
3725}
3726
3727#[cfg(feature = "experimental-element-recent-emojis")]
3728impl<'a> MockEndpoint<'a, UpdateRecentEmojisEndpoint> {
3729 /// Returns a mock that will check the body of the request, making sure its
3730 /// contents match the provided list of emojis.
3731 pub fn match_emojis_in_request_body(self, emojis: Vec<(String, UInt)>) -> Self {
3732 Self::new(
3733 self.server,
3734 self.mock.and(body_json(json!(RecentEmojisContent::new(emojis)))),
3735 self.endpoint,
3736 )
3737 }
3738
3739 /// Returns a mock for a successful update of the recent emojis account data
3740 /// event. The request body contents should match the provided emoji
3741 /// list.
3742 #[cfg(feature = "experimental-element-recent-emojis")]
3743 pub fn ok(self, user_id: &UserId) -> MatrixMock<'a> {
3744 let mock =
3745 global_account_data_mock_builder(self.mock, user_id, "io.element.recent_emoji".into())
3746 .respond_with(ResponseTemplate::new(200).set_body_json(()));
3747 MatrixMock { server: self.server, mock }
3748 }
3749}
3750
3751/// A prebuilt mock for a `GET
3752/// /_matrix/client/v3/user/{userId}/account_data/m.secret_storage.default_key`
3753/// request, which fetches the ID of the default secret storage key.
3754#[cfg(feature = "e2e-encryption")]
3755pub struct GetDefaultSecretStorageKeyEndpoint;
3756
3757#[cfg(feature = "e2e-encryption")]
3758impl<'a> MockEndpoint<'a, GetDefaultSecretStorageKeyEndpoint> {
3759 /// Returns a mock for a successful fetch of the default secret storage key.
3760 pub fn ok(self, user_id: &UserId, key_id: &str) -> MatrixMock<'a> {
3761 let mock = global_account_data_mock_builder(
3762 self.mock,
3763 user_id,
3764 GlobalAccountDataEventType::SecretStorageDefaultKey,
3765 )
3766 .respond_with(ResponseTemplate::new(200).set_body_json(json!({
3767 "key": key_id
3768 })));
3769 MatrixMock { server: self.server, mock }
3770 }
3771}
3772
3773/// A prebuilt mock for a `GET
3774/// /_matrix/client/v3/user/{userId}/account_data/m.secret_storage.key.{keyId}`
3775/// request, which fetches information about a secret storage key.
3776#[cfg(feature = "e2e-encryption")]
3777pub struct GetSecretStorageKeyEndpoint;
3778
3779#[cfg(feature = "e2e-encryption")]
3780impl<'a> MockEndpoint<'a, GetSecretStorageKeyEndpoint> {
3781 /// Returns a mock for a successful fetch of the secret storage key
3782 pub fn ok(
3783 self,
3784 user_id: &UserId,
3785 secret_storage_key_event_content: &ruma::events::secret_storage::key::SecretStorageKeyEventContent,
3786 ) -> MatrixMock<'a> {
3787 let mock = global_account_data_mock_builder(
3788 self.mock,
3789 user_id,
3790 GlobalAccountDataEventType::SecretStorageKey(
3791 secret_storage_key_event_content.key_id.clone(),
3792 ),
3793 )
3794 .respond_with(ResponseTemplate::new(200).set_body_json(secret_storage_key_event_content));
3795 MatrixMock { server: self.server, mock }
3796 }
3797}
3798
3799/// A prebuilt mock for a `GET
3800/// /_matrix/client/v3/user/{userId}/account_data/m.cross_signing.master`
3801/// request, which fetches information about the master signing key.
3802#[cfg(feature = "e2e-encryption")]
3803pub struct GetMasterSigningKeyEndpoint;
3804
3805#[cfg(feature = "e2e-encryption")]
3806impl<'a> MockEndpoint<'a, GetMasterSigningKeyEndpoint> {
3807 /// Returns a mock for a successful fetch of the master signing key
3808 pub fn ok<B: Serialize>(self, user_id: &UserId, key_json: B) -> MatrixMock<'a> {
3809 let mock = global_account_data_mock_builder(
3810 self.mock,
3811 user_id,
3812 GlobalAccountDataEventType::from("m.cross_signing.master".to_owned()),
3813 )
3814 .respond_with(ResponseTemplate::new(200).set_body_json(key_json));
3815 MatrixMock { server: self.server, mock }
3816 }
3817}
3818
3819/// A response to a [`RoomRelationsEndpoint`] query.
3820#[derive(Default)]
3821pub struct RoomRelationsResponseTemplate {
3822 /// The set of timeline events returned by this query.
3823 pub chunk: Vec<Raw<AnyTimelineEvent>>,
3824
3825 /// An opaque string representing a pagination token, which semantics depend
3826 /// on the direction used in the request.
3827 pub next_batch: Option<String>,
3828
3829 /// An opaque string representing a pagination token, which semantics depend
3830 /// on the direction used in the request.
3831 pub prev_batch: Option<String>,
3832
3833 /// If `recurse` was set on the request, the depth to which the server
3834 /// recursed.
3835 ///
3836 /// If `recurse` was not set, this field must be absent.
3837 pub recursion_depth: Option<u32>,
3838}
3839
3840impl RoomRelationsResponseTemplate {
3841 /// Fill the events returned as part of this response.
3842 pub fn events(mut self, chunk: Vec<impl Into<Raw<AnyTimelineEvent>>>) -> Self {
3843 self.chunk = chunk.into_iter().map(Into::into).collect();
3844 self
3845 }
3846
3847 /// Fill the `next_batch` token returned as part of this response.
3848 pub fn next_batch(mut self, token: impl Into<String>) -> Self {
3849 self.next_batch = Some(token.into());
3850 self
3851 }
3852
3853 /// Fill the `prev_batch` token returned as part of this response.
3854 pub fn prev_batch(mut self, token: impl Into<String>) -> Self {
3855 self.prev_batch = Some(token.into());
3856 self
3857 }
3858
3859 /// Fill the recursion depth returned in this response.
3860 pub fn recursion_depth(mut self, depth: u32) -> Self {
3861 self.recursion_depth = Some(depth);
3862 self
3863 }
3864}
3865
3866/// A prebuilt mock for `POST /rooms/{roomId}/receipt/{receiptType}/{eventId}`
3867/// request.
3868pub struct ReceiptEndpoint;
3869
3870impl<'a> MockEndpoint<'a, ReceiptEndpoint> {
3871 /// Returns a successful empty response.
3872 pub fn ok(self) -> MatrixMock<'a> {
3873 self.respond_with(ResponseTemplate::new(200).set_body_json(json!({})))
3874 }
3875
3876 /// Ensures that the body of the request is a superset of the provided
3877 /// `body` parameter.
3878 pub fn body_matches_partial_json(self, body: Value) -> Self {
3879 Self { mock: self.mock.and(body_partial_json(body)), ..self }
3880 }
3881
3882 /// Ensures that the body of the request is the exact provided `body`
3883 /// parameter.
3884 pub fn body_json(self, body: Value) -> Self {
3885 Self { mock: self.mock.and(body_json(body)), ..self }
3886 }
3887
3888 /// Ensures that the request matches a specific receipt thread.
3889 pub fn match_thread(self, thread: ReceiptThread) -> Self {
3890 if let Some(thread_str) = thread.as_str() {
3891 self.body_matches_partial_json(json!({
3892 "thread_id": thread_str
3893 }))
3894 } else {
3895 self
3896 }
3897 }
3898
3899 /// Ensures that the request matches a specific event id.
3900 pub fn match_event_id(self, event_id: &EventId) -> Self {
3901 Self {
3902 mock: self.mock.and(path_regex(format!(
3903 r"^/_matrix/client/v3/rooms/.*/receipt/.*/{}$",
3904 event_id.as_str().replace("$", "\\$")
3905 ))),
3906 ..self
3907 }
3908 }
3909}
3910
3911/// A prebuilt mock for `POST /rooms/{roomId}/read_markers` request.
3912pub struct ReadMarkersEndpoint;
3913
3914impl<'a> MockEndpoint<'a, ReadMarkersEndpoint> {
3915 /// Returns a successful empty response.
3916 pub fn ok(self) -> MatrixMock<'a> {
3917 self.respond_with(ResponseTemplate::new(200).set_body_json(json!({})))
3918 }
3919}
3920
3921/// A prebuilt mock for `PUT /user/{userId}/rooms/{roomId}/account_data/{type}`
3922/// request.
3923pub struct RoomAccountDataEndpoint;
3924
3925impl<'a> MockEndpoint<'a, RoomAccountDataEndpoint> {
3926 /// Returns a successful empty response.
3927 pub fn ok(self) -> MatrixMock<'a> {
3928 self.respond_with(ResponseTemplate::new(200).set_body_json(json!({})))
3929 }
3930}
3931
3932/// A prebuilt mock for `GET /_matrix/client/v1/media/config` request.
3933pub struct AuthenticatedMediaConfigEndpoint;
3934
3935impl<'a> MockEndpoint<'a, AuthenticatedMediaConfigEndpoint> {
3936 /// Returns a successful response with the provided max upload size.
3937 pub fn ok(self, max_upload_size: UInt) -> MatrixMock<'a> {
3938 self.respond_with(ResponseTemplate::new(200).set_body_json(json!({
3939 "m.upload.size": max_upload_size,
3940 })))
3941 }
3942
3943 /// Returns a successful response with a maxed out max upload size.
3944 pub fn ok_default(self) -> MatrixMock<'a> {
3945 self.respond_with(ResponseTemplate::new(200).set_body_json(json!({
3946 "m.upload.size": UInt::MAX,
3947 })))
3948 }
3949}
3950
3951/// A prebuilt mock for `GET /_matrix/media/v3/config` request.
3952pub struct MediaConfigEndpoint;
3953
3954impl<'a> MockEndpoint<'a, MediaConfigEndpoint> {
3955 /// Returns a successful response with the provided max upload size.
3956 pub fn ok(self, max_upload_size: UInt) -> MatrixMock<'a> {
3957 self.respond_with(ResponseTemplate::new(200).set_body_json(json!({
3958 "m.upload.size": max_upload_size,
3959 })))
3960 }
3961}
3962
3963/// A prebuilt mock for `POST /login` requests.
3964pub struct LoginEndpoint;
3965
3966impl<'a> MockEndpoint<'a, LoginEndpoint> {
3967 /// Returns a successful response.
3968 pub fn ok(self) -> MatrixMock<'a> {
3969 self.respond_with(ResponseTemplate::new(200).set_body_json(&*test_json::LOGIN))
3970 }
3971
3972 /// Returns a given response on POST /login requests
3973 ///
3974 /// # Arguments
3975 ///
3976 /// * `response` - The response that the mock server sends on POST /login
3977 /// requests.
3978 ///
3979 /// # Returns
3980 ///
3981 /// Returns a [`MatrixMock`] which can be mounted.
3982 ///
3983 /// # Examples
3984 ///
3985 /// ```
3986 /// use matrix_sdk::test_utils::mocks::{
3987 /// LoginResponseTemplate200, MatrixMockServer,
3988 /// };
3989 /// use matrix_sdk_test::async_test;
3990 /// use ruma::{device_id, time::Duration, user_id};
3991 ///
3992 /// #[async_test]
3993 /// async fn test_ok_with() {
3994 /// let server = MatrixMockServer::new().await;
3995 /// server
3996 /// .mock_login()
3997 /// .ok_with(LoginResponseTemplate200::new(
3998 /// "qwerty",
3999 /// device_id!("DEADBEEF"),
4000 /// user_id!("@cheeky_monkey:matrix.org"),
4001 /// ))
4002 /// .mount()
4003 /// .await;
4004 ///
4005 /// let client = server.client_builder().unlogged().build().await;
4006 ///
4007 /// let result = client
4008 /// .matrix_auth()
4009 /// .login_username("example", "wordpass")
4010 /// .send()
4011 /// .await
4012 /// .unwrap();
4013 ///
4014 /// assert!(
4015 /// result.access_tokesn.unwrap() == "qwerty",
4016 /// "wrong access token in response"
4017 /// );
4018 /// assert!(
4019 /// result.device_id.unwrap() == "DEADBEEF",
4020 /// "wrong device id in response"
4021 /// );
4022 /// assert!(
4023 /// result.user_id.unwrap() == "@cheeky_monkey:matrix.org",
4024 /// "wrong user id in response"
4025 /// );
4026 /// }
4027 /// ```
4028 pub fn ok_with(self, response: LoginResponseTemplate200) -> MatrixMock<'a> {
4029 self.respond_with(ResponseTemplate::new(200).set_body_json(json!({
4030 "access_token": response.access_token,
4031 "device_id": response.device_id,
4032 "user_id": response.user_id,
4033 "expires_in": response.expires_in.map(|duration| { duration.as_millis() }),
4034 "refresh_token": response.refresh_token,
4035 "well_known": response.well_known.map(|vals| {
4036 json!({
4037 "m.homeserver": {
4038 "base_url": vals.homeserver_url
4039 },
4040 "m.identity_server": vals.identity_url.map(|url| {
4041 json!({
4042 "base_url": url
4043 })
4044 })
4045 })
4046 }),
4047 })))
4048 }
4049
4050 /// Ensures that the body of the request is a superset of the provided
4051 /// `body` parameter.
4052 pub fn body_matches_partial_json(self, body: Value) -> Self {
4053 Self { mock: self.mock.and(body_partial_json(body)), ..self }
4054 }
4055}
4056
4057#[derive(Default)]
4058struct LoginResponseWellKnown {
4059 /// Required if well_known is used: The base URL for the homeserver for
4060 /// client-server connections.
4061 homeserver_url: String,
4062
4063 /// Required if well_known and m.identity_server are used: The base URL for
4064 /// the identity server for client-server connections.
4065 identity_url: Option<String>,
4066}
4067
4068/// A response to a [`LoginEndpoint`] query with status code 200.
4069#[derive(Default)]
4070pub struct LoginResponseTemplate200 {
4071 /// Required: An access token for the account. This access token can then be
4072 /// used to authorize other requests.
4073 access_token: Option<String>,
4074
4075 /// Required: ID of the logged-in device. Will be the same as the
4076 /// corresponding parameter in the request, if one was specified.
4077 device_id: Option<OwnedDeviceId>,
4078
4079 /// The lifetime of the access token, in milliseconds. Once the access token
4080 /// has expired a new access token can be obtained by using the provided
4081 /// refresh token. If no refresh token is provided, the client will need
4082 /// to re-log in to obtain a new access token. If not given, the client
4083 /// can assume that the access token will not expire.
4084 expires_in: Option<Duration>,
4085
4086 /// A refresh token for the account. This token can be used to obtain a new
4087 /// access token when it expires by calling the /refresh endpoint.
4088 refresh_token: Option<String>,
4089
4090 /// Required: The fully-qualified Matrix ID for the account.
4091 user_id: Option<OwnedUserId>,
4092
4093 /// Optional client configuration provided by the server.
4094 well_known: Option<LoginResponseWellKnown>,
4095}
4096
4097impl LoginResponseTemplate200 {
4098 /// Constructor for empty response
4099 pub fn new<T1: Into<OwnedDeviceId>, T2: Into<OwnedUserId>>(
4100 access_token: &str,
4101 device_id: T1,
4102 user_id: T2,
4103 ) -> Self {
4104 Self {
4105 access_token: Some(access_token.to_owned()),
4106 device_id: Some(device_id.into()),
4107 user_id: Some(user_id.into()),
4108 ..Default::default()
4109 }
4110 }
4111
4112 /// sets expires_in
4113 pub fn expires_in(mut self, value: Duration) -> Self {
4114 self.expires_in = Some(value);
4115 self
4116 }
4117
4118 /// sets refresh_token
4119 pub fn refresh_token(mut self, value: &str) -> Self {
4120 self.refresh_token = Some(value.to_owned());
4121 self
4122 }
4123
4124 /// sets well_known which takes a homeserver_url and an optional
4125 /// identity_url
4126 pub fn well_known(mut self, homeserver_url: String, identity_url: Option<String>) -> Self {
4127 self.well_known = Some(LoginResponseWellKnown { homeserver_url, identity_url });
4128 self
4129 }
4130}
4131
4132/// A prebuilt mock for `GET /devices` requests.
4133pub struct DevicesEndpoint;
4134
4135impl<'a> MockEndpoint<'a, DevicesEndpoint> {
4136 /// Returns a successful response.
4137 pub fn ok(self) -> MatrixMock<'a> {
4138 self.respond_with(ResponseTemplate::new(200).set_body_json(&*test_json::DEVICES))
4139 }
4140}
4141
4142/// A prebuilt mock for `POST /user_directory/search` requests.
4143pub struct UserDirectoryEndpoint;
4144
4145impl<'a> MockEndpoint<'a, UserDirectoryEndpoint> {
4146 /// Returns a successful response.
4147 pub fn ok(self) -> MatrixMock<'a> {
4148 self.respond_with(
4149 ResponseTemplate::new(200)
4150 .set_body_json(&*test_json::search_users::SEARCH_USERS_RESPONSE),
4151 )
4152 }
4153}
4154
4155/// A prebuilt mock for `POST /createRoom` requests.
4156pub struct CreateRoomEndpoint;
4157
4158impl<'a> MockEndpoint<'a, CreateRoomEndpoint> {
4159 /// Returns a successful response.
4160 pub fn ok(self) -> MatrixMock<'a> {
4161 self.respond_with(
4162 ResponseTemplate::new(200).set_body_json(json!({ "room_id": "!room:example.org"})),
4163 )
4164 }
4165}
4166
4167/// A prebuilt mock for `POST /rooms/{roomId}/upgrade` requests.
4168pub struct UpgradeRoomEndpoint;
4169
4170impl<'a> MockEndpoint<'a, UpgradeRoomEndpoint> {
4171 /// Returns a successful response with desired replacement_room ID.
4172 pub fn ok_with(self, new_room_id: &RoomId) -> MatrixMock<'a> {
4173 self.respond_with(
4174 ResponseTemplate::new(200)
4175 .set_body_json(json!({ "replacement_room": new_room_id.as_str()})),
4176 )
4177 }
4178}
4179
4180/// A prebuilt mock for `POST /media/v1/create` requests.
4181pub struct MediaAllocateEndpoint;
4182
4183impl<'a> MockEndpoint<'a, MediaAllocateEndpoint> {
4184 /// Returns a successful response.
4185 pub fn ok(self) -> MatrixMock<'a> {
4186 self.respond_with(ResponseTemplate::new(200).set_body_json(json!({
4187 "content_uri": "mxc://example.com/AQwafuaFswefuhsfAFAgsw"
4188 })))
4189 }
4190}
4191
4192/// A prebuilt mock for `PUT /media/v3/upload/{server_name}/{media_id}`
4193/// requests.
4194pub struct MediaAllocatedUploadEndpoint;
4195
4196impl<'a> MockEndpoint<'a, MediaAllocatedUploadEndpoint> {
4197 /// Returns a successful response.
4198 pub fn ok(self) -> MatrixMock<'a> {
4199 self.respond_with(ResponseTemplate::new(200).set_body_json(json!({})))
4200 }
4201}
4202
4203/// A prebuilt mock for `GET /media/v3/download` requests.
4204pub struct MediaDownloadEndpoint;
4205
4206impl<'a> MockEndpoint<'a, MediaDownloadEndpoint> {
4207 /// Returns a successful response with a plain text content.
4208 pub fn ok_plain_text(self) -> MatrixMock<'a> {
4209 self.respond_with(ResponseTemplate::new(200).set_body_string("Hello, World!"))
4210 }
4211
4212 /// Returns a successful response with a fake image content.
4213 pub fn ok_image(self) -> MatrixMock<'a> {
4214 self.respond_with(
4215 ResponseTemplate::new(200).set_body_raw(b"binaryjpegfullimagedata", "image/jpeg"),
4216 )
4217 }
4218}
4219
4220/// A prebuilt mock for `GET /media/v3/thumbnail` requests.
4221pub struct MediaThumbnailEndpoint;
4222
4223impl<'a> MockEndpoint<'a, MediaThumbnailEndpoint> {
4224 /// Returns a successful response with a fake image content.
4225 pub fn ok(self) -> MatrixMock<'a> {
4226 self.respond_with(
4227 ResponseTemplate::new(200).set_body_raw(b"binaryjpegthumbnaildata", "image/jpeg"),
4228 )
4229 }
4230}
4231
4232/// A prebuilt mock for `GET /client/v1/media/download` requests.
4233pub struct AuthedMediaDownloadEndpoint;
4234
4235impl<'a> MockEndpoint<'a, AuthedMediaDownloadEndpoint> {
4236 /// Returns a successful response with a plain text content.
4237 pub fn ok_plain_text(self) -> MatrixMock<'a> {
4238 self.respond_with(ResponseTemplate::new(200).set_body_string("Hello, World!"))
4239 }
4240
4241 /// Returns a successful response with the given bytes.
4242 pub fn ok_bytes(self, bytes: Vec<u8>) -> MatrixMock<'a> {
4243 self.respond_with(
4244 ResponseTemplate::new(200).set_body_raw(bytes, "application/octet-stream"),
4245 )
4246 }
4247
4248 /// Returns a successful response with a fake image content.
4249 pub fn ok_image(self) -> MatrixMock<'a> {
4250 self.respond_with(
4251 ResponseTemplate::new(200).set_body_raw(b"binaryjpegfullimagedata", "image/jpeg"),
4252 )
4253 }
4254}
4255
4256/// A prebuilt mock for `GET /client/v1/media/thumbnail` requests.
4257pub struct AuthedMediaThumbnailEndpoint;
4258
4259impl<'a> MockEndpoint<'a, AuthedMediaThumbnailEndpoint> {
4260 /// Returns a successful response with a fake image content.
4261 pub fn ok(self) -> MatrixMock<'a> {
4262 self.respond_with(
4263 ResponseTemplate::new(200).set_body_raw(b"binaryjpegthumbnaildata", "image/jpeg"),
4264 )
4265 }
4266}
4267
4268/// A prebuilt mock for `GET /client/v3/rooms/{room_id}/join` requests.
4269pub struct JoinRoomEndpoint {
4270 room_id: OwnedRoomId,
4271}
4272
4273impl<'a> MockEndpoint<'a, JoinRoomEndpoint> {
4274 /// Returns a successful response using the provided [`RoomId`].
4275 pub fn ok(self) -> MatrixMock<'a> {
4276 let room_id = self.endpoint.room_id.to_owned();
4277
4278 self.respond_with(ResponseTemplate::new(200).set_body_json(json!({
4279 "room_id": room_id,
4280 })))
4281 }
4282}
4283
4284#[derive(Default)]
4285struct ThreadSubscriptionMatchers {
4286 /// Optional room id to match in the query.
4287 room_id: Option<OwnedRoomId>,
4288 /// Optional thread root event id to match in the query.
4289 thread_root: Option<OwnedEventId>,
4290}
4291
4292impl ThreadSubscriptionMatchers {
4293 /// Match the request parameter against a specific room id.
4294 fn match_room_id(mut self, room_id: OwnedRoomId) -> Self {
4295 self.room_id = Some(room_id);
4296 self
4297 }
4298
4299 /// Match the request parameter against a specific thread root event id.
4300 fn match_thread_id(mut self, thread_root: OwnedEventId) -> Self {
4301 self.thread_root = Some(thread_root);
4302 self
4303 }
4304
4305 /// Compute the final URI for the thread subscription endpoint.
4306 fn endpoint_regexp_uri(&self) -> String {
4307 if self.room_id.is_some() || self.thread_root.is_some() {
4308 format!(
4309 "^/_matrix/client/unstable/io.element.msc4306/rooms/{}/thread/{}/subscription$",
4310 self.room_id.as_deref().map(|s| s.as_str()).unwrap_or(".*"),
4311 self.thread_root.as_deref().map(|s| s.as_str()).unwrap_or(".*").replace("$", "\\$")
4312 )
4313 } else {
4314 "^/_matrix/client/unstable/io.element.msc4306/rooms/.*/thread/.*/subscription$"
4315 .to_owned()
4316 }
4317 }
4318}
4319
4320/// A prebuilt mock for `GET
4321/// /client/*/rooms/{room_id}/threads/{thread_root}/subscription`
4322#[derive(Default)]
4323pub struct RoomGetThreadSubscriptionEndpoint {
4324 matchers: ThreadSubscriptionMatchers,
4325}
4326
4327impl<'a> MockEndpoint<'a, RoomGetThreadSubscriptionEndpoint> {
4328 /// Returns a successful response for the given thread subscription.
4329 pub fn ok(mut self, automatic: bool) -> MatrixMock<'a> {
4330 self.mock = self.mock.and(path_regex(self.endpoint.matchers.endpoint_regexp_uri()));
4331 self.respond_with(ResponseTemplate::new(200).set_body_json(json!({
4332 "automatic": automatic
4333 })))
4334 }
4335
4336 /// Match the request parameter against a specific room id.
4337 pub fn match_room_id(mut self, room_id: OwnedRoomId) -> Self {
4338 self.endpoint.matchers = self.endpoint.matchers.match_room_id(room_id);
4339 self
4340 }
4341 /// Match the request parameter against a specific thread root event id.
4342 pub fn match_thread_id(mut self, thread_root: OwnedEventId) -> Self {
4343 self.endpoint.matchers = self.endpoint.matchers.match_thread_id(thread_root);
4344 self
4345 }
4346}
4347
4348/// A prebuilt mock for `PUT
4349/// /client/*/rooms/{room_id}/threads/{thread_root}/subscription`
4350#[derive(Default)]
4351pub struct RoomPutThreadSubscriptionEndpoint {
4352 matchers: ThreadSubscriptionMatchers,
4353}
4354
4355impl<'a> MockEndpoint<'a, RoomPutThreadSubscriptionEndpoint> {
4356 /// Returns a successful response for the given setting of thread
4357 /// subscription.
4358 pub fn ok(mut self) -> MatrixMock<'a> {
4359 self.mock = self.mock.and(path_regex(self.endpoint.matchers.endpoint_regexp_uri()));
4360 self.respond_with(ResponseTemplate::new(200))
4361 }
4362
4363 /// Returns that the server skipped an automated thread subscription,
4364 /// because the user unsubscribed to the thread after the event id passed in
4365 /// the automatic subscription.
4366 pub fn conflicting_unsubscription(mut self) -> MatrixMock<'a> {
4367 self.mock = self.mock.and(path_regex(self.endpoint.matchers.endpoint_regexp_uri()));
4368 self.respond_with(ResponseTemplate::new(409).set_body_json(json!({
4369 "errcode": "IO.ELEMENT.MSC4306.M_CONFLICTING_UNSUBSCRIPTION",
4370 "error": "the user unsubscribed after the subscription event id"
4371 })))
4372 }
4373
4374 /// Match the request parameter against a specific room id.
4375 pub fn match_room_id(mut self, room_id: OwnedRoomId) -> Self {
4376 self.endpoint.matchers = self.endpoint.matchers.match_room_id(room_id);
4377 self
4378 }
4379 /// Match the request parameter against a specific thread root event id.
4380 pub fn match_thread_id(mut self, thread_root: OwnedEventId) -> Self {
4381 self.endpoint.matchers = self.endpoint.matchers.match_thread_id(thread_root);
4382 self
4383 }
4384 /// Match the request body's `automatic` field against a specific event id.
4385 pub fn match_automatic_event_id(mut self, up_to_event_id: &EventId) -> Self {
4386 self.mock = self.mock.and(body_json(json!({
4387 "automatic": up_to_event_id
4388 })));
4389 self
4390 }
4391}
4392
4393/// A prebuilt mock for `DELETE
4394/// /client/*/rooms/{room_id}/threads/{thread_root}/subscription`
4395#[derive(Default)]
4396pub struct RoomDeleteThreadSubscriptionEndpoint {
4397 matchers: ThreadSubscriptionMatchers,
4398}
4399
4400impl<'a> MockEndpoint<'a, RoomDeleteThreadSubscriptionEndpoint> {
4401 /// Returns a successful response for the deletion of a given thread
4402 /// subscription.
4403 pub fn ok(mut self) -> MatrixMock<'a> {
4404 self.mock = self.mock.and(path_regex(self.endpoint.matchers.endpoint_regexp_uri()));
4405 self.respond_with(ResponseTemplate::new(200))
4406 }
4407
4408 /// Match the request parameter against a specific room id.
4409 pub fn match_room_id(mut self, room_id: OwnedRoomId) -> Self {
4410 self.endpoint.matchers = self.endpoint.matchers.match_room_id(room_id);
4411 self
4412 }
4413 /// Match the request parameter against a specific thread root event id.
4414 pub fn match_thread_id(mut self, thread_root: OwnedEventId) -> Self {
4415 self.endpoint.matchers = self.endpoint.matchers.match_thread_id(thread_root);
4416 self
4417 }
4418}
4419
4420/// A prebuilt mock for `PUT
4421/// /_matrix/client/v3/pushrules/global/{kind}/{ruleId}/enabled`.
4422pub struct EnablePushRuleEndpoint;
4423
4424impl<'a> MockEndpoint<'a, EnablePushRuleEndpoint> {
4425 /// Returns a successful empty JSON response.
4426 pub fn ok(self) -> MatrixMock<'a> {
4427 self.ok_empty_json()
4428 }
4429}
4430
4431/// A prebuilt mock for `PUT
4432/// /_matrix/client/v3/pushrules/global/{kind}/{ruleId}/actions`.
4433pub struct SetPushRulesActionsEndpoint;
4434
4435impl<'a> MockEndpoint<'a, SetPushRulesActionsEndpoint> {
4436 /// Returns a successful empty JSON response.
4437 pub fn ok(self) -> MatrixMock<'a> {
4438 self.ok_empty_json()
4439 }
4440}
4441
4442/// A prebuilt mock for `PUT
4443/// /_matrix/client/v3/pushrules/global/{kind}/{ruleId}`.
4444pub struct SetPushRulesEndpoint;
4445
4446impl<'a> MockEndpoint<'a, SetPushRulesEndpoint> {
4447 /// Returns a successful empty JSON response.
4448 pub fn ok(self) -> MatrixMock<'a> {
4449 self.ok_empty_json()
4450 }
4451}
4452
4453/// A prebuilt mock for `DELETE
4454/// /_matrix/client/v3/pushrules/global/{kind}/{ruleId}`.
4455pub struct DeletePushRulesEndpoint;
4456
4457impl<'a> MockEndpoint<'a, DeletePushRulesEndpoint> {
4458 /// Returns a successful empty JSON response.
4459 pub fn ok(self) -> MatrixMock<'a> {
4460 self.ok_empty_json()
4461 }
4462}
4463
4464/// A prebuilt mock for the federation version endpoint.
4465pub struct FederationVersionEndpoint;
4466
4467impl<'a> MockEndpoint<'a, FederationVersionEndpoint> {
4468 /// Returns a successful response with the given server name and version.
4469 pub fn ok(self, server_name: &str, version: &str) -> MatrixMock<'a> {
4470 let response_body = json!({
4471 "server": {
4472 "name": server_name,
4473 "version": version
4474 }
4475 });
4476 self.respond_with(ResponseTemplate::new(200).set_body_json(response_body))
4477 }
4478
4479 /// Returns a successful response with empty/missing server information.
4480 pub fn ok_empty(self) -> MatrixMock<'a> {
4481 let response_body = json!({});
4482 self.respond_with(ResponseTemplate::new(200).set_body_json(response_body))
4483 }
4484}
4485
4486/// A prebuilt mock for `GET ^/_matrix/client/v3/thread_subscriptions`.
4487#[derive(Default)]
4488pub struct GetThreadSubscriptionsEndpoint {
4489 /// New thread subscriptions per (room id, thread root event id).
4490 subscribed: BTreeMap<OwnedRoomId, BTreeMap<OwnedEventId, ThreadSubscription>>,
4491 /// New thread unsubscriptions per (room id, thread root event id).
4492 unsubscribed: BTreeMap<OwnedRoomId, BTreeMap<OwnedEventId, ThreadUnsubscription>>,
4493 /// Optional delay to respond to the query.
4494 delay: Option<Duration>,
4495}
4496
4497impl<'a> MockEndpoint<'a, GetThreadSubscriptionsEndpoint> {
4498 /// Add a single thread subscription to the response.
4499 pub fn add_subscription(
4500 mut self,
4501 room_id: OwnedRoomId,
4502 thread_root: OwnedEventId,
4503 subscription: ThreadSubscription,
4504 ) -> Self {
4505 self.endpoint.subscribed.entry(room_id).or_default().insert(thread_root, subscription);
4506 self
4507 }
4508
4509 /// Add a single thread unsubscription to the response.
4510 pub fn add_unsubscription(
4511 mut self,
4512 room_id: OwnedRoomId,
4513 thread_root: OwnedEventId,
4514 unsubscription: ThreadUnsubscription,
4515 ) -> Self {
4516 self.endpoint.unsubscribed.entry(room_id).or_default().insert(thread_root, unsubscription);
4517 self
4518 }
4519
4520 /// Respond with a given delay to the query.
4521 pub fn with_delay(mut self, delay: Duration) -> Self {
4522 self.endpoint.delay = Some(delay);
4523 self
4524 }
4525
4526 /// Match the `from` query parameter to a given value.
4527 pub fn match_from(self, from: &str) -> Self {
4528 Self { mock: self.mock.and(query_param("from", from)), ..self }
4529 }
4530 /// Match the `to` query parameter to a given value.
4531 pub fn match_to(self, to: &str) -> Self {
4532 Self { mock: self.mock.and(query_param("to", to)), ..self }
4533 }
4534
4535 /// Returns a successful response with the given thread subscriptions, and
4536 /// "end" parameter to be used in the next query.
4537 pub fn ok(self, end: Option<String>) -> MatrixMock<'a> {
4538 let response_body = json!({
4539 "subscribed": self.endpoint.subscribed,
4540 "unsubscribed": self.endpoint.unsubscribed,
4541 "end": end,
4542 });
4543
4544 let mut template = ResponseTemplate::new(200).set_body_json(response_body);
4545
4546 if let Some(delay) = self.endpoint.delay {
4547 template = template.set_delay(delay);
4548 }
4549
4550 self.respond_with(template)
4551 }
4552}
4553
4554/// A prebuilt mock for `GET /client/*/rooms/{roomId}/hierarchy`
4555#[derive(Default)]
4556pub struct GetHierarchyEndpoint;
4557
4558impl<'a> MockEndpoint<'a, GetHierarchyEndpoint> {
4559 /// Returns a successful response containing the given room IDs.
4560 pub fn ok_with_room_ids(self, room_ids: Vec<&RoomId>) -> MatrixMock<'a> {
4561 let rooms = room_ids
4562 .iter()
4563 .map(|id| {
4564 json!({
4565 "room_id": id,
4566 "num_joined_members": 1,
4567 "world_readable": false,
4568 "guest_can_join": false,
4569 "children_state": []
4570 })
4571 })
4572 .collect::<Vec<_>>();
4573
4574 self.respond_with(ResponseTemplate::new(200).set_body_json(json!({
4575 "rooms": rooms,
4576 })))
4577 }
4578
4579 /// Returns a successful response containing the given room IDs and children
4580 /// states
4581 pub fn ok_with_room_ids_and_children_state(
4582 self,
4583 room_ids: Vec<&RoomId>,
4584 children_state: Vec<(&RoomId, Vec<&ServerName>)>,
4585 ) -> MatrixMock<'a> {
4586 let children_state = children_state
4587 .into_iter()
4588 .map(|(id, via)| {
4589 json!({
4590 "type":
4591 "m.space.child",
4592 "state_key": id,
4593 "content": { "via": via },
4594 "sender": "@bob:matrix.org",
4595 "origin_server_ts": MilliSecondsSinceUnixEpoch::now()
4596 })
4597 })
4598 .collect::<Vec<_>>();
4599
4600 let rooms = room_ids
4601 .iter()
4602 .map(|id| {
4603 json!({
4604 "room_id": id,
4605 "num_joined_members": 1,
4606 "world_readable": false,
4607 "guest_can_join": false,
4608 "children_state": children_state
4609 })
4610 })
4611 .collect::<Vec<_>>();
4612
4613 self.respond_with(ResponseTemplate::new(200).set_body_json(json!({
4614 "rooms": rooms,
4615 })))
4616 }
4617
4618 /// Returns a successful response with an empty list of rooms.
4619 pub fn ok(self) -> MatrixMock<'a> {
4620 self.respond_with(ResponseTemplate::new(200).set_body_json(json!({
4621 "rooms": []
4622 })))
4623 }
4624}
4625
4626/// A prebuilt mock for running simplified sliding sync.
4627pub struct SlidingSyncEndpoint;
4628
4629impl<'a> MockEndpoint<'a, SlidingSyncEndpoint> {
4630 /// Mocks the sliding sync endpoint with the given response.
4631 pub fn ok(self, response: v5::Response) -> MatrixMock<'a> {
4632 // A bit silly that we need to destructure all the fields ourselves, but
4633 // Response isn't serializable :'(
4634 self.respond_with(ResponseTemplate::new(200).set_body_json(json!({
4635 "txn_id": response.txn_id,
4636 "pos": response.pos,
4637 "lists": response.lists,
4638 "rooms": response.rooms,
4639 "extensions": response.extensions,
4640 })))
4641 }
4642
4643 /// Temporarily mocks the sync with the given endpoint and runs a client
4644 /// sync with it.
4645 ///
4646 /// After calling this function, the sync endpoint isn't mocked anymore.
4647 pub async fn ok_and_run<F: FnOnce(SlidingSyncBuilder) -> SlidingSyncBuilder>(
4648 self,
4649 client: &Client,
4650 on_builder: F,
4651 response: v5::Response,
4652 ) {
4653 let _scope = self.ok(response).mount_as_scoped().await;
4654
4655 let sliding_sync =
4656 on_builder(client.sliding_sync("test_id").unwrap()).build().await.unwrap();
4657
4658 let _summary = sliding_sync.sync_once().await.unwrap();
4659 }
4660}