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},
23};
24
25use matrix_sdk_base::deserialized_responses::TimelineEvent;
26use matrix_sdk_test::{
27 test_json, InvitedRoomBuilder, JoinedRoomBuilder, KnockedRoomBuilder, LeftRoomBuilder,
28 SyncResponseBuilder,
29};
30use percent_encoding::{AsciiSet, CONTROLS};
31use ruma::{
32 api::client::room::Visibility,
33 device_id,
34 directory::PublicRoomsChunk,
35 events::{
36 room::member::RoomMemberEvent, AnyStateEvent, AnyTimelineEvent, MessageLikeEventType,
37 StateEventType,
38 },
39 serde::Raw,
40 time::Duration,
41 DeviceId, MxcUri, OwnedEventId, OwnedRoomId, RoomId, ServerName,
42};
43use serde::Deserialize;
44use serde_json::{json, Value};
45use wiremock::{
46 matchers::{body_partial_json, header, method, path, path_regex, query_param},
47 Mock, MockBuilder, MockGuard, MockServer, Request, Respond, ResponseTemplate, Times,
48};
49
50pub mod oauth;
51
52use super::client::MockClientBuilder;
53use crate::{Client, OwnedServerName, Room};
54
55/// A [`wiremock`] [`MockServer`] along with useful methods to help mocking
56/// Matrix client-server API endpoints easily.
57///
58/// It implements mock endpoints, limiting the shared code as much as possible,
59/// so the mocks are still flexible to use as scoped/unscoped mounts, named, and
60/// so on.
61///
62/// It works like this:
63///
64/// * start by saying which endpoint you'd like to mock, e.g.
65/// [`Self::mock_room_send()`]. This returns a specialized [`MockEndpoint`]
66/// data structure, with its own impl. For this example, it's
67/// `MockEndpoint<RoomSendEndpoint>`.
68/// * configure the response on the endpoint-specific mock data structure. For
69/// instance, if you want the sending to result in a transient failure, call
70/// [`MockEndpoint::error500`]; if you want it to succeed and return the event
71/// `$42`, call [`MockEndpoint::ok()`]. It's still possible to call
72/// [`MockEndpoint::respond_with()`], as we do with wiremock MockBuilder, for
73/// maximum flexibility when the helpers aren't sufficient.
74/// * once the endpoint's response is configured, for any mock builder, you get
75/// a [`MatrixMock`]; this is a plain [`wiremock::Mock`] with the server
76/// curried, so one doesn't have to pass it around when calling
77/// [`MatrixMock::mount()`] or [`MatrixMock::mount_as_scoped()`]. As such, it
78/// mostly defers its implementations to [`wiremock::Mock`] under the hood.
79///
80/// # Examples
81///
82/// ```
83/// # tokio_test::block_on(async {
84/// use matrix_sdk::{ruma::{room_id, event_id}, test_utils::mocks::MatrixMockServer};
85/// use serde_json::json;
86///
87/// // First create the mock server and client pair.
88/// let mock_server = MatrixMockServer::new().await;
89/// let client = mock_server.client_builder().build().await;
90///
91/// // Let's say that our rooms are not encrypted.
92/// mock_server.mock_room_state_encryption().plain().mount().await;
93///
94/// // Let us get a room where we will send an event.
95/// let room = mock_server
96/// .sync_joined_room(&client, room_id!("!room_id:localhost"))
97/// .await;
98///
99/// // Now we mock the endpoint so we can actually send the event.
100/// let event_id = event_id!("$some_id");
101/// let send_guard = mock_server
102/// .mock_room_send()
103/// .ok(event_id)
104/// .expect(1)
105/// .mount_as_scoped()
106/// .await;
107///
108/// // And we send it out.
109/// let response = room.send_raw("m.room.message", json!({ "body": "Hello world" })).await?;
110///
111/// assert_eq!(
112/// event_id,
113/// response.event_id,
114/// "The event ID we mocked should match the one we received when we sent the event"
115/// );
116/// # anyhow::Ok(()) });
117/// ```
118pub struct MatrixMockServer {
119 server: MockServer,
120
121 /// Make the sync response builder stateful, to keep in memory the batch
122 /// token and avoid the client ignoring subsequent responses after the first
123 /// one.
124 sync_response_builder: Arc<Mutex<SyncResponseBuilder>>,
125}
126
127impl MatrixMockServer {
128 /// Create a new [`wiremock`] server specialized for Matrix usage.
129 pub async fn new() -> Self {
130 let server = MockServer::start().await;
131 Self { server, sync_response_builder: Default::default() }
132 }
133
134 /// Creates a new [`MatrixMockServer`] from a [`wiremock`] server.
135 pub fn from_server(server: MockServer) -> Self {
136 Self { server, sync_response_builder: Default::default() }
137 }
138
139 /// Creates a new [`MockClientBuilder`] configured to use this server,
140 /// preconfigured with a session expected by the server endpoints.
141 pub fn client_builder(&self) -> MockClientBuilder {
142 MockClientBuilder::new(self.server.uri())
143 }
144
145 /// Return the underlying [`wiremock`] server.
146 pub fn server(&self) -> &MockServer {
147 &self.server
148 }
149
150 /// Get an `OAuthMockServer` that uses the same mock server as this one.
151 pub fn oauth(&self) -> oauth::OAuthMockServer<'_> {
152 oauth::OAuthMockServer::new(self)
153 }
154
155 /// Mock the given endpoint.
156 fn mock_endpoint<T>(&self, mock: MockBuilder, endpoint: T) -> MockEndpoint<'_, T> {
157 MockEndpoint::new(&self.server, mock, endpoint)
158 }
159
160 /// Overrides the sync/ endpoint with knowledge that the given
161 /// invited/joined/knocked/left room exists, runs a sync and returns the
162 /// given room.
163 ///
164 /// # Examples
165 ///
166 /// ```
167 /// # tokio_test::block_on(async {
168 /// use matrix_sdk::{ruma::{room_id, event_id}, test_utils::mocks::MatrixMockServer};
169 /// use matrix_sdk_test::LeftRoomBuilder;
170 ///
171 /// let mock_server = MatrixMockServer::new().await;
172 /// let client = mock_server.client_builder().build().await;
173 ///
174 /// let left_room = mock_server
175 /// .sync_room(&client, LeftRoomBuilder::new(room_id!("!room_id:localhost")))
176 /// .await;
177 /// # anyhow::Ok(()) });
178 pub async fn sync_room(&self, client: &Client, room_data: impl Into<AnyRoomBuilder>) -> Room {
179 let any_room = room_data.into();
180 let room_id = any_room.room_id().to_owned();
181
182 self.mock_sync()
183 .ok_and_run(client, move |builder| match any_room {
184 AnyRoomBuilder::Invited(invited) => {
185 builder.add_invited_room(invited);
186 }
187 AnyRoomBuilder::Joined(joined) => {
188 builder.add_joined_room(joined);
189 }
190 AnyRoomBuilder::Left(left) => {
191 builder.add_left_room(left);
192 }
193 AnyRoomBuilder::Knocked(knocked) => {
194 builder.add_knocked_room(knocked);
195 }
196 })
197 .await;
198
199 client.get_room(&room_id).expect("look at me, the room is known now")
200 }
201
202 /// Overrides the sync/ endpoint with knowledge that the given room exists
203 /// in the joined state, runs a sync and returns the given room.
204 ///
205 /// # Examples
206 ///
207 /// ```
208 /// # tokio_test::block_on(async {
209 /// use matrix_sdk::{ruma::room_id, test_utils::mocks::MatrixMockServer};
210 ///
211 /// let mock_server = MatrixMockServer::new().await;
212 /// let client = mock_server.client_builder().build().await;
213 ///
214 /// let room = mock_server
215 /// .sync_joined_room(&client, room_id!("!room_id:localhost"))
216 /// .await;
217 /// # anyhow::Ok(()) });
218 pub async fn sync_joined_room(&self, client: &Client, room_id: &RoomId) -> Room {
219 self.sync_room(client, JoinedRoomBuilder::new(room_id)).await
220 }
221
222 /// Verify that the previous mocks expected number of requests match
223 /// reality, and then cancels all active mocks.
224 ///
225 /// # Examples
226 ///
227 /// ```
228 /// # tokio_test::block_on(async {
229 /// use matrix_sdk::{ruma::{room_id, event_id}, test_utils::mocks::MatrixMockServer};
230 /// use serde_json::json;
231 ///
232 /// let mock_server = MatrixMockServer::new().await;
233 /// let client = mock_server.client_builder().build().await;
234 ///
235 /// mock_server.mock_room_state_encryption().plain().mount().await;
236 /// let room = mock_server
237 /// .sync_joined_room(&client, room_id!("!room_id:localhost"))
238 /// .await;
239 /// mock_server.mock_room_send().ok(event_id!("$some_id")).mount().await;
240 ///
241 /// // This will succeed.
242 /// let response = room.send_raw("m.room.message", json!({ "body": "Hello world" })).await?;
243 ///
244 /// // Now we reset the mocks.
245 /// mock_server.verify_and_reset().await;
246 ///
247 /// // And we can't send anymore.
248 /// let response = room
249 /// .send_raw("m.room.message", json!({ "body": "Hello world" }))
250 /// .await
251 /// .expect_err("We removed the mock so sending should now fail");
252 /// # anyhow::Ok(()) });
253 /// ```
254 pub async fn verify_and_reset(&self) {
255 self.server.verify().await;
256 self.server.reset().await;
257 }
258}
259
260// Specific mount endpoints.
261impl MatrixMockServer {
262 /// Mocks a sync endpoint.
263 ///
264 /// # Examples
265 ///
266 /// ```
267 /// # tokio_test::block_on(async {
268 /// use matrix_sdk::{ruma::room_id, test_utils::mocks::MatrixMockServer};
269 /// use matrix_sdk_test::JoinedRoomBuilder;
270 ///
271 /// // First create the mock server and client pair.
272 /// let mock_server = MatrixMockServer::new().await;
273 /// let client = mock_server.client_builder().build().await;
274 /// let room_id = room_id!("!room_id:localhost");
275 ///
276 /// // Let's emulate what `MatrixMockServer::sync_joined_room()` does.
277 /// mock_server
278 /// .mock_sync()
279 /// .ok_and_run(&client, |builder| {
280 /// builder.add_joined_room(JoinedRoomBuilder::new(room_id));
281 /// })
282 /// .await;
283 ///
284 /// let room = client
285 /// .get_room(room_id)
286 /// .expect("The room should be available after we mocked the sync");
287 /// # anyhow::Ok(()) });
288 /// ```
289 pub fn mock_sync(&self) -> MockEndpoint<'_, SyncEndpoint> {
290 let mock = Mock::given(method("GET")).and(path("/_matrix/client/v3/sync"));
291 self.mock_endpoint(
292 mock,
293 SyncEndpoint { sync_response_builder: self.sync_response_builder.clone() },
294 )
295 .expect_default_access_token()
296 }
297
298 /// Creates a prebuilt mock for sending an event in a room.
299 ///
300 /// Note: works with *any* room.
301 ///
302 /// # Examples
303 ///
304 /// ```
305 /// # tokio_test::block_on(async {
306 /// use matrix_sdk::{ruma::{room_id, event_id}, test_utils::mocks::MatrixMockServer};
307 /// use serde_json::json;
308 ///
309 /// let mock_server = MatrixMockServer::new().await;
310 /// let client = mock_server.client_builder().build().await;
311 ///
312 /// mock_server.mock_room_state_encryption().plain().mount().await;
313 ///
314 /// let room = mock_server
315 /// .sync_joined_room(&client, room_id!("!room_id:localhost"))
316 /// .await;
317 ///
318 /// let event_id = event_id!("$some_id");
319 /// mock_server
320 /// .mock_room_send()
321 /// .ok(event_id)
322 /// .expect(1)
323 /// .mount()
324 /// .await;
325 ///
326 /// let response = room.send_raw("m.room.message", json!({ "body": "Hello world" })).await?;
327 ///
328 /// assert_eq!(
329 /// event_id,
330 /// response.event_id,
331 /// "The event ID we mocked should match the one we received when we sent the event"
332 /// );
333 /// # anyhow::Ok(()) });
334 /// ```
335 pub fn mock_room_send(&self) -> MockEndpoint<'_, RoomSendEndpoint> {
336 let mock = Mock::given(method("PUT"))
337 .and(path_regex(r"^/_matrix/client/v3/rooms/.*/send/.*".to_owned()));
338 self.mock_endpoint(mock, RoomSendEndpoint).expect_default_access_token()
339 }
340
341 /// Creates a prebuilt mock for sending a state event in a room.
342 ///
343 /// Similar to: [`MatrixMockServer::mock_room_send`]
344 ///
345 /// Note: works with *any* room.
346 /// Note: works with *any* event type.
347 ///
348 /// ```
349 /// # tokio_test::block_on(async {
350 /// use matrix_sdk::{ruma::{room_id, event_id}, test_utils::mocks::MatrixMockServer};
351 /// use serde_json::json;
352 ///
353 /// let mock_server = MatrixMockServer::new().await;
354 /// let client = mock_server.client_builder().build().await;
355 ///
356 /// mock_server.mock_room_state_encryption().plain().mount().await;
357 ///
358 /// let room = mock_server
359 /// .sync_joined_room(&client, room_id!("!room_id:localhost"))
360 /// .await;
361 ///
362 /// let event_id = event_id!("$some_id");
363 /// mock_server
364 /// .mock_room_send_state()
365 /// .ok(event_id)
366 /// .expect(1)
367 /// .mount()
368 /// .await;
369 ///
370 /// let response_not_mocked = room.send_raw("m.room.create", json!({ "body": "Hello world" })).await;
371 /// // The `/send` endpoint should not be mocked by the server.
372 /// assert!(response_not_mocked.is_err());
373 ///
374 ///
375 /// let response = room.send_state_event_raw("m.room.message", "my_key", json!({ "body": "Hello world" })).await?;
376 /// // The `/state` endpoint should be mocked by the server.
377 /// assert_eq!(
378 /// event_id,
379 /// response.event_id,
380 /// "The event ID we mocked should match the one we received when we sent the event"
381 /// );
382 /// # anyhow::Ok(()) });
383 /// ```
384 pub fn mock_room_send_state(&self) -> MockEndpoint<'_, RoomSendStateEndpoint> {
385 let mock =
386 Mock::given(method("PUT")).and(path_regex(r"^/_matrix/client/v3/rooms/.*/state/.*/.*"));
387 self.mock_endpoint(mock, RoomSendStateEndpoint::default()).expect_default_access_token()
388 }
389
390 /// Creates a prebuilt mock for asking whether *a* room is encrypted or not.
391 ///
392 /// Note: Applies to all rooms.
393 ///
394 /// # Examples
395 ///
396 /// ```
397 /// # tokio_test::block_on(async {
398 /// use matrix_sdk::{ruma::room_id, test_utils::mocks::MatrixMockServer};
399 ///
400 /// let mock_server = MatrixMockServer::new().await;
401 /// let client = mock_server.client_builder().build().await;
402 ///
403 /// mock_server.mock_room_state_encryption().encrypted().mount().await;
404 ///
405 /// let room = mock_server
406 /// .sync_joined_room(&client, room_id!("!room_id:localhost"))
407 /// .await;
408 ///
409 /// assert!(
410 /// room.latest_encryption_state().await?.is_encrypted(),
411 /// "The room should be marked as encrypted."
412 /// );
413 /// # anyhow::Ok(()) });
414 /// ```
415 pub fn mock_room_state_encryption(&self) -> MockEndpoint<'_, EncryptionStateEndpoint> {
416 let mock = Mock::given(method("GET"))
417 .and(path_regex(r"^/_matrix/client/v3/rooms/.*/state/m.*room.*encryption.?"));
418 self.mock_endpoint(mock, EncryptionStateEndpoint).expect_default_access_token()
419 }
420
421 /// Creates a prebuilt mock for setting the room encryption state.
422 ///
423 /// Note: Applies to all rooms.
424 ///
425 /// # Examples
426 ///
427 /// ```
428 /// # tokio_test::block_on(async {
429 /// use matrix_sdk::{
430 /// ruma::{event_id, room_id},
431 /// test_utils::mocks::MatrixMockServer,
432 /// };
433 ///
434 /// let mock_server = MatrixMockServer::new().await;
435 /// let client = mock_server.client_builder().build().await;
436 ///
437 /// mock_server.mock_room_state_encryption().plain().mount().await;
438 /// mock_server
439 /// .mock_set_room_state_encryption()
440 /// .ok(event_id!("$id"))
441 /// .mock_once()
442 /// .mount()
443 /// .await;
444 ///
445 /// let room = mock_server
446 /// .sync_joined_room(&client, room_id!("!room_id:localhost"))
447 /// .await;
448 ///
449 /// room.enable_encryption()
450 /// .await
451 /// .expect("We should be able to enable encryption in the room");
452 /// # anyhow::Ok(()) });
453 /// ```
454 pub fn mock_set_room_state_encryption(&self) -> MockEndpoint<'_, SetEncryptionStateEndpoint> {
455 let mock = Mock::given(method("PUT"))
456 .and(path_regex(r"^/_matrix/client/v3/rooms/.*/state/m.*room.*encryption.?"));
457 self.mock_endpoint(mock, SetEncryptionStateEndpoint).expect_default_access_token()
458 }
459
460 /// Creates a prebuilt mock for the room redact endpoint.
461 ///
462 /// # Examples
463 ///
464 /// ```
465 /// # tokio_test::block_on(async {
466 /// use matrix_sdk::{
467 /// ruma::{event_id, room_id},
468 /// test_utils::mocks::MatrixMockServer,
469 /// };
470 ///
471 /// let mock_server = MatrixMockServer::new().await;
472 /// let client = mock_server.client_builder().build().await;
473 /// let event_id = event_id!("$id");
474 ///
475 /// mock_server.mock_room_redact().ok(event_id).mock_once().mount().await;
476 ///
477 /// let room = mock_server
478 /// .sync_joined_room(&client, room_id!("!room_id:localhost"))
479 /// .await;
480 ///
481 /// room.redact(event_id, None, None)
482 /// .await
483 /// .expect("We should be able to redact events in the room");
484 /// # anyhow::Ok(()) });
485 /// ```
486 pub fn mock_room_redact(&self) -> MockEndpoint<'_, RoomRedactEndpoint> {
487 let mock = Mock::given(method("PUT"))
488 .and(path_regex(r"^/_matrix/client/v3/rooms/.*/redact/.*?/.*?"));
489 self.mock_endpoint(mock, RoomRedactEndpoint).expect_default_access_token()
490 }
491
492 /// Creates a prebuilt mock for retrieving an event with /room/.../event.
493 pub fn mock_room_event(&self) -> MockEndpoint<'_, RoomEventEndpoint> {
494 let mock = Mock::given(method("GET"));
495 self.mock_endpoint(mock, RoomEventEndpoint { room: None, match_event_id: false })
496 .expect_default_access_token()
497 }
498
499 /// Create a prebuild mock for paginating room message with the `/messages`
500 /// endpoint.
501 pub fn mock_room_messages(&self) -> MockEndpoint<'_, RoomMessagesEndpoint> {
502 let mock =
503 Mock::given(method("GET")).and(path_regex(r"^/_matrix/client/v3/rooms/.*/messages$"));
504 self.mock_endpoint(mock, RoomMessagesEndpoint).expect_default_access_token()
505 }
506
507 /// Create a prebuilt mock for uploading media.
508 pub fn mock_upload(&self) -> MockEndpoint<'_, UploadEndpoint> {
509 let mock = Mock::given(method("POST")).and(path("/_matrix/media/v3/upload"));
510 self.mock_endpoint(mock, UploadEndpoint).expect_default_access_token()
511 }
512
513 /// Create a prebuilt mock for resolving room aliases.
514 ///
515 /// # Examples
516 ///
517 /// ```
518 /// # tokio_test::block_on(async {
519 /// use matrix_sdk::{
520 /// ruma::{owned_room_id, room_alias_id},
521 /// test_utils::mocks::MatrixMockServer,
522 /// };
523 /// let mock_server = MatrixMockServer::new().await;
524 /// let client = mock_server.client_builder().build().await;
525 ///
526 /// mock_server
527 /// .mock_room_directory_resolve_alias()
528 /// .ok("!a:b.c", Vec::new())
529 /// .mock_once()
530 /// .mount()
531 /// .await;
532 ///
533 /// let res = client
534 /// .resolve_room_alias(room_alias_id!("#a:b.c"))
535 /// .await
536 /// .expect("We should be able to resolve the room alias");
537 /// assert_eq!(res.room_id, owned_room_id!("!a:b.c"));
538 /// # anyhow::Ok(()) });
539 /// ```
540 pub fn mock_room_directory_resolve_alias(&self) -> MockEndpoint<'_, ResolveRoomAliasEndpoint> {
541 let mock =
542 Mock::given(method("GET")).and(path_regex(r"/_matrix/client/v3/directory/room/.*"));
543 self.mock_endpoint(mock, ResolveRoomAliasEndpoint)
544 }
545
546 /// Create a prebuilt mock for publishing room aliases in the room
547 /// directory.
548 ///
549 /// # Examples
550 ///
551 /// ```
552 /// # tokio_test::block_on(async {
553 /// use matrix_sdk::{
554 /// ruma::{room_alias_id, room_id},
555 /// test_utils::mocks::MatrixMockServer,
556 /// };
557 ///
558 /// let mock_server = MatrixMockServer::new().await;
559 /// let client = mock_server.client_builder().build().await;
560 ///
561 /// mock_server
562 /// .mock_room_directory_create_room_alias()
563 /// .ok()
564 /// .mock_once()
565 /// .mount()
566 /// .await;
567 ///
568 /// client
569 /// .create_room_alias(room_alias_id!("#a:b.c"), room_id!("!a:b.c"))
570 /// .await
571 /// .expect("We should be able to create a room alias");
572 /// # anyhow::Ok(()) });
573 /// ```
574 pub fn mock_room_directory_create_room_alias(
575 &self,
576 ) -> MockEndpoint<'_, CreateRoomAliasEndpoint> {
577 let mock =
578 Mock::given(method("PUT")).and(path_regex(r"/_matrix/client/v3/directory/room/.*"));
579 self.mock_endpoint(mock, CreateRoomAliasEndpoint)
580 }
581
582 /// Create a prebuilt mock for removing room aliases from the room
583 /// directory.
584 ///
585 /// # Examples
586 ///
587 /// ```
588 /// # tokio_test::block_on(async {
589 /// use matrix_sdk::{
590 /// ruma::room_alias_id, test_utils::mocks::MatrixMockServer,
591 /// };
592 ///
593 /// let mock_server = MatrixMockServer::new().await;
594 /// let client = mock_server.client_builder().build().await;
595 ///
596 /// mock_server
597 /// .mock_room_directory_remove_room_alias()
598 /// .ok()
599 /// .mock_once()
600 /// .mount()
601 /// .await;
602 ///
603 /// client
604 /// .remove_room_alias(room_alias_id!("#a:b.c"))
605 /// .await
606 /// .expect("We should be able to remove the room alias");
607 /// # anyhow::Ok(()) });
608 /// ```
609 pub fn mock_room_directory_remove_room_alias(
610 &self,
611 ) -> MockEndpoint<'_, RemoveRoomAliasEndpoint> {
612 let mock =
613 Mock::given(method("DELETE")).and(path_regex(r"/_matrix/client/v3/directory/room/.*"));
614 self.mock_endpoint(mock, RemoveRoomAliasEndpoint)
615 }
616
617 /// Create a prebuilt mock for listing public rooms.
618 ///
619 /// # Examples
620 ///
621 /// ```
622 /// #
623 /// tokio_test::block_on(async {
624 /// use js_int::uint;
625 /// use ruma::directory::PublicRoomsChunkInit;
626 /// use matrix_sdk::room_directory_search::RoomDirectorySearch;
627 /// use matrix_sdk::{
628 /// ruma::{event_id, room_id},
629 /// test_utils::mocks::MatrixMockServer,
630 /// };
631 /// let mock_server = MatrixMockServer::new().await;
632 /// let client = mock_server.client_builder().build().await;
633 /// let event_id = event_id!("$id");
634 /// let room_id = room_id!("!room_id:localhost");
635 ///
636 /// let chunk = vec![PublicRoomsChunkInit {
637 /// num_joined_members: uint!(0),
638 /// room_id: room_id.to_owned(),
639 /// world_readable: true,
640 /// guest_can_join: true,
641 /// }.into()];
642 ///
643 /// mock_server.mock_public_rooms().ok(chunk, None, None, Some(20)).mock_once().mount().await;
644 /// let mut room_directory_search = RoomDirectorySearch::new(client);
645 ///
646 /// room_directory_search.search(Some("some-alias".to_owned()), 100, None)
647 /// .await
648 /// .expect("Room directory search failed");
649 ///
650 /// let (results, _) = room_directory_search.results();
651 /// assert_eq!(results.len(), 1);
652 /// assert_eq!(results.get(0).unwrap().room_id, room_id.to_owned());
653 /// # });
654 /// ```
655 pub fn mock_public_rooms(&self) -> MockEndpoint<'_, PublicRoomsEndpoint> {
656 let mock = Mock::given(method("POST")).and(path_regex(r"/_matrix/client/v3/publicRooms"));
657 self.mock_endpoint(mock, PublicRoomsEndpoint)
658 }
659
660 /// Create a prebuilt mock for setting a room's visibility in the room
661 /// directory.
662 ///
663 /// # Examples
664 ///
665 /// ```
666 /// # tokio_test::block_on(async {
667 /// use matrix_sdk::{ruma::room_id, test_utils::mocks::MatrixMockServer};
668 /// use ruma::api::client::room::Visibility;
669 ///
670 /// let mock_server = MatrixMockServer::new().await;
671 /// let client = mock_server.client_builder().build().await;
672 ///
673 /// mock_server
674 /// .mock_room_directory_set_room_visibility()
675 /// .ok()
676 /// .mock_once()
677 /// .mount()
678 /// .await;
679 ///
680 /// let room = mock_server
681 /// .sync_joined_room(&client, room_id!("!room_id:localhost"))
682 /// .await;
683 ///
684 /// room.privacy_settings()
685 /// .update_room_visibility(Visibility::Private)
686 /// .await
687 /// .expect("We should be able to update the room's visibility");
688 /// # anyhow::Ok(()) });
689 /// ```
690 pub fn mock_room_directory_set_room_visibility(
691 &self,
692 ) -> MockEndpoint<'_, SetRoomVisibilityEndpoint> {
693 let mock = Mock::given(method("PUT"))
694 .and(path_regex(r"^/_matrix/client/v3/directory/list/room/.*$"));
695 self.mock_endpoint(mock, SetRoomVisibilityEndpoint)
696 }
697
698 /// Create a prebuilt mock for getting a room's visibility in the room
699 /// directory.
700 ///
701 /// # Examples
702 ///
703 /// ```
704 /// # tokio_test::block_on(async {
705 /// use matrix_sdk::{ruma::room_id, test_utils::mocks::MatrixMockServer};
706 /// use ruma::api::client::room::Visibility;
707 ///
708 /// let mock_server = MatrixMockServer::new().await;
709 /// let client = mock_server.client_builder().build().await;
710 ///
711 /// mock_server
712 /// .mock_room_directory_get_room_visibility()
713 /// .ok(Visibility::Public)
714 /// .mock_once()
715 /// .mount()
716 /// .await;
717 ///
718 /// let room = mock_server
719 /// .sync_joined_room(&client, room_id!("!room_id:localhost"))
720 /// .await;
721 ///
722 /// let visibility = room
723 /// .privacy_settings()
724 /// .get_room_visibility()
725 /// .await
726 /// .expect("We should be able to get the room's visibility");
727 /// assert_eq!(visibility, Visibility::Public);
728 /// # anyhow::Ok(()) });
729 /// ```
730 pub fn mock_room_directory_get_room_visibility(
731 &self,
732 ) -> MockEndpoint<'_, GetRoomVisibilityEndpoint> {
733 let mock = Mock::given(method("GET"))
734 .and(path_regex(r"^/_matrix/client/v3/directory/list/room/.*$"));
735 self.mock_endpoint(mock, GetRoomVisibilityEndpoint)
736 }
737
738 /// Create a prebuilt mock for fetching information about key storage
739 /// backups.
740 ///
741 /// # Examples
742 ///
743 /// ```
744 /// # #[cfg(feature = "e2e-encryption")]
745 /// # {
746 /// # tokio_test::block_on(async {
747 /// use matrix_sdk::test_utils::mocks::MatrixMockServer;
748 ///
749 /// let mock_server = MatrixMockServer::new().await;
750 /// let client = mock_server.client_builder().build().await;
751 ///
752 /// mock_server.mock_room_keys_version().exists().expect(1).mount().await;
753 ///
754 /// let exists =
755 /// client.encryption().backups().fetch_exists_on_server().await.unwrap();
756 ///
757 /// assert!(exists);
758 /// # });
759 /// # }
760 /// ```
761 pub fn mock_room_keys_version(&self) -> MockEndpoint<'_, RoomKeysVersionEndpoint> {
762 let mock =
763 Mock::given(method("GET")).and(path_regex(r"_matrix/client/v3/room_keys/version"));
764 self.mock_endpoint(mock, RoomKeysVersionEndpoint).expect_default_access_token()
765 }
766
767 /// Create a prebuilt mock for adding key storage backups via POST
768 pub fn mock_add_room_keys_version(&self) -> MockEndpoint<'_, AddRoomKeysVersionEndpoint> {
769 let mock =
770 Mock::given(method("POST")).and(path_regex(r"_matrix/client/v3/room_keys/version"));
771 self.mock_endpoint(mock, AddRoomKeysVersionEndpoint).expect_default_access_token()
772 }
773
774 /// Create a prebuilt mock for adding key storage backups via POST
775 pub fn mock_delete_room_keys_version(&self) -> MockEndpoint<'_, DeleteRoomKeysVersionEndpoint> {
776 let mock = Mock::given(method("DELETE"))
777 .and(path_regex(r"_matrix/client/v3/room_keys/version/[^/]*"));
778 self.mock_endpoint(mock, DeleteRoomKeysVersionEndpoint).expect_default_access_token()
779 }
780
781 /// Create a prebuilt mock for getting the room members in a room.
782 ///
783 /// # Examples
784 ///
785 /// ```
786 /// # tokio_test::block_on(async {
787 /// use matrix_sdk::{
788 /// ruma::{event_id, room_id},
789 /// test_utils::mocks::MatrixMockServer,
790 /// };
791 /// use matrix_sdk_base::RoomMemberships;
792 /// use matrix_sdk_test::event_factory::EventFactory;
793 /// use ruma::{
794 /// events::room::member::{MembershipState, RoomMemberEventContent},
795 /// user_id,
796 /// };
797 /// let mock_server = MatrixMockServer::new().await;
798 /// let client = mock_server.client_builder().build().await;
799 /// let event_id = event_id!("$id");
800 /// let room_id = room_id!("!room_id:localhost");
801 ///
802 /// let f = EventFactory::new().room(room_id);
803 /// let alice_user_id = user_id!("@alice:b.c");
804 /// let alice_knock_event = f
805 /// .event(RoomMemberEventContent::new(MembershipState::Knock))
806 /// .event_id(event_id)
807 /// .sender(alice_user_id)
808 /// .state_key(alice_user_id)
809 /// .into_raw_timeline()
810 /// .cast();
811 ///
812 /// mock_server
813 /// .mock_get_members()
814 /// .ok(vec![alice_knock_event])
815 /// .mock_once()
816 /// .mount()
817 /// .await;
818 /// let room = mock_server.sync_joined_room(&client, room_id).await;
819 ///
820 /// let members = room.members(RoomMemberships::all()).await.unwrap();
821 /// assert_eq!(members.len(), 1);
822 /// # });
823 /// ```
824 pub fn mock_get_members(&self) -> MockEndpoint<'_, GetRoomMembersEndpoint> {
825 let mock =
826 Mock::given(method("GET")).and(path_regex(r"^/_matrix/client/v3/rooms/.*/members$"));
827 self.mock_endpoint(mock, GetRoomMembersEndpoint)
828 }
829
830 /// Creates a prebuilt mock for inviting a user to a room by its id.
831 ///
832 /// # Examples
833 ///
834 /// ```
835 /// # use ruma::user_id;
836 /// tokio_test::block_on(async {
837 /// use matrix_sdk::{
838 /// ruma::room_id,
839 /// test_utils::mocks::MatrixMockServer,
840 /// };
841 ///
842 /// let mock_server = MatrixMockServer::new().await;
843 /// let client = mock_server.client_builder().build().await;
844 ///
845 /// mock_server.mock_invite_user_by_id().ok().mock_once().mount().await;
846 ///
847 /// let room = mock_server
848 /// .sync_joined_room(&client, room_id!("!room_id:localhost"))
849 /// .await;
850 ///
851 /// room.invite_user_by_id(user_id!("@alice:localhost")).await.unwrap();
852 /// # anyhow::Ok(()) });
853 /// ```
854 pub fn mock_invite_user_by_id(&self) -> MockEndpoint<'_, InviteUserByIdEndpoint> {
855 let mock =
856 Mock::given(method("POST")).and(path_regex(r"^/_matrix/client/v3/rooms/.*/invite$"));
857 self.mock_endpoint(mock, InviteUserByIdEndpoint)
858 }
859
860 /// Creates a prebuilt mock for kicking a user from a room.
861 ///
862 /// # Examples
863 ///
864 /// ```
865 /// # use ruma::user_id;
866 /// tokio_test::block_on(async {
867 /// use matrix_sdk::{
868 /// ruma::room_id,
869 /// test_utils::mocks::MatrixMockServer,
870 /// };
871 ///
872 /// let mock_server = MatrixMockServer::new().await;
873 /// let client = mock_server.client_builder().build().await;
874 ///
875 /// mock_server.mock_kick_user().ok().mock_once().mount().await;
876 ///
877 /// let room = mock_server
878 /// .sync_joined_room(&client, room_id!("!room_id:localhost"))
879 /// .await;
880 ///
881 /// room.kick_user(user_id!("@alice:localhost"), None).await.unwrap();
882 /// # anyhow::Ok(()) });
883 /// ```
884 pub fn mock_kick_user(&self) -> MockEndpoint<'_, KickUserEndpoint> {
885 let mock =
886 Mock::given(method("POST")).and(path_regex(r"^/_matrix/client/v3/rooms/.*/kick"));
887 self.mock_endpoint(mock, KickUserEndpoint)
888 }
889
890 /// Creates a prebuilt mock for banning a user from a room.
891 ///
892 /// # Examples
893 ///
894 /// ```
895 /// # use ruma::user_id;
896 /// tokio_test::block_on(async {
897 /// use matrix_sdk::{
898 /// ruma::room_id,
899 /// test_utils::mocks::MatrixMockServer,
900 /// };
901 ///
902 /// let mock_server = MatrixMockServer::new().await;
903 /// let client = mock_server.client_builder().build().await;
904 ///
905 /// mock_server.mock_ban_user().ok().mock_once().mount().await;
906 ///
907 /// let room = mock_server
908 /// .sync_joined_room(&client, room_id!("!room_id:localhost"))
909 /// .await;
910 ///
911 /// room.ban_user(user_id!("@alice:localhost"), None).await.unwrap();
912 /// # anyhow::Ok(()) });
913 /// ```
914 pub fn mock_ban_user(&self) -> MockEndpoint<'_, BanUserEndpoint> {
915 let mock = Mock::given(method("POST")).and(path_regex(r"^/_matrix/client/v3/rooms/.*/ban"));
916 self.mock_endpoint(mock, BanUserEndpoint)
917 }
918
919 /// Creates a prebuilt mock for the `/_matrix/client/versions` endpoint.
920 pub fn mock_versions(&self) -> MockEndpoint<'_, VersionsEndpoint> {
921 let mock = Mock::given(method("GET")).and(path_regex(r"^/_matrix/client/versions"));
922 self.mock_endpoint(mock, VersionsEndpoint)
923 }
924
925 /// Creates a prebuilt mock for the room summary endpoint [MSC3266](https://github.com/matrix-org/matrix-spec-proposals/pull/3266).
926 pub fn mock_room_summary(&self) -> MockEndpoint<'_, RoomSummaryEndpoint> {
927 let mock = Mock::given(method("GET"))
928 .and(path_regex(r"^/_matrix/client/unstable/im.nheko.summary/rooms/.*/summary"));
929 self.mock_endpoint(mock, RoomSummaryEndpoint)
930 }
931
932 /// Creates a prebuilt mock for the endpoint used to set a room's pinned
933 /// events.
934 pub fn mock_set_room_pinned_events(&self) -> MockEndpoint<'_, SetRoomPinnedEventsEndpoint> {
935 let mock = Mock::given(method("PUT"))
936 .and(path_regex(r"^/_matrix/client/v3/rooms/.*/state/m.room.pinned_events/.*?"));
937 self.mock_endpoint(mock, SetRoomPinnedEventsEndpoint).expect_default_access_token()
938 }
939
940 /// Creates a prebuilt mock for the endpoint used to get information about
941 /// the owner of the given access token.
942 ///
943 /// If no access token is provided, the access token to match is `"1234"`,
944 /// which matches the default value in the mock data.
945 pub fn mock_who_am_i(&self) -> MockEndpoint<'_, WhoAmIEndpoint> {
946 let mock =
947 Mock::given(method("GET")).and(path_regex(r"^/_matrix/client/v3/account/whoami"));
948 self.mock_endpoint(mock, WhoAmIEndpoint).expect_default_access_token()
949 }
950
951 /// Creates a prebuilt mock for the endpoint used to publish end-to-end
952 /// encryption keys.
953 pub fn mock_upload_keys(&self) -> MockEndpoint<'_, UploadKeysEndpoint> {
954 let mock = Mock::given(method("POST")).and(path_regex(r"^/_matrix/client/v3/keys/upload"));
955 self.mock_endpoint(mock, UploadKeysEndpoint).expect_default_access_token()
956 }
957
958 /// Creates a prebuilt mock for the endpoint used to query end-to-end
959 /// encryption keys.
960 pub fn mock_query_keys(&self) -> MockEndpoint<'_, QueryKeysEndpoint> {
961 let mock = Mock::given(method("POST")).and(path_regex(r"^/_matrix/client/v3/keys/query"));
962 self.mock_endpoint(mock, QueryKeysEndpoint).expect_default_access_token()
963 }
964
965 /// Creates a prebuilt mock for the endpoint used to discover the URL of a
966 /// homeserver.
967 pub fn mock_well_known(&self) -> MockEndpoint<'_, WellKnownEndpoint> {
968 let mock = Mock::given(method("GET")).and(path_regex(r"^/.well-known/matrix/client"));
969 self.mock_endpoint(mock, WellKnownEndpoint)
970 }
971
972 /// Creates a prebuilt mock for the endpoint used to publish cross-signing
973 /// keys.
974 pub fn mock_upload_cross_signing_keys(
975 &self,
976 ) -> MockEndpoint<'_, UploadCrossSigningKeysEndpoint> {
977 let mock = Mock::given(method("POST"))
978 .and(path_regex(r"^/_matrix/client/v3/keys/device_signing/upload"));
979 self.mock_endpoint(mock, UploadCrossSigningKeysEndpoint).expect_default_access_token()
980 }
981
982 /// Creates a prebuilt mock for the endpoint used to publish cross-signing
983 /// signatures.
984 pub fn mock_upload_cross_signing_signatures(
985 &self,
986 ) -> MockEndpoint<'_, UploadCrossSigningSignaturesEndpoint> {
987 let mock = Mock::given(method("POST"))
988 .and(path_regex(r"^/_matrix/client/v3/keys/signatures/upload"));
989 self.mock_endpoint(mock, UploadCrossSigningSignaturesEndpoint).expect_default_access_token()
990 }
991
992 /// Creates a prebuilt mock for the endpoint used to leave a room.
993 pub fn mock_room_leave(&self) -> MockEndpoint<'_, RoomLeaveEndpoint> {
994 let mock =
995 Mock::given(method("POST")).and(path_regex(r"^/_matrix/client/v3/rooms/.*/leave"));
996 self.mock_endpoint(mock, RoomLeaveEndpoint).expect_default_access_token()
997 }
998
999 /// Create a prebuilt mock for the endpoint use to log out a session.
1000 pub fn mock_logout(&self) -> MockEndpoint<'_, LogoutEndpoint> {
1001 let mock = Mock::given(method("POST")).and(path("/_matrix/client/v3/logout"));
1002 self.mock_endpoint(mock, LogoutEndpoint).expect_default_access_token()
1003 }
1004}
1005
1006/// Parameter to [`MatrixMockServer::sync_room`].
1007pub enum AnyRoomBuilder {
1008 /// A room we've been invited to.
1009 Invited(InvitedRoomBuilder),
1010 /// A room we've joined.
1011 Joined(JoinedRoomBuilder),
1012 /// A room we've left.
1013 Left(LeftRoomBuilder),
1014 /// A room we've knocked to.
1015 Knocked(KnockedRoomBuilder),
1016}
1017
1018impl AnyRoomBuilder {
1019 /// Get the [`RoomId`] of the room this [`AnyRoomBuilder`] will create.
1020 fn room_id(&self) -> &RoomId {
1021 match self {
1022 AnyRoomBuilder::Invited(r) => r.room_id(),
1023 AnyRoomBuilder::Joined(r) => r.room_id(),
1024 AnyRoomBuilder::Left(r) => r.room_id(),
1025 AnyRoomBuilder::Knocked(r) => r.room_id(),
1026 }
1027 }
1028}
1029
1030impl From<InvitedRoomBuilder> for AnyRoomBuilder {
1031 fn from(val: InvitedRoomBuilder) -> AnyRoomBuilder {
1032 AnyRoomBuilder::Invited(val)
1033 }
1034}
1035
1036impl From<JoinedRoomBuilder> for AnyRoomBuilder {
1037 fn from(val: JoinedRoomBuilder) -> AnyRoomBuilder {
1038 AnyRoomBuilder::Joined(val)
1039 }
1040}
1041
1042impl From<LeftRoomBuilder> for AnyRoomBuilder {
1043 fn from(val: LeftRoomBuilder) -> AnyRoomBuilder {
1044 AnyRoomBuilder::Left(val)
1045 }
1046}
1047
1048impl From<KnockedRoomBuilder> for AnyRoomBuilder {
1049 fn from(val: KnockedRoomBuilder) -> AnyRoomBuilder {
1050 AnyRoomBuilder::Knocked(val)
1051 }
1052}
1053
1054/// The [path percent-encode set] as defined in the WHATWG URL standard + `/`
1055/// since we always encode single segments of the path.
1056///
1057/// [path percent-encode set]: https://url.spec.whatwg.org/#path-percent-encode-set
1058///
1059/// Copied from Ruma:
1060/// https://github.com/ruma/ruma/blob/e4cb409ff3aaa16f31a7fe1e61fee43b2d144f7b/crates/ruma-common/src/percent_encode.rs#L7
1061const PATH_PERCENT_ENCODE_SET: &AsciiSet = &CONTROLS
1062 .add(b' ')
1063 .add(b'"')
1064 .add(b'#')
1065 .add(b'<')
1066 .add(b'>')
1067 .add(b'?')
1068 .add(b'`')
1069 .add(b'{')
1070 .add(b'}')
1071 .add(b'/');
1072
1073fn percent_encoded_path(path: &str) -> String {
1074 percent_encoding::utf8_percent_encode(path, PATH_PERCENT_ENCODE_SET).to_string()
1075}
1076
1077/// A wrapper for a [`Mock`] as well as a [`MockServer`], allowing us to call
1078/// [`Mock::mount`] or [`Mock::mount_as_scoped`] without having to pass the
1079/// [`MockServer`] reference (i.e. call `mount()` instead of `mount(&server)`).
1080pub struct MatrixMock<'a> {
1081 mock: Mock,
1082 server: &'a MockServer,
1083}
1084
1085impl MatrixMock<'_> {
1086 /// Set an expectation on the number of times this [`MatrixMock`] should
1087 /// match in the current test case.
1088 ///
1089 /// Expectations are verified when the server is shutting down: if
1090 /// the expectation is not satisfied, the [`MatrixMockServer`] will panic
1091 /// and the `error_message` is shown.
1092 ///
1093 /// By default, no expectation is set for [`MatrixMock`]s.
1094 pub fn expect<T: Into<Times>>(self, num_calls: T) -> Self {
1095 Self { mock: self.mock.expect(num_calls), ..self }
1096 }
1097
1098 /// Assign a name to your mock.
1099 ///
1100 /// The mock name will be used in error messages (e.g. if the mock
1101 /// expectation is not satisfied) and debug logs to help you identify
1102 /// what failed.
1103 pub fn named(self, name: impl Into<String>) -> Self {
1104 Self { mock: self.mock.named(name), ..self }
1105 }
1106
1107 /// Respond to a response of this endpoint exactly once.
1108 ///
1109 /// After it's been called, subsequent responses will hit the next handler
1110 /// or a 404.
1111 ///
1112 /// Also verifies that it's been called once.
1113 pub fn mock_once(self) -> Self {
1114 Self { mock: self.mock.up_to_n_times(1).expect(1), ..self }
1115 }
1116
1117 /// Makes sure the endpoint is never reached.
1118 pub fn never(self) -> Self {
1119 Self { mock: self.mock.expect(0), ..self }
1120 }
1121
1122 /// Specify an upper limit to the number of times you would like this
1123 /// [`MatrixMock`] to respond to incoming requests that satisfy the
1124 /// conditions imposed by your matchers.
1125 pub fn up_to_n_times(self, num: u64) -> Self {
1126 Self { mock: self.mock.up_to_n_times(num), ..self }
1127 }
1128
1129 /// Mount a [`MatrixMock`] on the attached server.
1130 ///
1131 /// The [`MatrixMock`] will remain active until the [`MatrixMockServer`] is
1132 /// shut down. If you want to control or limit how long your
1133 /// [`MatrixMock`] stays active, check out [`Self::mount_as_scoped`].
1134 pub async fn mount(self) {
1135 self.mock.mount(self.server).await;
1136 }
1137
1138 /// Mount a [`MatrixMock`] as **scoped** on the attached server.
1139 ///
1140 /// When using [`Self::mount`], your [`MatrixMock`]s will be active until
1141 /// the [`MatrixMockServer`] is shut down.
1142 ///
1143 /// When using `mount_as_scoped`, your [`MatrixMock`]s will be active as
1144 /// long as the returned [`MockGuard`] is not dropped.
1145 ///
1146 /// When the returned [`MockGuard`] is dropped, [`MatrixMockServer`] will
1147 /// verify that the expectations set on the scoped [`MatrixMock`] were
1148 /// verified - if not, it will panic.
1149 pub async fn mount_as_scoped(self) -> MockGuard {
1150 self.mock.mount_as_scoped(self.server).await
1151 }
1152}
1153
1154/// Generic mocked endpoint, with useful common helpers.
1155pub struct MockEndpoint<'a, T> {
1156 server: &'a MockServer,
1157 mock: MockBuilder,
1158 endpoint: T,
1159 expected_access_token: ExpectedAccessToken,
1160}
1161
1162impl<'a, T> MockEndpoint<'a, T> {
1163 fn new(server: &'a MockServer, mock: MockBuilder, endpoint: T) -> Self {
1164 Self { server, mock, endpoint, expected_access_token: ExpectedAccessToken::None }
1165 }
1166
1167 /// Expect authentication with the default access token on this endpoint.
1168 pub fn expect_default_access_token(mut self) -> Self {
1169 self.expected_access_token = ExpectedAccessToken::Default;
1170 self
1171 }
1172
1173 /// Expect authentication with the given access token on this endpoint.
1174 pub fn expect_access_token(mut self, access_token: &'static str) -> Self {
1175 self.expected_access_token = ExpectedAccessToken::Custom(access_token);
1176 self
1177 }
1178
1179 /// Specify how to respond to a query (viz., like
1180 /// [`MockBuilder::respond_with`] does), when other predefined responses
1181 /// aren't sufficient.
1182 ///
1183 /// # Examples
1184 ///
1185 /// ```
1186 /// # tokio_test::block_on(async {
1187 /// use matrix_sdk::{ruma::{room_id, event_id}, test_utils::mocks::MatrixMockServer};
1188 /// use serde_json::json;
1189 /// use wiremock::ResponseTemplate;
1190 ///
1191 /// let mock_server = MatrixMockServer::new().await;
1192 /// let client = mock_server.client_builder().build().await;
1193 ///
1194 /// mock_server.mock_room_state_encryption().plain().mount().await;
1195 ///
1196 /// let room = mock_server
1197 /// .sync_joined_room(&client, room_id!("!room_id:localhost"))
1198 /// .await;
1199 ///
1200 /// let event_id = event_id!("$some_id");
1201 /// mock_server
1202 /// .mock_room_send()
1203 /// .respond_with(
1204 /// ResponseTemplate::new(429)
1205 /// .insert_header("Retry-After", "100")
1206 /// .set_body_json(json!({
1207 /// "errcode": "M_LIMIT_EXCEEDED",
1208 /// "custom_field": "with custom data",
1209 /// })))
1210 /// .expect(1)
1211 /// .mount()
1212 /// .await;
1213 ///
1214 /// room
1215 /// .send_raw("m.room.message", json!({ "body": "Hello world" }))
1216 /// .await
1217 /// .expect_err("The sending of the event should fail");
1218 /// # anyhow::Ok(()) });
1219 /// ```
1220 pub fn respond_with<R: Respond + 'static>(self, func: R) -> MatrixMock<'a> {
1221 let mock = self
1222 .expected_access_token
1223 .maybe_match_authorization_header(self.mock)
1224 .respond_with(func);
1225 MatrixMock { mock, server: self.server }
1226 }
1227
1228 /// Returns a send endpoint that emulates a transient failure, i.e responds
1229 /// with error 500.
1230 ///
1231 /// # Examples
1232 /// ```
1233 /// # tokio_test::block_on(async {
1234 /// use matrix_sdk::{ruma::{room_id, event_id}, test_utils::mocks::MatrixMockServer};
1235 /// use serde_json::json;
1236 ///
1237 /// let mock_server = MatrixMockServer::new().await;
1238 /// let client = mock_server.client_builder().build().await;
1239 ///
1240 /// mock_server.mock_room_state_encryption().plain().mount().await;
1241 ///
1242 /// let room = mock_server
1243 /// .sync_joined_room(&client, room_id!("!room_id:localhost"))
1244 /// .await;
1245 ///
1246 /// mock_server
1247 /// .mock_room_send()
1248 /// .error500()
1249 /// .expect(1)
1250 /// .mount()
1251 /// .await;
1252 ///
1253 /// room
1254 /// .send_raw("m.room.message", json!({ "body": "Hello world" }))
1255 /// .await.expect_err("The sending of the event should have failed");
1256 /// # anyhow::Ok(()) });
1257 /// ```
1258 pub fn error500(self) -> MatrixMock<'a> {
1259 self.respond_with(ResponseTemplate::new(500))
1260 }
1261
1262 /// Internal helper to return an `{ event_id }` JSON struct along with a 200
1263 /// ok response.
1264 fn ok_with_event_id(self, event_id: OwnedEventId) -> MatrixMock<'a> {
1265 self.respond_with(ResponseTemplate::new(200).set_body_json(json!({ "event_id": event_id })))
1266 }
1267
1268 /// Returns an endpoint that emulates a permanent failure error (e.g. event
1269 /// is too large).
1270 ///
1271 /// # Examples
1272 /// ```
1273 /// # tokio_test::block_on(async {
1274 /// use matrix_sdk::{ruma::{room_id, event_id}, test_utils::mocks::MatrixMockServer};
1275 /// use serde_json::json;
1276 ///
1277 /// let mock_server = MatrixMockServer::new().await;
1278 /// let client = mock_server.client_builder().build().await;
1279 ///
1280 /// mock_server.mock_room_state_encryption().plain().mount().await;
1281 ///
1282 /// let room = mock_server
1283 /// .sync_joined_room(&client, room_id!("!room_id:localhost"))
1284 /// .await;
1285 ///
1286 /// mock_server
1287 /// .mock_room_send()
1288 /// .error_too_large()
1289 /// .expect(1)
1290 /// .mount()
1291 /// .await;
1292 ///
1293 /// room
1294 /// .send_raw("m.room.message", json!({ "body": "Hello world" }))
1295 /// .await.expect_err("The sending of the event should have failed");
1296 /// # anyhow::Ok(()) });
1297 /// ```
1298 pub fn error_too_large(self) -> MatrixMock<'a> {
1299 self.respond_with(ResponseTemplate::new(413).set_body_json(json!({
1300 // From https://spec.matrix.org/v1.10/client-server-api/#standard-error-response
1301 "errcode": "M_TOO_LARGE",
1302 })))
1303 }
1304}
1305
1306/// The access token to expect on an endpoint.
1307enum ExpectedAccessToken {
1308 /// We don't expect an access token.
1309 None,
1310
1311 /// We expect the default access token.
1312 Default,
1313
1314 /// We expect the given access token.
1315 Custom(&'static str),
1316}
1317
1318impl ExpectedAccessToken {
1319 /// Match an `Authorization` header on the given mock if one is expected.
1320 fn maybe_match_authorization_header(&self, mock: MockBuilder) -> MockBuilder {
1321 let token = match self {
1322 Self::None => return mock,
1323 Self::Default => "1234",
1324 Self::Custom(token) => token,
1325 };
1326 mock.and(header(http::header::AUTHORIZATION, format!("Bearer {token}")))
1327 }
1328}
1329
1330/// A prebuilt mock for sending a message like event in a room.
1331pub struct RoomSendEndpoint;
1332
1333impl<'a> MockEndpoint<'a, RoomSendEndpoint> {
1334 /// Ensures that the body of the request is a superset of the provided
1335 /// `body` parameter.
1336 ///
1337 /// # Examples
1338 /// ```
1339 /// # tokio_test::block_on(async {
1340 /// use matrix_sdk::{
1341 /// ruma::{room_id, event_id, events::room::message::RoomMessageEventContent},
1342 /// test_utils::mocks::MatrixMockServer
1343 /// };
1344 /// use serde_json::json;
1345 ///
1346 /// let mock_server = MatrixMockServer::new().await;
1347 /// let client = mock_server.client_builder().build().await;
1348 ///
1349 /// mock_server.mock_room_state_encryption().plain().mount().await;
1350 ///
1351 /// let room = mock_server
1352 /// .sync_joined_room(&client, room_id!("!room_id:localhost"))
1353 /// .await;
1354 ///
1355 /// let event_id = event_id!("$some_id");
1356 /// mock_server
1357 /// .mock_room_send()
1358 /// .body_matches_partial_json(json!({
1359 /// "body": "Hello world",
1360 /// }))
1361 /// .ok(event_id)
1362 /// .expect(1)
1363 /// .mount()
1364 /// .await;
1365 ///
1366 /// let content = RoomMessageEventContent::text_plain("Hello world");
1367 /// let response = room.send(content).await?;
1368 ///
1369 /// assert_eq!(
1370 /// event_id,
1371 /// response.event_id,
1372 /// "The event ID we mocked should match the one we received when we sent the event"
1373 /// );
1374 /// # anyhow::Ok(()) });
1375 /// ```
1376 pub fn body_matches_partial_json(self, body: Value) -> Self {
1377 Self { mock: self.mock.and(body_partial_json(body)), ..self }
1378 }
1379
1380 /// Ensures that the send endpoint request uses a specific event type.
1381 ///
1382 /// # Examples
1383 ///
1384 /// see also [`MatrixMockServer::mock_room_send`] for more context.
1385 ///
1386 /// ```
1387 /// # tokio_test::block_on(async {
1388 /// use matrix_sdk::{ruma::{room_id, event_id}, test_utils::mocks::MatrixMockServer};
1389 /// use serde_json::json;
1390 ///
1391 /// let mock_server = MatrixMockServer::new().await;
1392 /// let client = mock_server.client_builder().build().await;
1393 ///
1394 /// mock_server.mock_room_state_encryption().plain().mount().await;
1395 ///
1396 /// let room = mock_server
1397 /// .sync_joined_room(&client, room_id!("!room_id:localhost"))
1398 /// .await;
1399 ///
1400 /// let event_id = event_id!("$some_id");
1401 /// mock_server
1402 /// .mock_room_send()
1403 /// .for_type("m.room.message".into())
1404 /// .ok(event_id)
1405 /// .expect(1)
1406 /// .mount()
1407 /// .await;
1408 ///
1409 /// let response_not_mocked = room.send_raw("m.room.reaction", json!({ "body": "Hello world" })).await;
1410 /// // The `m.room.reaction` event type should not be mocked by the server.
1411 /// assert!(response_not_mocked.is_err());
1412 ///
1413 /// let response = room.send_raw("m.room.message", json!({ "body": "Hello world" })).await?;
1414 /// // The `m.room.message` event type should be mocked by the server.
1415 /// assert_eq!(
1416 /// event_id,
1417 /// response.event_id,
1418 /// "The event ID we mocked should match the one we received when we sent the event"
1419 /// );
1420 /// # anyhow::Ok(()) });
1421 /// ```
1422 pub fn for_type(self, event_type: MessageLikeEventType) -> Self {
1423 Self {
1424 // Note: we already defined a path when constructing the mock builder, but this one
1425 // ought to be more specialized.
1426 mock: self
1427 .mock
1428 .and(path_regex(format!(r"^/_matrix/client/v3/rooms/.*/send/{event_type}",))),
1429 ..self
1430 }
1431 }
1432
1433 /// Ensures the event was sent as a delayed event.
1434 ///
1435 /// See also [the MSC](https://github.com/matrix-org/matrix-spec-proposals/pull/4140).
1436 ///
1437 /// Note: works with *any* room.
1438 ///
1439 /// # Examples
1440 ///
1441 /// see also [`MatrixMockServer::mock_room_send`] for more context.
1442 ///
1443 /// ```
1444 /// # tokio_test::block_on(async {
1445 /// use matrix_sdk::{
1446 /// ruma::{
1447 /// api::client::delayed_events::{delayed_message_event, DelayParameters},
1448 /// events::{message::MessageEventContent, AnyMessageLikeEventContent},
1449 /// room_id,
1450 /// time::Duration,
1451 /// TransactionId,
1452 /// },
1453 /// test_utils::mocks::MatrixMockServer,
1454 /// };
1455 /// use serde_json::json;
1456 /// use wiremock::ResponseTemplate;
1457 ///
1458 /// let mock_server = MatrixMockServer::new().await;
1459 /// let client = mock_server.client_builder().build().await;
1460 ///
1461 /// mock_server.mock_room_state_encryption().plain().mount().await;
1462 ///
1463 /// let room = mock_server.sync_joined_room(&client, room_id!("!room_id:localhost")).await;
1464 ///
1465 /// mock_server
1466 /// .mock_room_send()
1467 /// .match_delayed_event(Duration::from_millis(500))
1468 /// .respond_with(ResponseTemplate::new(200).set_body_json(json!({"delay_id":"$some_id"})))
1469 /// .mock_once()
1470 /// .mount()
1471 /// .await;
1472 ///
1473 /// let response_not_mocked =
1474 /// room.send_raw("m.room.message", json!({ "body": "Hello world" })).await;
1475 ///
1476 /// // A non delayed event should not be mocked by the server.
1477 /// assert!(response_not_mocked.is_err());
1478 ///
1479 /// let r = delayed_message_event::unstable::Request::new(
1480 /// room.room_id().to_owned(),
1481 /// TransactionId::new(),
1482 /// DelayParameters::Timeout { timeout: Duration::from_millis(500) },
1483 /// &AnyMessageLikeEventContent::Message(MessageEventContent::plain("hello world")),
1484 /// )
1485 /// .unwrap();
1486 ///
1487 /// let response = room.client().send(r).await.unwrap();
1488 /// // The delayed `m.room.message` event type should be mocked by the server.
1489 /// assert_eq!("$some_id", response.delay_id);
1490 /// # anyhow::Ok(()) });
1491 /// ```
1492 pub fn match_delayed_event(self, delay: Duration) -> Self {
1493 Self {
1494 mock: self
1495 .mock
1496 .and(query_param("org.matrix.msc4140.delay", delay.as_millis().to_string())),
1497 ..self
1498 }
1499 }
1500
1501 /// Returns a send endpoint that emulates success, i.e. the event has been
1502 /// sent with the given event id.
1503 ///
1504 /// # Examples
1505 /// ```
1506 /// # tokio_test::block_on(async {
1507 /// use matrix_sdk::{ruma::{room_id, event_id}, test_utils::mocks::MatrixMockServer};
1508 /// use serde_json::json;
1509 ///
1510 /// let mock_server = MatrixMockServer::new().await;
1511 /// let client = mock_server.client_builder().build().await;
1512 ///
1513 /// mock_server.mock_room_state_encryption().plain().mount().await;
1514 ///
1515 /// let room = mock_server
1516 /// .sync_joined_room(&client, room_id!("!room_id:localhost"))
1517 /// .await;
1518 ///
1519 /// let event_id = event_id!("$some_id");
1520 /// let send_guard = mock_server
1521 /// .mock_room_send()
1522 /// .ok(event_id)
1523 /// .expect(1)
1524 /// .mount_as_scoped()
1525 /// .await;
1526 ///
1527 /// let response = room.send_raw("m.room.message", json!({ "body": "Hello world" })).await?;
1528 ///
1529 /// assert_eq!(
1530 /// event_id,
1531 /// response.event_id,
1532 /// "The event ID we mocked should match the one we received when we sent the event"
1533 /// );
1534 /// # anyhow::Ok(()) });
1535 /// ```
1536 pub fn ok(self, returned_event_id: impl Into<OwnedEventId>) -> MatrixMock<'a> {
1537 self.ok_with_event_id(returned_event_id.into())
1538 }
1539}
1540
1541/// A prebuilt mock for sending a state event in a room.
1542#[derive(Default)]
1543pub struct RoomSendStateEndpoint {
1544 state_key: Option<String>,
1545 event_type: Option<StateEventType>,
1546}
1547
1548impl<'a> MockEndpoint<'a, RoomSendStateEndpoint> {
1549 fn generate_path_regexp(endpoint: &RoomSendStateEndpoint) -> String {
1550 format!(
1551 r"^/_matrix/client/v3/rooms/.*/state/{}/{}",
1552 endpoint.event_type.as_ref().map_or_else(|| ".*".to_owned(), |t| t.to_string()),
1553 endpoint.state_key.as_ref().map_or_else(|| ".*".to_owned(), |k| k.to_string())
1554 )
1555 }
1556
1557 /// Ensures that the body of the request is a superset of the provided
1558 /// `body` parameter.
1559 ///
1560 /// # Examples
1561 /// ```
1562 /// # tokio_test::block_on(async {
1563 /// use matrix_sdk::{
1564 /// ruma::{room_id, event_id, events::room::power_levels::RoomPowerLevelsEventContent},
1565 /// test_utils::mocks::MatrixMockServer
1566 /// };
1567 /// use serde_json::json;
1568 ///
1569 /// let mock_server = MatrixMockServer::new().await;
1570 /// let client = mock_server.client_builder().build().await;
1571 ///
1572 /// mock_server.mock_room_state_encryption().plain().mount().await;
1573 ///
1574 /// let room = mock_server
1575 /// .sync_joined_room(&client, room_id!("!room_id:localhost"))
1576 /// .await;
1577 ///
1578 /// let event_id = event_id!("$some_id");
1579 /// mock_server
1580 /// .mock_room_send_state()
1581 /// .body_matches_partial_json(json!({
1582 /// "redact": 51,
1583 /// }))
1584 /// .ok(event_id)
1585 /// .expect(1)
1586 /// .mount()
1587 /// .await;
1588 ///
1589 /// let mut content = RoomPowerLevelsEventContent::new();
1590 /// // Update the power level to a non default value.
1591 /// // Otherwise it will be skipped from serialization.
1592 /// content.redact = 51.into();
1593 ///
1594 /// let response = room.send_state_event(content).await?;
1595 ///
1596 /// assert_eq!(
1597 /// event_id,
1598 /// response.event_id,
1599 /// "The event ID we mocked should match the one we received when we sent the event"
1600 /// );
1601 /// # anyhow::Ok(()) });
1602 /// ```
1603 pub fn body_matches_partial_json(self, body: Value) -> Self {
1604 Self { mock: self.mock.and(body_partial_json(body)), ..self }
1605 }
1606
1607 /// Ensures that the send endpoint request uses a specific event type.
1608 ///
1609 /// Note: works with *any* room.
1610 ///
1611 /// # Examples
1612 ///
1613 /// see also [`MatrixMockServer::mock_room_send`] for more context.
1614 ///
1615 /// ```
1616 /// # tokio_test::block_on(async {
1617 /// use matrix_sdk::{
1618 /// ruma::{
1619 /// event_id,
1620 /// events::room::{
1621 /// create::RoomCreateEventContent, power_levels::RoomPowerLevelsEventContent,
1622 /// },
1623 /// events::StateEventType,
1624 /// room_id,
1625 /// },
1626 /// test_utils::mocks::MatrixMockServer,
1627 /// };
1628 ///
1629 /// let mock_server = MatrixMockServer::new().await;
1630 /// let client = mock_server.client_builder().build().await;
1631 ///
1632 /// mock_server.mock_room_state_encryption().plain().mount().await;
1633 ///
1634 /// let room = mock_server.sync_joined_room(&client, room_id!("!room_id:localhost")).await;
1635 ///
1636 /// let event_id = event_id!("$some_id");
1637 ///
1638 /// mock_server
1639 /// .mock_room_send_state()
1640 /// .for_type(StateEventType::RoomPowerLevels)
1641 /// .ok(event_id)
1642 /// .expect(1)
1643 /// .mount()
1644 /// .await;
1645 ///
1646 /// let response_not_mocked = room.send_state_event(RoomCreateEventContent::new_v11()).await;
1647 /// // The `m.room.reaction` event type should not be mocked by the server.
1648 /// assert!(response_not_mocked.is_err());
1649 ///
1650 /// let response = room.send_state_event(RoomPowerLevelsEventContent::new()).await?;
1651 /// // The `m.room.message` event type should be mocked by the server.
1652 /// assert_eq!(
1653 /// event_id, response.event_id,
1654 /// "The event ID we mocked should match the one we received when we sent the event"
1655 /// );
1656 ///
1657 /// # anyhow::Ok(()) });
1658 /// ```
1659 pub fn for_type(mut self, event_type: StateEventType) -> Self {
1660 self.endpoint.event_type = Some(event_type);
1661 // Note: we may have already defined a path, but this one ought to be more
1662 // specialized (unless for_key/for_type were called multiple times).
1663 Self { mock: self.mock.and(path_regex(Self::generate_path_regexp(&self.endpoint))), ..self }
1664 }
1665
1666 /// Ensures the event was sent as a delayed event.
1667 ///
1668 /// See also [the MSC](https://github.com/matrix-org/matrix-spec-proposals/pull/4140).
1669 ///
1670 /// Note: works with *any* room.
1671 ///
1672 /// # Examples
1673 ///
1674 /// see also [`MatrixMockServer::mock_room_send`] for more context.
1675 ///
1676 /// ```
1677 /// # tokio_test::block_on(async {
1678 /// use matrix_sdk::{
1679 /// ruma::{
1680 /// api::client::delayed_events::{delayed_state_event, DelayParameters},
1681 /// events::{room::create::RoomCreateEventContent, AnyStateEventContent},
1682 /// room_id,
1683 /// time::Duration,
1684 /// },
1685 /// test_utils::mocks::MatrixMockServer,
1686 /// };
1687 /// use wiremock::ResponseTemplate;
1688 /// use serde_json::json;
1689 ///
1690 /// let mock_server = MatrixMockServer::new().await;
1691 /// let client = mock_server.client_builder().build().await;
1692 ///
1693 /// mock_server.mock_room_state_encryption().plain().mount().await;
1694 ///
1695 /// let room = mock_server.sync_joined_room(&client, room_id!("!room_id:localhost")).await;
1696 ///
1697 /// mock_server
1698 /// .mock_room_send_state()
1699 /// .match_delayed_event(Duration::from_millis(500))
1700 /// .respond_with(ResponseTemplate::new(200).set_body_json(json!({"delay_id":"$some_id"})))
1701 /// .mock_once()
1702 /// .mount()
1703 /// .await;
1704 ///
1705 /// let response_not_mocked = room.send_state_event(RoomCreateEventContent::new_v11()).await;
1706 /// // A non delayed event should not be mocked by the server.
1707 /// assert!(response_not_mocked.is_err());
1708 ///
1709 /// let r = delayed_state_event::unstable::Request::new(
1710 /// room.room_id().to_owned(),
1711 /// "".to_owned(),
1712 /// DelayParameters::Timeout { timeout: Duration::from_millis(500) },
1713 /// &AnyStateEventContent::RoomCreate(RoomCreateEventContent::new_v11()),
1714 /// )
1715 /// .unwrap();
1716 /// let response = room.client().send(r).await.unwrap();
1717 /// // The delayed `m.room.message` event type should be mocked by the server.
1718 /// assert_eq!("$some_id", response.delay_id);
1719 ///
1720 /// # anyhow::Ok(()) });
1721 /// ```
1722 pub fn match_delayed_event(self, delay: Duration) -> Self {
1723 Self {
1724 mock: self
1725 .mock
1726 .and(query_param("org.matrix.msc4140.delay", delay.as_millis().to_string())),
1727 ..self
1728 }
1729 }
1730
1731 ///
1732 /// ```
1733 /// # tokio_test::block_on(async {
1734 /// use matrix_sdk::{
1735 /// ruma::{
1736 /// event_id,
1737 /// events::{call::member::CallMemberEventContent, AnyStateEventContent},
1738 /// room_id,
1739 /// },
1740 /// test_utils::mocks::MatrixMockServer,
1741 /// };
1742 ///
1743 /// let mock_server = MatrixMockServer::new().await;
1744 /// let client = mock_server.client_builder().build().await;
1745 ///
1746 /// mock_server.mock_room_state_encryption().plain().mount().await;
1747 ///
1748 /// let room = mock_server.sync_joined_room(&client, room_id!("!room_id:localhost")).await;
1749 ///
1750 /// let event_id = event_id!("$some_id");
1751 ///
1752 /// mock_server
1753 /// .mock_room_send_state()
1754 /// .for_key("my_key".to_owned())
1755 /// .ok(event_id)
1756 /// .expect(1)
1757 /// .mount()
1758 /// .await;
1759 ///
1760 /// let response_not_mocked = room
1761 /// .send_state_event_for_key(
1762 /// "",
1763 /// AnyStateEventContent::CallMember(CallMemberEventContent::new_empty(None)),
1764 /// )
1765 /// .await;
1766 /// // The `m.room.reaction` event type should not be mocked by the server.
1767 /// assert!(response_not_mocked.is_err());
1768 ///
1769 /// let response = room
1770 /// .send_state_event_for_key(
1771 /// "my_key",
1772 /// AnyStateEventContent::CallMember(CallMemberEventContent::new_empty(None)),
1773 /// )
1774 /// .await
1775 /// .unwrap();
1776 ///
1777 /// // The `m.room.message` event type should be mocked by the server.
1778 /// assert_eq!(
1779 /// event_id, response.event_id,
1780 /// "The event ID we mocked should match the one we received when we sent the event"
1781 /// );
1782 /// # anyhow::Ok(()) });
1783 /// ```
1784 pub fn for_key(mut self, state_key: String) -> Self {
1785 self.endpoint.state_key = Some(state_key);
1786 // Note: we may have already defined a path, but this one ought to be more
1787 // specialized (unless for_key/for_type were called multiple times).
1788 Self { mock: self.mock.and(path_regex(Self::generate_path_regexp(&self.endpoint))), ..self }
1789 }
1790
1791 /// Returns a send endpoint that emulates success, i.e. the event has been
1792 /// sent with the given event id.
1793 ///
1794 /// # Examples
1795 /// ```
1796 /// # tokio_test::block_on(async {
1797 /// use matrix_sdk::{ruma::{room_id, event_id}, test_utils::mocks::MatrixMockServer};
1798 /// use serde_json::json;
1799 ///
1800 /// let mock_server = MatrixMockServer::new().await;
1801 /// let client = mock_server.client_builder().build().await;
1802 ///
1803 /// mock_server.mock_room_state_encryption().plain().mount().await;
1804 ///
1805 /// let room = mock_server
1806 /// .sync_joined_room(&client, room_id!("!room_id:localhost"))
1807 /// .await;
1808 ///
1809 /// let event_id = event_id!("$some_id");
1810 /// let send_guard = mock_server
1811 /// .mock_room_send_state()
1812 /// .ok(event_id)
1813 /// .expect(1)
1814 /// .mount_as_scoped()
1815 /// .await;
1816 ///
1817 /// let response = room.send_state_event_raw("m.room.message", "my_key", json!({ "body": "Hello world" })).await?;
1818 ///
1819 /// assert_eq!(
1820 /// event_id,
1821 /// response.event_id,
1822 /// "The event ID we mocked should match the one we received when we sent the event"
1823 /// );
1824 /// # anyhow::Ok(()) });
1825 /// ```
1826 pub fn ok(self, returned_event_id: impl Into<OwnedEventId>) -> MatrixMock<'a> {
1827 self.ok_with_event_id(returned_event_id.into())
1828 }
1829}
1830
1831/// A prebuilt mock for running sync v2.
1832pub struct SyncEndpoint {
1833 sync_response_builder: Arc<Mutex<SyncResponseBuilder>>,
1834}
1835
1836impl MockEndpoint<'_, SyncEndpoint> {
1837 /// Temporarily mocks the sync with the given endpoint and runs a client
1838 /// sync with it.
1839 ///
1840 /// After calling this function, the sync endpoint isn't mocked anymore.
1841 ///
1842 /// # Examples
1843 ///
1844 /// ```
1845 /// # tokio_test::block_on(async {
1846 /// use matrix_sdk::{ruma::room_id, test_utils::mocks::MatrixMockServer};
1847 /// use matrix_sdk_test::JoinedRoomBuilder;
1848 ///
1849 /// // First create the mock server and client pair.
1850 /// let mock_server = MatrixMockServer::new().await;
1851 /// let client = mock_server.client_builder().build().await;
1852 /// let room_id = room_id!("!room_id:localhost");
1853 ///
1854 /// // Let's emulate what `MatrixMockServer::sync_joined_room()` does.
1855 /// mock_server
1856 /// .mock_sync()
1857 /// .ok_and_run(&client, |builder| {
1858 /// builder.add_joined_room(JoinedRoomBuilder::new(room_id));
1859 /// })
1860 /// .await;
1861 ///
1862 /// let room = client
1863 /// .get_room(room_id)
1864 /// .expect("The room should be available after we mocked the sync");
1865 /// # anyhow::Ok(()) });
1866 /// ```
1867 pub async fn ok_and_run<F: FnOnce(&mut SyncResponseBuilder)>(self, client: &Client, func: F) {
1868 let json_response = {
1869 let mut builder = self.endpoint.sync_response_builder.lock().unwrap();
1870 func(&mut builder);
1871 builder.build_json_sync_response()
1872 };
1873
1874 let _scope = self
1875 .mock
1876 .respond_with(ResponseTemplate::new(200).set_body_json(json_response))
1877 .mount_as_scoped(self.server)
1878 .await;
1879
1880 let _response = client.sync_once(Default::default()).await.unwrap();
1881 }
1882}
1883
1884/// A prebuilt mock for reading the encryption state of a room.
1885pub struct EncryptionStateEndpoint;
1886
1887impl<'a> MockEndpoint<'a, EncryptionStateEndpoint> {
1888 /// Marks the room as encrypted.
1889 ///
1890 /// # Examples
1891 ///
1892 /// ```
1893 /// # tokio_test::block_on(async {
1894 /// use matrix_sdk::{ruma::room_id, test_utils::mocks::MatrixMockServer};
1895 ///
1896 /// let mock_server = MatrixMockServer::new().await;
1897 /// let client = mock_server.client_builder().build().await;
1898 ///
1899 /// mock_server.mock_room_state_encryption().encrypted().mount().await;
1900 ///
1901 /// let room = mock_server
1902 /// .sync_joined_room(&client, room_id!("!room_id:localhost"))
1903 /// .await;
1904 ///
1905 /// assert!(
1906 /// room.latest_encryption_state().await?.is_encrypted(),
1907 /// "The room should be marked as encrypted."
1908 /// );
1909 /// # anyhow::Ok(()) });
1910 /// ```
1911 pub fn encrypted(self) -> MatrixMock<'a> {
1912 self.respond_with(
1913 ResponseTemplate::new(200).set_body_json(&*test_json::sync_events::ENCRYPTION_CONTENT),
1914 )
1915 }
1916
1917 /// Marks the room as not encrypted.
1918 ///
1919 /// # Examples
1920 ///
1921 /// ```
1922 /// # tokio_test::block_on(async {
1923 /// use matrix_sdk::{ruma::room_id, test_utils::mocks::MatrixMockServer};
1924 ///
1925 /// let mock_server = MatrixMockServer::new().await;
1926 /// let client = mock_server.client_builder().build().await;
1927 ///
1928 /// mock_server.mock_room_state_encryption().plain().mount().await;
1929 ///
1930 /// let room = mock_server
1931 /// .sync_joined_room(&client, room_id!("!room_id:localhost"))
1932 /// .await;
1933 ///
1934 /// assert!(
1935 /// !room.latest_encryption_state().await?.is_encrypted(),
1936 /// "The room should not be marked as encrypted."
1937 /// );
1938 /// # anyhow::Ok(()) });
1939 /// ```
1940 pub fn plain(self) -> MatrixMock<'a> {
1941 self.respond_with(ResponseTemplate::new(404).set_body_json(&*test_json::NOT_FOUND))
1942 }
1943}
1944
1945/// A prebuilt mock for setting the encryption state of a room.
1946pub struct SetEncryptionStateEndpoint;
1947
1948impl<'a> MockEndpoint<'a, SetEncryptionStateEndpoint> {
1949 /// Returns a mock for a successful setting of the encryption state event.
1950 pub fn ok(self, returned_event_id: impl Into<OwnedEventId>) -> MatrixMock<'a> {
1951 self.ok_with_event_id(returned_event_id.into())
1952 }
1953}
1954
1955/// A prebuilt mock for redacting an event in a room.
1956pub struct RoomRedactEndpoint;
1957
1958impl<'a> MockEndpoint<'a, RoomRedactEndpoint> {
1959 /// Returns a redact endpoint that emulates success, i.e. the redaction
1960 /// event has been sent with the given event id.
1961 pub fn ok(self, returned_event_id: impl Into<OwnedEventId>) -> MatrixMock<'a> {
1962 self.ok_with_event_id(returned_event_id.into())
1963 }
1964}
1965
1966/// A prebuilt mock for getting a single event in a room.
1967pub struct RoomEventEndpoint {
1968 room: Option<OwnedRoomId>,
1969 match_event_id: bool,
1970}
1971
1972impl<'a> MockEndpoint<'a, RoomEventEndpoint> {
1973 /// Limits the scope of this mock to a specific room.
1974 pub fn room(mut self, room: impl Into<OwnedRoomId>) -> Self {
1975 self.endpoint.room = Some(room.into());
1976 self
1977 }
1978
1979 /// Whether the mock checks for the event id from the event.
1980 pub fn match_event_id(mut self) -> Self {
1981 self.endpoint.match_event_id = true;
1982 self
1983 }
1984
1985 /// Returns a redact endpoint that emulates success, i.e. the redaction
1986 /// event has been sent with the given event id.
1987 pub fn ok(self, event: TimelineEvent) -> MatrixMock<'a> {
1988 let event_path = if self.endpoint.match_event_id {
1989 let event_id = event.kind.event_id().expect("an event id is required");
1990 // The event id should begin with `$`, which would be taken as the end of the
1991 // regex so we need to escape it
1992 event_id.as_str().replace("$", "\\$")
1993 } else {
1994 // Event is at the end, so no need to add anything.
1995 "".to_owned()
1996 };
1997
1998 let room_path = self.endpoint.room.map_or_else(|| ".*".to_owned(), |room| room.to_string());
1999
2000 let mock = self
2001 .mock
2002 .and(path_regex(format!(r"^/_matrix/client/v3/rooms/{room_path}/event/{event_path}")))
2003 .respond_with(ResponseTemplate::new(200).set_body_json(event.into_raw().json()));
2004 MatrixMock { server: self.server, mock }
2005 }
2006}
2007
2008/// A prebuilt mock for the `/messages` endpoint.
2009pub struct RoomMessagesEndpoint;
2010
2011/// A prebuilt mock for getting a room messages in a room.
2012impl<'a> MockEndpoint<'a, RoomMessagesEndpoint> {
2013 /// Expects an optional limit to be set on the request.
2014 pub fn match_limit(self, limit: u32) -> Self {
2015 Self { mock: self.mock.and(query_param("limit", limit.to_string())), ..self }
2016 }
2017
2018 /// Expects an optional `from` to be set on the request.
2019 pub fn match_from(self, from: &str) -> Self {
2020 Self { mock: self.mock.and(query_param("from", from)), ..self }
2021 }
2022
2023 /// Returns a messages endpoint that emulates success, i.e. the messages
2024 /// provided as `response` could be retrieved.
2025 ///
2026 /// Note: pass `chunk` in the correct order: topological for forward
2027 /// pagination, reverse topological for backwards pagination.
2028 pub fn ok(self, response: RoomMessagesResponseTemplate) -> MatrixMock<'a> {
2029 let mut template = ResponseTemplate::new(200).set_body_json(json!({
2030 "start": response.start,
2031 "end": response.end,
2032 "chunk": response.chunk,
2033 "state": response.state,
2034 }));
2035
2036 if let Some(delay) = response.delay {
2037 template = template.set_delay(delay);
2038 }
2039
2040 self.respond_with(template)
2041 }
2042}
2043
2044/// A response to a [`RoomMessagesEndpoint`] query.
2045pub struct RoomMessagesResponseTemplate {
2046 /// The start token for this /messages query.
2047 pub start: String,
2048 /// The end token for this /messages query (previous batch for back
2049 /// paginations, next batch for forward paginations).
2050 pub end: Option<String>,
2051 /// The set of timeline events returned by this query.
2052 pub chunk: Vec<Raw<AnyTimelineEvent>>,
2053 /// The set of state events returned by this query.
2054 pub state: Vec<Raw<AnyStateEvent>>,
2055 /// Optional delay to respond to the query.
2056 pub delay: Option<Duration>,
2057}
2058
2059impl RoomMessagesResponseTemplate {
2060 /// Fill the events returned as part of this response.
2061 pub fn events(mut self, chunk: Vec<impl Into<Raw<AnyTimelineEvent>>>) -> Self {
2062 self.chunk = chunk.into_iter().map(Into::into).collect();
2063 self
2064 }
2065
2066 /// Fill the end token.
2067 pub fn end_token(mut self, token: impl Into<String>) -> Self {
2068 self.end = Some(token.into());
2069 self
2070 }
2071
2072 /// Respond with a given delay to the query.
2073 pub fn with_delay(mut self, delay: Duration) -> Self {
2074 self.delay = Some(delay);
2075 self
2076 }
2077}
2078
2079impl Default for RoomMessagesResponseTemplate {
2080 fn default() -> Self {
2081 Self {
2082 start: "start-token-unused".to_owned(),
2083 end: Default::default(),
2084 chunk: Default::default(),
2085 state: Default::default(),
2086 delay: None,
2087 }
2088 }
2089}
2090
2091/// A prebuilt mock for uploading media.
2092pub struct UploadEndpoint;
2093
2094impl<'a> MockEndpoint<'a, UploadEndpoint> {
2095 /// Expect that the content type matches what's given here.
2096 pub fn expect_mime_type(self, content_type: &str) -> Self {
2097 Self { mock: self.mock.and(header("content-type", content_type)), ..self }
2098 }
2099
2100 /// Returns a redact endpoint that emulates success, i.e. the redaction
2101 /// event has been sent with the given event id.
2102 pub fn ok(self, mxc_id: &MxcUri) -> MatrixMock<'a> {
2103 self.respond_with(ResponseTemplate::new(200).set_body_json(json!({
2104 "content_uri": mxc_id
2105 })))
2106 }
2107}
2108
2109/// A prebuilt mock for resolving a room alias.
2110pub struct ResolveRoomAliasEndpoint;
2111
2112impl<'a> MockEndpoint<'a, ResolveRoomAliasEndpoint> {
2113 /// Sets up the endpoint to only intercept requests for the given room
2114 /// alias.
2115 pub fn for_alias(self, alias: impl Into<String>) -> Self {
2116 let alias = alias.into();
2117 Self {
2118 mock: self.mock.and(path_regex(format!(
2119 r"^/_matrix/client/v3/directory/room/{}",
2120 percent_encoded_path(&alias)
2121 ))),
2122 ..self
2123 }
2124 }
2125
2126 /// Returns a data endpoint with a resolved room alias.
2127 pub fn ok(self, room_id: &str, servers: Vec<String>) -> MatrixMock<'a> {
2128 self.respond_with(ResponseTemplate::new(200).set_body_json(json!({
2129 "room_id": room_id,
2130 "servers": servers,
2131 })))
2132 }
2133
2134 /// Returns a data endpoint for a room alias that does not exit.
2135 pub fn not_found(self) -> MatrixMock<'a> {
2136 self.respond_with(ResponseTemplate::new(404).set_body_json(json!({
2137 "errcode": "M_NOT_FOUND",
2138 "error": "Room alias not found."
2139 })))
2140 }
2141}
2142
2143/// A prebuilt mock for creating a room alias.
2144pub struct CreateRoomAliasEndpoint;
2145
2146impl<'a> MockEndpoint<'a, CreateRoomAliasEndpoint> {
2147 /// Returns a data endpoint for creating a room alias.
2148 pub fn ok(self) -> MatrixMock<'a> {
2149 self.respond_with(ResponseTemplate::new(200).set_body_json(json!({})))
2150 }
2151}
2152
2153/// A prebuilt mock for removing a room alias.
2154pub struct RemoveRoomAliasEndpoint;
2155
2156impl<'a> MockEndpoint<'a, RemoveRoomAliasEndpoint> {
2157 /// Returns a data endpoint for removing a room alias.
2158 pub fn ok(self) -> MatrixMock<'a> {
2159 self.respond_with(ResponseTemplate::new(200).set_body_json(json!({})))
2160 }
2161}
2162
2163/// A prebuilt mock for paginating the public room list.
2164pub struct PublicRoomsEndpoint;
2165
2166impl<'a> MockEndpoint<'a, PublicRoomsEndpoint> {
2167 /// Returns a data endpoint for paginating the public room list.
2168 pub fn ok(
2169 self,
2170 chunk: Vec<PublicRoomsChunk>,
2171 next_batch: Option<String>,
2172 prev_batch: Option<String>,
2173 total_room_count_estimate: Option<u64>,
2174 ) -> MatrixMock<'a> {
2175 self.respond_with(ResponseTemplate::new(200).set_body_json(json!({
2176 "chunk": chunk,
2177 "next_batch": next_batch,
2178 "prev_batch": prev_batch,
2179 "total_room_count_estimate": total_room_count_estimate,
2180 })))
2181 }
2182
2183 /// Returns a data endpoint for paginating the public room list with several
2184 /// `via` params.
2185 ///
2186 /// Each `via` param must be in the `server_map` parameter, otherwise it'll
2187 /// fail.
2188 pub fn ok_with_via_params(
2189 self,
2190 server_map: BTreeMap<OwnedServerName, Vec<PublicRoomsChunk>>,
2191 ) -> MatrixMock<'a> {
2192 self.respond_with(move |req: &Request| {
2193 #[derive(Deserialize)]
2194 struct PartialRequest {
2195 server: Option<OwnedServerName>,
2196 }
2197
2198 let (_, server) = req
2199 .url
2200 .query_pairs()
2201 .into_iter()
2202 .find(|(key, _)| key == "server")
2203 .expect("Server param not found in request URL");
2204 let server = ServerName::parse(server).expect("Couldn't parse server name");
2205 let chunk = server_map.get(&server).expect("Chunk for the server param not found");
2206 ResponseTemplate::new(200).set_body_json(json!({
2207 "chunk": chunk,
2208 "total_room_count_estimate": chunk.len(),
2209 }))
2210 })
2211 }
2212}
2213
2214/// A prebuilt mock for getting the room's visibility in the room directory.
2215pub struct GetRoomVisibilityEndpoint;
2216
2217impl<'a> MockEndpoint<'a, GetRoomVisibilityEndpoint> {
2218 /// Returns an endpoint that get the room's public visibility.
2219 pub fn ok(self, visibility: Visibility) -> MatrixMock<'a> {
2220 self.respond_with(ResponseTemplate::new(200).set_body_json(json!({
2221 "visibility": visibility,
2222 })))
2223 }
2224}
2225
2226/// A prebuilt mock for setting the room's visibility in the room directory.
2227pub struct SetRoomVisibilityEndpoint;
2228
2229impl<'a> MockEndpoint<'a, SetRoomVisibilityEndpoint> {
2230 /// Returns an endpoint that updates the room's visibility.
2231 pub fn ok(self) -> MatrixMock<'a> {
2232 self.respond_with(ResponseTemplate::new(200).set_body_json(json!({})))
2233 }
2234}
2235
2236/// A prebuilt mock for `GET room_keys/version`: storage ("backup") of room
2237/// keys.
2238pub struct RoomKeysVersionEndpoint;
2239
2240impl<'a> MockEndpoint<'a, RoomKeysVersionEndpoint> {
2241 /// Returns an endpoint that says there is a single room keys backup
2242 pub fn exists(self) -> MatrixMock<'a> {
2243 self.respond_with(ResponseTemplate::new(200).set_body_json(json!({
2244 "algorithm": "m.megolm_backup.v1.curve25519-aes-sha2",
2245 "auth_data": {
2246 "public_key": "abcdefg",
2247 "signatures": {},
2248 },
2249 "count": 42,
2250 "etag": "anopaquestring",
2251 "version": "1",
2252 })))
2253 }
2254
2255 /// Returns an endpoint that says there is no room keys backup
2256 pub fn none(self) -> MatrixMock<'a> {
2257 self.respond_with(ResponseTemplate::new(404).set_body_json(json!({
2258 "errcode": "M_NOT_FOUND",
2259 "error": "No current backup version"
2260 })))
2261 }
2262
2263 /// Returns an endpoint that 429 errors when we get it
2264 pub fn error429(self) -> MatrixMock<'a> {
2265 self.respond_with(ResponseTemplate::new(429).set_body_json(json!({
2266 "errcode": "M_LIMIT_EXCEEDED",
2267 "error": "Too many requests",
2268 "retry_after_ms": 2000
2269 })))
2270 }
2271
2272 /// Returns an endpoint that 404 errors when we get it
2273 pub fn error404(self) -> MatrixMock<'a> {
2274 self.respond_with(ResponseTemplate::new(404))
2275 }
2276}
2277
2278/// A prebuilt mock for `POST room_keys/version`: adding room key backups.
2279pub struct AddRoomKeysVersionEndpoint;
2280
2281impl<'a> MockEndpoint<'a, AddRoomKeysVersionEndpoint> {
2282 /// Returns an endpoint that may be used to add room key backups
2283 pub fn ok(self) -> MatrixMock<'a> {
2284 self.respond_with(ResponseTemplate::new(200).set_body_json(json!({
2285 "version": "1"
2286 })))
2287 .named("POST for the backup creation")
2288 }
2289}
2290
2291/// A prebuilt mock for `DELETE room_keys/version/xxx`: deleting room key
2292/// backups.
2293pub struct DeleteRoomKeysVersionEndpoint;
2294
2295impl<'a> MockEndpoint<'a, DeleteRoomKeysVersionEndpoint> {
2296 /// Returns an endpoint that allows deleting room key backups
2297 pub fn ok(self) -> MatrixMock<'a> {
2298 self.respond_with(ResponseTemplate::new(200).set_body_json(json!({})))
2299 .named("DELETE for the backup deletion")
2300 }
2301}
2302
2303/// A prebuilt mock for `GET /members` request.
2304pub struct GetRoomMembersEndpoint;
2305
2306impl<'a> MockEndpoint<'a, GetRoomMembersEndpoint> {
2307 /// Returns a successful get members request with a list of members.
2308 pub fn ok(self, members: Vec<Raw<RoomMemberEvent>>) -> MatrixMock<'a> {
2309 self.respond_with(ResponseTemplate::new(200).set_body_json(json!({
2310 "chunk": members,
2311 })))
2312 }
2313}
2314
2315/// A prebuilt mock for `POST /invite` request.
2316pub struct InviteUserByIdEndpoint;
2317
2318impl<'a> MockEndpoint<'a, InviteUserByIdEndpoint> {
2319 /// Returns a successful invite user by id request.
2320 pub fn ok(self) -> MatrixMock<'a> {
2321 self.respond_with(ResponseTemplate::new(200).set_body_json(json!({})))
2322 }
2323}
2324
2325/// A prebuilt mock for `POST /kick` request.
2326pub struct KickUserEndpoint;
2327
2328impl<'a> MockEndpoint<'a, KickUserEndpoint> {
2329 /// Returns a successful kick user request.
2330 pub fn ok(self) -> MatrixMock<'a> {
2331 self.respond_with(ResponseTemplate::new(200).set_body_json(json!({})))
2332 }
2333}
2334
2335/// A prebuilt mock for `POST /ban` request.
2336pub struct BanUserEndpoint;
2337
2338impl<'a> MockEndpoint<'a, BanUserEndpoint> {
2339 /// Returns a successful ban user request.
2340 pub fn ok(self) -> MatrixMock<'a> {
2341 self.respond_with(ResponseTemplate::new(200).set_body_json(json!({})))
2342 }
2343}
2344
2345/// A prebuilt mock for `GET /versions` request.
2346pub struct VersionsEndpoint;
2347
2348impl<'a> MockEndpoint<'a, VersionsEndpoint> {
2349 /// Returns a successful `/_matrix/client/versions` request.
2350 ///
2351 /// The response will return some commonly supported versions.
2352 pub fn ok(self) -> MatrixMock<'a> {
2353 self.respond_with(ResponseTemplate::new(200).set_body_json(json!({
2354 "unstable_features": {
2355 },
2356 "versions": [
2357 "r0.0.1",
2358 "r0.2.0",
2359 "r0.3.0",
2360 "r0.4.0",
2361 "r0.5.0",
2362 "r0.6.0",
2363 "r0.6.1",
2364 "v1.1",
2365 "v1.2",
2366 "v1.3",
2367 "v1.4",
2368 "v1.5",
2369 "v1.6",
2370 "v1.7",
2371 "v1.8",
2372 "v1.9",
2373 "v1.10",
2374 "v1.11"
2375 ]
2376 })))
2377 }
2378}
2379
2380/// A prebuilt mock for the room summary endpoint.
2381pub struct RoomSummaryEndpoint;
2382
2383impl<'a> MockEndpoint<'a, RoomSummaryEndpoint> {
2384 /// Returns a successful response with some default data for the given room
2385 /// id.
2386 pub fn ok(self, room_id: &RoomId) -> MatrixMock<'a> {
2387 self.respond_with(ResponseTemplate::new(200).set_body_json(json!({
2388 "room_id": room_id,
2389 "guest_can_join": true,
2390 "num_joined_members": 1,
2391 "world_readable": true,
2392 "join_rule": "public",
2393 })))
2394 }
2395}
2396
2397/// A prebuilt mock to set a room's pinned events.
2398pub struct SetRoomPinnedEventsEndpoint;
2399
2400impl<'a> MockEndpoint<'a, SetRoomPinnedEventsEndpoint> {
2401 /// Returns a successful response with a given event id.
2402 /// id.
2403 pub fn ok(self, event_id: OwnedEventId) -> MatrixMock<'a> {
2404 self.ok_with_event_id(event_id)
2405 }
2406
2407 /// Returns an error response with a generic error code indicating the
2408 /// client is not authorized to set pinned events.
2409 pub fn unauthorized(self) -> MatrixMock<'a> {
2410 self.respond_with(ResponseTemplate::new(400))
2411 }
2412}
2413
2414/// A prebuilt mock for `GET /account/whoami` request.
2415pub struct WhoAmIEndpoint;
2416
2417impl<'a> MockEndpoint<'a, WhoAmIEndpoint> {
2418 /// Returns a successful response with the default device ID.
2419 pub fn ok(self) -> MatrixMock<'a> {
2420 self.ok_with_device_id(device_id!("D3V1C31D"))
2421 }
2422
2423 /// Returns a successful response with the given device ID.
2424 pub fn ok_with_device_id(self, device_id: &DeviceId) -> MatrixMock<'a> {
2425 self.respond_with(ResponseTemplate::new(200).set_body_json(json!({
2426 "user_id": "@joe:example.org",
2427 "device_id": device_id,
2428 })))
2429 }
2430
2431 /// Returns an error response with an `M_UNKNOWN_TOKEN`.
2432 pub fn err_unknown_token(self) -> MatrixMock<'a> {
2433 self.respond_with(ResponseTemplate::new(401).set_body_json(json!({
2434 "errcode": "M_UNKNOWN_TOKEN",
2435 "error": "Invalid token"
2436 })))
2437 }
2438}
2439
2440/// A prebuilt mock for `POST /keys/upload` request.
2441pub struct UploadKeysEndpoint;
2442
2443impl<'a> MockEndpoint<'a, UploadKeysEndpoint> {
2444 /// Returns a successful response with counts of 10 curve25519 keys and 20
2445 /// signed curve25519 keys.
2446 pub fn ok(self) -> MatrixMock<'a> {
2447 self.respond_with(ResponseTemplate::new(200).set_body_json(json!({
2448 "one_time_key_counts": {
2449 "curve25519": 10,
2450 "signed_curve25519": 20,
2451 },
2452 })))
2453 }
2454}
2455
2456/// A prebuilt mock for `POST /keys/query` request.
2457pub struct QueryKeysEndpoint;
2458
2459impl<'a> MockEndpoint<'a, QueryKeysEndpoint> {
2460 /// Returns a successful empty response.
2461 pub fn ok(self) -> MatrixMock<'a> {
2462 self.respond_with(ResponseTemplate::new(200).set_body_json(json!({})))
2463 }
2464}
2465
2466/// A prebuilt mock for `GET /.well-known/matrix/client` request.
2467pub struct WellKnownEndpoint;
2468
2469impl<'a> MockEndpoint<'a, WellKnownEndpoint> {
2470 /// Returns a successful response.
2471 pub fn ok(self) -> MatrixMock<'a> {
2472 let server_uri = self.server.uri();
2473 self.respond_with(ResponseTemplate::new(200).set_body_json(json!({
2474 "m.homeserver": {
2475 "base_url": server_uri,
2476 },
2477 })))
2478 }
2479}
2480
2481/// A prebuilt mock for `POST /keys/device_signing/upload` request.
2482pub struct UploadCrossSigningKeysEndpoint;
2483
2484impl<'a> MockEndpoint<'a, UploadCrossSigningKeysEndpoint> {
2485 /// Returns a successful empty response.
2486 pub fn ok(self) -> MatrixMock<'a> {
2487 self.respond_with(ResponseTemplate::new(200).set_body_json(json!({})))
2488 }
2489
2490 /// Returns an error response with a UIAA stage that failed to authenticate
2491 /// because of an invalid password.
2492 pub fn uiaa_invalid_password(self) -> MatrixMock<'a> {
2493 self.respond_with(ResponseTemplate::new(401).set_body_json(json!({
2494 "errcode": "M_FORBIDDEN",
2495 "error": "Invalid password",
2496 "flows": [
2497 {
2498 "stages": [
2499 "m.login.password"
2500 ]
2501 }
2502 ],
2503 "params": {},
2504 "session": "oFIJVvtEOCKmRUTYKTYIIPHL"
2505 })))
2506 }
2507
2508 /// Returns an error response with a UIAA stage.
2509 pub fn uiaa(self) -> MatrixMock<'a> {
2510 self.respond_with(ResponseTemplate::new(401).set_body_json(json!({
2511 "flows": [
2512 {
2513 "stages": [
2514 "m.login.password"
2515 ]
2516 }
2517 ],
2518 "params": {},
2519 "session": "oFIJVvtEOCKmRUTYKTYIIPHL"
2520 })))
2521 }
2522
2523 /// Returns an error response with an OAuth 2.0 UIAA stage.
2524 pub fn uiaa_oauth(self) -> MatrixMock<'a> {
2525 let server_uri = self.server.uri();
2526 self.respond_with(ResponseTemplate::new(401).set_body_json(json!({
2527 "session": "dummy",
2528 "flows": [{
2529 "stages": [ "org.matrix.cross_signing_reset" ]
2530 }],
2531 "params": {
2532 "org.matrix.cross_signing_reset": {
2533 "url": format!("{server_uri}/account/?action=org.matrix.cross_signing_reset"),
2534 }
2535 },
2536 "msg": "To reset your end-to-end encryption cross-signing identity, you first need to approve it and then try again."
2537 })))
2538 }
2539}
2540
2541/// A prebuilt mock for `POST /keys/signatures/upload` request.
2542pub struct UploadCrossSigningSignaturesEndpoint;
2543
2544impl<'a> MockEndpoint<'a, UploadCrossSigningSignaturesEndpoint> {
2545 /// Returns a successful empty response.
2546 pub fn ok(self) -> MatrixMock<'a> {
2547 self.respond_with(ResponseTemplate::new(200).set_body_json(json!({})))
2548 }
2549}
2550
2551/// A prebuilt mock for the room leave endpoint.
2552pub struct RoomLeaveEndpoint;
2553
2554impl<'a> MockEndpoint<'a, RoomLeaveEndpoint> {
2555 /// Returns a successful response with some default data for the given room
2556 /// id.
2557 pub fn ok(self, room_id: &RoomId) -> MatrixMock<'a> {
2558 self.respond_with(ResponseTemplate::new(200).set_body_json(json!({
2559 "room_id": room_id,
2560 })))
2561 }
2562}
2563
2564/// A prebuilt mock for `POST /logout` request.
2565pub struct LogoutEndpoint;
2566
2567impl<'a> MockEndpoint<'a, LogoutEndpoint> {
2568 /// Returns a successful empty response.
2569 pub fn ok(self) -> MatrixMock<'a> {
2570 self.respond_with(ResponseTemplate::new(200).set_body_json(json!({})))
2571 }
2572}