Skip to main content

matrix_sdk/test_utils/mocks/
mod.rs

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