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