7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1 | use std::{fmt, sync::LazyLock}; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 2 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 3 | use http::Response; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 4 | pub use matrix_sdk_test_macros::async_test; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 5 | use ruma::{ |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 6 | RoomId, UserId, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 7 | api::{IncomingResponse, OutgoingResponse}, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 8 | room_id, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 9 | serde::{Base64, base64::UrlSafe}, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 10 | user_id, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 11 | }; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 12 | use sha2::{Digest, Sha256}; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 13 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 14 | /// Create a `Raw<AnyMessageLikeEventContent>` from arbitrary JSON. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 15 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 16 | /// Forwards all arguments to [`serde_json::json`]. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 17 | #[macro_export] |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 18 | macro_rules! message_like_event_content { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 19 | ($( $tt:tt )*) => { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 20 | ::ruma::serde::Raw::new(&::serde_json::json!( $($tt)* )) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 21 | .unwrap() |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 22 | .cast_unchecked::<::ruma::events::AnyMessageLikeEventContent>() |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 23 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 24 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 25 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 26 | /// Create a `Raw<AnyTimelineEvent>` from arbitrary JSON. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 27 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 28 | /// Forwards all arguments to [`serde_json::json`]. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 29 | #[macro_export] |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 30 | macro_rules! timeline_event { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 31 | ($( $tt:tt )*) => { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 32 | ::ruma::serde::Raw::new(&::serde_json::json!( $($tt)* )) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 33 | .unwrap() |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 34 | .cast_unchecked::<::ruma::events::AnyTimelineEvent>() |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 35 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 36 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 37 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 38 | /// Create a `Raw<AnySyncTimelineEvent>` from arbitrary JSON. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 39 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 40 | /// Forwards all arguments to [`serde_json::json`]. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 41 | #[macro_export] |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 42 | macro_rules! sync_timeline_event { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 43 | ($( $tt:tt )*) => { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 44 | ::ruma::serde::Raw::new(&::serde_json::json!( $($tt)* )) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 45 | .unwrap() |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 46 | .cast_unchecked::<::ruma::events::AnySyncTimelineEvent>() |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 47 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 48 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 49 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 50 | /// Create a `Raw<AnySyncStateEvent>` from arbitrary JSON. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 51 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 52 | /// Forwards all arguments to [`serde_json::json`]. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 53 | #[macro_export] |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 54 | macro_rules! sync_state_event { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 55 | ($( $tt:tt )*) => { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 56 | ::ruma::serde::Raw::new(&::serde_json::json!( $($tt)* )) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 57 | .unwrap() |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 58 | .cast_unchecked::<::ruma::events::AnySyncStateEvent>() |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 59 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 60 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 61 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 62 | /// Create a `Raw<AnyStrippedStateEvent>` from arbitrary JSON. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 63 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 64 | /// Forwards all arguments to [`serde_json::json`]. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 65 | #[macro_export] |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 66 | macro_rules! stripped_state_event { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 67 | ($( $tt:tt )*) => { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 68 | ::ruma::serde::Raw::new(&::serde_json::json!( $($tt)* )) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 69 | .unwrap() |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 70 | .cast_unchecked::<::ruma::events::AnyStrippedStateEvent>() |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 71 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 72 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 73 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 74 | #[cfg(not(target_family = "wasm"))] |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 75 | pub mod mocks; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 76 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 77 | pub mod event_factory; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 78 | pub mod notification_settings; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 79 | mod sync_builder; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 80 | pub mod test_json; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 81 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 82 | pub use self::sync_builder::{ |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 83 | InvitedRoomBuilder, JoinedRoomBuilder, KnockedRoomBuilder, LeftRoomBuilder, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 84 | SyncResponseBuilder, bulk_room_members, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 85 | }; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 86 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 87 | pub static ALICE: LazyLock<&UserId> = LazyLock::new(|| user_id!("@alice:server.name")); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 88 | pub static BOB: LazyLock<&UserId> = LazyLock::new(|| user_id!("@bob:other.server")); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 89 | pub static CAROL: LazyLock<&UserId> = LazyLock::new(|| user_id!("@carol:other.server")); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 90 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 91 | /// The default room ID for tests. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 92 | pub static DEFAULT_TEST_ROOM_ID: LazyLock<&RoomId> = |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 93 | LazyLock::new(|| room_id!("!SVkFJHzfwvuaIEawgC:localhost")); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 94 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 95 | /// Build a typed Ruma [`IncomingResponse`] object from a json body. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 96 | pub fn ruma_response_from_json<ResponseType: IncomingResponse>( |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 97 | json: &serde_json::Value, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 98 | ) -> ResponseType { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 99 | let json_bytes = serde_json::to_vec(json).expect("JSON-serialization of response value failed"); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 100 | let http_response = |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 101 | Response::builder().status(200).body(json_bytes).expect("Failed to build HTTP response"); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 102 | ResponseType::try_from_http_response(http_response).expect("Can't parse the response json") |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 103 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 104 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 105 | /// Serialise a typed Ruma [`OutgoingResponse`] object to JSON. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 106 | pub fn ruma_response_to_json<ResponseType: OutgoingResponse>( |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 107 | response: ResponseType, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 108 | ) -> serde_json::Value { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 109 | let http_response: Response<Vec<u8>> = |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 110 | response.try_into_http_response().expect("Failed to build HTTP response"); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 111 | let json_bytes = http_response.into_body(); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 112 | serde_json::from_slice(&json_bytes).expect("Can't parse the response JSON") |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 113 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 114 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 115 | #[derive(Debug)] // required to be able to return TestResult from #[test] fns |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 116 | pub enum TestError {} |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 117 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 118 | // If this was just `T: Debug`, it would conflict with |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 119 | // the `impl From<T> for T` in `std`. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 120 | // |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 121 | // Adding a dummy `Display` bound works around this. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 122 | impl<T: fmt::Display + fmt::Debug> From<T> for TestError { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 123 | #[track_caller] |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 124 | fn from(value: T) -> Self { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 125 | panic!("error: {value:?}") |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 126 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 127 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 128 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 129 | pub type TestResult = Result<(), TestError>; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 130 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 131 | /// Compute the sha256 hash of the given bytes and convert it to unpadded |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 132 | /// URL-safe base64. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 133 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 134 | /// This matches the format of event IDs since room version 4, and of room IDs |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 135 | /// since room version 12. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 136 | pub fn base64_sha256_hash(bytes: &[u8]) -> String { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 137 | Base64::<UrlSafe, _>::new(Sha256::digest(bytes)).encode() |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 138 | } |