Skip to main content

matrix_sdk/test_utils/mocks/
mod.rs

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