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