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