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