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