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::{atomic::AtomicU32, Arc, Mutex},
23};
24
25use js_int::UInt;
26use matrix_sdk_base::deserialized_responses::TimelineEvent;
27use matrix_sdk_test::{
28 test_json, InvitedRoomBuilder, JoinedRoomBuilder, KnockedRoomBuilder, LeftRoomBuilder,
29 SyncResponseBuilder,
30};
31use percent_encoding::{AsciiSet, CONTROLS};
32use ruma::{
33 api::client::{
34 receipt::create_receipt::v3::ReceiptType,
35 room::Visibility,
36 threads::get_thread_subscriptions_changes::unstable::{
37 ThreadSubscription, ThreadUnsubscription,
38 },
39 },
40 device_id,
41 directory::PublicRoomsChunk,
42 encryption::{CrossSigningKey, DeviceKeys, OneTimeKey},
43 events::{
44 receipt::ReceiptThread, room::member::RoomMemberEvent, AnyStateEvent, AnySyncTimelineEvent,
45 AnyTimelineEvent, GlobalAccountDataEventType, MessageLikeEventType,
46 RoomAccountDataEventType, StateEventType,
47 },
48 media::Method,
49 push::RuleKind,
50 serde::Raw,
51 time::Duration,
52 DeviceId, EventId, MilliSecondsSinceUnixEpoch, MxcUri, OwnedDeviceId, OwnedEventId,
53 OwnedOneTimeKeyId, OwnedRoomId, OwnedUserId, RoomId, ServerName, UserId,
54};
55use serde::Deserialize;
56use serde_json::{from_value, json, Value};
57use tokio::sync::oneshot::{self, Receiver};
58use wiremock::{
59 matchers::{
60 body_json, body_partial_json, header, method, path, path_regex, query_param,
61 query_param_is_missing,
62 },
63 Mock, MockBuilder, MockGuard, MockServer, Request, Respond, ResponseTemplate, Times,
64};
65
66#[cfg(feature = "e2e-encryption")]
67pub mod encryption;
68pub mod oauth;
69
70use super::client::MockClientBuilder;
71use crate::{room::IncludeRelations, Client, OwnedServerName, Room};
72
73/// Structure used to store the crypto keys uploaded to the server.
74/// They will be served back to clients when requested.
75#[derive(Debug, Default)]
76struct Keys {
77 device: BTreeMap<OwnedUserId, BTreeMap<String, Raw<DeviceKeys>>>,
78 master: BTreeMap<OwnedUserId, Raw<CrossSigningKey>>,
79 self_signing: BTreeMap<OwnedUserId, Raw<CrossSigningKey>>,
80 user_signing: BTreeMap<OwnedUserId, Raw<CrossSigningKey>>,
81 one_time_keys: BTreeMap<
82 OwnedUserId,
83 BTreeMap<OwnedDeviceId, BTreeMap<OwnedOneTimeKeyId, Raw<OneTimeKey>>>,
84 >,
85}
86
87/// A [`wiremock`] [`MockServer`] along with useful methods to help mocking
88/// Matrix client-server API endpoints easily.
89///
90/// It implements mock endpoints, limiting the shared code as much as possible,
91/// so the mocks are still flexible to use as scoped/unscoped mounts, named, and
92/// so on.
93///
94/// It works like this:
95///
96/// * start by saying which endpoint you'd like to mock, e.g.
97/// [`Self::mock_room_send()`]. This returns a specialized [`MockEndpoint`]
98/// data structure, with its own impl. For this example, it's
99/// `MockEndpoint<RoomSendEndpoint>`.
100/// * configure the response on the endpoint-specific mock data structure. For
101/// instance, if you want the sending to result in a transient failure, call
102/// [`MockEndpoint::error500`]; if you want it to succeed and return the event
103/// `$42`, call [`MockEndpoint::ok()`]. It's still possible to call
104/// [`MockEndpoint::respond_with()`], as we do with wiremock MockBuilder, for
105/// maximum flexibility when the helpers aren't sufficient.
106/// * once the endpoint's response is configured, for any mock builder, you get
107/// a [`MatrixMock`]; this is a plain [`wiremock::Mock`] with the server
108/// curried, so one doesn't have to pass it around when calling
109/// [`MatrixMock::mount()`] or [`MatrixMock::mount_as_scoped()`]. As such, it
110/// mostly defers its implementations to [`wiremock::Mock`] under the hood.
111///
112/// # Examples
113///
114/// ```
115/// # tokio_test::block_on(async {
116/// use matrix_sdk::{ruma::{room_id, event_id}, test_utils::mocks::MatrixMockServer};
117/// use serde_json::json;
118///
119/// // First create the mock server and client pair.
120/// let mock_server = MatrixMockServer::new().await;
121/// let client = mock_server.client_builder().build().await;
122///
123/// // Let's say that our rooms are not encrypted.
124/// mock_server.mock_room_state_encryption().plain().mount().await;
125///
126/// // Let us get a room where we will send an event.
127/// let room = mock_server
128/// .sync_joined_room(&client, room_id!("!room_id:localhost"))
129/// .await;
130///
131/// // Now we mock the endpoint so we can actually send the event.
132/// let event_id = event_id!("$some_id");
133/// let send_guard = mock_server
134/// .mock_room_send()
135/// .ok(event_id)
136/// .expect(1)
137/// .mount_as_scoped()
138/// .await;
139///
140/// // And we send it out.
141/// let response = room.send_raw("m.room.message", json!({ "body": "Hello world" })).await?;
142///
143/// assert_eq!(
144/// event_id,
145/// response.event_id,
146/// "The event ID we mocked should match the one we received when we sent the event"
147/// );
148/// # anyhow::Ok(()) });
149/// ```
150pub struct MatrixMockServer {
151 server: MockServer,
152
153 /// Make the sync response builder stateful, to keep in memory the batch
154 /// token and avoid the client ignoring subsequent responses after the first
155 /// one.
156 sync_response_builder: Arc<Mutex<SyncResponseBuilder>>,
157
158 /// Make this mock server capable of mocking real end to end communications
159 keys: Arc<Mutex<Keys>>,
160
161 /// For crypto API end-points to work we need to be able to recognise
162 /// what client is doing the request by mapping the token to the user_id
163 token_to_user_id_map: Arc<Mutex<BTreeMap<String, OwnedUserId>>>,
164 token_counter: AtomicU32,
165}
166
167impl MatrixMockServer {
168 /// Create a new [`wiremock`] server specialized for Matrix usage.
169 pub async fn new() -> Self {
170 let server = MockServer::start().await;
171 let keys: Arc<Mutex<Keys>> = Default::default();
172 Self {
173 server,
174 sync_response_builder: Default::default(),
175 keys,
176 token_to_user_id_map: Default::default(),
177 token_counter: AtomicU32::new(0),
178 }
179 }
180
181 /// Creates a new [`MatrixMockServer`] from a [`wiremock`] server.
182 pub fn from_server(server: MockServer) -> Self {
183 let keys: Arc<Mutex<Keys>> = Default::default();
184 Self {
185 server,
186 sync_response_builder: Default::default(),
187 keys,
188 token_to_user_id_map: Default::default(),
189 token_counter: AtomicU32::new(0),
190 }
191 }
192
193 /// Creates a new [`MockClientBuilder`] configured to use this server,
194 /// preconfigured with a session expected by the server endpoints.
195 pub fn client_builder(&self) -> MockClientBuilder {
196 MockClientBuilder::new(Some(&self.server.uri()))
197 }
198
199 /// Return the underlying [`wiremock`] server.
200 pub fn server(&self) -> &MockServer {
201 &self.server
202 }
203
204 /// Return the URI of this server.
205 pub fn uri(&self) -> String {
206 self.server.uri()
207 }
208
209 /// Get an `OAuthMockServer` that uses the same mock server as this one.
210 pub fn oauth(&self) -> oauth::OAuthMockServer<'_> {
211 oauth::OAuthMockServer::new(self)
212 }
213
214 /// Mock the given endpoint.
215 fn mock_endpoint<T>(&self, mock: MockBuilder, endpoint: T) -> MockEndpoint<'_, T> {
216 MockEndpoint::new(&self.server, mock, endpoint)
217 }
218
219 /// Overrides the sync/ endpoint with knowledge that the given
220 /// invited/joined/knocked/left room exists, runs a sync and returns the
221 /// given room.
222 ///
223 /// # Examples
224 ///
225 /// ```
226 /// # tokio_test::block_on(async {
227 /// use matrix_sdk::{ruma::{room_id, event_id}, test_utils::mocks::MatrixMockServer};
228 /// use matrix_sdk_test::LeftRoomBuilder;
229 ///
230 /// let mock_server = MatrixMockServer::new().await;
231 /// let client = mock_server.client_builder().build().await;
232 ///
233 /// let left_room = mock_server
234 /// .sync_room(&client, LeftRoomBuilder::new(room_id!("!room_id:localhost")))
235 /// .await;
236 /// # anyhow::Ok(()) });
237 pub async fn sync_room(&self, client: &Client, room_data: impl Into<AnyRoomBuilder>) -> Room {
238 let any_room = room_data.into();
239 let room_id = any_room.room_id().to_owned();
240
241 self.mock_sync()
242 .ok_and_run(client, move |builder| match any_room {
243 AnyRoomBuilder::Invited(invited) => {
244 builder.add_invited_room(invited);
245 }
246 AnyRoomBuilder::Joined(joined) => {
247 builder.add_joined_room(joined);
248 }
249 AnyRoomBuilder::Left(left) => {
250 builder.add_left_room(left);
251 }
252 AnyRoomBuilder::Knocked(knocked) => {
253 builder.add_knocked_room(knocked);
254 }
255 })
256 .await;
257
258 client.get_room(&room_id).expect("look at me, the room is known now")
259 }
260
261 /// Overrides the sync/ endpoint with knowledge that the given room exists
262 /// in the joined state, runs a sync and returns the given room.
263 ///
264 /// # Examples
265 ///
266 /// ```
267 /// # tokio_test::block_on(async {
268 /// use matrix_sdk::{ruma::room_id, test_utils::mocks::MatrixMockServer};
269 ///
270 /// let mock_server = MatrixMockServer::new().await;
271 /// let client = mock_server.client_builder().build().await;
272 ///
273 /// let room = mock_server
274 /// .sync_joined_room(&client, room_id!("!room_id:localhost"))
275 /// .await;
276 /// # anyhow::Ok(()) });
277 pub async fn sync_joined_room(&self, client: &Client, room_id: &RoomId) -> Room {
278 self.sync_room(client, JoinedRoomBuilder::new(room_id)).await
279 }
280
281 /// Verify that the previous mocks expected number of requests match
282 /// reality, and then cancels all active mocks.
283 ///
284 /// # Examples
285 ///
286 /// ```
287 /// # tokio_test::block_on(async {
288 /// use matrix_sdk::{ruma::{room_id, event_id}, test_utils::mocks::MatrixMockServer};
289 /// use serde_json::json;
290 ///
291 /// let mock_server = MatrixMockServer::new().await;
292 /// let client = mock_server.client_builder().build().await;
293 ///
294 /// mock_server.mock_room_state_encryption().plain().mount().await;
295 /// let room = mock_server
296 /// .sync_joined_room(&client, room_id!("!room_id:localhost"))
297 /// .await;
298 /// mock_server.mock_room_send().ok(event_id!("$some_id")).mount().await;
299 ///
300 /// // This will succeed.
301 /// let response = room.send_raw("m.room.message", json!({ "body": "Hello world" })).await?;
302 ///
303 /// // Now we reset the mocks.
304 /// mock_server.verify_and_reset().await;
305 ///
306 /// // And we can't send anymore.
307 /// let response = room
308 /// .send_raw("m.room.message", json!({ "body": "Hello world" }))
309 /// .await
310 /// .expect_err("We removed the mock so sending should now fail");
311 /// # anyhow::Ok(()) });
312 /// ```
313 pub async fn verify_and_reset(&self) {
314 self.server.verify().await;
315 self.server.reset().await;
316 }
317}
318
319// Specific mount endpoints.
320impl MatrixMockServer {
321 /// Mocks a sync endpoint.
322 ///
323 /// # Examples
324 ///
325 /// ```
326 /// # tokio_test::block_on(async {
327 /// use matrix_sdk::{ruma::room_id, test_utils::mocks::MatrixMockServer};
328 /// use matrix_sdk_test::JoinedRoomBuilder;
329 ///
330 /// // First create the mock server and client pair.
331 /// let mock_server = MatrixMockServer::new().await;
332 /// let client = mock_server.client_builder().build().await;
333 /// let room_id = room_id!("!room_id:localhost");
334 ///
335 /// // Let's emulate what `MatrixMockServer::sync_joined_room()` does.
336 /// mock_server
337 /// .mock_sync()
338 /// .ok_and_run(&client, |builder| {
339 /// builder.add_joined_room(JoinedRoomBuilder::new(room_id));
340 /// })
341 /// .await;
342 ///
343 /// let room = client
344 /// .get_room(room_id)
345 /// .expect("The room should be available after we mocked the sync");
346 /// # anyhow::Ok(()) });
347 /// ```
348 pub fn mock_sync(&self) -> MockEndpoint<'_, SyncEndpoint> {
349 let mock = Mock::given(method("GET")).and(path("/_matrix/client/v3/sync"));
350 self.mock_endpoint(
351 mock,
352 SyncEndpoint { sync_response_builder: self.sync_response_builder.clone() },
353 )
354 }
355
356 /// Creates a prebuilt mock for joining a room.
357 ///
358 /// # Examples
359 ///
360 /// ```
361 /// # tokio_test::block_on(async {
362 /// use matrix_sdk::{ruma::{room_id, event_id}, test_utils::mocks::MatrixMockServer};
363 /// use serde_json::json;
364 ///
365 /// let mock_server = MatrixMockServer::new().await;
366 /// let client = mock_server.client_builder().build().await;
367 /// let room_id = room_id!("!test:localhost");
368 ///
369 /// mock_server.mock_room_join(room_id).ok().mount();
370 ///
371 /// let room = client.join_room_by_id(room_id).await?;
372 ///
373 /// assert_eq!(
374 /// room_id,
375 /// room.room_id(),
376 /// "The room ID we mocked should match the one we received when we joined the room"
377 /// );
378 /// # anyhow::Ok(()) });
379 /// ```
380 pub fn mock_room_join(&self, room_id: &RoomId) -> MockEndpoint<'_, JoinRoomEndpoint> {
381 let mock = Mock::given(method("POST"))
382 .and(path_regex(format!("^/_matrix/client/v3/rooms/{room_id}/join")));
383 self.mock_endpoint(mock, JoinRoomEndpoint { room_id: room_id.to_owned() })
384 }
385
386 /// Creates a prebuilt mock for sending an event in a room.
387 ///
388 /// Note: works with *any* room.
389 ///
390 /// # Examples
391 ///
392 /// ```
393 /// # tokio_test::block_on(async {
394 /// use matrix_sdk::{ruma::{room_id, event_id}, test_utils::mocks::MatrixMockServer};
395 /// use serde_json::json;
396 ///
397 /// let mock_server = MatrixMockServer::new().await;
398 /// let client = mock_server.client_builder().build().await;
399 ///
400 /// mock_server.mock_room_state_encryption().plain().mount().await;
401 ///
402 /// let room = mock_server
403 /// .sync_joined_room(&client, room_id!("!room_id:localhost"))
404 /// .await;
405 ///
406 /// let event_id = event_id!("$some_id");
407 /// mock_server
408 /// .mock_room_send()
409 /// .ok(event_id)
410 /// .expect(1)
411 /// .mount()
412 /// .await;
413 ///
414 /// let response = room.send_raw("m.room.message", json!({ "body": "Hello world" })).await?;
415 ///
416 /// assert_eq!(
417 /// event_id,
418 /// response.event_id,
419 /// "The event ID we mocked should match the one we received when we sent the event"
420 /// );
421 /// # anyhow::Ok(()) });
422 /// ```
423 pub fn mock_room_send(&self) -> MockEndpoint<'_, RoomSendEndpoint> {
424 let mock = Mock::given(method("PUT"))
425 .and(path_regex(r"^/_matrix/client/v3/rooms/.*/send/.*".to_owned()));
426 self.mock_endpoint(mock, RoomSendEndpoint)
427 }
428
429 /// Creates a prebuilt mock for sending a state event in a room.
430 ///
431 /// Similar to: [`MatrixMockServer::mock_room_send`]
432 ///
433 /// Note: works with *any* room.
434 /// Note: works with *any* event type.
435 ///
436 /// ```
437 /// # tokio_test::block_on(async {
438 /// use matrix_sdk::{ruma::{room_id, event_id}, test_utils::mocks::MatrixMockServer};
439 /// use serde_json::json;
440 ///
441 /// let mock_server = MatrixMockServer::new().await;
442 /// let client = mock_server.client_builder().build().await;
443 ///
444 /// mock_server.mock_room_state_encryption().plain().mount().await;
445 ///
446 /// let room = mock_server
447 /// .sync_joined_room(&client, room_id!("!room_id:localhost"))
448 /// .await;
449 ///
450 /// let event_id = event_id!("$some_id");
451 /// mock_server
452 /// .mock_room_send_state()
453 /// .ok(event_id)
454 /// .expect(1)
455 /// .mount()
456 /// .await;
457 ///
458 /// let response_not_mocked = room.send_raw("m.room.create", json!({ "body": "Hello world" })).await;
459 /// // The `/send` endpoint should not be mocked by the server.
460 /// assert!(response_not_mocked.is_err());
461 ///
462 ///
463 /// let response = room.send_state_event_raw("m.room.message", "my_key", json!({ "body": "Hello world" })).await?;
464 /// // The `/state` endpoint should be mocked by the server.
465 /// assert_eq!(
466 /// event_id,
467 /// response.event_id,
468 /// "The event ID we mocked should match the one we received when we sent the event"
469 /// );
470 /// # anyhow::Ok(()) });
471 /// ```
472 pub fn mock_room_send_state(&self) -> MockEndpoint<'_, RoomSendStateEndpoint> {
473 let mock =
474 Mock::given(method("PUT")).and(path_regex(r"^/_matrix/client/v3/rooms/.*/state/.*/.*"));
475 self.mock_endpoint(mock, RoomSendStateEndpoint::default()).expect_default_access_token()
476 }
477
478 /// Creates a prebuilt mock for asking whether *a* room is encrypted or not.
479 ///
480 /// Note: Applies to all rooms.
481 ///
482 /// # Examples
483 ///
484 /// ```
485 /// # tokio_test::block_on(async {
486 /// use matrix_sdk::{ruma::room_id, test_utils::mocks::MatrixMockServer};
487 ///
488 /// let mock_server = MatrixMockServer::new().await;
489 /// let client = mock_server.client_builder().build().await;
490 ///
491 /// mock_server.mock_room_state_encryption().encrypted().mount().await;
492 ///
493 /// let room = mock_server
494 /// .sync_joined_room(&client, room_id!("!room_id:localhost"))
495 /// .await;
496 ///
497 /// assert!(
498 /// room.latest_encryption_state().await?.is_encrypted(),
499 /// "The room should be marked as encrypted."
500 /// );
501 /// # anyhow::Ok(()) });
502 /// ```
503 pub fn mock_room_state_encryption(&self) -> MockEndpoint<'_, EncryptionStateEndpoint> {
504 let mock = Mock::given(method("GET"))
505 .and(path_regex(r"^/_matrix/client/v3/rooms/.*/state/m.*room.*encryption.?"));
506 self.mock_endpoint(mock, EncryptionStateEndpoint).expect_default_access_token()
507 }
508
509 /// Creates a prebuilt mock for setting the room encryption state.
510 ///
511 /// Note: Applies to all rooms.
512 ///
513 /// # Examples
514 ///
515 /// ```
516 /// # tokio_test::block_on(async {
517 /// use matrix_sdk::{
518 /// ruma::{event_id, room_id},
519 /// test_utils::mocks::MatrixMockServer,
520 /// };
521 ///
522 /// let mock_server = MatrixMockServer::new().await;
523 /// let client = mock_server.client_builder().build().await;
524 ///
525 /// mock_server.mock_room_state_encryption().plain().mount().await;
526 /// mock_server
527 /// .mock_set_room_state_encryption()
528 /// .ok(event_id!("$id"))
529 /// .mock_once()
530 /// .mount()
531 /// .await;
532 ///
533 /// let room = mock_server
534 /// .sync_joined_room(&client, room_id!("!room_id:localhost"))
535 /// .await;
536 ///
537 /// room.enable_encryption()
538 /// .await
539 /// .expect("We should be able to enable encryption in the room");
540 /// # anyhow::Ok(()) });
541 /// ```
542 pub fn mock_set_room_state_encryption(&self) -> MockEndpoint<'_, SetEncryptionStateEndpoint> {
543 let mock = Mock::given(method("PUT"))
544 .and(path_regex(r"^/_matrix/client/v3/rooms/.*/state/m.*room.*encryption.?"));
545 self.mock_endpoint(mock, SetEncryptionStateEndpoint).expect_default_access_token()
546 }
547
548 /// Creates a prebuilt mock for the room redact endpoint.
549 ///
550 /// # Examples
551 ///
552 /// ```
553 /// # tokio_test::block_on(async {
554 /// use matrix_sdk::{
555 /// ruma::{event_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 /// let event_id = event_id!("$id");
562 ///
563 /// mock_server.mock_room_redact().ok(event_id).mock_once().mount().await;
564 ///
565 /// let room = mock_server
566 /// .sync_joined_room(&client, room_id!("!room_id:localhost"))
567 /// .await;
568 ///
569 /// room.redact(event_id, None, None)
570 /// .await
571 /// .expect("We should be able to redact events in the room");
572 /// # anyhow::Ok(()) });
573 /// ```
574 pub fn mock_room_redact(&self) -> MockEndpoint<'_, RoomRedactEndpoint> {
575 let mock = Mock::given(method("PUT"))
576 .and(path_regex(r"^/_matrix/client/v3/rooms/.*/redact/.*?/.*?"));
577 self.mock_endpoint(mock, RoomRedactEndpoint).expect_default_access_token()
578 }
579
580 /// Creates a prebuilt mock for retrieving an event with /room/.../event.
581 pub fn mock_room_event(&self) -> MockEndpoint<'_, RoomEventEndpoint> {
582 let mock = Mock::given(method("GET"));
583 self.mock_endpoint(mock, RoomEventEndpoint { room: None, match_event_id: false })
584 .expect_default_access_token()
585 }
586
587 /// Creates a prebuilt mock for retrieving an event with /room/.../context.
588 pub fn mock_room_event_context(&self) -> MockEndpoint<'_, RoomEventContextEndpoint> {
589 let mock = Mock::given(method("GET"));
590 self.mock_endpoint(mock, RoomEventContextEndpoint { room: None, match_event_id: false })
591 .expect_default_access_token()
592 }
593
594 /// Create a prebuild mock for paginating room message with the `/messages`
595 /// endpoint.
596 pub fn mock_room_messages(&self) -> MockEndpoint<'_, RoomMessagesEndpoint> {
597 let mock =
598 Mock::given(method("GET")).and(path_regex(r"^/_matrix/client/v3/rooms/.*/messages$"));
599 self.mock_endpoint(mock, RoomMessagesEndpoint).expect_default_access_token()
600 }
601
602 /// Create a prebuilt mock for uploading media.
603 pub fn mock_upload(&self) -> MockEndpoint<'_, UploadEndpoint> {
604 let mock = Mock::given(method("POST")).and(path("/_matrix/media/v3/upload"));
605 self.mock_endpoint(mock, UploadEndpoint)
606 }
607
608 /// Create a prebuilt mock for resolving room aliases.
609 ///
610 /// # Examples
611 ///
612 /// ```
613 /// # tokio_test::block_on(async {
614 /// use matrix_sdk::{
615 /// ruma::{owned_room_id, room_alias_id},
616 /// test_utils::mocks::MatrixMockServer,
617 /// };
618 /// let mock_server = MatrixMockServer::new().await;
619 /// let client = mock_server.client_builder().build().await;
620 ///
621 /// mock_server
622 /// .mock_room_directory_resolve_alias()
623 /// .ok("!a:b.c", Vec::new())
624 /// .mock_once()
625 /// .mount()
626 /// .await;
627 ///
628 /// let res = client
629 /// .resolve_room_alias(room_alias_id!("#a:b.c"))
630 /// .await
631 /// .expect("We should be able to resolve the room alias");
632 /// assert_eq!(res.room_id, owned_room_id!("!a:b.c"));
633 /// # anyhow::Ok(()) });
634 /// ```
635 pub fn mock_room_directory_resolve_alias(&self) -> MockEndpoint<'_, ResolveRoomAliasEndpoint> {
636 let mock =
637 Mock::given(method("GET")).and(path_regex(r"/_matrix/client/v3/directory/room/.*"));
638 self.mock_endpoint(mock, ResolveRoomAliasEndpoint)
639 }
640
641 /// Create a prebuilt mock for publishing room aliases in the room
642 /// directory.
643 ///
644 /// # Examples
645 ///
646 /// ```
647 /// # tokio_test::block_on(async {
648 /// use matrix_sdk::{
649 /// ruma::{room_alias_id, room_id},
650 /// test_utils::mocks::MatrixMockServer,
651 /// };
652 ///
653 /// let mock_server = MatrixMockServer::new().await;
654 /// let client = mock_server.client_builder().build().await;
655 ///
656 /// mock_server
657 /// .mock_room_directory_create_room_alias()
658 /// .ok()
659 /// .mock_once()
660 /// .mount()
661 /// .await;
662 ///
663 /// client
664 /// .create_room_alias(room_alias_id!("#a:b.c"), room_id!("!a:b.c"))
665 /// .await
666 /// .expect("We should be able to create a room alias");
667 /// # anyhow::Ok(()) });
668 /// ```
669 pub fn mock_room_directory_create_room_alias(
670 &self,
671 ) -> MockEndpoint<'_, CreateRoomAliasEndpoint> {
672 let mock =
673 Mock::given(method("PUT")).and(path_regex(r"/_matrix/client/v3/directory/room/.*"));
674 self.mock_endpoint(mock, CreateRoomAliasEndpoint)
675 }
676
677 /// Create a prebuilt mock for removing room aliases from the room
678 /// directory.
679 ///
680 /// # Examples
681 ///
682 /// ```
683 /// # tokio_test::block_on(async {
684 /// use matrix_sdk::{
685 /// ruma::room_alias_id, test_utils::mocks::MatrixMockServer,
686 /// };
687 ///
688 /// let mock_server = MatrixMockServer::new().await;
689 /// let client = mock_server.client_builder().build().await;
690 ///
691 /// mock_server
692 /// .mock_room_directory_remove_room_alias()
693 /// .ok()
694 /// .mock_once()
695 /// .mount()
696 /// .await;
697 ///
698 /// client
699 /// .remove_room_alias(room_alias_id!("#a:b.c"))
700 /// .await
701 /// .expect("We should be able to remove the room alias");
702 /// # anyhow::Ok(()) });
703 /// ```
704 pub fn mock_room_directory_remove_room_alias(
705 &self,
706 ) -> MockEndpoint<'_, RemoveRoomAliasEndpoint> {
707 let mock =
708 Mock::given(method("DELETE")).and(path_regex(r"/_matrix/client/v3/directory/room/.*"));
709 self.mock_endpoint(mock, RemoveRoomAliasEndpoint)
710 }
711
712 /// Create a prebuilt mock for listing public rooms.
713 ///
714 /// # Examples
715 ///
716 /// ```
717 /// #
718 /// tokio_test::block_on(async {
719 /// use js_int::uint;
720 /// use ruma::directory::PublicRoomsChunkInit;
721 /// use matrix_sdk::room_directory_search::RoomDirectorySearch;
722 /// use matrix_sdk::{
723 /// ruma::{event_id, room_id},
724 /// test_utils::mocks::MatrixMockServer,
725 /// };
726 /// let mock_server = MatrixMockServer::new().await;
727 /// let client = mock_server.client_builder().build().await;
728 /// let event_id = event_id!("$id");
729 /// let room_id = room_id!("!room_id:localhost");
730 ///
731 /// let chunk = vec![PublicRoomsChunkInit {
732 /// num_joined_members: uint!(0),
733 /// room_id: room_id.to_owned(),
734 /// world_readable: true,
735 /// guest_can_join: true,
736 /// }.into()];
737 ///
738 /// mock_server.mock_public_rooms().ok(chunk, None, None, Some(20)).mock_once().mount().await;
739 /// let mut room_directory_search = RoomDirectorySearch::new(client);
740 ///
741 /// room_directory_search.search(Some("some-alias".to_owned()), 100, None)
742 /// .await
743 /// .expect("Room directory search failed");
744 ///
745 /// let (results, _) = room_directory_search.results();
746 /// assert_eq!(results.len(), 1);
747 /// assert_eq!(results.get(0).unwrap().room_id, room_id.to_owned());
748 /// # });
749 /// ```
750 pub fn mock_public_rooms(&self) -> MockEndpoint<'_, PublicRoomsEndpoint> {
751 let mock = Mock::given(method("POST")).and(path_regex(r"/_matrix/client/v3/publicRooms"));
752 self.mock_endpoint(mock, PublicRoomsEndpoint)
753 }
754
755 /// Create a prebuilt mock for setting a room's visibility in the room
756 /// directory.
757 ///
758 /// # Examples
759 ///
760 /// ```
761 /// # tokio_test::block_on(async {
762 /// use matrix_sdk::{ruma::room_id, test_utils::mocks::MatrixMockServer};
763 /// use ruma::api::client::room::Visibility;
764 ///
765 /// let mock_server = MatrixMockServer::new().await;
766 /// let client = mock_server.client_builder().build().await;
767 ///
768 /// mock_server
769 /// .mock_room_directory_set_room_visibility()
770 /// .ok()
771 /// .mock_once()
772 /// .mount()
773 /// .await;
774 ///
775 /// let room = mock_server
776 /// .sync_joined_room(&client, room_id!("!room_id:localhost"))
777 /// .await;
778 ///
779 /// room.privacy_settings()
780 /// .update_room_visibility(Visibility::Private)
781 /// .await
782 /// .expect("We should be able to update the room's visibility");
783 /// # anyhow::Ok(()) });
784 /// ```
785 pub fn mock_room_directory_set_room_visibility(
786 &self,
787 ) -> MockEndpoint<'_, SetRoomVisibilityEndpoint> {
788 let mock = Mock::given(method("PUT"))
789 .and(path_regex(r"^/_matrix/client/v3/directory/list/room/.*$"));
790 self.mock_endpoint(mock, SetRoomVisibilityEndpoint)
791 }
792
793 /// Create a prebuilt mock for getting a room's visibility in the room
794 /// directory.
795 ///
796 /// # Examples
797 ///
798 /// ```
799 /// # tokio_test::block_on(async {
800 /// use matrix_sdk::{ruma::room_id, test_utils::mocks::MatrixMockServer};
801 /// use ruma::api::client::room::Visibility;
802 ///
803 /// let mock_server = MatrixMockServer::new().await;
804 /// let client = mock_server.client_builder().build().await;
805 ///
806 /// mock_server
807 /// .mock_room_directory_get_room_visibility()
808 /// .ok(Visibility::Public)
809 /// .mock_once()
810 /// .mount()
811 /// .await;
812 ///
813 /// let room = mock_server
814 /// .sync_joined_room(&client, room_id!("!room_id:localhost"))
815 /// .await;
816 ///
817 /// let visibility = room
818 /// .privacy_settings()
819 /// .get_room_visibility()
820 /// .await
821 /// .expect("We should be able to get the room's visibility");
822 /// assert_eq!(visibility, Visibility::Public);
823 /// # anyhow::Ok(()) });
824 /// ```
825 pub fn mock_room_directory_get_room_visibility(
826 &self,
827 ) -> MockEndpoint<'_, GetRoomVisibilityEndpoint> {
828 let mock = Mock::given(method("GET"))
829 .and(path_regex(r"^/_matrix/client/v3/directory/list/room/.*$"));
830 self.mock_endpoint(mock, GetRoomVisibilityEndpoint)
831 }
832
833 /// Create a prebuilt mock for fetching information about key storage
834 /// backups.
835 ///
836 /// # Examples
837 ///
838 /// ```
839 /// # #[cfg(feature = "e2e-encryption")]
840 /// # {
841 /// # tokio_test::block_on(async {
842 /// use matrix_sdk::test_utils::mocks::MatrixMockServer;
843 ///
844 /// let mock_server = MatrixMockServer::new().await;
845 /// let client = mock_server.client_builder().build().await;
846 ///
847 /// mock_server.mock_room_keys_version().exists().expect(1).mount().await;
848 ///
849 /// let exists =
850 /// client.encryption().backups().fetch_exists_on_server().await.unwrap();
851 ///
852 /// assert!(exists);
853 /// # });
854 /// # }
855 /// ```
856 pub fn mock_room_keys_version(&self) -> MockEndpoint<'_, RoomKeysVersionEndpoint> {
857 let mock =
858 Mock::given(method("GET")).and(path_regex(r"_matrix/client/v3/room_keys/version"));
859 self.mock_endpoint(mock, RoomKeysVersionEndpoint).expect_default_access_token()
860 }
861
862 /// Create a prebuilt mock for adding key storage backups via POST
863 pub fn mock_add_room_keys_version(&self) -> MockEndpoint<'_, AddRoomKeysVersionEndpoint> {
864 let mock =
865 Mock::given(method("POST")).and(path_regex(r"_matrix/client/v3/room_keys/version"));
866 self.mock_endpoint(mock, AddRoomKeysVersionEndpoint).expect_default_access_token()
867 }
868
869 /// Create a prebuilt mock for adding key storage backups via POST
870 pub fn mock_delete_room_keys_version(&self) -> MockEndpoint<'_, DeleteRoomKeysVersionEndpoint> {
871 let mock = Mock::given(method("DELETE"))
872 .and(path_regex(r"_matrix/client/v3/room_keys/version/[^/]*"));
873 self.mock_endpoint(mock, DeleteRoomKeysVersionEndpoint).expect_default_access_token()
874 }
875
876 /// Creates a prebuilt mock for the `/sendToDevice` endpoint.
877 ///
878 /// This mock can be used to simulate sending to-device messages in tests.
879 /// # Examples
880 ///
881 /// ```
882 /// # #[cfg(feature = "e2e-encryption")]
883 /// # {
884 /// # tokio_test::block_on(async {
885 /// use std::collections::BTreeMap;
886 /// use matrix_sdk::{
887 /// ruma::{
888 /// serde::Raw,
889 /// api::client::to_device::send_event_to_device::v3::Request as ToDeviceRequest,
890 /// to_device::DeviceIdOrAllDevices,
891 /// user_id,owned_device_id
892 /// },
893 /// test_utils::mocks::MatrixMockServer,
894 /// };
895 /// use serde_json::json;
896 ///
897 /// let mock_server = MatrixMockServer::new().await;
898 /// let client = mock_server.client_builder().build().await;
899 ///
900 /// mock_server.mock_send_to_device().ok().mock_once().mount().await;
901 ///
902 /// let request = ToDeviceRequest::new_raw(
903 /// "m.custom.event".into(),
904 /// "txn_id".into(),
905 /// BTreeMap::from([
906 /// (user_id!("@alice:localhost").to_owned(), BTreeMap::from([(
907 /// DeviceIdOrAllDevices::AllDevices,
908 /// Raw::new(&ruma::events::AnyToDeviceEventContent::Dummy(ruma::events::dummy::ToDeviceDummyEventContent {})).unwrap(),
909 /// )])),
910 /// ])
911 /// );
912 ///
913 /// client
914 /// .send(request)
915 /// .await
916 /// .expect("We should be able to send a to-device message");
917 /// # anyhow::Ok(()) });
918 /// # }
919 /// ```
920 pub fn mock_send_to_device(&self) -> MockEndpoint<'_, SendToDeviceEndpoint> {
921 let mock =
922 Mock::given(method("PUT")).and(path_regex(r"^/_matrix/client/v3/sendToDevice/.*/.*"));
923 self.mock_endpoint(mock, SendToDeviceEndpoint).expect_default_access_token()
924 }
925
926 /// Create a prebuilt mock for getting the room members in a room.
927 ///
928 /// # Examples
929 ///
930 /// ```
931 /// # tokio_test::block_on(async {
932 /// use matrix_sdk::{
933 /// ruma::{event_id, room_id},
934 /// test_utils::mocks::MatrixMockServer,
935 /// };
936 /// use matrix_sdk_base::RoomMemberships;
937 /// use matrix_sdk_test::event_factory::EventFactory;
938 /// use ruma::{
939 /// events::room::member::{MembershipState, RoomMemberEventContent},
940 /// user_id,
941 /// };
942 /// let mock_server = MatrixMockServer::new().await;
943 /// let client = mock_server.client_builder().build().await;
944 /// let event_id = event_id!("$id");
945 /// let room_id = room_id!("!room_id:localhost");
946 ///
947 /// let f = EventFactory::new().room(room_id);
948 /// let alice_user_id = user_id!("@alice:b.c");
949 /// let alice_knock_event = f
950 /// .event(RoomMemberEventContent::new(MembershipState::Knock))
951 /// .event_id(event_id)
952 /// .sender(alice_user_id)
953 /// .state_key(alice_user_id)
954 /// .into_raw();
955 ///
956 /// mock_server
957 /// .mock_get_members()
958 /// .ok(vec![alice_knock_event])
959 /// .mock_once()
960 /// .mount()
961 /// .await;
962 /// let room = mock_server.sync_joined_room(&client, room_id).await;
963 ///
964 /// let members = room.members(RoomMemberships::all()).await.unwrap();
965 /// assert_eq!(members.len(), 1);
966 /// # });
967 /// ```
968 pub fn mock_get_members(&self) -> MockEndpoint<'_, GetRoomMembersEndpoint> {
969 let mock =
970 Mock::given(method("GET")).and(path_regex(r"^/_matrix/client/v3/rooms/.*/members$"));
971 self.mock_endpoint(mock, GetRoomMembersEndpoint)
972 }
973
974 /// Creates a prebuilt mock for inviting a user to a room by its id.
975 ///
976 /// # Examples
977 ///
978 /// ```
979 /// # use ruma::user_id;
980 /// tokio_test::block_on(async {
981 /// use matrix_sdk::{
982 /// ruma::room_id,
983 /// test_utils::mocks::MatrixMockServer,
984 /// };
985 ///
986 /// let mock_server = MatrixMockServer::new().await;
987 /// let client = mock_server.client_builder().build().await;
988 ///
989 /// mock_server.mock_invite_user_by_id().ok().mock_once().mount().await;
990 ///
991 /// let room = mock_server
992 /// .sync_joined_room(&client, room_id!("!room_id:localhost"))
993 /// .await;
994 ///
995 /// room.invite_user_by_id(user_id!("@alice:localhost")).await.unwrap();
996 /// # anyhow::Ok(()) });
997 /// ```
998 pub fn mock_invite_user_by_id(&self) -> MockEndpoint<'_, InviteUserByIdEndpoint> {
999 let mock =
1000 Mock::given(method("POST")).and(path_regex(r"^/_matrix/client/v3/rooms/.*/invite$"));
1001 self.mock_endpoint(mock, InviteUserByIdEndpoint)
1002 }
1003
1004 /// Creates a prebuilt mock for kicking a user from a room.
1005 ///
1006 /// # Examples
1007 ///
1008 /// ```
1009 /// # use ruma::user_id;
1010 /// tokio_test::block_on(async {
1011 /// use matrix_sdk::{
1012 /// ruma::room_id,
1013 /// test_utils::mocks::MatrixMockServer,
1014 /// };
1015 ///
1016 /// let mock_server = MatrixMockServer::new().await;
1017 /// let client = mock_server.client_builder().build().await;
1018 ///
1019 /// mock_server.mock_kick_user().ok().mock_once().mount().await;
1020 ///
1021 /// let room = mock_server
1022 /// .sync_joined_room(&client, room_id!("!room_id:localhost"))
1023 /// .await;
1024 ///
1025 /// room.kick_user(user_id!("@alice:localhost"), None).await.unwrap();
1026 /// # anyhow::Ok(()) });
1027 /// ```
1028 pub fn mock_kick_user(&self) -> MockEndpoint<'_, KickUserEndpoint> {
1029 let mock =
1030 Mock::given(method("POST")).and(path_regex(r"^/_matrix/client/v3/rooms/.*/kick"));
1031 self.mock_endpoint(mock, KickUserEndpoint)
1032 }
1033
1034 /// Creates a prebuilt mock for banning a user from a room.
1035 ///
1036 /// # Examples
1037 ///
1038 /// ```
1039 /// # use ruma::user_id;
1040 /// tokio_test::block_on(async {
1041 /// use matrix_sdk::{
1042 /// ruma::room_id,
1043 /// test_utils::mocks::MatrixMockServer,
1044 /// };
1045 ///
1046 /// let mock_server = MatrixMockServer::new().await;
1047 /// let client = mock_server.client_builder().build().await;
1048 ///
1049 /// mock_server.mock_ban_user().ok().mock_once().mount().await;
1050 ///
1051 /// let room = mock_server
1052 /// .sync_joined_room(&client, room_id!("!room_id:localhost"))
1053 /// .await;
1054 ///
1055 /// room.ban_user(user_id!("@alice:localhost"), None).await.unwrap();
1056 /// # anyhow::Ok(()) });
1057 /// ```
1058 pub fn mock_ban_user(&self) -> MockEndpoint<'_, BanUserEndpoint> {
1059 let mock = Mock::given(method("POST")).and(path_regex(r"^/_matrix/client/v3/rooms/.*/ban"));
1060 self.mock_endpoint(mock, BanUserEndpoint)
1061 }
1062
1063 /// Creates a prebuilt mock for the `/_matrix/client/versions` endpoint.
1064 pub fn mock_versions(&self) -> MockEndpoint<'_, VersionsEndpoint> {
1065 let mock = Mock::given(method("GET")).and(path_regex(r"^/_matrix/client/versions"));
1066 self.mock_endpoint(mock, VersionsEndpoint)
1067 }
1068
1069 /// Creates a prebuilt mock for the room summary endpoint [MSC3266](https://github.com/matrix-org/matrix-spec-proposals/pull/3266).
1070 pub fn mock_room_summary(&self) -> MockEndpoint<'_, RoomSummaryEndpoint> {
1071 let mock = Mock::given(method("GET"))
1072 .and(path_regex(r"^/_matrix/client/unstable/im.nheko.summary/rooms/.*/summary"));
1073 self.mock_endpoint(mock, RoomSummaryEndpoint)
1074 }
1075
1076 /// Creates a prebuilt mock for the endpoint used to set a room's pinned
1077 /// events.
1078 pub fn mock_set_room_pinned_events(&self) -> MockEndpoint<'_, SetRoomPinnedEventsEndpoint> {
1079 let mock = Mock::given(method("PUT"))
1080 .and(path_regex(r"^/_matrix/client/v3/rooms/.*/state/m.room.pinned_events/.*?"));
1081 self.mock_endpoint(mock, SetRoomPinnedEventsEndpoint).expect_default_access_token()
1082 }
1083
1084 /// Creates a prebuilt mock for the endpoint used to get information about
1085 /// the owner of the given access token.
1086 ///
1087 /// If no access token is provided, the access token to match is `"1234"`,
1088 /// which matches the default value in the mock data.
1089 pub fn mock_who_am_i(&self) -> MockEndpoint<'_, WhoAmIEndpoint> {
1090 let mock =
1091 Mock::given(method("GET")).and(path_regex(r"^/_matrix/client/v3/account/whoami"));
1092 self.mock_endpoint(mock, WhoAmIEndpoint).expect_default_access_token()
1093 }
1094
1095 /// Creates a prebuilt mock for the endpoint used to publish end-to-end
1096 /// encryption keys.
1097 pub fn mock_upload_keys(&self) -> MockEndpoint<'_, UploadKeysEndpoint> {
1098 let mock = Mock::given(method("POST")).and(path_regex(r"^/_matrix/client/v3/keys/upload"));
1099 self.mock_endpoint(mock, UploadKeysEndpoint).expect_default_access_token()
1100 }
1101
1102 /// Creates a prebuilt mock for the endpoint used to query end-to-end
1103 /// encryption keys.
1104 pub fn mock_query_keys(&self) -> MockEndpoint<'_, QueryKeysEndpoint> {
1105 let mock = Mock::given(method("POST")).and(path_regex(r"^/_matrix/client/v3/keys/query"));
1106 self.mock_endpoint(mock, QueryKeysEndpoint).expect_default_access_token()
1107 }
1108
1109 /// Creates a prebuilt mock for the endpoint used to discover the URL of a
1110 /// homeserver.
1111 pub fn mock_well_known(&self) -> MockEndpoint<'_, WellKnownEndpoint> {
1112 let mock = Mock::given(method("GET")).and(path_regex(r"^/.well-known/matrix/client"));
1113 self.mock_endpoint(mock, WellKnownEndpoint)
1114 }
1115
1116 /// Creates a prebuilt mock for the endpoint used to publish cross-signing
1117 /// keys.
1118 pub fn mock_upload_cross_signing_keys(
1119 &self,
1120 ) -> MockEndpoint<'_, UploadCrossSigningKeysEndpoint> {
1121 let mock = Mock::given(method("POST"))
1122 .and(path_regex(r"^/_matrix/client/v3/keys/device_signing/upload"));
1123 self.mock_endpoint(mock, UploadCrossSigningKeysEndpoint).expect_default_access_token()
1124 }
1125
1126 /// Creates a prebuilt mock for the endpoint used to publish cross-signing
1127 /// signatures.
1128 pub fn mock_upload_cross_signing_signatures(
1129 &self,
1130 ) -> MockEndpoint<'_, UploadCrossSigningSignaturesEndpoint> {
1131 let mock = Mock::given(method("POST"))
1132 .and(path_regex(r"^/_matrix/client/v3/keys/signatures/upload"));
1133 self.mock_endpoint(mock, UploadCrossSigningSignaturesEndpoint).expect_default_access_token()
1134 }
1135
1136 /// Creates a prebuilt mock for the endpoint used to leave a room.
1137 pub fn mock_room_leave(&self) -> MockEndpoint<'_, RoomLeaveEndpoint> {
1138 let mock =
1139 Mock::given(method("POST")).and(path_regex(r"^/_matrix/client/v3/rooms/.*/leave"));
1140 self.mock_endpoint(mock, RoomLeaveEndpoint).expect_default_access_token()
1141 }
1142
1143 /// Creates a prebuilt mock for the endpoint used to forget a room.
1144 pub fn mock_room_forget(&self) -> MockEndpoint<'_, RoomForgetEndpoint> {
1145 let mock =
1146 Mock::given(method("POST")).and(path_regex(r"^/_matrix/client/v3/rooms/.*/forget"));
1147 self.mock_endpoint(mock, RoomForgetEndpoint).expect_default_access_token()
1148 }
1149
1150 /// Create a prebuilt mock for the endpoint use to log out a session.
1151 pub fn mock_logout(&self) -> MockEndpoint<'_, LogoutEndpoint> {
1152 let mock = Mock::given(method("POST")).and(path("/_matrix/client/v3/logout"));
1153 self.mock_endpoint(mock, LogoutEndpoint).expect_default_access_token()
1154 }
1155
1156 /// Create a prebuilt mock for the endpoint used to get the list of thread
1157 /// roots.
1158 pub fn mock_room_threads(&self) -> MockEndpoint<'_, RoomThreadsEndpoint> {
1159 let mock =
1160 Mock::given(method("GET")).and(path_regex(r"^/_matrix/client/v1/rooms/.*/threads$"));
1161 self.mock_endpoint(mock, RoomThreadsEndpoint).expect_default_access_token()
1162 }
1163
1164 /// Create a prebuilt mock for the endpoint used to get the related events.
1165 pub fn mock_room_relations(&self) -> MockEndpoint<'_, RoomRelationsEndpoint> {
1166 // Routing happens in the final method ok(), since it can get complicated.
1167 let mock = Mock::given(method("GET"));
1168 self.mock_endpoint(mock, RoomRelationsEndpoint::default()).expect_default_access_token()
1169 }
1170
1171 /// Create a prebuilt mock for the endpoint used to get the global account
1172 /// data.
1173 ///
1174 /// # Examples
1175 ///
1176 /// ```
1177 /// tokio_test::block_on(async {
1178 /// use matrix_sdk::test_utils::mocks::MatrixMockServer;
1179 /// use serde_json::json;
1180 /// use ruma::events::media_preview_config::MediaPreviews;
1181 ///
1182 /// let mock_server = MatrixMockServer::new().await;
1183 /// let client = mock_server.client_builder().build().await;
1184 ///
1185 /// mock_server.mock_global_account_data().ok(
1186 /// client.user_id().unwrap(),
1187 /// ruma::events::GlobalAccountDataEventType::MediaPreviewConfig,
1188 /// json!({
1189 /// "media_previews": "private",
1190 /// "invite_avatars": "off"
1191 /// })
1192 /// )
1193 /// .mock_once()
1194 /// .mount()
1195 /// .await;
1196 ///
1197 /// client.account().fetch_media_preview_config_event_content().await.unwrap();
1198 ///
1199 /// # anyhow::Ok(()) });
1200 /// ```
1201 pub fn mock_global_account_data(&self) -> MockEndpoint<'_, GlobalAccountDataEndpoint> {
1202 let mock = Mock::given(method("GET"));
1203 self.mock_endpoint(mock, GlobalAccountDataEndpoint).expect_default_access_token()
1204 }
1205
1206 /// Create a prebuilt mock for the endpoint used to send a single receipt.
1207 pub fn mock_send_receipt(
1208 &self,
1209 receipt_type: ReceiptType,
1210 ) -> MockEndpoint<'_, ReceiptEndpoint> {
1211 let mock = Mock::given(method("POST"))
1212 .and(path_regex(format!("^/_matrix/client/v3/rooms/.*/receipt/{receipt_type}/")));
1213 self.mock_endpoint(mock, ReceiptEndpoint).expect_default_access_token()
1214 }
1215
1216 /// Create a prebuilt mock for the endpoint used to send multiple receipts.
1217 pub fn mock_send_read_markers(&self) -> MockEndpoint<'_, ReadMarkersEndpoint> {
1218 let mock = Mock::given(method("POST"))
1219 .and(path_regex(r"^/_matrix/client/v3/rooms/.*/read_markers"));
1220 self.mock_endpoint(mock, ReadMarkersEndpoint).expect_default_access_token()
1221 }
1222
1223 /// Create a prebuilt mock for the endpoint used to set room account data.
1224 pub fn mock_set_room_account_data(
1225 &self,
1226 data_type: RoomAccountDataEventType,
1227 ) -> MockEndpoint<'_, RoomAccountDataEndpoint> {
1228 let mock = Mock::given(method("PUT")).and(path_regex(format!(
1229 "^/_matrix/client/v3/user/.*/rooms/.*/account_data/{data_type}"
1230 )));
1231 self.mock_endpoint(mock, RoomAccountDataEndpoint).expect_default_access_token()
1232 }
1233
1234 /// Create a prebuilt mock for the endpoint used to get the media config of
1235 /// the homeserver that requires authentication.
1236 pub fn mock_authenticated_media_config(
1237 &self,
1238 ) -> MockEndpoint<'_, AuthenticatedMediaConfigEndpoint> {
1239 let mock = Mock::given(method("GET")).and(path("/_matrix/client/v1/media/config"));
1240 self.mock_endpoint(mock, AuthenticatedMediaConfigEndpoint)
1241 }
1242
1243 /// Create a prebuilt mock for the endpoint used to get the media config of
1244 /// the homeserver without requiring authentication.
1245 pub fn mock_media_config(&self) -> MockEndpoint<'_, MediaConfigEndpoint> {
1246 let mock = Mock::given(method("GET")).and(path("/_matrix/media/v3/config"));
1247 self.mock_endpoint(mock, MediaConfigEndpoint)
1248 }
1249
1250 /// Create a prebuilt mock for the endpoint used to log into a session.
1251 pub fn mock_login(&self) -> MockEndpoint<'_, LoginEndpoint> {
1252 let mock = Mock::given(method("POST")).and(path("/_matrix/client/v3/login"));
1253 self.mock_endpoint(mock, LoginEndpoint)
1254 }
1255
1256 /// Create a prebuilt mock for the endpoint used to list the devices of a
1257 /// user.
1258 pub fn mock_devices(&self) -> MockEndpoint<'_, DevicesEndpoint> {
1259 let mock = Mock::given(method("GET")).and(path("/_matrix/client/v3/devices"));
1260 self.mock_endpoint(mock, DevicesEndpoint).expect_default_access_token()
1261 }
1262
1263 /// Create a prebuilt mock for the endpoint used to search in the user
1264 /// directory.
1265 pub fn mock_user_directory(&self) -> MockEndpoint<'_, UserDirectoryEndpoint> {
1266 let mock = Mock::given(method("POST"))
1267 .and(path("/_matrix/client/v3/user_directory/search"))
1268 .and(body_json(&*test_json::search_users::SEARCH_USERS_REQUEST));
1269 self.mock_endpoint(mock, UserDirectoryEndpoint).expect_default_access_token()
1270 }
1271
1272 /// Create a prebuilt mock for the endpoint used to create a new room.
1273 pub fn mock_create_room(&self) -> MockEndpoint<'_, CreateRoomEndpoint> {
1274 let mock = Mock::given(method("POST")).and(path("/_matrix/client/v3/createRoom"));
1275 self.mock_endpoint(mock, CreateRoomEndpoint).expect_default_access_token()
1276 }
1277
1278 /// Create a prebuilt mock for the endpoint used to upgrade a room.
1279 pub fn mock_upgrade_room(&self) -> MockEndpoint<'_, UpgradeRoomEndpoint> {
1280 let mock =
1281 Mock::given(method("POST")).and(path_regex("/_matrix/client/v3/rooms/.*/upgrade"));
1282 self.mock_endpoint(mock, UpgradeRoomEndpoint).expect_default_access_token()
1283 }
1284
1285 /// Create a prebuilt mock for the endpoint used to pre-allocate a MXC URI
1286 /// for a media file.
1287 pub fn mock_media_allocate(&self) -> MockEndpoint<'_, MediaAllocateEndpoint> {
1288 let mock = Mock::given(method("POST")).and(path("/_matrix/media/v1/create"));
1289 self.mock_endpoint(mock, MediaAllocateEndpoint)
1290 }
1291
1292 /// Create a prebuilt mock for the endpoint used to upload a media file with
1293 /// a pre-allocated MXC URI.
1294 pub fn mock_media_allocated_upload(
1295 &self,
1296 server_name: &str,
1297 media_id: &str,
1298 ) -> MockEndpoint<'_, MediaAllocatedUploadEndpoint> {
1299 let mock = Mock::given(method("PUT"))
1300 .and(path(format!("/_matrix/media/v3/upload/{server_name}/{media_id}")));
1301 self.mock_endpoint(mock, MediaAllocatedUploadEndpoint)
1302 }
1303
1304 /// Create a prebuilt mock for the endpoint used to download a media file
1305 /// without requiring authentication.
1306 pub fn mock_media_download(&self) -> MockEndpoint<'_, MediaDownloadEndpoint> {
1307 let mock = Mock::given(method("GET")).and(path_regex("^/_matrix/media/v3/download/"));
1308 self.mock_endpoint(mock, MediaDownloadEndpoint)
1309 }
1310
1311 /// Create a prebuilt mock for the endpoint used to download a thumbnail of
1312 /// a media file without requiring authentication.
1313 pub fn mock_media_thumbnail(
1314 &self,
1315 resize_method: Method,
1316 width: u16,
1317 height: u16,
1318 animated: bool,
1319 ) -> MockEndpoint<'_, MediaThumbnailEndpoint> {
1320 let mock = Mock::given(method("GET"))
1321 .and(path_regex("^/_matrix/media/v3/thumbnail/"))
1322 .and(query_param("method", resize_method.as_str()))
1323 .and(query_param("width", width.to_string()))
1324 .and(query_param("height", height.to_string()))
1325 .and(query_param("animated", animated.to_string()));
1326 self.mock_endpoint(mock, MediaThumbnailEndpoint)
1327 }
1328
1329 /// Create a prebuilt mock for the endpoint used to download a media file
1330 /// that requires authentication.
1331 pub fn mock_authed_media_download(&self) -> MockEndpoint<'_, AuthedMediaDownloadEndpoint> {
1332 let mock =
1333 Mock::given(method("GET")).and(path_regex("^/_matrix/client/v1/media/download/"));
1334 self.mock_endpoint(mock, AuthedMediaDownloadEndpoint).expect_default_access_token()
1335 }
1336
1337 /// Create a prebuilt mock for the endpoint used to download a thumbnail of
1338 /// a media file that requires authentication.
1339 pub fn mock_authed_media_thumbnail(
1340 &self,
1341 resize_method: Method,
1342 width: u16,
1343 height: u16,
1344 animated: bool,
1345 ) -> MockEndpoint<'_, AuthedMediaThumbnailEndpoint> {
1346 let mock = Mock::given(method("GET"))
1347 .and(path_regex("^/_matrix/client/v1/media/thumbnail/"))
1348 .and(query_param("method", resize_method.as_str()))
1349 .and(query_param("width", width.to_string()))
1350 .and(query_param("height", height.to_string()))
1351 .and(query_param("animated", animated.to_string()));
1352 self.mock_endpoint(mock, AuthedMediaThumbnailEndpoint).expect_default_access_token()
1353 }
1354
1355 /// Create a prebuilt mock for the endpoint used to get a single thread
1356 /// subscription status in a given room.
1357 pub fn mock_room_get_thread_subscription(
1358 &self,
1359 ) -> MockEndpoint<'_, RoomGetThreadSubscriptionEndpoint> {
1360 let mock = Mock::given(method("GET"));
1361 self.mock_endpoint(mock, RoomGetThreadSubscriptionEndpoint::default())
1362 .expect_default_access_token()
1363 }
1364
1365 /// Create a prebuilt mock for the endpoint used to define a thread
1366 /// subscription in a given room.
1367 pub fn mock_room_put_thread_subscription(
1368 &self,
1369 ) -> MockEndpoint<'_, RoomPutThreadSubscriptionEndpoint> {
1370 let mock = Mock::given(method("PUT"));
1371 self.mock_endpoint(mock, RoomPutThreadSubscriptionEndpoint::default())
1372 .expect_default_access_token()
1373 }
1374
1375 /// Create a prebuilt mock for the endpoint used to delete a thread
1376 /// subscription in a given room.
1377 pub fn mock_room_delete_thread_subscription(
1378 &self,
1379 ) -> MockEndpoint<'_, RoomDeleteThreadSubscriptionEndpoint> {
1380 let mock = Mock::given(method("DELETE"));
1381 self.mock_endpoint(mock, RoomDeleteThreadSubscriptionEndpoint::default())
1382 .expect_default_access_token()
1383 }
1384
1385 /// Create a prebuilt mock for the endpoint used to enable a push rule.
1386 pub fn mock_enable_push_rule(
1387 &self,
1388 kind: RuleKind,
1389 rule_id: impl AsRef<str>,
1390 ) -> MockEndpoint<'_, EnablePushRuleEndpoint> {
1391 let rule_id = rule_id.as_ref();
1392 let mock = Mock::given(method("PUT")).and(path_regex(format!(
1393 "^/_matrix/client/v3/pushrules/global/{kind}/{rule_id}/enabled",
1394 )));
1395 self.mock_endpoint(mock, EnablePushRuleEndpoint).expect_default_access_token()
1396 }
1397
1398 /// Create a prebuilt mock for the endpoint used to set push rules actions.
1399 pub fn mock_set_push_rules_actions(
1400 &self,
1401 kind: RuleKind,
1402 rule_id: PushRuleIdSpec<'_>,
1403 ) -> MockEndpoint<'_, SetPushRulesActionsEndpoint> {
1404 let rule_id = rule_id.to_path();
1405 let mock = Mock::given(method("PUT")).and(path_regex(format!(
1406 "^/_matrix/client/v3/pushrules/global/{kind}/{rule_id}/actions",
1407 )));
1408 self.mock_endpoint(mock, SetPushRulesActionsEndpoint).expect_default_access_token()
1409 }
1410
1411 /// Create a prebuilt mock for the endpoint used to set push rules.
1412 pub fn mock_set_push_rules(
1413 &self,
1414 kind: RuleKind,
1415 rule_id: PushRuleIdSpec<'_>,
1416 ) -> MockEndpoint<'_, SetPushRulesEndpoint> {
1417 let rule_id = rule_id.to_path();
1418 let mock = Mock::given(method("PUT"))
1419 .and(path_regex(format!("^/_matrix/client/v3/pushrules/global/{kind}/{rule_id}$",)));
1420 self.mock_endpoint(mock, SetPushRulesEndpoint).expect_default_access_token()
1421 }
1422
1423 /// Create a prebuilt mock for the endpoint used to delete push rules.
1424 pub fn mock_delete_push_rules(
1425 &self,
1426 kind: RuleKind,
1427 rule_id: PushRuleIdSpec<'_>,
1428 ) -> MockEndpoint<'_, DeletePushRulesEndpoint> {
1429 let rule_id = rule_id.to_path();
1430 let mock = Mock::given(method("DELETE"))
1431 .and(path_regex(format!("^/_matrix/client/v3/pushrules/global/{kind}/{rule_id}$",)));
1432 self.mock_endpoint(mock, DeletePushRulesEndpoint).expect_default_access_token()
1433 }
1434
1435 /// Create a prebuilt mock for the federation version endpoint.
1436 pub fn mock_federation_version(&self) -> MockEndpoint<'_, FederationVersionEndpoint> {
1437 let mock = Mock::given(method("GET")).and(path("/_matrix/federation/v1/version"));
1438 self.mock_endpoint(mock, FederationVersionEndpoint)
1439 }
1440
1441 /// Create a prebuilt mock for the endpoint used to get all thread
1442 /// subscriptions across all rooms.
1443 pub fn mock_get_thread_subscriptions(
1444 &self,
1445 ) -> MockEndpoint<'_, GetThreadSubscriptionsEndpoint> {
1446 let mock = Mock::given(method("GET"))
1447 .and(path_regex(r"^/_matrix/client/unstable/io.element.msc4308/thread_subscriptions$"));
1448 self.mock_endpoint(mock, GetThreadSubscriptionsEndpoint::default())
1449 .expect_default_access_token()
1450 }
1451
1452 /// Create a prebuilt mock for the endpoint used to retrieve a space tree
1453 pub fn mock_get_hierarchy(&self) -> MockEndpoint<'_, GetHierarchyEndpoint> {
1454 let mock =
1455 Mock::given(method("GET")).and(path_regex(r"^/_matrix/client/v1/rooms/.*/hierarchy"));
1456 self.mock_endpoint(mock, GetHierarchyEndpoint).expect_default_access_token()
1457 }
1458}
1459
1460/// A specification for a push rule ID.
1461pub enum PushRuleIdSpec<'a> {
1462 /// A precise rule ID.
1463 Some(&'a str),
1464 /// Any rule ID should match.
1465 Any,
1466}
1467
1468impl<'a> PushRuleIdSpec<'a> {
1469 /// Convert this [`PushRuleIdSpec`] to a path.
1470 pub fn to_path(&self) -> &str {
1471 match self {
1472 PushRuleIdSpec::Some(id) => id,
1473 PushRuleIdSpec::Any => "[^/]*",
1474 }
1475 }
1476}
1477
1478/// Parameter to [`MatrixMockServer::sync_room`].
1479pub enum AnyRoomBuilder {
1480 /// A room we've been invited to.
1481 Invited(InvitedRoomBuilder),
1482 /// A room we've joined.
1483 Joined(JoinedRoomBuilder),
1484 /// A room we've left.
1485 Left(LeftRoomBuilder),
1486 /// A room we've knocked to.
1487 Knocked(KnockedRoomBuilder),
1488}
1489
1490impl AnyRoomBuilder {
1491 /// Get the [`RoomId`] of the room this [`AnyRoomBuilder`] will create.
1492 fn room_id(&self) -> &RoomId {
1493 match self {
1494 AnyRoomBuilder::Invited(r) => r.room_id(),
1495 AnyRoomBuilder::Joined(r) => r.room_id(),
1496 AnyRoomBuilder::Left(r) => r.room_id(),
1497 AnyRoomBuilder::Knocked(r) => r.room_id(),
1498 }
1499 }
1500}
1501
1502impl From<InvitedRoomBuilder> for AnyRoomBuilder {
1503 fn from(val: InvitedRoomBuilder) -> AnyRoomBuilder {
1504 AnyRoomBuilder::Invited(val)
1505 }
1506}
1507
1508impl From<JoinedRoomBuilder> for AnyRoomBuilder {
1509 fn from(val: JoinedRoomBuilder) -> AnyRoomBuilder {
1510 AnyRoomBuilder::Joined(val)
1511 }
1512}
1513
1514impl From<LeftRoomBuilder> for AnyRoomBuilder {
1515 fn from(val: LeftRoomBuilder) -> AnyRoomBuilder {
1516 AnyRoomBuilder::Left(val)
1517 }
1518}
1519
1520impl From<KnockedRoomBuilder> for AnyRoomBuilder {
1521 fn from(val: KnockedRoomBuilder) -> AnyRoomBuilder {
1522 AnyRoomBuilder::Knocked(val)
1523 }
1524}
1525
1526/// The [path percent-encode set] as defined in the WHATWG URL standard + `/`
1527/// since we always encode single segments of the path.
1528///
1529/// [path percent-encode set]: https://url.spec.whatwg.org/#path-percent-encode-set
1530///
1531/// Copied from Ruma:
1532/// https://github.com/ruma/ruma/blob/e4cb409ff3aaa16f31a7fe1e61fee43b2d144f7b/crates/ruma-common/src/percent_encode.rs#L7
1533const PATH_PERCENT_ENCODE_SET: &AsciiSet = &CONTROLS
1534 .add(b' ')
1535 .add(b'"')
1536 .add(b'#')
1537 .add(b'<')
1538 .add(b'>')
1539 .add(b'?')
1540 .add(b'`')
1541 .add(b'{')
1542 .add(b'}')
1543 .add(b'/');
1544
1545fn percent_encoded_path(path: &str) -> String {
1546 percent_encoding::utf8_percent_encode(path, PATH_PERCENT_ENCODE_SET).to_string()
1547}
1548
1549/// A wrapper for a [`Mock`] as well as a [`MockServer`], allowing us to call
1550/// [`Mock::mount`] or [`Mock::mount_as_scoped`] without having to pass the
1551/// [`MockServer`] reference (i.e. call `mount()` instead of `mount(&server)`).
1552pub struct MatrixMock<'a> {
1553 mock: Mock,
1554 server: &'a MockServer,
1555}
1556
1557impl MatrixMock<'_> {
1558 /// Set an expectation on the number of times this [`MatrixMock`] should
1559 /// match in the current test case.
1560 ///
1561 /// Expectations are verified when the server is shutting down: if
1562 /// the expectation is not satisfied, the [`MatrixMockServer`] will panic
1563 /// and the `error_message` is shown.
1564 ///
1565 /// By default, no expectation is set for [`MatrixMock`]s.
1566 pub fn expect<T: Into<Times>>(self, num_calls: T) -> Self {
1567 Self { mock: self.mock.expect(num_calls), ..self }
1568 }
1569
1570 /// Assign a name to your mock.
1571 ///
1572 /// The mock name will be used in error messages (e.g. if the mock
1573 /// expectation is not satisfied) and debug logs to help you identify
1574 /// what failed.
1575 pub fn named(self, name: impl Into<String>) -> Self {
1576 Self { mock: self.mock.named(name), ..self }
1577 }
1578
1579 /// Respond to a response of this endpoint exactly once.
1580 ///
1581 /// After it's been called, subsequent responses will hit the next handler
1582 /// or a 404.
1583 ///
1584 /// Also verifies that it's been called once.
1585 pub fn mock_once(self) -> Self {
1586 Self { mock: self.mock.up_to_n_times(1).expect(1), ..self }
1587 }
1588
1589 /// Makes sure the endpoint is never reached.
1590 pub fn never(self) -> Self {
1591 Self { mock: self.mock.expect(0), ..self }
1592 }
1593
1594 /// Specify an upper limit to the number of times you would like this
1595 /// [`MatrixMock`] to respond to incoming requests that satisfy the
1596 /// conditions imposed by your matchers.
1597 pub fn up_to_n_times(self, num: u64) -> Self {
1598 Self { mock: self.mock.up_to_n_times(num), ..self }
1599 }
1600
1601 /// Mount a [`MatrixMock`] on the attached server.
1602 ///
1603 /// The [`MatrixMock`] will remain active until the [`MatrixMockServer`] is
1604 /// shut down. If you want to control or limit how long your
1605 /// [`MatrixMock`] stays active, check out [`Self::mount_as_scoped`].
1606 pub async fn mount(self) {
1607 self.mock.mount(self.server).await;
1608 }
1609
1610 /// Mount a [`MatrixMock`] as **scoped** on the attached server.
1611 ///
1612 /// When using [`Self::mount`], your [`MatrixMock`]s will be active until
1613 /// the [`MatrixMockServer`] is shut down.
1614 ///
1615 /// When using `mount_as_scoped`, your [`MatrixMock`]s will be active as
1616 /// long as the returned [`MockGuard`] is not dropped.
1617 ///
1618 /// When the returned [`MockGuard`] is dropped, [`MatrixMockServer`] will
1619 /// verify that the expectations set on the scoped [`MatrixMock`] were
1620 /// verified - if not, it will panic.
1621 pub async fn mount_as_scoped(self) -> MockGuard {
1622 self.mock.mount_as_scoped(self.server).await
1623 }
1624}
1625
1626/// Generic mocked endpoint, with useful common helpers.
1627pub struct MockEndpoint<'a, T> {
1628 server: &'a MockServer,
1629 mock: MockBuilder,
1630 endpoint: T,
1631 expected_access_token: ExpectedAccessToken,
1632}
1633
1634impl<'a, T> MockEndpoint<'a, T> {
1635 fn new(server: &'a MockServer, mock: MockBuilder, endpoint: T) -> Self {
1636 Self { server, mock, endpoint, expected_access_token: ExpectedAccessToken::None }
1637 }
1638
1639 /// Expect authentication with the default access token on this endpoint.
1640 pub fn expect_default_access_token(mut self) -> Self {
1641 self.expected_access_token = ExpectedAccessToken::Default;
1642 self
1643 }
1644
1645 /// Expect authentication with the given access token on this endpoint.
1646 pub fn expect_access_token(mut self, access_token: &'static str) -> Self {
1647 self.expected_access_token = ExpectedAccessToken::Custom(access_token);
1648 self
1649 }
1650
1651 /// Don't expect authentication with an access token on this endpoint.
1652 ///
1653 /// This should be used to override the default behavior of the endpoint,
1654 /// when the access token is unknown for example.
1655 pub fn do_not_expect_access_token(mut self) -> Self {
1656 self.expected_access_token = ExpectedAccessToken::None;
1657 self
1658 }
1659
1660 /// Specify how to respond to a query (viz., like
1661 /// [`MockBuilder::respond_with`] does), when other predefined responses
1662 /// aren't sufficient.
1663 ///
1664 /// # Examples
1665 ///
1666 /// ```
1667 /// # tokio_test::block_on(async {
1668 /// use matrix_sdk::{ruma::{room_id, event_id}, test_utils::mocks::MatrixMockServer};
1669 /// use serde_json::json;
1670 /// use wiremock::ResponseTemplate;
1671 ///
1672 /// let mock_server = MatrixMockServer::new().await;
1673 /// let client = mock_server.client_builder().build().await;
1674 ///
1675 /// mock_server.mock_room_state_encryption().plain().mount().await;
1676 ///
1677 /// let room = mock_server
1678 /// .sync_joined_room(&client, room_id!("!room_id:localhost"))
1679 /// .await;
1680 ///
1681 /// let event_id = event_id!("$some_id");
1682 /// mock_server
1683 /// .mock_room_send()
1684 /// .respond_with(
1685 /// ResponseTemplate::new(429)
1686 /// .insert_header("Retry-After", "100")
1687 /// .set_body_json(json!({
1688 /// "errcode": "M_LIMIT_EXCEEDED",
1689 /// "custom_field": "with custom data",
1690 /// })))
1691 /// .expect(1)
1692 /// .mount()
1693 /// .await;
1694 ///
1695 /// room
1696 /// .send_raw("m.room.message", json!({ "body": "Hello world" }))
1697 /// .await
1698 /// .expect_err("The sending of the event should fail");
1699 /// # anyhow::Ok(()) });
1700 /// ```
1701 pub fn respond_with<R: Respond + 'static>(self, func: R) -> MatrixMock<'a> {
1702 let mock = self
1703 .expected_access_token
1704 .maybe_match_authorization_header(self.mock)
1705 .respond_with(func);
1706 MatrixMock { mock, server: self.server }
1707 }
1708
1709 /// Returns a send endpoint that emulates a transient failure, i.e responds
1710 /// with error 500.
1711 ///
1712 /// # Examples
1713 /// ```
1714 /// # tokio_test::block_on(async {
1715 /// use matrix_sdk::{ruma::{room_id, event_id}, test_utils::mocks::MatrixMockServer};
1716 /// use serde_json::json;
1717 ///
1718 /// let mock_server = MatrixMockServer::new().await;
1719 /// let client = mock_server.client_builder().build().await;
1720 ///
1721 /// mock_server.mock_room_state_encryption().plain().mount().await;
1722 ///
1723 /// let room = mock_server
1724 /// .sync_joined_room(&client, room_id!("!room_id:localhost"))
1725 /// .await;
1726 ///
1727 /// mock_server
1728 /// .mock_room_send()
1729 /// .error500()
1730 /// .expect(1)
1731 /// .mount()
1732 /// .await;
1733 ///
1734 /// room
1735 /// .send_raw("m.room.message", json!({ "body": "Hello world" }))
1736 /// .await.expect_err("The sending of the event should have failed");
1737 /// # anyhow::Ok(()) });
1738 /// ```
1739 pub fn error500(self) -> MatrixMock<'a> {
1740 self.respond_with(ResponseTemplate::new(500))
1741 }
1742
1743 /// Internal helper to return an `{ event_id }` JSON struct along with a 200
1744 /// ok response.
1745 fn ok_with_event_id(self, event_id: OwnedEventId) -> MatrixMock<'a> {
1746 self.respond_with(ResponseTemplate::new(200).set_body_json(json!({ "event_id": event_id })))
1747 }
1748
1749 /// Internal helper to return a 200 OK response with an empty JSON object in
1750 /// the body.
1751 fn ok_empty_json(self) -> MatrixMock<'a> {
1752 self.respond_with(ResponseTemplate::new(200).set_body_json(json!({})))
1753 }
1754
1755 /// Returns an endpoint that emulates a permanent failure error (e.g. event
1756 /// is too large).
1757 ///
1758 /// # Examples
1759 /// ```
1760 /// # tokio_test::block_on(async {
1761 /// use matrix_sdk::{ruma::{room_id, event_id}, test_utils::mocks::MatrixMockServer};
1762 /// use serde_json::json;
1763 ///
1764 /// let mock_server = MatrixMockServer::new().await;
1765 /// let client = mock_server.client_builder().build().await;
1766 ///
1767 /// mock_server.mock_room_state_encryption().plain().mount().await;
1768 ///
1769 /// let room = mock_server
1770 /// .sync_joined_room(&client, room_id!("!room_id:localhost"))
1771 /// .await;
1772 ///
1773 /// mock_server
1774 /// .mock_room_send()
1775 /// .error_too_large()
1776 /// .expect(1)
1777 /// .mount()
1778 /// .await;
1779 ///
1780 /// room
1781 /// .send_raw("m.room.message", json!({ "body": "Hello world" }))
1782 /// .await.expect_err("The sending of the event should have failed");
1783 /// # anyhow::Ok(()) });
1784 /// ```
1785 pub fn error_too_large(self) -> MatrixMock<'a> {
1786 self.respond_with(ResponseTemplate::new(413).set_body_json(json!({
1787 // From https://spec.matrix.org/v1.10/client-server-api/#standard-error-response
1788 "errcode": "M_TOO_LARGE",
1789 })))
1790 }
1791}
1792
1793/// The access token to expect on an endpoint.
1794enum ExpectedAccessToken {
1795 /// We don't expect an access token.
1796 None,
1797
1798 /// We expect the default access token.
1799 Default,
1800
1801 /// We expect the given access token.
1802 Custom(&'static str),
1803}
1804
1805impl ExpectedAccessToken {
1806 /// Match an `Authorization` header on the given mock if one is expected.
1807 fn maybe_match_authorization_header(&self, mock: MockBuilder) -> MockBuilder {
1808 let token = match self {
1809 Self::None => return mock,
1810 Self::Default => "1234",
1811 Self::Custom(token) => token,
1812 };
1813 mock.and(header(http::header::AUTHORIZATION, format!("Bearer {token}")))
1814 }
1815}
1816
1817/// A prebuilt mock for sending a message like event in a room.
1818pub struct RoomSendEndpoint;
1819
1820impl<'a> MockEndpoint<'a, RoomSendEndpoint> {
1821 /// Ensures that the body of the request is a superset of the provided
1822 /// `body` parameter.
1823 ///
1824 /// # Examples
1825 /// ```
1826 /// # tokio_test::block_on(async {
1827 /// use matrix_sdk::{
1828 /// ruma::{room_id, event_id, events::room::message::RoomMessageEventContent},
1829 /// test_utils::mocks::MatrixMockServer
1830 /// };
1831 /// use serde_json::json;
1832 ///
1833 /// let mock_server = MatrixMockServer::new().await;
1834 /// let client = mock_server.client_builder().build().await;
1835 ///
1836 /// mock_server.mock_room_state_encryption().plain().mount().await;
1837 ///
1838 /// let room = mock_server
1839 /// .sync_joined_room(&client, room_id!("!room_id:localhost"))
1840 /// .await;
1841 ///
1842 /// let event_id = event_id!("$some_id");
1843 /// mock_server
1844 /// .mock_room_send()
1845 /// .body_matches_partial_json(json!({
1846 /// "body": "Hello world",
1847 /// }))
1848 /// .ok(event_id)
1849 /// .expect(1)
1850 /// .mount()
1851 /// .await;
1852 ///
1853 /// let content = RoomMessageEventContent::text_plain("Hello world");
1854 /// let response = room.send(content).await?;
1855 ///
1856 /// assert_eq!(
1857 /// event_id,
1858 /// response.event_id,
1859 /// "The event ID we mocked should match the one we received when we sent the event"
1860 /// );
1861 /// # anyhow::Ok(()) });
1862 /// ```
1863 pub fn body_matches_partial_json(self, body: Value) -> Self {
1864 Self { mock: self.mock.and(body_partial_json(body)), ..self }
1865 }
1866
1867 /// Ensures that the send endpoint request uses a specific event type.
1868 ///
1869 /// # Examples
1870 ///
1871 /// see also [`MatrixMockServer::mock_room_send`] for more context.
1872 ///
1873 /// ```
1874 /// # tokio_test::block_on(async {
1875 /// use matrix_sdk::{ruma::{room_id, event_id}, test_utils::mocks::MatrixMockServer};
1876 /// use serde_json::json;
1877 ///
1878 /// let mock_server = MatrixMockServer::new().await;
1879 /// let client = mock_server.client_builder().build().await;
1880 ///
1881 /// mock_server.mock_room_state_encryption().plain().mount().await;
1882 ///
1883 /// let room = mock_server
1884 /// .sync_joined_room(&client, room_id!("!room_id:localhost"))
1885 /// .await;
1886 ///
1887 /// let event_id = event_id!("$some_id");
1888 /// mock_server
1889 /// .mock_room_send()
1890 /// .for_type("m.room.message".into())
1891 /// .ok(event_id)
1892 /// .expect(1)
1893 /// .mount()
1894 /// .await;
1895 ///
1896 /// let response_not_mocked = room.send_raw("m.room.reaction", json!({ "body": "Hello world" })).await;
1897 /// // The `m.room.reaction` event type should not be mocked by the server.
1898 /// assert!(response_not_mocked.is_err());
1899 ///
1900 /// let response = room.send_raw("m.room.message", json!({ "body": "Hello world" })).await?;
1901 /// // The `m.room.message` event type should be mocked by the server.
1902 /// assert_eq!(
1903 /// event_id,
1904 /// response.event_id,
1905 /// "The event ID we mocked should match the one we received when we sent the event"
1906 /// );
1907 /// # anyhow::Ok(()) });
1908 /// ```
1909 pub fn for_type(self, event_type: MessageLikeEventType) -> Self {
1910 Self {
1911 // Note: we already defined a path when constructing the mock builder, but this one
1912 // ought to be more specialized.
1913 mock: self
1914 .mock
1915 .and(path_regex(format!(r"^/_matrix/client/v3/rooms/.*/send/{event_type}",))),
1916 ..self
1917 }
1918 }
1919
1920 /// Ensures the event was sent as a delayed event.
1921 ///
1922 /// See also [the MSC](https://github.com/matrix-org/matrix-spec-proposals/pull/4140).
1923 ///
1924 /// Note: works with *any* room.
1925 ///
1926 /// # Examples
1927 ///
1928 /// see also [`MatrixMockServer::mock_room_send`] for more context.
1929 ///
1930 /// ```
1931 /// # tokio_test::block_on(async {
1932 /// use matrix_sdk::{
1933 /// ruma::{
1934 /// api::client::delayed_events::{delayed_message_event, DelayParameters},
1935 /// events::{message::MessageEventContent, AnyMessageLikeEventContent},
1936 /// room_id,
1937 /// time::Duration,
1938 /// TransactionId,
1939 /// },
1940 /// test_utils::mocks::MatrixMockServer,
1941 /// };
1942 /// use serde_json::json;
1943 /// use wiremock::ResponseTemplate;
1944 ///
1945 /// let mock_server = MatrixMockServer::new().await;
1946 /// let client = mock_server.client_builder().build().await;
1947 ///
1948 /// mock_server.mock_room_state_encryption().plain().mount().await;
1949 ///
1950 /// let room = mock_server.sync_joined_room(&client, room_id!("!room_id:localhost")).await;
1951 ///
1952 /// mock_server
1953 /// .mock_room_send()
1954 /// .match_delayed_event(Duration::from_millis(500))
1955 /// .respond_with(ResponseTemplate::new(200).set_body_json(json!({"delay_id":"$some_id"})))
1956 /// .mock_once()
1957 /// .mount()
1958 /// .await;
1959 ///
1960 /// let response_not_mocked =
1961 /// room.send_raw("m.room.message", json!({ "body": "Hello world" })).await;
1962 ///
1963 /// // A non delayed event should not be mocked by the server.
1964 /// assert!(response_not_mocked.is_err());
1965 ///
1966 /// let r = delayed_message_event::unstable::Request::new(
1967 /// room.room_id().to_owned(),
1968 /// TransactionId::new(),
1969 /// DelayParameters::Timeout { timeout: Duration::from_millis(500) },
1970 /// &AnyMessageLikeEventContent::Message(MessageEventContent::plain("hello world")),
1971 /// )
1972 /// .unwrap();
1973 ///
1974 /// let response = room.client().send(r).await.unwrap();
1975 /// // The delayed `m.room.message` event type should be mocked by the server.
1976 /// assert_eq!("$some_id", response.delay_id);
1977 /// # anyhow::Ok(()) });
1978 /// ```
1979 pub fn match_delayed_event(self, delay: Duration) -> Self {
1980 Self {
1981 mock: self
1982 .mock
1983 .and(query_param("org.matrix.msc4140.delay", delay.as_millis().to_string())),
1984 ..self
1985 }
1986 }
1987
1988 /// Returns a send endpoint that emulates success, i.e. the event has been
1989 /// sent with the given event id.
1990 ///
1991 /// # Examples
1992 /// ```
1993 /// # tokio_test::block_on(async {
1994 /// use matrix_sdk::{ruma::{room_id, event_id}, test_utils::mocks::MatrixMockServer};
1995 /// use serde_json::json;
1996 ///
1997 /// let mock_server = MatrixMockServer::new().await;
1998 /// let client = mock_server.client_builder().build().await;
1999 ///
2000 /// mock_server.mock_room_state_encryption().plain().mount().await;
2001 ///
2002 /// let room = mock_server
2003 /// .sync_joined_room(&client, room_id!("!room_id:localhost"))
2004 /// .await;
2005 ///
2006 /// let event_id = event_id!("$some_id");
2007 /// let send_guard = mock_server
2008 /// .mock_room_send()
2009 /// .ok(event_id)
2010 /// .expect(1)
2011 /// .mount_as_scoped()
2012 /// .await;
2013 ///
2014 /// let response = room.send_raw("m.room.message", json!({ "body": "Hello world" })).await?;
2015 ///
2016 /// assert_eq!(
2017 /// event_id,
2018 /// response.event_id,
2019 /// "The event ID we mocked should match the one we received when we sent the event"
2020 /// );
2021 /// # anyhow::Ok(()) });
2022 /// ```
2023 pub fn ok(self, returned_event_id: impl Into<OwnedEventId>) -> MatrixMock<'a> {
2024 self.ok_with_event_id(returned_event_id.into())
2025 }
2026
2027 /// Returns a send endpoint that emulates success, i.e. the event has been
2028 /// sent with the given event id.
2029 ///
2030 /// The sent event is captured and can be accessed using the returned
2031 /// [`Receiver`]. The [`Receiver`] is valid only for a send call. The given
2032 /// `event_sender` are added to the event JSON.
2033 ///
2034 /// # Examples
2035 ///
2036 /// ```no_run
2037 /// # tokio_test::block_on(async {
2038 /// use matrix_sdk::{
2039 /// ruma::{
2040 /// event_id, events::room::message::RoomMessageEventContent, room_id,
2041 /// },
2042 /// test_utils::mocks::MatrixMockServer,
2043 /// };
2044 /// use matrix_sdk_test::JoinedRoomBuilder;
2045 ///
2046 /// let room_id = room_id!("!room_id:localhost");
2047 /// let event_id = event_id!("$some_id");
2048 ///
2049 /// let server = MatrixMockServer::new().await;
2050 /// let client = server.client_builder().build().await;
2051 ///
2052 /// let user_id = client.user_id().expect("We should have a user ID by now");
2053 ///
2054 /// let (receiver, mock) =
2055 /// server.mock_room_send().ok_with_capture(event_id, user_id);
2056 ///
2057 /// server
2058 /// .mock_sync()
2059 /// .ok_and_run(&client, |builder| {
2060 /// builder.add_joined_room(JoinedRoomBuilder::new(room_id));
2061 /// })
2062 /// .await;
2063 ///
2064 /// // Mock any additional endpoints that might be needed to send the message.
2065 ///
2066 /// let room = client
2067 /// .get_room(room_id)
2068 /// .expect("We should have access to our room now");
2069 ///
2070 /// let event_id = room
2071 /// .send(RoomMessageEventContent::text_plain("It's a secret to everybody"))
2072 /// .await
2073 /// .expect("We should be able to send an initial message")
2074 /// .event_id;
2075 ///
2076 /// let event = receiver.await?;
2077 /// # anyhow::Ok(()) });
2078 /// ```
2079 pub fn ok_with_capture(
2080 self,
2081 returned_event_id: impl Into<OwnedEventId>,
2082 event_sender: impl Into<OwnedUserId>,
2083 ) -> (Receiver<Raw<AnySyncTimelineEvent>>, MatrixMock<'a>) {
2084 let event_id = returned_event_id.into();
2085 let event_sender = event_sender.into();
2086
2087 let (sender, receiver) = oneshot::channel();
2088 let sender = Arc::new(Mutex::new(Some(sender)));
2089
2090 let ret = self.respond_with(move |request: &Request| {
2091 if let Some(sender) = sender.lock().unwrap().take() {
2092 let uri = &request.url;
2093 let path_segments = uri.path_segments();
2094 let maybe_event_type = path_segments.and_then(|mut s| s.nth_back(1));
2095 let event_type = maybe_event_type
2096 .as_ref()
2097 .map(|&e| e.to_owned())
2098 .unwrap_or("m.room.message".to_owned());
2099
2100 let body: Value =
2101 request.body_json().expect("The received body should be valid JSON");
2102
2103 let event = json!({
2104 "event_id": event_id.clone(),
2105 "sender": event_sender,
2106 "type": event_type,
2107 "origin_server_ts": MilliSecondsSinceUnixEpoch::now(),
2108 "content": body,
2109 });
2110
2111 let event: Raw<AnySyncTimelineEvent> = from_value(event)
2112 .expect("We should be able to create a raw event from the content");
2113
2114 sender.send(event).expect("We should be able to send the event to the receiver");
2115 }
2116
2117 ResponseTemplate::new(200).set_body_json(json!({ "event_id": event_id.clone() }))
2118 });
2119
2120 (receiver, ret)
2121 }
2122}
2123
2124/// A prebuilt mock for sending a state event in a room.
2125#[derive(Default)]
2126pub struct RoomSendStateEndpoint {
2127 state_key: Option<String>,
2128 event_type: Option<StateEventType>,
2129}
2130
2131impl<'a> MockEndpoint<'a, RoomSendStateEndpoint> {
2132 fn generate_path_regexp(endpoint: &RoomSendStateEndpoint) -> String {
2133 format!(
2134 r"^/_matrix/client/v3/rooms/.*/state/{}/{}",
2135 endpoint.event_type.as_ref().map_or_else(|| ".*".to_owned(), |t| t.to_string()),
2136 endpoint.state_key.as_ref().map_or_else(|| ".*".to_owned(), |k| k.to_string())
2137 )
2138 }
2139
2140 /// Ensures that the body of the request is a superset of the provided
2141 /// `body` parameter.
2142 ///
2143 /// # Examples
2144 /// ```
2145 /// # tokio_test::block_on(async {
2146 /// use matrix_sdk::{
2147 /// ruma::{
2148 /// room_id, event_id,
2149 /// events::room::power_levels::RoomPowerLevelsEventContent,
2150 /// room_version_rules::AuthorizationRules
2151 /// },
2152 /// test_utils::mocks::MatrixMockServer
2153 /// };
2154 /// use serde_json::json;
2155 ///
2156 /// let mock_server = MatrixMockServer::new().await;
2157 /// let client = mock_server.client_builder().build().await;
2158 ///
2159 /// mock_server.mock_room_state_encryption().plain().mount().await;
2160 ///
2161 /// let room = mock_server
2162 /// .sync_joined_room(&client, room_id!("!room_id:localhost"))
2163 /// .await;
2164 ///
2165 /// let event_id = event_id!("$some_id");
2166 /// mock_server
2167 /// .mock_room_send_state()
2168 /// .body_matches_partial_json(json!({
2169 /// "redact": 51,
2170 /// }))
2171 /// .ok(event_id)
2172 /// .expect(1)
2173 /// .mount()
2174 /// .await;
2175 ///
2176 /// let mut content = RoomPowerLevelsEventContent::new(&AuthorizationRules::V1);
2177 /// // Update the power level to a non default value.
2178 /// // Otherwise it will be skipped from serialization.
2179 /// content.redact = 51.into();
2180 ///
2181 /// let response = room.send_state_event(content).await?;
2182 ///
2183 /// assert_eq!(
2184 /// event_id,
2185 /// response.event_id,
2186 /// "The event ID we mocked should match the one we received when we sent the event"
2187 /// );
2188 /// # anyhow::Ok(()) });
2189 /// ```
2190 pub fn body_matches_partial_json(self, body: Value) -> Self {
2191 Self { mock: self.mock.and(body_partial_json(body)), ..self }
2192 }
2193
2194 /// Ensures that the send endpoint request uses a specific event type.
2195 ///
2196 /// Note: works with *any* room.
2197 ///
2198 /// # Examples
2199 ///
2200 /// see also [`MatrixMockServer::mock_room_send`] for more context.
2201 ///
2202 /// ```
2203 /// # tokio_test::block_on(async {
2204 /// use matrix_sdk::{
2205 /// ruma::{
2206 /// event_id,
2207 /// events::room::{
2208 /// create::RoomCreateEventContent, power_levels::RoomPowerLevelsEventContent,
2209 /// },
2210 /// events::StateEventType,
2211 /// room_id,
2212 /// room_version_rules::AuthorizationRules,
2213 /// },
2214 /// test_utils::mocks::MatrixMockServer,
2215 /// };
2216 ///
2217 /// let mock_server = MatrixMockServer::new().await;
2218 /// let client = mock_server.client_builder().build().await;
2219 ///
2220 /// mock_server.mock_room_state_encryption().plain().mount().await;
2221 ///
2222 /// let room = mock_server.sync_joined_room(&client, room_id!("!room_id:localhost")).await;
2223 ///
2224 /// let event_id = event_id!("$some_id");
2225 ///
2226 /// mock_server
2227 /// .mock_room_send_state()
2228 /// .for_type(StateEventType::RoomPowerLevels)
2229 /// .ok(event_id)
2230 /// .expect(1)
2231 /// .mount()
2232 /// .await;
2233 ///
2234 /// let response_not_mocked = room.send_state_event(RoomCreateEventContent::new_v11()).await;
2235 /// // The `m.room.reaction` event type should not be mocked by the server.
2236 /// assert!(response_not_mocked.is_err());
2237 ///
2238 /// let response = room.send_state_event(RoomPowerLevelsEventContent::new(&AuthorizationRules::V1)).await?;
2239 /// // The `m.room.message` event type should be mocked by the server.
2240 /// assert_eq!(
2241 /// event_id, response.event_id,
2242 /// "The event ID we mocked should match the one we received when we sent the event"
2243 /// );
2244 ///
2245 /// # anyhow::Ok(()) });
2246 /// ```
2247 pub fn for_type(mut self, event_type: StateEventType) -> Self {
2248 self.endpoint.event_type = Some(event_type);
2249 // Note: we may have already defined a path, but this one ought to be more
2250 // specialized (unless for_key/for_type were called multiple times).
2251 Self { mock: self.mock.and(path_regex(Self::generate_path_regexp(&self.endpoint))), ..self }
2252 }
2253
2254 /// Ensures the event was sent as a delayed event.
2255 ///
2256 /// See also [the MSC](https://github.com/matrix-org/matrix-spec-proposals/pull/4140).
2257 ///
2258 /// Note: works with *any* room.
2259 ///
2260 /// # Examples
2261 ///
2262 /// see also [`MatrixMockServer::mock_room_send`] for more context.
2263 ///
2264 /// ```
2265 /// # tokio_test::block_on(async {
2266 /// use matrix_sdk::{
2267 /// ruma::{
2268 /// api::client::delayed_events::{delayed_state_event, DelayParameters},
2269 /// events::{room::create::RoomCreateEventContent, AnyStateEventContent},
2270 /// room_id,
2271 /// time::Duration,
2272 /// },
2273 /// test_utils::mocks::MatrixMockServer,
2274 /// };
2275 /// use wiremock::ResponseTemplate;
2276 /// use serde_json::json;
2277 ///
2278 /// let mock_server = MatrixMockServer::new().await;
2279 /// let client = mock_server.client_builder().build().await;
2280 ///
2281 /// mock_server.mock_room_state_encryption().plain().mount().await;
2282 ///
2283 /// let room = mock_server.sync_joined_room(&client, room_id!("!room_id:localhost")).await;
2284 ///
2285 /// mock_server
2286 /// .mock_room_send_state()
2287 /// .match_delayed_event(Duration::from_millis(500))
2288 /// .respond_with(ResponseTemplate::new(200).set_body_json(json!({"delay_id":"$some_id"})))
2289 /// .mock_once()
2290 /// .mount()
2291 /// .await;
2292 ///
2293 /// let response_not_mocked = room.send_state_event(RoomCreateEventContent::new_v11()).await;
2294 /// // A non delayed event should not be mocked by the server.
2295 /// assert!(response_not_mocked.is_err());
2296 ///
2297 /// let r = delayed_state_event::unstable::Request::new(
2298 /// room.room_id().to_owned(),
2299 /// "".to_owned(),
2300 /// DelayParameters::Timeout { timeout: Duration::from_millis(500) },
2301 /// &AnyStateEventContent::RoomCreate(RoomCreateEventContent::new_v11()),
2302 /// )
2303 /// .unwrap();
2304 /// let response = room.client().send(r).await.unwrap();
2305 /// // The delayed `m.room.message` event type should be mocked by the server.
2306 /// assert_eq!("$some_id", response.delay_id);
2307 ///
2308 /// # anyhow::Ok(()) });
2309 /// ```
2310 pub fn match_delayed_event(self, delay: Duration) -> Self {
2311 Self {
2312 mock: self
2313 .mock
2314 .and(query_param("org.matrix.msc4140.delay", delay.as_millis().to_string())),
2315 ..self
2316 }
2317 }
2318
2319 ///
2320 /// ```
2321 /// # tokio_test::block_on(async {
2322 /// use matrix_sdk::{
2323 /// ruma::{
2324 /// event_id,
2325 /// events::{call::member::CallMemberEventContent, AnyStateEventContent},
2326 /// room_id,
2327 /// },
2328 /// test_utils::mocks::MatrixMockServer,
2329 /// };
2330 ///
2331 /// let mock_server = MatrixMockServer::new().await;
2332 /// let client = mock_server.client_builder().build().await;
2333 ///
2334 /// mock_server.mock_room_state_encryption().plain().mount().await;
2335 ///
2336 /// let room = mock_server.sync_joined_room(&client, room_id!("!room_id:localhost")).await;
2337 ///
2338 /// let event_id = event_id!("$some_id");
2339 ///
2340 /// mock_server
2341 /// .mock_room_send_state()
2342 /// .for_key("my_key".to_owned())
2343 /// .ok(event_id)
2344 /// .expect(1)
2345 /// .mount()
2346 /// .await;
2347 ///
2348 /// let response_not_mocked = room
2349 /// .send_state_event_for_key(
2350 /// "",
2351 /// AnyStateEventContent::CallMember(CallMemberEventContent::new_empty(None)),
2352 /// )
2353 /// .await;
2354 /// // The `m.room.reaction` event type should not be mocked by the server.
2355 /// assert!(response_not_mocked.is_err());
2356 ///
2357 /// let response = room
2358 /// .send_state_event_for_key(
2359 /// "my_key",
2360 /// AnyStateEventContent::CallMember(CallMemberEventContent::new_empty(None)),
2361 /// )
2362 /// .await
2363 /// .unwrap();
2364 ///
2365 /// // The `m.room.message` event type should be mocked by the server.
2366 /// assert_eq!(
2367 /// event_id, response.event_id,
2368 /// "The event ID we mocked should match the one we received when we sent the event"
2369 /// );
2370 /// # anyhow::Ok(()) });
2371 /// ```
2372 pub fn for_key(mut self, state_key: String) -> Self {
2373 self.endpoint.state_key = Some(state_key);
2374 // Note: we may have already defined a path, but this one ought to be more
2375 // specialized (unless for_key/for_type were called multiple times).
2376 Self { mock: self.mock.and(path_regex(Self::generate_path_regexp(&self.endpoint))), ..self }
2377 }
2378
2379 /// Returns a send endpoint that emulates success, i.e. the event has been
2380 /// sent with the given event id.
2381 ///
2382 /// # Examples
2383 /// ```
2384 /// # tokio_test::block_on(async {
2385 /// use matrix_sdk::{ruma::{room_id, event_id}, test_utils::mocks::MatrixMockServer};
2386 /// use serde_json::json;
2387 ///
2388 /// let mock_server = MatrixMockServer::new().await;
2389 /// let client = mock_server.client_builder().build().await;
2390 ///
2391 /// mock_server.mock_room_state_encryption().plain().mount().await;
2392 ///
2393 /// let room = mock_server
2394 /// .sync_joined_room(&client, room_id!("!room_id:localhost"))
2395 /// .await;
2396 ///
2397 /// let event_id = event_id!("$some_id");
2398 /// let send_guard = mock_server
2399 /// .mock_room_send_state()
2400 /// .ok(event_id)
2401 /// .expect(1)
2402 /// .mount_as_scoped()
2403 /// .await;
2404 ///
2405 /// let response = room.send_state_event_raw("m.room.message", "my_key", json!({ "body": "Hello world" })).await?;
2406 ///
2407 /// assert_eq!(
2408 /// event_id,
2409 /// response.event_id,
2410 /// "The event ID we mocked should match the one we received when we sent the event"
2411 /// );
2412 /// # anyhow::Ok(()) });
2413 /// ```
2414 pub fn ok(self, returned_event_id: impl Into<OwnedEventId>) -> MatrixMock<'a> {
2415 self.ok_with_event_id(returned_event_id.into())
2416 }
2417}
2418
2419/// A prebuilt mock for running sync v2.
2420pub struct SyncEndpoint {
2421 sync_response_builder: Arc<Mutex<SyncResponseBuilder>>,
2422}
2423
2424impl<'a> MockEndpoint<'a, SyncEndpoint> {
2425 /// Expect the given timeout, or lack thereof, in the request.
2426 pub fn timeout(mut self, timeout: Option<Duration>) -> Self {
2427 if let Some(timeout) = timeout {
2428 self.mock = self.mock.and(query_param("timeout", timeout.as_millis().to_string()));
2429 } else {
2430 self.mock = self.mock.and(query_param_is_missing("timeout"));
2431 }
2432
2433 self
2434 }
2435
2436 /// Mocks the sync endpoint, using the given function to generate the
2437 /// response.
2438 pub fn ok<F: FnOnce(&mut SyncResponseBuilder)>(self, func: F) -> MatrixMock<'a> {
2439 let json_response = {
2440 let mut builder = self.endpoint.sync_response_builder.lock().unwrap();
2441 func(&mut builder);
2442 builder.build_json_sync_response()
2443 };
2444
2445 self.respond_with(ResponseTemplate::new(200).set_body_json(json_response))
2446 }
2447
2448 /// Temporarily mocks the sync with the given endpoint and runs a client
2449 /// sync with it.
2450 ///
2451 /// After calling this function, the sync endpoint isn't mocked anymore.
2452 ///
2453 /// # Examples
2454 ///
2455 /// ```
2456 /// # tokio_test::block_on(async {
2457 /// use matrix_sdk::{ruma::room_id, test_utils::mocks::MatrixMockServer};
2458 /// use matrix_sdk_test::JoinedRoomBuilder;
2459 ///
2460 /// // First create the mock server and client pair.
2461 /// let mock_server = MatrixMockServer::new().await;
2462 /// let client = mock_server.client_builder().build().await;
2463 /// let room_id = room_id!("!room_id:localhost");
2464 ///
2465 /// // Let's emulate what `MatrixMockServer::sync_joined_room()` does.
2466 /// mock_server
2467 /// .mock_sync()
2468 /// .ok_and_run(&client, |builder| {
2469 /// builder.add_joined_room(JoinedRoomBuilder::new(room_id));
2470 /// })
2471 /// .await;
2472 ///
2473 /// let room = client
2474 /// .get_room(room_id)
2475 /// .expect("The room should be available after we mocked the sync");
2476 /// # anyhow::Ok(()) });
2477 /// ```
2478 pub async fn ok_and_run<F: FnOnce(&mut SyncResponseBuilder)>(self, client: &Client, func: F) {
2479 let _scope = self.ok(func).mount_as_scoped().await;
2480
2481 let _response = client.sync_once(Default::default()).await.unwrap();
2482 }
2483}
2484
2485/// A prebuilt mock for reading the encryption state of a room.
2486pub struct EncryptionStateEndpoint;
2487
2488impl<'a> MockEndpoint<'a, EncryptionStateEndpoint> {
2489 /// Marks the room as encrypted.
2490 ///
2491 /// # Examples
2492 ///
2493 /// ```
2494 /// # tokio_test::block_on(async {
2495 /// use matrix_sdk::{ruma::room_id, test_utils::mocks::MatrixMockServer};
2496 ///
2497 /// let mock_server = MatrixMockServer::new().await;
2498 /// let client = mock_server.client_builder().build().await;
2499 ///
2500 /// mock_server.mock_room_state_encryption().encrypted().mount().await;
2501 ///
2502 /// let room = mock_server
2503 /// .sync_joined_room(&client, room_id!("!room_id:localhost"))
2504 /// .await;
2505 ///
2506 /// assert!(
2507 /// room.latest_encryption_state().await?.is_encrypted(),
2508 /// "The room should be marked as encrypted."
2509 /// );
2510 /// # anyhow::Ok(()) });
2511 /// ```
2512 pub fn encrypted(self) -> MatrixMock<'a> {
2513 self.respond_with(
2514 ResponseTemplate::new(200).set_body_json(&*test_json::sync_events::ENCRYPTION_CONTENT),
2515 )
2516 }
2517
2518 /// Marks the room as encrypted, opting into experimental state event
2519 /// encryption.
2520 ///
2521 /// # Examples
2522 ///
2523 /// ```
2524 /// # tokio_test::block_on(async {
2525 /// use matrix_sdk::{ruma::room_id, test_utils::mocks::MatrixMockServer};
2526 ///
2527 /// let mock_server = MatrixMockServer::new().await;
2528 /// let client = mock_server.client_builder().build().await;
2529 ///
2530 /// mock_server.mock_room_state_encryption().state_encrypted().mount().await;
2531 ///
2532 /// let room = mock_server
2533 /// .sync_joined_room(&client, room_id!("!room_id:localhost"))
2534 /// .await;
2535 ///
2536 /// assert!(
2537 /// room.latest_encryption_state().await?.is_state_encrypted(),
2538 /// "The room should be marked as state encrypted."
2539 /// );
2540 /// # anyhow::Ok(()) });
2541 #[cfg(feature = "experimental-encrypted-state-events")]
2542 pub fn state_encrypted(self) -> MatrixMock<'a> {
2543 self.respond_with(ResponseTemplate::new(200).set_body_json(
2544 &*test_json::sync_events::ENCRYPTION_WITH_ENCRYPTED_STATE_EVENTS_CONTENT,
2545 ))
2546 }
2547
2548 /// Marks the room as not encrypted.
2549 ///
2550 /// # Examples
2551 ///
2552 /// ```
2553 /// # tokio_test::block_on(async {
2554 /// use matrix_sdk::{ruma::room_id, test_utils::mocks::MatrixMockServer};
2555 ///
2556 /// let mock_server = MatrixMockServer::new().await;
2557 /// let client = mock_server.client_builder().build().await;
2558 ///
2559 /// mock_server.mock_room_state_encryption().plain().mount().await;
2560 ///
2561 /// let room = mock_server
2562 /// .sync_joined_room(&client, room_id!("!room_id:localhost"))
2563 /// .await;
2564 ///
2565 /// assert!(
2566 /// !room.latest_encryption_state().await?.is_encrypted(),
2567 /// "The room should not be marked as encrypted."
2568 /// );
2569 /// # anyhow::Ok(()) });
2570 /// ```
2571 pub fn plain(self) -> MatrixMock<'a> {
2572 self.respond_with(ResponseTemplate::new(404).set_body_json(&*test_json::NOT_FOUND))
2573 }
2574}
2575
2576/// A prebuilt mock for setting the encryption state of a room.
2577pub struct SetEncryptionStateEndpoint;
2578
2579impl<'a> MockEndpoint<'a, SetEncryptionStateEndpoint> {
2580 /// Returns a mock for a successful setting of the encryption state event.
2581 pub fn ok(self, returned_event_id: impl Into<OwnedEventId>) -> MatrixMock<'a> {
2582 self.ok_with_event_id(returned_event_id.into())
2583 }
2584}
2585
2586/// A prebuilt mock for redacting an event in a room.
2587pub struct RoomRedactEndpoint;
2588
2589impl<'a> MockEndpoint<'a, RoomRedactEndpoint> {
2590 /// Returns a redact endpoint that emulates success, i.e. the redaction
2591 /// event has been sent with the given event id.
2592 pub fn ok(self, returned_event_id: impl Into<OwnedEventId>) -> MatrixMock<'a> {
2593 self.ok_with_event_id(returned_event_id.into())
2594 }
2595}
2596
2597/// A prebuilt mock for getting a single event in a room.
2598pub struct RoomEventEndpoint {
2599 room: Option<OwnedRoomId>,
2600 match_event_id: bool,
2601}
2602
2603impl<'a> MockEndpoint<'a, RoomEventEndpoint> {
2604 /// Limits the scope of this mock to a specific room.
2605 pub fn room(mut self, room: impl Into<OwnedRoomId>) -> Self {
2606 self.endpoint.room = Some(room.into());
2607 self
2608 }
2609
2610 /// Whether the mock checks for the event id from the event.
2611 pub fn match_event_id(mut self) -> Self {
2612 self.endpoint.match_event_id = true;
2613 self
2614 }
2615
2616 /// Returns a redact endpoint that emulates success, i.e. the redaction
2617 /// event has been sent with the given event id.
2618 pub fn ok(self, event: TimelineEvent) -> MatrixMock<'a> {
2619 let event_path = if self.endpoint.match_event_id {
2620 let event_id = event.kind.event_id().expect("an event id is required");
2621 // The event id should begin with `$`, which would be taken as the end of the
2622 // regex so we need to escape it
2623 event_id.as_str().replace("$", "\\$")
2624 } else {
2625 // Event is at the end, so no need to add anything.
2626 "".to_owned()
2627 };
2628
2629 let room_path = self.endpoint.room.map_or_else(|| ".*".to_owned(), |room| room.to_string());
2630
2631 let mock = self
2632 .mock
2633 .and(path_regex(format!(r"^/_matrix/client/v3/rooms/{room_path}/event/{event_path}")))
2634 .respond_with(ResponseTemplate::new(200).set_body_json(event.into_raw().json()));
2635 MatrixMock { server: self.server, mock }
2636 }
2637}
2638
2639/// A prebuilt mock for getting a single event with its context in a room.
2640pub struct RoomEventContextEndpoint {
2641 room: Option<OwnedRoomId>,
2642 match_event_id: bool,
2643}
2644
2645impl<'a> MockEndpoint<'a, RoomEventContextEndpoint> {
2646 /// Limits the scope of this mock to a specific room.
2647 pub fn room(mut self, room: impl Into<OwnedRoomId>) -> Self {
2648 self.endpoint.room = Some(room.into());
2649 self
2650 }
2651
2652 /// Whether the mock checks for the event id from the event.
2653 pub fn match_event_id(mut self) -> Self {
2654 self.endpoint.match_event_id = true;
2655 self
2656 }
2657
2658 /// Returns an endpoint that emulates success.
2659 pub fn ok(
2660 self,
2661 event: TimelineEvent,
2662 start: impl Into<String>,
2663 end: impl Into<String>,
2664 state_events: Vec<Raw<AnyStateEvent>>,
2665 ) -> MatrixMock<'a> {
2666 let event_path = if self.endpoint.match_event_id {
2667 let event_id = event.event_id().expect("an event id is required");
2668 // The event id should begin with `$`, which would be taken as the end of the
2669 // regex so we need to escape it
2670 event_id.as_str().replace("$", "\\$")
2671 } else {
2672 // Event is at the end, so no need to add anything.
2673 "".to_owned()
2674 };
2675
2676 let room_path = self.endpoint.room.map_or_else(|| ".*".to_owned(), |room| room.to_string());
2677
2678 let mock = self
2679 .mock
2680 .and(path_regex(format!(r"^/_matrix/client/v3/rooms/{room_path}/context/{event_path}")))
2681 .respond_with(ResponseTemplate::new(200).set_body_json(json!({
2682 "event": event.into_raw().json(),
2683 "end": end.into(),
2684 "start": start.into(),
2685 "state": state_events
2686 })));
2687 MatrixMock { server: self.server, mock }
2688 }
2689}
2690
2691/// A prebuilt mock for the `/messages` endpoint.
2692pub struct RoomMessagesEndpoint;
2693
2694/// A prebuilt mock for getting a room messages in a room.
2695impl<'a> MockEndpoint<'a, RoomMessagesEndpoint> {
2696 /// Expects an optional limit to be set on the request.
2697 pub fn match_limit(self, limit: u32) -> Self {
2698 Self { mock: self.mock.and(query_param("limit", limit.to_string())), ..self }
2699 }
2700
2701 /// Expects an optional `from` to be set on the request.
2702 pub fn match_from(self, from: &str) -> Self {
2703 Self { mock: self.mock.and(query_param("from", from)), ..self }
2704 }
2705
2706 /// Returns a messages endpoint that emulates success, i.e. the messages
2707 /// provided as `response` could be retrieved.
2708 ///
2709 /// Note: pass `chunk` in the correct order: topological for forward
2710 /// pagination, reverse topological for backwards pagination.
2711 pub fn ok(self, response: RoomMessagesResponseTemplate) -> MatrixMock<'a> {
2712 let mut template = ResponseTemplate::new(200).set_body_json(json!({
2713 "start": response.start,
2714 "end": response.end,
2715 "chunk": response.chunk,
2716 "state": response.state,
2717 }));
2718
2719 if let Some(delay) = response.delay {
2720 template = template.set_delay(delay);
2721 }
2722
2723 self.respond_with(template)
2724 }
2725}
2726
2727/// A response to a [`RoomMessagesEndpoint`] query.
2728pub struct RoomMessagesResponseTemplate {
2729 /// The start token for this /messages query.
2730 pub start: String,
2731 /// The end token for this /messages query (previous batch for back
2732 /// paginations, next batch for forward paginations).
2733 pub end: Option<String>,
2734 /// The set of timeline events returned by this query.
2735 pub chunk: Vec<Raw<AnyTimelineEvent>>,
2736 /// The set of state events returned by this query.
2737 pub state: Vec<Raw<AnyStateEvent>>,
2738 /// Optional delay to respond to the query.
2739 pub delay: Option<Duration>,
2740}
2741
2742impl RoomMessagesResponseTemplate {
2743 /// Fill the events returned as part of this response.
2744 pub fn events(mut self, chunk: Vec<impl Into<Raw<AnyTimelineEvent>>>) -> Self {
2745 self.chunk = chunk.into_iter().map(Into::into).collect();
2746 self
2747 }
2748
2749 /// Fill the end token.
2750 pub fn end_token(mut self, token: impl Into<String>) -> Self {
2751 self.end = Some(token.into());
2752 self
2753 }
2754
2755 /// Respond with a given delay to the query.
2756 pub fn with_delay(mut self, delay: Duration) -> Self {
2757 self.delay = Some(delay);
2758 self
2759 }
2760}
2761
2762impl Default for RoomMessagesResponseTemplate {
2763 fn default() -> Self {
2764 Self {
2765 start: "start-token-unused".to_owned(),
2766 end: Default::default(),
2767 chunk: Default::default(),
2768 state: Default::default(),
2769 delay: None,
2770 }
2771 }
2772}
2773
2774/// A prebuilt mock for uploading media.
2775pub struct UploadEndpoint;
2776
2777impl<'a> MockEndpoint<'a, UploadEndpoint> {
2778 /// Expect that the content type matches what's given here.
2779 pub fn expect_mime_type(self, content_type: &str) -> Self {
2780 Self { mock: self.mock.and(header("content-type", content_type)), ..self }
2781 }
2782
2783 /// Returns a upload endpoint that emulates success, i.e. the media has been
2784 /// uploaded to the media server and can be accessed using the given
2785 /// event has been sent with the given [`MxcUri`].
2786 ///
2787 /// The uploaded content is captured and can be accessed using the returned
2788 /// [`Receiver`]. The [`Receiver`] is valid only for a single media
2789 /// upload.
2790 ///
2791 /// # Examples
2792 ///
2793 /// ```no_run
2794 /// # tokio_test::block_on(async {
2795 /// use matrix_sdk::{
2796 /// ruma::{event_id, mxc_uri, room_id},
2797 /// test_utils::mocks::MatrixMockServer,
2798 /// };
2799 ///
2800 /// let mxid = mxc_uri!("mxc://localhost/12345");
2801 ///
2802 /// let server = MatrixMockServer::new().await;
2803 /// let (receiver, upload_mock) = server.mock_upload().ok_with_capture(mxid);
2804 /// let client = server.client_builder().build().await;
2805 ///
2806 /// client.media().upload(&mime::TEXT_PLAIN, vec![1, 2, 3, 4, 5], None).await?;
2807 ///
2808 /// let uploaded = receiver.await?;
2809 ///
2810 /// assert_eq!(uploaded, vec![1, 2, 3, 4, 5]);
2811 /// # anyhow::Ok(()) });
2812 /// ```
2813 pub fn ok_with_capture(self, mxc_id: &MxcUri) -> (Receiver<Vec<u8>>, MatrixMock<'a>) {
2814 let (sender, receiver) = oneshot::channel();
2815 let sender = Arc::new(Mutex::new(Some(sender)));
2816 let response_body = json!({"content_uri": mxc_id});
2817
2818 let ret = self.respond_with(move |request: &Request| {
2819 let maybe_sender = sender.lock().unwrap().take();
2820
2821 if let Some(sender) = maybe_sender {
2822 let body = request.body.clone();
2823 let _ = sender.send(body);
2824 }
2825
2826 ResponseTemplate::new(200).set_body_json(response_body.clone())
2827 });
2828
2829 (receiver, ret)
2830 }
2831
2832 /// Returns a upload endpoint that emulates success, i.e. the media has been
2833 /// uploaded to the media server and can be accessed using the given
2834 /// event has been sent with the given [`MxcUri`].
2835 pub fn ok(self, mxc_id: &MxcUri) -> MatrixMock<'a> {
2836 self.respond_with(ResponseTemplate::new(200).set_body_json(json!({
2837 "content_uri": mxc_id
2838 })))
2839 }
2840}
2841
2842/// A prebuilt mock for resolving a room alias.
2843pub struct ResolveRoomAliasEndpoint;
2844
2845impl<'a> MockEndpoint<'a, ResolveRoomAliasEndpoint> {
2846 /// Sets up the endpoint to only intercept requests for the given room
2847 /// alias.
2848 pub fn for_alias(self, alias: impl Into<String>) -> Self {
2849 let alias = alias.into();
2850 Self {
2851 mock: self.mock.and(path_regex(format!(
2852 r"^/_matrix/client/v3/directory/room/{}",
2853 percent_encoded_path(&alias)
2854 ))),
2855 ..self
2856 }
2857 }
2858
2859 /// Returns a data endpoint with a resolved room alias.
2860 pub fn ok(self, room_id: &str, servers: Vec<String>) -> MatrixMock<'a> {
2861 self.respond_with(ResponseTemplate::new(200).set_body_json(json!({
2862 "room_id": room_id,
2863 "servers": servers,
2864 })))
2865 }
2866
2867 /// Returns a data endpoint for a room alias that does not exit.
2868 pub fn not_found(self) -> MatrixMock<'a> {
2869 self.respond_with(ResponseTemplate::new(404).set_body_json(json!({
2870 "errcode": "M_NOT_FOUND",
2871 "error": "Room alias not found."
2872 })))
2873 }
2874}
2875
2876/// A prebuilt mock for creating a room alias.
2877pub struct CreateRoomAliasEndpoint;
2878
2879impl<'a> MockEndpoint<'a, CreateRoomAliasEndpoint> {
2880 /// Returns a data endpoint for creating a room alias.
2881 pub fn ok(self) -> MatrixMock<'a> {
2882 self.respond_with(ResponseTemplate::new(200).set_body_json(json!({})))
2883 }
2884}
2885
2886/// A prebuilt mock for removing a room alias.
2887pub struct RemoveRoomAliasEndpoint;
2888
2889impl<'a> MockEndpoint<'a, RemoveRoomAliasEndpoint> {
2890 /// Returns a data endpoint for removing a room alias.
2891 pub fn ok(self) -> MatrixMock<'a> {
2892 self.respond_with(ResponseTemplate::new(200).set_body_json(json!({})))
2893 }
2894}
2895
2896/// A prebuilt mock for paginating the public room list.
2897pub struct PublicRoomsEndpoint;
2898
2899impl<'a> MockEndpoint<'a, PublicRoomsEndpoint> {
2900 /// Returns a data endpoint for paginating the public room list.
2901 pub fn ok(
2902 self,
2903 chunk: Vec<PublicRoomsChunk>,
2904 next_batch: Option<String>,
2905 prev_batch: Option<String>,
2906 total_room_count_estimate: Option<u64>,
2907 ) -> MatrixMock<'a> {
2908 self.respond_with(ResponseTemplate::new(200).set_body_json(json!({
2909 "chunk": chunk,
2910 "next_batch": next_batch,
2911 "prev_batch": prev_batch,
2912 "total_room_count_estimate": total_room_count_estimate,
2913 })))
2914 }
2915
2916 /// Returns a data endpoint for paginating the public room list with several
2917 /// `via` params.
2918 ///
2919 /// Each `via` param must be in the `server_map` parameter, otherwise it'll
2920 /// fail.
2921 pub fn ok_with_via_params(
2922 self,
2923 server_map: BTreeMap<OwnedServerName, Vec<PublicRoomsChunk>>,
2924 ) -> MatrixMock<'a> {
2925 self.respond_with(move |req: &Request| {
2926 #[derive(Deserialize)]
2927 struct PartialRequest {
2928 server: Option<OwnedServerName>,
2929 }
2930
2931 let (_, server) = req
2932 .url
2933 .query_pairs()
2934 .into_iter()
2935 .find(|(key, _)| key == "server")
2936 .expect("Server param not found in request URL");
2937 let server = ServerName::parse(server).expect("Couldn't parse server name");
2938 let chunk = server_map.get(&server).expect("Chunk for the server param not found");
2939 ResponseTemplate::new(200).set_body_json(json!({
2940 "chunk": chunk,
2941 "total_room_count_estimate": chunk.len(),
2942 }))
2943 })
2944 }
2945}
2946
2947/// A prebuilt mock for getting the room's visibility in the room directory.
2948pub struct GetRoomVisibilityEndpoint;
2949
2950impl<'a> MockEndpoint<'a, GetRoomVisibilityEndpoint> {
2951 /// Returns an endpoint that get the room's public visibility.
2952 pub fn ok(self, visibility: Visibility) -> MatrixMock<'a> {
2953 self.respond_with(ResponseTemplate::new(200).set_body_json(json!({
2954 "visibility": visibility,
2955 })))
2956 }
2957}
2958
2959/// A prebuilt mock for setting the room's visibility in the room directory.
2960pub struct SetRoomVisibilityEndpoint;
2961
2962impl<'a> MockEndpoint<'a, SetRoomVisibilityEndpoint> {
2963 /// Returns an endpoint that updates the room's visibility.
2964 pub fn ok(self) -> MatrixMock<'a> {
2965 self.respond_with(ResponseTemplate::new(200).set_body_json(json!({})))
2966 }
2967}
2968
2969/// A prebuilt mock for `GET room_keys/version`: storage ("backup") of room
2970/// keys.
2971pub struct RoomKeysVersionEndpoint;
2972
2973impl<'a> MockEndpoint<'a, RoomKeysVersionEndpoint> {
2974 /// Returns an endpoint that says there is a single room keys backup
2975 pub fn exists(self) -> MatrixMock<'a> {
2976 self.respond_with(ResponseTemplate::new(200).set_body_json(json!({
2977 "algorithm": "m.megolm_backup.v1.curve25519-aes-sha2",
2978 "auth_data": {
2979 "public_key": "abcdefg",
2980 "signatures": {},
2981 },
2982 "count": 42,
2983 "etag": "anopaquestring",
2984 "version": "1",
2985 })))
2986 }
2987
2988 /// Returns an endpoint that says there is no room keys backup
2989 pub fn none(self) -> MatrixMock<'a> {
2990 self.respond_with(ResponseTemplate::new(404).set_body_json(json!({
2991 "errcode": "M_NOT_FOUND",
2992 "error": "No current backup version"
2993 })))
2994 }
2995
2996 /// Returns an endpoint that 429 errors when we get it
2997 pub fn error429(self) -> MatrixMock<'a> {
2998 self.respond_with(ResponseTemplate::new(429).set_body_json(json!({
2999 "errcode": "M_LIMIT_EXCEEDED",
3000 "error": "Too many requests",
3001 "retry_after_ms": 2000
3002 })))
3003 }
3004
3005 /// Returns an endpoint that 404 errors when we get it
3006 pub fn error404(self) -> MatrixMock<'a> {
3007 self.respond_with(ResponseTemplate::new(404))
3008 }
3009}
3010
3011/// A prebuilt mock for `POST room_keys/version`: adding room key backups.
3012pub struct AddRoomKeysVersionEndpoint;
3013
3014impl<'a> MockEndpoint<'a, AddRoomKeysVersionEndpoint> {
3015 /// Returns an endpoint that may be used to add room key backups
3016 pub fn ok(self) -> MatrixMock<'a> {
3017 self.respond_with(ResponseTemplate::new(200).set_body_json(json!({
3018 "version": "1"
3019 })))
3020 .named("POST for the backup creation")
3021 }
3022}
3023
3024/// A prebuilt mock for `DELETE room_keys/version/xxx`: deleting room key
3025/// backups.
3026pub struct DeleteRoomKeysVersionEndpoint;
3027
3028impl<'a> MockEndpoint<'a, DeleteRoomKeysVersionEndpoint> {
3029 /// Returns an endpoint that allows deleting room key backups
3030 pub fn ok(self) -> MatrixMock<'a> {
3031 self.respond_with(ResponseTemplate::new(200).set_body_json(json!({})))
3032 .named("DELETE for the backup deletion")
3033 }
3034}
3035
3036/// A prebuilt mock for the `/sendToDevice` endpoint.
3037///
3038/// This mock can be used to simulate sending to-device messages in tests.
3039pub struct SendToDeviceEndpoint;
3040impl<'a> MockEndpoint<'a, SendToDeviceEndpoint> {
3041 /// Returns a successful response with default data.
3042 pub fn ok(self) -> MatrixMock<'a> {
3043 self.respond_with(ResponseTemplate::new(200).set_body_json(json!({})))
3044 }
3045}
3046
3047/// A prebuilt mock for `GET /members` request.
3048pub struct GetRoomMembersEndpoint;
3049
3050impl<'a> MockEndpoint<'a, GetRoomMembersEndpoint> {
3051 /// Returns a successful get members request with a list of members.
3052 pub fn ok(self, members: Vec<Raw<RoomMemberEvent>>) -> MatrixMock<'a> {
3053 self.respond_with(ResponseTemplate::new(200).set_body_json(json!({
3054 "chunk": members,
3055 })))
3056 }
3057}
3058
3059/// A prebuilt mock for `POST /invite` request.
3060pub struct InviteUserByIdEndpoint;
3061
3062impl<'a> MockEndpoint<'a, InviteUserByIdEndpoint> {
3063 /// Returns a successful invite user by id request.
3064 pub fn ok(self) -> MatrixMock<'a> {
3065 self.respond_with(ResponseTemplate::new(200).set_body_json(json!({})))
3066 }
3067}
3068
3069/// A prebuilt mock for `POST /kick` request.
3070pub struct KickUserEndpoint;
3071
3072impl<'a> MockEndpoint<'a, KickUserEndpoint> {
3073 /// Returns a successful kick user request.
3074 pub fn ok(self) -> MatrixMock<'a> {
3075 self.respond_with(ResponseTemplate::new(200).set_body_json(json!({})))
3076 }
3077}
3078
3079/// A prebuilt mock for `POST /ban` request.
3080pub struct BanUserEndpoint;
3081
3082impl<'a> MockEndpoint<'a, BanUserEndpoint> {
3083 /// Returns a successful ban user request.
3084 pub fn ok(self) -> MatrixMock<'a> {
3085 self.respond_with(ResponseTemplate::new(200).set_body_json(json!({})))
3086 }
3087}
3088
3089/// A prebuilt mock for `GET /versions` request.
3090pub struct VersionsEndpoint;
3091
3092impl<'a> MockEndpoint<'a, VersionsEndpoint> {
3093 // Get a JSON array of commonly supported versions.
3094 fn versions() -> Value {
3095 json!([
3096 "r0.0.1", "r0.2.0", "r0.3.0", "r0.4.0", "r0.5.0", "r0.6.0", "r0.6.1", "v1.1", "v1.2",
3097 "v1.3", "v1.4", "v1.5", "v1.6", "v1.7", "v1.8", "v1.9", "v1.10", "v1.11"
3098 ])
3099 }
3100
3101 /// Returns a successful `/_matrix/client/versions` request.
3102 ///
3103 /// The response will return some commonly supported versions.
3104 pub fn ok(self) -> MatrixMock<'a> {
3105 self.respond_with(ResponseTemplate::new(200).set_body_json(json!({
3106 "unstable_features": {},
3107 "versions": Self::versions()
3108 })))
3109 }
3110
3111 /// Returns a successful `/_matrix/client/versions` request.
3112 ///
3113 /// The response will return some commonly supported versions and unstable
3114 /// features supported by the SDK.
3115 pub fn ok_with_unstable_features(self) -> MatrixMock<'a> {
3116 self.respond_with(ResponseTemplate::new(200).set_body_json(json!({
3117 "unstable_features": {
3118 "org.matrix.label_based_filtering": true,
3119 "org.matrix.e2e_cross_signing": true,
3120 "org.matrix.msc4028": true,
3121 "org.matrix.simplified_msc3575": true,
3122 },
3123 "versions": Self::versions()
3124 })))
3125 }
3126
3127 /// Returns a successful `/_matrix/client/versions` request with the given
3128 /// versions and unstable features.
3129 pub fn ok_custom(
3130 self,
3131 versions: &[&str],
3132 unstable_features: &BTreeMap<&str, bool>,
3133 ) -> MatrixMock<'a> {
3134 self.respond_with(ResponseTemplate::new(200).set_body_json(json!({
3135 "unstable_features": unstable_features,
3136 "versions": versions,
3137 })))
3138 }
3139}
3140
3141/// A prebuilt mock for the room summary endpoint.
3142pub struct RoomSummaryEndpoint;
3143
3144impl<'a> MockEndpoint<'a, RoomSummaryEndpoint> {
3145 /// Returns a successful response with some default data for the given room
3146 /// id.
3147 pub fn ok(self, room_id: &RoomId) -> MatrixMock<'a> {
3148 self.respond_with(ResponseTemplate::new(200).set_body_json(json!({
3149 "room_id": room_id,
3150 "guest_can_join": true,
3151 "num_joined_members": 1,
3152 "world_readable": true,
3153 "join_rule": "public",
3154 })))
3155 }
3156}
3157
3158/// A prebuilt mock to set a room's pinned events.
3159pub struct SetRoomPinnedEventsEndpoint;
3160
3161impl<'a> MockEndpoint<'a, SetRoomPinnedEventsEndpoint> {
3162 /// Returns a successful response with a given event id.
3163 /// id.
3164 pub fn ok(self, event_id: OwnedEventId) -> MatrixMock<'a> {
3165 self.ok_with_event_id(event_id)
3166 }
3167
3168 /// Returns an error response with a generic error code indicating the
3169 /// client is not authorized to set pinned events.
3170 pub fn unauthorized(self) -> MatrixMock<'a> {
3171 self.respond_with(ResponseTemplate::new(400))
3172 }
3173}
3174
3175/// A prebuilt mock for `GET /account/whoami` request.
3176pub struct WhoAmIEndpoint;
3177
3178impl<'a> MockEndpoint<'a, WhoAmIEndpoint> {
3179 /// Returns a successful response with the default device ID.
3180 pub fn ok(self) -> MatrixMock<'a> {
3181 self.ok_with_device_id(device_id!("D3V1C31D"))
3182 }
3183
3184 /// Returns a successful response with the given device ID.
3185 pub fn ok_with_device_id(self, device_id: &DeviceId) -> MatrixMock<'a> {
3186 self.respond_with(ResponseTemplate::new(200).set_body_json(json!({
3187 "user_id": "@joe:example.org",
3188 "device_id": device_id,
3189 })))
3190 }
3191
3192 /// Returns an error response with an `M_UNKNOWN_TOKEN`.
3193 pub fn err_unknown_token(self) -> MatrixMock<'a> {
3194 self.respond_with(ResponseTemplate::new(401).set_body_json(json!({
3195 "errcode": "M_UNKNOWN_TOKEN",
3196 "error": "Invalid token"
3197 })))
3198 }
3199}
3200
3201/// A prebuilt mock for `POST /keys/upload` request.
3202pub struct UploadKeysEndpoint;
3203
3204impl<'a> MockEndpoint<'a, UploadKeysEndpoint> {
3205 /// Returns a successful response with counts of 10 curve25519 keys and 20
3206 /// signed curve25519 keys.
3207 pub fn ok(self) -> MatrixMock<'a> {
3208 self.respond_with(ResponseTemplate::new(200).set_body_json(json!({
3209 "one_time_key_counts": {
3210 "curve25519": 10,
3211 "signed_curve25519": 20,
3212 },
3213 })))
3214 }
3215}
3216
3217/// A prebuilt mock for `POST /keys/query` request.
3218pub struct QueryKeysEndpoint;
3219
3220impl<'a> MockEndpoint<'a, QueryKeysEndpoint> {
3221 /// Returns a successful empty response.
3222 pub fn ok(self) -> MatrixMock<'a> {
3223 self.respond_with(ResponseTemplate::new(200).set_body_json(json!({})))
3224 }
3225}
3226
3227/// A prebuilt mock for `GET /.well-known/matrix/client` request.
3228pub struct WellKnownEndpoint;
3229
3230impl<'a> MockEndpoint<'a, WellKnownEndpoint> {
3231 /// Returns a successful response with the URL for this homeserver.
3232 pub fn ok(self) -> MatrixMock<'a> {
3233 let server_uri = self.server.uri();
3234 self.ok_with_homeserver_url(&server_uri)
3235 }
3236
3237 /// Returns a successful response with the given homeserver URL.
3238 pub fn ok_with_homeserver_url(self, homeserver_url: &str) -> MatrixMock<'a> {
3239 self.respond_with(ResponseTemplate::new(200).set_body_json(json!({
3240 "m.homeserver": {
3241 "base_url": homeserver_url,
3242 },
3243 "m.rtc_foci": [
3244 {
3245 "type": "livekit",
3246 "livekit_service_url": "https://livekit.example.com",
3247 },
3248 ],
3249 })))
3250 }
3251
3252 /// Returns a 404 error response.
3253 pub fn error404(self) -> MatrixMock<'a> {
3254 self.respond_with(ResponseTemplate::new(404))
3255 }
3256}
3257
3258/// A prebuilt mock for `POST /keys/device_signing/upload` request.
3259pub struct UploadCrossSigningKeysEndpoint;
3260
3261impl<'a> MockEndpoint<'a, UploadCrossSigningKeysEndpoint> {
3262 /// Returns a successful empty response.
3263 pub fn ok(self) -> MatrixMock<'a> {
3264 self.respond_with(ResponseTemplate::new(200).set_body_json(json!({})))
3265 }
3266
3267 /// Returns an error response with a UIAA stage that failed to authenticate
3268 /// because of an invalid password.
3269 pub fn uiaa_invalid_password(self) -> MatrixMock<'a> {
3270 self.respond_with(ResponseTemplate::new(401).set_body_json(json!({
3271 "errcode": "M_FORBIDDEN",
3272 "error": "Invalid password",
3273 "flows": [
3274 {
3275 "stages": [
3276 "m.login.password"
3277 ]
3278 }
3279 ],
3280 "params": {},
3281 "session": "oFIJVvtEOCKmRUTYKTYIIPHL"
3282 })))
3283 }
3284
3285 /// Returns an error response with a UIAA stage.
3286 pub fn uiaa(self) -> MatrixMock<'a> {
3287 self.respond_with(ResponseTemplate::new(401).set_body_json(json!({
3288 "flows": [
3289 {
3290 "stages": [
3291 "m.login.password"
3292 ]
3293 }
3294 ],
3295 "params": {},
3296 "session": "oFIJVvtEOCKmRUTYKTYIIPHL"
3297 })))
3298 }
3299
3300 /// Returns an error response with an OAuth 2.0 UIAA stage.
3301 pub fn uiaa_oauth(self) -> MatrixMock<'a> {
3302 let server_uri = self.server.uri();
3303 self.respond_with(ResponseTemplate::new(401).set_body_json(json!({
3304 "session": "dummy",
3305 "flows": [{
3306 "stages": [ "org.matrix.cross_signing_reset" ]
3307 }],
3308 "params": {
3309 "org.matrix.cross_signing_reset": {
3310 "url": format!("{server_uri}/account/?action=org.matrix.cross_signing_reset"),
3311 }
3312 },
3313 "msg": "To reset your end-to-end encryption cross-signing identity, you first need to approve it and then try again."
3314 })))
3315 }
3316}
3317
3318/// A prebuilt mock for `POST /keys/signatures/upload` request.
3319pub struct UploadCrossSigningSignaturesEndpoint;
3320
3321impl<'a> MockEndpoint<'a, UploadCrossSigningSignaturesEndpoint> {
3322 /// Returns a successful empty response.
3323 pub fn ok(self) -> MatrixMock<'a> {
3324 self.respond_with(ResponseTemplate::new(200).set_body_json(json!({})))
3325 }
3326}
3327
3328/// A prebuilt mock for the room leave endpoint.
3329pub struct RoomLeaveEndpoint;
3330
3331impl<'a> MockEndpoint<'a, RoomLeaveEndpoint> {
3332 /// Returns a successful response with some default data for the given room
3333 /// id.
3334 pub fn ok(self, room_id: &RoomId) -> MatrixMock<'a> {
3335 self.respond_with(ResponseTemplate::new(200).set_body_json(json!({
3336 "room_id": room_id,
3337 })))
3338 }
3339
3340 /// Returns a `M_FORBIDDEN` response.
3341 pub fn forbidden(self) -> MatrixMock<'a> {
3342 self.respond_with(ResponseTemplate::new(403).set_body_json(json!({
3343 "errcode": "M_FORBIDDEN",
3344 "error": "sowwy",
3345 })))
3346 }
3347}
3348
3349/// A prebuilt mock for the room forget endpoint.
3350pub struct RoomForgetEndpoint;
3351
3352impl<'a> MockEndpoint<'a, RoomForgetEndpoint> {
3353 /// Returns a successful response with some default data for the given room
3354 /// id.
3355 pub fn ok(self) -> MatrixMock<'a> {
3356 self.respond_with(ResponseTemplate::new(200).set_body_json(json!({})))
3357 }
3358}
3359
3360/// A prebuilt mock for `POST /logout` request.
3361pub struct LogoutEndpoint;
3362
3363impl<'a> MockEndpoint<'a, LogoutEndpoint> {
3364 /// Returns a successful empty response.
3365 pub fn ok(self) -> MatrixMock<'a> {
3366 self.respond_with(ResponseTemplate::new(200).set_body_json(json!({})))
3367 }
3368}
3369
3370/// A prebuilt mock for a `GET /rooms/{roomId}/threads` request.
3371pub struct RoomThreadsEndpoint;
3372
3373impl<'a> MockEndpoint<'a, RoomThreadsEndpoint> {
3374 /// Expects an optional `from` to be set on the request.
3375 pub fn match_from(self, from: &str) -> Self {
3376 Self { mock: self.mock.and(query_param("from", from)), ..self }
3377 }
3378
3379 /// Returns a successful response with some optional events and previous
3380 /// batch token.
3381 pub fn ok(
3382 self,
3383 chunk: Vec<Raw<AnyTimelineEvent>>,
3384 next_batch: Option<String>,
3385 ) -> MatrixMock<'a> {
3386 self.respond_with(ResponseTemplate::new(200).set_body_json(json!({
3387 "chunk": chunk,
3388 "next_batch": next_batch
3389 })))
3390 }
3391}
3392
3393/// A prebuilt mock for a `GET /rooms/{roomId}/relations/{eventId}` family of
3394/// requests.
3395#[derive(Default)]
3396pub struct RoomRelationsEndpoint {
3397 event_id: Option<OwnedEventId>,
3398 spec: Option<IncludeRelations>,
3399}
3400
3401impl<'a> MockEndpoint<'a, RoomRelationsEndpoint> {
3402 /// Expects an optional `from` to be set on the request.
3403 pub fn match_from(self, from: &str) -> Self {
3404 Self { mock: self.mock.and(query_param("from", from)), ..self }
3405 }
3406
3407 /// Expects an optional `limit` to be set on the request.
3408 pub fn match_limit(self, limit: u32) -> Self {
3409 Self { mock: self.mock.and(query_param("limit", limit.to_string())), ..self }
3410 }
3411
3412 /// Match the given subrequest, according to the given specification.
3413 pub fn match_subrequest(mut self, spec: IncludeRelations) -> Self {
3414 self.endpoint.spec = Some(spec);
3415 self
3416 }
3417
3418 /// Expects the request to match a specific event id.
3419 pub fn match_target_event(mut self, event_id: OwnedEventId) -> Self {
3420 self.endpoint.event_id = Some(event_id);
3421 self
3422 }
3423
3424 /// Returns a successful response with some optional events and pagination
3425 /// tokens.
3426 pub fn ok(mut self, response: RoomRelationsResponseTemplate) -> MatrixMock<'a> {
3427 // Escape the leading $ to not confuse the regular expression engine.
3428 let event_spec = self
3429 .endpoint
3430 .event_id
3431 .take()
3432 .map(|event_id| event_id.as_str().replace("$", "\\$"))
3433 .unwrap_or_else(|| ".*".to_owned());
3434
3435 match self.endpoint.spec.take() {
3436 Some(IncludeRelations::RelationsOfType(rel_type)) => {
3437 self.mock = self.mock.and(path_regex(format!(
3438 r"^/_matrix/client/v1/rooms/.*/relations/{event_spec}/{rel_type}$"
3439 )));
3440 }
3441 Some(IncludeRelations::RelationsOfTypeAndEventType(rel_type, event_type)) => {
3442 self.mock = self.mock.and(path_regex(format!(
3443 r"^/_matrix/client/v1/rooms/.*/relations/{event_spec}/{rel_type}/{event_type}$"
3444 )));
3445 }
3446 _ => {
3447 self.mock = self.mock.and(path_regex(format!(
3448 r"^/_matrix/client/v1/rooms/.*/relations/{event_spec}",
3449 )));
3450 }
3451 }
3452
3453 self.respond_with(ResponseTemplate::new(200).set_body_json(json!({
3454 "chunk": response.chunk,
3455 "next_batch": response.next_batch,
3456 "prev_batch": response.prev_batch,
3457 "recursion_depth": response.recursion_depth,
3458 })))
3459 }
3460}
3461
3462/// A prebuilt mock for the global account data endpoint.
3463pub struct GlobalAccountDataEndpoint;
3464
3465impl<'a> MockEndpoint<'a, GlobalAccountDataEndpoint> {
3466 /// Returns a mock for a successful global account data event.
3467 pub fn ok(
3468 self,
3469 user_id: &UserId,
3470 event_type: GlobalAccountDataEventType,
3471 json_response: Value,
3472 ) -> MatrixMock<'a> {
3473 let mock = self
3474 .mock
3475 .and(path_regex(format!(
3476 r"^/_matrix/client/v3/user/{user_id}/account_data/{event_type}"
3477 )))
3478 .respond_with(ResponseTemplate::new(200).set_body_json(json_response));
3479 MatrixMock { server: self.server, mock }
3480 }
3481
3482 /// Returns a mock for a not found global account data event.
3483 pub fn not_found(
3484 self,
3485 user_id: &UserId,
3486 event_type: GlobalAccountDataEventType,
3487 ) -> MatrixMock<'a> {
3488 let mock = self
3489 .mock
3490 .and(path_regex(format!(
3491 r"^/_matrix/client/v3/user/{user_id}/account_data/{event_type}"
3492 )))
3493 .respond_with(ResponseTemplate::new(404).set_body_json(json!({
3494 "errcode": "M_NOT_FOUND",
3495 "error": "Not found"
3496 })));
3497 MatrixMock { server: self.server, mock }
3498 }
3499}
3500
3501/// A response to a [`RoomRelationsEndpoint`] query.
3502#[derive(Default)]
3503pub struct RoomRelationsResponseTemplate {
3504 /// The set of timeline events returned by this query.
3505 pub chunk: Vec<Raw<AnyTimelineEvent>>,
3506
3507 /// An opaque string representing a pagination token, which semantics depend
3508 /// on the direction used in the request.
3509 pub next_batch: Option<String>,
3510
3511 /// An opaque string representing a pagination token, which semantics depend
3512 /// on the direction used in the request.
3513 pub prev_batch: Option<String>,
3514
3515 /// If `recurse` was set on the request, the depth to which the server
3516 /// recursed.
3517 ///
3518 /// If `recurse` was not set, this field must be absent.
3519 pub recursion_depth: Option<u32>,
3520}
3521
3522impl RoomRelationsResponseTemplate {
3523 /// Fill the events returned as part of this response.
3524 pub fn events(mut self, chunk: Vec<impl Into<Raw<AnyTimelineEvent>>>) -> Self {
3525 self.chunk = chunk.into_iter().map(Into::into).collect();
3526 self
3527 }
3528
3529 /// Fill the `next_batch` token returned as part of this response.
3530 pub fn next_batch(mut self, token: impl Into<String>) -> Self {
3531 self.next_batch = Some(token.into());
3532 self
3533 }
3534
3535 /// Fill the `prev_batch` token returned as part of this response.
3536 pub fn prev_batch(mut self, token: impl Into<String>) -> Self {
3537 self.prev_batch = Some(token.into());
3538 self
3539 }
3540
3541 /// Fill the recursion depth returned in this response.
3542 pub fn recursion_depth(mut self, depth: u32) -> Self {
3543 self.recursion_depth = Some(depth);
3544 self
3545 }
3546}
3547
3548/// A prebuilt mock for `POST /rooms/{roomId}/receipt/{receiptType}/{eventId}`
3549/// request.
3550pub struct ReceiptEndpoint;
3551
3552impl<'a> MockEndpoint<'a, ReceiptEndpoint> {
3553 /// Returns a successful empty response.
3554 pub fn ok(self) -> MatrixMock<'a> {
3555 self.respond_with(ResponseTemplate::new(200).set_body_json(json!({})))
3556 }
3557
3558 /// Ensures that the body of the request is a superset of the provided
3559 /// `body` parameter.
3560 pub fn body_matches_partial_json(self, body: Value) -> Self {
3561 Self { mock: self.mock.and(body_partial_json(body)), ..self }
3562 }
3563
3564 /// Ensures that the body of the request is the exact provided `body`
3565 /// parameter.
3566 pub fn body_json(self, body: Value) -> Self {
3567 Self { mock: self.mock.and(body_json(body)), ..self }
3568 }
3569
3570 /// Ensures that the request matches a specific receipt thread.
3571 pub fn match_thread(self, thread: ReceiptThread) -> Self {
3572 if let Some(thread_str) = thread.as_str() {
3573 self.body_matches_partial_json(json!({
3574 "thread_id": thread_str
3575 }))
3576 } else {
3577 self
3578 }
3579 }
3580
3581 /// Ensures that the request matches a specific event id.
3582 pub fn match_event_id(self, event_id: &EventId) -> Self {
3583 Self {
3584 mock: self.mock.and(path_regex(format!(
3585 r"^/_matrix/client/v3/rooms/.*/receipt/.*/{}$",
3586 event_id.as_str().replace("$", "\\$")
3587 ))),
3588 ..self
3589 }
3590 }
3591}
3592
3593/// A prebuilt mock for `POST /rooms/{roomId}/read_markers` request.
3594pub struct ReadMarkersEndpoint;
3595
3596impl<'a> MockEndpoint<'a, ReadMarkersEndpoint> {
3597 /// Returns a successful empty response.
3598 pub fn ok(self) -> MatrixMock<'a> {
3599 self.respond_with(ResponseTemplate::new(200).set_body_json(json!({})))
3600 }
3601}
3602
3603/// A prebuilt mock for `PUT /user/{userId}/rooms/{roomId}/account_data/{type}`
3604/// request.
3605pub struct RoomAccountDataEndpoint;
3606
3607impl<'a> MockEndpoint<'a, RoomAccountDataEndpoint> {
3608 /// Returns a successful empty response.
3609 pub fn ok(self) -> MatrixMock<'a> {
3610 self.respond_with(ResponseTemplate::new(200).set_body_json(json!({})))
3611 }
3612}
3613
3614/// A prebuilt mock for `GET /_matrix/client/v1/media/config` request.
3615pub struct AuthenticatedMediaConfigEndpoint;
3616
3617impl<'a> MockEndpoint<'a, AuthenticatedMediaConfigEndpoint> {
3618 /// Returns a successful response with the provided max upload size.
3619 pub fn ok(self, max_upload_size: UInt) -> MatrixMock<'a> {
3620 self.respond_with(ResponseTemplate::new(200).set_body_json(json!({
3621 "m.upload.size": max_upload_size,
3622 })))
3623 }
3624
3625 /// Returns a successful response with a maxed out max upload size.
3626 pub fn ok_default(self) -> MatrixMock<'a> {
3627 self.respond_with(ResponseTemplate::new(200).set_body_json(json!({
3628 "m.upload.size": UInt::MAX,
3629 })))
3630 }
3631}
3632
3633/// A prebuilt mock for `GET /_matrix/media/v3/config` request.
3634pub struct MediaConfigEndpoint;
3635
3636impl<'a> MockEndpoint<'a, MediaConfigEndpoint> {
3637 /// Returns a successful response with the provided max upload size.
3638 pub fn ok(self, max_upload_size: UInt) -> MatrixMock<'a> {
3639 self.respond_with(ResponseTemplate::new(200).set_body_json(json!({
3640 "m.upload.size": max_upload_size,
3641 })))
3642 }
3643}
3644
3645/// A prebuilt mock for `POST /login` requests.
3646pub struct LoginEndpoint;
3647
3648impl<'a> MockEndpoint<'a, LoginEndpoint> {
3649 /// Returns a successful response.
3650 pub fn ok(self) -> MatrixMock<'a> {
3651 self.respond_with(ResponseTemplate::new(200).set_body_json(&*test_json::LOGIN))
3652 }
3653
3654 /// Returns a given response on POST /login requests
3655 ///
3656 /// # Arguments
3657 ///
3658 /// * `response` - The response that the mock server sends on POST /login
3659 /// requests.
3660 ///
3661 /// # Returns
3662 ///
3663 /// Returns a [`MatrixMock`] which can be mounted.
3664 ///
3665 /// # Examples
3666 ///
3667 /// ```
3668 /// use matrix_sdk::test_utils::mocks::{
3669 /// LoginResponseTemplate200, MatrixMockServer,
3670 /// };
3671 /// use matrix_sdk_test::async_test;
3672 /// use ruma::{device_id, time::Duration, user_id};
3673 ///
3674 /// #[async_test]
3675 /// async fn test_ok_with() {
3676 /// let server = MatrixMockServer::new().await;
3677 /// server
3678 /// .mock_login()
3679 /// .ok_with(LoginResponseTemplate200::new(
3680 /// "qwerty",
3681 /// device_id!("DEADBEEF"),
3682 /// user_id!("@cheeky_monkey:matrix.org"),
3683 /// ))
3684 /// .mount()
3685 /// .await;
3686 ///
3687 /// let client = server.client_builder().unlogged().build().await;
3688 ///
3689 /// let result = client
3690 /// .matrix_auth()
3691 /// .login_username("example", "wordpass")
3692 /// .send()
3693 /// .await
3694 /// .unwrap();
3695 ///
3696 /// assert!(
3697 /// result.access_tokesn.unwrap() == "qwerty",
3698 /// "wrong access token in response"
3699 /// );
3700 /// assert!(
3701 /// result.device_id.unwrap() == "DEADBEEF",
3702 /// "wrong device id in response"
3703 /// );
3704 /// assert!(
3705 /// result.user_id.unwrap() == "@cheeky_monkey:matrix.org",
3706 /// "wrong user id in response"
3707 /// );
3708 /// }
3709 /// ```
3710 pub fn ok_with(self, response: LoginResponseTemplate200) -> MatrixMock<'a> {
3711 self.respond_with(ResponseTemplate::new(200).set_body_json(json!({
3712 "access_token": response.access_token,
3713 "device_id": response.device_id,
3714 "user_id": response.user_id,
3715 "expires_in": response.expires_in.map(|duration| { duration.as_millis() }),
3716 "refresh_token": response.refresh_token,
3717 "well_known": response.well_known.map(|vals| {
3718 json!({
3719 "m.homeserver": {
3720 "base_url": vals.homeserver_url
3721 },
3722 "m.identity_server": vals.identity_url.map(|url| {
3723 json!({
3724 "base_url": url
3725 })
3726 })
3727 })
3728 }),
3729 })))
3730 }
3731
3732 /// Ensures that the body of the request is a superset of the provided
3733 /// `body` parameter.
3734 pub fn body_matches_partial_json(self, body: Value) -> Self {
3735 Self { mock: self.mock.and(body_partial_json(body)), ..self }
3736 }
3737}
3738
3739#[derive(Default)]
3740struct LoginResponseWellKnown {
3741 /// Required if well_known is used: The base URL for the homeserver for
3742 /// client-server connections.
3743 homeserver_url: String,
3744
3745 /// Required if well_known and m.identity_server are used: The base URL for
3746 /// the identity server for client-server connections.
3747 identity_url: Option<String>,
3748}
3749
3750/// A response to a [`LoginEndpoint`] query with status code 200.
3751#[derive(Default)]
3752pub struct LoginResponseTemplate200 {
3753 /// Required: An access token for the account. This access token can then be
3754 /// used to authorize other requests.
3755 access_token: Option<String>,
3756
3757 /// Required: ID of the logged-in device. Will be the same as the
3758 /// corresponding parameter in the request, if one was specified.
3759 device_id: Option<OwnedDeviceId>,
3760
3761 /// The lifetime of the access token, in milliseconds. Once the access token
3762 /// has expired a new access token can be obtained by using the provided
3763 /// refresh token. If no refresh token is provided, the client will need
3764 /// to re-log in to obtain a new access token. If not given, the client
3765 /// can assume that the access token will not expire.
3766 expires_in: Option<Duration>,
3767
3768 /// A refresh token for the account. This token can be used to obtain a new
3769 /// access token when it expires by calling the /refresh endpoint.
3770 refresh_token: Option<String>,
3771
3772 /// Required: The fully-qualified Matrix ID for the account.
3773 user_id: Option<OwnedUserId>,
3774
3775 /// Optional client configuration provided by the server.
3776 well_known: Option<LoginResponseWellKnown>,
3777}
3778
3779impl LoginResponseTemplate200 {
3780 /// Constructor for empty response
3781 pub fn new<T1: Into<OwnedDeviceId>, T2: Into<OwnedUserId>>(
3782 access_token: &str,
3783 device_id: T1,
3784 user_id: T2,
3785 ) -> Self {
3786 Self {
3787 access_token: Some(access_token.to_owned()),
3788 device_id: Some(device_id.into()),
3789 user_id: Some(user_id.into()),
3790 ..Default::default()
3791 }
3792 }
3793
3794 /// sets expires_in
3795 pub fn expires_in(mut self, value: Duration) -> Self {
3796 self.expires_in = Some(value);
3797 self
3798 }
3799
3800 /// sets refresh_token
3801 pub fn refresh_token(mut self, value: &str) -> Self {
3802 self.refresh_token = Some(value.to_owned());
3803 self
3804 }
3805
3806 /// sets well_known which takes a homeserver_url and an optional
3807 /// identity_url
3808 pub fn well_known(mut self, homeserver_url: String, identity_url: Option<String>) -> Self {
3809 self.well_known = Some(LoginResponseWellKnown { homeserver_url, identity_url });
3810 self
3811 }
3812}
3813
3814/// A prebuilt mock for `GET /devices` requests.
3815pub struct DevicesEndpoint;
3816
3817impl<'a> MockEndpoint<'a, DevicesEndpoint> {
3818 /// Returns a successful response.
3819 pub fn ok(self) -> MatrixMock<'a> {
3820 self.respond_with(ResponseTemplate::new(200).set_body_json(&*test_json::DEVICES))
3821 }
3822}
3823
3824/// A prebuilt mock for `POST /user_directory/search` requests.
3825pub struct UserDirectoryEndpoint;
3826
3827impl<'a> MockEndpoint<'a, UserDirectoryEndpoint> {
3828 /// Returns a successful response.
3829 pub fn ok(self) -> MatrixMock<'a> {
3830 self.respond_with(
3831 ResponseTemplate::new(200)
3832 .set_body_json(&*test_json::search_users::SEARCH_USERS_RESPONSE),
3833 )
3834 }
3835}
3836
3837/// A prebuilt mock for `POST /createRoom` requests.
3838pub struct CreateRoomEndpoint;
3839
3840impl<'a> MockEndpoint<'a, CreateRoomEndpoint> {
3841 /// Returns a successful response.
3842 pub fn ok(self) -> MatrixMock<'a> {
3843 self.respond_with(
3844 ResponseTemplate::new(200).set_body_json(json!({ "room_id": "!room:example.org"})),
3845 )
3846 }
3847}
3848
3849/// A prebuilt mock for `POST /rooms/{roomId}/upgrade` requests.
3850pub struct UpgradeRoomEndpoint;
3851
3852impl<'a> MockEndpoint<'a, UpgradeRoomEndpoint> {
3853 /// Returns a successful response with desired replacement_room ID.
3854 pub fn ok_with(self, new_room_id: &RoomId) -> MatrixMock<'a> {
3855 self.respond_with(
3856 ResponseTemplate::new(200)
3857 .set_body_json(json!({ "replacement_room": new_room_id.as_str()})),
3858 )
3859 }
3860}
3861
3862/// A prebuilt mock for `POST /media/v1/create` requests.
3863pub struct MediaAllocateEndpoint;
3864
3865impl<'a> MockEndpoint<'a, MediaAllocateEndpoint> {
3866 /// Returns a successful response.
3867 pub fn ok(self) -> MatrixMock<'a> {
3868 self.respond_with(ResponseTemplate::new(200).set_body_json(json!({
3869 "content_uri": "mxc://example.com/AQwafuaFswefuhsfAFAgsw"
3870 })))
3871 }
3872}
3873
3874/// A prebuilt mock for `PUT /media/v3/upload/{server_name}/{media_id}`
3875/// requests.
3876pub struct MediaAllocatedUploadEndpoint;
3877
3878impl<'a> MockEndpoint<'a, MediaAllocatedUploadEndpoint> {
3879 /// Returns a successful response.
3880 pub fn ok(self) -> MatrixMock<'a> {
3881 self.respond_with(ResponseTemplate::new(200).set_body_json(json!({})))
3882 }
3883}
3884
3885/// A prebuilt mock for `GET /media/v3/download` requests.
3886pub struct MediaDownloadEndpoint;
3887
3888impl<'a> MockEndpoint<'a, MediaDownloadEndpoint> {
3889 /// Returns a successful response with a plain text content.
3890 pub fn ok_plain_text(self) -> MatrixMock<'a> {
3891 self.respond_with(ResponseTemplate::new(200).set_body_string("Hello, World!"))
3892 }
3893
3894 /// Returns a successful response with a fake image content.
3895 pub fn ok_image(self) -> MatrixMock<'a> {
3896 self.respond_with(
3897 ResponseTemplate::new(200).set_body_raw(b"binaryjpegfullimagedata", "image/jpeg"),
3898 )
3899 }
3900}
3901
3902/// A prebuilt mock for `GET /media/v3/thumbnail` requests.
3903pub struct MediaThumbnailEndpoint;
3904
3905impl<'a> MockEndpoint<'a, MediaThumbnailEndpoint> {
3906 /// Returns a successful response with a fake image content.
3907 pub fn ok(self) -> MatrixMock<'a> {
3908 self.respond_with(
3909 ResponseTemplate::new(200).set_body_raw(b"binaryjpegthumbnaildata", "image/jpeg"),
3910 )
3911 }
3912}
3913
3914/// A prebuilt mock for `GET /client/v1/media/download` requests.
3915pub struct AuthedMediaDownloadEndpoint;
3916
3917impl<'a> MockEndpoint<'a, AuthedMediaDownloadEndpoint> {
3918 /// Returns a successful response with a plain text content.
3919 pub fn ok_plain_text(self) -> MatrixMock<'a> {
3920 self.respond_with(ResponseTemplate::new(200).set_body_string("Hello, World!"))
3921 }
3922
3923 /// Returns a successful response with the given bytes.
3924 pub fn ok_bytes(self, bytes: Vec<u8>) -> MatrixMock<'a> {
3925 self.respond_with(
3926 ResponseTemplate::new(200).set_body_raw(bytes, "application/octet-stream"),
3927 )
3928 }
3929
3930 /// Returns a successful response with a fake image content.
3931 pub fn ok_image(self) -> MatrixMock<'a> {
3932 self.respond_with(
3933 ResponseTemplate::new(200).set_body_raw(b"binaryjpegfullimagedata", "image/jpeg"),
3934 )
3935 }
3936}
3937
3938/// A prebuilt mock for `GET /client/v1/media/thumbnail` requests.
3939pub struct AuthedMediaThumbnailEndpoint;
3940
3941impl<'a> MockEndpoint<'a, AuthedMediaThumbnailEndpoint> {
3942 /// Returns a successful response with a fake image content.
3943 pub fn ok(self) -> MatrixMock<'a> {
3944 self.respond_with(
3945 ResponseTemplate::new(200).set_body_raw(b"binaryjpegthumbnaildata", "image/jpeg"),
3946 )
3947 }
3948}
3949
3950/// A prebuilt mock for `GET /client/v3/rooms/{room_id}/join` requests.
3951pub struct JoinRoomEndpoint {
3952 room_id: OwnedRoomId,
3953}
3954
3955impl<'a> MockEndpoint<'a, JoinRoomEndpoint> {
3956 /// Returns a successful response using the provided [`RoomId`].
3957 pub fn ok(self) -> MatrixMock<'a> {
3958 let room_id = self.endpoint.room_id.to_owned();
3959
3960 self.respond_with(ResponseTemplate::new(200).set_body_json(json!({
3961 "room_id": room_id,
3962 })))
3963 }
3964}
3965
3966#[derive(Default)]
3967struct ThreadSubscriptionMatchers {
3968 /// Optional room id to match in the query.
3969 room_id: Option<OwnedRoomId>,
3970 /// Optional thread root event id to match in the query.
3971 thread_root: Option<OwnedEventId>,
3972}
3973
3974impl ThreadSubscriptionMatchers {
3975 /// Match the request parameter against a specific room id.
3976 fn match_room_id(mut self, room_id: OwnedRoomId) -> Self {
3977 self.room_id = Some(room_id);
3978 self
3979 }
3980
3981 /// Match the request parameter against a specific thread root event id.
3982 fn match_thread_id(mut self, thread_root: OwnedEventId) -> Self {
3983 self.thread_root = Some(thread_root);
3984 self
3985 }
3986
3987 /// Compute the final URI for the thread subscription endpoint.
3988 fn endpoint_regexp_uri(&self) -> String {
3989 if self.room_id.is_some() || self.thread_root.is_some() {
3990 format!(
3991 "^/_matrix/client/unstable/io.element.msc4306/rooms/{}/thread/{}/subscription$",
3992 self.room_id.as_deref().map(|s| s.as_str()).unwrap_or(".*"),
3993 self.thread_root.as_deref().map(|s| s.as_str()).unwrap_or(".*").replace("$", "\\$")
3994 )
3995 } else {
3996 "^/_matrix/client/unstable/io.element.msc4306/rooms/.*/thread/.*/subscription$"
3997 .to_owned()
3998 }
3999 }
4000}
4001
4002/// A prebuilt mock for `GET
4003/// /client/*/rooms/{room_id}/threads/{thread_root}/subscription`
4004#[derive(Default)]
4005pub struct RoomGetThreadSubscriptionEndpoint {
4006 matchers: ThreadSubscriptionMatchers,
4007}
4008
4009impl<'a> MockEndpoint<'a, RoomGetThreadSubscriptionEndpoint> {
4010 /// Returns a successful response for the given thread subscription.
4011 pub fn ok(mut self, automatic: bool) -> MatrixMock<'a> {
4012 self.mock = self.mock.and(path_regex(self.endpoint.matchers.endpoint_regexp_uri()));
4013 self.respond_with(ResponseTemplate::new(200).set_body_json(json!({
4014 "automatic": automatic
4015 })))
4016 }
4017
4018 /// Match the request parameter against a specific room id.
4019 pub fn match_room_id(mut self, room_id: OwnedRoomId) -> Self {
4020 self.endpoint.matchers = self.endpoint.matchers.match_room_id(room_id);
4021 self
4022 }
4023 /// Match the request parameter against a specific thread root event id.
4024 pub fn match_thread_id(mut self, thread_root: OwnedEventId) -> Self {
4025 self.endpoint.matchers = self.endpoint.matchers.match_thread_id(thread_root);
4026 self
4027 }
4028}
4029
4030/// A prebuilt mock for `PUT
4031/// /client/*/rooms/{room_id}/threads/{thread_root}/subscription`
4032#[derive(Default)]
4033pub struct RoomPutThreadSubscriptionEndpoint {
4034 matchers: ThreadSubscriptionMatchers,
4035}
4036
4037impl<'a> MockEndpoint<'a, RoomPutThreadSubscriptionEndpoint> {
4038 /// Returns a successful response for the given setting of thread
4039 /// subscription.
4040 pub fn ok(mut self) -> MatrixMock<'a> {
4041 self.mock = self.mock.and(path_regex(self.endpoint.matchers.endpoint_regexp_uri()));
4042 self.respond_with(ResponseTemplate::new(200))
4043 }
4044
4045 /// Returns that the server skipped an automated thread subscription,
4046 /// because the user unsubscribed to the thread after the event id passed in
4047 /// the automatic subscription.
4048 pub fn conflicting_unsubscription(mut self) -> MatrixMock<'a> {
4049 self.mock = self.mock.and(path_regex(self.endpoint.matchers.endpoint_regexp_uri()));
4050 self.respond_with(ResponseTemplate::new(409).set_body_json(json!({
4051 "errcode": "IO.ELEMENT.MSC4306.M_CONFLICTING_UNSUBSCRIPTION",
4052 "error": "the user unsubscribed after the subscription event id"
4053 })))
4054 }
4055
4056 /// Match the request parameter against a specific room id.
4057 pub fn match_room_id(mut self, room_id: OwnedRoomId) -> Self {
4058 self.endpoint.matchers = self.endpoint.matchers.match_room_id(room_id);
4059 self
4060 }
4061 /// Match the request parameter against a specific thread root event id.
4062 pub fn match_thread_id(mut self, thread_root: OwnedEventId) -> Self {
4063 self.endpoint.matchers = self.endpoint.matchers.match_thread_id(thread_root);
4064 self
4065 }
4066 /// Match the request body's `automatic` field against a specific event id.
4067 pub fn match_automatic_event_id(mut self, up_to_event_id: &EventId) -> Self {
4068 self.mock = self.mock.and(body_json(json!({
4069 "automatic": up_to_event_id
4070 })));
4071 self
4072 }
4073}
4074
4075/// A prebuilt mock for `DELETE
4076/// /client/*/rooms/{room_id}/threads/{thread_root}/subscription`
4077#[derive(Default)]
4078pub struct RoomDeleteThreadSubscriptionEndpoint {
4079 matchers: ThreadSubscriptionMatchers,
4080}
4081
4082impl<'a> MockEndpoint<'a, RoomDeleteThreadSubscriptionEndpoint> {
4083 /// Returns a successful response for the deletion of a given thread
4084 /// subscription.
4085 pub fn ok(mut self) -> MatrixMock<'a> {
4086 self.mock = self.mock.and(path_regex(self.endpoint.matchers.endpoint_regexp_uri()));
4087 self.respond_with(ResponseTemplate::new(200))
4088 }
4089
4090 /// Match the request parameter against a specific room id.
4091 pub fn match_room_id(mut self, room_id: OwnedRoomId) -> Self {
4092 self.endpoint.matchers = self.endpoint.matchers.match_room_id(room_id);
4093 self
4094 }
4095 /// Match the request parameter against a specific thread root event id.
4096 pub fn match_thread_id(mut self, thread_root: OwnedEventId) -> Self {
4097 self.endpoint.matchers = self.endpoint.matchers.match_thread_id(thread_root);
4098 self
4099 }
4100}
4101
4102/// A prebuilt mock for `PUT
4103/// /_matrix/client/v3/pushrules/global/{kind}/{ruleId}/enabled`.
4104pub struct EnablePushRuleEndpoint;
4105
4106impl<'a> MockEndpoint<'a, EnablePushRuleEndpoint> {
4107 /// Returns a successful empty JSON response.
4108 pub fn ok(self) -> MatrixMock<'a> {
4109 self.ok_empty_json()
4110 }
4111}
4112
4113/// A prebuilt mock for `PUT
4114/// /_matrix/client/v3/pushrules/global/{kind}/{ruleId}/actions`.
4115pub struct SetPushRulesActionsEndpoint;
4116
4117impl<'a> MockEndpoint<'a, SetPushRulesActionsEndpoint> {
4118 /// Returns a successful empty JSON response.
4119 pub fn ok(self) -> MatrixMock<'a> {
4120 self.ok_empty_json()
4121 }
4122}
4123
4124/// A prebuilt mock for `PUT
4125/// /_matrix/client/v3/pushrules/global/{kind}/{ruleId}`.
4126pub struct SetPushRulesEndpoint;
4127
4128impl<'a> MockEndpoint<'a, SetPushRulesEndpoint> {
4129 /// Returns a successful empty JSON response.
4130 pub fn ok(self) -> MatrixMock<'a> {
4131 self.ok_empty_json()
4132 }
4133}
4134
4135/// A prebuilt mock for `DELETE
4136/// /_matrix/client/v3/pushrules/global/{kind}/{ruleId}`.
4137pub struct DeletePushRulesEndpoint;
4138
4139impl<'a> MockEndpoint<'a, DeletePushRulesEndpoint> {
4140 /// Returns a successful empty JSON response.
4141 pub fn ok(self) -> MatrixMock<'a> {
4142 self.ok_empty_json()
4143 }
4144}
4145
4146/// A prebuilt mock for the federation version endpoint.
4147pub struct FederationVersionEndpoint;
4148
4149impl<'a> MockEndpoint<'a, FederationVersionEndpoint> {
4150 /// Returns a successful response with the given server name and version.
4151 pub fn ok(self, server_name: &str, version: &str) -> MatrixMock<'a> {
4152 let response_body = json!({
4153 "server": {
4154 "name": server_name,
4155 "version": version
4156 }
4157 });
4158 self.respond_with(ResponseTemplate::new(200).set_body_json(response_body))
4159 }
4160
4161 /// Returns a successful response with empty/missing server information.
4162 pub fn ok_empty(self) -> MatrixMock<'a> {
4163 let response_body = json!({});
4164 self.respond_with(ResponseTemplate::new(200).set_body_json(response_body))
4165 }
4166}
4167
4168/// A prebuilt mock for `GET ^/_matrix/client/v3/thread_subscriptions`.
4169#[derive(Default)]
4170pub struct GetThreadSubscriptionsEndpoint {
4171 /// New thread subscriptions per (room id, thread root event id).
4172 subscribed: BTreeMap<OwnedRoomId, BTreeMap<OwnedEventId, ThreadSubscription>>,
4173 /// New thread unsubscriptions per (room id, thread root event id).
4174 unsubscribed: BTreeMap<OwnedRoomId, BTreeMap<OwnedEventId, ThreadUnsubscription>>,
4175}
4176
4177impl<'a> MockEndpoint<'a, GetThreadSubscriptionsEndpoint> {
4178 /// Add a single thread subscription to the response.
4179 pub fn add_subscription(
4180 mut self,
4181 room_id: OwnedRoomId,
4182 thread_root: OwnedEventId,
4183 subscription: ThreadSubscription,
4184 ) -> Self {
4185 self.endpoint.subscribed.entry(room_id).or_default().insert(thread_root, subscription);
4186 self
4187 }
4188
4189 /// Add a single thread unsubscription to the response.
4190 pub fn add_unsubcription(
4191 mut self,
4192 room_id: OwnedRoomId,
4193 thread_root: OwnedEventId,
4194 unsubscription: ThreadUnsubscription,
4195 ) -> Self {
4196 self.endpoint.unsubscribed.entry(room_id).or_default().insert(thread_root, unsubscription);
4197 self
4198 }
4199
4200 /// Match the `from` query parameter to a given value.
4201 pub fn match_from(self, from: &str) -> Self {
4202 Self { mock: self.mock.and(query_param("from", from)), ..self }
4203 }
4204 /// Match the `to` query parameter to a given value.
4205 pub fn match_to(self, to: &str) -> Self {
4206 Self { mock: self.mock.and(query_param("to", to)), ..self }
4207 }
4208
4209 /// Returns a successful response with the given thread subscriptions, and
4210 /// "end" parameter to be used in the next query.
4211 pub fn ok(self, end: Option<String>) -> MatrixMock<'a> {
4212 let response_body = json!({
4213 "subscribed": self.endpoint.subscribed,
4214 "unsubscribed": self.endpoint.unsubscribed,
4215 "end": end,
4216 });
4217 self.respond_with(ResponseTemplate::new(200).set_body_json(response_body))
4218 }
4219}
4220
4221/// A prebuilt mock for `GET /client/*/rooms/{roomId}/hierarchy`
4222#[derive(Default)]
4223pub struct GetHierarchyEndpoint;
4224
4225impl<'a> MockEndpoint<'a, GetHierarchyEndpoint> {
4226 /// Returns a successful response containing the given room IDs.
4227 pub fn ok_with_room_ids(self, room_ids: Vec<&RoomId>) -> MatrixMock<'a> {
4228 let rooms = room_ids
4229 .iter()
4230 .map(|id| {
4231 json!({
4232 "room_id": id,
4233 "num_joined_members": 1,
4234 "world_readable": false,
4235 "guest_can_join": false,
4236 "children_state": []
4237 })
4238 })
4239 .collect::<Vec<_>>();
4240
4241 self.respond_with(ResponseTemplate::new(200).set_body_json(json!({
4242 "rooms": rooms,
4243 })))
4244 }
4245
4246 /// Returns a successful response containing the given room IDs and children
4247 /// states
4248 pub fn ok_with_room_ids_and_children_state(
4249 self,
4250 room_ids: Vec<&RoomId>,
4251 children_state: Vec<&RoomId>,
4252 ) -> MatrixMock<'a> {
4253 let children_state = children_state
4254 .into_iter()
4255 .map(|id| json!({ "type": "m.space.child", "state_key": id }))
4256 .collect::<Vec<_>>();
4257
4258 let rooms = room_ids
4259 .iter()
4260 .map(|id| {
4261 json!({
4262 "room_id": id,
4263 "num_joined_members": 1,
4264 "world_readable": false,
4265 "guest_can_join": false,
4266 "children_state": children_state
4267 })
4268 })
4269 .collect::<Vec<_>>();
4270
4271 self.respond_with(ResponseTemplate::new(200).set_body_json(json!({
4272 "rooms": rooms,
4273 })))
4274 }
4275
4276 /// Returns a successful response with an empty list of rooms.
4277 pub fn ok(self) -> MatrixMock<'a> {
4278 self.respond_with(ResponseTemplate::new(200).set_body_json(json!({
4279 "rooms": []
4280 })))
4281 }
4282}