matrix_sdk/test_utils/mocks/
mod.rs

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