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},
23};
24
25use matrix_sdk_base::deserialized_responses::TimelineEvent;
26use matrix_sdk_test::{
27    test_json, InvitedRoomBuilder, JoinedRoomBuilder, KnockedRoomBuilder, LeftRoomBuilder,
28    SyncResponseBuilder,
29};
30use percent_encoding::{AsciiSet, CONTROLS};
31use ruma::{
32    api::client::room::Visibility,
33    device_id,
34    directory::PublicRoomsChunk,
35    events::{
36        room::member::RoomMemberEvent, AnyStateEvent, AnyTimelineEvent, MessageLikeEventType,
37        StateEventType,
38    },
39    serde::Raw,
40    time::Duration,
41    DeviceId, MxcUri, OwnedEventId, OwnedRoomId, RoomId, ServerName,
42};
43use serde::Deserialize;
44use serde_json::{json, Value};
45use wiremock::{
46    matchers::{body_partial_json, header, method, path, path_regex, query_param},
47    Mock, MockBuilder, MockGuard, MockServer, Request, Respond, ResponseTemplate, Times,
48};
49
50pub mod oauth;
51
52use super::client::MockClientBuilder;
53use crate::{Client, OwnedServerName, Room};
54
55/// A [`wiremock`] [`MockServer`] along with useful methods to help mocking
56/// Matrix client-server API endpoints easily.
57///
58/// It implements mock endpoints, limiting the shared code as much as possible,
59/// so the mocks are still flexible to use as scoped/unscoped mounts, named, and
60/// so on.
61///
62/// It works like this:
63///
64/// * start by saying which endpoint you'd like to mock, e.g.
65///   [`Self::mock_room_send()`]. This returns a specialized [`MockEndpoint`]
66///   data structure, with its own impl. For this example, it's
67///   `MockEndpoint<RoomSendEndpoint>`.
68/// * configure the response on the endpoint-specific mock data structure. For
69///   instance, if you want the sending to result in a transient failure, call
70///   [`MockEndpoint::error500`]; if you want it to succeed and return the event
71///   `$42`, call [`MockEndpoint::ok()`]. It's still possible to call
72///   [`MockEndpoint::respond_with()`], as we do with wiremock MockBuilder, for
73///   maximum flexibility when the helpers aren't sufficient.
74/// * once the endpoint's response is configured, for any mock builder, you get
75///   a [`MatrixMock`]; this is a plain [`wiremock::Mock`] with the server
76///   curried, so one doesn't have to pass it around when calling
77///   [`MatrixMock::mount()`] or [`MatrixMock::mount_as_scoped()`]. As such, it
78///   mostly defers its implementations to [`wiremock::Mock`] under the hood.
79///
80/// # Examples
81///
82/// ```
83/// # tokio_test::block_on(async {
84/// use matrix_sdk::{ruma::{room_id, event_id}, test_utils::mocks::MatrixMockServer};
85/// use serde_json::json;
86///
87/// // First create the mock server and client pair.
88/// let mock_server = MatrixMockServer::new().await;
89/// let client = mock_server.client_builder().build().await;
90///
91/// // Let's say that our rooms are not encrypted.
92/// mock_server.mock_room_state_encryption().plain().mount().await;
93///
94/// // Let us get a room where we will send an event.
95/// let room = mock_server
96///     .sync_joined_room(&client, room_id!("!room_id:localhost"))
97///     .await;
98///
99/// // Now we mock the endpoint so we can actually send the event.
100/// let event_id = event_id!("$some_id");
101/// let send_guard = mock_server
102///     .mock_room_send()
103///     .ok(event_id)
104///     .expect(1)
105///     .mount_as_scoped()
106///     .await;
107///
108/// // And we send it out.
109/// let response = room.send_raw("m.room.message", json!({ "body": "Hello world" })).await?;
110///
111/// assert_eq!(
112///     event_id,
113///     response.event_id,
114///     "The event ID we mocked should match the one we received when we sent the event"
115/// );
116/// # anyhow::Ok(()) });
117/// ```
118pub struct MatrixMockServer {
119    server: MockServer,
120
121    /// Make the sync response builder stateful, to keep in memory the batch
122    /// token and avoid the client ignoring subsequent responses after the first
123    /// one.
124    sync_response_builder: Arc<Mutex<SyncResponseBuilder>>,
125}
126
127impl MatrixMockServer {
128    /// Create a new [`wiremock`] server specialized for Matrix usage.
129    pub async fn new() -> Self {
130        let server = MockServer::start().await;
131        Self { server, sync_response_builder: Default::default() }
132    }
133
134    /// Creates a new [`MatrixMockServer`] from a [`wiremock`] server.
135    pub fn from_server(server: MockServer) -> Self {
136        Self { server, sync_response_builder: Default::default() }
137    }
138
139    /// Creates a new [`MockClientBuilder`] configured to use this server,
140    /// preconfigured with a session expected by the server endpoints.
141    pub fn client_builder(&self) -> MockClientBuilder {
142        MockClientBuilder::new(self.server.uri())
143    }
144
145    /// Return the underlying [`wiremock`] server.
146    pub fn server(&self) -> &MockServer {
147        &self.server
148    }
149
150    /// Get an `OAuthMockServer` that uses the same mock server as this one.
151    pub fn oauth(&self) -> oauth::OAuthMockServer<'_> {
152        oauth::OAuthMockServer::new(self)
153    }
154
155    /// Mock the given endpoint.
156    fn mock_endpoint<T>(&self, mock: MockBuilder, endpoint: T) -> MockEndpoint<'_, T> {
157        MockEndpoint::new(&self.server, mock, endpoint)
158    }
159
160    /// Overrides the sync/ endpoint with knowledge that the given
161    /// invited/joined/knocked/left room exists, runs a sync and returns the
162    /// given room.
163    ///
164    /// # Examples
165    ///
166    /// ```
167    /// # tokio_test::block_on(async {
168    /// use matrix_sdk::{ruma::{room_id, event_id}, test_utils::mocks::MatrixMockServer};
169    /// use matrix_sdk_test::LeftRoomBuilder;
170    ///
171    /// let mock_server = MatrixMockServer::new().await;
172    /// let client = mock_server.client_builder().build().await;
173    ///
174    /// let left_room = mock_server
175    ///     .sync_room(&client, LeftRoomBuilder::new(room_id!("!room_id:localhost")))
176    ///     .await;
177    /// # anyhow::Ok(()) });
178    pub async fn sync_room(&self, client: &Client, room_data: impl Into<AnyRoomBuilder>) -> Room {
179        let any_room = room_data.into();
180        let room_id = any_room.room_id().to_owned();
181
182        self.mock_sync()
183            .ok_and_run(client, move |builder| match any_room {
184                AnyRoomBuilder::Invited(invited) => {
185                    builder.add_invited_room(invited);
186                }
187                AnyRoomBuilder::Joined(joined) => {
188                    builder.add_joined_room(joined);
189                }
190                AnyRoomBuilder::Left(left) => {
191                    builder.add_left_room(left);
192                }
193                AnyRoomBuilder::Knocked(knocked) => {
194                    builder.add_knocked_room(knocked);
195                }
196            })
197            .await;
198
199        client.get_room(&room_id).expect("look at me, the room is known now")
200    }
201
202    /// Overrides the sync/ endpoint with knowledge that the given room exists
203    /// in the joined state, runs a sync and returns the given room.
204    ///
205    /// # Examples
206    ///
207    /// ```
208    /// # tokio_test::block_on(async {
209    /// use matrix_sdk::{ruma::room_id, test_utils::mocks::MatrixMockServer};
210    ///
211    /// let mock_server = MatrixMockServer::new().await;
212    /// let client = mock_server.client_builder().build().await;
213    ///
214    /// let room = mock_server
215    ///     .sync_joined_room(&client, room_id!("!room_id:localhost"))
216    ///     .await;
217    /// # anyhow::Ok(()) });
218    pub async fn sync_joined_room(&self, client: &Client, room_id: &RoomId) -> Room {
219        self.sync_room(client, JoinedRoomBuilder::new(room_id)).await
220    }
221
222    /// Verify that the previous mocks expected number of requests match
223    /// reality, and then cancels all active mocks.
224    ///
225    /// # Examples
226    ///
227    /// ```
228    /// # tokio_test::block_on(async {
229    /// use matrix_sdk::{ruma::{room_id, event_id}, test_utils::mocks::MatrixMockServer};
230    /// use serde_json::json;
231    ///
232    /// let mock_server = MatrixMockServer::new().await;
233    /// let client = mock_server.client_builder().build().await;
234    ///
235    /// mock_server.mock_room_state_encryption().plain().mount().await;
236    /// let room = mock_server
237    ///     .sync_joined_room(&client, room_id!("!room_id:localhost"))
238    ///     .await;
239    /// mock_server.mock_room_send().ok(event_id!("$some_id")).mount().await;
240    ///
241    /// // This will succeed.
242    /// let response = room.send_raw("m.room.message", json!({ "body": "Hello world" })).await?;
243    ///
244    /// // Now we reset the mocks.
245    /// mock_server.verify_and_reset().await;
246    ///
247    /// // And we can't send anymore.
248    /// let response = room
249    ///     .send_raw("m.room.message", json!({ "body": "Hello world" }))
250    ///     .await
251    ///     .expect_err("We removed the mock so sending should now fail");
252    /// # anyhow::Ok(()) });
253    /// ```
254    pub async fn verify_and_reset(&self) {
255        self.server.verify().await;
256        self.server.reset().await;
257    }
258}
259
260// Specific mount endpoints.
261impl MatrixMockServer {
262    /// Mocks a sync endpoint.
263    ///
264    /// # Examples
265    ///
266    /// ```
267    /// # tokio_test::block_on(async {
268    /// use matrix_sdk::{ruma::room_id, test_utils::mocks::MatrixMockServer};
269    /// use matrix_sdk_test::JoinedRoomBuilder;
270    ///
271    /// // First create the mock server and client pair.
272    /// let mock_server = MatrixMockServer::new().await;
273    /// let client = mock_server.client_builder().build().await;
274    /// let room_id = room_id!("!room_id:localhost");
275    ///
276    /// // Let's emulate what `MatrixMockServer::sync_joined_room()` does.
277    /// mock_server
278    ///     .mock_sync()
279    ///     .ok_and_run(&client, |builder| {
280    ///         builder.add_joined_room(JoinedRoomBuilder::new(room_id));
281    ///     })
282    ///     .await;
283    ///
284    /// let room = client
285    ///     .get_room(room_id)
286    ///     .expect("The room should be available after we mocked the sync");
287    /// # anyhow::Ok(()) });
288    /// ```
289    pub fn mock_sync(&self) -> MockEndpoint<'_, SyncEndpoint> {
290        let mock = Mock::given(method("GET")).and(path("/_matrix/client/v3/sync"));
291        self.mock_endpoint(
292            mock,
293            SyncEndpoint { sync_response_builder: self.sync_response_builder.clone() },
294        )
295        .expect_default_access_token()
296    }
297
298    /// Creates a prebuilt mock for sending an event in a room.
299    ///
300    /// Note: works with *any* room.
301    ///
302    /// # Examples
303    ///
304    /// ```
305    /// # tokio_test::block_on(async {
306    /// use matrix_sdk::{ruma::{room_id, event_id}, test_utils::mocks::MatrixMockServer};
307    /// use serde_json::json;
308    ///
309    /// let mock_server = MatrixMockServer::new().await;
310    /// let client = mock_server.client_builder().build().await;
311    ///
312    /// mock_server.mock_room_state_encryption().plain().mount().await;
313    ///
314    /// let room = mock_server
315    ///     .sync_joined_room(&client, room_id!("!room_id:localhost"))
316    ///     .await;
317    ///
318    /// let event_id = event_id!("$some_id");
319    /// mock_server
320    ///     .mock_room_send()
321    ///     .ok(event_id)
322    ///     .expect(1)
323    ///     .mount()
324    ///     .await;
325    ///
326    /// let response = room.send_raw("m.room.message", json!({ "body": "Hello world" })).await?;
327    ///
328    /// assert_eq!(
329    ///     event_id,
330    ///     response.event_id,
331    ///     "The event ID we mocked should match the one we received when we sent the event"
332    /// );
333    /// # anyhow::Ok(()) });
334    /// ```
335    pub fn mock_room_send(&self) -> MockEndpoint<'_, RoomSendEndpoint> {
336        let mock = Mock::given(method("PUT"))
337            .and(path_regex(r"^/_matrix/client/v3/rooms/.*/send/.*".to_owned()));
338        self.mock_endpoint(mock, RoomSendEndpoint).expect_default_access_token()
339    }
340
341    /// Creates a prebuilt mock for sending a state event in a room.
342    ///
343    /// Similar to: [`MatrixMockServer::mock_room_send`]
344    ///
345    /// Note: works with *any* room.
346    /// Note: works with *any* event type.
347    ///
348    /// ```
349    /// # tokio_test::block_on(async {
350    /// use matrix_sdk::{ruma::{room_id, event_id}, test_utils::mocks::MatrixMockServer};
351    /// use serde_json::json;
352    ///
353    /// let mock_server = MatrixMockServer::new().await;
354    /// let client = mock_server.client_builder().build().await;
355    ///
356    /// mock_server.mock_room_state_encryption().plain().mount().await;
357    ///
358    /// let room = mock_server
359    ///     .sync_joined_room(&client, room_id!("!room_id:localhost"))
360    ///     .await;
361    ///
362    /// let event_id = event_id!("$some_id");
363    /// mock_server
364    ///     .mock_room_send_state()
365    ///     .ok(event_id)
366    ///     .expect(1)
367    ///     .mount()
368    ///     .await;
369    ///
370    /// let response_not_mocked = room.send_raw("m.room.create", json!({ "body": "Hello world" })).await;
371    /// // The `/send` endpoint should not be mocked by the server.
372    /// assert!(response_not_mocked.is_err());
373    ///
374    ///
375    /// let response = room.send_state_event_raw("m.room.message", "my_key", json!({ "body": "Hello world" })).await?;
376    /// // The `/state` endpoint should be mocked by the server.
377    /// assert_eq!(
378    ///     event_id,
379    ///     response.event_id,
380    ///     "The event ID we mocked should match the one we received when we sent the event"
381    /// );
382    /// # anyhow::Ok(()) });
383    /// ```
384    pub fn mock_room_send_state(&self) -> MockEndpoint<'_, RoomSendStateEndpoint> {
385        let mock =
386            Mock::given(method("PUT")).and(path_regex(r"^/_matrix/client/v3/rooms/.*/state/.*/.*"));
387        self.mock_endpoint(mock, RoomSendStateEndpoint::default()).expect_default_access_token()
388    }
389
390    /// Creates a prebuilt mock for asking whether *a* room is encrypted or not.
391    ///
392    /// Note: Applies to all rooms.
393    ///
394    /// # Examples
395    ///
396    /// ```
397    /// # tokio_test::block_on(async {
398    /// use matrix_sdk::{ruma::room_id, test_utils::mocks::MatrixMockServer};
399    ///
400    /// let mock_server = MatrixMockServer::new().await;
401    /// let client = mock_server.client_builder().build().await;
402    ///
403    /// mock_server.mock_room_state_encryption().encrypted().mount().await;
404    ///
405    /// let room = mock_server
406    ///     .sync_joined_room(&client, room_id!("!room_id:localhost"))
407    ///     .await;
408    ///
409    /// assert!(
410    ///     room.latest_encryption_state().await?.is_encrypted(),
411    ///     "The room should be marked as encrypted."
412    /// );
413    /// # anyhow::Ok(()) });
414    /// ```
415    pub fn mock_room_state_encryption(&self) -> MockEndpoint<'_, EncryptionStateEndpoint> {
416        let mock = Mock::given(method("GET"))
417            .and(path_regex(r"^/_matrix/client/v3/rooms/.*/state/m.*room.*encryption.?"));
418        self.mock_endpoint(mock, EncryptionStateEndpoint).expect_default_access_token()
419    }
420
421    /// Creates a prebuilt mock for setting the room encryption state.
422    ///
423    /// Note: Applies to all rooms.
424    ///
425    /// # Examples
426    ///
427    /// ```
428    /// # tokio_test::block_on(async {
429    /// use matrix_sdk::{
430    ///     ruma::{event_id, room_id},
431    ///     test_utils::mocks::MatrixMockServer,
432    /// };
433    ///
434    /// let mock_server = MatrixMockServer::new().await;
435    /// let client = mock_server.client_builder().build().await;
436    ///
437    /// mock_server.mock_room_state_encryption().plain().mount().await;
438    /// mock_server
439    ///     .mock_set_room_state_encryption()
440    ///     .ok(event_id!("$id"))
441    ///     .mock_once()
442    ///     .mount()
443    ///     .await;
444    ///
445    /// let room = mock_server
446    ///     .sync_joined_room(&client, room_id!("!room_id:localhost"))
447    ///     .await;
448    ///
449    /// room.enable_encryption()
450    ///     .await
451    ///     .expect("We should be able to enable encryption in the room");
452    /// # anyhow::Ok(()) });
453    /// ```
454    pub fn mock_set_room_state_encryption(&self) -> MockEndpoint<'_, SetEncryptionStateEndpoint> {
455        let mock = Mock::given(method("PUT"))
456            .and(path_regex(r"^/_matrix/client/v3/rooms/.*/state/m.*room.*encryption.?"));
457        self.mock_endpoint(mock, SetEncryptionStateEndpoint).expect_default_access_token()
458    }
459
460    /// Creates a prebuilt mock for the room redact endpoint.
461    ///
462    /// # Examples
463    ///
464    /// ```
465    /// # tokio_test::block_on(async {
466    /// use matrix_sdk::{
467    ///     ruma::{event_id, room_id},
468    ///     test_utils::mocks::MatrixMockServer,
469    /// };
470    ///
471    /// let mock_server = MatrixMockServer::new().await;
472    /// let client = mock_server.client_builder().build().await;
473    /// let event_id = event_id!("$id");
474    ///
475    /// mock_server.mock_room_redact().ok(event_id).mock_once().mount().await;
476    ///
477    /// let room = mock_server
478    ///     .sync_joined_room(&client, room_id!("!room_id:localhost"))
479    ///     .await;
480    ///
481    /// room.redact(event_id, None, None)
482    ///     .await
483    ///     .expect("We should be able to redact events in the room");
484    /// # anyhow::Ok(()) });
485    /// ```
486    pub fn mock_room_redact(&self) -> MockEndpoint<'_, RoomRedactEndpoint> {
487        let mock = Mock::given(method("PUT"))
488            .and(path_regex(r"^/_matrix/client/v3/rooms/.*/redact/.*?/.*?"));
489        self.mock_endpoint(mock, RoomRedactEndpoint).expect_default_access_token()
490    }
491
492    /// Creates a prebuilt mock for retrieving an event with /room/.../event.
493    pub fn mock_room_event(&self) -> MockEndpoint<'_, RoomEventEndpoint> {
494        let mock = Mock::given(method("GET"));
495        self.mock_endpoint(mock, RoomEventEndpoint { room: None, match_event_id: false })
496            .expect_default_access_token()
497    }
498
499    /// Create a prebuild mock for paginating room message with the `/messages`
500    /// endpoint.
501    pub fn mock_room_messages(&self) -> MockEndpoint<'_, RoomMessagesEndpoint> {
502        let mock =
503            Mock::given(method("GET")).and(path_regex(r"^/_matrix/client/v3/rooms/.*/messages$"));
504        self.mock_endpoint(mock, RoomMessagesEndpoint).expect_default_access_token()
505    }
506
507    /// Create a prebuilt mock for uploading media.
508    pub fn mock_upload(&self) -> MockEndpoint<'_, UploadEndpoint> {
509        let mock = Mock::given(method("POST")).and(path("/_matrix/media/v3/upload"));
510        self.mock_endpoint(mock, UploadEndpoint).expect_default_access_token()
511    }
512
513    /// Create a prebuilt mock for resolving room aliases.
514    ///
515    /// # Examples
516    ///
517    /// ```
518    /// # tokio_test::block_on(async {
519    /// use matrix_sdk::{
520    ///     ruma::{owned_room_id, room_alias_id},
521    ///     test_utils::mocks::MatrixMockServer,
522    /// };
523    /// let mock_server = MatrixMockServer::new().await;
524    /// let client = mock_server.client_builder().build().await;
525    ///
526    /// mock_server
527    ///     .mock_room_directory_resolve_alias()
528    ///     .ok("!a:b.c", Vec::new())
529    ///     .mock_once()
530    ///     .mount()
531    ///     .await;
532    ///
533    /// let res = client
534    ///     .resolve_room_alias(room_alias_id!("#a:b.c"))
535    ///     .await
536    ///     .expect("We should be able to resolve the room alias");
537    /// assert_eq!(res.room_id, owned_room_id!("!a:b.c"));
538    /// # anyhow::Ok(()) });
539    /// ```
540    pub fn mock_room_directory_resolve_alias(&self) -> MockEndpoint<'_, ResolveRoomAliasEndpoint> {
541        let mock =
542            Mock::given(method("GET")).and(path_regex(r"/_matrix/client/v3/directory/room/.*"));
543        self.mock_endpoint(mock, ResolveRoomAliasEndpoint)
544    }
545
546    /// Create a prebuilt mock for publishing room aliases in the room
547    /// directory.
548    ///
549    /// # Examples
550    ///
551    /// ```
552    /// # tokio_test::block_on(async {
553    /// use matrix_sdk::{
554    ///     ruma::{room_alias_id, room_id},
555    ///     test_utils::mocks::MatrixMockServer,
556    /// };
557    ///
558    /// let mock_server = MatrixMockServer::new().await;
559    /// let client = mock_server.client_builder().build().await;
560    ///
561    /// mock_server
562    ///     .mock_room_directory_create_room_alias()
563    ///     .ok()
564    ///     .mock_once()
565    ///     .mount()
566    ///     .await;
567    ///
568    /// client
569    ///     .create_room_alias(room_alias_id!("#a:b.c"), room_id!("!a:b.c"))
570    ///     .await
571    ///     .expect("We should be able to create a room alias");
572    /// # anyhow::Ok(()) });
573    /// ```
574    pub fn mock_room_directory_create_room_alias(
575        &self,
576    ) -> MockEndpoint<'_, CreateRoomAliasEndpoint> {
577        let mock =
578            Mock::given(method("PUT")).and(path_regex(r"/_matrix/client/v3/directory/room/.*"));
579        self.mock_endpoint(mock, CreateRoomAliasEndpoint)
580    }
581
582    /// Create a prebuilt mock for removing room aliases from the room
583    /// directory.
584    ///
585    /// # Examples
586    ///
587    /// ```
588    /// # tokio_test::block_on(async {
589    /// use matrix_sdk::{
590    ///     ruma::room_alias_id, test_utils::mocks::MatrixMockServer,
591    /// };
592    ///
593    /// let mock_server = MatrixMockServer::new().await;
594    /// let client = mock_server.client_builder().build().await;
595    ///
596    /// mock_server
597    ///     .mock_room_directory_remove_room_alias()
598    ///     .ok()
599    ///     .mock_once()
600    ///     .mount()
601    ///     .await;
602    ///
603    /// client
604    ///     .remove_room_alias(room_alias_id!("#a:b.c"))
605    ///     .await
606    ///     .expect("We should be able to remove the room alias");
607    /// # anyhow::Ok(()) });
608    /// ```
609    pub fn mock_room_directory_remove_room_alias(
610        &self,
611    ) -> MockEndpoint<'_, RemoveRoomAliasEndpoint> {
612        let mock =
613            Mock::given(method("DELETE")).and(path_regex(r"/_matrix/client/v3/directory/room/.*"));
614        self.mock_endpoint(mock, RemoveRoomAliasEndpoint)
615    }
616
617    /// Create a prebuilt mock for listing public rooms.
618    ///
619    /// # Examples
620    ///
621    /// ```
622    /// #
623    /// tokio_test::block_on(async {
624    /// use js_int::uint;
625    /// use ruma::directory::PublicRoomsChunkInit;
626    /// use matrix_sdk::room_directory_search::RoomDirectorySearch;
627    /// use matrix_sdk::{
628    ///     ruma::{event_id, room_id},
629    ///     test_utils::mocks::MatrixMockServer,
630    /// };
631    /// let mock_server = MatrixMockServer::new().await;
632    /// let client = mock_server.client_builder().build().await;
633    /// let event_id = event_id!("$id");
634    /// let room_id = room_id!("!room_id:localhost");
635    ///
636    /// let chunk = vec![PublicRoomsChunkInit {
637    ///     num_joined_members: uint!(0),
638    ///     room_id: room_id.to_owned(),
639    ///     world_readable: true,
640    ///     guest_can_join: true,
641    /// }.into()];
642    ///
643    /// mock_server.mock_public_rooms().ok(chunk, None, None, Some(20)).mock_once().mount().await;
644    /// let mut room_directory_search = RoomDirectorySearch::new(client);
645    ///
646    /// room_directory_search.search(Some("some-alias".to_owned()), 100, None)
647    ///     .await
648    ///     .expect("Room directory search failed");
649    ///
650    /// let (results, _) = room_directory_search.results();
651    /// assert_eq!(results.len(), 1);
652    /// assert_eq!(results.get(0).unwrap().room_id, room_id.to_owned());
653    /// # });
654    /// ```
655    pub fn mock_public_rooms(&self) -> MockEndpoint<'_, PublicRoomsEndpoint> {
656        let mock = Mock::given(method("POST")).and(path_regex(r"/_matrix/client/v3/publicRooms"));
657        self.mock_endpoint(mock, PublicRoomsEndpoint)
658    }
659
660    /// Create a prebuilt mock for setting a room's visibility in the room
661    /// directory.
662    ///
663    /// # Examples
664    ///
665    /// ```
666    /// # tokio_test::block_on(async {
667    /// use matrix_sdk::{ruma::room_id, test_utils::mocks::MatrixMockServer};
668    /// use ruma::api::client::room::Visibility;
669    ///
670    /// let mock_server = MatrixMockServer::new().await;
671    /// let client = mock_server.client_builder().build().await;
672    ///
673    /// mock_server
674    ///     .mock_room_directory_set_room_visibility()
675    ///     .ok()
676    ///     .mock_once()
677    ///     .mount()
678    ///     .await;
679    ///
680    /// let room = mock_server
681    ///     .sync_joined_room(&client, room_id!("!room_id:localhost"))
682    ///     .await;
683    ///
684    /// room.privacy_settings()
685    ///     .update_room_visibility(Visibility::Private)
686    ///     .await
687    ///     .expect("We should be able to update the room's visibility");
688    /// # anyhow::Ok(()) });
689    /// ```
690    pub fn mock_room_directory_set_room_visibility(
691        &self,
692    ) -> MockEndpoint<'_, SetRoomVisibilityEndpoint> {
693        let mock = Mock::given(method("PUT"))
694            .and(path_regex(r"^/_matrix/client/v3/directory/list/room/.*$"));
695        self.mock_endpoint(mock, SetRoomVisibilityEndpoint)
696    }
697
698    /// Create a prebuilt mock for getting a room's visibility in the room
699    /// directory.
700    ///
701    /// # Examples
702    ///
703    /// ```
704    /// # tokio_test::block_on(async {
705    /// use matrix_sdk::{ruma::room_id, test_utils::mocks::MatrixMockServer};
706    /// use ruma::api::client::room::Visibility;
707    ///
708    /// let mock_server = MatrixMockServer::new().await;
709    /// let client = mock_server.client_builder().build().await;
710    ///
711    /// mock_server
712    ///     .mock_room_directory_get_room_visibility()
713    ///     .ok(Visibility::Public)
714    ///     .mock_once()
715    ///     .mount()
716    ///     .await;
717    ///
718    /// let room = mock_server
719    ///     .sync_joined_room(&client, room_id!("!room_id:localhost"))
720    ///     .await;
721    ///
722    /// let visibility = room
723    ///     .privacy_settings()
724    ///     .get_room_visibility()
725    ///     .await
726    ///     .expect("We should be able to get the room's visibility");
727    /// assert_eq!(visibility, Visibility::Public);
728    /// # anyhow::Ok(()) });
729    /// ```
730    pub fn mock_room_directory_get_room_visibility(
731        &self,
732    ) -> MockEndpoint<'_, GetRoomVisibilityEndpoint> {
733        let mock = Mock::given(method("GET"))
734            .and(path_regex(r"^/_matrix/client/v3/directory/list/room/.*$"));
735        self.mock_endpoint(mock, GetRoomVisibilityEndpoint)
736    }
737
738    /// Create a prebuilt mock for fetching information about key storage
739    /// backups.
740    ///
741    /// # Examples
742    ///
743    /// ```
744    /// # #[cfg(feature = "e2e-encryption")]
745    /// # {
746    /// # tokio_test::block_on(async {
747    /// use matrix_sdk::test_utils::mocks::MatrixMockServer;
748    ///
749    /// let mock_server = MatrixMockServer::new().await;
750    /// let client = mock_server.client_builder().build().await;
751    ///
752    /// mock_server.mock_room_keys_version().exists().expect(1).mount().await;
753    ///
754    /// let exists =
755    ///     client.encryption().backups().fetch_exists_on_server().await.unwrap();
756    ///
757    /// assert!(exists);
758    /// # });
759    /// # }
760    /// ```
761    pub fn mock_room_keys_version(&self) -> MockEndpoint<'_, RoomKeysVersionEndpoint> {
762        let mock =
763            Mock::given(method("GET")).and(path_regex(r"_matrix/client/v3/room_keys/version"));
764        self.mock_endpoint(mock, RoomKeysVersionEndpoint).expect_default_access_token()
765    }
766
767    /// Create a prebuilt mock for adding key storage backups via POST
768    pub fn mock_add_room_keys_version(&self) -> MockEndpoint<'_, AddRoomKeysVersionEndpoint> {
769        let mock =
770            Mock::given(method("POST")).and(path_regex(r"_matrix/client/v3/room_keys/version"));
771        self.mock_endpoint(mock, AddRoomKeysVersionEndpoint).expect_default_access_token()
772    }
773
774    /// Create a prebuilt mock for adding key storage backups via POST
775    pub fn mock_delete_room_keys_version(&self) -> MockEndpoint<'_, DeleteRoomKeysVersionEndpoint> {
776        let mock = Mock::given(method("DELETE"))
777            .and(path_regex(r"_matrix/client/v3/room_keys/version/[^/]*"));
778        self.mock_endpoint(mock, DeleteRoomKeysVersionEndpoint).expect_default_access_token()
779    }
780
781    /// Create a prebuilt mock for getting the room members in a room.
782    ///
783    /// # Examples
784    ///
785    /// ```
786    /// # tokio_test::block_on(async {
787    /// use matrix_sdk::{
788    ///     ruma::{event_id, room_id},
789    ///     test_utils::mocks::MatrixMockServer,
790    /// };
791    /// use matrix_sdk_base::RoomMemberships;
792    /// use matrix_sdk_test::event_factory::EventFactory;
793    /// use ruma::{
794    ///     events::room::member::{MembershipState, RoomMemberEventContent},
795    ///     user_id,
796    /// };
797    /// let mock_server = MatrixMockServer::new().await;
798    /// let client = mock_server.client_builder().build().await;
799    /// let event_id = event_id!("$id");
800    /// let room_id = room_id!("!room_id:localhost");
801    ///
802    /// let f = EventFactory::new().room(room_id);
803    /// let alice_user_id = user_id!("@alice:b.c");
804    /// let alice_knock_event = f
805    ///     .event(RoomMemberEventContent::new(MembershipState::Knock))
806    ///     .event_id(event_id)
807    ///     .sender(alice_user_id)
808    ///     .state_key(alice_user_id)
809    ///     .into_raw_timeline()
810    ///     .cast();
811    ///
812    /// mock_server
813    ///     .mock_get_members()
814    ///     .ok(vec![alice_knock_event])
815    ///     .mock_once()
816    ///     .mount()
817    ///     .await;
818    /// let room = mock_server.sync_joined_room(&client, room_id).await;
819    ///
820    /// let members = room.members(RoomMemberships::all()).await.unwrap();
821    /// assert_eq!(members.len(), 1);
822    /// # });
823    /// ```
824    pub fn mock_get_members(&self) -> MockEndpoint<'_, GetRoomMembersEndpoint> {
825        let mock =
826            Mock::given(method("GET")).and(path_regex(r"^/_matrix/client/v3/rooms/.*/members$"));
827        self.mock_endpoint(mock, GetRoomMembersEndpoint)
828    }
829
830    /// Creates a prebuilt mock for inviting a user to a room by its id.
831    ///
832    /// # Examples
833    ///
834    /// ```
835    /// # use ruma::user_id;
836    /// tokio_test::block_on(async {
837    /// use matrix_sdk::{
838    ///     ruma::room_id,
839    ///     test_utils::mocks::MatrixMockServer,
840    /// };
841    ///
842    /// let mock_server = MatrixMockServer::new().await;
843    /// let client = mock_server.client_builder().build().await;
844    ///
845    /// mock_server.mock_invite_user_by_id().ok().mock_once().mount().await;
846    ///
847    /// let room = mock_server
848    ///     .sync_joined_room(&client, room_id!("!room_id:localhost"))
849    ///     .await;
850    ///
851    /// room.invite_user_by_id(user_id!("@alice:localhost")).await.unwrap();
852    /// # anyhow::Ok(()) });
853    /// ```
854    pub fn mock_invite_user_by_id(&self) -> MockEndpoint<'_, InviteUserByIdEndpoint> {
855        let mock =
856            Mock::given(method("POST")).and(path_regex(r"^/_matrix/client/v3/rooms/.*/invite$"));
857        self.mock_endpoint(mock, InviteUserByIdEndpoint)
858    }
859
860    /// Creates a prebuilt mock for kicking a user from a room.
861    ///
862    /// # Examples
863    ///
864    /// ```
865    /// # use ruma::user_id;
866    /// tokio_test::block_on(async {
867    /// use matrix_sdk::{
868    ///     ruma::room_id,
869    ///     test_utils::mocks::MatrixMockServer,
870    /// };
871    ///
872    /// let mock_server = MatrixMockServer::new().await;
873    /// let client = mock_server.client_builder().build().await;
874    ///
875    /// mock_server.mock_kick_user().ok().mock_once().mount().await;
876    ///
877    /// let room = mock_server
878    ///     .sync_joined_room(&client, room_id!("!room_id:localhost"))
879    ///     .await;
880    ///
881    /// room.kick_user(user_id!("@alice:localhost"), None).await.unwrap();
882    /// # anyhow::Ok(()) });
883    /// ```
884    pub fn mock_kick_user(&self) -> MockEndpoint<'_, KickUserEndpoint> {
885        let mock =
886            Mock::given(method("POST")).and(path_regex(r"^/_matrix/client/v3/rooms/.*/kick"));
887        self.mock_endpoint(mock, KickUserEndpoint)
888    }
889
890    /// Creates a prebuilt mock for banning a user from a room.
891    ///
892    /// # Examples
893    ///
894    /// ```
895    /// # use ruma::user_id;
896    /// tokio_test::block_on(async {
897    /// use matrix_sdk::{
898    ///     ruma::room_id,
899    ///     test_utils::mocks::MatrixMockServer,
900    /// };
901    ///
902    /// let mock_server = MatrixMockServer::new().await;
903    /// let client = mock_server.client_builder().build().await;
904    ///
905    /// mock_server.mock_ban_user().ok().mock_once().mount().await;
906    ///
907    /// let room = mock_server
908    ///     .sync_joined_room(&client, room_id!("!room_id:localhost"))
909    ///     .await;
910    ///
911    /// room.ban_user(user_id!("@alice:localhost"), None).await.unwrap();
912    /// # anyhow::Ok(()) });
913    /// ```
914    pub fn mock_ban_user(&self) -> MockEndpoint<'_, BanUserEndpoint> {
915        let mock = Mock::given(method("POST")).and(path_regex(r"^/_matrix/client/v3/rooms/.*/ban"));
916        self.mock_endpoint(mock, BanUserEndpoint)
917    }
918
919    /// Creates a prebuilt mock for the `/_matrix/client/versions` endpoint.
920    pub fn mock_versions(&self) -> MockEndpoint<'_, VersionsEndpoint> {
921        let mock = Mock::given(method("GET")).and(path_regex(r"^/_matrix/client/versions"));
922        self.mock_endpoint(mock, VersionsEndpoint)
923    }
924
925    /// Creates a prebuilt mock for the room summary endpoint [MSC3266](https://github.com/matrix-org/matrix-spec-proposals/pull/3266).
926    pub fn mock_room_summary(&self) -> MockEndpoint<'_, RoomSummaryEndpoint> {
927        let mock = Mock::given(method("GET"))
928            .and(path_regex(r"^/_matrix/client/unstable/im.nheko.summary/rooms/.*/summary"));
929        self.mock_endpoint(mock, RoomSummaryEndpoint)
930    }
931
932    /// Creates a prebuilt mock for the endpoint used to set a room's pinned
933    /// events.
934    pub fn mock_set_room_pinned_events(&self) -> MockEndpoint<'_, SetRoomPinnedEventsEndpoint> {
935        let mock = Mock::given(method("PUT"))
936            .and(path_regex(r"^/_matrix/client/v3/rooms/.*/state/m.room.pinned_events/.*?"));
937        self.mock_endpoint(mock, SetRoomPinnedEventsEndpoint).expect_default_access_token()
938    }
939
940    /// Creates a prebuilt mock for the endpoint used to get information about
941    /// the owner of the given access token.
942    ///
943    /// If no access token is provided, the access token to match is `"1234"`,
944    /// which matches the default value in the mock data.
945    pub fn mock_who_am_i(&self) -> MockEndpoint<'_, WhoAmIEndpoint> {
946        let mock =
947            Mock::given(method("GET")).and(path_regex(r"^/_matrix/client/v3/account/whoami"));
948        self.mock_endpoint(mock, WhoAmIEndpoint).expect_default_access_token()
949    }
950
951    /// Creates a prebuilt mock for the endpoint used to publish end-to-end
952    /// encryption keys.
953    pub fn mock_upload_keys(&self) -> MockEndpoint<'_, UploadKeysEndpoint> {
954        let mock = Mock::given(method("POST")).and(path_regex(r"^/_matrix/client/v3/keys/upload"));
955        self.mock_endpoint(mock, UploadKeysEndpoint).expect_default_access_token()
956    }
957
958    /// Creates a prebuilt mock for the endpoint used to query end-to-end
959    /// encryption keys.
960    pub fn mock_query_keys(&self) -> MockEndpoint<'_, QueryKeysEndpoint> {
961        let mock = Mock::given(method("POST")).and(path_regex(r"^/_matrix/client/v3/keys/query"));
962        self.mock_endpoint(mock, QueryKeysEndpoint).expect_default_access_token()
963    }
964
965    /// Creates a prebuilt mock for the endpoint used to discover the URL of a
966    /// homeserver.
967    pub fn mock_well_known(&self) -> MockEndpoint<'_, WellKnownEndpoint> {
968        let mock = Mock::given(method("GET")).and(path_regex(r"^/.well-known/matrix/client"));
969        self.mock_endpoint(mock, WellKnownEndpoint)
970    }
971
972    /// Creates a prebuilt mock for the endpoint used to publish cross-signing
973    /// keys.
974    pub fn mock_upload_cross_signing_keys(
975        &self,
976    ) -> MockEndpoint<'_, UploadCrossSigningKeysEndpoint> {
977        let mock = Mock::given(method("POST"))
978            .and(path_regex(r"^/_matrix/client/v3/keys/device_signing/upload"));
979        self.mock_endpoint(mock, UploadCrossSigningKeysEndpoint).expect_default_access_token()
980    }
981
982    /// Creates a prebuilt mock for the endpoint used to publish cross-signing
983    /// signatures.
984    pub fn mock_upload_cross_signing_signatures(
985        &self,
986    ) -> MockEndpoint<'_, UploadCrossSigningSignaturesEndpoint> {
987        let mock = Mock::given(method("POST"))
988            .and(path_regex(r"^/_matrix/client/v3/keys/signatures/upload"));
989        self.mock_endpoint(mock, UploadCrossSigningSignaturesEndpoint).expect_default_access_token()
990    }
991
992    /// Creates a prebuilt mock for the endpoint used to leave a room.
993    pub fn mock_room_leave(&self) -> MockEndpoint<'_, RoomLeaveEndpoint> {
994        let mock =
995            Mock::given(method("POST")).and(path_regex(r"^/_matrix/client/v3/rooms/.*/leave"));
996        self.mock_endpoint(mock, RoomLeaveEndpoint).expect_default_access_token()
997    }
998
999    /// Create a prebuilt mock for the endpoint use to log out a session.
1000    pub fn mock_logout(&self) -> MockEndpoint<'_, LogoutEndpoint> {
1001        let mock = Mock::given(method("POST")).and(path("/_matrix/client/v3/logout"));
1002        self.mock_endpoint(mock, LogoutEndpoint).expect_default_access_token()
1003    }
1004}
1005
1006/// Parameter to [`MatrixMockServer::sync_room`].
1007pub enum AnyRoomBuilder {
1008    /// A room we've been invited to.
1009    Invited(InvitedRoomBuilder),
1010    /// A room we've joined.
1011    Joined(JoinedRoomBuilder),
1012    /// A room we've left.
1013    Left(LeftRoomBuilder),
1014    /// A room we've knocked to.
1015    Knocked(KnockedRoomBuilder),
1016}
1017
1018impl AnyRoomBuilder {
1019    /// Get the [`RoomId`] of the room this [`AnyRoomBuilder`] will create.
1020    fn room_id(&self) -> &RoomId {
1021        match self {
1022            AnyRoomBuilder::Invited(r) => r.room_id(),
1023            AnyRoomBuilder::Joined(r) => r.room_id(),
1024            AnyRoomBuilder::Left(r) => r.room_id(),
1025            AnyRoomBuilder::Knocked(r) => r.room_id(),
1026        }
1027    }
1028}
1029
1030impl From<InvitedRoomBuilder> for AnyRoomBuilder {
1031    fn from(val: InvitedRoomBuilder) -> AnyRoomBuilder {
1032        AnyRoomBuilder::Invited(val)
1033    }
1034}
1035
1036impl From<JoinedRoomBuilder> for AnyRoomBuilder {
1037    fn from(val: JoinedRoomBuilder) -> AnyRoomBuilder {
1038        AnyRoomBuilder::Joined(val)
1039    }
1040}
1041
1042impl From<LeftRoomBuilder> for AnyRoomBuilder {
1043    fn from(val: LeftRoomBuilder) -> AnyRoomBuilder {
1044        AnyRoomBuilder::Left(val)
1045    }
1046}
1047
1048impl From<KnockedRoomBuilder> for AnyRoomBuilder {
1049    fn from(val: KnockedRoomBuilder) -> AnyRoomBuilder {
1050        AnyRoomBuilder::Knocked(val)
1051    }
1052}
1053
1054/// The [path percent-encode set] as defined in the WHATWG URL standard + `/`
1055/// since we always encode single segments of the path.
1056///
1057/// [path percent-encode set]: https://url.spec.whatwg.org/#path-percent-encode-set
1058///
1059/// Copied from Ruma:
1060/// https://github.com/ruma/ruma/blob/e4cb409ff3aaa16f31a7fe1e61fee43b2d144f7b/crates/ruma-common/src/percent_encode.rs#L7
1061const PATH_PERCENT_ENCODE_SET: &AsciiSet = &CONTROLS
1062    .add(b' ')
1063    .add(b'"')
1064    .add(b'#')
1065    .add(b'<')
1066    .add(b'>')
1067    .add(b'?')
1068    .add(b'`')
1069    .add(b'{')
1070    .add(b'}')
1071    .add(b'/');
1072
1073fn percent_encoded_path(path: &str) -> String {
1074    percent_encoding::utf8_percent_encode(path, PATH_PERCENT_ENCODE_SET).to_string()
1075}
1076
1077/// A wrapper for a [`Mock`] as well as a [`MockServer`], allowing us to call
1078/// [`Mock::mount`] or [`Mock::mount_as_scoped`] without having to pass the
1079/// [`MockServer`] reference (i.e. call `mount()` instead of `mount(&server)`).
1080pub struct MatrixMock<'a> {
1081    mock: Mock,
1082    server: &'a MockServer,
1083}
1084
1085impl MatrixMock<'_> {
1086    /// Set an expectation on the number of times this [`MatrixMock`] should
1087    /// match in the current test case.
1088    ///
1089    /// Expectations are verified when the server is shutting down: if
1090    /// the expectation is not satisfied, the [`MatrixMockServer`] will panic
1091    /// and the `error_message` is shown.
1092    ///
1093    /// By default, no expectation is set for [`MatrixMock`]s.
1094    pub fn expect<T: Into<Times>>(self, num_calls: T) -> Self {
1095        Self { mock: self.mock.expect(num_calls), ..self }
1096    }
1097
1098    /// Assign a name to your mock.
1099    ///
1100    /// The mock name will be used in error messages (e.g. if the mock
1101    /// expectation is not satisfied) and debug logs to help you identify
1102    /// what failed.
1103    pub fn named(self, name: impl Into<String>) -> Self {
1104        Self { mock: self.mock.named(name), ..self }
1105    }
1106
1107    /// Respond to a response of this endpoint exactly once.
1108    ///
1109    /// After it's been called, subsequent responses will hit the next handler
1110    /// or a 404.
1111    ///
1112    /// Also verifies that it's been called once.
1113    pub fn mock_once(self) -> Self {
1114        Self { mock: self.mock.up_to_n_times(1).expect(1), ..self }
1115    }
1116
1117    /// Makes sure the endpoint is never reached.
1118    pub fn never(self) -> Self {
1119        Self { mock: self.mock.expect(0), ..self }
1120    }
1121
1122    /// Specify an upper limit to the number of times you would like this
1123    /// [`MatrixMock`] to respond to incoming requests that satisfy the
1124    /// conditions imposed by your matchers.
1125    pub fn up_to_n_times(self, num: u64) -> Self {
1126        Self { mock: self.mock.up_to_n_times(num), ..self }
1127    }
1128
1129    /// Mount a [`MatrixMock`] on the attached server.
1130    ///
1131    /// The [`MatrixMock`] will remain active until the [`MatrixMockServer`] is
1132    /// shut down. If you want to control or limit how long your
1133    /// [`MatrixMock`] stays active, check out [`Self::mount_as_scoped`].
1134    pub async fn mount(self) {
1135        self.mock.mount(self.server).await;
1136    }
1137
1138    /// Mount a [`MatrixMock`] as **scoped** on the attached server.
1139    ///
1140    /// When using [`Self::mount`], your [`MatrixMock`]s will be active until
1141    /// the [`MatrixMockServer`] is shut down.
1142    ///
1143    /// When using `mount_as_scoped`, your [`MatrixMock`]s will be active as
1144    /// long as the returned [`MockGuard`] is not dropped.
1145    ///
1146    /// When the returned [`MockGuard`] is dropped, [`MatrixMockServer`] will
1147    /// verify that the expectations set on the scoped [`MatrixMock`] were
1148    /// verified - if not, it will panic.
1149    pub async fn mount_as_scoped(self) -> MockGuard {
1150        self.mock.mount_as_scoped(self.server).await
1151    }
1152}
1153
1154/// Generic mocked endpoint, with useful common helpers.
1155pub struct MockEndpoint<'a, T> {
1156    server: &'a MockServer,
1157    mock: MockBuilder,
1158    endpoint: T,
1159    expected_access_token: ExpectedAccessToken,
1160}
1161
1162impl<'a, T> MockEndpoint<'a, T> {
1163    fn new(server: &'a MockServer, mock: MockBuilder, endpoint: T) -> Self {
1164        Self { server, mock, endpoint, expected_access_token: ExpectedAccessToken::None }
1165    }
1166
1167    /// Expect authentication with the default access token on this endpoint.
1168    pub fn expect_default_access_token(mut self) -> Self {
1169        self.expected_access_token = ExpectedAccessToken::Default;
1170        self
1171    }
1172
1173    /// Expect authentication with the given access token on this endpoint.
1174    pub fn expect_access_token(mut self, access_token: &'static str) -> Self {
1175        self.expected_access_token = ExpectedAccessToken::Custom(access_token);
1176        self
1177    }
1178
1179    /// Specify how to respond to a query (viz., like
1180    /// [`MockBuilder::respond_with`] does), when other predefined responses
1181    /// aren't sufficient.
1182    ///
1183    /// # Examples
1184    ///
1185    /// ```
1186    /// # tokio_test::block_on(async {
1187    /// use matrix_sdk::{ruma::{room_id, event_id}, test_utils::mocks::MatrixMockServer};
1188    /// use serde_json::json;
1189    /// use wiremock::ResponseTemplate;
1190    ///
1191    /// let mock_server = MatrixMockServer::new().await;
1192    /// let client = mock_server.client_builder().build().await;
1193    ///
1194    /// mock_server.mock_room_state_encryption().plain().mount().await;
1195    ///
1196    /// let room = mock_server
1197    ///     .sync_joined_room(&client, room_id!("!room_id:localhost"))
1198    ///     .await;
1199    ///
1200    /// let event_id = event_id!("$some_id");
1201    /// mock_server
1202    ///     .mock_room_send()
1203    ///     .respond_with(
1204    ///         ResponseTemplate::new(429)
1205    ///             .insert_header("Retry-After", "100")
1206    ///             .set_body_json(json!({
1207    ///                 "errcode": "M_LIMIT_EXCEEDED",
1208    ///                 "custom_field": "with custom data",
1209    ///     })))
1210    ///     .expect(1)
1211    ///     .mount()
1212    ///     .await;
1213    ///
1214    /// room
1215    ///     .send_raw("m.room.message", json!({ "body": "Hello world" }))
1216    ///     .await
1217    ///     .expect_err("The sending of the event should fail");
1218    /// # anyhow::Ok(()) });
1219    /// ```
1220    pub fn respond_with<R: Respond + 'static>(self, func: R) -> MatrixMock<'a> {
1221        let mock = self
1222            .expected_access_token
1223            .maybe_match_authorization_header(self.mock)
1224            .respond_with(func);
1225        MatrixMock { mock, server: self.server }
1226    }
1227
1228    /// Returns a send endpoint that emulates a transient failure, i.e responds
1229    /// with error 500.
1230    ///
1231    /// # Examples
1232    /// ```
1233    /// # tokio_test::block_on(async {
1234    /// use matrix_sdk::{ruma::{room_id, event_id}, test_utils::mocks::MatrixMockServer};
1235    /// use serde_json::json;
1236    ///
1237    /// let mock_server = MatrixMockServer::new().await;
1238    /// let client = mock_server.client_builder().build().await;
1239    ///
1240    /// mock_server.mock_room_state_encryption().plain().mount().await;
1241    ///
1242    /// let room = mock_server
1243    ///     .sync_joined_room(&client, room_id!("!room_id:localhost"))
1244    ///     .await;
1245    ///
1246    /// mock_server
1247    ///     .mock_room_send()
1248    ///     .error500()
1249    ///     .expect(1)
1250    ///     .mount()
1251    ///     .await;
1252    ///
1253    /// room
1254    ///     .send_raw("m.room.message", json!({ "body": "Hello world" }))
1255    ///     .await.expect_err("The sending of the event should have failed");
1256    /// # anyhow::Ok(()) });
1257    /// ```
1258    pub fn error500(self) -> MatrixMock<'a> {
1259        self.respond_with(ResponseTemplate::new(500))
1260    }
1261
1262    /// Internal helper to return an `{ event_id }` JSON struct along with a 200
1263    /// ok response.
1264    fn ok_with_event_id(self, event_id: OwnedEventId) -> MatrixMock<'a> {
1265        self.respond_with(ResponseTemplate::new(200).set_body_json(json!({ "event_id": event_id })))
1266    }
1267
1268    /// Returns an endpoint that emulates a permanent failure error (e.g. event
1269    /// is too large).
1270    ///
1271    /// # Examples
1272    /// ```
1273    /// # tokio_test::block_on(async {
1274    /// use matrix_sdk::{ruma::{room_id, event_id}, test_utils::mocks::MatrixMockServer};
1275    /// use serde_json::json;
1276    ///
1277    /// let mock_server = MatrixMockServer::new().await;
1278    /// let client = mock_server.client_builder().build().await;
1279    ///
1280    /// mock_server.mock_room_state_encryption().plain().mount().await;
1281    ///
1282    /// let room = mock_server
1283    ///     .sync_joined_room(&client, room_id!("!room_id:localhost"))
1284    ///     .await;
1285    ///
1286    /// mock_server
1287    ///     .mock_room_send()
1288    ///     .error_too_large()
1289    ///     .expect(1)
1290    ///     .mount()
1291    ///     .await;
1292    ///
1293    /// room
1294    ///     .send_raw("m.room.message", json!({ "body": "Hello world" }))
1295    ///     .await.expect_err("The sending of the event should have failed");
1296    /// # anyhow::Ok(()) });
1297    /// ```
1298    pub fn error_too_large(self) -> MatrixMock<'a> {
1299        self.respond_with(ResponseTemplate::new(413).set_body_json(json!({
1300            // From https://spec.matrix.org/v1.10/client-server-api/#standard-error-response
1301            "errcode": "M_TOO_LARGE",
1302        })))
1303    }
1304}
1305
1306/// The access token to expect on an endpoint.
1307enum ExpectedAccessToken {
1308    /// We don't expect an access token.
1309    None,
1310
1311    /// We expect the default access token.
1312    Default,
1313
1314    /// We expect the given access token.
1315    Custom(&'static str),
1316}
1317
1318impl ExpectedAccessToken {
1319    /// Match an `Authorization` header on the given mock if one is expected.
1320    fn maybe_match_authorization_header(&self, mock: MockBuilder) -> MockBuilder {
1321        let token = match self {
1322            Self::None => return mock,
1323            Self::Default => "1234",
1324            Self::Custom(token) => token,
1325        };
1326        mock.and(header(http::header::AUTHORIZATION, format!("Bearer {token}")))
1327    }
1328}
1329
1330/// A prebuilt mock for sending a message like event in a room.
1331pub struct RoomSendEndpoint;
1332
1333impl<'a> MockEndpoint<'a, RoomSendEndpoint> {
1334    /// Ensures that the body of the request is a superset of the provided
1335    /// `body` parameter.
1336    ///
1337    /// # Examples
1338    /// ```
1339    /// # tokio_test::block_on(async {
1340    /// use matrix_sdk::{
1341    ///     ruma::{room_id, event_id, events::room::message::RoomMessageEventContent},
1342    ///     test_utils::mocks::MatrixMockServer
1343    /// };
1344    /// use serde_json::json;
1345    ///
1346    /// let mock_server = MatrixMockServer::new().await;
1347    /// let client = mock_server.client_builder().build().await;
1348    ///
1349    /// mock_server.mock_room_state_encryption().plain().mount().await;
1350    ///
1351    /// let room = mock_server
1352    ///     .sync_joined_room(&client, room_id!("!room_id:localhost"))
1353    ///     .await;
1354    ///
1355    /// let event_id = event_id!("$some_id");
1356    /// mock_server
1357    ///     .mock_room_send()
1358    ///     .body_matches_partial_json(json!({
1359    ///         "body": "Hello world",
1360    ///     }))
1361    ///     .ok(event_id)
1362    ///     .expect(1)
1363    ///     .mount()
1364    ///     .await;
1365    ///
1366    /// let content = RoomMessageEventContent::text_plain("Hello world");
1367    /// let response = room.send(content).await?;
1368    ///
1369    /// assert_eq!(
1370    ///     event_id,
1371    ///     response.event_id,
1372    ///     "The event ID we mocked should match the one we received when we sent the event"
1373    /// );
1374    /// # anyhow::Ok(()) });
1375    /// ```
1376    pub fn body_matches_partial_json(self, body: Value) -> Self {
1377        Self { mock: self.mock.and(body_partial_json(body)), ..self }
1378    }
1379
1380    /// Ensures that the send endpoint request uses a specific event type.
1381    ///
1382    /// # Examples
1383    ///
1384    /// see also [`MatrixMockServer::mock_room_send`] for more context.
1385    ///
1386    /// ```
1387    /// # tokio_test::block_on(async {
1388    /// use matrix_sdk::{ruma::{room_id, event_id}, test_utils::mocks::MatrixMockServer};
1389    /// use serde_json::json;
1390    ///
1391    /// let mock_server = MatrixMockServer::new().await;
1392    /// let client = mock_server.client_builder().build().await;
1393    ///
1394    /// mock_server.mock_room_state_encryption().plain().mount().await;
1395    ///
1396    /// let room = mock_server
1397    ///     .sync_joined_room(&client, room_id!("!room_id:localhost"))
1398    ///     .await;
1399    ///
1400    /// let event_id = event_id!("$some_id");
1401    /// mock_server
1402    ///     .mock_room_send()
1403    ///     .for_type("m.room.message".into())
1404    ///     .ok(event_id)
1405    ///     .expect(1)
1406    ///     .mount()
1407    ///     .await;
1408    ///
1409    /// let response_not_mocked = room.send_raw("m.room.reaction", json!({ "body": "Hello world" })).await;
1410    /// // The `m.room.reaction` event type should not be mocked by the server.
1411    /// assert!(response_not_mocked.is_err());
1412    ///
1413    /// let response = room.send_raw("m.room.message", json!({ "body": "Hello world" })).await?;
1414    /// // The `m.room.message` event type should be mocked by the server.
1415    /// assert_eq!(
1416    ///     event_id,
1417    ///     response.event_id,
1418    ///     "The event ID we mocked should match the one we received when we sent the event"
1419    /// );
1420    /// # anyhow::Ok(()) });
1421    /// ```
1422    pub fn for_type(self, event_type: MessageLikeEventType) -> Self {
1423        Self {
1424            // Note: we already defined a path when constructing the mock builder, but this one
1425            // ought to be more specialized.
1426            mock: self
1427                .mock
1428                .and(path_regex(format!(r"^/_matrix/client/v3/rooms/.*/send/{event_type}",))),
1429            ..self
1430        }
1431    }
1432
1433    /// Ensures the event was sent as a delayed event.
1434    ///
1435    /// See also [the MSC](https://github.com/matrix-org/matrix-spec-proposals/pull/4140).
1436    ///
1437    /// Note: works with *any* room.
1438    ///
1439    /// # Examples
1440    ///
1441    /// see also [`MatrixMockServer::mock_room_send`] for more context.
1442    ///
1443    /// ```
1444    /// # tokio_test::block_on(async {
1445    /// use matrix_sdk::{
1446    ///     ruma::{
1447    ///         api::client::delayed_events::{delayed_message_event, DelayParameters},
1448    ///         events::{message::MessageEventContent, AnyMessageLikeEventContent},
1449    ///         room_id,
1450    ///         time::Duration,
1451    ///         TransactionId,
1452    ///     },
1453    ///     test_utils::mocks::MatrixMockServer,
1454    /// };
1455    /// use serde_json::json;
1456    /// use wiremock::ResponseTemplate;
1457    ///
1458    /// let mock_server = MatrixMockServer::new().await;
1459    /// let client = mock_server.client_builder().build().await;
1460    ///
1461    /// mock_server.mock_room_state_encryption().plain().mount().await;
1462    ///
1463    /// let room = mock_server.sync_joined_room(&client, room_id!("!room_id:localhost")).await;
1464    ///
1465    /// mock_server
1466    ///     .mock_room_send()
1467    ///     .match_delayed_event(Duration::from_millis(500))
1468    ///     .respond_with(ResponseTemplate::new(200).set_body_json(json!({"delay_id":"$some_id"})))
1469    ///     .mock_once()
1470    ///     .mount()
1471    ///     .await;
1472    ///
1473    /// let response_not_mocked =
1474    ///     room.send_raw("m.room.message", json!({ "body": "Hello world" })).await;
1475    ///
1476    /// // A non delayed event should not be mocked by the server.
1477    /// assert!(response_not_mocked.is_err());
1478    ///
1479    /// let r = delayed_message_event::unstable::Request::new(
1480    ///     room.room_id().to_owned(),
1481    ///     TransactionId::new(),
1482    ///     DelayParameters::Timeout { timeout: Duration::from_millis(500) },
1483    ///     &AnyMessageLikeEventContent::Message(MessageEventContent::plain("hello world")),
1484    /// )
1485    /// .unwrap();
1486    ///
1487    /// let response = room.client().send(r).await.unwrap();
1488    /// // The delayed `m.room.message` event type should be mocked by the server.
1489    /// assert_eq!("$some_id", response.delay_id);
1490    /// # anyhow::Ok(()) });
1491    /// ```
1492    pub fn match_delayed_event(self, delay: Duration) -> Self {
1493        Self {
1494            mock: self
1495                .mock
1496                .and(query_param("org.matrix.msc4140.delay", delay.as_millis().to_string())),
1497            ..self
1498        }
1499    }
1500
1501    /// Returns a send endpoint that emulates success, i.e. the event has been
1502    /// sent with the given event id.
1503    ///
1504    /// # Examples
1505    /// ```
1506    /// # tokio_test::block_on(async {
1507    /// use matrix_sdk::{ruma::{room_id, event_id}, test_utils::mocks::MatrixMockServer};
1508    /// use serde_json::json;
1509    ///
1510    /// let mock_server = MatrixMockServer::new().await;
1511    /// let client = mock_server.client_builder().build().await;
1512    ///
1513    /// mock_server.mock_room_state_encryption().plain().mount().await;
1514    ///
1515    /// let room = mock_server
1516    ///     .sync_joined_room(&client, room_id!("!room_id:localhost"))
1517    ///     .await;
1518    ///
1519    /// let event_id = event_id!("$some_id");
1520    /// let send_guard = mock_server
1521    ///     .mock_room_send()
1522    ///     .ok(event_id)
1523    ///     .expect(1)
1524    ///     .mount_as_scoped()
1525    ///     .await;
1526    ///
1527    /// let response = room.send_raw("m.room.message", json!({ "body": "Hello world" })).await?;
1528    ///
1529    /// assert_eq!(
1530    ///     event_id,
1531    ///     response.event_id,
1532    ///     "The event ID we mocked should match the one we received when we sent the event"
1533    /// );
1534    /// # anyhow::Ok(()) });
1535    /// ```
1536    pub fn ok(self, returned_event_id: impl Into<OwnedEventId>) -> MatrixMock<'a> {
1537        self.ok_with_event_id(returned_event_id.into())
1538    }
1539}
1540
1541/// A prebuilt mock for sending a state event in a room.
1542#[derive(Default)]
1543pub struct RoomSendStateEndpoint {
1544    state_key: Option<String>,
1545    event_type: Option<StateEventType>,
1546}
1547
1548impl<'a> MockEndpoint<'a, RoomSendStateEndpoint> {
1549    fn generate_path_regexp(endpoint: &RoomSendStateEndpoint) -> String {
1550        format!(
1551            r"^/_matrix/client/v3/rooms/.*/state/{}/{}",
1552            endpoint.event_type.as_ref().map_or_else(|| ".*".to_owned(), |t| t.to_string()),
1553            endpoint.state_key.as_ref().map_or_else(|| ".*".to_owned(), |k| k.to_string())
1554        )
1555    }
1556
1557    /// Ensures that the body of the request is a superset of the provided
1558    /// `body` parameter.
1559    ///
1560    /// # Examples
1561    /// ```
1562    /// # tokio_test::block_on(async {
1563    /// use matrix_sdk::{
1564    ///     ruma::{room_id, event_id, events::room::power_levels::RoomPowerLevelsEventContent},
1565    ///     test_utils::mocks::MatrixMockServer
1566    /// };
1567    /// use serde_json::json;
1568    ///
1569    /// let mock_server = MatrixMockServer::new().await;
1570    /// let client = mock_server.client_builder().build().await;
1571    ///
1572    /// mock_server.mock_room_state_encryption().plain().mount().await;
1573    ///
1574    /// let room = mock_server
1575    ///     .sync_joined_room(&client, room_id!("!room_id:localhost"))
1576    ///     .await;
1577    ///
1578    /// let event_id = event_id!("$some_id");
1579    /// mock_server
1580    ///     .mock_room_send_state()
1581    ///     .body_matches_partial_json(json!({
1582    ///         "redact": 51,
1583    ///     }))
1584    ///     .ok(event_id)
1585    ///     .expect(1)
1586    ///     .mount()
1587    ///     .await;
1588    ///
1589    /// let mut content = RoomPowerLevelsEventContent::new();
1590    /// // Update the power level to a non default value.
1591    /// // Otherwise it will be skipped from serialization.
1592    /// content.redact = 51.into();
1593    ///
1594    /// let response = room.send_state_event(content).await?;
1595    ///
1596    /// assert_eq!(
1597    ///     event_id,
1598    ///     response.event_id,
1599    ///     "The event ID we mocked should match the one we received when we sent the event"
1600    /// );
1601    /// # anyhow::Ok(()) });
1602    /// ```
1603    pub fn body_matches_partial_json(self, body: Value) -> Self {
1604        Self { mock: self.mock.and(body_partial_json(body)), ..self }
1605    }
1606
1607    /// Ensures that the send endpoint request uses a specific event type.
1608    ///
1609    /// Note: works with *any* room.
1610    ///
1611    /// # Examples
1612    ///
1613    /// see also [`MatrixMockServer::mock_room_send`] for more context.
1614    ///
1615    /// ```
1616    /// # tokio_test::block_on(async {
1617    /// use matrix_sdk::{
1618    ///     ruma::{
1619    ///         event_id,
1620    ///         events::room::{
1621    ///             create::RoomCreateEventContent, power_levels::RoomPowerLevelsEventContent,
1622    ///         },
1623    ///         events::StateEventType,
1624    ///         room_id,
1625    ///     },
1626    ///     test_utils::mocks::MatrixMockServer,
1627    /// };
1628    ///
1629    /// let mock_server = MatrixMockServer::new().await;
1630    /// let client = mock_server.client_builder().build().await;
1631    ///
1632    /// mock_server.mock_room_state_encryption().plain().mount().await;
1633    ///
1634    /// let room = mock_server.sync_joined_room(&client, room_id!("!room_id:localhost")).await;
1635    ///
1636    /// let event_id = event_id!("$some_id");
1637    ///
1638    /// mock_server
1639    ///     .mock_room_send_state()
1640    ///     .for_type(StateEventType::RoomPowerLevels)
1641    ///     .ok(event_id)
1642    ///     .expect(1)
1643    ///     .mount()
1644    ///     .await;
1645    ///
1646    /// let response_not_mocked = room.send_state_event(RoomCreateEventContent::new_v11()).await;
1647    /// // The `m.room.reaction` event type should not be mocked by the server.
1648    /// assert!(response_not_mocked.is_err());
1649    ///
1650    /// let response = room.send_state_event(RoomPowerLevelsEventContent::new()).await?;
1651    /// // The `m.room.message` event type should be mocked by the server.
1652    /// assert_eq!(
1653    ///     event_id, response.event_id,
1654    ///     "The event ID we mocked should match the one we received when we sent the event"
1655    /// );
1656    ///
1657    /// # anyhow::Ok(()) });
1658    /// ```
1659    pub fn for_type(mut self, event_type: StateEventType) -> Self {
1660        self.endpoint.event_type = Some(event_type);
1661        // Note: we may have already defined a path, but this one ought to be more
1662        // specialized (unless for_key/for_type were called multiple times).
1663        Self { mock: self.mock.and(path_regex(Self::generate_path_regexp(&self.endpoint))), ..self }
1664    }
1665
1666    /// Ensures the event was sent as a delayed event.
1667    ///
1668    /// See also [the MSC](https://github.com/matrix-org/matrix-spec-proposals/pull/4140).
1669    ///
1670    /// Note: works with *any* room.
1671    ///
1672    /// # Examples
1673    ///
1674    /// see also [`MatrixMockServer::mock_room_send`] for more context.
1675    ///
1676    /// ```
1677    /// # tokio_test::block_on(async {
1678    /// use matrix_sdk::{
1679    ///     ruma::{
1680    ///         api::client::delayed_events::{delayed_state_event, DelayParameters},
1681    ///         events::{room::create::RoomCreateEventContent, AnyStateEventContent},
1682    ///         room_id,
1683    ///         time::Duration,
1684    ///     },
1685    ///     test_utils::mocks::MatrixMockServer,
1686    /// };
1687    /// use wiremock::ResponseTemplate;
1688    /// use serde_json::json;
1689    ///
1690    /// let mock_server = MatrixMockServer::new().await;
1691    /// let client = mock_server.client_builder().build().await;
1692    ///
1693    /// mock_server.mock_room_state_encryption().plain().mount().await;
1694    ///
1695    /// let room = mock_server.sync_joined_room(&client, room_id!("!room_id:localhost")).await;
1696    ///
1697    /// mock_server
1698    ///     .mock_room_send_state()
1699    ///     .match_delayed_event(Duration::from_millis(500))
1700    ///     .respond_with(ResponseTemplate::new(200).set_body_json(json!({"delay_id":"$some_id"})))
1701    ///     .mock_once()
1702    ///     .mount()
1703    ///     .await;
1704    ///
1705    /// let response_not_mocked = room.send_state_event(RoomCreateEventContent::new_v11()).await;
1706    /// // A non delayed event should not be mocked by the server.
1707    /// assert!(response_not_mocked.is_err());
1708    ///
1709    /// let r = delayed_state_event::unstable::Request::new(
1710    ///     room.room_id().to_owned(),
1711    ///     "".to_owned(),
1712    ///     DelayParameters::Timeout { timeout: Duration::from_millis(500) },
1713    ///     &AnyStateEventContent::RoomCreate(RoomCreateEventContent::new_v11()),
1714    /// )
1715    /// .unwrap();
1716    /// let response = room.client().send(r).await.unwrap();
1717    /// // The delayed `m.room.message` event type should be mocked by the server.
1718    /// assert_eq!("$some_id", response.delay_id);
1719    ///
1720    /// # anyhow::Ok(()) });
1721    /// ```
1722    pub fn match_delayed_event(self, delay: Duration) -> Self {
1723        Self {
1724            mock: self
1725                .mock
1726                .and(query_param("org.matrix.msc4140.delay", delay.as_millis().to_string())),
1727            ..self
1728        }
1729    }
1730
1731    ///
1732    /// ```
1733    /// # tokio_test::block_on(async {
1734    /// use matrix_sdk::{
1735    ///     ruma::{
1736    ///         event_id,
1737    ///         events::{call::member::CallMemberEventContent, AnyStateEventContent},
1738    ///         room_id,
1739    ///     },
1740    ///     test_utils::mocks::MatrixMockServer,
1741    /// };
1742    ///
1743    /// let mock_server = MatrixMockServer::new().await;
1744    /// let client = mock_server.client_builder().build().await;
1745    ///
1746    /// mock_server.mock_room_state_encryption().plain().mount().await;
1747    ///
1748    /// let room = mock_server.sync_joined_room(&client, room_id!("!room_id:localhost")).await;
1749    ///
1750    /// let event_id = event_id!("$some_id");
1751    ///
1752    /// mock_server
1753    ///     .mock_room_send_state()
1754    ///     .for_key("my_key".to_owned())
1755    ///     .ok(event_id)
1756    ///     .expect(1)
1757    ///     .mount()
1758    ///     .await;
1759    ///
1760    /// let response_not_mocked = room
1761    ///     .send_state_event_for_key(
1762    ///         "",
1763    ///         AnyStateEventContent::CallMember(CallMemberEventContent::new_empty(None)),
1764    ///     )
1765    ///     .await;
1766    /// // The `m.room.reaction` event type should not be mocked by the server.
1767    /// assert!(response_not_mocked.is_err());
1768    ///
1769    /// let response = room
1770    ///     .send_state_event_for_key(
1771    ///         "my_key",
1772    ///         AnyStateEventContent::CallMember(CallMemberEventContent::new_empty(None)),
1773    ///     )
1774    ///     .await
1775    ///     .unwrap();
1776    ///
1777    /// // The `m.room.message` event type should be mocked by the server.
1778    /// assert_eq!(
1779    ///     event_id, response.event_id,
1780    ///     "The event ID we mocked should match the one we received when we sent the event"
1781    /// );
1782    /// # anyhow::Ok(()) });
1783    /// ```
1784    pub fn for_key(mut self, state_key: String) -> Self {
1785        self.endpoint.state_key = Some(state_key);
1786        // Note: we may have already defined a path, but this one ought to be more
1787        // specialized (unless for_key/for_type were called multiple times).
1788        Self { mock: self.mock.and(path_regex(Self::generate_path_regexp(&self.endpoint))), ..self }
1789    }
1790
1791    /// Returns a send endpoint that emulates success, i.e. the event has been
1792    /// sent with the given event id.
1793    ///
1794    /// # Examples
1795    /// ```
1796    /// # tokio_test::block_on(async {
1797    /// use matrix_sdk::{ruma::{room_id, event_id}, test_utils::mocks::MatrixMockServer};
1798    /// use serde_json::json;
1799    ///
1800    /// let mock_server = MatrixMockServer::new().await;
1801    /// let client = mock_server.client_builder().build().await;
1802    ///
1803    /// mock_server.mock_room_state_encryption().plain().mount().await;
1804    ///
1805    /// let room = mock_server
1806    ///     .sync_joined_room(&client, room_id!("!room_id:localhost"))
1807    ///     .await;
1808    ///
1809    /// let event_id = event_id!("$some_id");
1810    /// let send_guard = mock_server
1811    ///     .mock_room_send_state()
1812    ///     .ok(event_id)
1813    ///     .expect(1)
1814    ///     .mount_as_scoped()
1815    ///     .await;
1816    ///
1817    /// let response = room.send_state_event_raw("m.room.message", "my_key", json!({ "body": "Hello world" })).await?;
1818    ///
1819    /// assert_eq!(
1820    ///     event_id,
1821    ///     response.event_id,
1822    ///     "The event ID we mocked should match the one we received when we sent the event"
1823    /// );
1824    /// # anyhow::Ok(()) });
1825    /// ```
1826    pub fn ok(self, returned_event_id: impl Into<OwnedEventId>) -> MatrixMock<'a> {
1827        self.ok_with_event_id(returned_event_id.into())
1828    }
1829}
1830
1831/// A prebuilt mock for running sync v2.
1832pub struct SyncEndpoint {
1833    sync_response_builder: Arc<Mutex<SyncResponseBuilder>>,
1834}
1835
1836impl MockEndpoint<'_, SyncEndpoint> {
1837    /// Temporarily mocks the sync with the given endpoint and runs a client
1838    /// sync with it.
1839    ///
1840    /// After calling this function, the sync endpoint isn't mocked anymore.
1841    ///
1842    /// # Examples
1843    ///
1844    /// ```
1845    /// # tokio_test::block_on(async {
1846    /// use matrix_sdk::{ruma::room_id, test_utils::mocks::MatrixMockServer};
1847    /// use matrix_sdk_test::JoinedRoomBuilder;
1848    ///
1849    /// // First create the mock server and client pair.
1850    /// let mock_server = MatrixMockServer::new().await;
1851    /// let client = mock_server.client_builder().build().await;
1852    /// let room_id = room_id!("!room_id:localhost");
1853    ///
1854    /// // Let's emulate what `MatrixMockServer::sync_joined_room()` does.
1855    /// mock_server
1856    ///     .mock_sync()
1857    ///     .ok_and_run(&client, |builder| {
1858    ///         builder.add_joined_room(JoinedRoomBuilder::new(room_id));
1859    ///     })
1860    ///     .await;
1861    ///
1862    /// let room = client
1863    ///     .get_room(room_id)
1864    ///     .expect("The room should be available after we mocked the sync");
1865    /// # anyhow::Ok(()) });
1866    /// ```
1867    pub async fn ok_and_run<F: FnOnce(&mut SyncResponseBuilder)>(self, client: &Client, func: F) {
1868        let json_response = {
1869            let mut builder = self.endpoint.sync_response_builder.lock().unwrap();
1870            func(&mut builder);
1871            builder.build_json_sync_response()
1872        };
1873
1874        let _scope = self
1875            .mock
1876            .respond_with(ResponseTemplate::new(200).set_body_json(json_response))
1877            .mount_as_scoped(self.server)
1878            .await;
1879
1880        let _response = client.sync_once(Default::default()).await.unwrap();
1881    }
1882}
1883
1884/// A prebuilt mock for reading the encryption state of a room.
1885pub struct EncryptionStateEndpoint;
1886
1887impl<'a> MockEndpoint<'a, EncryptionStateEndpoint> {
1888    /// Marks the room as encrypted.
1889    ///
1890    /// # Examples
1891    ///
1892    /// ```
1893    /// # tokio_test::block_on(async {
1894    /// use matrix_sdk::{ruma::room_id, test_utils::mocks::MatrixMockServer};
1895    ///
1896    /// let mock_server = MatrixMockServer::new().await;
1897    /// let client = mock_server.client_builder().build().await;
1898    ///
1899    /// mock_server.mock_room_state_encryption().encrypted().mount().await;
1900    ///
1901    /// let room = mock_server
1902    ///     .sync_joined_room(&client, room_id!("!room_id:localhost"))
1903    ///     .await;
1904    ///
1905    /// assert!(
1906    ///     room.latest_encryption_state().await?.is_encrypted(),
1907    ///     "The room should be marked as encrypted."
1908    /// );
1909    /// # anyhow::Ok(()) });
1910    /// ```
1911    pub fn encrypted(self) -> MatrixMock<'a> {
1912        self.respond_with(
1913            ResponseTemplate::new(200).set_body_json(&*test_json::sync_events::ENCRYPTION_CONTENT),
1914        )
1915    }
1916
1917    /// Marks the room as not encrypted.
1918    ///
1919    /// # Examples
1920    ///
1921    /// ```
1922    /// # tokio_test::block_on(async {
1923    /// use matrix_sdk::{ruma::room_id, test_utils::mocks::MatrixMockServer};
1924    ///
1925    /// let mock_server = MatrixMockServer::new().await;
1926    /// let client = mock_server.client_builder().build().await;
1927    ///
1928    /// mock_server.mock_room_state_encryption().plain().mount().await;
1929    ///
1930    /// let room = mock_server
1931    ///     .sync_joined_room(&client, room_id!("!room_id:localhost"))
1932    ///     .await;
1933    ///
1934    /// assert!(
1935    ///     !room.latest_encryption_state().await?.is_encrypted(),
1936    ///     "The room should not be marked as encrypted."
1937    /// );
1938    /// # anyhow::Ok(()) });
1939    /// ```
1940    pub fn plain(self) -> MatrixMock<'a> {
1941        self.respond_with(ResponseTemplate::new(404).set_body_json(&*test_json::NOT_FOUND))
1942    }
1943}
1944
1945/// A prebuilt mock for setting the encryption state of a room.
1946pub struct SetEncryptionStateEndpoint;
1947
1948impl<'a> MockEndpoint<'a, SetEncryptionStateEndpoint> {
1949    /// Returns a mock for a successful setting of the encryption state event.
1950    pub fn ok(self, returned_event_id: impl Into<OwnedEventId>) -> MatrixMock<'a> {
1951        self.ok_with_event_id(returned_event_id.into())
1952    }
1953}
1954
1955/// A prebuilt mock for redacting an event in a room.
1956pub struct RoomRedactEndpoint;
1957
1958impl<'a> MockEndpoint<'a, RoomRedactEndpoint> {
1959    /// Returns a redact endpoint that emulates success, i.e. the redaction
1960    /// event has been sent with the given event id.
1961    pub fn ok(self, returned_event_id: impl Into<OwnedEventId>) -> MatrixMock<'a> {
1962        self.ok_with_event_id(returned_event_id.into())
1963    }
1964}
1965
1966/// A prebuilt mock for getting a single event in a room.
1967pub struct RoomEventEndpoint {
1968    room: Option<OwnedRoomId>,
1969    match_event_id: bool,
1970}
1971
1972impl<'a> MockEndpoint<'a, RoomEventEndpoint> {
1973    /// Limits the scope of this mock to a specific room.
1974    pub fn room(mut self, room: impl Into<OwnedRoomId>) -> Self {
1975        self.endpoint.room = Some(room.into());
1976        self
1977    }
1978
1979    /// Whether the mock checks for the event id from the event.
1980    pub fn match_event_id(mut self) -> Self {
1981        self.endpoint.match_event_id = true;
1982        self
1983    }
1984
1985    /// Returns a redact endpoint that emulates success, i.e. the redaction
1986    /// event has been sent with the given event id.
1987    pub fn ok(self, event: TimelineEvent) -> MatrixMock<'a> {
1988        let event_path = if self.endpoint.match_event_id {
1989            let event_id = event.kind.event_id().expect("an event id is required");
1990            // The event id should begin with `$`, which would be taken as the end of the
1991            // regex so we need to escape it
1992            event_id.as_str().replace("$", "\\$")
1993        } else {
1994            // Event is at the end, so no need to add anything.
1995            "".to_owned()
1996        };
1997
1998        let room_path = self.endpoint.room.map_or_else(|| ".*".to_owned(), |room| room.to_string());
1999
2000        let mock = self
2001            .mock
2002            .and(path_regex(format!(r"^/_matrix/client/v3/rooms/{room_path}/event/{event_path}")))
2003            .respond_with(ResponseTemplate::new(200).set_body_json(event.into_raw().json()));
2004        MatrixMock { server: self.server, mock }
2005    }
2006}
2007
2008/// A prebuilt mock for the `/messages` endpoint.
2009pub struct RoomMessagesEndpoint;
2010
2011/// A prebuilt mock for getting a room messages in a room.
2012impl<'a> MockEndpoint<'a, RoomMessagesEndpoint> {
2013    /// Expects an optional limit to be set on the request.
2014    pub fn match_limit(self, limit: u32) -> Self {
2015        Self { mock: self.mock.and(query_param("limit", limit.to_string())), ..self }
2016    }
2017
2018    /// Expects an optional `from` to be set on the request.
2019    pub fn match_from(self, from: &str) -> Self {
2020        Self { mock: self.mock.and(query_param("from", from)), ..self }
2021    }
2022
2023    /// Returns a messages endpoint that emulates success, i.e. the messages
2024    /// provided as `response` could be retrieved.
2025    ///
2026    /// Note: pass `chunk` in the correct order: topological for forward
2027    /// pagination, reverse topological for backwards pagination.
2028    pub fn ok(self, response: RoomMessagesResponseTemplate) -> MatrixMock<'a> {
2029        let mut template = ResponseTemplate::new(200).set_body_json(json!({
2030            "start": response.start,
2031            "end": response.end,
2032            "chunk": response.chunk,
2033            "state": response.state,
2034        }));
2035
2036        if let Some(delay) = response.delay {
2037            template = template.set_delay(delay);
2038        }
2039
2040        self.respond_with(template)
2041    }
2042}
2043
2044/// A response to a [`RoomMessagesEndpoint`] query.
2045pub struct RoomMessagesResponseTemplate {
2046    /// The start token for this /messages query.
2047    pub start: String,
2048    /// The end token for this /messages query (previous batch for back
2049    /// paginations, next batch for forward paginations).
2050    pub end: Option<String>,
2051    /// The set of timeline events returned by this query.
2052    pub chunk: Vec<Raw<AnyTimelineEvent>>,
2053    /// The set of state events returned by this query.
2054    pub state: Vec<Raw<AnyStateEvent>>,
2055    /// Optional delay to respond to the query.
2056    pub delay: Option<Duration>,
2057}
2058
2059impl RoomMessagesResponseTemplate {
2060    /// Fill the events returned as part of this response.
2061    pub fn events(mut self, chunk: Vec<impl Into<Raw<AnyTimelineEvent>>>) -> Self {
2062        self.chunk = chunk.into_iter().map(Into::into).collect();
2063        self
2064    }
2065
2066    /// Fill the end token.
2067    pub fn end_token(mut self, token: impl Into<String>) -> Self {
2068        self.end = Some(token.into());
2069        self
2070    }
2071
2072    /// Respond with a given delay to the query.
2073    pub fn with_delay(mut self, delay: Duration) -> Self {
2074        self.delay = Some(delay);
2075        self
2076    }
2077}
2078
2079impl Default for RoomMessagesResponseTemplate {
2080    fn default() -> Self {
2081        Self {
2082            start: "start-token-unused".to_owned(),
2083            end: Default::default(),
2084            chunk: Default::default(),
2085            state: Default::default(),
2086            delay: None,
2087        }
2088    }
2089}
2090
2091/// A prebuilt mock for uploading media.
2092pub struct UploadEndpoint;
2093
2094impl<'a> MockEndpoint<'a, UploadEndpoint> {
2095    /// Expect that the content type matches what's given here.
2096    pub fn expect_mime_type(self, content_type: &str) -> Self {
2097        Self { mock: self.mock.and(header("content-type", content_type)), ..self }
2098    }
2099
2100    /// Returns a redact endpoint that emulates success, i.e. the redaction
2101    /// event has been sent with the given event id.
2102    pub fn ok(self, mxc_id: &MxcUri) -> MatrixMock<'a> {
2103        self.respond_with(ResponseTemplate::new(200).set_body_json(json!({
2104            "content_uri": mxc_id
2105        })))
2106    }
2107}
2108
2109/// A prebuilt mock for resolving a room alias.
2110pub struct ResolveRoomAliasEndpoint;
2111
2112impl<'a> MockEndpoint<'a, ResolveRoomAliasEndpoint> {
2113    /// Sets up the endpoint to only intercept requests for the given room
2114    /// alias.
2115    pub fn for_alias(self, alias: impl Into<String>) -> Self {
2116        let alias = alias.into();
2117        Self {
2118            mock: self.mock.and(path_regex(format!(
2119                r"^/_matrix/client/v3/directory/room/{}",
2120                percent_encoded_path(&alias)
2121            ))),
2122            ..self
2123        }
2124    }
2125
2126    /// Returns a data endpoint with a resolved room alias.
2127    pub fn ok(self, room_id: &str, servers: Vec<String>) -> MatrixMock<'a> {
2128        self.respond_with(ResponseTemplate::new(200).set_body_json(json!({
2129            "room_id": room_id,
2130            "servers": servers,
2131        })))
2132    }
2133
2134    /// Returns a data endpoint for a room alias that does not exit.
2135    pub fn not_found(self) -> MatrixMock<'a> {
2136        self.respond_with(ResponseTemplate::new(404).set_body_json(json!({
2137          "errcode": "M_NOT_FOUND",
2138          "error": "Room alias not found."
2139        })))
2140    }
2141}
2142
2143/// A prebuilt mock for creating a room alias.
2144pub struct CreateRoomAliasEndpoint;
2145
2146impl<'a> MockEndpoint<'a, CreateRoomAliasEndpoint> {
2147    /// Returns a data endpoint for creating a room alias.
2148    pub fn ok(self) -> MatrixMock<'a> {
2149        self.respond_with(ResponseTemplate::new(200).set_body_json(json!({})))
2150    }
2151}
2152
2153/// A prebuilt mock for removing a room alias.
2154pub struct RemoveRoomAliasEndpoint;
2155
2156impl<'a> MockEndpoint<'a, RemoveRoomAliasEndpoint> {
2157    /// Returns a data endpoint for removing a room alias.
2158    pub fn ok(self) -> MatrixMock<'a> {
2159        self.respond_with(ResponseTemplate::new(200).set_body_json(json!({})))
2160    }
2161}
2162
2163/// A prebuilt mock for paginating the public room list.
2164pub struct PublicRoomsEndpoint;
2165
2166impl<'a> MockEndpoint<'a, PublicRoomsEndpoint> {
2167    /// Returns a data endpoint for paginating the public room list.
2168    pub fn ok(
2169        self,
2170        chunk: Vec<PublicRoomsChunk>,
2171        next_batch: Option<String>,
2172        prev_batch: Option<String>,
2173        total_room_count_estimate: Option<u64>,
2174    ) -> MatrixMock<'a> {
2175        self.respond_with(ResponseTemplate::new(200).set_body_json(json!({
2176            "chunk": chunk,
2177            "next_batch": next_batch,
2178            "prev_batch": prev_batch,
2179            "total_room_count_estimate": total_room_count_estimate,
2180        })))
2181    }
2182
2183    /// Returns a data endpoint for paginating the public room list with several
2184    /// `via` params.
2185    ///
2186    /// Each `via` param must be in the `server_map` parameter, otherwise it'll
2187    /// fail.
2188    pub fn ok_with_via_params(
2189        self,
2190        server_map: BTreeMap<OwnedServerName, Vec<PublicRoomsChunk>>,
2191    ) -> MatrixMock<'a> {
2192        self.respond_with(move |req: &Request| {
2193            #[derive(Deserialize)]
2194            struct PartialRequest {
2195                server: Option<OwnedServerName>,
2196            }
2197
2198            let (_, server) = req
2199                .url
2200                .query_pairs()
2201                .into_iter()
2202                .find(|(key, _)| key == "server")
2203                .expect("Server param not found in request URL");
2204            let server = ServerName::parse(server).expect("Couldn't parse server name");
2205            let chunk = server_map.get(&server).expect("Chunk for the server param not found");
2206            ResponseTemplate::new(200).set_body_json(json!({
2207                "chunk": chunk,
2208                "total_room_count_estimate": chunk.len(),
2209            }))
2210        })
2211    }
2212}
2213
2214/// A prebuilt mock for getting the room's visibility in the room directory.
2215pub struct GetRoomVisibilityEndpoint;
2216
2217impl<'a> MockEndpoint<'a, GetRoomVisibilityEndpoint> {
2218    /// Returns an endpoint that get the room's public visibility.
2219    pub fn ok(self, visibility: Visibility) -> MatrixMock<'a> {
2220        self.respond_with(ResponseTemplate::new(200).set_body_json(json!({
2221            "visibility": visibility,
2222        })))
2223    }
2224}
2225
2226/// A prebuilt mock for setting the room's visibility in the room directory.
2227pub struct SetRoomVisibilityEndpoint;
2228
2229impl<'a> MockEndpoint<'a, SetRoomVisibilityEndpoint> {
2230    /// Returns an endpoint that updates the room's visibility.
2231    pub fn ok(self) -> MatrixMock<'a> {
2232        self.respond_with(ResponseTemplate::new(200).set_body_json(json!({})))
2233    }
2234}
2235
2236/// A prebuilt mock for `GET room_keys/version`: storage ("backup") of room
2237/// keys.
2238pub struct RoomKeysVersionEndpoint;
2239
2240impl<'a> MockEndpoint<'a, RoomKeysVersionEndpoint> {
2241    /// Returns an endpoint that says there is a single room keys backup
2242    pub fn exists(self) -> MatrixMock<'a> {
2243        self.respond_with(ResponseTemplate::new(200).set_body_json(json!({
2244            "algorithm": "m.megolm_backup.v1.curve25519-aes-sha2",
2245            "auth_data": {
2246                "public_key": "abcdefg",
2247                "signatures": {},
2248            },
2249            "count": 42,
2250            "etag": "anopaquestring",
2251            "version": "1",
2252        })))
2253    }
2254
2255    /// Returns an endpoint that says there is no room keys backup
2256    pub fn none(self) -> MatrixMock<'a> {
2257        self.respond_with(ResponseTemplate::new(404).set_body_json(json!({
2258            "errcode": "M_NOT_FOUND",
2259            "error": "No current backup version"
2260        })))
2261    }
2262
2263    /// Returns an endpoint that 429 errors when we get it
2264    pub fn error429(self) -> MatrixMock<'a> {
2265        self.respond_with(ResponseTemplate::new(429).set_body_json(json!({
2266            "errcode": "M_LIMIT_EXCEEDED",
2267            "error": "Too many requests",
2268            "retry_after_ms": 2000
2269        })))
2270    }
2271
2272    /// Returns an endpoint that 404 errors when we get it
2273    pub fn error404(self) -> MatrixMock<'a> {
2274        self.respond_with(ResponseTemplate::new(404))
2275    }
2276}
2277
2278/// A prebuilt mock for `POST room_keys/version`: adding room key backups.
2279pub struct AddRoomKeysVersionEndpoint;
2280
2281impl<'a> MockEndpoint<'a, AddRoomKeysVersionEndpoint> {
2282    /// Returns an endpoint that may be used to add room key backups
2283    pub fn ok(self) -> MatrixMock<'a> {
2284        self.respond_with(ResponseTemplate::new(200).set_body_json(json!({
2285          "version": "1"
2286        })))
2287        .named("POST for the backup creation")
2288    }
2289}
2290
2291/// A prebuilt mock for `DELETE room_keys/version/xxx`: deleting room key
2292/// backups.
2293pub struct DeleteRoomKeysVersionEndpoint;
2294
2295impl<'a> MockEndpoint<'a, DeleteRoomKeysVersionEndpoint> {
2296    /// Returns an endpoint that allows deleting room key backups
2297    pub fn ok(self) -> MatrixMock<'a> {
2298        self.respond_with(ResponseTemplate::new(200).set_body_json(json!({})))
2299            .named("DELETE for the backup deletion")
2300    }
2301}
2302
2303/// A prebuilt mock for `GET /members` request.
2304pub struct GetRoomMembersEndpoint;
2305
2306impl<'a> MockEndpoint<'a, GetRoomMembersEndpoint> {
2307    /// Returns a successful get members request with a list of members.
2308    pub fn ok(self, members: Vec<Raw<RoomMemberEvent>>) -> MatrixMock<'a> {
2309        self.respond_with(ResponseTemplate::new(200).set_body_json(json!({
2310            "chunk": members,
2311        })))
2312    }
2313}
2314
2315/// A prebuilt mock for `POST /invite` request.
2316pub struct InviteUserByIdEndpoint;
2317
2318impl<'a> MockEndpoint<'a, InviteUserByIdEndpoint> {
2319    /// Returns a successful invite user by id request.
2320    pub fn ok(self) -> MatrixMock<'a> {
2321        self.respond_with(ResponseTemplate::new(200).set_body_json(json!({})))
2322    }
2323}
2324
2325/// A prebuilt mock for `POST /kick` request.
2326pub struct KickUserEndpoint;
2327
2328impl<'a> MockEndpoint<'a, KickUserEndpoint> {
2329    /// Returns a successful kick user request.
2330    pub fn ok(self) -> MatrixMock<'a> {
2331        self.respond_with(ResponseTemplate::new(200).set_body_json(json!({})))
2332    }
2333}
2334
2335/// A prebuilt mock for `POST /ban` request.
2336pub struct BanUserEndpoint;
2337
2338impl<'a> MockEndpoint<'a, BanUserEndpoint> {
2339    /// Returns a successful ban user request.
2340    pub fn ok(self) -> MatrixMock<'a> {
2341        self.respond_with(ResponseTemplate::new(200).set_body_json(json!({})))
2342    }
2343}
2344
2345/// A prebuilt mock for `GET /versions` request.
2346pub struct VersionsEndpoint;
2347
2348impl<'a> MockEndpoint<'a, VersionsEndpoint> {
2349    /// Returns a successful `/_matrix/client/versions` request.
2350    ///
2351    /// The response will return some commonly supported versions.
2352    pub fn ok(self) -> MatrixMock<'a> {
2353        self.respond_with(ResponseTemplate::new(200).set_body_json(json!({
2354            "unstable_features": {
2355            },
2356            "versions": [
2357                "r0.0.1",
2358                "r0.2.0",
2359                "r0.3.0",
2360                "r0.4.0",
2361                "r0.5.0",
2362                "r0.6.0",
2363                "r0.6.1",
2364                "v1.1",
2365                "v1.2",
2366                "v1.3",
2367                "v1.4",
2368                "v1.5",
2369                "v1.6",
2370                "v1.7",
2371                "v1.8",
2372                "v1.9",
2373                "v1.10",
2374                "v1.11"
2375            ]
2376        })))
2377    }
2378}
2379
2380/// A prebuilt mock for the room summary endpoint.
2381pub struct RoomSummaryEndpoint;
2382
2383impl<'a> MockEndpoint<'a, RoomSummaryEndpoint> {
2384    /// Returns a successful response with some default data for the given room
2385    /// id.
2386    pub fn ok(self, room_id: &RoomId) -> MatrixMock<'a> {
2387        self.respond_with(ResponseTemplate::new(200).set_body_json(json!({
2388            "room_id": room_id,
2389            "guest_can_join": true,
2390            "num_joined_members": 1,
2391            "world_readable": true,
2392            "join_rule": "public",
2393        })))
2394    }
2395}
2396
2397/// A prebuilt mock to set a room's pinned events.
2398pub struct SetRoomPinnedEventsEndpoint;
2399
2400impl<'a> MockEndpoint<'a, SetRoomPinnedEventsEndpoint> {
2401    /// Returns a successful response with a given event id.
2402    /// id.
2403    pub fn ok(self, event_id: OwnedEventId) -> MatrixMock<'a> {
2404        self.ok_with_event_id(event_id)
2405    }
2406
2407    /// Returns an error response with a generic error code indicating the
2408    /// client is not authorized to set pinned events.
2409    pub fn unauthorized(self) -> MatrixMock<'a> {
2410        self.respond_with(ResponseTemplate::new(400))
2411    }
2412}
2413
2414/// A prebuilt mock for `GET /account/whoami` request.
2415pub struct WhoAmIEndpoint;
2416
2417impl<'a> MockEndpoint<'a, WhoAmIEndpoint> {
2418    /// Returns a successful response with the default device ID.
2419    pub fn ok(self) -> MatrixMock<'a> {
2420        self.ok_with_device_id(device_id!("D3V1C31D"))
2421    }
2422
2423    /// Returns a successful response with the given device ID.
2424    pub fn ok_with_device_id(self, device_id: &DeviceId) -> MatrixMock<'a> {
2425        self.respond_with(ResponseTemplate::new(200).set_body_json(json!({
2426            "user_id": "@joe:example.org",
2427            "device_id": device_id,
2428        })))
2429    }
2430
2431    /// Returns an error response with an `M_UNKNOWN_TOKEN`.
2432    pub fn err_unknown_token(self) -> MatrixMock<'a> {
2433        self.respond_with(ResponseTemplate::new(401).set_body_json(json!({
2434            "errcode": "M_UNKNOWN_TOKEN",
2435            "error": "Invalid token"
2436        })))
2437    }
2438}
2439
2440/// A prebuilt mock for `POST /keys/upload` request.
2441pub struct UploadKeysEndpoint;
2442
2443impl<'a> MockEndpoint<'a, UploadKeysEndpoint> {
2444    /// Returns a successful response with counts of 10 curve25519 keys and 20
2445    /// signed curve25519 keys.
2446    pub fn ok(self) -> MatrixMock<'a> {
2447        self.respond_with(ResponseTemplate::new(200).set_body_json(json!({
2448            "one_time_key_counts": {
2449                "curve25519": 10,
2450                "signed_curve25519": 20,
2451            },
2452        })))
2453    }
2454}
2455
2456/// A prebuilt mock for `POST /keys/query` request.
2457pub struct QueryKeysEndpoint;
2458
2459impl<'a> MockEndpoint<'a, QueryKeysEndpoint> {
2460    /// Returns a successful empty response.
2461    pub fn ok(self) -> MatrixMock<'a> {
2462        self.respond_with(ResponseTemplate::new(200).set_body_json(json!({})))
2463    }
2464}
2465
2466/// A prebuilt mock for `GET /.well-known/matrix/client` request.
2467pub struct WellKnownEndpoint;
2468
2469impl<'a> MockEndpoint<'a, WellKnownEndpoint> {
2470    /// Returns a successful response.
2471    pub fn ok(self) -> MatrixMock<'a> {
2472        let server_uri = self.server.uri();
2473        self.respond_with(ResponseTemplate::new(200).set_body_json(json!({
2474            "m.homeserver": {
2475                "base_url": server_uri,
2476            },
2477        })))
2478    }
2479}
2480
2481/// A prebuilt mock for `POST /keys/device_signing/upload` request.
2482pub struct UploadCrossSigningKeysEndpoint;
2483
2484impl<'a> MockEndpoint<'a, UploadCrossSigningKeysEndpoint> {
2485    /// Returns a successful empty response.
2486    pub fn ok(self) -> MatrixMock<'a> {
2487        self.respond_with(ResponseTemplate::new(200).set_body_json(json!({})))
2488    }
2489
2490    /// Returns an error response with a UIAA stage that failed to authenticate
2491    /// because of an invalid password.
2492    pub fn uiaa_invalid_password(self) -> MatrixMock<'a> {
2493        self.respond_with(ResponseTemplate::new(401).set_body_json(json!({
2494            "errcode": "M_FORBIDDEN",
2495            "error": "Invalid password",
2496            "flows": [
2497                {
2498                    "stages": [
2499                        "m.login.password"
2500                    ]
2501                }
2502            ],
2503            "params": {},
2504            "session": "oFIJVvtEOCKmRUTYKTYIIPHL"
2505        })))
2506    }
2507
2508    /// Returns an error response with a UIAA stage.
2509    pub fn uiaa(self) -> MatrixMock<'a> {
2510        self.respond_with(ResponseTemplate::new(401).set_body_json(json!({
2511            "flows": [
2512                {
2513                    "stages": [
2514                        "m.login.password"
2515                    ]
2516                }
2517            ],
2518            "params": {},
2519            "session": "oFIJVvtEOCKmRUTYKTYIIPHL"
2520        })))
2521    }
2522
2523    /// Returns an error response with an OAuth 2.0 UIAA stage.
2524    pub fn uiaa_oauth(self) -> MatrixMock<'a> {
2525        let server_uri = self.server.uri();
2526        self.respond_with(ResponseTemplate::new(401).set_body_json(json!({
2527            "session": "dummy",
2528            "flows": [{
2529                "stages": [ "org.matrix.cross_signing_reset" ]
2530            }],
2531            "params": {
2532                "org.matrix.cross_signing_reset": {
2533                    "url": format!("{server_uri}/account/?action=org.matrix.cross_signing_reset"),
2534                }
2535            },
2536            "msg": "To reset your end-to-end encryption cross-signing identity, you first need to approve it and then try again."
2537        })))
2538    }
2539}
2540
2541/// A prebuilt mock for `POST /keys/signatures/upload` request.
2542pub struct UploadCrossSigningSignaturesEndpoint;
2543
2544impl<'a> MockEndpoint<'a, UploadCrossSigningSignaturesEndpoint> {
2545    /// Returns a successful empty response.
2546    pub fn ok(self) -> MatrixMock<'a> {
2547        self.respond_with(ResponseTemplate::new(200).set_body_json(json!({})))
2548    }
2549}
2550
2551/// A prebuilt mock for the room leave endpoint.
2552pub struct RoomLeaveEndpoint;
2553
2554impl<'a> MockEndpoint<'a, RoomLeaveEndpoint> {
2555    /// Returns a successful response with some default data for the given room
2556    /// id.
2557    pub fn ok(self, room_id: &RoomId) -> MatrixMock<'a> {
2558        self.respond_with(ResponseTemplate::new(200).set_body_json(json!({
2559            "room_id": room_id,
2560        })))
2561    }
2562}
2563
2564/// A prebuilt mock for `POST /logout` request.
2565pub struct LogoutEndpoint;
2566
2567impl<'a> MockEndpoint<'a, LogoutEndpoint> {
2568    /// Returns a successful empty response.
2569    pub fn ok(self) -> MatrixMock<'a> {
2570        self.respond_with(ResponseTemplate::new(200).set_body_json(json!({})))
2571    }
2572}