Skip to main content

matrix_sdk/test_utils/mocks/
mod.rs

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