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