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 /// Mount a [`MatrixMock`] on the attached server.
1897 ///
1898 /// The [`MatrixMock`] will remain active until the [`MatrixMockServer`] is
1899 /// shut down. If you want to control or limit how long your
1900 /// [`MatrixMock`] stays active, check out [`Self::mount_as_scoped`].
1901 pub async fn mount(self) {
1902 self.mock.mount(self.server).await;
1903 }
1904
1905 /// Mount a [`MatrixMock`] as **scoped** on the attached server.
1906 ///
1907 /// When using [`Self::mount`], your [`MatrixMock`]s will be active until
1908 /// the [`MatrixMockServer`] is shut down.
1909 ///
1910 /// When using `mount_as_scoped`, your [`MatrixMock`]s will be active as
1911 /// long as the returned [`MockGuard`] is not dropped.
1912 ///
1913 /// When the returned [`MockGuard`] is dropped, [`MatrixMockServer`] will
1914 /// verify that the expectations set on the scoped [`MatrixMock`] were
1915 /// verified - if not, it will panic.
1916 pub async fn mount_as_scoped(self) -> MockGuard {
1917 self.mock.mount_as_scoped(self.server).await
1918 }
1919}
1920
1921/// Generic mocked endpoint, with useful common helpers.
1922pub struct MockEndpoint<'a, T> {
1923 server: &'a MockServer,
1924 mock: MockBuilder,
1925 endpoint: T,
1926 expected_access_token: ExpectedAccessToken,
1927}
1928
1929impl<'a, T> MockEndpoint<'a, T> {
1930 fn new(server: &'a MockServer, mock: MockBuilder, endpoint: T) -> Self {
1931 Self { server, mock, endpoint, expected_access_token: ExpectedAccessToken::Ignore }
1932 }
1933
1934 /// Expect authentication with the default access token on this endpoint.
1935 pub fn expect_default_access_token(mut self) -> Self {
1936 self.expected_access_token = ExpectedAccessToken::Default;
1937 self
1938 }
1939
1940 /// Expect authentication with the given access token on this endpoint.
1941 pub fn expect_access_token(mut self, access_token: &'static str) -> Self {
1942 self.expected_access_token = ExpectedAccessToken::Custom(access_token);
1943 self
1944 }
1945
1946 /// Expect authentication with any access token on this endpoint, regardless
1947 /// of its value.
1948 ///
1949 /// This is useful if we don't want to track the value of the access token.
1950 pub fn expect_any_access_token(mut self) -> Self {
1951 self.expected_access_token = ExpectedAccessToken::Any;
1952 self
1953 }
1954
1955 /// Expect no authentication on this endpoint.
1956 ///
1957 /// This means that the endpoint will not match if an `AUTHENTICATION`
1958 /// header is present.
1959 pub fn expect_missing_access_token(mut self) -> Self {
1960 self.expected_access_token = ExpectedAccessToken::Missing;
1961 self
1962 }
1963
1964 /// Ignore the access token on this endpoint.
1965 ///
1966 /// This should be used to override the default behavior of an endpoint that
1967 /// requires access tokens.
1968 pub fn ignore_access_token(mut self) -> Self {
1969 self.expected_access_token = ExpectedAccessToken::Ignore;
1970 self
1971 }
1972
1973 /// Expect the given UIAA auth data in the body of the request.
1974 pub fn expect_uiaa_auth_data(mut self, auth_data: &uiaa::AuthData) -> Self {
1975 self.mock = self.mock.and(body_partial_json(json!({
1976 "auth": auth_data,
1977 })));
1978 self
1979 }
1980
1981 /// Specify how to respond to a query (viz., like
1982 /// [`MockBuilder::respond_with`] does), when other predefined responses
1983 /// aren't sufficient.
1984 ///
1985 /// # Examples
1986 ///
1987 /// ```
1988 /// # tokio_test::block_on(async {
1989 /// use matrix_sdk::{ruma::{room_id, event_id}, test_utils::mocks::MatrixMockServer};
1990 /// use serde_json::json;
1991 /// use wiremock::ResponseTemplate;
1992 ///
1993 /// let mock_server = MatrixMockServer::new().await;
1994 /// let client = mock_server.client_builder().build().await;
1995 ///
1996 /// mock_server.mock_room_state_encryption().plain().mount().await;
1997 ///
1998 /// let room = mock_server
1999 /// .sync_joined_room(&client, room_id!("!room_id:localhost"))
2000 /// .await;
2001 ///
2002 /// let event_id = event_id!("$some_id");
2003 /// mock_server
2004 /// .mock_room_send()
2005 /// .respond_with(
2006 /// ResponseTemplate::new(429)
2007 /// .insert_header("Retry-After", "100")
2008 /// .set_body_json(json!({
2009 /// "errcode": "M_LIMIT_EXCEEDED",
2010 /// "custom_field": "with custom data",
2011 /// })))
2012 /// .expect(1)
2013 /// .mount()
2014 /// .await;
2015 ///
2016 /// room
2017 /// .send_raw("m.room.message", json!({ "body": "Hello world" }))
2018 /// .await
2019 /// .expect_err("The sending of the event should fail");
2020 /// # anyhow::Ok(()) });
2021 /// ```
2022 pub fn respond_with<R: Respond + 'static>(self, func: R) -> MatrixMock<'a> {
2023 let mock = self.mock.and(self.expected_access_token).respond_with(func);
2024 MatrixMock { mock, server: self.server }
2025 }
2026
2027 /// Returns a send endpoint that emulates a transient failure, i.e responds
2028 /// with error 500.
2029 ///
2030 /// # Examples
2031 /// ```
2032 /// # tokio_test::block_on(async {
2033 /// use matrix_sdk::{ruma::{room_id, event_id}, test_utils::mocks::MatrixMockServer};
2034 /// use serde_json::json;
2035 ///
2036 /// let mock_server = MatrixMockServer::new().await;
2037 /// let client = mock_server.client_builder().build().await;
2038 ///
2039 /// mock_server.mock_room_state_encryption().plain().mount().await;
2040 ///
2041 /// let room = mock_server
2042 /// .sync_joined_room(&client, room_id!("!room_id:localhost"))
2043 /// .await;
2044 ///
2045 /// mock_server
2046 /// .mock_room_send()
2047 /// .error500()
2048 /// .expect(1)
2049 /// .mount()
2050 /// .await;
2051 ///
2052 /// room
2053 /// .send_raw("m.room.message", json!({ "body": "Hello world" }))
2054 /// .await.expect_err("The sending of the event should have failed");
2055 /// # anyhow::Ok(()) });
2056 /// ```
2057 pub fn error500(self) -> MatrixMock<'a> {
2058 self.respond_with(ResponseTemplate::new(500))
2059 }
2060
2061 /// Returns a mocked endpoint that emulates an unimplemented endpoint, i.e
2062 /// responds with a 404 HTTP status code and an `M_UNRECOGNIZED` Matrix
2063 /// error code.
2064 ///
2065 /// Note that the default behavior of the mock server is to return a 404
2066 /// status code for endpoints that are not mocked with an empty response.
2067 ///
2068 /// This can be useful to check if an endpoint is called, even if it is not
2069 /// implemented by the server.
2070 pub fn error_unrecognized(self) -> MatrixMock<'a> {
2071 self.respond_with(ResponseTemplate::new(404).set_body_json(json!({
2072 "errcode": "M_UNRECOGNIZED",
2073 "error": "Unrecognized request",
2074 })))
2075 }
2076
2077 /// Returns a mocked endpoint that emulates an unknown token error, i.e
2078 /// responds with a 401 HTTP status code and an `M_UNKNOWN_TOKEN` Matrix
2079 /// error code.
2080 pub fn error_unknown_token(self, soft_logout: bool) -> MatrixMock<'a> {
2081 self.respond_with(ResponseTemplate::new(401).set_body_json(json!({
2082 "errcode": "M_UNKNOWN_TOKEN",
2083 "error": "Unrecognized access token",
2084 "soft_logout": soft_logout,
2085 })))
2086 }
2087
2088 /// Internal helper to return an `{ event_id }` JSON struct along with a 200
2089 /// ok response.
2090 fn ok_with_event_id(self, event_id: OwnedEventId) -> MatrixMock<'a> {
2091 self.respond_with(ResponseTemplate::new(200).set_body_json(json!({ "event_id": event_id })))
2092 }
2093
2094 /// Internal helper to return a 200 OK response with an empty JSON object in
2095 /// the body.
2096 fn ok_empty_json(self) -> MatrixMock<'a> {
2097 self.respond_with(ResponseTemplate::new(200).set_body_json(json!({})))
2098 }
2099
2100 /// Returns an endpoint that emulates a permanent failure error (e.g. event
2101 /// is too large).
2102 ///
2103 /// # Examples
2104 /// ```
2105 /// # tokio_test::block_on(async {
2106 /// use matrix_sdk::{ruma::{room_id, event_id}, test_utils::mocks::MatrixMockServer};
2107 /// use serde_json::json;
2108 ///
2109 /// let mock_server = MatrixMockServer::new().await;
2110 /// let client = mock_server.client_builder().build().await;
2111 ///
2112 /// mock_server.mock_room_state_encryption().plain().mount().await;
2113 ///
2114 /// let room = mock_server
2115 /// .sync_joined_room(&client, room_id!("!room_id:localhost"))
2116 /// .await;
2117 ///
2118 /// mock_server
2119 /// .mock_room_send()
2120 /// .error_too_large()
2121 /// .expect(1)
2122 /// .mount()
2123 /// .await;
2124 ///
2125 /// room
2126 /// .send_raw("m.room.message", json!({ "body": "Hello world" }))
2127 /// .await.expect_err("The sending of the event should have failed");
2128 /// # anyhow::Ok(()) });
2129 /// ```
2130 pub fn error_too_large(self) -> MatrixMock<'a> {
2131 self.respond_with(ResponseTemplate::new(413).set_body_json(json!({
2132 // From https://spec.matrix.org/v1.10/client-server-api/#standard-error-response
2133 "errcode": "M_TOO_LARGE",
2134 "error": "Request body too large",
2135 })))
2136 }
2137}
2138
2139/// The access token to expect on an endpoint.
2140enum ExpectedAccessToken {
2141 /// Ignore any access token or lack thereof.
2142 Ignore,
2143
2144 /// We expect the default access token.
2145 Default,
2146
2147 /// We expect the given access token.
2148 Custom(&'static str),
2149
2150 /// We expect any access token.
2151 Any,
2152
2153 /// We expect that there is no access token.
2154 Missing,
2155}
2156
2157impl ExpectedAccessToken {
2158 /// Get the access token from the given request.
2159 fn access_token(request: &Request) -> Option<&str> {
2160 request
2161 .headers
2162 .get(&http::header::AUTHORIZATION)?
2163 .to_str()
2164 .ok()?
2165 .strip_prefix("Bearer ")
2166 .filter(|token| !token.is_empty())
2167 }
2168}
2169
2170impl wiremock::Match for ExpectedAccessToken {
2171 fn matches(&self, request: &Request) -> bool {
2172 match self {
2173 Self::Ignore => true,
2174 Self::Default => Self::access_token(request) == Some("1234"),
2175 Self::Custom(token) => Self::access_token(request) == Some(token),
2176 Self::Any => Self::access_token(request).is_some(),
2177 Self::Missing => request.headers.get(&http::header::AUTHORIZATION).is_none(),
2178 }
2179 }
2180}
2181
2182/// A prebuilt mock for sending a message like event in a room.
2183pub struct RoomSendEndpoint;
2184
2185impl<'a> MockEndpoint<'a, RoomSendEndpoint> {
2186 /// Ensures that the body of the request is a superset of the provided
2187 /// `body` parameter.
2188 ///
2189 /// # Examples
2190 /// ```
2191 /// # tokio_test::block_on(async {
2192 /// use matrix_sdk::{
2193 /// ruma::{room_id, event_id, events::room::message::RoomMessageEventContent},
2194 /// test_utils::mocks::MatrixMockServer
2195 /// };
2196 /// use serde_json::json;
2197 ///
2198 /// let mock_server = MatrixMockServer::new().await;
2199 /// let client = mock_server.client_builder().build().await;
2200 ///
2201 /// mock_server.mock_room_state_encryption().plain().mount().await;
2202 ///
2203 /// let room = mock_server
2204 /// .sync_joined_room(&client, room_id!("!room_id:localhost"))
2205 /// .await;
2206 ///
2207 /// let event_id = event_id!("$some_id");
2208 /// mock_server
2209 /// .mock_room_send()
2210 /// .body_matches_partial_json(json!({
2211 /// "body": "Hello world",
2212 /// }))
2213 /// .ok(event_id)
2214 /// .expect(1)
2215 /// .mount()
2216 /// .await;
2217 ///
2218 /// let content = RoomMessageEventContent::text_plain("Hello world");
2219 /// let result = room.send(content).await?;
2220 ///
2221 /// assert_eq!(
2222 /// event_id,
2223 /// result.response.event_id,
2224 /// "The event ID we mocked should match the one we received when we sent the event"
2225 /// );
2226 /// # anyhow::Ok(()) });
2227 /// ```
2228 pub fn body_matches_partial_json(self, body: Value) -> Self {
2229 Self { mock: self.mock.and(body_partial_json(body)), ..self }
2230 }
2231
2232 /// Ensures that the send endpoint request uses a specific event type.
2233 ///
2234 /// # Examples
2235 ///
2236 /// see also [`MatrixMockServer::mock_room_send`] for more context.
2237 ///
2238 /// ```
2239 /// # tokio_test::block_on(async {
2240 /// use matrix_sdk::{ruma::{room_id, event_id}, test_utils::mocks::MatrixMockServer};
2241 /// use serde_json::json;
2242 ///
2243 /// let mock_server = MatrixMockServer::new().await;
2244 /// let client = mock_server.client_builder().build().await;
2245 ///
2246 /// mock_server.mock_room_state_encryption().plain().mount().await;
2247 ///
2248 /// let room = mock_server
2249 /// .sync_joined_room(&client, room_id!("!room_id:localhost"))
2250 /// .await;
2251 ///
2252 /// let event_id = event_id!("$some_id");
2253 /// mock_server
2254 /// .mock_room_send()
2255 /// .for_type("m.room.message".into())
2256 /// .ok(event_id)
2257 /// .expect(1)
2258 /// .mount()
2259 /// .await;
2260 ///
2261 /// let response_not_mocked = room.send_raw("m.room.reaction", json!({ "body": "Hello world" })).await;
2262 /// // The `m.room.reaction` event type should not be mocked by the server.
2263 /// assert!(response_not_mocked.is_err());
2264 ///
2265 /// let result = room.send_raw("m.room.message", json!({ "body": "Hello world" })).await?;
2266 /// // The `m.room.message` event type should be mocked by the server.
2267 /// assert_eq!(
2268 /// event_id,
2269 /// result.response.event_id,
2270 /// "The event ID we mocked should match the one we received when we sent the event"
2271 /// );
2272 /// # anyhow::Ok(()) });
2273 /// ```
2274 pub fn for_type(self, event_type: MessageLikeEventType) -> Self {
2275 Self {
2276 // Note: we already defined a path when constructing the mock builder, but this one
2277 // ought to be more specialized.
2278 mock: self
2279 .mock
2280 .and(path_regex(format!(r"^/_matrix/client/v3/rooms/.*/send/{event_type}",))),
2281 ..self
2282 }
2283 }
2284
2285 /// Ensures the event was sent as a delayed event.
2286 ///
2287 /// See also [the MSC](https://github.com/matrix-org/matrix-spec-proposals/pull/4140).
2288 ///
2289 /// Note: works with *any* room.
2290 ///
2291 /// # Examples
2292 ///
2293 /// see also [`MatrixMockServer::mock_room_send`] for more context.
2294 ///
2295 /// ```
2296 /// # tokio_test::block_on(async {
2297 /// use matrix_sdk::{
2298 /// ruma::{
2299 /// api::client::delayed_events::{delayed_message_event, DelayParameters},
2300 /// events::{message::MessageEventContent, AnyMessageLikeEventContent},
2301 /// room_id,
2302 /// time::Duration,
2303 /// TransactionId,
2304 /// },
2305 /// test_utils::mocks::MatrixMockServer,
2306 /// };
2307 /// use serde_json::json;
2308 /// use wiremock::ResponseTemplate;
2309 ///
2310 /// let mock_server = MatrixMockServer::new().await;
2311 /// let client = mock_server.client_builder().build().await;
2312 ///
2313 /// mock_server.mock_room_state_encryption().plain().mount().await;
2314 ///
2315 /// let room = mock_server.sync_joined_room(&client, room_id!("!room_id:localhost")).await;
2316 ///
2317 /// mock_server
2318 /// .mock_room_send()
2319 /// .match_delayed_event(Duration::from_millis(500))
2320 /// .respond_with(ResponseTemplate::new(200).set_body_json(json!({"delay_id":"$some_id"})))
2321 /// .mock_once()
2322 /// .mount()
2323 /// .await;
2324 ///
2325 /// let response_not_mocked =
2326 /// room.send_raw("m.room.message", json!({ "body": "Hello world" })).await;
2327 ///
2328 /// // A non delayed event should not be mocked by the server.
2329 /// assert!(response_not_mocked.is_err());
2330 ///
2331 /// let r = delayed_message_event::unstable::Request::new(
2332 /// room.room_id().to_owned(),
2333 /// TransactionId::new(),
2334 /// DelayParameters::Timeout { timeout: Duration::from_millis(500) },
2335 /// &AnyMessageLikeEventContent::Message(MessageEventContent::plain("hello world")),
2336 /// )
2337 /// .unwrap();
2338 ///
2339 /// let response = room.client().send(r).await.unwrap();
2340 /// // The delayed `m.room.message` event type should be mocked by the server.
2341 /// assert_eq!("$some_id", response.delay_id);
2342 /// # anyhow::Ok(()) });
2343 /// ```
2344 pub fn match_delayed_event(self, delay: Duration) -> Self {
2345 Self {
2346 mock: self
2347 .mock
2348 .and(query_param("org.matrix.msc4140.delay", delay.as_millis().to_string())),
2349 ..self
2350 }
2351 }
2352
2353 /// Returns a send endpoint that emulates success, i.e. the event has been
2354 /// sent with the given event id.
2355 ///
2356 /// # Examples
2357 /// ```
2358 /// # tokio_test::block_on(async {
2359 /// use matrix_sdk::{ruma::{room_id, event_id}, test_utils::mocks::MatrixMockServer};
2360 /// use serde_json::json;
2361 ///
2362 /// let mock_server = MatrixMockServer::new().await;
2363 /// let client = mock_server.client_builder().build().await;
2364 ///
2365 /// mock_server.mock_room_state_encryption().plain().mount().await;
2366 ///
2367 /// let room = mock_server
2368 /// .sync_joined_room(&client, room_id!("!room_id:localhost"))
2369 /// .await;
2370 ///
2371 /// let event_id = event_id!("$some_id");
2372 /// let send_guard = mock_server
2373 /// .mock_room_send()
2374 /// .ok(event_id)
2375 /// .expect(1)
2376 /// .mount_as_scoped()
2377 /// .await;
2378 ///
2379 /// let result = room.send_raw("m.room.message", json!({ "body": "Hello world" })).await?;
2380 ///
2381 /// assert_eq!(
2382 /// event_id,
2383 /// result.response.event_id,
2384 /// "The event ID we mocked should match the one we received when we sent the event"
2385 /// );
2386 /// # anyhow::Ok(()) });
2387 /// ```
2388 pub fn ok(self, returned_event_id: impl Into<OwnedEventId>) -> MatrixMock<'a> {
2389 self.ok_with_event_id(returned_event_id.into())
2390 }
2391
2392 /// Returns a send endpoint that emulates success after a delay, i.e. the
2393 /// event has been sent with the given event id, but the response is delayed
2394 /// by the given duration.
2395 ///
2396 /// This is useful for testing ordering guarantees when multiple events are
2397 /// in-flight simultaneously.
2398 ///
2399 /// # Examples
2400 /// ```
2401 /// # tokio_test::block_on(async {
2402 /// use std::time::Duration;
2403 ///
2404 /// use matrix_sdk::{
2405 /// ruma::{event_id, room_id},
2406 /// test_utils::mocks::MatrixMockServer,
2407 /// };
2408 /// use serde_json::json;
2409 ///
2410 /// let mock_server = MatrixMockServer::new().await;
2411 /// let client = mock_server.client_builder().build().await;
2412 ///
2413 /// mock_server.mock_room_state_encryption().plain().mount().await;
2414 ///
2415 /// let room = mock_server
2416 /// .sync_joined_room(&client, room_id!("!room_id:localhost"))
2417 /// .await;
2418 ///
2419 /// mock_server
2420 /// .mock_room_send()
2421 /// .ok_with_delay(event_id!("$some_id"), Duration::from_millis(100))
2422 /// .mock_once()
2423 /// .mount()
2424 /// .await;
2425 ///
2426 /// let result = room.send_raw("m.room.message", json!({ "body": "Hello world" })).await?;
2427 ///
2428 /// assert_eq!(
2429 /// event_id!("$some_id"),
2430 /// result.response.event_id,
2431 /// "The event ID we mocked should match the one we received when we sent the event"
2432 /// );
2433 /// # anyhow::Ok(()) });
2434 /// ```
2435 pub fn ok_with_delay(
2436 self,
2437 returned_event_id: impl Into<OwnedEventId>,
2438 delay: Duration,
2439 ) -> MatrixMock<'a> {
2440 let event_id = returned_event_id.into();
2441 self.respond_with(
2442 ResponseTemplate::new(200)
2443 .set_body_json(json!({ "event_id": event_id }))
2444 .set_delay(delay),
2445 )
2446 }
2447
2448 /// Returns a send endpoint that emulates success, i.e. the event has been
2449 /// sent with the given event id.
2450 ///
2451 /// The sent event is captured and can be accessed using the returned
2452 /// [`Receiver`]. The [`Receiver`] is valid only for a send call. The given
2453 /// `event_sender` are added to the event JSON.
2454 ///
2455 /// # Examples
2456 ///
2457 /// ```no_run
2458 /// # tokio_test::block_on(async {
2459 /// use matrix_sdk::{
2460 /// ruma::{
2461 /// event_id, events::room::message::RoomMessageEventContent, room_id,
2462 /// },
2463 /// test_utils::mocks::MatrixMockServer,
2464 /// };
2465 /// use matrix_sdk_test::JoinedRoomBuilder;
2466 ///
2467 /// let room_id = room_id!("!room_id:localhost");
2468 /// let event_id = event_id!("$some_id");
2469 ///
2470 /// let server = MatrixMockServer::new().await;
2471 /// let client = server.client_builder().build().await;
2472 ///
2473 /// let user_id = client.user_id().expect("We should have a user ID by now");
2474 ///
2475 /// let (receiver, mock) =
2476 /// server.mock_room_send().ok_with_capture(event_id, user_id);
2477 ///
2478 /// server
2479 /// .mock_sync()
2480 /// .ok_and_run(&client, |builder| {
2481 /// builder.add_joined_room(JoinedRoomBuilder::new(room_id));
2482 /// })
2483 /// .await;
2484 ///
2485 /// // Mock any additional endpoints that might be needed to send the message.
2486 ///
2487 /// let room = client
2488 /// .get_room(room_id)
2489 /// .expect("We should have access to our room now");
2490 ///
2491 /// let event_id = room
2492 /// .send(RoomMessageEventContent::text_plain("It's a secret to everybody"))
2493 /// .await
2494 /// .expect("We should be able to send an initial message")
2495 /// .response
2496 /// .event_id;
2497 ///
2498 /// let event = receiver.await?;
2499 /// # anyhow::Ok(()) });
2500 /// ```
2501 pub fn ok_with_capture(
2502 self,
2503 returned_event_id: impl Into<OwnedEventId>,
2504 event_sender: impl Into<OwnedUserId>,
2505 ) -> (Receiver<Raw<AnySyncTimelineEvent>>, MatrixMock<'a>) {
2506 let event_id = returned_event_id.into();
2507 let event_sender = event_sender.into();
2508
2509 let (sender, receiver) = oneshot::channel();
2510 let sender = Arc::new(Mutex::new(Some(sender)));
2511
2512 let ret = self.respond_with(move |request: &Request| {
2513 if let Some(sender) = sender.lock().unwrap().take() {
2514 let uri = &request.url;
2515 let path_segments = uri.path_segments();
2516 let maybe_event_type = path_segments.and_then(|mut s| s.nth_back(1));
2517 let event_type = maybe_event_type
2518 .as_ref()
2519 .map(|&e| e.to_owned())
2520 .unwrap_or("m.room.message".to_owned());
2521
2522 let body: Value =
2523 request.body_json().expect("The received body should be valid JSON");
2524
2525 let event = json!({
2526 "event_id": event_id.clone(),
2527 "sender": event_sender,
2528 "type": event_type,
2529 "origin_server_ts": MilliSecondsSinceUnixEpoch::now(),
2530 "content": body,
2531 });
2532
2533 let event: Raw<AnySyncTimelineEvent> = from_value(event)
2534 .expect("We should be able to create a raw event from the content");
2535
2536 sender.send(event).expect("We should be able to send the event to the receiver");
2537 }
2538
2539 ResponseTemplate::new(200).set_body_json(json!({ "event_id": event_id.clone() }))
2540 });
2541
2542 (receiver, ret)
2543 }
2544}
2545
2546/// A prebuilt mock for sending a state event in a room.
2547#[derive(Default)]
2548pub struct RoomSendStateEndpoint {
2549 state_key: Option<String>,
2550 event_type: Option<StateEventType>,
2551}
2552
2553impl<'a> MockEndpoint<'a, RoomSendStateEndpoint> {
2554 fn generate_path_regexp(endpoint: &RoomSendStateEndpoint) -> String {
2555 format!(
2556 r"^/_matrix/client/v3/rooms/.*/state/{}/{}",
2557 endpoint.event_type.as_ref().map_or_else(|| ".*".to_owned(), |t| t.to_string()),
2558 endpoint.state_key.as_ref().map_or_else(|| ".*".to_owned(), |k| k.to_string())
2559 )
2560 }
2561
2562 /// Ensures that the body of the request is a superset of the provided
2563 /// `body` parameter.
2564 ///
2565 /// # Examples
2566 /// ```
2567 /// # tokio_test::block_on(async {
2568 /// use matrix_sdk::{
2569 /// ruma::{
2570 /// room_id, event_id,
2571 /// events::room::power_levels::RoomPowerLevelsEventContent,
2572 /// room_version_rules::AuthorizationRules
2573 /// },
2574 /// test_utils::mocks::MatrixMockServer
2575 /// };
2576 /// use serde_json::json;
2577 ///
2578 /// let mock_server = MatrixMockServer::new().await;
2579 /// let client = mock_server.client_builder().build().await;
2580 ///
2581 /// mock_server.mock_room_state_encryption().plain().mount().await;
2582 ///
2583 /// let room = mock_server
2584 /// .sync_joined_room(&client, room_id!("!room_id:localhost"))
2585 /// .await;
2586 ///
2587 /// let event_id = event_id!("$some_id");
2588 /// mock_server
2589 /// .mock_room_send_state()
2590 /// .body_matches_partial_json(json!({
2591 /// "redact": 51,
2592 /// }))
2593 /// .ok(event_id)
2594 /// .expect(1)
2595 /// .mount()
2596 /// .await;
2597 ///
2598 /// let mut content = RoomPowerLevelsEventContent::new(&AuthorizationRules::V1);
2599 /// // Update the power level to a non default value.
2600 /// // Otherwise it will be skipped from serialization.
2601 /// content.redact = 51.into();
2602 ///
2603 /// let response = room.send_state_event(content).await?;
2604 ///
2605 /// assert_eq!(
2606 /// event_id,
2607 /// response.event_id,
2608 /// "The event ID we mocked should match the one we received when we sent the event"
2609 /// );
2610 /// # anyhow::Ok(()) });
2611 /// ```
2612 pub fn body_matches_partial_json(self, body: Value) -> Self {
2613 Self { mock: self.mock.and(body_partial_json(body)), ..self }
2614 }
2615
2616 /// Ensures that the send endpoint request uses a specific event type.
2617 ///
2618 /// Note: works with *any* room.
2619 ///
2620 /// # Examples
2621 ///
2622 /// see also [`MatrixMockServer::mock_room_send`] for more context.
2623 ///
2624 /// ```
2625 /// # tokio_test::block_on(async {
2626 /// use matrix_sdk::{
2627 /// ruma::{
2628 /// event_id,
2629 /// events::room::{
2630 /// create::RoomCreateEventContent, power_levels::RoomPowerLevelsEventContent,
2631 /// },
2632 /// events::StateEventType,
2633 /// room_id,
2634 /// room_version_rules::AuthorizationRules,
2635 /// },
2636 /// test_utils::mocks::MatrixMockServer,
2637 /// };
2638 ///
2639 /// let mock_server = MatrixMockServer::new().await;
2640 /// let client = mock_server.client_builder().build().await;
2641 ///
2642 /// mock_server.mock_room_state_encryption().plain().mount().await;
2643 ///
2644 /// let room = mock_server.sync_joined_room(&client, room_id!("!room_id:localhost")).await;
2645 ///
2646 /// let event_id = event_id!("$some_id");
2647 ///
2648 /// mock_server
2649 /// .mock_room_send_state()
2650 /// .for_type(StateEventType::RoomPowerLevels)
2651 /// .ok(event_id)
2652 /// .expect(1)
2653 /// .mount()
2654 /// .await;
2655 ///
2656 /// let response_not_mocked = room.send_state_event(RoomCreateEventContent::new_v11()).await;
2657 /// // The `m.room.reaction` event type should not be mocked by the server.
2658 /// assert!(response_not_mocked.is_err());
2659 ///
2660 /// let response = room.send_state_event(RoomPowerLevelsEventContent::new(&AuthorizationRules::V1)).await?;
2661 /// // The `m.room.message` event type should be mocked by the server.
2662 /// assert_eq!(
2663 /// event_id, response.event_id,
2664 /// "The event ID we mocked should match the one we received when we sent the event"
2665 /// );
2666 ///
2667 /// # anyhow::Ok(()) });
2668 /// ```
2669 pub fn for_type(mut self, event_type: StateEventType) -> Self {
2670 self.endpoint.event_type = Some(event_type);
2671 // Note: we may have already defined a path, but this one ought to be more
2672 // specialized (unless for_key/for_type were called multiple times).
2673 Self { mock: self.mock.and(path_regex(Self::generate_path_regexp(&self.endpoint))), ..self }
2674 }
2675
2676 /// Ensures the event was sent as a delayed event.
2677 ///
2678 /// See also [the MSC](https://github.com/matrix-org/matrix-spec-proposals/pull/4140).
2679 ///
2680 /// Note: works with *any* room.
2681 ///
2682 /// # Examples
2683 ///
2684 /// see also [`MatrixMockServer::mock_room_send`] for more context.
2685 ///
2686 /// ```
2687 /// # tokio_test::block_on(async {
2688 /// use matrix_sdk::{
2689 /// ruma::{
2690 /// api::client::delayed_events::{delayed_state_event, DelayParameters},
2691 /// events::{room::create::RoomCreateEventContent, AnyStateEventContent},
2692 /// room_id,
2693 /// time::Duration,
2694 /// },
2695 /// test_utils::mocks::MatrixMockServer,
2696 /// };
2697 /// use wiremock::ResponseTemplate;
2698 /// use serde_json::json;
2699 ///
2700 /// let mock_server = MatrixMockServer::new().await;
2701 /// let client = mock_server.client_builder().build().await;
2702 ///
2703 /// mock_server.mock_room_state_encryption().plain().mount().await;
2704 ///
2705 /// let room = mock_server.sync_joined_room(&client, room_id!("!room_id:localhost")).await;
2706 ///
2707 /// mock_server
2708 /// .mock_room_send_state()
2709 /// .match_delayed_event(Duration::from_millis(500))
2710 /// .respond_with(ResponseTemplate::new(200).set_body_json(json!({"delay_id":"$some_id"})))
2711 /// .mock_once()
2712 /// .mount()
2713 /// .await;
2714 ///
2715 /// let response_not_mocked = room.send_state_event(RoomCreateEventContent::new_v11()).await;
2716 /// // A non delayed event should not be mocked by the server.
2717 /// assert!(response_not_mocked.is_err());
2718 ///
2719 /// let r = delayed_state_event::unstable::Request::new(
2720 /// room.room_id().to_owned(),
2721 /// "".to_owned(),
2722 /// DelayParameters::Timeout { timeout: Duration::from_millis(500) },
2723 /// &AnyStateEventContent::RoomCreate(RoomCreateEventContent::new_v11()),
2724 /// )
2725 /// .unwrap();
2726 /// let response = room.client().send(r).await.unwrap();
2727 /// // The delayed `m.room.message` event type should be mocked by the server.
2728 /// assert_eq!("$some_id", response.delay_id);
2729 ///
2730 /// # anyhow::Ok(()) });
2731 /// ```
2732 pub fn match_delayed_event(self, delay: Duration) -> Self {
2733 Self {
2734 mock: self
2735 .mock
2736 .and(query_param("org.matrix.msc4140.delay", delay.as_millis().to_string())),
2737 ..self
2738 }
2739 }
2740
2741 ///
2742 /// ```
2743 /// # tokio_test::block_on(async {
2744 /// use matrix_sdk::{
2745 /// ruma::{
2746 /// event_id,
2747 /// events::{call::member::CallMemberEventContent, AnyStateEventContent},
2748 /// room_id,
2749 /// },
2750 /// test_utils::mocks::MatrixMockServer,
2751 /// };
2752 ///
2753 /// let mock_server = MatrixMockServer::new().await;
2754 /// let client = mock_server.client_builder().build().await;
2755 ///
2756 /// mock_server.mock_room_state_encryption().plain().mount().await;
2757 ///
2758 /// let room = mock_server.sync_joined_room(&client, room_id!("!room_id:localhost")).await;
2759 ///
2760 /// let event_id = event_id!("$some_id");
2761 ///
2762 /// mock_server
2763 /// .mock_room_send_state()
2764 /// .for_key("my_key".to_owned())
2765 /// .ok(event_id)
2766 /// .expect(1)
2767 /// .mount()
2768 /// .await;
2769 ///
2770 /// let response_not_mocked = room
2771 /// .send_state_event_for_key(
2772 /// "",
2773 /// AnyStateEventContent::CallMember(CallMemberEventContent::new_empty(None)),
2774 /// )
2775 /// .await;
2776 /// // The `m.room.reaction` event type should not be mocked by the server.
2777 /// assert!(response_not_mocked.is_err());
2778 ///
2779 /// let response = room
2780 /// .send_state_event_for_key(
2781 /// "my_key",
2782 /// AnyStateEventContent::CallMember(CallMemberEventContent::new_empty(None)),
2783 /// )
2784 /// .await
2785 /// .unwrap();
2786 ///
2787 /// // The `m.room.message` event type should be mocked by the server.
2788 /// assert_eq!(
2789 /// event_id, response.event_id,
2790 /// "The event ID we mocked should match the one we received when we sent the event"
2791 /// );
2792 /// # anyhow::Ok(()) });
2793 /// ```
2794 pub fn for_key(mut self, state_key: String) -> Self {
2795 self.endpoint.state_key = Some(state_key);
2796 // Note: we may have already defined a path, but this one ought to be more
2797 // specialized (unless for_key/for_type were called multiple times).
2798 Self { mock: self.mock.and(path_regex(Self::generate_path_regexp(&self.endpoint))), ..self }
2799 }
2800
2801 /// Returns a send endpoint that emulates success, i.e. the event has been
2802 /// sent with the given event id.
2803 ///
2804 /// # Examples
2805 /// ```
2806 /// # tokio_test::block_on(async {
2807 /// use matrix_sdk::{ruma::{room_id, event_id}, test_utils::mocks::MatrixMockServer};
2808 /// use serde_json::json;
2809 ///
2810 /// let mock_server = MatrixMockServer::new().await;
2811 /// let client = mock_server.client_builder().build().await;
2812 ///
2813 /// mock_server.mock_room_state_encryption().plain().mount().await;
2814 ///
2815 /// let room = mock_server
2816 /// .sync_joined_room(&client, room_id!("!room_id:localhost"))
2817 /// .await;
2818 ///
2819 /// let event_id = event_id!("$some_id");
2820 /// let send_guard = mock_server
2821 /// .mock_room_send_state()
2822 /// .ok(event_id)
2823 /// .expect(1)
2824 /// .mount_as_scoped()
2825 /// .await;
2826 ///
2827 /// let response = room.send_state_event_raw("m.room.message", "my_key", json!({ "body": "Hello world" })).await?;
2828 ///
2829 /// assert_eq!(
2830 /// event_id,
2831 /// response.event_id,
2832 /// "The event ID we mocked should match the one we received when we sent the event"
2833 /// );
2834 /// # anyhow::Ok(()) });
2835 /// ```
2836 pub fn ok(self, returned_event_id: impl Into<OwnedEventId>) -> MatrixMock<'a> {
2837 self.ok_with_event_id(returned_event_id.into())
2838 }
2839}
2840
2841/// A prebuilt mock for running sync v2.
2842pub struct SyncEndpoint {
2843 sync_response_builder: Arc<Mutex<SyncResponseBuilder>>,
2844}
2845
2846impl<'a> MockEndpoint<'a, SyncEndpoint> {
2847 /// Expect the given timeout, or lack thereof, in the request.
2848 pub fn timeout(mut self, timeout: Option<Duration>) -> Self {
2849 if let Some(timeout) = timeout {
2850 self.mock = self.mock.and(query_param("timeout", timeout.as_millis().to_string()));
2851 } else {
2852 self.mock = self.mock.and(query_param_is_missing("timeout"));
2853 }
2854
2855 self
2856 }
2857
2858 /// Expect the given `set_presence` value in the request.
2859 pub fn set_presence(mut self, presence: impl Into<String>) -> Self {
2860 self.mock = self.mock.and(query_param("set_presence", presence.into()));
2861 self
2862 }
2863
2864 /// Expect no explicit `set_presence` value in the request.
2865 pub fn set_presence_missing(mut self) -> Self {
2866 self.mock = self.mock.and(query_param_is_missing("set_presence"));
2867 self
2868 }
2869
2870 /// Mocks the sync endpoint, using the given function to generate the
2871 /// response.
2872 pub fn ok<F: FnOnce(&mut SyncResponseBuilder)>(self, func: F) -> MatrixMock<'a> {
2873 let json_response = {
2874 let mut builder = self.endpoint.sync_response_builder.lock().unwrap();
2875 func(&mut builder);
2876 builder.build_json_sync_response()
2877 };
2878
2879 self.respond_with(ResponseTemplate::new(200).set_body_json(json_response))
2880 }
2881
2882 /// Temporarily mocks the sync with the given endpoint and runs a client
2883 /// sync with it.
2884 ///
2885 /// After calling this function, the sync endpoint isn't mocked anymore.
2886 ///
2887 /// # Examples
2888 ///
2889 /// ```
2890 /// # tokio_test::block_on(async {
2891 /// use matrix_sdk::{ruma::room_id, test_utils::mocks::MatrixMockServer};
2892 /// use matrix_sdk_test::JoinedRoomBuilder;
2893 ///
2894 /// // First create the mock server and client pair.
2895 /// let mock_server = MatrixMockServer::new().await;
2896 /// let client = mock_server.client_builder().build().await;
2897 /// let room_id = room_id!("!room_id:localhost");
2898 ///
2899 /// // Let's emulate what `MatrixMockServer::sync_joined_room()` does.
2900 /// mock_server
2901 /// .mock_sync()
2902 /// .ok_and_run(&client, |builder| {
2903 /// builder.add_joined_room(JoinedRoomBuilder::new(room_id));
2904 /// })
2905 /// .await;
2906 ///
2907 /// let room = client
2908 /// .get_room(room_id)
2909 /// .expect("The room should be available after we mocked the sync");
2910 /// # anyhow::Ok(()) });
2911 /// ```
2912 pub async fn ok_and_run<F: FnOnce(&mut SyncResponseBuilder)>(self, client: &Client, func: F) {
2913 let _scope = self.ok(func).mount_as_scoped().await;
2914
2915 let _response = client.sync_once(Default::default()).await.unwrap();
2916 }
2917}
2918
2919/// A prebuilt mock for reading the encryption state of a room.
2920pub struct EncryptionStateEndpoint;
2921
2922impl<'a> MockEndpoint<'a, EncryptionStateEndpoint> {
2923 /// Marks the room as encrypted.
2924 ///
2925 /// # Examples
2926 ///
2927 /// ```
2928 /// # tokio_test::block_on(async {
2929 /// use matrix_sdk::{ruma::room_id, test_utils::mocks::MatrixMockServer};
2930 ///
2931 /// let mock_server = MatrixMockServer::new().await;
2932 /// let client = mock_server.client_builder().build().await;
2933 ///
2934 /// mock_server.mock_room_state_encryption().encrypted().mount().await;
2935 ///
2936 /// let room = mock_server
2937 /// .sync_joined_room(&client, room_id!("!room_id:localhost"))
2938 /// .await;
2939 ///
2940 /// assert!(
2941 /// room.latest_encryption_state().await?.is_encrypted(),
2942 /// "The room should be marked as encrypted."
2943 /// );
2944 /// # anyhow::Ok(()) });
2945 /// ```
2946 pub fn encrypted(self) -> MatrixMock<'a> {
2947 self.respond_with(
2948 ResponseTemplate::new(200)
2949 .set_body_json(EventFactory::new().room_encryption().into_content()),
2950 )
2951 }
2952
2953 /// Marks the room as encrypted, opting into experimental state event
2954 /// encryption.
2955 ///
2956 /// # Examples
2957 ///
2958 /// ```
2959 /// # tokio_test::block_on(async {
2960 /// use matrix_sdk::{ruma::room_id, test_utils::mocks::MatrixMockServer};
2961 ///
2962 /// let mock_server = MatrixMockServer::new().await;
2963 /// let client = mock_server.client_builder().build().await;
2964 ///
2965 /// mock_server.mock_room_state_encryption().state_encrypted().mount().await;
2966 ///
2967 /// let room = mock_server
2968 /// .sync_joined_room(&client, room_id!("!room_id:localhost"))
2969 /// .await;
2970 ///
2971 /// assert!(
2972 /// room.latest_encryption_state().await?.is_state_encrypted(),
2973 /// "The room should be marked as state encrypted."
2974 /// );
2975 /// # anyhow::Ok(()) });
2976 #[cfg(feature = "experimental-encrypted-state-events")]
2977 pub fn state_encrypted(self) -> MatrixMock<'a> {
2978 self.respond_with(ResponseTemplate::new(200).set_body_json(
2979 EventFactory::new().room_encryption_with_state_encryption().into_content(),
2980 ))
2981 }
2982
2983 /// Marks the room as not encrypted.
2984 ///
2985 /// # Examples
2986 ///
2987 /// ```
2988 /// # tokio_test::block_on(async {
2989 /// use matrix_sdk::{ruma::room_id, test_utils::mocks::MatrixMockServer};
2990 ///
2991 /// let mock_server = MatrixMockServer::new().await;
2992 /// let client = mock_server.client_builder().build().await;
2993 ///
2994 /// mock_server.mock_room_state_encryption().plain().mount().await;
2995 ///
2996 /// let room = mock_server
2997 /// .sync_joined_room(&client, room_id!("!room_id:localhost"))
2998 /// .await;
2999 ///
3000 /// assert!(
3001 /// !room.latest_encryption_state().await?.is_encrypted(),
3002 /// "The room should not be marked as encrypted."
3003 /// );
3004 /// # anyhow::Ok(()) });
3005 /// ```
3006 pub fn plain(self) -> MatrixMock<'a> {
3007 self.respond_with(ResponseTemplate::new(404).set_body_json(&*test_json::NOT_FOUND))
3008 }
3009}
3010
3011/// A prebuilt mock for setting the encryption state of a room.
3012pub struct SetEncryptionStateEndpoint;
3013
3014impl<'a> MockEndpoint<'a, SetEncryptionStateEndpoint> {
3015 /// Returns a mock for a successful setting of the encryption state event.
3016 pub fn ok(self, returned_event_id: impl Into<OwnedEventId>) -> MatrixMock<'a> {
3017 self.ok_with_event_id(returned_event_id.into())
3018 }
3019}
3020
3021/// A prebuilt mock for redacting an event in a room.
3022pub struct RoomRedactEndpoint;
3023
3024impl<'a> MockEndpoint<'a, RoomRedactEndpoint> {
3025 /// Returns a redact endpoint that emulates success, i.e. the redaction
3026 /// event has been sent with the given event id.
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 getting a single event in a room.
3033pub struct RoomEventEndpoint {
3034 room: Option<OwnedRoomId>,
3035 match_event_id: bool,
3036}
3037
3038impl<'a> MockEndpoint<'a, RoomEventEndpoint> {
3039 /// Limits the scope of this mock to a specific room.
3040 pub fn room(mut self, room: impl Into<OwnedRoomId>) -> Self {
3041 self.endpoint.room = Some(room.into());
3042 self
3043 }
3044
3045 /// Whether the mock checks for the event id from the event.
3046 pub fn match_event_id(mut self) -> Self {
3047 self.endpoint.match_event_id = true;
3048 self
3049 }
3050
3051 /// Returns a redact endpoint that emulates success, i.e. the redaction
3052 /// event has been sent with the given event id.
3053 pub fn ok(self, event: TimelineEvent) -> MatrixMock<'a> {
3054 let event_path = if self.endpoint.match_event_id {
3055 let event_id = event.event_id().expect("an event id is required");
3056 // The event id should begin with `$`, which would be taken as the end of the
3057 // regex so we need to escape it
3058 event_id.as_str().replace("$", "\\$")
3059 } else {
3060 // Event is at the end, so no need to add anything.
3061 "".to_owned()
3062 };
3063
3064 let room_path = self.endpoint.room.map_or_else(|| ".*".to_owned(), |room| room.to_string());
3065
3066 let mock = self
3067 .mock
3068 .and(path_regex(format!(r"^/_matrix/client/v3/rooms/{room_path}/event/{event_path}")))
3069 .respond_with(ResponseTemplate::new(200).set_body_json(event.into_raw().json()));
3070 MatrixMock { server: self.server, mock }
3071 }
3072
3073 /// Returns a room event endpoint mock with a custom [`ResponseTemplate`].
3074 ///
3075 /// The path restriction is applied automatically. This is useful when you
3076 /// need to configure specific response properties like delays.
3077 pub fn ok_with_template(self, template: ResponseTemplate) -> MatrixMock<'a> {
3078 let room_path = self.endpoint.room.map_or_else(|| ".*".to_owned(), |room| room.to_string());
3079 let mock = self
3080 .mock
3081 .and(path_regex(format!(r"^/_matrix/client/v3/rooms/{room_path}/event/")))
3082 .respond_with(template);
3083 MatrixMock { server: self.server, mock }
3084 }
3085}
3086
3087/// A builder pattern for the response to a [`RoomEventContextEndpoint`]
3088/// request.
3089pub struct RoomContextResponseTemplate {
3090 event: TimelineEvent,
3091 events_before: Vec<TimelineEvent>,
3092 events_after: Vec<TimelineEvent>,
3093 start: Option<String>,
3094 end: Option<String>,
3095 state_events: Vec<Raw<AnyStateEvent>>,
3096}
3097
3098impl RoomContextResponseTemplate {
3099 /// Creates a new context response with the given focused event.
3100 pub fn new(event: TimelineEvent) -> Self {
3101 Self {
3102 event,
3103 events_before: Vec::new(),
3104 events_after: Vec::new(),
3105 start: None,
3106 end: None,
3107 state_events: Vec::new(),
3108 }
3109 }
3110
3111 /// Add some events before the target event.
3112 pub fn events_before(mut self, events: Vec<TimelineEvent>) -> Self {
3113 self.events_before = events;
3114 self
3115 }
3116
3117 /// Add some events after the target event.
3118 pub fn events_after(mut self, events: Vec<TimelineEvent>) -> Self {
3119 self.events_after = events;
3120 self
3121 }
3122
3123 /// Set the start token that could be used for paginating backwards.
3124 pub fn start(mut self, start: impl Into<String>) -> Self {
3125 self.start = Some(start.into());
3126 self
3127 }
3128
3129 /// Set the end token that could be used for paginating forwards.
3130 pub fn end(mut self, end: impl Into<String>) -> Self {
3131 self.end = Some(end.into());
3132 self
3133 }
3134
3135 /// Pass some extra state events to this response.
3136 pub fn state_events(mut self, state_events: Vec<Raw<AnyStateEvent>>) -> Self {
3137 self.state_events = state_events;
3138 self
3139 }
3140}
3141
3142/// A prebuilt mock for getting a single event with its context in a room.
3143pub struct RoomEventContextEndpoint {
3144 room: Option<OwnedRoomId>,
3145 match_event_id: bool,
3146}
3147
3148impl<'a> MockEndpoint<'a, RoomEventContextEndpoint> {
3149 /// Limits the scope of this mock to a specific room.
3150 pub fn room(mut self, room: impl Into<OwnedRoomId>) -> Self {
3151 self.endpoint.room = Some(room.into());
3152 self
3153 }
3154
3155 /// Whether the mock checks for the event id from the event.
3156 pub fn match_event_id(mut self) -> Self {
3157 self.endpoint.match_event_id = true;
3158 self
3159 }
3160
3161 /// Returns an endpoint that emulates a successful response.
3162 pub fn ok(self, response: RoomContextResponseTemplate) -> MatrixMock<'a> {
3163 let event_path = if self.endpoint.match_event_id {
3164 let event_id = response.event.event_id().expect("an event id is required");
3165 // The event id should begin with `$`, which would be taken as the end of the
3166 // regex so we need to escape it
3167 event_id.as_str().replace("$", "\\$")
3168 } else {
3169 // Event is at the end, so no need to add anything.
3170 "".to_owned()
3171 };
3172
3173 let room_path = self.endpoint.room.map_or_else(|| ".*".to_owned(), |room| room.to_string());
3174
3175 let mock = self
3176 .mock
3177 .and(path_regex(format!(r"^/_matrix/client/v3/rooms/{room_path}/context/{event_path}")))
3178 .respond_with(ResponseTemplate::new(200).set_body_json(json!({
3179 "event": response.event.into_raw().json(),
3180 "events_before": response.events_before.into_iter().map(|event| event.into_raw().json().to_owned()).collect::<Vec<_>>(),
3181 "events_after": response.events_after.into_iter().map(|event| event.into_raw().json().to_owned()).collect::<Vec<_>>(),
3182 "end": response.end,
3183 "start": response.start,
3184 "state": response.state_events,
3185 })));
3186 MatrixMock { server: self.server, mock }
3187 }
3188}
3189
3190/// A prebuilt mock for the `/messages` endpoint.
3191pub struct RoomMessagesEndpoint;
3192
3193/// A prebuilt mock for getting a room messages in a room.
3194impl<'a> MockEndpoint<'a, RoomMessagesEndpoint> {
3195 /// Expects an optional limit to be set on the request.
3196 pub fn match_limit(self, limit: u32) -> Self {
3197 Self { mock: self.mock.and(query_param("limit", limit.to_string())), ..self }
3198 }
3199
3200 /// Expects an optional `from` to be set on the request.
3201 pub fn match_from(self, from: &str) -> Self {
3202 Self { mock: self.mock.and(query_param("from", from)), ..self }
3203 }
3204
3205 /// Returns a messages endpoint that emulates success, i.e. the messages
3206 /// provided as `response` could be retrieved.
3207 ///
3208 /// Note: pass `chunk` in the correct order: topological for forward
3209 /// pagination, reverse topological for backwards pagination.
3210 pub fn ok(self, response: RoomMessagesResponseTemplate) -> MatrixMock<'a> {
3211 let mut template = ResponseTemplate::new(200).set_body_json(json!({
3212 "start": response.start,
3213 "end": response.end,
3214 "chunk": response.chunk,
3215 "state": response.state,
3216 }));
3217
3218 if let Some(delay) = response.delay {
3219 template = template.set_delay(delay);
3220 }
3221
3222 self.respond_with(template)
3223 }
3224}
3225
3226/// A response to a [`RoomMessagesEndpoint`] query.
3227pub struct RoomMessagesResponseTemplate {
3228 /// The start token for this /messages query.
3229 pub start: String,
3230 /// The end token for this /messages query (previous batch for back
3231 /// paginations, next batch for forward paginations).
3232 pub end: Option<String>,
3233 /// The set of timeline events returned by this query.
3234 pub chunk: Vec<Raw<AnyTimelineEvent>>,
3235 /// The set of state events returned by this query.
3236 pub state: Vec<Raw<AnyStateEvent>>,
3237 /// Optional delay to respond to the query.
3238 pub delay: Option<Duration>,
3239}
3240
3241impl RoomMessagesResponseTemplate {
3242 /// Fill the events returned as part of this response.
3243 pub fn events(mut self, chunk: Vec<impl Into<Raw<AnyTimelineEvent>>>) -> Self {
3244 self.chunk = chunk.into_iter().map(Into::into).collect();
3245 self
3246 }
3247
3248 /// Fill the end token.
3249 pub fn end_token(mut self, token: impl Into<String>) -> Self {
3250 self.end = Some(token.into());
3251 self
3252 }
3253
3254 /// Respond with a given delay to the query.
3255 pub fn with_delay(mut self, delay: Duration) -> Self {
3256 self.delay = Some(delay);
3257 self
3258 }
3259}
3260
3261impl Default for RoomMessagesResponseTemplate {
3262 fn default() -> Self {
3263 Self {
3264 start: "start-token-unused".to_owned(),
3265 end: Default::default(),
3266 chunk: Default::default(),
3267 state: Default::default(),
3268 delay: None,
3269 }
3270 }
3271}
3272
3273/// A prebuilt mock for uploading media.
3274pub struct UploadEndpoint;
3275
3276impl<'a> MockEndpoint<'a, UploadEndpoint> {
3277 /// Expect that the content type matches what's given here.
3278 pub fn expect_mime_type(self, content_type: &str) -> Self {
3279 Self { mock: self.mock.and(header("content-type", content_type)), ..self }
3280 }
3281
3282 /// Returns a upload endpoint that emulates success, i.e. the media has been
3283 /// uploaded to the media server and can be accessed using the given
3284 /// event has been sent with the given [`MxcUri`].
3285 ///
3286 /// The uploaded content is captured and can be accessed using the returned
3287 /// [`Receiver`]. The [`Receiver`] is valid only for a single media
3288 /// upload.
3289 ///
3290 /// # Examples
3291 ///
3292 /// ```no_run
3293 /// # tokio_test::block_on(async {
3294 /// use matrix_sdk::{
3295 /// ruma::{event_id, mxc_uri, room_id},
3296 /// test_utils::mocks::MatrixMockServer,
3297 /// };
3298 ///
3299 /// let mxid = mxc_uri!("mxc://localhost/12345");
3300 ///
3301 /// let server = MatrixMockServer::new().await;
3302 /// let (receiver, upload_mock) = server.mock_upload().ok_with_capture(mxid);
3303 /// let client = server.client_builder().build().await;
3304 ///
3305 /// client.media().upload(&mime::TEXT_PLAIN, vec![1, 2, 3, 4, 5], None).await?;
3306 ///
3307 /// let uploaded = receiver.await?;
3308 ///
3309 /// assert_eq!(uploaded, vec![1, 2, 3, 4, 5]);
3310 /// # anyhow::Ok(()) });
3311 /// ```
3312 pub fn ok_with_capture(self, mxc_id: &MxcUri) -> (Receiver<Vec<u8>>, MatrixMock<'a>) {
3313 let (sender, receiver) = oneshot::channel();
3314 let sender = Arc::new(Mutex::new(Some(sender)));
3315 let response_body = json!({"content_uri": mxc_id});
3316
3317 let ret = self.respond_with(move |request: &Request| {
3318 let maybe_sender = sender.lock().unwrap().take();
3319
3320 if let Some(sender) = maybe_sender {
3321 let body = request.body.clone();
3322 let _ = sender.send(body);
3323 }
3324
3325 ResponseTemplate::new(200).set_body_json(response_body.clone())
3326 });
3327
3328 (receiver, ret)
3329 }
3330
3331 /// Returns a upload endpoint that emulates success, i.e. the media has been
3332 /// uploaded to the media server and can be accessed using the given
3333 /// event has been sent with the given [`MxcUri`].
3334 pub fn ok(self, mxc_id: &MxcUri) -> MatrixMock<'a> {
3335 self.respond_with(ResponseTemplate::new(200).set_body_json(json!({
3336 "content_uri": mxc_id
3337 })))
3338 }
3339}
3340
3341/// A prebuilt mock for resolving a room alias.
3342pub struct ResolveRoomAliasEndpoint;
3343
3344impl<'a> MockEndpoint<'a, ResolveRoomAliasEndpoint> {
3345 /// Sets up the endpoint to only intercept requests for the given room
3346 /// alias.
3347 pub fn for_alias(self, alias: impl Into<String>) -> Self {
3348 let alias = alias.into();
3349 Self {
3350 mock: self.mock.and(path_regex(format!(
3351 r"^/_matrix/client/v3/directory/room/{}",
3352 percent_encoded_path(&alias)
3353 ))),
3354 ..self
3355 }
3356 }
3357
3358 /// Returns a data endpoint with a resolved room alias.
3359 pub fn ok(self, room_id: &str, servers: Vec<String>) -> MatrixMock<'a> {
3360 self.respond_with(ResponseTemplate::new(200).set_body_json(json!({
3361 "room_id": room_id,
3362 "servers": servers,
3363 })))
3364 }
3365
3366 /// Returns a data endpoint for a room alias that does not exit.
3367 pub fn not_found(self) -> MatrixMock<'a> {
3368 self.respond_with(ResponseTemplate::new(404).set_body_json(json!({
3369 "errcode": "M_NOT_FOUND",
3370 "error": "Room alias not found."
3371 })))
3372 }
3373}
3374
3375/// A prebuilt mock for creating a room alias.
3376pub struct CreateRoomAliasEndpoint;
3377
3378impl<'a> MockEndpoint<'a, CreateRoomAliasEndpoint> {
3379 /// Returns a data endpoint for creating a room alias.
3380 pub fn ok(self) -> MatrixMock<'a> {
3381 self.respond_with(ResponseTemplate::new(200).set_body_json(json!({})))
3382 }
3383}
3384
3385/// A prebuilt mock for removing a room alias.
3386pub struct RemoveRoomAliasEndpoint;
3387
3388impl<'a> MockEndpoint<'a, RemoveRoomAliasEndpoint> {
3389 /// Returns a data endpoint for removing a room alias.
3390 pub fn ok(self) -> MatrixMock<'a> {
3391 self.respond_with(ResponseTemplate::new(200).set_body_json(json!({})))
3392 }
3393}
3394
3395/// A prebuilt mock for paginating the public room list.
3396pub struct PublicRoomsEndpoint;
3397
3398impl<'a> MockEndpoint<'a, PublicRoomsEndpoint> {
3399 /// Returns a data endpoint for paginating the public room list.
3400 pub fn ok(
3401 self,
3402 chunk: Vec<PublicRoomsChunk>,
3403 next_batch: Option<String>,
3404 prev_batch: Option<String>,
3405 total_room_count_estimate: Option<u64>,
3406 ) -> MatrixMock<'a> {
3407 self.respond_with(ResponseTemplate::new(200).set_body_json(json!({
3408 "chunk": chunk,
3409 "next_batch": next_batch,
3410 "prev_batch": prev_batch,
3411 "total_room_count_estimate": total_room_count_estimate,
3412 })))
3413 }
3414
3415 /// Returns a data endpoint for paginating the public room list with several
3416 /// `via` params.
3417 ///
3418 /// Each `via` param must be in the `server_map` parameter, otherwise it'll
3419 /// fail.
3420 pub fn ok_with_via_params(
3421 self,
3422 server_map: BTreeMap<OwnedServerName, Vec<PublicRoomsChunk>>,
3423 ) -> MatrixMock<'a> {
3424 self.respond_with(move |req: &Request| {
3425 #[derive(Deserialize)]
3426 struct PartialRequest {
3427 server: Option<OwnedServerName>,
3428 }
3429
3430 let (_, server) = req
3431 .url
3432 .query_pairs()
3433 .into_iter()
3434 .find(|(key, _)| key == "server")
3435 .expect("Server param not found in request URL");
3436 let server = ServerName::parse(server).expect("Couldn't parse server name");
3437 let chunk = server_map.get(&server).expect("Chunk for the server param not found");
3438 ResponseTemplate::new(200).set_body_json(json!({
3439 "chunk": chunk,
3440 "total_room_count_estimate": chunk.len(),
3441 }))
3442 })
3443 }
3444}
3445
3446/// A prebuilt mock for getting the room's visibility in the room directory.
3447pub struct GetRoomVisibilityEndpoint;
3448
3449impl<'a> MockEndpoint<'a, GetRoomVisibilityEndpoint> {
3450 /// Returns an endpoint that get the room's public visibility.
3451 pub fn ok(self, visibility: Visibility) -> MatrixMock<'a> {
3452 self.respond_with(ResponseTemplate::new(200).set_body_json(json!({
3453 "visibility": visibility,
3454 })))
3455 }
3456}
3457
3458/// A prebuilt mock for setting the room's visibility in the room directory.
3459pub struct SetRoomVisibilityEndpoint;
3460
3461impl<'a> MockEndpoint<'a, SetRoomVisibilityEndpoint> {
3462 /// Returns an endpoint that updates the room's visibility.
3463 pub fn ok(self) -> MatrixMock<'a> {
3464 self.respond_with(ResponseTemplate::new(200).set_body_json(json!({})))
3465 }
3466}
3467
3468/// A prebuilt mock for `GET room_keys/version`: storage ("backup") of room
3469/// keys.
3470pub struct RoomKeysVersionEndpoint;
3471
3472impl<'a> MockEndpoint<'a, RoomKeysVersionEndpoint> {
3473 /// Returns an endpoint that says there is a single room keys backup
3474 pub fn exists(self) -> MatrixMock<'a> {
3475 self.respond_with(ResponseTemplate::new(200).set_body_json(json!({
3476 "algorithm": "m.megolm_backup.v1.curve25519-aes-sha2",
3477 "auth_data": {
3478 "public_key": "abcdefg",
3479 "signatures": {},
3480 },
3481 "count": 42,
3482 "etag": "anopaquestring",
3483 "version": "1",
3484 })))
3485 }
3486
3487 /// Returns an endpoint that says there is a single room keys backup
3488 pub fn exists_with_key(self, public_key: &str) -> MatrixMock<'a> {
3489 self.respond_with(ResponseTemplate::new(200).set_body_json(json!({
3490 "algorithm": "m.megolm_backup.v1.curve25519-aes-sha2",
3491 "auth_data": {
3492 "public_key": public_key,
3493 "signatures": {},
3494 },
3495 "count": 42,
3496 "etag": "anopaquestring",
3497 "version": "1",
3498 })))
3499 }
3500
3501 /// Returns an endpoint that says there is no room keys backup
3502 pub fn none(self) -> MatrixMock<'a> {
3503 self.respond_with(ResponseTemplate::new(404).set_body_json(json!({
3504 "errcode": "M_NOT_FOUND",
3505 "error": "No current backup version"
3506 })))
3507 }
3508
3509 /// Returns an endpoint that 429 errors when we get it
3510 pub fn error429(self) -> MatrixMock<'a> {
3511 self.respond_with(ResponseTemplate::new(429).set_body_json(json!({
3512 "errcode": "M_LIMIT_EXCEEDED",
3513 "error": "Too many requests",
3514 "retry_after_ms": 2000
3515 })))
3516 }
3517
3518 /// Returns an endpoint that 404 errors when we get it
3519 pub fn error404(self) -> MatrixMock<'a> {
3520 self.respond_with(ResponseTemplate::new(404))
3521 }
3522}
3523
3524/// A prebuilt mock for `POST room_keys/version`: adding room key backups.
3525pub struct AddRoomKeysVersionEndpoint;
3526
3527impl<'a> MockEndpoint<'a, AddRoomKeysVersionEndpoint> {
3528 /// Returns an endpoint that may be used to add room key backups
3529 pub fn ok(self) -> MatrixMock<'a> {
3530 self.respond_with(ResponseTemplate::new(200).set_body_json(json!({
3531 "version": "1"
3532 })))
3533 .named("POST for the backup creation")
3534 }
3535}
3536
3537/// A prebuilt mock for `DELETE room_keys/version/xxx`: deleting room key
3538/// backups.
3539pub struct DeleteRoomKeysVersionEndpoint;
3540
3541impl<'a> MockEndpoint<'a, DeleteRoomKeysVersionEndpoint> {
3542 /// Returns an endpoint that allows deleting room key backups
3543 pub fn ok(self) -> MatrixMock<'a> {
3544 self.respond_with(ResponseTemplate::new(200).set_body_json(json!({})))
3545 .named("DELETE for the backup deletion")
3546 }
3547}
3548
3549/// A prebuilt mock for the `/sendToDevice` endpoint.
3550///
3551/// This mock can be used to simulate sending to-device messages in tests.
3552pub struct SendToDeviceEndpoint;
3553impl<'a> MockEndpoint<'a, SendToDeviceEndpoint> {
3554 /// Returns a successful response with default data.
3555 pub fn ok(self) -> MatrixMock<'a> {
3556 self.respond_with(ResponseTemplate::new(200).set_body_json(json!({})))
3557 }
3558}
3559
3560/// A prebuilt mock for `GET /members` request.
3561pub struct GetRoomMembersEndpoint;
3562
3563impl<'a> MockEndpoint<'a, GetRoomMembersEndpoint> {
3564 /// Returns a successful get members request with a list of members.
3565 pub fn ok(self, members: Vec<Raw<RoomMemberEvent>>) -> MatrixMock<'a> {
3566 self.respond_with(ResponseTemplate::new(200).set_body_json(json!({
3567 "chunk": members,
3568 })))
3569 }
3570}
3571
3572/// A prebuilt mock for `POST /invite` request.
3573pub struct InviteUserByIdEndpoint;
3574
3575impl<'a> MockEndpoint<'a, InviteUserByIdEndpoint> {
3576 /// Returns a successful invite user by id request.
3577 pub fn ok(self) -> MatrixMock<'a> {
3578 self.respond_with(ResponseTemplate::new(200).set_body_json(json!({})))
3579 }
3580}
3581
3582/// A prebuilt mock for `POST /kick` request.
3583pub struct KickUserEndpoint;
3584
3585impl<'a> MockEndpoint<'a, KickUserEndpoint> {
3586 /// Returns a successful kick user request.
3587 pub fn ok(self) -> MatrixMock<'a> {
3588 self.respond_with(ResponseTemplate::new(200).set_body_json(json!({})))
3589 }
3590}
3591
3592/// A prebuilt mock for `POST /ban` request.
3593pub struct BanUserEndpoint;
3594
3595impl<'a> MockEndpoint<'a, BanUserEndpoint> {
3596 /// Returns a successful ban user request.
3597 pub fn ok(self) -> MatrixMock<'a> {
3598 self.respond_with(ResponseTemplate::new(200).set_body_json(json!({})))
3599 }
3600}
3601
3602/// A prebuilt mock for `GET /versions` request.
3603pub struct VersionsEndpoint {
3604 versions: Vec<&'static str>,
3605 features: BTreeMap<&'static str, bool>,
3606}
3607
3608impl VersionsEndpoint {
3609 // Get a JSON array of commonly supported versions.
3610 fn commonly_supported_versions() -> Vec<&'static str> {
3611 vec![
3612 "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",
3613 "v1.3", "v1.4", "v1.5", "v1.6", "v1.7", "v1.8", "v1.9", "v1.10", "v1.11",
3614 ]
3615 }
3616}
3617
3618impl Default for VersionsEndpoint {
3619 fn default() -> Self {
3620 Self { versions: Self::commonly_supported_versions(), features: BTreeMap::new() }
3621 }
3622}
3623
3624impl<'a> MockEndpoint<'a, VersionsEndpoint> {
3625 /// Returns a successful `/_matrix/client/versions` request.
3626 ///
3627 /// The response will return some commonly supported versions.
3628 pub fn ok(mut self) -> MatrixMock<'a> {
3629 let features = std::mem::take(&mut self.endpoint.features);
3630 let versions = std::mem::take(&mut self.endpoint.versions);
3631 self.respond_with(ResponseTemplate::new(200).set_body_json(json!({
3632 "unstable_features": features,
3633 "versions": versions
3634 })))
3635 }
3636
3637 /// Set the supported flag for the given unstable feature in the response of
3638 /// this endpoint.
3639 pub fn with_feature(mut self, feature: &'static str, supported: bool) -> Self {
3640 self.endpoint.features.insert(feature, supported);
3641 self
3642 }
3643
3644 /// Indicate that push for encrypted events is supported by this homeserver.
3645 pub fn with_push_encrypted_events(self) -> Self {
3646 self.with_feature("org.matrix.msc4028", true)
3647 }
3648
3649 /// Indicate that thread subscriptions are supported by this homeserver.
3650 pub fn with_thread_subscriptions(self) -> Self {
3651 self.with_feature("org.matrix.msc4306", true)
3652 }
3653
3654 /// Indicate that simplified sliding sync is supported by this homeserver.
3655 pub fn with_simplified_sliding_sync(self) -> Self {
3656 self.with_feature("org.matrix.simplified_msc3575", true)
3657 }
3658
3659 /// Set the supported versions in the response of this endpoint.
3660 pub fn with_versions(mut self, versions: Vec<&'static str>) -> Self {
3661 self.endpoint.versions = versions;
3662 self
3663 }
3664}
3665
3666/// A prebuilt mock for the room summary endpoint.
3667pub struct RoomSummaryEndpoint;
3668
3669impl<'a> MockEndpoint<'a, RoomSummaryEndpoint> {
3670 /// Returns a successful response with some default data for the given room
3671 /// id.
3672 pub fn ok(self, room_id: &RoomId) -> MatrixMock<'a> {
3673 self.respond_with(ResponseTemplate::new(200).set_body_json(json!({
3674 "room_id": room_id,
3675 "guest_can_join": true,
3676 "num_joined_members": 1,
3677 "world_readable": true,
3678 "join_rule": "public",
3679 })))
3680 }
3681}
3682
3683/// A prebuilt mock to set a room's pinned events.
3684pub struct SetRoomPinnedEventsEndpoint;
3685
3686impl<'a> MockEndpoint<'a, SetRoomPinnedEventsEndpoint> {
3687 /// Returns a successful response with a given event id.
3688 /// id.
3689 pub fn ok(self, event_id: OwnedEventId) -> MatrixMock<'a> {
3690 self.ok_with_event_id(event_id)
3691 }
3692
3693 /// Returns an error response with a generic error code indicating the
3694 /// client is not authorized to set pinned events.
3695 pub fn unauthorized(self) -> MatrixMock<'a> {
3696 self.respond_with(ResponseTemplate::new(400))
3697 }
3698}
3699
3700/// A prebuilt mock for `GET /account/whoami` request.
3701pub struct WhoAmIEndpoint;
3702
3703impl<'a> MockEndpoint<'a, WhoAmIEndpoint> {
3704 /// Returns a successful response with the default device ID.
3705 pub fn ok(self) -> MatrixMock<'a> {
3706 self.ok_with_device_id(device_id!("D3V1C31D"))
3707 }
3708
3709 /// Returns a successful response with the given device ID.
3710 pub fn ok_with_device_id(self, device_id: &DeviceId) -> MatrixMock<'a> {
3711 self.respond_with(ResponseTemplate::new(200).set_body_json(json!({
3712 "user_id": "@joe:example.org",
3713 "device_id": device_id,
3714 })))
3715 }
3716}
3717
3718/// A prebuilt mock for `POST /keys/upload` request.
3719pub struct UploadKeysEndpoint;
3720
3721impl<'a> MockEndpoint<'a, UploadKeysEndpoint> {
3722 /// Returns a successful response with counts of 10 curve25519 keys and 20
3723 /// signed curve25519 keys.
3724 pub fn ok(self) -> MatrixMock<'a> {
3725 self.respond_with(ResponseTemplate::new(200).set_body_json(json!({
3726 "one_time_key_counts": {
3727 "curve25519": 10,
3728 "signed_curve25519": 20,
3729 },
3730 })))
3731 }
3732
3733 /// Returns a successful response with the given number of signed curve25519
3734 /// one-time keys.
3735 pub fn ok_with_signed_curve_key_count(self, n: u32) -> MatrixMock<'a> {
3736 self.respond_with(ResponseTemplate::new(200).set_body_json(json!({
3737 "one_time_key_counts": {
3738 "signed_curve25519": n,
3739 },
3740 })))
3741 }
3742}
3743
3744/// A prebuilt mock for `POST /keys/query` request.
3745pub struct QueryKeysEndpoint;
3746
3747impl<'a> MockEndpoint<'a, QueryKeysEndpoint> {
3748 /// Returns a successful empty response.
3749 pub fn ok(self) -> MatrixMock<'a> {
3750 self.respond_with(ResponseTemplate::new(200).set_body_json(json!({})))
3751 }
3752}
3753
3754/// A prebuilt mock for `GET /.well-known/matrix/client` request.
3755pub struct WellKnownEndpoint;
3756
3757impl<'a> MockEndpoint<'a, WellKnownEndpoint> {
3758 /// Returns a successful response with the URL for this homeserver.
3759 pub fn ok(self) -> MatrixMock<'a> {
3760 let server_uri = self.server.uri();
3761 self.ok_with_homeserver_url(&server_uri)
3762 }
3763
3764 /// Returns a successful response with the given homeserver URL.
3765 pub fn ok_with_homeserver_url(self, homeserver_url: &str) -> MatrixMock<'a> {
3766 self.respond_with(ResponseTemplate::new(200).set_body_json(json!({
3767 "m.homeserver": {
3768 "base_url": homeserver_url,
3769 },
3770 "m.rtc_foci": [
3771 {
3772 "type": "livekit",
3773 "livekit_service_url": "https://livekit.example.com",
3774 },
3775 ],
3776 })))
3777 }
3778
3779 /// Returns a 404 error response.
3780 pub fn error404(self) -> MatrixMock<'a> {
3781 self.respond_with(ResponseTemplate::new(404))
3782 }
3783}
3784
3785/// A prebuilt mock for `POST /keys/device_signing/upload` request.
3786pub struct UploadCrossSigningKeysEndpoint;
3787
3788impl<'a> MockEndpoint<'a, UploadCrossSigningKeysEndpoint> {
3789 /// Returns a successful empty response.
3790 pub fn ok(self) -> MatrixMock<'a> {
3791 self.respond_with(ResponseTemplate::new(200).set_body_json(json!({})))
3792 }
3793
3794 /// Returns an error response with a UIAA stage that failed to authenticate
3795 /// because of an invalid password.
3796 pub fn uiaa_invalid_password(self) -> MatrixMock<'a> {
3797 self.respond_with(ResponseTemplate::new(401).set_body_json(json!({
3798 "errcode": "M_FORBIDDEN",
3799 "error": "Invalid password",
3800 "flows": [
3801 {
3802 "stages": [
3803 "m.login.password"
3804 ]
3805 }
3806 ],
3807 "params": {},
3808 "session": "oFIJVvtEOCKmRUTYKTYIIPHL"
3809 })))
3810 }
3811
3812 /// Returns an error response with a UIAA stage.
3813 pub fn uiaa(self) -> MatrixMock<'a> {
3814 self.respond_with(ResponseTemplate::new(401).set_body_json(json!({
3815 "flows": [
3816 {
3817 "stages": [
3818 "m.login.password"
3819 ]
3820 }
3821 ],
3822 "params": {},
3823 "session": "oFIJVvtEOCKmRUTYKTYIIPHL"
3824 })))
3825 }
3826
3827 /// Returns an error response with an unstable OAuth 2.0 UIAA stage.
3828 pub fn uiaa_unstable_oauth(self) -> MatrixMock<'a> {
3829 let server_uri = self.server.uri();
3830 self.respond_with(ResponseTemplate::new(401).set_body_json(json!({
3831 "session": "dummy",
3832 "flows": [{
3833 "stages": [ "org.matrix.cross_signing_reset" ]
3834 }],
3835 "params": {
3836 "org.matrix.cross_signing_reset": {
3837 "url": format!("{server_uri}/account/?action=org.matrix.cross_signing_reset"),
3838 }
3839 },
3840 "msg": "To reset your end-to-end encryption cross-signing identity, you first need to approve it and then try again."
3841 })))
3842 }
3843
3844 /// Returns an error response with a stable OAuth 2.0 UIAA stage with the
3845 /// given session key and optional extra error message.
3846 pub fn uiaa_stable_oauth(
3847 self,
3848 session: &str,
3849 extra_error: Option<&StandardErrorBody>,
3850 ) -> MatrixMock<'a> {
3851 let mut json = json!({
3852 "session": session,
3853 "flows": [{
3854 "stages": [ "m.oauth" ]
3855 }],
3856 "params": {
3857 "m.oauth": {
3858 "url": format!("{}/account/?action=org.matrix.cross_signing_reset", self.server.uri()),
3859 }
3860 },
3861 "msg": "To reset your end-to-end encryption cross-signing identity, you first need to approve it and then try again."
3862 });
3863
3864 if let Some(extra_error) = extra_error {
3865 let extra_json = as_variant!(
3866 serde_json::to_value(extra_error)
3867 .expect("extra error should serialize successfully"),
3868 Value::Object
3869 )
3870 .expect("extra error should be a JSON object");
3871
3872 let json_object = json.as_object_mut().expect("UIAA response should be a JSON object");
3873 json_object.extend(extra_json);
3874 }
3875
3876 self.respond_with(ResponseTemplate::new(401).set_body_json(json))
3877 }
3878}
3879
3880/// A prebuilt mock for `POST /keys/signatures/upload` request.
3881pub struct UploadCrossSigningSignaturesEndpoint;
3882
3883impl<'a> MockEndpoint<'a, UploadCrossSigningSignaturesEndpoint> {
3884 /// Returns a successful empty response.
3885 pub fn ok(self) -> MatrixMock<'a> {
3886 self.respond_with(ResponseTemplate::new(200).set_body_json(json!({})))
3887 }
3888}
3889
3890/// A prebuilt mock for the MSC3814 `GET /dehydrated_device` request.
3891#[cfg(feature = "e2e-encryption")]
3892pub struct GetDehydratedDeviceEndpoint;
3893
3894#[cfg(feature = "e2e-encryption")]
3895impl<'a> MockEndpoint<'a, GetDehydratedDeviceEndpoint> {
3896 /// Returns a successful response carrying the given dehydrated device.
3897 pub fn ok(self, device_id: &DeviceId, device_data: Value) -> MatrixMock<'a> {
3898 self.respond_with(ResponseTemplate::new(200).set_body_json(json!({
3899 "device_id": device_id,
3900 "device_data": device_data,
3901 })))
3902 }
3903
3904 /// Returns a 404 response with `M_NOT_FOUND`, signalling that no device
3905 /// is currently dehydrated for the user.
3906 pub fn not_found(self) -> MatrixMock<'a> {
3907 self.respond_with(ResponseTemplate::new(404).set_body_json(json!({
3908 "errcode": "M_NOT_FOUND",
3909 "error": "No dehydrated device found",
3910 })))
3911 }
3912}
3913
3914/// A prebuilt mock for the MSC3814 `PUT /dehydrated_device` request.
3915#[cfg(feature = "e2e-encryption")]
3916pub struct PutDehydratedDeviceEndpoint;
3917
3918#[cfg(feature = "e2e-encryption")]
3919impl<'a> MockEndpoint<'a, PutDehydratedDeviceEndpoint> {
3920 /// Returns a successful response echoing the supplied device ID.
3921 pub fn ok(self, device_id: &DeviceId) -> MatrixMock<'a> {
3922 self.respond_with(ResponseTemplate::new(200).set_body_json(json!({
3923 "device_id": device_id,
3924 })))
3925 }
3926
3927 /// Returns a successful response, computing the response body from the
3928 /// `device_id` field in the request payload. Useful when the caller does
3929 /// not know the device ID ahead of time.
3930 pub fn ok_echo(self) -> MatrixMock<'a> {
3931 self.respond_with(|req: &Request| {
3932 #[derive(serde::Deserialize)]
3933 struct Body {
3934 device_id: OwnedDeviceId,
3935 }
3936 let body: Body = req.body_json().expect("dehydrated device PUT body");
3937 ResponseTemplate::new(200).set_body_json(json!({ "device_id": body.device_id }))
3938 })
3939 }
3940}
3941
3942/// A prebuilt mock for the MSC3814 `DELETE /dehydrated_device` request.
3943#[cfg(feature = "e2e-encryption")]
3944pub struct DeleteDehydratedDeviceEndpoint;
3945
3946#[cfg(feature = "e2e-encryption")]
3947impl<'a> MockEndpoint<'a, DeleteDehydratedDeviceEndpoint> {
3948 /// Returns a successful response echoing the deleted device ID.
3949 pub fn ok(self, device_id: &DeviceId) -> MatrixMock<'a> {
3950 self.respond_with(ResponseTemplate::new(200).set_body_json(json!({
3951 "device_id": device_id,
3952 })))
3953 }
3954
3955 /// Returns a 404 with `M_NOT_FOUND`.
3956 pub fn not_found(self) -> MatrixMock<'a> {
3957 self.respond_with(ResponseTemplate::new(404).set_body_json(json!({
3958 "errcode": "M_NOT_FOUND",
3959 "error": "No dehydrated device to delete",
3960 })))
3961 }
3962}
3963
3964/// A prebuilt mock for the MSC3814
3965/// `POST /dehydrated_device/{device_id}/events` request.
3966#[cfg(feature = "e2e-encryption")]
3967pub struct DehydratedDeviceEventsEndpoint;
3968
3969#[cfg(feature = "e2e-encryption")]
3970impl<'a> MockEndpoint<'a, DehydratedDeviceEventsEndpoint> {
3971 /// Returns a successful response with the supplied events array and an
3972 /// optional pagination cursor.
3973 pub fn ok(self, events: Vec<Value>, next_batch: Option<&str>) -> MatrixMock<'a> {
3974 self.respond_with(ResponseTemplate::new(200).set_body_json(json!({
3975 "events": events,
3976 "next_batch": next_batch,
3977 })))
3978 }
3979
3980 /// Constrain the mock to only match requests whose `next_batch` body field
3981 /// equals the given token. Pair with [`Self::match_missing_next_batch`]
3982 /// for the initial request in a paginated flow.
3983 pub fn match_next_batch(mut self, token: &str) -> Self {
3984 self.mock = self.mock.and(body_partial_json(json!({ "next_batch": token })));
3985 self
3986 }
3987
3988 /// Constrain the mock to only match requests whose body has no
3989 /// `next_batch` field (i.e. the first call in a paginated flow).
3990 pub fn match_missing_next_batch(mut self) -> Self {
3991 self.mock = self.mock.and(body_json(json!({})));
3992 self
3993 }
3994}
3995
3996/// A prebuilt mock for the room leave endpoint.
3997pub struct RoomLeaveEndpoint;
3998
3999impl<'a> MockEndpoint<'a, RoomLeaveEndpoint> {
4000 /// Returns a successful response with some default data for the given room
4001 /// id.
4002 pub fn ok(self, room_id: &RoomId) -> MatrixMock<'a> {
4003 self.respond_with(ResponseTemplate::new(200).set_body_json(json!({
4004 "room_id": room_id,
4005 })))
4006 }
4007
4008 /// Returns a `M_FORBIDDEN` response.
4009 pub fn forbidden(self) -> MatrixMock<'a> {
4010 self.respond_with(ResponseTemplate::new(403).set_body_json(json!({
4011 "errcode": "M_FORBIDDEN",
4012 "error": "sowwy",
4013 })))
4014 }
4015}
4016
4017/// A prebuilt mock for the room forget endpoint.
4018pub struct RoomForgetEndpoint;
4019
4020impl<'a> MockEndpoint<'a, RoomForgetEndpoint> {
4021 /// Returns a successful response with some default data for the given room
4022 /// id.
4023 pub fn ok(self) -> MatrixMock<'a> {
4024 self.respond_with(ResponseTemplate::new(200).set_body_json(json!({})))
4025 }
4026}
4027
4028/// A prebuilt mock for `POST /logout` request.
4029pub struct LogoutEndpoint;
4030
4031impl<'a> MockEndpoint<'a, LogoutEndpoint> {
4032 /// Returns a successful empty response.
4033 pub fn ok(self) -> MatrixMock<'a> {
4034 self.respond_with(ResponseTemplate::new(200).set_body_json(json!({})))
4035 }
4036}
4037
4038/// A prebuilt mock for a `GET /rooms/{roomId}/threads` request.
4039pub struct RoomThreadsEndpoint;
4040
4041impl<'a> MockEndpoint<'a, RoomThreadsEndpoint> {
4042 /// Expects an optional `from` to be set on the request.
4043 pub fn match_from(self, from: &str) -> Self {
4044 Self { mock: self.mock.and(query_param("from", from)), ..self }
4045 }
4046
4047 /// Returns a successful response with some optional events and previous
4048 /// batch token.
4049 pub fn ok(
4050 self,
4051 chunk: Vec<Raw<AnyTimelineEvent>>,
4052 next_batch: Option<String>,
4053 ) -> MatrixMock<'a> {
4054 self.respond_with(ResponseTemplate::new(200).set_body_json(json!({
4055 "chunk": chunk,
4056 "next_batch": next_batch
4057 })))
4058 }
4059}
4060
4061/// A prebuilt mock for a `GET /rooms/{roomId}/relations/{eventId}` family of
4062/// requests.
4063#[derive(Default)]
4064pub struct RoomRelationsEndpoint {
4065 event_id: Option<OwnedEventId>,
4066 spec: Option<IncludeRelations>,
4067}
4068
4069impl<'a> MockEndpoint<'a, RoomRelationsEndpoint> {
4070 /// Expects an optional `from` to be set on the request.
4071 pub fn match_from(self, from: &str) -> Self {
4072 Self { mock: self.mock.and(query_param("from", from)), ..self }
4073 }
4074
4075 /// Expects an optional `limit` to be set on the request.
4076 pub fn match_limit(self, limit: u32) -> Self {
4077 Self { mock: self.mock.and(query_param("limit", limit.to_string())), ..self }
4078 }
4079
4080 /// Match the given subrequest, according to the given specification.
4081 pub fn match_subrequest(mut self, spec: IncludeRelations) -> Self {
4082 self.endpoint.spec = Some(spec);
4083 self
4084 }
4085
4086 /// Expects the request to match a specific event id.
4087 pub fn match_target_event(mut self, event_id: OwnedEventId) -> Self {
4088 self.endpoint.event_id = Some(event_id);
4089 self
4090 }
4091
4092 /// Returns a successful response with some optional events and pagination
4093 /// tokens.
4094 pub fn ok(mut self, response: RoomRelationsResponseTemplate) -> MatrixMock<'a> {
4095 // Escape the leading $ to not confuse the regular expression engine.
4096 let event_spec = self
4097 .endpoint
4098 .event_id
4099 .take()
4100 .map(|event_id| event_id.as_str().replace("$", "\\$"))
4101 .unwrap_or_else(|| ".*".to_owned());
4102
4103 match self.endpoint.spec.take() {
4104 Some(IncludeRelations::RelationsOfType(rel_type)) => {
4105 self.mock = self.mock.and(path_regex(format!(
4106 r"^/_matrix/client/v1/rooms/.*/relations/{event_spec}/{rel_type}$"
4107 )));
4108 }
4109 Some(IncludeRelations::RelationsOfTypeAndEventType(rel_type, event_type)) => {
4110 self.mock = self.mock.and(path_regex(format!(
4111 r"^/_matrix/client/v1/rooms/.*/relations/{event_spec}/{rel_type}/{event_type}$"
4112 )));
4113 }
4114 _ => {
4115 self.mock = self.mock.and(path_regex(format!(
4116 r"^/_matrix/client/v1/rooms/.*/relations/{event_spec}",
4117 )));
4118 }
4119 }
4120
4121 self.respond_with(ResponseTemplate::new(200).set_body_json(json!({
4122 "chunk": response.chunk,
4123 "next_batch": response.next_batch,
4124 "prev_batch": response.prev_batch,
4125 "recursion_depth": response.recursion_depth,
4126 })))
4127 }
4128}
4129
4130/// Helper function to set up a [`MockBuilder`] so it intercepts the account
4131/// data URLs.
4132fn global_account_data_mock_builder(
4133 builder: MockBuilder,
4134 user_id: &UserId,
4135 event_type: GlobalAccountDataEventType,
4136) -> MockBuilder {
4137 builder
4138 .and(path_regex(format!(r"^/_matrix/client/v3/user/{user_id}/account_data/{event_type}",)))
4139}
4140
4141/// A prebuilt mock for a `GET
4142/// /_matrix/client/v3/user/{userId}/account_data/io.element.recent_emoji`
4143/// request, which fetches the recently used emojis in the account data.
4144#[cfg(feature = "experimental-element-recent-emojis")]
4145pub struct GetRecentEmojisEndpoint;
4146
4147#[cfg(feature = "experimental-element-recent-emojis")]
4148impl<'a> MockEndpoint<'a, GetRecentEmojisEndpoint> {
4149 /// Returns a mock for a successful fetch of the recently used emojis in the
4150 /// account data.
4151 pub fn ok(self, user_id: &UserId, emojis: Vec<(String, UInt)>) -> MatrixMock<'a> {
4152 let mock =
4153 global_account_data_mock_builder(self.mock, user_id, "io.element.recent_emoji".into())
4154 .respond_with(
4155 ResponseTemplate::new(200).set_body_json(json!({ "recent_emoji": emojis })),
4156 );
4157 MatrixMock { server: self.server, mock }
4158 }
4159}
4160
4161/// A prebuilt mock for a `PUT
4162/// /_matrix/client/v3/user/{userId}/account_data/io.element.recent_emoji`
4163/// request, which updates the recently used emojis in the account data.
4164#[cfg(feature = "experimental-element-recent-emojis")]
4165pub struct UpdateRecentEmojisEndpoint {
4166 pub(crate) request_body: Option<Vec<(String, UInt)>>,
4167}
4168
4169#[cfg(feature = "experimental-element-recent-emojis")]
4170impl UpdateRecentEmojisEndpoint {
4171 /// Creates a new instance of the recent update recent emojis mock endpoint.
4172 fn new() -> Self {
4173 Self { request_body: None }
4174 }
4175}
4176
4177#[cfg(feature = "experimental-element-recent-emojis")]
4178impl<'a> MockEndpoint<'a, UpdateRecentEmojisEndpoint> {
4179 /// Returns a mock that will check the body of the request, making sure its
4180 /// contents match the provided list of emojis.
4181 pub fn match_emojis_in_request_body(self, emojis: Vec<(String, UInt)>) -> Self {
4182 Self::new(
4183 self.server,
4184 self.mock.and(body_json(json!(RecentEmojisContent::new(emojis)))),
4185 self.endpoint,
4186 )
4187 }
4188
4189 /// Returns a mock for a successful update of the recent emojis account data
4190 /// event. The request body contents should match the provided emoji
4191 /// list.
4192 #[cfg(feature = "experimental-element-recent-emojis")]
4193 pub fn ok(self, user_id: &UserId) -> MatrixMock<'a> {
4194 let mock =
4195 global_account_data_mock_builder(self.mock, user_id, "io.element.recent_emoji".into())
4196 .respond_with(ResponseTemplate::new(200).set_body_json(()));
4197 MatrixMock { server: self.server, mock }
4198 }
4199}
4200
4201/// A prebuilt mock for a `GET
4202/// /_matrix/client/v3/user/{userId}/account_data/m.secret_storage.default_key`
4203/// request, which fetches the ID of the default secret storage key.
4204#[cfg(feature = "e2e-encryption")]
4205pub struct GetDefaultSecretStorageKeyEndpoint;
4206
4207#[cfg(feature = "e2e-encryption")]
4208impl<'a> MockEndpoint<'a, GetDefaultSecretStorageKeyEndpoint> {
4209 /// Returns a mock for a successful fetch of the default secret storage key.
4210 pub fn ok(self, user_id: &UserId, key_id: &str) -> MatrixMock<'a> {
4211 let mock = global_account_data_mock_builder(
4212 self.mock,
4213 user_id,
4214 GlobalAccountDataEventType::SecretStorageDefaultKey,
4215 )
4216 .respond_with(ResponseTemplate::new(200).set_body_json(json!({
4217 "key": key_id
4218 })));
4219 MatrixMock { server: self.server, mock }
4220 }
4221}
4222
4223/// A prebuilt mock for a `GET
4224/// /_matrix/client/v3/user/{userId}/account_data/m.secret_storage.key.{keyId}`
4225/// request, which fetches information about a secret storage key.
4226#[cfg(feature = "e2e-encryption")]
4227pub struct GetSecretStorageKeyEndpoint;
4228
4229#[cfg(feature = "e2e-encryption")]
4230impl<'a> MockEndpoint<'a, GetSecretStorageKeyEndpoint> {
4231 /// Returns a mock for a successful fetch of the secret storage key
4232 pub fn ok(
4233 self,
4234 user_id: &UserId,
4235 secret_storage_key_event_content: &ruma::events::secret_storage::key::SecretStorageKeyEventContent,
4236 ) -> MatrixMock<'a> {
4237 let mock = global_account_data_mock_builder(
4238 self.mock,
4239 user_id,
4240 GlobalAccountDataEventType::SecretStorageKey(
4241 secret_storage_key_event_content.key_id.clone(),
4242 ),
4243 )
4244 .respond_with(ResponseTemplate::new(200).set_body_json(secret_storage_key_event_content));
4245 MatrixMock { server: self.server, mock }
4246 }
4247}
4248
4249/// A prebuilt mock for a `GET
4250/// /_matrix/client/v3/user/{userId}/account_data/m.cross_signing.master`
4251/// request, which fetches information about the master signing key.
4252#[cfg(feature = "e2e-encryption")]
4253pub struct GetMasterSigningKeyEndpoint;
4254
4255#[cfg(feature = "e2e-encryption")]
4256impl<'a> MockEndpoint<'a, GetMasterSigningKeyEndpoint> {
4257 /// Returns a mock for a successful fetch of the master signing key
4258 pub fn ok<B: Serialize>(self, user_id: &UserId, key_json: B) -> MatrixMock<'a> {
4259 let mock = global_account_data_mock_builder(
4260 self.mock,
4261 user_id,
4262 GlobalAccountDataEventType::from("m.cross_signing.master".to_owned()),
4263 )
4264 .respond_with(ResponseTemplate::new(200).set_body_json(key_json));
4265 MatrixMock { server: self.server, mock }
4266 }
4267}
4268
4269/// A response to a [`RoomRelationsEndpoint`] query.
4270#[derive(Default)]
4271pub struct RoomRelationsResponseTemplate {
4272 /// The set of timeline events returned by this query.
4273 pub chunk: Vec<Raw<AnyTimelineEvent>>,
4274
4275 /// An opaque string representing a pagination token, which semantics depend
4276 /// on the direction used in the request.
4277 pub next_batch: Option<String>,
4278
4279 /// An opaque string representing a pagination token, which semantics depend
4280 /// on the direction used in the request.
4281 pub prev_batch: Option<String>,
4282
4283 /// If `recurse` was set on the request, the depth to which the server
4284 /// recursed.
4285 ///
4286 /// If `recurse` was not set, this field must be absent.
4287 pub recursion_depth: Option<u32>,
4288}
4289
4290impl RoomRelationsResponseTemplate {
4291 /// Fill the events returned as part of this response.
4292 pub fn events(mut self, chunk: Vec<impl Into<Raw<AnyTimelineEvent>>>) -> Self {
4293 self.chunk = chunk.into_iter().map(Into::into).collect();
4294 self
4295 }
4296
4297 /// Fill the `next_batch` token returned as part of this response.
4298 pub fn next_batch(mut self, token: impl Into<String>) -> Self {
4299 self.next_batch = Some(token.into());
4300 self
4301 }
4302
4303 /// Fill the `prev_batch` token returned as part of this response.
4304 pub fn prev_batch(mut self, token: impl Into<String>) -> Self {
4305 self.prev_batch = Some(token.into());
4306 self
4307 }
4308
4309 /// Fill the recursion depth returned in this response.
4310 pub fn recursion_depth(mut self, depth: u32) -> Self {
4311 self.recursion_depth = Some(depth);
4312 self
4313 }
4314}
4315
4316/// A prebuilt mock for `POST /rooms/{roomId}/receipt/{receiptType}/{eventId}`
4317/// request.
4318pub struct ReceiptEndpoint;
4319
4320impl<'a> MockEndpoint<'a, ReceiptEndpoint> {
4321 /// Returns a successful empty response.
4322 pub fn ok(self) -> MatrixMock<'a> {
4323 self.respond_with(ResponseTemplate::new(200).set_body_json(json!({})))
4324 }
4325
4326 /// Ensures that the body of the request is a superset of the provided
4327 /// `body` parameter.
4328 pub fn body_matches_partial_json(self, body: Value) -> Self {
4329 Self { mock: self.mock.and(body_partial_json(body)), ..self }
4330 }
4331
4332 /// Ensures that the body of the request is the exact provided `body`
4333 /// parameter.
4334 pub fn body_json(self, body: Value) -> Self {
4335 Self { mock: self.mock.and(body_json(body)), ..self }
4336 }
4337
4338 /// Ensures that the request matches a specific receipt thread.
4339 pub fn match_thread(self, thread: ReceiptThread) -> Self {
4340 if let Some(thread_str) = thread.as_str() {
4341 self.body_matches_partial_json(json!({
4342 "thread_id": thread_str
4343 }))
4344 } else {
4345 self
4346 }
4347 }
4348
4349 /// Ensures that the request matches a specific event id.
4350 pub fn match_event_id(self, event_id: &EventId) -> Self {
4351 Self {
4352 mock: self.mock.and(path_regex(format!(
4353 r"^/_matrix/client/v3/rooms/.*/receipt/.*/{}$",
4354 event_id.as_str().replace("$", "\\$")
4355 ))),
4356 ..self
4357 }
4358 }
4359}
4360
4361/// A prebuilt mock for `POST /rooms/{roomId}/read_markers` request.
4362pub struct ReadMarkersEndpoint;
4363
4364impl<'a> MockEndpoint<'a, ReadMarkersEndpoint> {
4365 /// Returns a successful empty response.
4366 pub fn ok(self) -> MatrixMock<'a> {
4367 self.respond_with(ResponseTemplate::new(200).set_body_json(json!({})))
4368 }
4369}
4370
4371/// A prebuilt mock for `PUT /user/{userId}/rooms/{roomId}/account_data/{type}`
4372/// request.
4373pub struct RoomAccountDataEndpoint;
4374
4375impl<'a> MockEndpoint<'a, RoomAccountDataEndpoint> {
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 `GET /_matrix/client/v1/media/config` request.
4383pub struct AuthenticatedMediaConfigEndpoint;
4384
4385impl<'a> MockEndpoint<'a, AuthenticatedMediaConfigEndpoint> {
4386 /// Returns a successful response with the provided max upload size.
4387 pub fn ok(self, max_upload_size: UInt) -> MatrixMock<'a> {
4388 self.respond_with(ResponseTemplate::new(200).set_body_json(json!({
4389 "m.upload.size": max_upload_size,
4390 })))
4391 }
4392
4393 /// Returns a successful response with a maxed out max upload size.
4394 pub fn ok_default(self) -> MatrixMock<'a> {
4395 self.respond_with(ResponseTemplate::new(200).set_body_json(json!({
4396 "m.upload.size": UInt::MAX,
4397 })))
4398 }
4399}
4400
4401/// A prebuilt mock for `GET /_matrix/media/v3/config` request.
4402pub struct MediaConfigEndpoint;
4403
4404impl<'a> MockEndpoint<'a, MediaConfigEndpoint> {
4405 /// Returns a successful response with the provided max upload size.
4406 pub fn ok(self, max_upload_size: UInt) -> MatrixMock<'a> {
4407 self.respond_with(ResponseTemplate::new(200).set_body_json(json!({
4408 "m.upload.size": max_upload_size,
4409 })))
4410 }
4411}
4412
4413/// A prebuilt mock for `POST /login` requests.
4414pub struct LoginEndpoint;
4415
4416impl<'a> MockEndpoint<'a, LoginEndpoint> {
4417 /// Returns a successful response.
4418 pub fn ok(self) -> MatrixMock<'a> {
4419 self.respond_with(ResponseTemplate::new(200).set_body_json(&*test_json::LOGIN))
4420 }
4421
4422 /// Returns a given response on POST /login requests
4423 ///
4424 /// # Arguments
4425 ///
4426 /// * `response` - The response that the mock server sends on POST /login
4427 /// requests.
4428 ///
4429 /// # Returns
4430 ///
4431 /// Returns a [`MatrixMock`] which can be mounted.
4432 ///
4433 /// # Examples
4434 ///
4435 /// ```
4436 /// use matrix_sdk::test_utils::mocks::{
4437 /// LoginResponseTemplate200, MatrixMockServer,
4438 /// };
4439 /// use matrix_sdk_test::async_test;
4440 /// use ruma::{device_id, time::Duration, user_id};
4441 ///
4442 /// #[async_test]
4443 /// async fn test_ok_with() {
4444 /// let server = MatrixMockServer::new().await;
4445 /// server
4446 /// .mock_login()
4447 /// .ok_with(LoginResponseTemplate200::new(
4448 /// "qwerty",
4449 /// device_id!("DEADBEEF"),
4450 /// user_id!("@cheeky_monkey:matrix.org"),
4451 /// ))
4452 /// .mount()
4453 /// .await;
4454 ///
4455 /// let client = server.client_builder().unlogged().build().await;
4456 ///
4457 /// let result = client
4458 /// .matrix_auth()
4459 /// .login_username("example", "wordpass")
4460 /// .send()
4461 /// .await
4462 /// .unwrap();
4463 ///
4464 /// assert!(
4465 /// result.access_tokesn.unwrap() == "qwerty",
4466 /// "wrong access token in response"
4467 /// );
4468 /// assert!(
4469 /// result.device_id.unwrap() == "DEADBEEF",
4470 /// "wrong device id in response"
4471 /// );
4472 /// assert!(
4473 /// result.user_id.unwrap() == "@cheeky_monkey:matrix.org",
4474 /// "wrong user id in response"
4475 /// );
4476 /// }
4477 /// ```
4478 pub fn ok_with(self, response: LoginResponseTemplate200) -> MatrixMock<'a> {
4479 self.respond_with(ResponseTemplate::new(200).set_body_json(json!({
4480 "access_token": response.access_token,
4481 "device_id": response.device_id,
4482 "user_id": response.user_id,
4483 "expires_in": response.expires_in.map(|duration| { duration.as_millis() }),
4484 "refresh_token": response.refresh_token,
4485 "well_known": response.well_known.map(|vals| {
4486 json!({
4487 "m.homeserver": {
4488 "base_url": vals.homeserver_url
4489 },
4490 "m.identity_server": vals.identity_url.map(|url| {
4491 json!({
4492 "base_url": url
4493 })
4494 })
4495 })
4496 }),
4497 })))
4498 }
4499
4500 /// Ensures that the body of the request is a superset of the provided
4501 /// `body` parameter.
4502 pub fn body_matches_partial_json(self, body: Value) -> Self {
4503 Self { mock: self.mock.and(body_partial_json(body)), ..self }
4504 }
4505}
4506
4507#[derive(Default)]
4508struct LoginResponseWellKnown {
4509 /// Required if well_known is used: The base URL for the homeserver for
4510 /// client-server connections.
4511 homeserver_url: String,
4512
4513 /// Required if well_known and m.identity_server are used: The base URL for
4514 /// the identity server for client-server connections.
4515 identity_url: Option<String>,
4516}
4517
4518/// A response to a [`LoginEndpoint`] query with status code 200.
4519#[derive(Default)]
4520pub struct LoginResponseTemplate200 {
4521 /// Required: An access token for the account. This access token can then be
4522 /// used to authorize other requests.
4523 access_token: Option<String>,
4524
4525 /// Required: ID of the logged-in device. Will be the same as the
4526 /// corresponding parameter in the request, if one was specified.
4527 device_id: Option<OwnedDeviceId>,
4528
4529 /// The lifetime of the access token, in milliseconds. Once the access token
4530 /// has expired a new access token can be obtained by using the provided
4531 /// refresh token. If no refresh token is provided, the client will need
4532 /// to re-log in to obtain a new access token. If not given, the client
4533 /// can assume that the access token will not expire.
4534 expires_in: Option<Duration>,
4535
4536 /// A refresh token for the account. This token can be used to obtain a new
4537 /// access token when it expires by calling the /refresh endpoint.
4538 refresh_token: Option<String>,
4539
4540 /// Required: The fully-qualified Matrix ID for the account.
4541 user_id: Option<OwnedUserId>,
4542
4543 /// Optional client configuration provided by the server.
4544 well_known: Option<LoginResponseWellKnown>,
4545}
4546
4547impl LoginResponseTemplate200 {
4548 /// Constructor for empty response
4549 pub fn new<T1: Into<OwnedDeviceId>, T2: Into<OwnedUserId>>(
4550 access_token: &str,
4551 device_id: T1,
4552 user_id: T2,
4553 ) -> Self {
4554 Self {
4555 access_token: Some(access_token.to_owned()),
4556 device_id: Some(device_id.into()),
4557 user_id: Some(user_id.into()),
4558 ..Default::default()
4559 }
4560 }
4561
4562 /// sets expires_in
4563 pub fn expires_in(mut self, value: Duration) -> Self {
4564 self.expires_in = Some(value);
4565 self
4566 }
4567
4568 /// sets refresh_token
4569 pub fn refresh_token(mut self, value: &str) -> Self {
4570 self.refresh_token = Some(value.to_owned());
4571 self
4572 }
4573
4574 /// sets well_known which takes a homeserver_url and an optional
4575 /// identity_url
4576 pub fn well_known(mut self, homeserver_url: String, identity_url: Option<String>) -> Self {
4577 self.well_known = Some(LoginResponseWellKnown { homeserver_url, identity_url });
4578 self
4579 }
4580}
4581
4582/// A prebuilt mock for `GET /devices` requests.
4583pub struct DevicesEndpoint;
4584
4585impl<'a> MockEndpoint<'a, DevicesEndpoint> {
4586 /// Returns a successful response.
4587 pub fn ok(self) -> MatrixMock<'a> {
4588 self.respond_with(ResponseTemplate::new(200).set_body_json(&*test_json::DEVICES))
4589 }
4590}
4591
4592/// A prebuilt mock for `GET /devices/{deviceId}` requests.
4593pub struct GetDeviceEndpoint;
4594
4595impl<'a> MockEndpoint<'a, GetDeviceEndpoint> {
4596 /// Returns a successful response.
4597 pub fn ok(self) -> MatrixMock<'a> {
4598 self.respond_with(ResponseTemplate::new(200).set_body_json(&*test_json::DEVICE))
4599 }
4600}
4601
4602/// A prebuilt mock for `POST /user_directory/search` requests.
4603pub struct UserDirectoryEndpoint;
4604
4605impl<'a> MockEndpoint<'a, UserDirectoryEndpoint> {
4606 /// Returns a successful response.
4607 pub fn ok(self) -> MatrixMock<'a> {
4608 self.respond_with(
4609 ResponseTemplate::new(200)
4610 .set_body_json(&*test_json::search_users::SEARCH_USERS_RESPONSE),
4611 )
4612 }
4613}
4614
4615/// A prebuilt mock for `POST /createRoom` requests.
4616pub struct CreateRoomEndpoint;
4617
4618impl<'a> MockEndpoint<'a, CreateRoomEndpoint> {
4619 /// Returns a successful response.
4620 pub fn ok(self) -> MatrixMock<'a> {
4621 self.respond_with(
4622 ResponseTemplate::new(200).set_body_json(json!({ "room_id": "!room:example.org"})),
4623 )
4624 }
4625}
4626
4627/// A prebuilt mock for `POST /rooms/{roomId}/upgrade` requests.
4628pub struct UpgradeRoomEndpoint;
4629
4630impl<'a> MockEndpoint<'a, UpgradeRoomEndpoint> {
4631 /// Returns a successful response with desired replacement_room ID.
4632 pub fn ok_with(self, new_room_id: &RoomId) -> MatrixMock<'a> {
4633 self.respond_with(
4634 ResponseTemplate::new(200)
4635 .set_body_json(json!({ "replacement_room": new_room_id.as_str()})),
4636 )
4637 }
4638}
4639
4640/// A prebuilt mock for `POST /media/v1/create` requests.
4641pub struct MediaAllocateEndpoint;
4642
4643impl<'a> MockEndpoint<'a, MediaAllocateEndpoint> {
4644 /// Returns a successful response.
4645 pub fn ok(self) -> MatrixMock<'a> {
4646 self.respond_with(ResponseTemplate::new(200).set_body_json(json!({
4647 "content_uri": "mxc://example.com/AQwafuaFswefuhsfAFAgsw"
4648 })))
4649 }
4650}
4651
4652/// A prebuilt mock for `PUT /media/v3/upload/{server_name}/{media_id}`
4653/// requests.
4654pub struct MediaAllocatedUploadEndpoint;
4655
4656impl<'a> MockEndpoint<'a, MediaAllocatedUploadEndpoint> {
4657 /// Returns a successful response.
4658 pub fn ok(self) -> MatrixMock<'a> {
4659 self.respond_with(ResponseTemplate::new(200).set_body_json(json!({})))
4660 }
4661}
4662
4663/// A prebuilt mock for `GET /media/v3/download` requests.
4664pub struct MediaDownloadEndpoint;
4665
4666impl<'a> MockEndpoint<'a, MediaDownloadEndpoint> {
4667 /// Returns a successful response with a plain text content.
4668 pub fn ok_plain_text(self) -> MatrixMock<'a> {
4669 self.respond_with(ResponseTemplate::new(200).set_body_string("Hello, World!"))
4670 }
4671
4672 /// Returns a successful response with a fake image content.
4673 pub fn ok_image(self) -> MatrixMock<'a> {
4674 self.respond_with(
4675 ResponseTemplate::new(200).set_body_raw(b"binaryjpegfullimagedata", "image/jpeg"),
4676 )
4677 }
4678}
4679
4680/// A prebuilt mock for `GET /media/v3/thumbnail` requests.
4681pub struct MediaThumbnailEndpoint;
4682
4683impl<'a> MockEndpoint<'a, MediaThumbnailEndpoint> {
4684 /// Returns a successful response with a fake image content.
4685 pub fn ok(self) -> MatrixMock<'a> {
4686 self.respond_with(
4687 ResponseTemplate::new(200).set_body_raw(b"binaryjpegthumbnaildata", "image/jpeg"),
4688 )
4689 }
4690}
4691
4692/// A prebuilt mock for `GET /client/v1/media/download` requests.
4693pub struct AuthedMediaDownloadEndpoint;
4694
4695impl<'a> MockEndpoint<'a, AuthedMediaDownloadEndpoint> {
4696 /// Returns a successful response with a plain text content.
4697 pub fn ok_plain_text(self) -> MatrixMock<'a> {
4698 self.respond_with(ResponseTemplate::new(200).set_body_string("Hello, World!"))
4699 }
4700
4701 /// Returns a successful response with the given bytes.
4702 pub fn ok_bytes(self, bytes: Vec<u8>) -> MatrixMock<'a> {
4703 self.respond_with(
4704 ResponseTemplate::new(200).set_body_raw(bytes, "application/octet-stream"),
4705 )
4706 }
4707
4708 /// Returns a successful response with a fake image content.
4709 pub fn ok_image(self) -> MatrixMock<'a> {
4710 self.respond_with(
4711 ResponseTemplate::new(200).set_body_raw(b"binaryjpegfullimagedata", "image/jpeg"),
4712 )
4713 }
4714}
4715
4716/// A prebuilt mock for `GET /client/v1/media/thumbnail` requests.
4717pub struct AuthedMediaThumbnailEndpoint;
4718
4719impl<'a> MockEndpoint<'a, AuthedMediaThumbnailEndpoint> {
4720 /// Returns a successful response with a fake image content.
4721 pub fn ok(self) -> MatrixMock<'a> {
4722 self.respond_with(
4723 ResponseTemplate::new(200).set_body_raw(b"binaryjpegthumbnaildata", "image/jpeg"),
4724 )
4725 }
4726}
4727
4728/// A prebuilt mock for `GET /client/v3/rooms/{room_id}/join` requests.
4729pub struct JoinRoomEndpoint {
4730 room_id: OwnedRoomId,
4731}
4732
4733impl<'a> MockEndpoint<'a, JoinRoomEndpoint> {
4734 /// Returns a successful response using the provided [`RoomId`].
4735 pub fn ok(self) -> MatrixMock<'a> {
4736 let room_id = self.endpoint.room_id.to_owned();
4737
4738 self.respond_with(ResponseTemplate::new(200).set_body_json(json!({
4739 "room_id": room_id,
4740 })))
4741 }
4742}
4743
4744#[derive(Default)]
4745struct ThreadSubscriptionMatchers {
4746 /// Optional room id to match in the query.
4747 room_id: Option<OwnedRoomId>,
4748 /// Optional thread root event id to match in the query.
4749 thread_root: Option<OwnedEventId>,
4750}
4751
4752impl ThreadSubscriptionMatchers {
4753 /// Match the request parameter against a specific room id.
4754 fn match_room_id(mut self, room_id: OwnedRoomId) -> Self {
4755 self.room_id = Some(room_id);
4756 self
4757 }
4758
4759 /// Match the request parameter against a specific thread root event id.
4760 fn match_thread_id(mut self, thread_root: OwnedEventId) -> Self {
4761 self.thread_root = Some(thread_root);
4762 self
4763 }
4764
4765 /// Compute the final URI for the thread subscription endpoint.
4766 fn endpoint_regexp_uri(&self) -> String {
4767 if self.room_id.is_some() || self.thread_root.is_some() {
4768 format!(
4769 "^/_matrix/client/unstable/io.element.msc4306/rooms/{}/thread/{}/subscription$",
4770 self.room_id.as_deref().map(|s| s.as_str()).unwrap_or(".*"),
4771 self.thread_root.as_deref().map(|s| s.as_str()).unwrap_or(".*").replace("$", "\\$")
4772 )
4773 } else {
4774 "^/_matrix/client/unstable/io.element.msc4306/rooms/.*/thread/.*/subscription$"
4775 .to_owned()
4776 }
4777 }
4778}
4779
4780/// A prebuilt mock for `GET
4781/// /client/*/rooms/{room_id}/threads/{thread_root}/subscription`
4782#[derive(Default)]
4783pub struct RoomGetThreadSubscriptionEndpoint {
4784 matchers: ThreadSubscriptionMatchers,
4785}
4786
4787impl<'a> MockEndpoint<'a, RoomGetThreadSubscriptionEndpoint> {
4788 /// Returns a successful response for the given thread subscription.
4789 pub fn ok(mut self, automatic: bool) -> MatrixMock<'a> {
4790 self.mock = self.mock.and(path_regex(self.endpoint.matchers.endpoint_regexp_uri()));
4791 self.respond_with(ResponseTemplate::new(200).set_body_json(json!({
4792 "automatic": automatic
4793 })))
4794 }
4795
4796 /// Match the request parameter against a specific room id.
4797 pub fn match_room_id(mut self, room_id: OwnedRoomId) -> Self {
4798 self.endpoint.matchers = self.endpoint.matchers.match_room_id(room_id);
4799 self
4800 }
4801 /// Match the request parameter against a specific thread root event id.
4802 pub fn match_thread_id(mut self, thread_root: OwnedEventId) -> Self {
4803 self.endpoint.matchers = self.endpoint.matchers.match_thread_id(thread_root);
4804 self
4805 }
4806}
4807
4808/// A prebuilt mock for `PUT
4809/// /client/*/rooms/{room_id}/threads/{thread_root}/subscription`
4810#[derive(Default)]
4811pub struct RoomPutThreadSubscriptionEndpoint {
4812 matchers: ThreadSubscriptionMatchers,
4813}
4814
4815impl<'a> MockEndpoint<'a, RoomPutThreadSubscriptionEndpoint> {
4816 /// Returns a successful response for the given setting of thread
4817 /// subscription.
4818 pub fn ok(mut self) -> MatrixMock<'a> {
4819 self.mock = self.mock.and(path_regex(self.endpoint.matchers.endpoint_regexp_uri()));
4820 self.respond_with(ResponseTemplate::new(200))
4821 }
4822
4823 /// Returns that the server skipped an automated thread subscription,
4824 /// because the user unsubscribed to the thread after the event id passed in
4825 /// the automatic subscription.
4826 pub fn conflicting_unsubscription(mut self) -> MatrixMock<'a> {
4827 self.mock = self.mock.and(path_regex(self.endpoint.matchers.endpoint_regexp_uri()));
4828 self.respond_with(ResponseTemplate::new(409).set_body_json(json!({
4829 "errcode": "IO.ELEMENT.MSC4306.M_CONFLICTING_UNSUBSCRIPTION",
4830 "error": "the user unsubscribed after the subscription event id"
4831 })))
4832 }
4833
4834 /// Match the request parameter against a specific room id.
4835 pub fn match_room_id(mut self, room_id: OwnedRoomId) -> Self {
4836 self.endpoint.matchers = self.endpoint.matchers.match_room_id(room_id);
4837 self
4838 }
4839 /// Match the request parameter against a specific thread root event id.
4840 pub fn match_thread_id(mut self, thread_root: OwnedEventId) -> Self {
4841 self.endpoint.matchers = self.endpoint.matchers.match_thread_id(thread_root);
4842 self
4843 }
4844 /// Match the request body's `automatic` field against a specific event id.
4845 pub fn match_automatic_event_id(mut self, up_to_event_id: &EventId) -> Self {
4846 self.mock = self.mock.and(body_json(json!({
4847 "automatic": up_to_event_id
4848 })));
4849 self
4850 }
4851}
4852
4853/// A prebuilt mock for `DELETE
4854/// /client/*/rooms/{room_id}/threads/{thread_root}/subscription`
4855#[derive(Default)]
4856pub struct RoomDeleteThreadSubscriptionEndpoint {
4857 matchers: ThreadSubscriptionMatchers,
4858}
4859
4860impl<'a> MockEndpoint<'a, RoomDeleteThreadSubscriptionEndpoint> {
4861 /// Returns a successful response for the deletion of a given thread
4862 /// subscription.
4863 pub fn ok(mut self) -> MatrixMock<'a> {
4864 self.mock = self.mock.and(path_regex(self.endpoint.matchers.endpoint_regexp_uri()));
4865 self.respond_with(ResponseTemplate::new(200))
4866 }
4867
4868 /// Match the request parameter against a specific room id.
4869 pub fn match_room_id(mut self, room_id: OwnedRoomId) -> Self {
4870 self.endpoint.matchers = self.endpoint.matchers.match_room_id(room_id);
4871 self
4872 }
4873 /// Match the request parameter against a specific thread root event id.
4874 pub fn match_thread_id(mut self, thread_root: OwnedEventId) -> Self {
4875 self.endpoint.matchers = self.endpoint.matchers.match_thread_id(thread_root);
4876 self
4877 }
4878}
4879
4880/// A prebuilt mock for `PUT
4881/// /_matrix/client/v3/pushrules/global/{kind}/{ruleId}/enabled`.
4882pub struct EnablePushRuleEndpoint;
4883
4884impl<'a> MockEndpoint<'a, EnablePushRuleEndpoint> {
4885 /// Returns a successful empty JSON response.
4886 pub fn ok(self) -> MatrixMock<'a> {
4887 self.ok_empty_json()
4888 }
4889}
4890
4891/// A prebuilt mock for `PUT
4892/// /_matrix/client/v3/pushrules/global/{kind}/{ruleId}/actions`.
4893pub struct SetPushRulesActionsEndpoint;
4894
4895impl<'a> MockEndpoint<'a, SetPushRulesActionsEndpoint> {
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}`.
4904pub struct SetPushRulesEndpoint;
4905
4906impl<'a> MockEndpoint<'a, SetPushRulesEndpoint> {
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 `DELETE
4914/// /_matrix/client/v3/pushrules/global/{kind}/{ruleId}`.
4915pub struct DeletePushRulesEndpoint;
4916
4917impl<'a> MockEndpoint<'a, DeletePushRulesEndpoint> {
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 the federation version endpoint.
4925pub struct FederationVersionEndpoint;
4926
4927impl<'a> MockEndpoint<'a, FederationVersionEndpoint> {
4928 /// Returns a successful response with the given server name and version.
4929 pub fn ok(self, server_name: &str, version: &str) -> MatrixMock<'a> {
4930 let response_body = json!({
4931 "server": {
4932 "name": server_name,
4933 "version": version
4934 }
4935 });
4936 self.respond_with(ResponseTemplate::new(200).set_body_json(response_body))
4937 }
4938
4939 /// Returns a successful response with empty/missing server information.
4940 pub fn ok_empty(self) -> MatrixMock<'a> {
4941 let response_body = json!({});
4942 self.respond_with(ResponseTemplate::new(200).set_body_json(response_body))
4943 }
4944}
4945
4946/// A prebuilt mock for `GET ^/_matrix/client/v3/thread_subscriptions`.
4947#[derive(Default)]
4948pub struct GetThreadSubscriptionsEndpoint {
4949 /// New thread subscriptions per (room id, thread root event id).
4950 subscribed: BTreeMap<OwnedRoomId, BTreeMap<OwnedEventId, ThreadSubscription>>,
4951 /// New thread unsubscriptions per (room id, thread root event id).
4952 unsubscribed: BTreeMap<OwnedRoomId, BTreeMap<OwnedEventId, ThreadUnsubscription>>,
4953 /// Optional delay to respond to the query.
4954 delay: Option<Duration>,
4955}
4956
4957impl<'a> MockEndpoint<'a, GetThreadSubscriptionsEndpoint> {
4958 /// Add a single thread subscription to the response.
4959 pub fn add_subscription(
4960 mut self,
4961 room_id: OwnedRoomId,
4962 thread_root: OwnedEventId,
4963 subscription: ThreadSubscription,
4964 ) -> Self {
4965 self.endpoint.subscribed.entry(room_id).or_default().insert(thread_root, subscription);
4966 self
4967 }
4968
4969 /// Add a single thread unsubscription to the response.
4970 pub fn add_unsubscription(
4971 mut self,
4972 room_id: OwnedRoomId,
4973 thread_root: OwnedEventId,
4974 unsubscription: ThreadUnsubscription,
4975 ) -> Self {
4976 self.endpoint.unsubscribed.entry(room_id).or_default().insert(thread_root, unsubscription);
4977 self
4978 }
4979
4980 /// Respond with a given delay to the query.
4981 pub fn with_delay(mut self, delay: Duration) -> Self {
4982 self.endpoint.delay = Some(delay);
4983 self
4984 }
4985
4986 /// Match the `from` query parameter to a given value.
4987 pub fn match_from(self, from: &str) -> Self {
4988 Self { mock: self.mock.and(query_param("from", from)), ..self }
4989 }
4990 /// Match the `to` query parameter to a given value.
4991 pub fn match_to(self, to: &str) -> Self {
4992 Self { mock: self.mock.and(query_param("to", to)), ..self }
4993 }
4994
4995 /// Returns a successful response with the given thread subscriptions, and
4996 /// "end" parameter to be used in the next query.
4997 pub fn ok(self, end: Option<String>) -> MatrixMock<'a> {
4998 let response_body = json!({
4999 "subscribed": self.endpoint.subscribed,
5000 "unsubscribed": self.endpoint.unsubscribed,
5001 "end": end,
5002 });
5003
5004 let mut template = ResponseTemplate::new(200).set_body_json(response_body);
5005
5006 if let Some(delay) = self.endpoint.delay {
5007 template = template.set_delay(delay);
5008 }
5009
5010 self.respond_with(template)
5011 }
5012}
5013
5014/// A prebuilt mock for `GET /client/*/rooms/{roomId}/hierarchy`
5015#[derive(Default)]
5016pub struct GetHierarchyEndpoint;
5017
5018impl<'a> MockEndpoint<'a, GetHierarchyEndpoint> {
5019 /// Returns a successful response containing the given room IDs.
5020 pub fn ok_with_room_ids(self, room_ids: Vec<&RoomId>) -> MatrixMock<'a> {
5021 let rooms = room_ids
5022 .iter()
5023 .map(|id| {
5024 json!({
5025 "room_id": id,
5026 "num_joined_members": 1,
5027 "world_readable": false,
5028 "guest_can_join": false,
5029 "children_state": []
5030 })
5031 })
5032 .collect::<Vec<_>>();
5033
5034 self.respond_with(ResponseTemplate::new(200).set_body_json(json!({
5035 "rooms": rooms,
5036 })))
5037 }
5038
5039 /// Returns a successful response containing the given room IDs and children
5040 /// states
5041 pub fn ok_with_room_ids_and_children_state(
5042 self,
5043 room_ids: Vec<&RoomId>,
5044 children_state: Vec<(&RoomId, Vec<&ServerName>)>,
5045 ) -> MatrixMock<'a> {
5046 let children_state = children_state
5047 .into_iter()
5048 .map(|(id, via)| {
5049 json!({
5050 "type":
5051 "m.space.child",
5052 "state_key": id,
5053 "content": { "via": via },
5054 "sender": "@bob:matrix.org",
5055 "origin_server_ts": MilliSecondsSinceUnixEpoch::now()
5056 })
5057 })
5058 .collect::<Vec<_>>();
5059
5060 let rooms = room_ids
5061 .iter()
5062 .map(|id| {
5063 json!({
5064 "room_id": id,
5065 "num_joined_members": 1,
5066 "world_readable": false,
5067 "guest_can_join": false,
5068 "children_state": children_state
5069 })
5070 })
5071 .collect::<Vec<_>>();
5072
5073 self.respond_with(ResponseTemplate::new(200).set_body_json(json!({
5074 "rooms": rooms,
5075 })))
5076 }
5077
5078 /// Returns a successful response with an empty list of rooms.
5079 pub fn ok(self) -> MatrixMock<'a> {
5080 self.respond_with(ResponseTemplate::new(200).set_body_json(json!({
5081 "rooms": []
5082 })))
5083 }
5084}
5085
5086/// A prebuilt mock for `PUT
5087/// /_matrix/client/v3/rooms/{roomId}/state/m.space.child/{stateKey}`
5088pub struct SetSpaceChildEndpoint;
5089
5090impl<'a> MockEndpoint<'a, SetSpaceChildEndpoint> {
5091 /// Returns a successful response with a given event id.
5092 pub fn ok(self, event_id: OwnedEventId) -> MatrixMock<'a> {
5093 self.ok_with_event_id(event_id)
5094 }
5095
5096 /// Returns an error response with a generic error code indicating the
5097 /// client is not authorized to set space children.
5098 pub fn unauthorized(self) -> MatrixMock<'a> {
5099 self.respond_with(ResponseTemplate::new(400))
5100 }
5101}
5102
5103/// A prebuilt mock for `PUT
5104/// /_matrix/client/v3/rooms/{roomId}/state/m.space.parent/{stateKey}`
5105pub struct SetSpaceParentEndpoint;
5106
5107impl<'a> MockEndpoint<'a, SetSpaceParentEndpoint> {
5108 /// Returns a successful response with a given event id.
5109 pub fn ok(self, event_id: OwnedEventId) -> MatrixMock<'a> {
5110 self.ok_with_event_id(event_id)
5111 }
5112
5113 /// Returns an error response with a generic error code indicating the
5114 /// client is not authorized to set space parents.
5115 pub fn unauthorized(self) -> MatrixMock<'a> {
5116 self.respond_with(ResponseTemplate::new(400))
5117 }
5118}
5119
5120/// A prebuilt mock for running simplified sliding sync.
5121pub struct SlidingSyncEndpoint;
5122
5123impl<'a> MockEndpoint<'a, SlidingSyncEndpoint> {
5124 /// Mocks the sliding sync endpoint with the given response.
5125 pub fn ok(self, response: v5::Response) -> MatrixMock<'a> {
5126 // A bit silly that we need to destructure all the fields ourselves, but
5127 // Response isn't serializable :'(
5128 self.respond_with(ResponseTemplate::new(200).set_body_json(json!({
5129 "txn_id": response.txn_id,
5130 "pos": response.pos,
5131 "lists": response.lists,
5132 "rooms": response.rooms,
5133 "extensions": response.extensions,
5134 })))
5135 }
5136
5137 /// Temporarily mocks the sync with the given endpoint and runs a client
5138 /// sync with it.
5139 ///
5140 /// After calling this function, the sync endpoint isn't mocked anymore.
5141 pub async fn ok_and_run<F: FnOnce(SlidingSyncBuilder) -> SlidingSyncBuilder>(
5142 self,
5143 client: &Client,
5144 on_builder: F,
5145 response: v5::Response,
5146 ) {
5147 let _scope = self.ok(response).mount_as_scoped().await;
5148
5149 let sliding_sync =
5150 on_builder(client.sliding_sync("test_id").unwrap()).build().await.unwrap();
5151
5152 let _summary = sliding_sync.sync_once().await.unwrap();
5153 }
5154}
5155
5156/// A prebuilt mock for `GET /_matrix/client/*/profile/{user_id}/{key_name}`.
5157pub struct GetProfileFieldEndpoint {
5158 field: ProfileFieldName,
5159}
5160
5161impl<'a> MockEndpoint<'a, GetProfileFieldEndpoint> {
5162 /// Returns a successful response containing the given value, if any.
5163 pub fn ok_with_value(self, value: Option<Value>) -> MatrixMock<'a> {
5164 if let Some(value) = value {
5165 let field = self.endpoint.field.to_string();
5166 self.respond_with(ResponseTemplate::new(200).set_body_json(json!({
5167 field: value,
5168 })))
5169 } else {
5170 self.ok_empty_json()
5171 }
5172 }
5173}
5174
5175/// A prebuilt mock for `PUT /_matrix/client/*/profile/{user_id}/{key_name}`.
5176pub struct SetProfileFieldEndpoint;
5177
5178impl<'a> MockEndpoint<'a, SetProfileFieldEndpoint> {
5179 /// Returns a successful empty response.
5180 pub fn ok(self) -> MatrixMock<'a> {
5181 self.ok_empty_json()
5182 }
5183
5184 /// Expect the request body to set the given [`ProfileFieldValue`].
5185 pub fn expect_field_value(mut self, value: ProfileFieldValue) -> Self {
5186 let body = BTreeMap::from([(value.field_name(), value.value())]);
5187 self.mock = self.mock.and(body_json(body));
5188 self
5189 }
5190}
5191
5192/// A prebuilt mock for `DELETE /_matrix/client/*/profile/{user_id}/{key_name}`.
5193pub struct DeleteProfileFieldEndpoint;
5194
5195impl<'a> MockEndpoint<'a, DeleteProfileFieldEndpoint> {
5196 /// Returns a successful empty response.
5197 pub fn ok(self) -> MatrixMock<'a> {
5198 self.ok_empty_json()
5199 }
5200}
5201
5202/// A prebuilt mock for `GET /_matrix/client/*/profile/{user_id}`.
5203pub struct GetProfileEndpoint;
5204
5205impl<'a> MockEndpoint<'a, GetProfileEndpoint> {
5206 /// Returns a successful empty response.
5207 pub fn ok_with_fields(self, fields: Vec<ProfileFieldValue>) -> MatrixMock<'a> {
5208 let profile = fields
5209 .iter()
5210 .map(|field| (field.field_name(), field.value()))
5211 .collect::<BTreeMap<_, _>>();
5212 self.respond_with(ResponseTemplate::new(200).set_body_json(profile))
5213 }
5214}
5215
5216/// A prebuilt mock for `GET /_matrix/client/*/capabilities`.
5217pub struct GetHomeserverCapabilitiesEndpoint;
5218
5219impl<'a> MockEndpoint<'a, GetHomeserverCapabilitiesEndpoint> {
5220 /// Returns a successful empty response.
5221 pub fn ok_with_capabilities(self, capabilities: Capabilities) -> MatrixMock<'a> {
5222 self.respond_with(ResponseTemplate::new(200).set_body_json(json!({
5223 "capabilities": capabilities,
5224 })))
5225 }
5226}