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