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