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