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