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;
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 send a single receipt.
1249    pub fn mock_send_receipt(
1250        &self,
1251        receipt_type: ReceiptType,
1252    ) -> MockEndpoint<'_, ReceiptEndpoint> {
1253        let mock = Mock::given(method("POST"))
1254            .and(path_regex(format!("^/_matrix/client/v3/rooms/.*/receipt/{receipt_type}/")));
1255        self.mock_endpoint(mock, ReceiptEndpoint).expect_default_access_token()
1256    }
1257
1258    /// Create a prebuilt mock for the endpoint used to send multiple receipts.
1259    pub fn mock_send_read_markers(&self) -> MockEndpoint<'_, ReadMarkersEndpoint> {
1260        let mock = Mock::given(method("POST"))
1261            .and(path_regex(r"^/_matrix/client/v3/rooms/.*/read_markers"));
1262        self.mock_endpoint(mock, ReadMarkersEndpoint).expect_default_access_token()
1263    }
1264
1265    /// Create a prebuilt mock for the endpoint used to set room account data.
1266    pub fn mock_set_room_account_data(
1267        &self,
1268        data_type: RoomAccountDataEventType,
1269    ) -> MockEndpoint<'_, RoomAccountDataEndpoint> {
1270        let mock = Mock::given(method("PUT")).and(path_regex(format!(
1271            "^/_matrix/client/v3/user/.*/rooms/.*/account_data/{data_type}"
1272        )));
1273        self.mock_endpoint(mock, RoomAccountDataEndpoint).expect_default_access_token()
1274    }
1275
1276    /// Create a prebuilt mock for the endpoint used to get the media config of
1277    /// the homeserver that requires authentication.
1278    pub fn mock_authenticated_media_config(
1279        &self,
1280    ) -> MockEndpoint<'_, AuthenticatedMediaConfigEndpoint> {
1281        let mock = Mock::given(method("GET")).and(path("/_matrix/client/v1/media/config"));
1282        self.mock_endpoint(mock, AuthenticatedMediaConfigEndpoint)
1283    }
1284
1285    /// Create a prebuilt mock for the endpoint used to get the media config of
1286    /// the homeserver without requiring authentication.
1287    pub fn mock_media_config(&self) -> MockEndpoint<'_, MediaConfigEndpoint> {
1288        let mock = Mock::given(method("GET")).and(path("/_matrix/media/v3/config"));
1289        self.mock_endpoint(mock, MediaConfigEndpoint)
1290    }
1291
1292    /// Create a prebuilt mock for the endpoint used to log into a session.
1293    pub fn mock_login(&self) -> MockEndpoint<'_, LoginEndpoint> {
1294        let mock = Mock::given(method("POST")).and(path("/_matrix/client/v3/login"));
1295        self.mock_endpoint(mock, LoginEndpoint)
1296    }
1297
1298    /// Create a prebuilt mock for the endpoint used to list the devices of a
1299    /// user.
1300    pub fn mock_devices(&self) -> MockEndpoint<'_, DevicesEndpoint> {
1301        let mock = Mock::given(method("GET")).and(path("/_matrix/client/v3/devices"));
1302        self.mock_endpoint(mock, DevicesEndpoint).expect_default_access_token()
1303    }
1304
1305    /// Create a prebuilt mock for the endpoint used to search in the user
1306    /// directory.
1307    pub fn mock_user_directory(&self) -> MockEndpoint<'_, UserDirectoryEndpoint> {
1308        let mock = Mock::given(method("POST"))
1309            .and(path("/_matrix/client/v3/user_directory/search"))
1310            .and(body_json(&*test_json::search_users::SEARCH_USERS_REQUEST));
1311        self.mock_endpoint(mock, UserDirectoryEndpoint).expect_default_access_token()
1312    }
1313
1314    /// Create a prebuilt mock for the endpoint used to create a new room.
1315    pub fn mock_create_room(&self) -> MockEndpoint<'_, CreateRoomEndpoint> {
1316        let mock = Mock::given(method("POST")).and(path("/_matrix/client/v3/createRoom"));
1317        self.mock_endpoint(mock, CreateRoomEndpoint).expect_default_access_token()
1318    }
1319
1320    /// Create a prebuilt mock for the endpoint used to upgrade a room.
1321    pub fn mock_upgrade_room(&self) -> MockEndpoint<'_, UpgradeRoomEndpoint> {
1322        let mock =
1323            Mock::given(method("POST")).and(path_regex("/_matrix/client/v3/rooms/.*/upgrade"));
1324        self.mock_endpoint(mock, UpgradeRoomEndpoint).expect_default_access_token()
1325    }
1326
1327    /// Create a prebuilt mock for the endpoint used to pre-allocate a MXC URI
1328    /// for a media file.
1329    pub fn mock_media_allocate(&self) -> MockEndpoint<'_, MediaAllocateEndpoint> {
1330        let mock = Mock::given(method("POST")).and(path("/_matrix/media/v1/create"));
1331        self.mock_endpoint(mock, MediaAllocateEndpoint)
1332    }
1333
1334    /// Create a prebuilt mock for the endpoint used to upload a media file with
1335    /// a pre-allocated MXC URI.
1336    pub fn mock_media_allocated_upload(
1337        &self,
1338        server_name: &str,
1339        media_id: &str,
1340    ) -> MockEndpoint<'_, MediaAllocatedUploadEndpoint> {
1341        let mock = Mock::given(method("PUT"))
1342            .and(path(format!("/_matrix/media/v3/upload/{server_name}/{media_id}")));
1343        self.mock_endpoint(mock, MediaAllocatedUploadEndpoint)
1344    }
1345
1346    /// Create a prebuilt mock for the endpoint used to download a media file
1347    /// without requiring authentication.
1348    pub fn mock_media_download(&self) -> MockEndpoint<'_, MediaDownloadEndpoint> {
1349        let mock = Mock::given(method("GET")).and(path_regex("^/_matrix/media/v3/download/"));
1350        self.mock_endpoint(mock, MediaDownloadEndpoint)
1351    }
1352
1353    /// Create a prebuilt mock for the endpoint used to download a thumbnail of
1354    /// a media file without requiring authentication.
1355    pub fn mock_media_thumbnail(
1356        &self,
1357        resize_method: Method,
1358        width: u16,
1359        height: u16,
1360        animated: bool,
1361    ) -> MockEndpoint<'_, MediaThumbnailEndpoint> {
1362        let mock = Mock::given(method("GET"))
1363            .and(path_regex("^/_matrix/media/v3/thumbnail/"))
1364            .and(query_param("method", resize_method.as_str()))
1365            .and(query_param("width", width.to_string()))
1366            .and(query_param("height", height.to_string()))
1367            .and(query_param("animated", animated.to_string()));
1368        self.mock_endpoint(mock, MediaThumbnailEndpoint)
1369    }
1370
1371    /// Create a prebuilt mock for the endpoint used to download a media file
1372    /// that requires authentication.
1373    pub fn mock_authed_media_download(&self) -> MockEndpoint<'_, AuthedMediaDownloadEndpoint> {
1374        let mock =
1375            Mock::given(method("GET")).and(path_regex("^/_matrix/client/v1/media/download/"));
1376        self.mock_endpoint(mock, AuthedMediaDownloadEndpoint).expect_default_access_token()
1377    }
1378
1379    /// Create a prebuilt mock for the endpoint used to download a thumbnail of
1380    /// a media file that requires authentication.
1381    pub fn mock_authed_media_thumbnail(
1382        &self,
1383        resize_method: Method,
1384        width: u16,
1385        height: u16,
1386        animated: bool,
1387    ) -> MockEndpoint<'_, AuthedMediaThumbnailEndpoint> {
1388        let mock = Mock::given(method("GET"))
1389            .and(path_regex("^/_matrix/client/v1/media/thumbnail/"))
1390            .and(query_param("method", resize_method.as_str()))
1391            .and(query_param("width", width.to_string()))
1392            .and(query_param("height", height.to_string()))
1393            .and(query_param("animated", animated.to_string()));
1394        self.mock_endpoint(mock, AuthedMediaThumbnailEndpoint).expect_default_access_token()
1395    }
1396
1397    /// Create a prebuilt mock for the endpoint used to get a single thread
1398    /// subscription status in a given room.
1399    pub fn mock_room_get_thread_subscription(
1400        &self,
1401    ) -> MockEndpoint<'_, RoomGetThreadSubscriptionEndpoint> {
1402        let mock = Mock::given(method("GET"));
1403        self.mock_endpoint(mock, RoomGetThreadSubscriptionEndpoint::default())
1404            .expect_default_access_token()
1405    }
1406
1407    /// Create a prebuilt mock for the endpoint used to define a thread
1408    /// subscription in a given room.
1409    pub fn mock_room_put_thread_subscription(
1410        &self,
1411    ) -> MockEndpoint<'_, RoomPutThreadSubscriptionEndpoint> {
1412        let mock = Mock::given(method("PUT"));
1413        self.mock_endpoint(mock, RoomPutThreadSubscriptionEndpoint::default())
1414            .expect_default_access_token()
1415    }
1416
1417    /// Create a prebuilt mock for the endpoint used to delete a thread
1418    /// subscription in a given room.
1419    pub fn mock_room_delete_thread_subscription(
1420        &self,
1421    ) -> MockEndpoint<'_, RoomDeleteThreadSubscriptionEndpoint> {
1422        let mock = Mock::given(method("DELETE"));
1423        self.mock_endpoint(mock, RoomDeleteThreadSubscriptionEndpoint::default())
1424            .expect_default_access_token()
1425    }
1426
1427    /// Create a prebuilt mock for the endpoint used to enable a push rule.
1428    pub fn mock_enable_push_rule(
1429        &self,
1430        kind: RuleKind,
1431        rule_id: impl AsRef<str>,
1432    ) -> MockEndpoint<'_, EnablePushRuleEndpoint> {
1433        let rule_id = rule_id.as_ref();
1434        let mock = Mock::given(method("PUT")).and(path_regex(format!(
1435            "^/_matrix/client/v3/pushrules/global/{kind}/{rule_id}/enabled",
1436        )));
1437        self.mock_endpoint(mock, EnablePushRuleEndpoint).expect_default_access_token()
1438    }
1439
1440    /// Create a prebuilt mock for the endpoint used to set push rules actions.
1441    pub fn mock_set_push_rules_actions(
1442        &self,
1443        kind: RuleKind,
1444        rule_id: PushRuleIdSpec<'_>,
1445    ) -> MockEndpoint<'_, SetPushRulesActionsEndpoint> {
1446        let rule_id = rule_id.to_path();
1447        let mock = Mock::given(method("PUT")).and(path_regex(format!(
1448            "^/_matrix/client/v3/pushrules/global/{kind}/{rule_id}/actions",
1449        )));
1450        self.mock_endpoint(mock, SetPushRulesActionsEndpoint).expect_default_access_token()
1451    }
1452
1453    /// Create a prebuilt mock for the endpoint used to set push rules.
1454    pub fn mock_set_push_rules(
1455        &self,
1456        kind: RuleKind,
1457        rule_id: PushRuleIdSpec<'_>,
1458    ) -> MockEndpoint<'_, SetPushRulesEndpoint> {
1459        let rule_id = rule_id.to_path();
1460        let mock = Mock::given(method("PUT"))
1461            .and(path_regex(format!("^/_matrix/client/v3/pushrules/global/{kind}/{rule_id}$",)));
1462        self.mock_endpoint(mock, SetPushRulesEndpoint).expect_default_access_token()
1463    }
1464
1465    /// Create a prebuilt mock for the endpoint used to delete push rules.
1466    pub fn mock_delete_push_rules(
1467        &self,
1468        kind: RuleKind,
1469        rule_id: PushRuleIdSpec<'_>,
1470    ) -> MockEndpoint<'_, DeletePushRulesEndpoint> {
1471        let rule_id = rule_id.to_path();
1472        let mock = Mock::given(method("DELETE"))
1473            .and(path_regex(format!("^/_matrix/client/v3/pushrules/global/{kind}/{rule_id}$",)));
1474        self.mock_endpoint(mock, DeletePushRulesEndpoint).expect_default_access_token()
1475    }
1476
1477    /// Create a prebuilt mock for the federation version endpoint.
1478    pub fn mock_federation_version(&self) -> MockEndpoint<'_, FederationVersionEndpoint> {
1479        let mock = Mock::given(method("GET")).and(path("/_matrix/federation/v1/version"));
1480        self.mock_endpoint(mock, FederationVersionEndpoint)
1481    }
1482
1483    /// Create a prebuilt mock for the endpoint used to get all thread
1484    /// subscriptions across all rooms.
1485    pub fn mock_get_thread_subscriptions(
1486        &self,
1487    ) -> MockEndpoint<'_, GetThreadSubscriptionsEndpoint> {
1488        let mock = Mock::given(method("GET"))
1489            .and(path_regex(r"^/_matrix/client/unstable/io.element.msc4308/thread_subscriptions$"));
1490        self.mock_endpoint(mock, GetThreadSubscriptionsEndpoint::default())
1491            .expect_default_access_token()
1492    }
1493
1494    /// Create a prebuilt mock for the endpoint used to retrieve a space tree
1495    pub fn mock_get_hierarchy(&self) -> MockEndpoint<'_, GetHierarchyEndpoint> {
1496        let mock =
1497            Mock::given(method("GET")).and(path_regex(r"^/_matrix/client/v1/rooms/.*/hierarchy"));
1498        self.mock_endpoint(mock, GetHierarchyEndpoint).expect_default_access_token()
1499    }
1500}
1501
1502/// A specification for a push rule ID.
1503pub enum PushRuleIdSpec<'a> {
1504    /// A precise rule ID.
1505    Some(&'a str),
1506    /// Any rule ID should match.
1507    Any,
1508}
1509
1510impl<'a> PushRuleIdSpec<'a> {
1511    /// Convert this [`PushRuleIdSpec`] to a path.
1512    pub fn to_path(&self) -> &str {
1513        match self {
1514            PushRuleIdSpec::Some(id) => id,
1515            PushRuleIdSpec::Any => "[^/]*",
1516        }
1517    }
1518}
1519
1520/// Parameter to [`MatrixMockServer::sync_room`].
1521pub enum AnyRoomBuilder {
1522    /// A room we've been invited to.
1523    Invited(InvitedRoomBuilder),
1524    /// A room we've joined.
1525    Joined(JoinedRoomBuilder),
1526    /// A room we've left.
1527    Left(LeftRoomBuilder),
1528    /// A room we've knocked to.
1529    Knocked(KnockedRoomBuilder),
1530}
1531
1532impl AnyRoomBuilder {
1533    /// Get the [`RoomId`] of the room this [`AnyRoomBuilder`] will create.
1534    fn room_id(&self) -> &RoomId {
1535        match self {
1536            AnyRoomBuilder::Invited(r) => r.room_id(),
1537            AnyRoomBuilder::Joined(r) => r.room_id(),
1538            AnyRoomBuilder::Left(r) => r.room_id(),
1539            AnyRoomBuilder::Knocked(r) => r.room_id(),
1540        }
1541    }
1542}
1543
1544impl From<InvitedRoomBuilder> for AnyRoomBuilder {
1545    fn from(val: InvitedRoomBuilder) -> AnyRoomBuilder {
1546        AnyRoomBuilder::Invited(val)
1547    }
1548}
1549
1550impl From<JoinedRoomBuilder> for AnyRoomBuilder {
1551    fn from(val: JoinedRoomBuilder) -> AnyRoomBuilder {
1552        AnyRoomBuilder::Joined(val)
1553    }
1554}
1555
1556impl From<LeftRoomBuilder> for AnyRoomBuilder {
1557    fn from(val: LeftRoomBuilder) -> AnyRoomBuilder {
1558        AnyRoomBuilder::Left(val)
1559    }
1560}
1561
1562impl From<KnockedRoomBuilder> for AnyRoomBuilder {
1563    fn from(val: KnockedRoomBuilder) -> AnyRoomBuilder {
1564        AnyRoomBuilder::Knocked(val)
1565    }
1566}
1567
1568/// The [path percent-encode set] as defined in the WHATWG URL standard + `/`
1569/// since we always encode single segments of the path.
1570///
1571/// [path percent-encode set]: https://url.spec.whatwg.org/#path-percent-encode-set
1572///
1573/// Copied from Ruma:
1574/// https://github.com/ruma/ruma/blob/e4cb409ff3aaa16f31a7fe1e61fee43b2d144f7b/crates/ruma-common/src/percent_encode.rs#L7
1575const PATH_PERCENT_ENCODE_SET: &AsciiSet = &CONTROLS
1576    .add(b' ')
1577    .add(b'"')
1578    .add(b'#')
1579    .add(b'<')
1580    .add(b'>')
1581    .add(b'?')
1582    .add(b'`')
1583    .add(b'{')
1584    .add(b'}')
1585    .add(b'/');
1586
1587fn percent_encoded_path(path: &str) -> String {
1588    percent_encoding::utf8_percent_encode(path, PATH_PERCENT_ENCODE_SET).to_string()
1589}
1590
1591/// A wrapper for a [`Mock`] as well as a [`MockServer`], allowing us to call
1592/// [`Mock::mount`] or [`Mock::mount_as_scoped`] without having to pass the
1593/// [`MockServer`] reference (i.e. call `mount()` instead of `mount(&server)`).
1594pub struct MatrixMock<'a> {
1595    pub(super) mock: Mock,
1596    pub(super) server: &'a MockServer,
1597}
1598
1599impl MatrixMock<'_> {
1600    /// Set an expectation on the number of times this [`MatrixMock`] should
1601    /// match in the current test case.
1602    ///
1603    /// Expectations are verified when the server is shutting down: if
1604    /// the expectation is not satisfied, the [`MatrixMockServer`] will panic
1605    /// and the `error_message` is shown.
1606    ///
1607    /// By default, no expectation is set for [`MatrixMock`]s.
1608    pub fn expect<T: Into<Times>>(self, num_calls: T) -> Self {
1609        Self { mock: self.mock.expect(num_calls), ..self }
1610    }
1611
1612    /// Assign a name to your mock.
1613    ///
1614    /// The mock name will be used in error messages (e.g. if the mock
1615    /// expectation is not satisfied) and debug logs to help you identify
1616    /// what failed.
1617    pub fn named(self, name: impl Into<String>) -> Self {
1618        Self { mock: self.mock.named(name), ..self }
1619    }
1620
1621    /// Respond to a response of this endpoint exactly once.
1622    ///
1623    /// After it's been called, subsequent responses will hit the next handler
1624    /// or a 404.
1625    ///
1626    /// Also verifies that it's been called once.
1627    pub fn mock_once(self) -> Self {
1628        Self { mock: self.mock.up_to_n_times(1).expect(1), ..self }
1629    }
1630
1631    /// Makes sure the endpoint is never reached.
1632    pub fn never(self) -> Self {
1633        Self { mock: self.mock.expect(0), ..self }
1634    }
1635
1636    /// Specify an upper limit to the number of times you would like this
1637    /// [`MatrixMock`] to respond to incoming requests that satisfy the
1638    /// conditions imposed by your matchers.
1639    pub fn up_to_n_times(self, num: u64) -> Self {
1640        Self { mock: self.mock.up_to_n_times(num), ..self }
1641    }
1642
1643    /// Mount a [`MatrixMock`] on the attached server.
1644    ///
1645    /// The [`MatrixMock`] will remain active until the [`MatrixMockServer`] is
1646    /// shut down. If you want to control or limit how long your
1647    /// [`MatrixMock`] stays active, check out [`Self::mount_as_scoped`].
1648    pub async fn mount(self) {
1649        self.mock.mount(self.server).await;
1650    }
1651
1652    /// Mount a [`MatrixMock`] as **scoped** on the attached server.
1653    ///
1654    /// When using [`Self::mount`], your [`MatrixMock`]s will be active until
1655    /// the [`MatrixMockServer`] is shut down.
1656    ///
1657    /// When using `mount_as_scoped`, your [`MatrixMock`]s will be active as
1658    /// long as the returned [`MockGuard`] is not dropped.
1659    ///
1660    /// When the returned [`MockGuard`] is dropped, [`MatrixMockServer`] will
1661    /// verify that the expectations set on the scoped [`MatrixMock`] were
1662    /// verified - if not, it will panic.
1663    pub async fn mount_as_scoped(self) -> MockGuard {
1664        self.mock.mount_as_scoped(self.server).await
1665    }
1666}
1667
1668/// Generic mocked endpoint, with useful common helpers.
1669pub struct MockEndpoint<'a, T> {
1670    server: &'a MockServer,
1671    mock: MockBuilder,
1672    endpoint: T,
1673    expected_access_token: ExpectedAccessToken,
1674}
1675
1676impl<'a, T> MockEndpoint<'a, T> {
1677    fn new(server: &'a MockServer, mock: MockBuilder, endpoint: T) -> Self {
1678        Self { server, mock, endpoint, expected_access_token: ExpectedAccessToken::None }
1679    }
1680
1681    /// Expect authentication with the default access token on this endpoint.
1682    pub fn expect_default_access_token(mut self) -> Self {
1683        self.expected_access_token = ExpectedAccessToken::Default;
1684        self
1685    }
1686
1687    /// Expect authentication with the given access token on this endpoint.
1688    pub fn expect_access_token(mut self, access_token: &'static str) -> Self {
1689        self.expected_access_token = ExpectedAccessToken::Custom(access_token);
1690        self
1691    }
1692
1693    /// Don't expect authentication with an access token on this endpoint.
1694    ///
1695    /// This should be used to override the default behavior of the endpoint,
1696    /// when the access token is unknown for example.
1697    pub fn do_not_expect_access_token(mut self) -> Self {
1698        self.expected_access_token = ExpectedAccessToken::None;
1699        self
1700    }
1701
1702    /// Specify how to respond to a query (viz., like
1703    /// [`MockBuilder::respond_with`] does), when other predefined responses
1704    /// aren't sufficient.
1705    ///
1706    /// # Examples
1707    ///
1708    /// ```
1709    /// # tokio_test::block_on(async {
1710    /// use matrix_sdk::{ruma::{room_id, event_id}, test_utils::mocks::MatrixMockServer};
1711    /// use serde_json::json;
1712    /// use wiremock::ResponseTemplate;
1713    ///
1714    /// let mock_server = MatrixMockServer::new().await;
1715    /// let client = mock_server.client_builder().build().await;
1716    ///
1717    /// mock_server.mock_room_state_encryption().plain().mount().await;
1718    ///
1719    /// let room = mock_server
1720    ///     .sync_joined_room(&client, room_id!("!room_id:localhost"))
1721    ///     .await;
1722    ///
1723    /// let event_id = event_id!("$some_id");
1724    /// mock_server
1725    ///     .mock_room_send()
1726    ///     .respond_with(
1727    ///         ResponseTemplate::new(429)
1728    ///             .insert_header("Retry-After", "100")
1729    ///             .set_body_json(json!({
1730    ///                 "errcode": "M_LIMIT_EXCEEDED",
1731    ///                 "custom_field": "with custom data",
1732    ///     })))
1733    ///     .expect(1)
1734    ///     .mount()
1735    ///     .await;
1736    ///
1737    /// room
1738    ///     .send_raw("m.room.message", json!({ "body": "Hello world" }))
1739    ///     .await
1740    ///     .expect_err("The sending of the event should fail");
1741    /// # anyhow::Ok(()) });
1742    /// ```
1743    pub fn respond_with<R: Respond + 'static>(self, func: R) -> MatrixMock<'a> {
1744        let mock = self
1745            .expected_access_token
1746            .maybe_match_authorization_header(self.mock)
1747            .respond_with(func);
1748        MatrixMock { mock, server: self.server }
1749    }
1750
1751    /// Returns a send endpoint that emulates a transient failure, i.e responds
1752    /// with error 500.
1753    ///
1754    /// # Examples
1755    /// ```
1756    /// # tokio_test::block_on(async {
1757    /// use matrix_sdk::{ruma::{room_id, event_id}, test_utils::mocks::MatrixMockServer};
1758    /// use serde_json::json;
1759    ///
1760    /// let mock_server = MatrixMockServer::new().await;
1761    /// let client = mock_server.client_builder().build().await;
1762    ///
1763    /// mock_server.mock_room_state_encryption().plain().mount().await;
1764    ///
1765    /// let room = mock_server
1766    ///     .sync_joined_room(&client, room_id!("!room_id:localhost"))
1767    ///     .await;
1768    ///
1769    /// mock_server
1770    ///     .mock_room_send()
1771    ///     .error500()
1772    ///     .expect(1)
1773    ///     .mount()
1774    ///     .await;
1775    ///
1776    /// room
1777    ///     .send_raw("m.room.message", json!({ "body": "Hello world" }))
1778    ///     .await.expect_err("The sending of the event should have failed");
1779    /// # anyhow::Ok(()) });
1780    /// ```
1781    pub fn error500(self) -> MatrixMock<'a> {
1782        self.respond_with(ResponseTemplate::new(500))
1783    }
1784
1785    /// Internal helper to return an `{ event_id }` JSON struct along with a 200
1786    /// ok response.
1787    fn ok_with_event_id(self, event_id: OwnedEventId) -> MatrixMock<'a> {
1788        self.respond_with(ResponseTemplate::new(200).set_body_json(json!({ "event_id": event_id })))
1789    }
1790
1791    /// Internal helper to return a 200 OK response with an empty JSON object in
1792    /// the body.
1793    fn ok_empty_json(self) -> MatrixMock<'a> {
1794        self.respond_with(ResponseTemplate::new(200).set_body_json(json!({})))
1795    }
1796
1797    /// Returns an endpoint that emulates a permanent failure error (e.g. event
1798    /// is too large).
1799    ///
1800    /// # Examples
1801    /// ```
1802    /// # tokio_test::block_on(async {
1803    /// use matrix_sdk::{ruma::{room_id, event_id}, test_utils::mocks::MatrixMockServer};
1804    /// use serde_json::json;
1805    ///
1806    /// let mock_server = MatrixMockServer::new().await;
1807    /// let client = mock_server.client_builder().build().await;
1808    ///
1809    /// mock_server.mock_room_state_encryption().plain().mount().await;
1810    ///
1811    /// let room = mock_server
1812    ///     .sync_joined_room(&client, room_id!("!room_id:localhost"))
1813    ///     .await;
1814    ///
1815    /// mock_server
1816    ///     .mock_room_send()
1817    ///     .error_too_large()
1818    ///     .expect(1)
1819    ///     .mount()
1820    ///     .await;
1821    ///
1822    /// room
1823    ///     .send_raw("m.room.message", json!({ "body": "Hello world" }))
1824    ///     .await.expect_err("The sending of the event should have failed");
1825    /// # anyhow::Ok(()) });
1826    /// ```
1827    pub fn error_too_large(self) -> MatrixMock<'a> {
1828        self.respond_with(ResponseTemplate::new(413).set_body_json(json!({
1829            // From https://spec.matrix.org/v1.10/client-server-api/#standard-error-response
1830            "errcode": "M_TOO_LARGE",
1831        })))
1832    }
1833}
1834
1835/// The access token to expect on an endpoint.
1836enum ExpectedAccessToken {
1837    /// We don't expect an access token.
1838    None,
1839
1840    /// We expect the default access token.
1841    Default,
1842
1843    /// We expect the given access token.
1844    Custom(&'static str),
1845}
1846
1847impl ExpectedAccessToken {
1848    /// Match an `Authorization` header on the given mock if one is expected.
1849    fn maybe_match_authorization_header(&self, mock: MockBuilder) -> MockBuilder {
1850        let token = match self {
1851            Self::None => return mock,
1852            Self::Default => "1234",
1853            Self::Custom(token) => token,
1854        };
1855        mock.and(header(http::header::AUTHORIZATION, format!("Bearer {token}")))
1856    }
1857}
1858
1859/// A prebuilt mock for sending a message like event in a room.
1860pub struct RoomSendEndpoint;
1861
1862impl<'a> MockEndpoint<'a, RoomSendEndpoint> {
1863    /// Ensures that the body of the request is a superset of the provided
1864    /// `body` parameter.
1865    ///
1866    /// # Examples
1867    /// ```
1868    /// # tokio_test::block_on(async {
1869    /// use matrix_sdk::{
1870    ///     ruma::{room_id, event_id, events::room::message::RoomMessageEventContent},
1871    ///     test_utils::mocks::MatrixMockServer
1872    /// };
1873    /// use serde_json::json;
1874    ///
1875    /// let mock_server = MatrixMockServer::new().await;
1876    /// let client = mock_server.client_builder().build().await;
1877    ///
1878    /// mock_server.mock_room_state_encryption().plain().mount().await;
1879    ///
1880    /// let room = mock_server
1881    ///     .sync_joined_room(&client, room_id!("!room_id:localhost"))
1882    ///     .await;
1883    ///
1884    /// let event_id = event_id!("$some_id");
1885    /// mock_server
1886    ///     .mock_room_send()
1887    ///     .body_matches_partial_json(json!({
1888    ///         "body": "Hello world",
1889    ///     }))
1890    ///     .ok(event_id)
1891    ///     .expect(1)
1892    ///     .mount()
1893    ///     .await;
1894    ///
1895    /// let content = RoomMessageEventContent::text_plain("Hello world");
1896    /// let response = room.send(content).await?;
1897    ///
1898    /// assert_eq!(
1899    ///     event_id,
1900    ///     response.event_id,
1901    ///     "The event ID we mocked should match the one we received when we sent the event"
1902    /// );
1903    /// # anyhow::Ok(()) });
1904    /// ```
1905    pub fn body_matches_partial_json(self, body: Value) -> Self {
1906        Self { mock: self.mock.and(body_partial_json(body)), ..self }
1907    }
1908
1909    /// Ensures that the send endpoint request uses a specific event type.
1910    ///
1911    /// # Examples
1912    ///
1913    /// see also [`MatrixMockServer::mock_room_send`] for more context.
1914    ///
1915    /// ```
1916    /// # tokio_test::block_on(async {
1917    /// use matrix_sdk::{ruma::{room_id, event_id}, test_utils::mocks::MatrixMockServer};
1918    /// use serde_json::json;
1919    ///
1920    /// let mock_server = MatrixMockServer::new().await;
1921    /// let client = mock_server.client_builder().build().await;
1922    ///
1923    /// mock_server.mock_room_state_encryption().plain().mount().await;
1924    ///
1925    /// let room = mock_server
1926    ///     .sync_joined_room(&client, room_id!("!room_id:localhost"))
1927    ///     .await;
1928    ///
1929    /// let event_id = event_id!("$some_id");
1930    /// mock_server
1931    ///     .mock_room_send()
1932    ///     .for_type("m.room.message".into())
1933    ///     .ok(event_id)
1934    ///     .expect(1)
1935    ///     .mount()
1936    ///     .await;
1937    ///
1938    /// let response_not_mocked = room.send_raw("m.room.reaction", json!({ "body": "Hello world" })).await;
1939    /// // The `m.room.reaction` event type should not be mocked by the server.
1940    /// assert!(response_not_mocked.is_err());
1941    ///
1942    /// let response = room.send_raw("m.room.message", json!({ "body": "Hello world" })).await?;
1943    /// // The `m.room.message` event type should be mocked by the server.
1944    /// assert_eq!(
1945    ///     event_id,
1946    ///     response.event_id,
1947    ///     "The event ID we mocked should match the one we received when we sent the event"
1948    /// );
1949    /// # anyhow::Ok(()) });
1950    /// ```
1951    pub fn for_type(self, event_type: MessageLikeEventType) -> Self {
1952        Self {
1953            // Note: we already defined a path when constructing the mock builder, but this one
1954            // ought to be more specialized.
1955            mock: self
1956                .mock
1957                .and(path_regex(format!(r"^/_matrix/client/v3/rooms/.*/send/{event_type}",))),
1958            ..self
1959        }
1960    }
1961
1962    /// Ensures the event was sent as a delayed event.
1963    ///
1964    /// See also [the MSC](https://github.com/matrix-org/matrix-spec-proposals/pull/4140).
1965    ///
1966    /// Note: works with *any* room.
1967    ///
1968    /// # Examples
1969    ///
1970    /// see also [`MatrixMockServer::mock_room_send`] for more context.
1971    ///
1972    /// ```
1973    /// # tokio_test::block_on(async {
1974    /// use matrix_sdk::{
1975    ///     ruma::{
1976    ///         api::client::delayed_events::{delayed_message_event, DelayParameters},
1977    ///         events::{message::MessageEventContent, AnyMessageLikeEventContent},
1978    ///         room_id,
1979    ///         time::Duration,
1980    ///         TransactionId,
1981    ///     },
1982    ///     test_utils::mocks::MatrixMockServer,
1983    /// };
1984    /// use serde_json::json;
1985    /// use wiremock::ResponseTemplate;
1986    ///
1987    /// let mock_server = MatrixMockServer::new().await;
1988    /// let client = mock_server.client_builder().build().await;
1989    ///
1990    /// mock_server.mock_room_state_encryption().plain().mount().await;
1991    ///
1992    /// let room = mock_server.sync_joined_room(&client, room_id!("!room_id:localhost")).await;
1993    ///
1994    /// mock_server
1995    ///     .mock_room_send()
1996    ///     .match_delayed_event(Duration::from_millis(500))
1997    ///     .respond_with(ResponseTemplate::new(200).set_body_json(json!({"delay_id":"$some_id"})))
1998    ///     .mock_once()
1999    ///     .mount()
2000    ///     .await;
2001    ///
2002    /// let response_not_mocked =
2003    ///     room.send_raw("m.room.message", json!({ "body": "Hello world" })).await;
2004    ///
2005    /// // A non delayed event should not be mocked by the server.
2006    /// assert!(response_not_mocked.is_err());
2007    ///
2008    /// let r = delayed_message_event::unstable::Request::new(
2009    ///     room.room_id().to_owned(),
2010    ///     TransactionId::new(),
2011    ///     DelayParameters::Timeout { timeout: Duration::from_millis(500) },
2012    ///     &AnyMessageLikeEventContent::Message(MessageEventContent::plain("hello world")),
2013    /// )
2014    /// .unwrap();
2015    ///
2016    /// let response = room.client().send(r).await.unwrap();
2017    /// // The delayed `m.room.message` event type should be mocked by the server.
2018    /// assert_eq!("$some_id", response.delay_id);
2019    /// # anyhow::Ok(()) });
2020    /// ```
2021    pub fn match_delayed_event(self, delay: Duration) -> Self {
2022        Self {
2023            mock: self
2024                .mock
2025                .and(query_param("org.matrix.msc4140.delay", delay.as_millis().to_string())),
2026            ..self
2027        }
2028    }
2029
2030    /// Returns a send endpoint that emulates success, i.e. the event has been
2031    /// sent with the given event id.
2032    ///
2033    /// # Examples
2034    /// ```
2035    /// # tokio_test::block_on(async {
2036    /// use matrix_sdk::{ruma::{room_id, event_id}, test_utils::mocks::MatrixMockServer};
2037    /// use serde_json::json;
2038    ///
2039    /// let mock_server = MatrixMockServer::new().await;
2040    /// let client = mock_server.client_builder().build().await;
2041    ///
2042    /// mock_server.mock_room_state_encryption().plain().mount().await;
2043    ///
2044    /// let room = mock_server
2045    ///     .sync_joined_room(&client, room_id!("!room_id:localhost"))
2046    ///     .await;
2047    ///
2048    /// let event_id = event_id!("$some_id");
2049    /// let send_guard = mock_server
2050    ///     .mock_room_send()
2051    ///     .ok(event_id)
2052    ///     .expect(1)
2053    ///     .mount_as_scoped()
2054    ///     .await;
2055    ///
2056    /// let response = room.send_raw("m.room.message", json!({ "body": "Hello world" })).await?;
2057    ///
2058    /// assert_eq!(
2059    ///     event_id,
2060    ///     response.event_id,
2061    ///     "The event ID we mocked should match the one we received when we sent the event"
2062    /// );
2063    /// # anyhow::Ok(()) });
2064    /// ```
2065    pub fn ok(self, returned_event_id: impl Into<OwnedEventId>) -> MatrixMock<'a> {
2066        self.ok_with_event_id(returned_event_id.into())
2067    }
2068
2069    /// Returns a send endpoint that emulates success, i.e. the event has been
2070    /// sent with the given event id.
2071    ///
2072    /// The sent event is captured and can be accessed using the returned
2073    /// [`Receiver`]. The [`Receiver`] is valid only for a send call. The given
2074    /// `event_sender` are added to the event JSON.
2075    ///
2076    /// # Examples
2077    ///
2078    /// ```no_run
2079    /// # tokio_test::block_on(async {
2080    /// use matrix_sdk::{
2081    ///     ruma::{
2082    ///         event_id, events::room::message::RoomMessageEventContent, room_id,
2083    ///     },
2084    ///     test_utils::mocks::MatrixMockServer,
2085    /// };
2086    /// use matrix_sdk_test::JoinedRoomBuilder;
2087    ///
2088    /// let room_id = room_id!("!room_id:localhost");
2089    /// let event_id = event_id!("$some_id");
2090    ///
2091    /// let server = MatrixMockServer::new().await;
2092    /// let client = server.client_builder().build().await;
2093    ///
2094    /// let user_id = client.user_id().expect("We should have a user ID by now");
2095    ///
2096    /// let (receiver, mock) =
2097    ///     server.mock_room_send().ok_with_capture(event_id, user_id);
2098    ///
2099    /// server
2100    ///     .mock_sync()
2101    ///     .ok_and_run(&client, |builder| {
2102    ///         builder.add_joined_room(JoinedRoomBuilder::new(room_id));
2103    ///     })
2104    ///     .await;
2105    ///
2106    /// // Mock any additional endpoints that might be needed to send the message.
2107    ///
2108    /// let room = client
2109    ///     .get_room(room_id)
2110    ///     .expect("We should have access to our room now");
2111    ///
2112    /// let event_id = room
2113    ///     .send(RoomMessageEventContent::text_plain("It's a secret to everybody"))
2114    ///     .await
2115    ///     .expect("We should be able to send an initial message")
2116    ///     .event_id;
2117    ///
2118    /// let event = receiver.await?;
2119    /// # anyhow::Ok(()) });
2120    /// ```
2121    pub fn ok_with_capture(
2122        self,
2123        returned_event_id: impl Into<OwnedEventId>,
2124        event_sender: impl Into<OwnedUserId>,
2125    ) -> (Receiver<Raw<AnySyncTimelineEvent>>, MatrixMock<'a>) {
2126        let event_id = returned_event_id.into();
2127        let event_sender = event_sender.into();
2128
2129        let (sender, receiver) = oneshot::channel();
2130        let sender = Arc::new(Mutex::new(Some(sender)));
2131
2132        let ret = self.respond_with(move |request: &Request| {
2133            if let Some(sender) = sender.lock().unwrap().take() {
2134                let uri = &request.url;
2135                let path_segments = uri.path_segments();
2136                let maybe_event_type = path_segments.and_then(|mut s| s.nth_back(1));
2137                let event_type = maybe_event_type
2138                    .as_ref()
2139                    .map(|&e| e.to_owned())
2140                    .unwrap_or("m.room.message".to_owned());
2141
2142                let body: Value =
2143                    request.body_json().expect("The received body should be valid JSON");
2144
2145                let event = json!({
2146                    "event_id": event_id.clone(),
2147                    "sender": event_sender,
2148                    "type": event_type,
2149                    "origin_server_ts": MilliSecondsSinceUnixEpoch::now(),
2150                    "content": body,
2151                });
2152
2153                let event: Raw<AnySyncTimelineEvent> = from_value(event)
2154                    .expect("We should be able to create a raw event from the content");
2155
2156                sender.send(event).expect("We should be able to send the event to the receiver");
2157            }
2158
2159            ResponseTemplate::new(200).set_body_json(json!({ "event_id": event_id.clone() }))
2160        });
2161
2162        (receiver, ret)
2163    }
2164}
2165
2166/// A prebuilt mock for sending a state event in a room.
2167#[derive(Default)]
2168pub struct RoomSendStateEndpoint {
2169    state_key: Option<String>,
2170    event_type: Option<StateEventType>,
2171}
2172
2173impl<'a> MockEndpoint<'a, RoomSendStateEndpoint> {
2174    fn generate_path_regexp(endpoint: &RoomSendStateEndpoint) -> String {
2175        format!(
2176            r"^/_matrix/client/v3/rooms/.*/state/{}/{}",
2177            endpoint.event_type.as_ref().map_or_else(|| ".*".to_owned(), |t| t.to_string()),
2178            endpoint.state_key.as_ref().map_or_else(|| ".*".to_owned(), |k| k.to_string())
2179        )
2180    }
2181
2182    /// Ensures that the body of the request is a superset of the provided
2183    /// `body` parameter.
2184    ///
2185    /// # Examples
2186    /// ```
2187    /// # tokio_test::block_on(async {
2188    /// use matrix_sdk::{
2189    ///     ruma::{
2190    ///         room_id, event_id,
2191    ///         events::room::power_levels::RoomPowerLevelsEventContent,
2192    ///         room_version_rules::AuthorizationRules
2193    ///     },
2194    ///     test_utils::mocks::MatrixMockServer
2195    /// };
2196    /// use serde_json::json;
2197    ///
2198    /// let mock_server = MatrixMockServer::new().await;
2199    /// let client = mock_server.client_builder().build().await;
2200    ///
2201    /// mock_server.mock_room_state_encryption().plain().mount().await;
2202    ///
2203    /// let room = mock_server
2204    ///     .sync_joined_room(&client, room_id!("!room_id:localhost"))
2205    ///     .await;
2206    ///
2207    /// let event_id = event_id!("$some_id");
2208    /// mock_server
2209    ///     .mock_room_send_state()
2210    ///     .body_matches_partial_json(json!({
2211    ///         "redact": 51,
2212    ///     }))
2213    ///     .ok(event_id)
2214    ///     .expect(1)
2215    ///     .mount()
2216    ///     .await;
2217    ///
2218    /// let mut content = RoomPowerLevelsEventContent::new(&AuthorizationRules::V1);
2219    /// // Update the power level to a non default value.
2220    /// // Otherwise it will be skipped from serialization.
2221    /// content.redact = 51.into();
2222    ///
2223    /// let response = room.send_state_event(content).await?;
2224    ///
2225    /// assert_eq!(
2226    ///     event_id,
2227    ///     response.event_id,
2228    ///     "The event ID we mocked should match the one we received when we sent the event"
2229    /// );
2230    /// # anyhow::Ok(()) });
2231    /// ```
2232    pub fn body_matches_partial_json(self, body: Value) -> Self {
2233        Self { mock: self.mock.and(body_partial_json(body)), ..self }
2234    }
2235
2236    /// Ensures that the send endpoint request uses a specific event type.
2237    ///
2238    /// Note: works with *any* room.
2239    ///
2240    /// # Examples
2241    ///
2242    /// see also [`MatrixMockServer::mock_room_send`] for more context.
2243    ///
2244    /// ```
2245    /// # tokio_test::block_on(async {
2246    /// use matrix_sdk::{
2247    ///     ruma::{
2248    ///         event_id,
2249    ///         events::room::{
2250    ///             create::RoomCreateEventContent, power_levels::RoomPowerLevelsEventContent,
2251    ///         },
2252    ///         events::StateEventType,
2253    ///         room_id,
2254    ///         room_version_rules::AuthorizationRules,
2255    ///     },
2256    ///     test_utils::mocks::MatrixMockServer,
2257    /// };
2258    ///
2259    /// let mock_server = MatrixMockServer::new().await;
2260    /// let client = mock_server.client_builder().build().await;
2261    ///
2262    /// mock_server.mock_room_state_encryption().plain().mount().await;
2263    ///
2264    /// let room = mock_server.sync_joined_room(&client, room_id!("!room_id:localhost")).await;
2265    ///
2266    /// let event_id = event_id!("$some_id");
2267    ///
2268    /// mock_server
2269    ///     .mock_room_send_state()
2270    ///     .for_type(StateEventType::RoomPowerLevels)
2271    ///     .ok(event_id)
2272    ///     .expect(1)
2273    ///     .mount()
2274    ///     .await;
2275    ///
2276    /// let response_not_mocked = room.send_state_event(RoomCreateEventContent::new_v11()).await;
2277    /// // The `m.room.reaction` event type should not be mocked by the server.
2278    /// assert!(response_not_mocked.is_err());
2279    ///
2280    /// let response = room.send_state_event(RoomPowerLevelsEventContent::new(&AuthorizationRules::V1)).await?;
2281    /// // The `m.room.message` event type should be mocked by the server.
2282    /// assert_eq!(
2283    ///     event_id, response.event_id,
2284    ///     "The event ID we mocked should match the one we received when we sent the event"
2285    /// );
2286    ///
2287    /// # anyhow::Ok(()) });
2288    /// ```
2289    pub fn for_type(mut self, event_type: StateEventType) -> Self {
2290        self.endpoint.event_type = Some(event_type);
2291        // Note: we may have already defined a path, but this one ought to be more
2292        // specialized (unless for_key/for_type were called multiple times).
2293        Self { mock: self.mock.and(path_regex(Self::generate_path_regexp(&self.endpoint))), ..self }
2294    }
2295
2296    /// Ensures the event was sent as a delayed event.
2297    ///
2298    /// See also [the MSC](https://github.com/matrix-org/matrix-spec-proposals/pull/4140).
2299    ///
2300    /// Note: works with *any* room.
2301    ///
2302    /// # Examples
2303    ///
2304    /// see also [`MatrixMockServer::mock_room_send`] for more context.
2305    ///
2306    /// ```
2307    /// # tokio_test::block_on(async {
2308    /// use matrix_sdk::{
2309    ///     ruma::{
2310    ///         api::client::delayed_events::{delayed_state_event, DelayParameters},
2311    ///         events::{room::create::RoomCreateEventContent, AnyStateEventContent},
2312    ///         room_id,
2313    ///         time::Duration,
2314    ///     },
2315    ///     test_utils::mocks::MatrixMockServer,
2316    /// };
2317    /// use wiremock::ResponseTemplate;
2318    /// use serde_json::json;
2319    ///
2320    /// let mock_server = MatrixMockServer::new().await;
2321    /// let client = mock_server.client_builder().build().await;
2322    ///
2323    /// mock_server.mock_room_state_encryption().plain().mount().await;
2324    ///
2325    /// let room = mock_server.sync_joined_room(&client, room_id!("!room_id:localhost")).await;
2326    ///
2327    /// mock_server
2328    ///     .mock_room_send_state()
2329    ///     .match_delayed_event(Duration::from_millis(500))
2330    ///     .respond_with(ResponseTemplate::new(200).set_body_json(json!({"delay_id":"$some_id"})))
2331    ///     .mock_once()
2332    ///     .mount()
2333    ///     .await;
2334    ///
2335    /// let response_not_mocked = room.send_state_event(RoomCreateEventContent::new_v11()).await;
2336    /// // A non delayed event should not be mocked by the server.
2337    /// assert!(response_not_mocked.is_err());
2338    ///
2339    /// let r = delayed_state_event::unstable::Request::new(
2340    ///     room.room_id().to_owned(),
2341    ///     "".to_owned(),
2342    ///     DelayParameters::Timeout { timeout: Duration::from_millis(500) },
2343    ///     &AnyStateEventContent::RoomCreate(RoomCreateEventContent::new_v11()),
2344    /// )
2345    /// .unwrap();
2346    /// let response = room.client().send(r).await.unwrap();
2347    /// // The delayed `m.room.message` event type should be mocked by the server.
2348    /// assert_eq!("$some_id", response.delay_id);
2349    ///
2350    /// # anyhow::Ok(()) });
2351    /// ```
2352    pub fn match_delayed_event(self, delay: Duration) -> Self {
2353        Self {
2354            mock: self
2355                .mock
2356                .and(query_param("org.matrix.msc4140.delay", delay.as_millis().to_string())),
2357            ..self
2358        }
2359    }
2360
2361    ///
2362    /// ```
2363    /// # tokio_test::block_on(async {
2364    /// use matrix_sdk::{
2365    ///     ruma::{
2366    ///         event_id,
2367    ///         events::{call::member::CallMemberEventContent, AnyStateEventContent},
2368    ///         room_id,
2369    ///     },
2370    ///     test_utils::mocks::MatrixMockServer,
2371    /// };
2372    ///
2373    /// let mock_server = MatrixMockServer::new().await;
2374    /// let client = mock_server.client_builder().build().await;
2375    ///
2376    /// mock_server.mock_room_state_encryption().plain().mount().await;
2377    ///
2378    /// let room = mock_server.sync_joined_room(&client, room_id!("!room_id:localhost")).await;
2379    ///
2380    /// let event_id = event_id!("$some_id");
2381    ///
2382    /// mock_server
2383    ///     .mock_room_send_state()
2384    ///     .for_key("my_key".to_owned())
2385    ///     .ok(event_id)
2386    ///     .expect(1)
2387    ///     .mount()
2388    ///     .await;
2389    ///
2390    /// let response_not_mocked = room
2391    ///     .send_state_event_for_key(
2392    ///         "",
2393    ///         AnyStateEventContent::CallMember(CallMemberEventContent::new_empty(None)),
2394    ///     )
2395    ///     .await;
2396    /// // The `m.room.reaction` event type should not be mocked by the server.
2397    /// assert!(response_not_mocked.is_err());
2398    ///
2399    /// let response = room
2400    ///     .send_state_event_for_key(
2401    ///         "my_key",
2402    ///         AnyStateEventContent::CallMember(CallMemberEventContent::new_empty(None)),
2403    ///     )
2404    ///     .await
2405    ///     .unwrap();
2406    ///
2407    /// // The `m.room.message` event type should be mocked by the server.
2408    /// assert_eq!(
2409    ///     event_id, response.event_id,
2410    ///     "The event ID we mocked should match the one we received when we sent the event"
2411    /// );
2412    /// # anyhow::Ok(()) });
2413    /// ```
2414    pub fn for_key(mut self, state_key: String) -> Self {
2415        self.endpoint.state_key = Some(state_key);
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    /// Returns a send endpoint that emulates success, i.e. the event has been
2422    /// sent with the given event id.
2423    ///
2424    /// # Examples
2425    /// ```
2426    /// # tokio_test::block_on(async {
2427    /// use matrix_sdk::{ruma::{room_id, event_id}, test_utils::mocks::MatrixMockServer};
2428    /// use serde_json::json;
2429    ///
2430    /// let mock_server = MatrixMockServer::new().await;
2431    /// let client = mock_server.client_builder().build().await;
2432    ///
2433    /// mock_server.mock_room_state_encryption().plain().mount().await;
2434    ///
2435    /// let room = mock_server
2436    ///     .sync_joined_room(&client, room_id!("!room_id:localhost"))
2437    ///     .await;
2438    ///
2439    /// let event_id = event_id!("$some_id");
2440    /// let send_guard = mock_server
2441    ///     .mock_room_send_state()
2442    ///     .ok(event_id)
2443    ///     .expect(1)
2444    ///     .mount_as_scoped()
2445    ///     .await;
2446    ///
2447    /// let response = room.send_state_event_raw("m.room.message", "my_key", json!({ "body": "Hello world" })).await?;
2448    ///
2449    /// assert_eq!(
2450    ///     event_id,
2451    ///     response.event_id,
2452    ///     "The event ID we mocked should match the one we received when we sent the event"
2453    /// );
2454    /// # anyhow::Ok(()) });
2455    /// ```
2456    pub fn ok(self, returned_event_id: impl Into<OwnedEventId>) -> MatrixMock<'a> {
2457        self.ok_with_event_id(returned_event_id.into())
2458    }
2459}
2460
2461/// A prebuilt mock for running sync v2.
2462pub struct SyncEndpoint {
2463    sync_response_builder: Arc<Mutex<SyncResponseBuilder>>,
2464}
2465
2466impl<'a> MockEndpoint<'a, SyncEndpoint> {
2467    /// Expect the given timeout, or lack thereof, in the request.
2468    pub fn timeout(mut self, timeout: Option<Duration>) -> Self {
2469        if let Some(timeout) = timeout {
2470            self.mock = self.mock.and(query_param("timeout", timeout.as_millis().to_string()));
2471        } else {
2472            self.mock = self.mock.and(query_param_is_missing("timeout"));
2473        }
2474
2475        self
2476    }
2477
2478    /// Mocks the sync endpoint, using the given function to generate the
2479    /// response.
2480    pub fn ok<F: FnOnce(&mut SyncResponseBuilder)>(self, func: F) -> MatrixMock<'a> {
2481        let json_response = {
2482            let mut builder = self.endpoint.sync_response_builder.lock().unwrap();
2483            func(&mut builder);
2484            builder.build_json_sync_response()
2485        };
2486
2487        self.respond_with(ResponseTemplate::new(200).set_body_json(json_response))
2488    }
2489
2490    /// Temporarily mocks the sync with the given endpoint and runs a client
2491    /// sync with it.
2492    ///
2493    /// After calling this function, the sync endpoint isn't mocked anymore.
2494    ///
2495    /// # Examples
2496    ///
2497    /// ```
2498    /// # tokio_test::block_on(async {
2499    /// use matrix_sdk::{ruma::room_id, test_utils::mocks::MatrixMockServer};
2500    /// use matrix_sdk_test::JoinedRoomBuilder;
2501    ///
2502    /// // First create the mock server and client pair.
2503    /// let mock_server = MatrixMockServer::new().await;
2504    /// let client = mock_server.client_builder().build().await;
2505    /// let room_id = room_id!("!room_id:localhost");
2506    ///
2507    /// // Let's emulate what `MatrixMockServer::sync_joined_room()` does.
2508    /// mock_server
2509    ///     .mock_sync()
2510    ///     .ok_and_run(&client, |builder| {
2511    ///         builder.add_joined_room(JoinedRoomBuilder::new(room_id));
2512    ///     })
2513    ///     .await;
2514    ///
2515    /// let room = client
2516    ///     .get_room(room_id)
2517    ///     .expect("The room should be available after we mocked the sync");
2518    /// # anyhow::Ok(()) });
2519    /// ```
2520    pub async fn ok_and_run<F: FnOnce(&mut SyncResponseBuilder)>(self, client: &Client, func: F) {
2521        let _scope = self.ok(func).mount_as_scoped().await;
2522
2523        let _response = client.sync_once(Default::default()).await.unwrap();
2524    }
2525}
2526
2527/// A prebuilt mock for reading the encryption state of a room.
2528pub struct EncryptionStateEndpoint;
2529
2530impl<'a> MockEndpoint<'a, EncryptionStateEndpoint> {
2531    /// Marks the room as encrypted.
2532    ///
2533    /// # Examples
2534    ///
2535    /// ```
2536    /// # tokio_test::block_on(async {
2537    /// use matrix_sdk::{ruma::room_id, test_utils::mocks::MatrixMockServer};
2538    ///
2539    /// let mock_server = MatrixMockServer::new().await;
2540    /// let client = mock_server.client_builder().build().await;
2541    ///
2542    /// mock_server.mock_room_state_encryption().encrypted().mount().await;
2543    ///
2544    /// let room = mock_server
2545    ///     .sync_joined_room(&client, room_id!("!room_id:localhost"))
2546    ///     .await;
2547    ///
2548    /// assert!(
2549    ///     room.latest_encryption_state().await?.is_encrypted(),
2550    ///     "The room should be marked as encrypted."
2551    /// );
2552    /// # anyhow::Ok(()) });
2553    /// ```
2554    pub fn encrypted(self) -> MatrixMock<'a> {
2555        self.respond_with(
2556            ResponseTemplate::new(200).set_body_json(&*test_json::sync_events::ENCRYPTION_CONTENT),
2557        )
2558    }
2559
2560    /// Marks the room as encrypted, opting into experimental state event
2561    /// encryption.
2562    ///
2563    /// # Examples
2564    ///
2565    /// ```
2566    /// # tokio_test::block_on(async {
2567    /// use matrix_sdk::{ruma::room_id, test_utils::mocks::MatrixMockServer};
2568    ///
2569    /// let mock_server = MatrixMockServer::new().await;
2570    /// let client = mock_server.client_builder().build().await;
2571    ///
2572    /// mock_server.mock_room_state_encryption().state_encrypted().mount().await;
2573    ///
2574    /// let room = mock_server
2575    ///     .sync_joined_room(&client, room_id!("!room_id:localhost"))
2576    ///     .await;
2577    ///
2578    /// assert!(
2579    ///     room.latest_encryption_state().await?.is_state_encrypted(),
2580    ///     "The room should be marked as state encrypted."
2581    /// );
2582    /// # anyhow::Ok(()) });
2583    #[cfg(feature = "experimental-encrypted-state-events")]
2584    pub fn state_encrypted(self) -> MatrixMock<'a> {
2585        self.respond_with(ResponseTemplate::new(200).set_body_json(
2586            &*test_json::sync_events::ENCRYPTION_WITH_ENCRYPTED_STATE_EVENTS_CONTENT,
2587        ))
2588    }
2589
2590    /// Marks the room as not encrypted.
2591    ///
2592    /// # Examples
2593    ///
2594    /// ```
2595    /// # tokio_test::block_on(async {
2596    /// use matrix_sdk::{ruma::room_id, test_utils::mocks::MatrixMockServer};
2597    ///
2598    /// let mock_server = MatrixMockServer::new().await;
2599    /// let client = mock_server.client_builder().build().await;
2600    ///
2601    /// mock_server.mock_room_state_encryption().plain().mount().await;
2602    ///
2603    /// let room = mock_server
2604    ///     .sync_joined_room(&client, room_id!("!room_id:localhost"))
2605    ///     .await;
2606    ///
2607    /// assert!(
2608    ///     !room.latest_encryption_state().await?.is_encrypted(),
2609    ///     "The room should not be marked as encrypted."
2610    /// );
2611    /// # anyhow::Ok(()) });
2612    /// ```
2613    pub fn plain(self) -> MatrixMock<'a> {
2614        self.respond_with(ResponseTemplate::new(404).set_body_json(&*test_json::NOT_FOUND))
2615    }
2616}
2617
2618/// A prebuilt mock for setting the encryption state of a room.
2619pub struct SetEncryptionStateEndpoint;
2620
2621impl<'a> MockEndpoint<'a, SetEncryptionStateEndpoint> {
2622    /// Returns a mock for a successful setting of the encryption state event.
2623    pub fn ok(self, returned_event_id: impl Into<OwnedEventId>) -> MatrixMock<'a> {
2624        self.ok_with_event_id(returned_event_id.into())
2625    }
2626}
2627
2628/// A prebuilt mock for redacting an event in a room.
2629pub struct RoomRedactEndpoint;
2630
2631impl<'a> MockEndpoint<'a, RoomRedactEndpoint> {
2632    /// Returns a redact endpoint that emulates success, i.e. the redaction
2633    /// event has been sent with the given event id.
2634    pub fn ok(self, returned_event_id: impl Into<OwnedEventId>) -> MatrixMock<'a> {
2635        self.ok_with_event_id(returned_event_id.into())
2636    }
2637}
2638
2639/// A prebuilt mock for getting a single event in a room.
2640pub struct RoomEventEndpoint {
2641    room: Option<OwnedRoomId>,
2642    match_event_id: bool,
2643}
2644
2645impl<'a> MockEndpoint<'a, RoomEventEndpoint> {
2646    /// Limits the scope of this mock to a specific room.
2647    pub fn room(mut self, room: impl Into<OwnedRoomId>) -> Self {
2648        self.endpoint.room = Some(room.into());
2649        self
2650    }
2651
2652    /// Whether the mock checks for the event id from the event.
2653    pub fn match_event_id(mut self) -> Self {
2654        self.endpoint.match_event_id = true;
2655        self
2656    }
2657
2658    /// Returns a redact endpoint that emulates success, i.e. the redaction
2659    /// event has been sent with the given event id.
2660    pub fn ok(self, event: TimelineEvent) -> MatrixMock<'a> {
2661        let event_path = if self.endpoint.match_event_id {
2662            let event_id = event.kind.event_id().expect("an event id is required");
2663            // The event id should begin with `$`, which would be taken as the end of the
2664            // regex so we need to escape it
2665            event_id.as_str().replace("$", "\\$")
2666        } else {
2667            // Event is at the end, so no need to add anything.
2668            "".to_owned()
2669        };
2670
2671        let room_path = self.endpoint.room.map_or_else(|| ".*".to_owned(), |room| room.to_string());
2672
2673        let mock = self
2674            .mock
2675            .and(path_regex(format!(r"^/_matrix/client/v3/rooms/{room_path}/event/{event_path}")))
2676            .respond_with(ResponseTemplate::new(200).set_body_json(event.into_raw().json()));
2677        MatrixMock { server: self.server, mock }
2678    }
2679}
2680
2681/// A prebuilt mock for getting a single event with its context in a room.
2682pub struct RoomEventContextEndpoint {
2683    room: Option<OwnedRoomId>,
2684    match_event_id: bool,
2685}
2686
2687impl<'a> MockEndpoint<'a, RoomEventContextEndpoint> {
2688    /// Limits the scope of this mock to a specific room.
2689    pub fn room(mut self, room: impl Into<OwnedRoomId>) -> Self {
2690        self.endpoint.room = Some(room.into());
2691        self
2692    }
2693
2694    /// Whether the mock checks for the event id from the event.
2695    pub fn match_event_id(mut self) -> Self {
2696        self.endpoint.match_event_id = true;
2697        self
2698    }
2699
2700    /// Returns an endpoint that emulates success.
2701    pub fn ok(
2702        self,
2703        event: TimelineEvent,
2704        start: impl Into<String>,
2705        end: impl Into<String>,
2706        state_events: Vec<Raw<AnyStateEvent>>,
2707    ) -> MatrixMock<'a> {
2708        let event_path = if self.endpoint.match_event_id {
2709            let event_id = event.event_id().expect("an event id is required");
2710            // The event id should begin with `$`, which would be taken as the end of the
2711            // regex so we need to escape it
2712            event_id.as_str().replace("$", "\\$")
2713        } else {
2714            // Event is at the end, so no need to add anything.
2715            "".to_owned()
2716        };
2717
2718        let room_path = self.endpoint.room.map_or_else(|| ".*".to_owned(), |room| room.to_string());
2719
2720        let mock = self
2721            .mock
2722            .and(path_regex(format!(r"^/_matrix/client/v3/rooms/{room_path}/context/{event_path}")))
2723            .respond_with(ResponseTemplate::new(200).set_body_json(json!({
2724                "event": event.into_raw().json(),
2725                "end": end.into(),
2726                "start": start.into(),
2727                "state": state_events
2728            })));
2729        MatrixMock { server: self.server, mock }
2730    }
2731}
2732
2733/// A prebuilt mock for the `/messages` endpoint.
2734pub struct RoomMessagesEndpoint;
2735
2736/// A prebuilt mock for getting a room messages in a room.
2737impl<'a> MockEndpoint<'a, RoomMessagesEndpoint> {
2738    /// Expects an optional limit to be set on the request.
2739    pub fn match_limit(self, limit: u32) -> Self {
2740        Self { mock: self.mock.and(query_param("limit", limit.to_string())), ..self }
2741    }
2742
2743    /// Expects an optional `from` to be set on the request.
2744    pub fn match_from(self, from: &str) -> Self {
2745        Self { mock: self.mock.and(query_param("from", from)), ..self }
2746    }
2747
2748    /// Returns a messages endpoint that emulates success, i.e. the messages
2749    /// provided as `response` could be retrieved.
2750    ///
2751    /// Note: pass `chunk` in the correct order: topological for forward
2752    /// pagination, reverse topological for backwards pagination.
2753    pub fn ok(self, response: RoomMessagesResponseTemplate) -> MatrixMock<'a> {
2754        let mut template = ResponseTemplate::new(200).set_body_json(json!({
2755            "start": response.start,
2756            "end": response.end,
2757            "chunk": response.chunk,
2758            "state": response.state,
2759        }));
2760
2761        if let Some(delay) = response.delay {
2762            template = template.set_delay(delay);
2763        }
2764
2765        self.respond_with(template)
2766    }
2767}
2768
2769/// A response to a [`RoomMessagesEndpoint`] query.
2770pub struct RoomMessagesResponseTemplate {
2771    /// The start token for this /messages query.
2772    pub start: String,
2773    /// The end token for this /messages query (previous batch for back
2774    /// paginations, next batch for forward paginations).
2775    pub end: Option<String>,
2776    /// The set of timeline events returned by this query.
2777    pub chunk: Vec<Raw<AnyTimelineEvent>>,
2778    /// The set of state events returned by this query.
2779    pub state: Vec<Raw<AnyStateEvent>>,
2780    /// Optional delay to respond to the query.
2781    pub delay: Option<Duration>,
2782}
2783
2784impl RoomMessagesResponseTemplate {
2785    /// Fill the events returned as part of this response.
2786    pub fn events(mut self, chunk: Vec<impl Into<Raw<AnyTimelineEvent>>>) -> Self {
2787        self.chunk = chunk.into_iter().map(Into::into).collect();
2788        self
2789    }
2790
2791    /// Fill the end token.
2792    pub fn end_token(mut self, token: impl Into<String>) -> Self {
2793        self.end = Some(token.into());
2794        self
2795    }
2796
2797    /// Respond with a given delay to the query.
2798    pub fn with_delay(mut self, delay: Duration) -> Self {
2799        self.delay = Some(delay);
2800        self
2801    }
2802}
2803
2804impl Default for RoomMessagesResponseTemplate {
2805    fn default() -> Self {
2806        Self {
2807            start: "start-token-unused".to_owned(),
2808            end: Default::default(),
2809            chunk: Default::default(),
2810            state: Default::default(),
2811            delay: None,
2812        }
2813    }
2814}
2815
2816/// A prebuilt mock for uploading media.
2817pub struct UploadEndpoint;
2818
2819impl<'a> MockEndpoint<'a, UploadEndpoint> {
2820    /// Expect that the content type matches what's given here.
2821    pub fn expect_mime_type(self, content_type: &str) -> Self {
2822        Self { mock: self.mock.and(header("content-type", content_type)), ..self }
2823    }
2824
2825    /// Returns a upload endpoint that emulates success, i.e. the media has been
2826    /// uploaded to the media server and can be accessed using the given
2827    /// event has been sent with the given [`MxcUri`].
2828    ///
2829    /// The uploaded content is captured and can be accessed using the returned
2830    /// [`Receiver`]. The [`Receiver`] is valid only for a single media
2831    /// upload.
2832    ///
2833    /// # Examples
2834    ///
2835    /// ```no_run
2836    /// # tokio_test::block_on(async {
2837    /// use matrix_sdk::{
2838    ///     ruma::{event_id, mxc_uri, room_id},
2839    ///     test_utils::mocks::MatrixMockServer,
2840    /// };
2841    ///
2842    /// let mxid = mxc_uri!("mxc://localhost/12345");
2843    ///
2844    /// let server = MatrixMockServer::new().await;
2845    /// let (receiver, upload_mock) = server.mock_upload().ok_with_capture(mxid);
2846    /// let client = server.client_builder().build().await;
2847    ///
2848    /// client.media().upload(&mime::TEXT_PLAIN, vec![1, 2, 3, 4, 5], None).await?;
2849    ///
2850    /// let uploaded = receiver.await?;
2851    ///
2852    /// assert_eq!(uploaded, vec![1, 2, 3, 4, 5]);
2853    /// # anyhow::Ok(()) });
2854    /// ```
2855    pub fn ok_with_capture(self, mxc_id: &MxcUri) -> (Receiver<Vec<u8>>, MatrixMock<'a>) {
2856        let (sender, receiver) = oneshot::channel();
2857        let sender = Arc::new(Mutex::new(Some(sender)));
2858        let response_body = json!({"content_uri": mxc_id});
2859
2860        let ret = self.respond_with(move |request: &Request| {
2861            let maybe_sender = sender.lock().unwrap().take();
2862
2863            if let Some(sender) = maybe_sender {
2864                let body = request.body.clone();
2865                let _ = sender.send(body);
2866            }
2867
2868            ResponseTemplate::new(200).set_body_json(response_body.clone())
2869        });
2870
2871        (receiver, ret)
2872    }
2873
2874    /// Returns a upload endpoint that emulates success, i.e. the media has been
2875    /// uploaded to the media server and can be accessed using the given
2876    /// event has been sent with the given [`MxcUri`].
2877    pub fn ok(self, mxc_id: &MxcUri) -> MatrixMock<'a> {
2878        self.respond_with(ResponseTemplate::new(200).set_body_json(json!({
2879            "content_uri": mxc_id
2880        })))
2881    }
2882}
2883
2884/// A prebuilt mock for resolving a room alias.
2885pub struct ResolveRoomAliasEndpoint;
2886
2887impl<'a> MockEndpoint<'a, ResolveRoomAliasEndpoint> {
2888    /// Sets up the endpoint to only intercept requests for the given room
2889    /// alias.
2890    pub fn for_alias(self, alias: impl Into<String>) -> Self {
2891        let alias = alias.into();
2892        Self {
2893            mock: self.mock.and(path_regex(format!(
2894                r"^/_matrix/client/v3/directory/room/{}",
2895                percent_encoded_path(&alias)
2896            ))),
2897            ..self
2898        }
2899    }
2900
2901    /// Returns a data endpoint with a resolved room alias.
2902    pub fn ok(self, room_id: &str, servers: Vec<String>) -> MatrixMock<'a> {
2903        self.respond_with(ResponseTemplate::new(200).set_body_json(json!({
2904            "room_id": room_id,
2905            "servers": servers,
2906        })))
2907    }
2908
2909    /// Returns a data endpoint for a room alias that does not exit.
2910    pub fn not_found(self) -> MatrixMock<'a> {
2911        self.respond_with(ResponseTemplate::new(404).set_body_json(json!({
2912          "errcode": "M_NOT_FOUND",
2913          "error": "Room alias not found."
2914        })))
2915    }
2916}
2917
2918/// A prebuilt mock for creating a room alias.
2919pub struct CreateRoomAliasEndpoint;
2920
2921impl<'a> MockEndpoint<'a, CreateRoomAliasEndpoint> {
2922    /// Returns a data endpoint for creating a room alias.
2923    pub fn ok(self) -> MatrixMock<'a> {
2924        self.respond_with(ResponseTemplate::new(200).set_body_json(json!({})))
2925    }
2926}
2927
2928/// A prebuilt mock for removing a room alias.
2929pub struct RemoveRoomAliasEndpoint;
2930
2931impl<'a> MockEndpoint<'a, RemoveRoomAliasEndpoint> {
2932    /// Returns a data endpoint for removing a room alias.
2933    pub fn ok(self) -> MatrixMock<'a> {
2934        self.respond_with(ResponseTemplate::new(200).set_body_json(json!({})))
2935    }
2936}
2937
2938/// A prebuilt mock for paginating the public room list.
2939pub struct PublicRoomsEndpoint;
2940
2941impl<'a> MockEndpoint<'a, PublicRoomsEndpoint> {
2942    /// Returns a data endpoint for paginating the public room list.
2943    pub fn ok(
2944        self,
2945        chunk: Vec<PublicRoomsChunk>,
2946        next_batch: Option<String>,
2947        prev_batch: Option<String>,
2948        total_room_count_estimate: Option<u64>,
2949    ) -> MatrixMock<'a> {
2950        self.respond_with(ResponseTemplate::new(200).set_body_json(json!({
2951            "chunk": chunk,
2952            "next_batch": next_batch,
2953            "prev_batch": prev_batch,
2954            "total_room_count_estimate": total_room_count_estimate,
2955        })))
2956    }
2957
2958    /// Returns a data endpoint for paginating the public room list with several
2959    /// `via` params.
2960    ///
2961    /// Each `via` param must be in the `server_map` parameter, otherwise it'll
2962    /// fail.
2963    pub fn ok_with_via_params(
2964        self,
2965        server_map: BTreeMap<OwnedServerName, Vec<PublicRoomsChunk>>,
2966    ) -> MatrixMock<'a> {
2967        self.respond_with(move |req: &Request| {
2968            #[derive(Deserialize)]
2969            struct PartialRequest {
2970                server: Option<OwnedServerName>,
2971            }
2972
2973            let (_, server) = req
2974                .url
2975                .query_pairs()
2976                .into_iter()
2977                .find(|(key, _)| key == "server")
2978                .expect("Server param not found in request URL");
2979            let server = ServerName::parse(server).expect("Couldn't parse server name");
2980            let chunk = server_map.get(&server).expect("Chunk for the server param not found");
2981            ResponseTemplate::new(200).set_body_json(json!({
2982                "chunk": chunk,
2983                "total_room_count_estimate": chunk.len(),
2984            }))
2985        })
2986    }
2987}
2988
2989/// A prebuilt mock for getting the room's visibility in the room directory.
2990pub struct GetRoomVisibilityEndpoint;
2991
2992impl<'a> MockEndpoint<'a, GetRoomVisibilityEndpoint> {
2993    /// Returns an endpoint that get the room's public visibility.
2994    pub fn ok(self, visibility: Visibility) -> MatrixMock<'a> {
2995        self.respond_with(ResponseTemplate::new(200).set_body_json(json!({
2996            "visibility": visibility,
2997        })))
2998    }
2999}
3000
3001/// A prebuilt mock for setting the room's visibility in the room directory.
3002pub struct SetRoomVisibilityEndpoint;
3003
3004impl<'a> MockEndpoint<'a, SetRoomVisibilityEndpoint> {
3005    /// Returns an endpoint that updates the room's visibility.
3006    pub fn ok(self) -> MatrixMock<'a> {
3007        self.respond_with(ResponseTemplate::new(200).set_body_json(json!({})))
3008    }
3009}
3010
3011/// A prebuilt mock for `GET room_keys/version`: storage ("backup") of room
3012/// keys.
3013pub struct RoomKeysVersionEndpoint;
3014
3015impl<'a> MockEndpoint<'a, RoomKeysVersionEndpoint> {
3016    /// Returns an endpoint that says there is a single room keys backup
3017    pub fn exists(self) -> MatrixMock<'a> {
3018        self.respond_with(ResponseTemplate::new(200).set_body_json(json!({
3019            "algorithm": "m.megolm_backup.v1.curve25519-aes-sha2",
3020            "auth_data": {
3021                "public_key": "abcdefg",
3022                "signatures": {},
3023            },
3024            "count": 42,
3025            "etag": "anopaquestring",
3026            "version": "1",
3027        })))
3028    }
3029
3030    /// Returns an endpoint that says there is no room keys backup
3031    pub fn none(self) -> MatrixMock<'a> {
3032        self.respond_with(ResponseTemplate::new(404).set_body_json(json!({
3033            "errcode": "M_NOT_FOUND",
3034            "error": "No current backup version"
3035        })))
3036    }
3037
3038    /// Returns an endpoint that 429 errors when we get it
3039    pub fn error429(self) -> MatrixMock<'a> {
3040        self.respond_with(ResponseTemplate::new(429).set_body_json(json!({
3041            "errcode": "M_LIMIT_EXCEEDED",
3042            "error": "Too many requests",
3043            "retry_after_ms": 2000
3044        })))
3045    }
3046
3047    /// Returns an endpoint that 404 errors when we get it
3048    pub fn error404(self) -> MatrixMock<'a> {
3049        self.respond_with(ResponseTemplate::new(404))
3050    }
3051}
3052
3053/// A prebuilt mock for `POST room_keys/version`: adding room key backups.
3054pub struct AddRoomKeysVersionEndpoint;
3055
3056impl<'a> MockEndpoint<'a, AddRoomKeysVersionEndpoint> {
3057    /// Returns an endpoint that may be used to add room key backups
3058    pub fn ok(self) -> MatrixMock<'a> {
3059        self.respond_with(ResponseTemplate::new(200).set_body_json(json!({
3060          "version": "1"
3061        })))
3062        .named("POST for the backup creation")
3063    }
3064}
3065
3066/// A prebuilt mock for `DELETE room_keys/version/xxx`: deleting room key
3067/// backups.
3068pub struct DeleteRoomKeysVersionEndpoint;
3069
3070impl<'a> MockEndpoint<'a, DeleteRoomKeysVersionEndpoint> {
3071    /// Returns an endpoint that allows deleting room key backups
3072    pub fn ok(self) -> MatrixMock<'a> {
3073        self.respond_with(ResponseTemplate::new(200).set_body_json(json!({})))
3074            .named("DELETE for the backup deletion")
3075    }
3076}
3077
3078/// A prebuilt mock for the `/sendToDevice` endpoint.
3079///
3080/// This mock can be used to simulate sending to-device messages in tests.
3081pub struct SendToDeviceEndpoint;
3082impl<'a> MockEndpoint<'a, SendToDeviceEndpoint> {
3083    /// Returns a successful response with default data.
3084    pub fn ok(self) -> MatrixMock<'a> {
3085        self.respond_with(ResponseTemplate::new(200).set_body_json(json!({})))
3086    }
3087}
3088
3089/// A prebuilt mock for `GET /members` request.
3090pub struct GetRoomMembersEndpoint;
3091
3092impl<'a> MockEndpoint<'a, GetRoomMembersEndpoint> {
3093    /// Returns a successful get members request with a list of members.
3094    pub fn ok(self, members: Vec<Raw<RoomMemberEvent>>) -> MatrixMock<'a> {
3095        self.respond_with(ResponseTemplate::new(200).set_body_json(json!({
3096            "chunk": members,
3097        })))
3098    }
3099}
3100
3101/// A prebuilt mock for `POST /invite` request.
3102pub struct InviteUserByIdEndpoint;
3103
3104impl<'a> MockEndpoint<'a, InviteUserByIdEndpoint> {
3105    /// Returns a successful invite user by id request.
3106    pub fn ok(self) -> MatrixMock<'a> {
3107        self.respond_with(ResponseTemplate::new(200).set_body_json(json!({})))
3108    }
3109}
3110
3111/// A prebuilt mock for `POST /kick` request.
3112pub struct KickUserEndpoint;
3113
3114impl<'a> MockEndpoint<'a, KickUserEndpoint> {
3115    /// Returns a successful kick user request.
3116    pub fn ok(self) -> MatrixMock<'a> {
3117        self.respond_with(ResponseTemplate::new(200).set_body_json(json!({})))
3118    }
3119}
3120
3121/// A prebuilt mock for `POST /ban` request.
3122pub struct BanUserEndpoint;
3123
3124impl<'a> MockEndpoint<'a, BanUserEndpoint> {
3125    /// Returns a successful ban user request.
3126    pub fn ok(self) -> MatrixMock<'a> {
3127        self.respond_with(ResponseTemplate::new(200).set_body_json(json!({})))
3128    }
3129}
3130
3131/// A prebuilt mock for `GET /versions` request.
3132pub struct VersionsEndpoint;
3133
3134impl<'a> MockEndpoint<'a, VersionsEndpoint> {
3135    // Get a JSON array of commonly supported versions.
3136    fn versions() -> Value {
3137        json!([
3138            "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",
3139            "v1.3", "v1.4", "v1.5", "v1.6", "v1.7", "v1.8", "v1.9", "v1.10", "v1.11"
3140        ])
3141    }
3142
3143    /// Returns a successful `/_matrix/client/versions` request.
3144    ///
3145    /// The response will return some commonly supported versions.
3146    pub fn ok(self) -> MatrixMock<'a> {
3147        self.respond_with(ResponseTemplate::new(200).set_body_json(json!({
3148            "unstable_features": {},
3149            "versions": Self::versions()
3150        })))
3151    }
3152
3153    /// Returns a successful `/_matrix/client/versions` request.
3154    ///
3155    /// The response will return some commonly supported versions and unstable
3156    /// features supported by the SDK.
3157    pub fn ok_with_unstable_features(self) -> MatrixMock<'a> {
3158        self.respond_with(ResponseTemplate::new(200).set_body_json(json!({
3159            "unstable_features": {
3160                "org.matrix.label_based_filtering": true,
3161                "org.matrix.e2e_cross_signing": true,
3162                "org.matrix.msc4028": true,
3163                "org.matrix.simplified_msc3575": true,
3164            },
3165            "versions": Self::versions()
3166        })))
3167    }
3168
3169    /// Returns a successful `/_matrix/client/versions` request with the given
3170    /// versions and unstable features.
3171    pub fn ok_custom(
3172        self,
3173        versions: &[&str],
3174        unstable_features: &BTreeMap<&str, bool>,
3175    ) -> MatrixMock<'a> {
3176        self.respond_with(ResponseTemplate::new(200).set_body_json(json!({
3177            "unstable_features": unstable_features,
3178            "versions": versions,
3179        })))
3180    }
3181}
3182
3183/// A prebuilt mock for the room summary endpoint.
3184pub struct RoomSummaryEndpoint;
3185
3186impl<'a> MockEndpoint<'a, RoomSummaryEndpoint> {
3187    /// Returns a successful response with some default data for the given room
3188    /// id.
3189    pub fn ok(self, room_id: &RoomId) -> MatrixMock<'a> {
3190        self.respond_with(ResponseTemplate::new(200).set_body_json(json!({
3191            "room_id": room_id,
3192            "guest_can_join": true,
3193            "num_joined_members": 1,
3194            "world_readable": true,
3195            "join_rule": "public",
3196        })))
3197    }
3198}
3199
3200/// A prebuilt mock to set a room's pinned events.
3201pub struct SetRoomPinnedEventsEndpoint;
3202
3203impl<'a> MockEndpoint<'a, SetRoomPinnedEventsEndpoint> {
3204    /// Returns a successful response with a given event id.
3205    /// id.
3206    pub fn ok(self, event_id: OwnedEventId) -> MatrixMock<'a> {
3207        self.ok_with_event_id(event_id)
3208    }
3209
3210    /// Returns an error response with a generic error code indicating the
3211    /// client is not authorized to set pinned events.
3212    pub fn unauthorized(self) -> MatrixMock<'a> {
3213        self.respond_with(ResponseTemplate::new(400))
3214    }
3215}
3216
3217/// A prebuilt mock for `GET /account/whoami` request.
3218pub struct WhoAmIEndpoint;
3219
3220impl<'a> MockEndpoint<'a, WhoAmIEndpoint> {
3221    /// Returns a successful response with the default device ID.
3222    pub fn ok(self) -> MatrixMock<'a> {
3223        self.ok_with_device_id(device_id!("D3V1C31D"))
3224    }
3225
3226    /// Returns a successful response with the given device ID.
3227    pub fn ok_with_device_id(self, device_id: &DeviceId) -> MatrixMock<'a> {
3228        self.respond_with(ResponseTemplate::new(200).set_body_json(json!({
3229            "user_id": "@joe:example.org",
3230            "device_id": device_id,
3231        })))
3232    }
3233
3234    /// Returns an error response with an `M_UNKNOWN_TOKEN`.
3235    pub fn err_unknown_token(self) -> MatrixMock<'a> {
3236        self.respond_with(ResponseTemplate::new(401).set_body_json(json!({
3237            "errcode": "M_UNKNOWN_TOKEN",
3238            "error": "Invalid token"
3239        })))
3240    }
3241}
3242
3243/// A prebuilt mock for `POST /keys/upload` request.
3244pub struct UploadKeysEndpoint;
3245
3246impl<'a> MockEndpoint<'a, UploadKeysEndpoint> {
3247    /// Returns a successful response with counts of 10 curve25519 keys and 20
3248    /// signed curve25519 keys.
3249    pub fn ok(self) -> MatrixMock<'a> {
3250        self.respond_with(ResponseTemplate::new(200).set_body_json(json!({
3251            "one_time_key_counts": {
3252                "curve25519": 10,
3253                "signed_curve25519": 20,
3254            },
3255        })))
3256    }
3257}
3258
3259/// A prebuilt mock for `POST /keys/query` request.
3260pub struct QueryKeysEndpoint;
3261
3262impl<'a> MockEndpoint<'a, QueryKeysEndpoint> {
3263    /// Returns a successful empty response.
3264    pub fn ok(self) -> MatrixMock<'a> {
3265        self.respond_with(ResponseTemplate::new(200).set_body_json(json!({})))
3266    }
3267}
3268
3269/// A prebuilt mock for `GET /.well-known/matrix/client` request.
3270pub struct WellKnownEndpoint;
3271
3272impl<'a> MockEndpoint<'a, WellKnownEndpoint> {
3273    /// Returns a successful response with the URL for this homeserver.
3274    pub fn ok(self) -> MatrixMock<'a> {
3275        let server_uri = self.server.uri();
3276        self.ok_with_homeserver_url(&server_uri)
3277    }
3278
3279    /// Returns a successful response with the given homeserver URL.
3280    pub fn ok_with_homeserver_url(self, homeserver_url: &str) -> MatrixMock<'a> {
3281        self.respond_with(ResponseTemplate::new(200).set_body_json(json!({
3282            "m.homeserver": {
3283                "base_url": homeserver_url,
3284            },
3285            "m.rtc_foci": [
3286                {
3287                    "type": "livekit",
3288                    "livekit_service_url": "https://livekit.example.com",
3289                },
3290            ],
3291        })))
3292    }
3293
3294    /// Returns a 404 error response.
3295    pub fn error404(self) -> MatrixMock<'a> {
3296        self.respond_with(ResponseTemplate::new(404))
3297    }
3298}
3299
3300/// A prebuilt mock for `POST /keys/device_signing/upload` request.
3301pub struct UploadCrossSigningKeysEndpoint;
3302
3303impl<'a> MockEndpoint<'a, UploadCrossSigningKeysEndpoint> {
3304    /// Returns a successful empty response.
3305    pub fn ok(self) -> MatrixMock<'a> {
3306        self.respond_with(ResponseTemplate::new(200).set_body_json(json!({})))
3307    }
3308
3309    /// Returns an error response with a UIAA stage that failed to authenticate
3310    /// because of an invalid password.
3311    pub fn uiaa_invalid_password(self) -> MatrixMock<'a> {
3312        self.respond_with(ResponseTemplate::new(401).set_body_json(json!({
3313            "errcode": "M_FORBIDDEN",
3314            "error": "Invalid password",
3315            "flows": [
3316                {
3317                    "stages": [
3318                        "m.login.password"
3319                    ]
3320                }
3321            ],
3322            "params": {},
3323            "session": "oFIJVvtEOCKmRUTYKTYIIPHL"
3324        })))
3325    }
3326
3327    /// Returns an error response with a UIAA stage.
3328    pub fn uiaa(self) -> MatrixMock<'a> {
3329        self.respond_with(ResponseTemplate::new(401).set_body_json(json!({
3330            "flows": [
3331                {
3332                    "stages": [
3333                        "m.login.password"
3334                    ]
3335                }
3336            ],
3337            "params": {},
3338            "session": "oFIJVvtEOCKmRUTYKTYIIPHL"
3339        })))
3340    }
3341
3342    /// Returns an error response with an OAuth 2.0 UIAA stage.
3343    pub fn uiaa_oauth(self) -> MatrixMock<'a> {
3344        let server_uri = self.server.uri();
3345        self.respond_with(ResponseTemplate::new(401).set_body_json(json!({
3346            "session": "dummy",
3347            "flows": [{
3348                "stages": [ "org.matrix.cross_signing_reset" ]
3349            }],
3350            "params": {
3351                "org.matrix.cross_signing_reset": {
3352                    "url": format!("{server_uri}/account/?action=org.matrix.cross_signing_reset"),
3353                }
3354            },
3355            "msg": "To reset your end-to-end encryption cross-signing identity, you first need to approve it and then try again."
3356        })))
3357    }
3358}
3359
3360/// A prebuilt mock for `POST /keys/signatures/upload` request.
3361pub struct UploadCrossSigningSignaturesEndpoint;
3362
3363impl<'a> MockEndpoint<'a, UploadCrossSigningSignaturesEndpoint> {
3364    /// Returns a successful empty response.
3365    pub fn ok(self) -> MatrixMock<'a> {
3366        self.respond_with(ResponseTemplate::new(200).set_body_json(json!({})))
3367    }
3368}
3369
3370/// A prebuilt mock for the room leave endpoint.
3371pub struct RoomLeaveEndpoint;
3372
3373impl<'a> MockEndpoint<'a, RoomLeaveEndpoint> {
3374    /// Returns a successful response with some default data for the given room
3375    /// id.
3376    pub fn ok(self, room_id: &RoomId) -> MatrixMock<'a> {
3377        self.respond_with(ResponseTemplate::new(200).set_body_json(json!({
3378            "room_id": room_id,
3379        })))
3380    }
3381
3382    /// Returns a `M_FORBIDDEN` response.
3383    pub fn forbidden(self) -> MatrixMock<'a> {
3384        self.respond_with(ResponseTemplate::new(403).set_body_json(json!({
3385            "errcode": "M_FORBIDDEN",
3386            "error": "sowwy",
3387        })))
3388    }
3389}
3390
3391/// A prebuilt mock for the room forget endpoint.
3392pub struct RoomForgetEndpoint;
3393
3394impl<'a> MockEndpoint<'a, RoomForgetEndpoint> {
3395    /// Returns a successful response with some default data for the given room
3396    /// id.
3397    pub fn ok(self) -> MatrixMock<'a> {
3398        self.respond_with(ResponseTemplate::new(200).set_body_json(json!({})))
3399    }
3400}
3401
3402/// A prebuilt mock for `POST /logout` request.
3403pub struct LogoutEndpoint;
3404
3405impl<'a> MockEndpoint<'a, LogoutEndpoint> {
3406    /// Returns a successful empty response.
3407    pub fn ok(self) -> MatrixMock<'a> {
3408        self.respond_with(ResponseTemplate::new(200).set_body_json(json!({})))
3409    }
3410}
3411
3412/// A prebuilt mock for a `GET /rooms/{roomId}/threads` request.
3413pub struct RoomThreadsEndpoint;
3414
3415impl<'a> MockEndpoint<'a, RoomThreadsEndpoint> {
3416    /// Expects an optional `from` to be set on the request.
3417    pub fn match_from(self, from: &str) -> Self {
3418        Self { mock: self.mock.and(query_param("from", from)), ..self }
3419    }
3420
3421    /// Returns a successful response with some optional events and previous
3422    /// batch token.
3423    pub fn ok(
3424        self,
3425        chunk: Vec<Raw<AnyTimelineEvent>>,
3426        next_batch: Option<String>,
3427    ) -> MatrixMock<'a> {
3428        self.respond_with(ResponseTemplate::new(200).set_body_json(json!({
3429            "chunk": chunk,
3430            "next_batch": next_batch
3431        })))
3432    }
3433}
3434
3435/// A prebuilt mock for a `GET /rooms/{roomId}/relations/{eventId}` family of
3436/// requests.
3437#[derive(Default)]
3438pub struct RoomRelationsEndpoint {
3439    event_id: Option<OwnedEventId>,
3440    spec: Option<IncludeRelations>,
3441}
3442
3443impl<'a> MockEndpoint<'a, RoomRelationsEndpoint> {
3444    /// Expects an optional `from` to be set on the request.
3445    pub fn match_from(self, from: &str) -> Self {
3446        Self { mock: self.mock.and(query_param("from", from)), ..self }
3447    }
3448
3449    /// Expects an optional `limit` to be set on the request.
3450    pub fn match_limit(self, limit: u32) -> Self {
3451        Self { mock: self.mock.and(query_param("limit", limit.to_string())), ..self }
3452    }
3453
3454    /// Match the given subrequest, according to the given specification.
3455    pub fn match_subrequest(mut self, spec: IncludeRelations) -> Self {
3456        self.endpoint.spec = Some(spec);
3457        self
3458    }
3459
3460    /// Expects the request to match a specific event id.
3461    pub fn match_target_event(mut self, event_id: OwnedEventId) -> Self {
3462        self.endpoint.event_id = Some(event_id);
3463        self
3464    }
3465
3466    /// Returns a successful response with some optional events and pagination
3467    /// tokens.
3468    pub fn ok(mut self, response: RoomRelationsResponseTemplate) -> MatrixMock<'a> {
3469        // Escape the leading $ to not confuse the regular expression engine.
3470        let event_spec = self
3471            .endpoint
3472            .event_id
3473            .take()
3474            .map(|event_id| event_id.as_str().replace("$", "\\$"))
3475            .unwrap_or_else(|| ".*".to_owned());
3476
3477        match self.endpoint.spec.take() {
3478            Some(IncludeRelations::RelationsOfType(rel_type)) => {
3479                self.mock = self.mock.and(path_regex(format!(
3480                    r"^/_matrix/client/v1/rooms/.*/relations/{event_spec}/{rel_type}$"
3481                )));
3482            }
3483            Some(IncludeRelations::RelationsOfTypeAndEventType(rel_type, event_type)) => {
3484                self.mock = self.mock.and(path_regex(format!(
3485                    r"^/_matrix/client/v1/rooms/.*/relations/{event_spec}/{rel_type}/{event_type}$"
3486                )));
3487            }
3488            _ => {
3489                self.mock = self.mock.and(path_regex(format!(
3490                    r"^/_matrix/client/v1/rooms/.*/relations/{event_spec}",
3491                )));
3492            }
3493        }
3494
3495        self.respond_with(ResponseTemplate::new(200).set_body_json(json!({
3496            "chunk": response.chunk,
3497            "next_batch": response.next_batch,
3498            "prev_batch": response.prev_batch,
3499            "recursion_depth": response.recursion_depth,
3500        })))
3501    }
3502}
3503
3504/// Helper function to set up a [`MockBuilder`] so it intercepts the account
3505/// data URLs.
3506fn global_account_data_mock_builder(
3507    builder: MockBuilder,
3508    user_id: &UserId,
3509    event_type: GlobalAccountDataEventType,
3510) -> MockBuilder {
3511    builder
3512        .and(path_regex(format!(r"^/_matrix/client/v3/user/{user_id}/account_data/{event_type}",)))
3513}
3514
3515/// A prebuilt mock for a `GET
3516/// /_matrix/client/v3/user/{userId}/account_data/io.element.recent_emoji`
3517/// request, which fetches the recently used emojis in the account data.
3518#[cfg(feature = "experimental-element-recent-emojis")]
3519pub struct GetRecentEmojisEndpoint;
3520
3521#[cfg(feature = "experimental-element-recent-emojis")]
3522impl<'a> MockEndpoint<'a, GetRecentEmojisEndpoint> {
3523    /// Returns a mock for a successful fetch of the recently used emojis in the
3524    /// account data.
3525    pub fn ok(self, user_id: &UserId, emojis: Vec<(String, UInt)>) -> MatrixMock<'a> {
3526        let mock =
3527            global_account_data_mock_builder(self.mock, user_id, "io.element.recent_emoji".into())
3528                .respond_with(
3529                    ResponseTemplate::new(200).set_body_json(json!({ "recent_emoji": emojis })),
3530                );
3531        MatrixMock { server: self.server, mock }
3532    }
3533}
3534
3535/// A prebuilt mock for a `PUT
3536/// /_matrix/client/v3/user/{userId}/account_data/io.element.recent_emoji`
3537/// request, which updates the recently used emojis in the account data.
3538#[cfg(feature = "experimental-element-recent-emojis")]
3539pub struct UpdateRecentEmojisEndpoint {
3540    pub(crate) request_body: Option<Vec<(String, UInt)>>,
3541}
3542
3543#[cfg(feature = "experimental-element-recent-emojis")]
3544impl UpdateRecentEmojisEndpoint {
3545    /// Creates a new instance of the recent update recent emojis mock endpoint.
3546    fn new() -> Self {
3547        Self { request_body: None }
3548    }
3549}
3550
3551#[cfg(feature = "experimental-element-recent-emojis")]
3552impl<'a> MockEndpoint<'a, UpdateRecentEmojisEndpoint> {
3553    /// Returns a mock that will check the body of the request, making sure its
3554    /// contents match the provided list of emojis.
3555    pub fn match_emojis_in_request_body(self, emojis: Vec<(String, UInt)>) -> Self {
3556        Self::new(
3557            self.server,
3558            self.mock.and(body_json(json!(RecentEmojisContent::new(emojis)))),
3559            self.endpoint,
3560        )
3561    }
3562
3563    /// Returns a mock for a successful update of the recent emojis account data
3564    /// event. The request body contents should match the provided emoji
3565    /// list.
3566    #[cfg(feature = "experimental-element-recent-emojis")]
3567    pub fn ok(self, user_id: &UserId) -> MatrixMock<'a> {
3568        let mock =
3569            global_account_data_mock_builder(self.mock, user_id, "io.element.recent_emoji".into())
3570                .respond_with(ResponseTemplate::new(200).set_body_json(()));
3571        MatrixMock { server: self.server, mock }
3572    }
3573}
3574
3575/// A response to a [`RoomRelationsEndpoint`] query.
3576#[derive(Default)]
3577pub struct RoomRelationsResponseTemplate {
3578    /// The set of timeline events returned by this query.
3579    pub chunk: Vec<Raw<AnyTimelineEvent>>,
3580
3581    /// An opaque string representing a pagination token, which semantics depend
3582    /// on the direction used in the request.
3583    pub next_batch: Option<String>,
3584
3585    /// An opaque string representing a pagination token, which semantics depend
3586    /// on the direction used in the request.
3587    pub prev_batch: Option<String>,
3588
3589    /// If `recurse` was set on the request, the depth to which the server
3590    /// recursed.
3591    ///
3592    /// If `recurse` was not set, this field must be absent.
3593    pub recursion_depth: Option<u32>,
3594}
3595
3596impl RoomRelationsResponseTemplate {
3597    /// Fill the events returned as part of this response.
3598    pub fn events(mut self, chunk: Vec<impl Into<Raw<AnyTimelineEvent>>>) -> Self {
3599        self.chunk = chunk.into_iter().map(Into::into).collect();
3600        self
3601    }
3602
3603    /// Fill the `next_batch` token returned as part of this response.
3604    pub fn next_batch(mut self, token: impl Into<String>) -> Self {
3605        self.next_batch = Some(token.into());
3606        self
3607    }
3608
3609    /// Fill the `prev_batch` token returned as part of this response.
3610    pub fn prev_batch(mut self, token: impl Into<String>) -> Self {
3611        self.prev_batch = Some(token.into());
3612        self
3613    }
3614
3615    /// Fill the recursion depth returned in this response.
3616    pub fn recursion_depth(mut self, depth: u32) -> Self {
3617        self.recursion_depth = Some(depth);
3618        self
3619    }
3620}
3621
3622/// A prebuilt mock for `POST /rooms/{roomId}/receipt/{receiptType}/{eventId}`
3623/// request.
3624pub struct ReceiptEndpoint;
3625
3626impl<'a> MockEndpoint<'a, ReceiptEndpoint> {
3627    /// Returns a successful empty response.
3628    pub fn ok(self) -> MatrixMock<'a> {
3629        self.respond_with(ResponseTemplate::new(200).set_body_json(json!({})))
3630    }
3631
3632    /// Ensures that the body of the request is a superset of the provided
3633    /// `body` parameter.
3634    pub fn body_matches_partial_json(self, body: Value) -> Self {
3635        Self { mock: self.mock.and(body_partial_json(body)), ..self }
3636    }
3637
3638    /// Ensures that the body of the request is the exact provided `body`
3639    /// parameter.
3640    pub fn body_json(self, body: Value) -> Self {
3641        Self { mock: self.mock.and(body_json(body)), ..self }
3642    }
3643
3644    /// Ensures that the request matches a specific receipt thread.
3645    pub fn match_thread(self, thread: ReceiptThread) -> Self {
3646        if let Some(thread_str) = thread.as_str() {
3647            self.body_matches_partial_json(json!({
3648                "thread_id": thread_str
3649            }))
3650        } else {
3651            self
3652        }
3653    }
3654
3655    /// Ensures that the request matches a specific event id.
3656    pub fn match_event_id(self, event_id: &EventId) -> Self {
3657        Self {
3658            mock: self.mock.and(path_regex(format!(
3659                r"^/_matrix/client/v3/rooms/.*/receipt/.*/{}$",
3660                event_id.as_str().replace("$", "\\$")
3661            ))),
3662            ..self
3663        }
3664    }
3665}
3666
3667/// A prebuilt mock for `POST /rooms/{roomId}/read_markers` request.
3668pub struct ReadMarkersEndpoint;
3669
3670impl<'a> MockEndpoint<'a, ReadMarkersEndpoint> {
3671    /// Returns a successful empty response.
3672    pub fn ok(self) -> MatrixMock<'a> {
3673        self.respond_with(ResponseTemplate::new(200).set_body_json(json!({})))
3674    }
3675}
3676
3677/// A prebuilt mock for `PUT /user/{userId}/rooms/{roomId}/account_data/{type}`
3678/// request.
3679pub struct RoomAccountDataEndpoint;
3680
3681impl<'a> MockEndpoint<'a, RoomAccountDataEndpoint> {
3682    /// Returns a successful empty response.
3683    pub fn ok(self) -> MatrixMock<'a> {
3684        self.respond_with(ResponseTemplate::new(200).set_body_json(json!({})))
3685    }
3686}
3687
3688/// A prebuilt mock for `GET /_matrix/client/v1/media/config` request.
3689pub struct AuthenticatedMediaConfigEndpoint;
3690
3691impl<'a> MockEndpoint<'a, AuthenticatedMediaConfigEndpoint> {
3692    /// Returns a successful response with the provided max upload size.
3693    pub fn ok(self, max_upload_size: UInt) -> MatrixMock<'a> {
3694        self.respond_with(ResponseTemplate::new(200).set_body_json(json!({
3695            "m.upload.size": max_upload_size,
3696        })))
3697    }
3698
3699    /// Returns a successful response with a maxed out max upload size.
3700    pub fn ok_default(self) -> MatrixMock<'a> {
3701        self.respond_with(ResponseTemplate::new(200).set_body_json(json!({
3702            "m.upload.size": UInt::MAX,
3703        })))
3704    }
3705}
3706
3707/// A prebuilt mock for `GET /_matrix/media/v3/config` request.
3708pub struct MediaConfigEndpoint;
3709
3710impl<'a> MockEndpoint<'a, MediaConfigEndpoint> {
3711    /// Returns a successful response with the provided max upload size.
3712    pub fn ok(self, max_upload_size: UInt) -> MatrixMock<'a> {
3713        self.respond_with(ResponseTemplate::new(200).set_body_json(json!({
3714            "m.upload.size": max_upload_size,
3715        })))
3716    }
3717}
3718
3719/// A prebuilt mock for `POST /login` requests.
3720pub struct LoginEndpoint;
3721
3722impl<'a> MockEndpoint<'a, LoginEndpoint> {
3723    /// Returns a successful response.
3724    pub fn ok(self) -> MatrixMock<'a> {
3725        self.respond_with(ResponseTemplate::new(200).set_body_json(&*test_json::LOGIN))
3726    }
3727
3728    /// Returns a given response on POST /login requests
3729    ///
3730    /// # Arguments
3731    ///
3732    /// * `response` - The response that the mock server sends on POST /login
3733    ///   requests.
3734    ///
3735    /// # Returns
3736    ///
3737    /// Returns a [`MatrixMock`] which can be mounted.
3738    ///
3739    /// # Examples
3740    ///
3741    /// ```
3742    /// use matrix_sdk::test_utils::mocks::{
3743    ///     LoginResponseTemplate200, MatrixMockServer,
3744    /// };
3745    /// use matrix_sdk_test::async_test;
3746    /// use ruma::{device_id, time::Duration, user_id};
3747    ///
3748    /// #[async_test]
3749    /// async fn test_ok_with() {
3750    ///     let server = MatrixMockServer::new().await;
3751    ///     server
3752    ///         .mock_login()
3753    ///         .ok_with(LoginResponseTemplate200::new(
3754    ///             "qwerty",
3755    ///             device_id!("DEADBEEF"),
3756    ///             user_id!("@cheeky_monkey:matrix.org"),
3757    ///         ))
3758    ///         .mount()
3759    ///         .await;
3760    ///
3761    ///     let client = server.client_builder().unlogged().build().await;
3762    ///
3763    ///     let result = client
3764    ///         .matrix_auth()
3765    ///         .login_username("example", "wordpass")
3766    ///         .send()
3767    ///         .await
3768    ///         .unwrap();
3769    ///
3770    ///     assert!(
3771    ///         result.access_tokesn.unwrap() == "qwerty",
3772    ///         "wrong access token in response"
3773    ///     );
3774    ///     assert!(
3775    ///         result.device_id.unwrap() == "DEADBEEF",
3776    ///         "wrong device id in response"
3777    ///     );
3778    ///     assert!(
3779    ///         result.user_id.unwrap() == "@cheeky_monkey:matrix.org",
3780    ///         "wrong user id in response"
3781    ///     );
3782    /// }
3783    /// ```
3784    pub fn ok_with(self, response: LoginResponseTemplate200) -> MatrixMock<'a> {
3785        self.respond_with(ResponseTemplate::new(200).set_body_json(json!({
3786            "access_token": response.access_token,
3787            "device_id": response.device_id,
3788            "user_id": response.user_id,
3789            "expires_in": response.expires_in.map(|duration| { duration.as_millis() }),
3790            "refresh_token": response.refresh_token,
3791            "well_known": response.well_known.map(|vals| {
3792                json!({
3793                    "m.homeserver": {
3794                        "base_url": vals.homeserver_url
3795                    },
3796                    "m.identity_server": vals.identity_url.map(|url| {
3797                        json!({
3798                            "base_url": url
3799                        })
3800                    })
3801                })
3802            }),
3803        })))
3804    }
3805
3806    /// Ensures that the body of the request is a superset of the provided
3807    /// `body` parameter.
3808    pub fn body_matches_partial_json(self, body: Value) -> Self {
3809        Self { mock: self.mock.and(body_partial_json(body)), ..self }
3810    }
3811}
3812
3813#[derive(Default)]
3814struct LoginResponseWellKnown {
3815    /// Required if well_known is used: The base URL for the homeserver for
3816    /// client-server connections.
3817    homeserver_url: String,
3818
3819    /// Required if well_known and m.identity_server are used: The base URL for
3820    /// the identity server for client-server connections.
3821    identity_url: Option<String>,
3822}
3823
3824/// A response to a [`LoginEndpoint`] query with status code 200.
3825#[derive(Default)]
3826pub struct LoginResponseTemplate200 {
3827    /// Required: An access token for the account. This access token can then be
3828    /// used to authorize other requests.
3829    access_token: Option<String>,
3830
3831    /// Required: ID of the logged-in device. Will be the same as the
3832    /// corresponding parameter in the request, if one was specified.
3833    device_id: Option<OwnedDeviceId>,
3834
3835    /// The lifetime of the access token, in milliseconds. Once the access token
3836    /// has expired a new access token can be obtained by using the provided
3837    /// refresh token. If no refresh token is provided, the client will need
3838    /// to re-log in to obtain a new access token. If not given, the client
3839    /// can assume that the access token will not expire.
3840    expires_in: Option<Duration>,
3841
3842    /// A refresh token for the account. This token can be used to obtain a new
3843    /// access token when it expires by calling the /refresh endpoint.
3844    refresh_token: Option<String>,
3845
3846    /// Required: The fully-qualified Matrix ID for the account.
3847    user_id: Option<OwnedUserId>,
3848
3849    /// Optional client configuration provided by the server.
3850    well_known: Option<LoginResponseWellKnown>,
3851}
3852
3853impl LoginResponseTemplate200 {
3854    /// Constructor for empty response
3855    pub fn new<T1: Into<OwnedDeviceId>, T2: Into<OwnedUserId>>(
3856        access_token: &str,
3857        device_id: T1,
3858        user_id: T2,
3859    ) -> Self {
3860        Self {
3861            access_token: Some(access_token.to_owned()),
3862            device_id: Some(device_id.into()),
3863            user_id: Some(user_id.into()),
3864            ..Default::default()
3865        }
3866    }
3867
3868    /// sets expires_in
3869    pub fn expires_in(mut self, value: Duration) -> Self {
3870        self.expires_in = Some(value);
3871        self
3872    }
3873
3874    /// sets refresh_token
3875    pub fn refresh_token(mut self, value: &str) -> Self {
3876        self.refresh_token = Some(value.to_owned());
3877        self
3878    }
3879
3880    /// sets well_known which takes a homeserver_url and an optional
3881    /// identity_url
3882    pub fn well_known(mut self, homeserver_url: String, identity_url: Option<String>) -> Self {
3883        self.well_known = Some(LoginResponseWellKnown { homeserver_url, identity_url });
3884        self
3885    }
3886}
3887
3888/// A prebuilt mock for `GET /devices` requests.
3889pub struct DevicesEndpoint;
3890
3891impl<'a> MockEndpoint<'a, DevicesEndpoint> {
3892    /// Returns a successful response.
3893    pub fn ok(self) -> MatrixMock<'a> {
3894        self.respond_with(ResponseTemplate::new(200).set_body_json(&*test_json::DEVICES))
3895    }
3896}
3897
3898/// A prebuilt mock for `POST /user_directory/search` requests.
3899pub struct UserDirectoryEndpoint;
3900
3901impl<'a> MockEndpoint<'a, UserDirectoryEndpoint> {
3902    /// Returns a successful response.
3903    pub fn ok(self) -> MatrixMock<'a> {
3904        self.respond_with(
3905            ResponseTemplate::new(200)
3906                .set_body_json(&*test_json::search_users::SEARCH_USERS_RESPONSE),
3907        )
3908    }
3909}
3910
3911/// A prebuilt mock for `POST /createRoom` requests.
3912pub struct CreateRoomEndpoint;
3913
3914impl<'a> MockEndpoint<'a, CreateRoomEndpoint> {
3915    /// Returns a successful response.
3916    pub fn ok(self) -> MatrixMock<'a> {
3917        self.respond_with(
3918            ResponseTemplate::new(200).set_body_json(json!({ "room_id": "!room:example.org"})),
3919        )
3920    }
3921}
3922
3923/// A prebuilt mock for `POST /rooms/{roomId}/upgrade` requests.
3924pub struct UpgradeRoomEndpoint;
3925
3926impl<'a> MockEndpoint<'a, UpgradeRoomEndpoint> {
3927    /// Returns a successful response with desired replacement_room ID.
3928    pub fn ok_with(self, new_room_id: &RoomId) -> MatrixMock<'a> {
3929        self.respond_with(
3930            ResponseTemplate::new(200)
3931                .set_body_json(json!({ "replacement_room": new_room_id.as_str()})),
3932        )
3933    }
3934}
3935
3936/// A prebuilt mock for `POST /media/v1/create` requests.
3937pub struct MediaAllocateEndpoint;
3938
3939impl<'a> MockEndpoint<'a, MediaAllocateEndpoint> {
3940    /// Returns a successful response.
3941    pub fn ok(self) -> MatrixMock<'a> {
3942        self.respond_with(ResponseTemplate::new(200).set_body_json(json!({
3943          "content_uri": "mxc://example.com/AQwafuaFswefuhsfAFAgsw"
3944        })))
3945    }
3946}
3947
3948/// A prebuilt mock for `PUT /media/v3/upload/{server_name}/{media_id}`
3949/// requests.
3950pub struct MediaAllocatedUploadEndpoint;
3951
3952impl<'a> MockEndpoint<'a, MediaAllocatedUploadEndpoint> {
3953    /// Returns a successful response.
3954    pub fn ok(self) -> MatrixMock<'a> {
3955        self.respond_with(ResponseTemplate::new(200).set_body_json(json!({})))
3956    }
3957}
3958
3959/// A prebuilt mock for `GET /media/v3/download` requests.
3960pub struct MediaDownloadEndpoint;
3961
3962impl<'a> MockEndpoint<'a, MediaDownloadEndpoint> {
3963    /// Returns a successful response with a plain text content.
3964    pub fn ok_plain_text(self) -> MatrixMock<'a> {
3965        self.respond_with(ResponseTemplate::new(200).set_body_string("Hello, World!"))
3966    }
3967
3968    /// Returns a successful response with a fake image content.
3969    pub fn ok_image(self) -> MatrixMock<'a> {
3970        self.respond_with(
3971            ResponseTemplate::new(200).set_body_raw(b"binaryjpegfullimagedata", "image/jpeg"),
3972        )
3973    }
3974}
3975
3976/// A prebuilt mock for `GET /media/v3/thumbnail` requests.
3977pub struct MediaThumbnailEndpoint;
3978
3979impl<'a> MockEndpoint<'a, MediaThumbnailEndpoint> {
3980    /// Returns a successful response with a fake image content.
3981    pub fn ok(self) -> MatrixMock<'a> {
3982        self.respond_with(
3983            ResponseTemplate::new(200).set_body_raw(b"binaryjpegthumbnaildata", "image/jpeg"),
3984        )
3985    }
3986}
3987
3988/// A prebuilt mock for `GET /client/v1/media/download` requests.
3989pub struct AuthedMediaDownloadEndpoint;
3990
3991impl<'a> MockEndpoint<'a, AuthedMediaDownloadEndpoint> {
3992    /// Returns a successful response with a plain text content.
3993    pub fn ok_plain_text(self) -> MatrixMock<'a> {
3994        self.respond_with(ResponseTemplate::new(200).set_body_string("Hello, World!"))
3995    }
3996
3997    /// Returns a successful response with the given bytes.
3998    pub fn ok_bytes(self, bytes: Vec<u8>) -> MatrixMock<'a> {
3999        self.respond_with(
4000            ResponseTemplate::new(200).set_body_raw(bytes, "application/octet-stream"),
4001        )
4002    }
4003
4004    /// Returns a successful response with a fake image content.
4005    pub fn ok_image(self) -> MatrixMock<'a> {
4006        self.respond_with(
4007            ResponseTemplate::new(200).set_body_raw(b"binaryjpegfullimagedata", "image/jpeg"),
4008        )
4009    }
4010}
4011
4012/// A prebuilt mock for `GET /client/v1/media/thumbnail` requests.
4013pub struct AuthedMediaThumbnailEndpoint;
4014
4015impl<'a> MockEndpoint<'a, AuthedMediaThumbnailEndpoint> {
4016    /// Returns a successful response with a fake image content.
4017    pub fn ok(self) -> MatrixMock<'a> {
4018        self.respond_with(
4019            ResponseTemplate::new(200).set_body_raw(b"binaryjpegthumbnaildata", "image/jpeg"),
4020        )
4021    }
4022}
4023
4024/// A prebuilt mock for `GET /client/v3/rooms/{room_id}/join` requests.
4025pub struct JoinRoomEndpoint {
4026    room_id: OwnedRoomId,
4027}
4028
4029impl<'a> MockEndpoint<'a, JoinRoomEndpoint> {
4030    /// Returns a successful response using the provided [`RoomId`].
4031    pub fn ok(self) -> MatrixMock<'a> {
4032        let room_id = self.endpoint.room_id.to_owned();
4033
4034        self.respond_with(ResponseTemplate::new(200).set_body_json(json!({
4035            "room_id": room_id,
4036        })))
4037    }
4038}
4039
4040#[derive(Default)]
4041struct ThreadSubscriptionMatchers {
4042    /// Optional room id to match in the query.
4043    room_id: Option<OwnedRoomId>,
4044    /// Optional thread root event id to match in the query.
4045    thread_root: Option<OwnedEventId>,
4046}
4047
4048impl ThreadSubscriptionMatchers {
4049    /// Match the request parameter against a specific room id.
4050    fn match_room_id(mut self, room_id: OwnedRoomId) -> Self {
4051        self.room_id = Some(room_id);
4052        self
4053    }
4054
4055    /// Match the request parameter against a specific thread root event id.
4056    fn match_thread_id(mut self, thread_root: OwnedEventId) -> Self {
4057        self.thread_root = Some(thread_root);
4058        self
4059    }
4060
4061    /// Compute the final URI for the thread subscription endpoint.
4062    fn endpoint_regexp_uri(&self) -> String {
4063        if self.room_id.is_some() || self.thread_root.is_some() {
4064            format!(
4065                "^/_matrix/client/unstable/io.element.msc4306/rooms/{}/thread/{}/subscription$",
4066                self.room_id.as_deref().map(|s| s.as_str()).unwrap_or(".*"),
4067                self.thread_root.as_deref().map(|s| s.as_str()).unwrap_or(".*").replace("$", "\\$")
4068            )
4069        } else {
4070            "^/_matrix/client/unstable/io.element.msc4306/rooms/.*/thread/.*/subscription$"
4071                .to_owned()
4072        }
4073    }
4074}
4075
4076/// A prebuilt mock for `GET
4077/// /client/*/rooms/{room_id}/threads/{thread_root}/subscription`
4078#[derive(Default)]
4079pub struct RoomGetThreadSubscriptionEndpoint {
4080    matchers: ThreadSubscriptionMatchers,
4081}
4082
4083impl<'a> MockEndpoint<'a, RoomGetThreadSubscriptionEndpoint> {
4084    /// Returns a successful response for the given thread subscription.
4085    pub fn ok(mut self, automatic: bool) -> MatrixMock<'a> {
4086        self.mock = self.mock.and(path_regex(self.endpoint.matchers.endpoint_regexp_uri()));
4087        self.respond_with(ResponseTemplate::new(200).set_body_json(json!({
4088            "automatic": automatic
4089        })))
4090    }
4091
4092    /// Match the request parameter against a specific room id.
4093    pub fn match_room_id(mut self, room_id: OwnedRoomId) -> Self {
4094        self.endpoint.matchers = self.endpoint.matchers.match_room_id(room_id);
4095        self
4096    }
4097    /// Match the request parameter against a specific thread root event id.
4098    pub fn match_thread_id(mut self, thread_root: OwnedEventId) -> Self {
4099        self.endpoint.matchers = self.endpoint.matchers.match_thread_id(thread_root);
4100        self
4101    }
4102}
4103
4104/// A prebuilt mock for `PUT
4105/// /client/*/rooms/{room_id}/threads/{thread_root}/subscription`
4106#[derive(Default)]
4107pub struct RoomPutThreadSubscriptionEndpoint {
4108    matchers: ThreadSubscriptionMatchers,
4109}
4110
4111impl<'a> MockEndpoint<'a, RoomPutThreadSubscriptionEndpoint> {
4112    /// Returns a successful response for the given setting of thread
4113    /// subscription.
4114    pub fn ok(mut self) -> MatrixMock<'a> {
4115        self.mock = self.mock.and(path_regex(self.endpoint.matchers.endpoint_regexp_uri()));
4116        self.respond_with(ResponseTemplate::new(200))
4117    }
4118
4119    /// Returns that the server skipped an automated thread subscription,
4120    /// because the user unsubscribed to the thread after the event id passed in
4121    /// the automatic subscription.
4122    pub fn conflicting_unsubscription(mut self) -> MatrixMock<'a> {
4123        self.mock = self.mock.and(path_regex(self.endpoint.matchers.endpoint_regexp_uri()));
4124        self.respond_with(ResponseTemplate::new(409).set_body_json(json!({
4125            "errcode": "IO.ELEMENT.MSC4306.M_CONFLICTING_UNSUBSCRIPTION",
4126            "error": "the user unsubscribed after the subscription event id"
4127        })))
4128    }
4129
4130    /// Match the request parameter against a specific room id.
4131    pub fn match_room_id(mut self, room_id: OwnedRoomId) -> Self {
4132        self.endpoint.matchers = self.endpoint.matchers.match_room_id(room_id);
4133        self
4134    }
4135    /// Match the request parameter against a specific thread root event id.
4136    pub fn match_thread_id(mut self, thread_root: OwnedEventId) -> Self {
4137        self.endpoint.matchers = self.endpoint.matchers.match_thread_id(thread_root);
4138        self
4139    }
4140    /// Match the request body's `automatic` field against a specific event id.
4141    pub fn match_automatic_event_id(mut self, up_to_event_id: &EventId) -> Self {
4142        self.mock = self.mock.and(body_json(json!({
4143            "automatic": up_to_event_id
4144        })));
4145        self
4146    }
4147}
4148
4149/// A prebuilt mock for `DELETE
4150/// /client/*/rooms/{room_id}/threads/{thread_root}/subscription`
4151#[derive(Default)]
4152pub struct RoomDeleteThreadSubscriptionEndpoint {
4153    matchers: ThreadSubscriptionMatchers,
4154}
4155
4156impl<'a> MockEndpoint<'a, RoomDeleteThreadSubscriptionEndpoint> {
4157    /// Returns a successful response for the deletion of a given thread
4158    /// subscription.
4159    pub fn ok(mut self) -> MatrixMock<'a> {
4160        self.mock = self.mock.and(path_regex(self.endpoint.matchers.endpoint_regexp_uri()));
4161        self.respond_with(ResponseTemplate::new(200))
4162    }
4163
4164    /// Match the request parameter against a specific room id.
4165    pub fn match_room_id(mut self, room_id: OwnedRoomId) -> Self {
4166        self.endpoint.matchers = self.endpoint.matchers.match_room_id(room_id);
4167        self
4168    }
4169    /// Match the request parameter against a specific thread root event id.
4170    pub fn match_thread_id(mut self, thread_root: OwnedEventId) -> Self {
4171        self.endpoint.matchers = self.endpoint.matchers.match_thread_id(thread_root);
4172        self
4173    }
4174}
4175
4176/// A prebuilt mock for `PUT
4177/// /_matrix/client/v3/pushrules/global/{kind}/{ruleId}/enabled`.
4178pub struct EnablePushRuleEndpoint;
4179
4180impl<'a> MockEndpoint<'a, EnablePushRuleEndpoint> {
4181    /// Returns a successful empty JSON response.
4182    pub fn ok(self) -> MatrixMock<'a> {
4183        self.ok_empty_json()
4184    }
4185}
4186
4187/// A prebuilt mock for `PUT
4188/// /_matrix/client/v3/pushrules/global/{kind}/{ruleId}/actions`.
4189pub struct SetPushRulesActionsEndpoint;
4190
4191impl<'a> MockEndpoint<'a, SetPushRulesActionsEndpoint> {
4192    /// Returns a successful empty JSON response.
4193    pub fn ok(self) -> MatrixMock<'a> {
4194        self.ok_empty_json()
4195    }
4196}
4197
4198/// A prebuilt mock for `PUT
4199/// /_matrix/client/v3/pushrules/global/{kind}/{ruleId}`.
4200pub struct SetPushRulesEndpoint;
4201
4202impl<'a> MockEndpoint<'a, SetPushRulesEndpoint> {
4203    /// Returns a successful empty JSON response.
4204    pub fn ok(self) -> MatrixMock<'a> {
4205        self.ok_empty_json()
4206    }
4207}
4208
4209/// A prebuilt mock for `DELETE
4210/// /_matrix/client/v3/pushrules/global/{kind}/{ruleId}`.
4211pub struct DeletePushRulesEndpoint;
4212
4213impl<'a> MockEndpoint<'a, DeletePushRulesEndpoint> {
4214    /// Returns a successful empty JSON response.
4215    pub fn ok(self) -> MatrixMock<'a> {
4216        self.ok_empty_json()
4217    }
4218}
4219
4220/// A prebuilt mock for the federation version endpoint.
4221pub struct FederationVersionEndpoint;
4222
4223impl<'a> MockEndpoint<'a, FederationVersionEndpoint> {
4224    /// Returns a successful response with the given server name and version.
4225    pub fn ok(self, server_name: &str, version: &str) -> MatrixMock<'a> {
4226        let response_body = json!({
4227            "server": {
4228                "name": server_name,
4229                "version": version
4230            }
4231        });
4232        self.respond_with(ResponseTemplate::new(200).set_body_json(response_body))
4233    }
4234
4235    /// Returns a successful response with empty/missing server information.
4236    pub fn ok_empty(self) -> MatrixMock<'a> {
4237        let response_body = json!({});
4238        self.respond_with(ResponseTemplate::new(200).set_body_json(response_body))
4239    }
4240}
4241
4242/// A prebuilt mock for `GET ^/_matrix/client/v3/thread_subscriptions`.
4243#[derive(Default)]
4244pub struct GetThreadSubscriptionsEndpoint {
4245    /// New thread subscriptions per (room id, thread root event id).
4246    subscribed: BTreeMap<OwnedRoomId, BTreeMap<OwnedEventId, ThreadSubscription>>,
4247    /// New thread unsubscriptions per (room id, thread root event id).
4248    unsubscribed: BTreeMap<OwnedRoomId, BTreeMap<OwnedEventId, ThreadUnsubscription>>,
4249    /// Optional delay to respond to the query.
4250    delay: Option<Duration>,
4251}
4252
4253impl<'a> MockEndpoint<'a, GetThreadSubscriptionsEndpoint> {
4254    /// Add a single thread subscription to the response.
4255    pub fn add_subscription(
4256        mut self,
4257        room_id: OwnedRoomId,
4258        thread_root: OwnedEventId,
4259        subscription: ThreadSubscription,
4260    ) -> Self {
4261        self.endpoint.subscribed.entry(room_id).or_default().insert(thread_root, subscription);
4262        self
4263    }
4264
4265    /// Add a single thread unsubscription to the response.
4266    pub fn add_unsubcription(
4267        mut self,
4268        room_id: OwnedRoomId,
4269        thread_root: OwnedEventId,
4270        unsubscription: ThreadUnsubscription,
4271    ) -> Self {
4272        self.endpoint.unsubscribed.entry(room_id).or_default().insert(thread_root, unsubscription);
4273        self
4274    }
4275
4276    /// Respond with a given delay to the query.
4277    pub fn with_delay(mut self, delay: Duration) -> Self {
4278        self.endpoint.delay = Some(delay);
4279        self
4280    }
4281
4282    /// Match the `from` query parameter to a given value.
4283    pub fn match_from(self, from: &str) -> Self {
4284        Self { mock: self.mock.and(query_param("from", from)), ..self }
4285    }
4286    /// Match the `to` query parameter to a given value.
4287    pub fn match_to(self, to: &str) -> Self {
4288        Self { mock: self.mock.and(query_param("to", to)), ..self }
4289    }
4290
4291    /// Returns a successful response with the given thread subscriptions, and
4292    /// "end" parameter to be used in the next query.
4293    pub fn ok(self, end: Option<String>) -> MatrixMock<'a> {
4294        let response_body = json!({
4295            "subscribed": self.endpoint.subscribed,
4296            "unsubscribed": self.endpoint.unsubscribed,
4297            "end": end,
4298        });
4299
4300        let mut template = ResponseTemplate::new(200).set_body_json(response_body);
4301
4302        if let Some(delay) = self.endpoint.delay {
4303            template = template.set_delay(delay);
4304        }
4305
4306        self.respond_with(template)
4307    }
4308}
4309
4310/// A prebuilt mock for `GET /client/*/rooms/{roomId}/hierarchy`
4311#[derive(Default)]
4312pub struct GetHierarchyEndpoint;
4313
4314impl<'a> MockEndpoint<'a, GetHierarchyEndpoint> {
4315    /// Returns a successful response containing the given room IDs.
4316    pub fn ok_with_room_ids(self, room_ids: Vec<&RoomId>) -> MatrixMock<'a> {
4317        let rooms = room_ids
4318            .iter()
4319            .map(|id| {
4320                json!({
4321                  "room_id": id,
4322                  "num_joined_members": 1,
4323                  "world_readable": false,
4324                  "guest_can_join": false,
4325                  "children_state": []
4326                })
4327            })
4328            .collect::<Vec<_>>();
4329
4330        self.respond_with(ResponseTemplate::new(200).set_body_json(json!({
4331            "rooms": rooms,
4332        })))
4333    }
4334
4335    /// Returns a successful response containing the given room IDs and children
4336    /// states
4337    pub fn ok_with_room_ids_and_children_state(
4338        self,
4339        room_ids: Vec<&RoomId>,
4340        children_state: Vec<(&RoomId, Vec<&ServerName>)>,
4341    ) -> MatrixMock<'a> {
4342        let children_state = children_state
4343            .into_iter()
4344            .map(|(id, via)| {
4345                json!({
4346                    "type":
4347                    "m.space.child",
4348                    "state_key": id,
4349                    "content": { "via": via },
4350                    "sender": "@bob:matrix.org",
4351                    "origin_server_ts": MilliSecondsSinceUnixEpoch::now()
4352                })
4353            })
4354            .collect::<Vec<_>>();
4355
4356        let rooms = room_ids
4357            .iter()
4358            .map(|id| {
4359                json!({
4360                  "room_id": id,
4361                  "num_joined_members": 1,
4362                  "world_readable": false,
4363                  "guest_can_join": false,
4364                  "children_state": children_state
4365                })
4366            })
4367            .collect::<Vec<_>>();
4368
4369        self.respond_with(ResponseTemplate::new(200).set_body_json(json!({
4370            "rooms": rooms,
4371        })))
4372    }
4373
4374    /// Returns a successful response with an empty list of rooms.
4375    pub fn ok(self) -> MatrixMock<'a> {
4376        self.respond_with(ResponseTemplate::new(200).set_body_json(json!({
4377            "rooms": []
4378        })))
4379    }
4380}
4381
4382/// A prebuilt mock for running simplified sliding sync.
4383pub struct SlidingSyncEndpoint;
4384
4385impl<'a> MockEndpoint<'a, SlidingSyncEndpoint> {
4386    /// Mocks the sliding sync endpoint with the given response.
4387    pub fn ok(self, response: v5::Response) -> MatrixMock<'a> {
4388        // A bit silly that we need to destructure all the fields ourselves, but
4389        // Response isn't serializable :'(
4390        self.respond_with(ResponseTemplate::new(200).set_body_json(json!({
4391            "txn_id": response.txn_id,
4392            "pos": response.pos,
4393            "lists": response.lists,
4394            "rooms": response.rooms,
4395            "extensions": response.extensions,
4396        })))
4397    }
4398
4399    /// Temporarily mocks the sync with the given endpoint and runs a client
4400    /// sync with it.
4401    ///
4402    /// After calling this function, the sync endpoint isn't mocked anymore.
4403    pub async fn ok_and_run<F: FnOnce(SlidingSyncBuilder) -> SlidingSyncBuilder>(
4404        self,
4405        client: &Client,
4406        on_builder: F,
4407        response: v5::Response,
4408    ) {
4409        let _scope = self.ok(response).mount_as_scoped().await;
4410
4411        let sliding_sync =
4412            on_builder(client.sliding_sync("test_id").unwrap()).build().await.unwrap();
4413
4414        let _summary = sliding_sync.sync_once().await.unwrap();
4415    }
4416}