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