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