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