matrix_sdk/test_utils/mocks/
mod.rs

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