From 4eb1337dc8067e513bd78c70ef5ef7e605570d6c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20Commaille?= Date: Fri, 1 Jul 2022 14:56:57 +0200 Subject: [PATCH 1/5] ci: Remove whitespaces in config file --- .github/workflows/ci.yml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8c3bb757f..fb813b1a3 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -175,7 +175,7 @@ jobs: steps: - name: Checkout the repo uses: actions/checkout@v2 - + - name: Install Rust uses: actions-rs/toolchain@v1 with: @@ -184,24 +184,24 @@ jobs: components: clippy profile: minimal override: true - + - name: Install wasm-pack uses: jetli/wasm-pack-action@v0.3.0 with: version: latest - + - name: Load cache uses: Swatinem/rust-cache@v1 - + - name: Install nextest uses: taiki-e/install-action@nextest - + - name: Rust Check uses: actions-rs/cargo@v1 with: command: run args: -p xtask -- ci wasm ${{ matrix.cmd }} - + - name: Wasm-Pack test uses: actions-rs/cargo@v1 with: From dd6a902240e7990172c9245b0a45f4aea5666075 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20Commaille?= Date: Fri, 1 Jul 2022 14:57:55 +0200 Subject: [PATCH 2/5] test(sdk): Move integration tests --- .github/workflows/ci.yml | 2 +- crates/matrix-sdk/Cargo.toml | 3 + crates/matrix-sdk/src/client/builder.rs | 4 +- crates/matrix-sdk/src/client/mod.rs | 2032 +---------------- crates/matrix-sdk/tests/integration/client.rs | 642 ++++++ crates/matrix-sdk/tests/integration/main.rs | 31 + .../tests/integration/room/common.rs | 813 +++++++ .../tests/integration/room/joined.rs | 569 +++++ .../matrix-sdk/tests/integration/room/left.rs | 34 + .../matrix-sdk/tests/integration/room/mod.rs | 3 + xtask/src/ci.rs | 2 +- 11 files changed, 2104 insertions(+), 2031 deletions(-) create mode 100644 crates/matrix-sdk/tests/integration/client.rs create mode 100644 crates/matrix-sdk/tests/integration/main.rs create mode 100644 crates/matrix-sdk/tests/integration/room/common.rs create mode 100644 crates/matrix-sdk/tests/integration/room/joined.rs create mode 100644 crates/matrix-sdk/tests/integration/room/left.rs create mode 100644 crates/matrix-sdk/tests/integration/room/mod.rs diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index fb813b1a3..537f3426b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -127,7 +127,7 @@ jobs: uses: actions-rs/cargo@v1 with: command: nextest - args: run --workspace + args: run --workspace --features __test - name: Test documentation uses: actions-rs/cargo@v1 diff --git a/crates/matrix-sdk/Cargo.toml b/crates/matrix-sdk/Cargo.toml index c6f87f915..d3ab05d32 100644 --- a/crates/matrix-sdk/Cargo.toml +++ b/crates/matrix-sdk/Cargo.toml @@ -55,6 +55,9 @@ docsrs = [ "image-proc", ] +# This is an internal feature only used for tests +__test = [] + [dependencies] anyhow = { version = "1.0.57", optional = true } anymap2 = "0.13.0" diff --git a/crates/matrix-sdk/src/client/builder.rs b/crates/matrix-sdk/src/client/builder.rs index 41705380a..34443d0e3 100644 --- a/crates/matrix-sdk/src/client/builder.rs +++ b/crates/matrix-sdk/src/client/builder.rs @@ -343,12 +343,12 @@ impl ClientBuilder { } fn homeserver_from_name(server_name: &ServerName) -> String { - #[cfg(not(test))] + #[cfg(not(any(test, feature = "__test")))] return format!("https://{}", server_name); // Mockito only knows how to test http endpoints: // https://github.com/lipanski/mockito/issues/127 - #[cfg(test)] + #[cfg(any(test, feature = "__test"))] return format!("http://{}", server_name); } diff --git a/crates/matrix-sdk/src/client/mod.rs b/crates/matrix-sdk/src/client/mod.rs index a6011e36e..19fd69b14 100644 --- a/crates/matrix-sdk/src/client/mod.rs +++ b/crates/matrix-sdk/src/client/mod.rs @@ -2193,62 +2193,18 @@ impl Client { // mockito (the http mocking library) is not supported for wasm32 #[cfg(all(test, not(target_arch = "wasm32")))] pub(crate) mod tests { - use matrix_sdk_test::async_test; + use std::time::Duration; + + use matrix_sdk_test::{async_test, test_json, EventBuilder, EventsJson}; #[cfg(target_arch = "wasm32")] wasm_bindgen_test::wasm_bindgen_test_configure!(run_in_browser); - use std::{collections::BTreeMap, convert::TryInto, io::Cursor, str::FromStr, time::Duration}; - - use matrix_sdk_base::{ - media::{MediaFormat, MediaRequest, MediaThumbnailSize}, - DisplayName, - }; - #[cfg(feature = "experimental-timeline")] - use matrix_sdk_common::deserialized_responses::SyncRoomEvent; - use matrix_sdk_test::{test_json, EventBuilder, EventsJson}; use mockito::{mock, Matcher}; - use ruma::{ - api::{ - client::{ - self as client_api, - account::register::{v3::Request as RegistrationRequest, RegistrationKind}, - directory::{ - get_public_rooms, - get_public_rooms_filtered::{self, v3::Request as PublicRoomsFilterRequest}, - }, - media::get_content_thumbnail::v3::Method, - membership::Invite3pidInit, - session::get_login_types::v3::LoginType, - uiaa::{self, UiaaResponse}, - }, - error::{FromHttpResponseError, ServerError}, - MatrixVersion, - }, - assign, device_id, - directory::Filter, - event_id, - events::{ - room::{ - message::{ImageMessageEventContent, RoomMessageEventContent}, - ImageInfo, MediaSource, - }, - AnySyncStateEvent, StateEventType, - }, - mxc_uri, room_id, thirdparty, uint, user_id, TransactionId, UserId, - }; - use serde_json::{json, Value as JsonValue}; + use ruma::{api::MatrixVersion, device_id, room_id, user_id}; use url::Url; use super::{Client, ClientBuilder, Session}; - use crate::{ - attachment::{ - AttachmentConfig, AttachmentInfo, BaseImageInfo, BaseThumbnailInfo, BaseVideoInfo, - Thumbnail, - }, - config::{RequestConfig, SyncSettings}, - error::RumaApiError, - HttpError, RoomMember, - }; + use crate::config::{RequestConfig, SyncSettings}; fn test_client_builder() -> ClientBuilder { let homeserver = Url::parse(&mockito::server_url()).unwrap(); @@ -2275,257 +2231,6 @@ pub(crate) mod tests { client } - #[async_test] - async fn set_homeserver() { - let client = no_retry_test_client().await; - let homeserver = Url::from_str("http://example.com/").unwrap(); - client.set_homeserver(homeserver.clone()).await; - - assert_eq!(client.homeserver().await, homeserver); - } - - #[async_test] - async fn successful_discovery() { - let server_url = mockito::server_url(); - let domain = server_url.strip_prefix("http://").unwrap(); - let alice = UserId::parse("@alice:".to_owned() + domain).unwrap(); - - let _m_well_known = mock("GET", "/.well-known/matrix/client") - .with_status(200) - .with_body( - test_json::WELL_KNOWN.to_string().replace("HOMESERVER_URL", server_url.as_ref()), - ) - .create(); - - let _m_versions = mock("GET", "/_matrix/client/versions") - .with_status(200) - .with_body(test_json::VERSIONS.to_string()) - .create(); - let client = Client::builder().user_id(&alice).build().await.unwrap(); - - assert_eq!(client.homeserver().await, Url::parse(server_url.as_ref()).unwrap()); - } - - #[async_test] - async fn discovery_broken_server() { - let server_url = mockito::server_url(); - let domain = server_url.strip_prefix("http://").unwrap(); - let alice = UserId::parse("@alice:".to_owned() + domain).unwrap(); - - let _m = mock("GET", "/.well-known/matrix/client").with_status(404).create(); - - assert!( - Client::builder().user_id(&alice).build().await.is_err(), - "Creating a client from a user ID should fail when the .well-known request fails." - ); - } - - #[async_test] - async fn login() { - let homeserver = Url::from_str(&mockito::server_url()).unwrap(); - let client = no_retry_test_client().await; - - let _m_types = mock("GET", "/_matrix/client/r0/login") - .with_status(200) - .with_body(test_json::LOGIN_TYPES.to_string()) - .create(); - - let can_password = client - .get_login_types() - .await - .unwrap() - .flows - .iter() - .any(|flow| matches!(flow, LoginType::Password(_))); - assert!(can_password); - - let _m_login = mock("POST", "/_matrix/client/r0/login") - .with_status(200) - .with_body(test_json::LOGIN.to_string()) - .create(); - - client.login_username("example", "wordpass").send().await.unwrap(); - - let logged_in = client.logged_in(); - assert!(logged_in, "Client should be logged in"); - - assert_eq!(client.homeserver().await, homeserver); - } - - #[async_test] - async fn login_with_discovery() { - let client = no_retry_test_client().await; - - let _m_login = mock("POST", "/_matrix/client/r0/login") - .with_status(200) - .with_body(test_json::LOGIN_WITH_DISCOVERY.to_string()) - .create(); - - client.login_username("example", "wordpass").send().await.unwrap(); - - let logged_in = client.logged_in(); - assert!(logged_in, "Client should be logged in"); - - assert_eq!(client.homeserver().await.as_str(), "https://example.org/"); - } - - #[async_test] - async fn login_no_discovery() { - let client = no_retry_test_client().await; - - let _m_login = mock("POST", "/_matrix/client/r0/login") - .with_status(200) - .with_body(test_json::LOGIN.to_string()) - .create(); - - client.login_username("example", "wordpass").send().await.unwrap(); - - let logged_in = client.logged_in(); - assert!(logged_in, "Client should be logged in"); - - assert_eq!(client.homeserver().await, Url::parse(&mockito::server_url()).unwrap()); - } - - #[async_test] - #[cfg(feature = "sso-login")] - async fn login_with_sso() { - let _m_login = mock("POST", "/_matrix/client/r0/login") - .with_status(200) - .with_body(test_json::LOGIN.to_string()) - .create(); - - let _homeserver = Url::from_str(&mockito::server_url()).unwrap(); - let client = no_retry_test_client().await; - let idp = crate::client::get_login_types::v3::IdentityProvider::new( - "some-id".to_owned(), - "idp-name".to_owned(), - ); - client - .login_sso(|sso_url| async move { - let sso_url = Url::parse(&sso_url).unwrap(); - - let (_, redirect) = - sso_url.query_pairs().find(|(key, _)| key == "redirectUrl").unwrap(); - - let mut redirect_url = Url::parse(&redirect).unwrap(); - redirect_url.set_query(Some("loginToken=tinytoken")); - - reqwest::get(redirect_url.to_string()).await.unwrap(); - - Ok(()) - }) - .identity_provider_id(&idp.id) - .send() - .await - .unwrap(); - - let logged_in = client.logged_in(); - assert!(logged_in, "Client should be logged in"); - } - - #[async_test] - async fn login_with_sso_token() { - let client = no_retry_test_client().await; - - let _m = mock("GET", "/_matrix/client/r0/login") - .with_status(200) - .with_body(test_json::LOGIN_TYPES.to_string()) - .create(); - - let can_sso = client - .get_login_types() - .await - .unwrap() - .flows - .iter() - .any(|flow| matches!(flow, LoginType::Sso(_))); - assert!(can_sso); - - let sso_url = client.get_sso_login_url("http://127.0.0.1:3030", None).await; - assert!(sso_url.is_ok()); - - let _m = mock("POST", "/_matrix/client/r0/login") - .with_status(200) - .with_body(test_json::LOGIN.to_string()) - .create(); - - client.login_token("averysmalltoken").send().await.unwrap(); - - let logged_in = client.logged_in(); - assert!(logged_in, "Client should be logged in"); - } - - #[async_test] - async fn devices() { - let client = logged_in_client().await; - - let _m = mock("GET", "/_matrix/client/r0/devices") - .with_status(200) - .with_body(test_json::DEVICES.to_string()) - .create(); - - assert!(client.devices().await.is_ok()); - } - - #[async_test] - async fn resolve_room_alias() { - let client = no_retry_test_client().await; - - let _m = mock("GET", "/_matrix/client/r0/directory/room/%23alias%3Aexample%2Eorg") - .with_status(200) - .with_body(test_json::GET_ALIAS.to_string()) - .create(); - - let alias = ruma::room_alias_id!("#alias:example.org"); - assert!(client.resolve_room_alias(alias).await.is_ok()); - } - - #[async_test] - async fn test_join_leave_room() { - let room_id = room_id!("!SVkFJHzfwvuaIEawgC:localhost"); - - let _m = mock("GET", Matcher::Regex(r"^/_matrix/client/r0/sync\?.*$".to_owned())) - .with_status(200) - .with_body(test_json::SYNC.to_string()) - .create(); - - let client = logged_in_client().await; - let session = client.session().unwrap().clone(); - - let room = client.get_joined_room(room_id); - assert!(room.is_none()); - - client.sync_once(SyncSettings::default()).await.unwrap(); - - let room = client.get_left_room(room_id); - assert!(room.is_none()); - - let room = client.get_joined_room(room_id); - assert!(room.is_some()); - - // test store reloads with correct room state from the state store - let joined_client = no_retry_test_client().await; - joined_client.restore_login(session).await.unwrap(); - - // joined room reloaded from state store - joined_client.sync_once(SyncSettings::default()).await.unwrap(); - let room = joined_client.get_joined_room(room_id); - assert!(room.is_some()); - - let _m = mock("GET", Matcher::Regex(r"^/_matrix/client/r0/sync\?.*$".to_owned())) - .with_status(200) - .with_body(test_json::LEAVE_SYNC_EVENT.to_string()) - .create(); - - joined_client.sync_once(SyncSettings::default()).await.unwrap(); - - let room = joined_client.get_joined_room(room_id); - assert!(room.is_none()); - - let room = joined_client.get_left_room(room_id); - assert!(room.is_some()); - } - #[async_test] async fn account_data() { let client = logged_in_client().await; @@ -2562,928 +2267,6 @@ pub(crate) mod tests { assert!(room.is_some()); } - #[async_test] - async fn login_error() { - let client = no_retry_test_client().await; - - let _m = mock("POST", "/_matrix/client/r0/login") - .with_status(403) - .with_body(test_json::LOGIN_RESPONSE_ERR.to_string()) - .create(); - - if let Err(err) = client.login_username("example", "wordpass").send().await { - if let crate::Error::Http(HttpError::Api(FromHttpResponseError::Server( - ServerError::Known(RumaApiError::ClientApi(client_api::Error { - kind, - message, - status_code, - })), - ))) = err - { - if let client_api::error::ErrorKind::Forbidden = kind { - } else { - panic!("found the wrong `ErrorKind` {:?}, expected `Forbidden", kind); - } - assert_eq!(message, "Invalid password".to_owned()); - assert_eq!(status_code, http::StatusCode::from_u16(403).unwrap()); - } else { - panic!("found the wrong `Error` type {:?}, expected `Error::RumaResponse", err); - } - } else { - panic!("this request should return an `Err` variant") - } - } - - #[async_test] - async fn register_error() { - let client = no_retry_test_client().await; - - let _m = mock("POST", Matcher::Regex(r"^/_matrix/client/r0/register\?.*$".to_owned())) - .with_status(403) - .with_body(test_json::REGISTRATION_RESPONSE_ERR.to_string()) - .create(); - - let user = assign!(RegistrationRequest::new(), { - username: Some("user"), - password: Some("password"), - auth: Some(uiaa::AuthData::FallbackAcknowledgement( - uiaa::FallbackAcknowledgement::new("foobar"), - )), - kind: RegistrationKind::User, - }); - - if let Err(err) = client.register(user).await { - if let HttpError::UiaaError(FromHttpResponseError::Server(ServerError::Known( - UiaaResponse::MatrixError(client_api::Error { kind, message, status_code }), - ))) = err - { - if let client_api::error::ErrorKind::Forbidden = kind { - } else { - panic!("found the wrong `ErrorKind` {:?}, expected `Forbidden", kind); - } - assert_eq!(message, "Invalid password".to_owned()); - assert_eq!(status_code, http::StatusCode::from_u16(403).unwrap()); - } else { - panic!("found the wrong `Error` type {:#?}, expected `UiaaResponse`", err); - } - } else { - panic!("this request should return an `Err` variant") - } - } - - #[async_test] - async fn join_room_by_id() { - let client = logged_in_client().await; - - let _m = mock("POST", Matcher::Regex(r"^/_matrix/client/r0/rooms/.*/join".to_owned())) - .with_status(200) - .with_body(test_json::ROOM_ID.to_string()) - .match_header("authorization", "Bearer 1234") - .create(); - - let room_id = room_id!("!testroom:example.org"); - - assert_eq!( - // this is the `join_by_room_id::Response` but since no PartialEq we check the RoomId - // field - client.join_room_by_id(room_id).await.unwrap().room_id, - room_id - ); - } - - #[async_test] - async fn join_room_by_id_or_alias() { - let client = logged_in_client().await; - - let _m = mock("POST", Matcher::Regex(r"^/_matrix/client/r0/join/".to_owned())) - .with_status(200) - .with_body(test_json::ROOM_ID.to_string()) - .match_header("authorization", "Bearer 1234") - .create(); - - let room_id = room_id!("!testroom:example.org").into(); - - assert_eq!( - // this is the `join_by_room_id::Response` but since no PartialEq we check the RoomId - // field - client - .join_room_by_id_or_alias(room_id, &["server.com".try_into().unwrap()]) - .await - .unwrap() - .room_id, - room_id!("!testroom:example.org") - ); - } - - #[async_test] - async fn invite_user_by_id() { - let client = logged_in_client().await; - - let _m = mock("POST", Matcher::Regex(r"^/_matrix/client/r0/rooms/.*/invite".to_owned())) - .with_status(200) - .with_body(test_json::LOGOUT.to_string()) - .match_header("authorization", "Bearer 1234") - .create(); - - let _m = mock("GET", Matcher::Regex(r"^/_matrix/client/r0/sync\?.*$".to_owned())) - .with_status(200) - .match_header("authorization", "Bearer 1234") - .with_body(test_json::SYNC.to_string()) - .create(); - - let sync_settings = SyncSettings::new().timeout(Duration::from_millis(3000)); - - let _response = client.sync_once(sync_settings).await.unwrap(); - - let user = user_id!("@example:localhost"); - let room = client.get_joined_room(room_id!("!SVkFJHzfwvuaIEawgC:localhost")).unwrap(); - - room.invite_user_by_id(user).await.unwrap(); - } - - #[async_test] - async fn invite_user_by_3pid() { - let client = logged_in_client().await; - - let _m = mock("POST", Matcher::Regex(r"^/_matrix/client/r0/rooms/.*/invite".to_owned())) - .with_status(200) - // empty JSON object - .with_body(test_json::LOGOUT.to_string()) - .match_header("authorization", "Bearer 1234") - .create(); - - let _m = mock("GET", Matcher::Regex(r"^/_matrix/client/r0/sync\?.*$".to_owned())) - .with_status(200) - .match_header("authorization", "Bearer 1234") - .with_body(test_json::SYNC.to_string()) - .create(); - - let sync_settings = SyncSettings::new().timeout(Duration::from_millis(3000)); - - let _response = client.sync_once(sync_settings).await.unwrap(); - - let room = client.get_joined_room(room_id!("!SVkFJHzfwvuaIEawgC:localhost")).unwrap(); - - room.invite_user_by_3pid( - Invite3pidInit { - id_server: "example.org", - id_access_token: "IdToken", - medium: thirdparty::Medium::Email, - address: "address", - } - .into(), - ) - .await - .unwrap(); - } - - #[async_test] - async fn room_search_all() { - let client = no_retry_test_client().await; - - let _m = mock("GET", Matcher::Regex(r"^/_matrix/client/r0/publicRooms".to_owned())) - .with_status(200) - .with_body(test_json::PUBLIC_ROOMS.to_string()) - .create(); - - let get_public_rooms::v3::Response { chunk, .. } = - client.public_rooms(Some(10), None, None).await.unwrap(); - assert_eq!(chunk.len(), 1); - } - - #[async_test] - async fn room_search_filtered() { - let client = logged_in_client().await; - - let _m = mock("POST", Matcher::Regex(r"^/_matrix/client/r0/publicRooms".to_owned())) - .with_status(200) - .with_body(test_json::PUBLIC_ROOMS.to_string()) - .match_header("authorization", "Bearer 1234") - .create(); - - let generic_search_term = Some("cheese"); - let filter = assign!(Filter::new(), { generic_search_term }); - let request = assign!(PublicRoomsFilterRequest::new(), { filter }); - - let get_public_rooms_filtered::v3::Response { chunk, .. } = - client.public_rooms_filtered(request).await.unwrap(); - assert_eq!(chunk.len(), 1); - } - - #[async_test] - async fn leave_room() { - let client = logged_in_client().await; - - let _m = mock("POST", Matcher::Regex(r"^/_matrix/client/r0/rooms/.*/leave".to_owned())) - .with_status(200) - // this is an empty JSON object - .with_body(test_json::LOGOUT.to_string()) - .match_header("authorization", "Bearer 1234") - .create(); - - let _m = mock("GET", Matcher::Regex(r"^/_matrix/client/r0/sync\?.*$".to_owned())) - .with_status(200) - .match_header("authorization", "Bearer 1234") - .with_body(test_json::SYNC.to_string()) - .create(); - - let sync_settings = SyncSettings::new().timeout(Duration::from_millis(3000)); - - let _response = client.sync_once(sync_settings).await.unwrap(); - - let room = client.get_joined_room(room_id!("!SVkFJHzfwvuaIEawgC:localhost")).unwrap(); - - room.leave().await.unwrap(); - } - - #[async_test] - async fn ban_user() { - let client = logged_in_client().await; - - let _m = mock("POST", Matcher::Regex(r"^/_matrix/client/r0/rooms/.*/ban".to_owned())) - .with_status(200) - // this is an empty JSON object - .with_body(test_json::LOGOUT.to_string()) - .match_header("authorization", "Bearer 1234") - .create(); - - let _m = mock("GET", Matcher::Regex(r"^/_matrix/client/r0/sync\?.*$".to_owned())) - .with_status(200) - .match_header("authorization", "Bearer 1234") - .with_body(test_json::SYNC.to_string()) - .create(); - - let sync_settings = SyncSettings::new().timeout(Duration::from_millis(3000)); - - let _response = client.sync_once(sync_settings).await.unwrap(); - - let user = user_id!("@example:localhost"); - let room = client.get_joined_room(room_id!("!SVkFJHzfwvuaIEawgC:localhost")).unwrap(); - - room.ban_user(user, None).await.unwrap(); - } - - #[async_test] - async fn kick_user() { - let client = logged_in_client().await; - - let _m = mock("POST", Matcher::Regex(r"^/_matrix/client/r0/rooms/.*/kick".to_owned())) - .with_status(200) - // this is an empty JSON object - .with_body(test_json::LOGOUT.to_string()) - .match_header("authorization", "Bearer 1234") - .create(); - - let _m = mock("GET", Matcher::Regex(r"^/_matrix/client/r0/sync\?.*$".to_owned())) - .with_status(200) - .match_header("authorization", "Bearer 1234") - .with_body(test_json::SYNC.to_string()) - .create(); - - let sync_settings = SyncSettings::new().timeout(Duration::from_millis(3000)); - - let _response = client.sync_once(sync_settings).await.unwrap(); - - let user = user_id!("@example:localhost"); - let room = client.get_joined_room(room_id!("!SVkFJHzfwvuaIEawgC:localhost")).unwrap(); - - room.kick_user(user, None).await.unwrap(); - } - - #[async_test] - async fn forget_room() { - let client = logged_in_client().await; - - let _m = mock("POST", Matcher::Regex(r"^/_matrix/client/r0/rooms/.*/forget".to_owned())) - .with_status(200) - // this is an empty JSON object - .with_body(test_json::LOGOUT.to_string()) - .match_header("authorization", "Bearer 1234") - .create(); - - let _m = mock("GET", Matcher::Regex(r"^/_matrix/client/r0/sync\?.*$".to_owned())) - .with_status(200) - .match_header("authorization", "Bearer 1234") - .with_body(test_json::LEAVE_SYNC.to_string()) - .create(); - - let sync_settings = SyncSettings::new().timeout(Duration::from_millis(3000)); - - let _response = client.sync_once(sync_settings).await.unwrap(); - - let room = client.get_left_room(room_id!("!SVkFJHzfwvuaIEawgC:localhost")).unwrap(); - - room.forget().await.unwrap(); - } - - #[async_test] - async fn read_receipt() { - let client = logged_in_client().await; - - let _m = mock("POST", Matcher::Regex(r"^/_matrix/client/r0/rooms/.*/receipt".to_owned())) - .with_status(200) - // this is an empty JSON object - .with_body(test_json::LOGOUT.to_string()) - .match_header("authorization", "Bearer 1234") - .create(); - - let _m = mock("GET", Matcher::Regex(r"^/_matrix/client/r0/sync\?.*$".to_owned())) - .with_status(200) - .match_header("authorization", "Bearer 1234") - .with_body(test_json::SYNC.to_string()) - .create(); - - let sync_settings = SyncSettings::new().timeout(Duration::from_millis(3000)); - - let _response = client.sync_once(sync_settings).await.unwrap(); - - let event_id = event_id!("$xxxxxx:example.org"); - let room = client.get_joined_room(room_id!("!SVkFJHzfwvuaIEawgC:localhost")).unwrap(); - - room.read_receipt(event_id).await.unwrap(); - } - - #[async_test] - async fn read_marker() { - let client = logged_in_client().await; - - let _m = - mock("POST", Matcher::Regex(r"^/_matrix/client/r0/rooms/.*/read_markers".to_owned())) - .with_status(200) - // this is an empty JSON object - .with_body(test_json::LOGOUT.to_string()) - .match_header("authorization", "Bearer 1234") - .create(); - - let _m = mock("GET", Matcher::Regex(r"^/_matrix/client/r0/sync\?.*$".to_owned())) - .with_status(200) - .match_header("authorization", "Bearer 1234") - .with_body(test_json::SYNC.to_string()) - .create(); - - let sync_settings = SyncSettings::new().timeout(Duration::from_millis(3000)); - - let _response = client.sync_once(sync_settings).await.unwrap(); - - let event_id = event_id!("$xxxxxx:example.org"); - let room = client.get_joined_room(room_id!("!SVkFJHzfwvuaIEawgC:localhost")).unwrap(); - - room.read_marker(event_id, None).await.unwrap(); - } - - #[async_test] - async fn typing_notice() { - let client = logged_in_client().await; - - let _m = mock("PUT", Matcher::Regex(r"^/_matrix/client/r0/rooms/.*/typing".to_owned())) - .with_status(200) - // this is an empty JSON object - .with_body(test_json::LOGOUT.to_string()) - .match_header("authorization", "Bearer 1234") - .create(); - - let _m = mock("GET", Matcher::Regex(r"^/_matrix/client/r0/sync\?.*$".to_owned())) - .with_status(200) - .match_header("authorization", "Bearer 1234") - .with_body(test_json::SYNC.to_string()) - .create(); - - let sync_settings = SyncSettings::new().timeout(Duration::from_millis(3000)); - - let _response = client.sync_once(sync_settings).await.unwrap(); - - let room = client.get_joined_room(room_id!("!SVkFJHzfwvuaIEawgC:localhost")).unwrap(); - - room.typing_notice(true).await.unwrap(); - } - - #[async_test] - async fn room_state_event_send() { - use ruma::events::room::member::{MembershipState, RoomMemberEventContent}; - - let client = logged_in_client().await; - - let _m = mock("PUT", Matcher::Regex(r"^/_matrix/client/r0/rooms/.*/state/.*".to_owned())) - .with_status(200) - .match_header("authorization", "Bearer 1234") - .with_body(test_json::EVENT_ID.to_string()) - .create(); - - let _m = mock("GET", Matcher::Regex(r"^/_matrix/client/r0/sync\?.*$".to_owned())) - .with_status(200) - .match_header("authorization", "Bearer 1234") - .with_body(test_json::SYNC.to_string()) - .create(); - - let sync_settings = SyncSettings::new().timeout(Duration::from_millis(3000)); - - let _response = client.sync_once(sync_settings).await.unwrap(); - - let room_id = room_id!("!SVkFJHzfwvuaIEawgC:localhost"); - - let room = client.get_joined_room(room_id).unwrap(); - - let avatar_url = mxc_uri!("mxc://example.org/avA7ar"); - let member_event = assign!(RoomMemberEventContent::new(MembershipState::Join), { - avatar_url: Some(avatar_url.to_owned()) - }); - let response = room.send_state_event(member_event, "").await.unwrap(); - assert_eq!(event_id!("$h29iv0s8:example.com"), response.event_id); - } - - #[async_test] - async fn room_message_send() { - let client = logged_in_client().await; - - let _m = mock("PUT", Matcher::Regex(r"^/_matrix/client/r0/rooms/.*/send/".to_owned())) - .with_status(200) - .match_header("authorization", "Bearer 1234") - .with_body(test_json::EVENT_ID.to_string()) - .create(); - - let _m = mock("GET", Matcher::Regex(r"^/_matrix/client/r0/sync\?.*$".to_owned())) - .with_status(200) - .match_header("authorization", "Bearer 1234") - .with_body(test_json::SYNC.to_string()) - .create(); - - let sync_settings = SyncSettings::new().timeout(Duration::from_millis(3000)); - - let _response = client.sync_once(sync_settings).await.unwrap(); - - let room = client.get_joined_room(room_id!("!SVkFJHzfwvuaIEawgC:localhost")).unwrap(); - - let content = RoomMessageEventContent::text_plain("Hello world"); - let txn_id = TransactionId::new(); - let response = room.send(content, Some(&txn_id)).await.unwrap(); - - assert_eq!(event_id!("$h29iv0s8:example.com"), response.event_id) - } - - #[async_test] - async fn room_attachment_send() { - let client = logged_in_client().await; - - let _m = mock("PUT", Matcher::Regex(r"^/_matrix/client/r0/rooms/.*/send/".to_owned())) - .with_status(200) - .match_header("authorization", "Bearer 1234") - .match_body(Matcher::PartialJson(json!({ - "info": { - "mimetype": "image/jpeg" - } - }))) - .with_body(test_json::EVENT_ID.to_string()) - .create(); - - let _m = mock("POST", Matcher::Regex(r"^/_matrix/media/r0/upload".to_owned())) - .with_status(200) - .match_header("content-type", "image/jpeg") - .with_body( - json!({ - "content_uri": "mxc://example.com/AQwafuaFswefuhsfAFAgsw" - }) - .to_string(), - ) - .create(); - - let _m = mock("GET", Matcher::Regex(r"^/_matrix/client/r0/sync\?.*$".to_owned())) - .with_status(200) - .match_header("authorization", "Bearer 1234") - .with_body(test_json::SYNC.to_string()) - .create(); - - let sync_settings = SyncSettings::new().timeout(Duration::from_millis(3000)); - - let _response = client.sync_once(sync_settings).await.unwrap(); - - let room = client.get_joined_room(room_id!("!SVkFJHzfwvuaIEawgC:localhost")).unwrap(); - - let mut media = Cursor::new("Hello world"); - - let response = room - .send_attachment("image", &mime::IMAGE_JPEG, &mut media, AttachmentConfig::new()) - .await - .unwrap(); - - assert_eq!(event_id!("$h29iv0s8:example.com"), response.event_id) - } - - #[async_test] - async fn room_attachment_send_info() { - let client = logged_in_client().await; - - let _m = mock("PUT", Matcher::Regex(r"^/_matrix/client/r0/rooms/.*/send/".to_owned())) - .with_status(200) - .match_header("authorization", "Bearer 1234") - .match_body(Matcher::PartialJson(json!({ - "info": { - "mimetype": "image/jpeg", - "h": 600, - "w": 800, - } - }))) - .with_body(test_json::EVENT_ID.to_string()) - .create(); - - let upload_mock = mock("POST", Matcher::Regex(r"^/_matrix/media/r0/upload".to_owned())) - .with_status(200) - .match_header("content-type", "image/jpeg") - .with_body( - json!({ - "content_uri": "mxc://example.com/AQwafuaFswefuhsfAFAgsw" - }) - .to_string(), - ) - .create(); - - let _m = mock("GET", Matcher::Regex(r"^/_matrix/client/r0/sync\?.*$".to_owned())) - .with_status(200) - .match_header("authorization", "Bearer 1234") - .with_body(test_json::SYNC.to_string()) - .create(); - - let sync_settings = SyncSettings::new().timeout(Duration::from_millis(3000)); - - let _response = client.sync_once(sync_settings).await.unwrap(); - - let room = client.get_joined_room(room_id!("!SVkFJHzfwvuaIEawgC:localhost")).unwrap(); - - let mut media = Cursor::new("Hello world"); - - let config = AttachmentConfig::new().info(AttachmentInfo::Image(BaseImageInfo { - height: Some(uint!(600)), - width: Some(uint!(800)), - size: None, - blurhash: None, - })); - - let response = - room.send_attachment("image", &mime::IMAGE_JPEG, &mut media, config).await.unwrap(); - - upload_mock.assert(); - assert_eq!(event_id!("$h29iv0s8:example.com"), response.event_id) - } - - #[async_test] - async fn room_attachment_send_wrong_info() { - let client = logged_in_client().await; - - let _m = mock("PUT", Matcher::Regex(r"^/_matrix/client/r0/rooms/.*/send/".to_owned())) - .with_status(200) - .match_header("authorization", "Bearer 1234") - .match_body(Matcher::PartialJson(json!({ - "info": { - "mimetype": "image/jpeg", - "h": 600, - "w": 800, - } - }))) - .with_body(test_json::EVENT_ID.to_string()) - .create(); - - let _m = mock("POST", Matcher::Regex(r"^/_matrix/media/r0/upload".to_owned())) - .with_status(200) - .match_header("content-type", "image/jpeg") - .with_body( - json!({ - "content_uri": "mxc://example.com/AQwafuaFswefuhsfAFAgsw" - }) - .to_string(), - ) - .create(); - - let _m = mock("GET", Matcher::Regex(r"^/_matrix/client/r0/sync\?.*$".to_owned())) - .with_status(200) - .match_header("authorization", "Bearer 1234") - .with_body(test_json::SYNC.to_string()) - .create(); - - let sync_settings = SyncSettings::new().timeout(Duration::from_millis(3000)); - - let _response = client.sync_once(sync_settings).await.unwrap(); - - let room = client.get_joined_room(room_id!("!SVkFJHzfwvuaIEawgC:localhost")).unwrap(); - - let mut media = Cursor::new("Hello world"); - - let config = AttachmentConfig::new().info(AttachmentInfo::Video(BaseVideoInfo { - height: Some(uint!(600)), - width: Some(uint!(800)), - duration: Some(Duration::from_millis(3600)), - size: None, - blurhash: None, - })); - - let response = room.send_attachment("image", &mime::IMAGE_JPEG, &mut media, config).await; - - assert!(response.is_err()) - } - - #[async_test] - async fn room_attachment_send_info_thumbnail() { - let client = logged_in_client().await; - - let _m = mock("PUT", Matcher::Regex(r"^/_matrix/client/r0/rooms/.*/send/".to_owned())) - .with_status(200) - .match_header("authorization", "Bearer 1234") - .match_body(Matcher::PartialJson(json!({ - "info": { - "mimetype": "image/jpeg", - "h": 600, - "w": 800, - "thumbnail_info": { - "h": 360, - "w": 480, - "mimetype":"image/jpeg", - "size": 3600, - }, - "thumbnail_url": "mxc://example.com/AQwafuaFswefuhsfAFAgsw", - } - }))) - .with_body(test_json::EVENT_ID.to_string()) - .create(); - - let upload_mock = mock("POST", Matcher::Regex(r"^/_matrix/media/r0/upload".to_owned())) - .with_status(200) - .match_header("content-type", "image/jpeg") - .with_body( - json!({ - "content_uri": "mxc://example.com/AQwafuaFswefuhsfAFAgsw" - }) - .to_string(), - ) - .expect(2) - .create(); - - let _m = mock("GET", Matcher::Regex(r"^/_matrix/client/r0/sync\?.*$".to_owned())) - .with_status(200) - .match_header("authorization", "Bearer 1234") - .with_body(test_json::SYNC.to_string()) - .create(); - - let sync_settings = SyncSettings::new().timeout(Duration::from_millis(3000)); - - let _response = client.sync_once(sync_settings).await.unwrap(); - - let room = client.get_joined_room(room_id!("!SVkFJHzfwvuaIEawgC:localhost")).unwrap(); - - let mut media = Cursor::new("Hello world"); - - let mut thumbnail_reader = Cursor::new("Thumbnail"); - - let config = AttachmentConfig::with_thumbnail(Thumbnail { - reader: &mut thumbnail_reader, - content_type: &mime::IMAGE_JPEG, - info: Some(BaseThumbnailInfo { - height: Some(uint!(360)), - width: Some(uint!(480)), - size: Some(uint!(3600)), - }), - }) - .info(AttachmentInfo::Image(BaseImageInfo { - height: Some(uint!(600)), - width: Some(uint!(800)), - size: None, - blurhash: None, - })); - - let response = - room.send_attachment("image", &mime::IMAGE_JPEG, &mut media, config).await.unwrap(); - - upload_mock.assert(); - assert_eq!(event_id!("$h29iv0s8:example.com"), response.event_id) - } - - #[async_test] - async fn room_redact() { - let client = logged_in_client().await; - - let _m = - mock("PUT", Matcher::Regex(r"^/_matrix/client/r0/rooms/.*/redact/.*?/.*?".to_owned())) - .with_status(200) - .match_header("authorization", "Bearer 1234") - .with_body(test_json::EVENT_ID.to_string()) - .create(); - - let _m = mock("GET", Matcher::Regex(r"^/_matrix/client/r0/sync\?.*$".to_owned())) - .with_status(200) - .match_header("authorization", "Bearer 1234") - .with_body(test_json::SYNC.to_string()) - .create(); - - let sync_settings = SyncSettings::new().timeout(Duration::from_millis(3000)); - - let _response = client.sync_once(sync_settings).await.unwrap(); - - let room = client.get_joined_room(room_id!("!SVkFJHzfwvuaIEawgC:localhost")).unwrap(); - - let event_id = event_id!("$xxxxxxxx:example.com"); - - let txn_id = TransactionId::new(); - let reason = Some("Indecent material"); - let response = room.redact(event_id, reason, Some(txn_id)).await.unwrap(); - - assert_eq!(event_id!("$h29iv0s8:example.com"), response.event_id) - } - - #[async_test] - async fn user_presence() { - let client = logged_in_client().await; - - let _m = mock("GET", Matcher::Regex(r"^/_matrix/client/r0/sync\?.*$".to_owned())) - .with_status(200) - .match_header("authorization", "Bearer 1234") - .with_body(test_json::SYNC.to_string()) - .create(); - - let _m = mock("GET", Matcher::Regex(r"^/_matrix/client/r0/rooms/.*/members".to_owned())) - .with_status(200) - .match_header("authorization", "Bearer 1234") - .with_body(test_json::MEMBERS.to_string()) - .create(); - - let sync_settings = SyncSettings::new().timeout(Duration::from_millis(3000)); - - let _response = client.sync_once(sync_settings).await.unwrap(); - - let room = client.get_joined_room(room_id!("!SVkFJHzfwvuaIEawgC:localhost")).unwrap(); - let members: Vec = room.active_members().await.unwrap(); - - assert_eq!(2, members.len()); - // assert!(room.power_levels.is_some()) - } - - #[async_test] - async fn calculate_room_names_from_summary() { - let client = logged_in_client().await; - - let _m = mock("GET", Matcher::Regex(r"^/_matrix/client/r0/sync\?.*$".to_owned())) - .with_status(200) - .match_header("authorization", "Bearer 1234") - .with_body(test_json::DEFAULT_SYNC_SUMMARY.to_string()) - .create(); - - let sync_settings = SyncSettings::new().timeout(Duration::from_millis(3000)); - let _response = client.sync_once(sync_settings).await.unwrap(); - let room = client.get_joined_room(room_id!("!SVkFJHzfwvuaIEawgC:localhost")).unwrap(); - - assert_eq!( - DisplayName::Calculated("example2".to_owned()), - room.display_name().await.unwrap() - ); - } - - #[async_test] - async fn invited_rooms() { - let client = logged_in_client().await; - - let _m = mock("GET", Matcher::Regex(r"^/_matrix/client/r0/sync\?.*$".to_owned())) - .with_status(200) - .match_header("authorization", "Bearer 1234") - .with_body(test_json::INVITE_SYNC.to_string()) - .create(); - - let _response = client.sync_once(SyncSettings::default()).await.unwrap(); - - assert!(client.joined_rooms().is_empty()); - assert!(client.left_rooms().is_empty()); - assert!(!client.invited_rooms().is_empty()); - - assert!(client.get_invited_room(room_id!("!696r7674:example.com")).is_some()); - } - - #[async_test] - async fn left_rooms() { - let client = logged_in_client().await; - - let _m = mock("GET", Matcher::Regex(r"^/_matrix/client/r0/sync\?.*$".to_owned())) - .with_status(200) - .match_header("authorization", "Bearer 1234") - .with_body(test_json::LEAVE_SYNC.to_string()) - .create(); - - let _response = client.sync_once(SyncSettings::default()).await.unwrap(); - - assert!(client.joined_rooms().is_empty()); - assert!(!client.left_rooms().is_empty()); - assert!(client.invited_rooms().is_empty()); - - assert!(client.get_left_room(room_id!("!SVkFJHzfwvuaIEawgC:localhost")).is_some()) - } - - #[async_test] - async fn sync() { - let client = logged_in_client().await; - - let _m = mock("GET", Matcher::Regex(r"^/_matrix/client/r0/sync\?.*$".to_owned())) - .with_status(200) - .with_body(test_json::SYNC.to_string()) - .match_header("authorization", "Bearer 1234") - .create(); - - let sync_settings = SyncSettings::new().timeout(Duration::from_millis(3000)); - - let response = client.sync_once(sync_settings).await.unwrap(); - - assert_ne!(response.next_batch, ""); - - assert!(client.sync_token().await.is_some()); - } - - #[async_test] - async fn room_names() { - let client = logged_in_client().await; - - let _m = mock("GET", Matcher::Regex(r"^/_matrix/client/r0/sync\?.*$".to_owned())) - .with_status(200) - .match_header("authorization", "Bearer 1234") - .with_body(test_json::SYNC.to_string()) - .expect_at_least(1) - .create(); - - let sync_settings = SyncSettings::new().timeout(Duration::from_millis(3000)); - - let _response = client.sync_once(sync_settings).await.unwrap(); - - assert_eq!(client.rooms().len(), 1); - let room = client.get_joined_room(room_id!("!SVkFJHzfwvuaIEawgC:localhost")).unwrap(); - - assert_eq!(DisplayName::Aliased("tutorial".to_owned()), room.display_name().await.unwrap()); - - let _m = mock("GET", Matcher::Regex(r"^/_matrix/client/r0/sync\?.*$".to_owned())) - .with_status(200) - .match_header("authorization", "Bearer 1234") - .with_body(test_json::INVITE_SYNC.to_string()) - .expect_at_least(1) - .create(); - - let _response = client.sync_once(SyncSettings::new()).await.unwrap(); - - assert_eq!(client.rooms().len(), 1); - let invited_room = client.get_invited_room(room_id!("!696r7674:example.com")).unwrap(); - - assert_eq!( - DisplayName::Named("My Room Name".to_owned()), - invited_room.display_name().await.unwrap() - ); - } - - #[async_test] - async fn delete_devices() { - let client = no_retry_test_client().await; - - let _m = mock("POST", "/_matrix/client/r0/delete_devices") - .with_status(401) - .with_body( - json!({ - "flows": [ - { - "stages": [ - "m.login.password" - ] - } - ], - "params": {}, - "session": "vBslorikviAjxzYBASOBGfPp" - }) - .to_string(), - ) - .create(); - - let _m = mock("POST", "/_matrix/client/r0/delete_devices") - .with_status(401) - // empty response - // TODO rename that response type. - .with_body(test_json::LOGOUT.to_string()) - .create(); - - let devices = &[device_id!("DEVICEID").to_owned()]; - - if let Err(e) = client.delete_devices(devices, None).await { - if let Some(info) = e.uiaa_response() { - let mut auth_parameters = BTreeMap::new(); - - let identifier = json!({ - "type": "m.id.user", - "user": "example", - }); - auth_parameters.insert("identifier".to_owned(), identifier); - auth_parameters.insert("password".to_owned(), "wordpass".into()); - - let auth_data = uiaa::AuthData::Password(assign!( - uiaa::Password::new( - uiaa::UserIdentifier::UserIdOrLocalpart("example"), - "wordpass", - ), { - session: info.session.as_deref(), - } - )); - - client.delete_devices(devices, Some(auth_data)).await.unwrap(); - } - } - } - #[async_test] async fn retry_limit_http_requests() { let client = test_client_builder() @@ -3551,809 +2334,4 @@ pub(crate) mod tests { panic!("this request should return an `Err` variant") } } - - #[async_test] - async fn get_media_content() { - let client = logged_in_client().await; - - let request = MediaRequest { - source: MediaSource::Plain(mxc_uri!("mxc://localhost/textfile").to_owned()), - format: MediaFormat::File, - }; - - let m = mock( - "GET", - Matcher::Regex(r"^/_matrix/media/r0/download/localhost/textfile\?.*$".to_owned()), - ) - .with_status(200) - .with_body("Some very interesting text.") - .expect(2) - .create(); - - assert!(client.get_media_content(&request, true).await.is_ok()); - assert!(client.get_media_content(&request, true).await.is_ok()); - assert!(client.get_media_content(&request, false).await.is_ok()); - m.assert(); - } - - #[async_test] - async fn get_media_file() { - let client = logged_in_client().await; - - let event_content = ImageMessageEventContent::plain( - "filename.jpg".into(), - mxc_uri!("mxc://example.org/image").to_owned(), - Some(Box::new(assign!(ImageInfo::new(), { - height: Some(uint!(398)), - width: Some(uint!(394)), - mimetype: Some("image/jpeg".into()), - size: Some(uint!(31037)), - }))), - ); - - let m = mock( - "GET", - Matcher::Regex(r"^/_matrix/media/r0/download/example%2Eorg/image\?.*$".to_owned()), - ) - .with_status(200) - .with_body("binaryjpegdata") - .create(); - - assert!(client.get_file(event_content.clone(), true).await.is_ok()); - assert!(client.get_file(event_content.clone(), true).await.is_ok()); - m.assert(); - - let m = mock( - "GET", - Matcher::Regex(r"^/_matrix/media/r0/thumbnail/example%2Eorg/image\?.*$".to_owned()), - ) - .with_status(200) - .with_body("smallerbinaryjpegdata") - .create(); - - assert!(client - .get_thumbnail( - event_content, - MediaThumbnailSize { method: Method::Scale, width: uint!(100), height: uint!(100) }, - true - ) - .await - .is_ok()); - m.assert(); - } - - #[async_test] - async fn whoami() { - let client = logged_in_client().await; - - let _m = mock("GET", "/_matrix/client/r0/account/whoami") - .with_status(200) - .with_body(test_json::WHOAMI.to_string()) - .match_header("authorization", "Bearer 1234") - .create(); - - let user_id = user_id!("@joe:example.org"); - - assert_eq!(client.whoami().await.unwrap().user_id, user_id); - } - - #[async_test] - async fn test_state_event_getting() { - let room_id = room_id!("!SVkFJHzfwvuaIEawgC:localhost"); - - let session = Session { - access_token: "1234".to_owned(), - user_id: user_id!("@example:localhost").to_owned(), - device_id: device_id!("DEVICEID").to_owned(), - }; - - let sync = json!({ - "next_batch": "1234", - "rooms": { - "join": { - "!SVkFJHzfwvuaIEawgC:localhost": { - "state": { - "events": [ - { - "type": "m.custom.note", - "sender": "@example:localhost", - "content": { - "body": "Note 1", - }, - "state_key": "note.1", - "origin_server_ts": 1611853078727u64, - "unsigned": { - "replaces_state": "$2s9GcbVxbbFS3EZY9vN1zhavaDJnF32cAIGAxi99NuQ", - "age": 15458166523u64 - }, - "event_id": "$NVCTvrlxodf3ZGjJ6foxepEq8ysSkTq8wG0wKeQBVZg" - }, - { - "type": "m.custom.note", - "sender": "@example2:localhost", - "content": { - "body": "Note 2", - }, - "state_key": "note.2", - "origin_server_ts": 1611853078727u64, - "unsigned": { - "replaces_state": "$2s9GcbVxbbFS3EZY9vN1zhavaDJnF32cAIGAxi99NuQ", - "age": 15458166523u64 - }, - "event_id": "$NVCTvrlxodf3ZGjJ6foxepEq8ysSkTq8wG0wKeQBVZg" - }, - { - "type": "m.room.encryption", - "sender": "@example:localhost", - "content": { - "algorithm": "m.megolm.v1.aes-sha2" - }, - "state_key": "", - "origin_server_ts": 1586437448151u64, - "unsigned": { - "age": 40873797099u64 - }, - "event_id": "$vyG3wu1QdJSh5gc-09SwjXBXlXo8gS7s4QV_Yxha0Xw" - }, - ] - } - } - } - } - }); - - let _m = mock("GET", Matcher::Regex(r"^/_matrix/client/r0/sync\?.*$".to_owned())) - .with_status(200) - .with_body(sync.to_string()) - .create(); - - let client = test_client_builder() - .request_config(RequestConfig::new().retry_limit(3)) - .build() - .await - .unwrap(); - client.restore_login(session.clone()).await.unwrap(); - - let room = client.get_joined_room(room_id); - assert!(room.is_none()); - - client.sync_once(SyncSettings::default()).await.unwrap(); - - let room = client.get_joined_room(room_id).unwrap(); - - let state_events = room.get_state_events(StateEventType::RoomEncryption).await.unwrap(); - assert_eq!(state_events.len(), 1); - - let state_events = room.get_state_events("m.custom.note".into()).await.unwrap(); - assert_eq!(state_events.len(), 2); - - let encryption_event = room - .get_state_event(StateEventType::RoomEncryption, "") - .await - .unwrap() - .unwrap() - .deserialize() - .unwrap(); - - matches::assert_matches!(encryption_event, AnySyncStateEvent::RoomEncryption(_)); - } - - // FIXME: removing timelines during reading the stream currently leaves to an - // inconsistent undefined state. This tests shows that, but because - // different implementations deal with problem in different, - // inconsistent manners, isn't activated. - //#[async_test] - #[allow(dead_code)] - #[cfg(feature = "experimental-timeline")] - async fn room_timeline_with_remove() { - let client = logged_in_client().await; - let sync_settings = SyncSettings::new().timeout(Duration::from_millis(3000)); - - let sync = mock("GET", Matcher::Regex(r"^/_matrix/client/r0/sync\?.*$".to_owned())) - .with_status(200) - .with_body(test_json::SYNC.to_string()) - .match_header("authorization", "Bearer 1234") - .create(); - - let _ = client.sync_once(sync_settings).await.unwrap(); - sync.assert(); - drop(sync); - let room = client.get_joined_room(room_id!("!SVkFJHzfwvuaIEawgC:localhost")).unwrap(); - let (forward_stream, backward_stream) = room.timeline().await.unwrap(); - - // these two syncs lead to the store removing its existing timeline - // and replace them with new ones - let sync_2 = mock( - "GET", - Matcher::Regex( - r"^/_matrix/client/r0/sync\?.*since=s526_47314_0_7_1_1_1_11444_1.*".to_owned(), - ), - ) - .with_status(200) - .with_body(test_json::MORE_SYNC.to_string()) - .match_header("authorization", "Bearer 1234") - .create(); - - let sync_3 = mock( - "GET", - Matcher::Regex( - r"^/_matrix/client/r0/sync\?.*since=s526_47314_0_7_1_1_1_11444_2.*".to_owned(), - ), - ) - .with_status(200) - .with_body(test_json::MORE_SYNC_2.to_string()) - .match_header("authorization", "Bearer 1234") - .create(); - - let mocked_messages = mock( - "GET", - Matcher::Regex( - r"^/_matrix/client/r0/rooms/.*/messages.*from=t392-516_47314_0_7_1_1_1_11444_1.*" - .to_owned(), - ), - ) - .with_status(200) - .with_body(test_json::SYNC_ROOM_MESSAGES_BATCH_1.to_string()) - .match_header("authorization", "Bearer 1234") - .create(); - - let mocked_messages_2 = mock( - "GET", - Matcher::Regex( - r"^/_matrix/client/r0/rooms/.*/messages.*from=t47409-4357353_219380_26003_2269.*" - .to_owned(), - ), - ) - .with_status(200) - .with_body(test_json::SYNC_ROOM_MESSAGES_BATCH_2.to_string()) - .match_header("authorization", "Bearer 1234") - .create(); - - assert_eq!(client.sync_token().await, Some("s526_47314_0_7_1_1_1_11444_1".to_owned())); - let sync_settings = SyncSettings::new() - .timeout(Duration::from_millis(3000)) - .token("s526_47314_0_7_1_1_1_11444_1"); - let _ = client.sync_once(sync_settings).await.unwrap(); - sync_2.assert(); - let sync_settings = SyncSettings::new() - .timeout(Duration::from_millis(3000)) - .token("s526_47314_0_7_1_1_1_11444_2"); - let _ = client.sync_once(sync_settings).await.unwrap(); - sync_3.assert(); - - let expected_forward_events = vec![ - "$152037280074GZeOm:localhost", - "$editevid:localhost", - "$151957878228ssqrJ:localhost", - "$15275046980maRLj:localhost", - "$15275047031IXQRi:localhost", - "$098237280074GZeOm:localhost", - "$152037280074GZeOm2:localhost", - "$editevid2:localhost", - "$151957878228ssqrJ2:localhost", - "$15275046980maRLj2:localhost", - "$15275047031IXQRi2:localhost", - "$098237280074GZeOm2:localhost", - ]; - - use futures_util::StreamExt; - let forward_events = forward_stream - .take(expected_forward_events.len()) - .collect::>() - .await; - - for (r, e) in forward_events.into_iter().zip(expected_forward_events.iter()) { - assert_eq!(&r.event_id().unwrap().as_str(), e); - } - - let expected_backwards_events = vec![ - "$152037280074GZeOm:localhost", - "$1444812213350496Caaaf:example.com", - "$1444812213350496Cbbbf:example.com", - "$1444812213350496Ccccf:example.com", - "$1444812213350496Caaak:example.com", - "$1444812213350496Cbbbk:example.com", - "$1444812213350496Cccck:example.com", - ]; - - let backward_events = backward_stream - .take(expected_backwards_events.len()) - .collect::>>() - .await; - - for (r, e) in backward_events.into_iter().zip(expected_backwards_events.iter()) { - assert_eq!(&r.unwrap().event_id().unwrap().as_str(), e); - } - - mocked_messages.assert(); - mocked_messages_2.assert(); - } - - #[async_test] - #[cfg(feature = "experimental-timeline")] - async fn room_timeline() { - let client = logged_in_client().await; - let sync_settings = SyncSettings::new().timeout(Duration::from_millis(3000)); - - let sync = mock("GET", Matcher::Regex(r"^/_matrix/client/r0/sync\?.*$".to_owned())) - .with_status(200) - .with_body(test_json::MORE_SYNC.to_string()) - .match_header("authorization", "Bearer 1234") - .create(); - - let _ = client.sync_once(sync_settings).await.unwrap(); - sync.assert(); - drop(sync); - let room = client.get_joined_room(room_id!("!SVkFJHzfwvuaIEawgC:localhost")).unwrap(); - let (forward_stream, backward_stream) = room.timeline().await.unwrap(); - - let sync_2 = mock( - "GET", - Matcher::Regex( - r"^/_matrix/client/r0/sync\?.*since=s526_47314_0_7_1_1_1_11444_2.*".to_owned(), - ), - ) - .with_status(200) - .with_body(test_json::MORE_SYNC_2.to_string()) - .match_header("authorization", "Bearer 1234") - .create(); - - let mocked_messages = mock( - "GET", - Matcher::Regex( - r"^/_matrix/client/r0/rooms/.*/messages.*from=t392-516_47314_0_7_1_1_1_11444_1.*" - .to_owned(), - ), - ) - .with_status(200) - .with_body(test_json::SYNC_ROOM_MESSAGES_BATCH_1.to_string()) - .match_header("authorization", "Bearer 1234") - .create(); - - let mocked_messages_2 = mock( - "GET", - Matcher::Regex( - r"^/_matrix/client/r0/rooms/.*/messages.*from=t47409-4357353_219380_26003_2269.*" - .to_owned(), - ), - ) - .with_status(200) - .with_body(test_json::SYNC_ROOM_MESSAGES_BATCH_2.to_string()) - .match_header("authorization", "Bearer 1234") - .create(); - - assert_eq!(client.sync_token().await, Some("s526_47314_0_7_1_1_1_11444_2".to_owned())); - let sync_settings = SyncSettings::new() - .timeout(Duration::from_millis(3000)) - .token("s526_47314_0_7_1_1_1_11444_2"); - let _ = client.sync_once(sync_settings).await.unwrap(); - sync_2.assert(); - - let expected_forward_events = vec![ - "$152037280074GZeOm2:localhost", - "$editevid2:localhost", - "$151957878228ssqrJ2:localhost", - "$15275046980maRLj2:localhost", - "$15275047031IXQRi2:localhost", - "$098237280074GZeOm2:localhost", - ]; - - use futures_util::StreamExt; - let forward_events = forward_stream - .take(expected_forward_events.len()) - .collect::>() - .await; - - for (r, e) in forward_events.into_iter().zip(expected_forward_events.iter()) { - assert_eq!(&r.event_id().unwrap().as_str(), e); - } - - let expected_backwards_events = vec![ - "$098237280074GZeOm:localhost", - "$15275047031IXQRi:localhost", - "$15275046980maRLj:localhost", - "$151957878228ssqrJ:localhost", - "$editevid:localhost", - "$152037280074GZeOm:localhost", - // ^^^ These come from the first sync before we asked for the timeline and thus - // where cached - // - // While the following are fetched over the network transparently to us after, - // when scrolling back in time: - "$1444812213350496Caaaf:example.com", - "$1444812213350496Cbbbf:example.com", - "$1444812213350496Ccccf:example.com", - "$1444812213350496Caaak:example.com", - "$1444812213350496Cbbbk:example.com", - "$1444812213350496Cccck:example.com", - ]; - - let backward_events = backward_stream - .take(expected_backwards_events.len()) - .collect::>>() - .await; - - for (r, e) in backward_events.into_iter().zip(expected_backwards_events.iter()) { - assert_eq!(&r.unwrap().event_id().unwrap().as_str(), e); - } - - mocked_messages.assert(); - mocked_messages_2.assert(); - } - - #[async_test] - async fn room_permalink() { - fn sync_response(index: u8, room_timeline_events: &[JsonValue]) -> JsonValue { - json!({ - "device_one_time_keys_count": {}, - "next_batch": format!("s526_47314_0_7_1_1_1_11444_{}", index + 1), - "device_lists": { - "changed": [], - "left": [] - }, - "account_data": { - "events": [] - }, - "rooms": { - "invite": {}, - "join": { - "!test_room:127.0.0.1": { - "summary": {}, - "account_data": { - "events": [] - }, - "ephemeral": { - "events": [] - }, - "state": { - "events": [] - }, - "timeline": { - "events": room_timeline_events, - "limited": false, - "prev_batch": format!("s526_47314_0_7_1_1_1_11444_{}", index - 1), - }, - "unread_notifications": { - "highlight_count": 0, - "notification_count": 0, - } - } - }, - "leave": {} - }, - "to_device": { - "events": [] - }, - "presence": { - "events": [] - } - }) - } - - fn room_member_events(nb: usize, server: &str) -> Vec { - let mut events = Vec::with_capacity(nb); - for i in 0..nb { - let id = format!("${server}{i}"); - let user = format!("@user{i}:{server}"); - events.push(json!({ - "content": { - "membership": "join", - }, - "event_id": id, - "origin_server_ts": 151800140, - "sender": user, - "state_key": user, - "type": "m.room.member", - })) - } - events - } - - let client = logged_in_client().await; - let sync_settings = SyncSettings::new(); - - // Without elligible server - let mut sync_index = 1; - let res = sync_response( - sync_index, - &[ - json!({ - "content": { - "creator": "@creator:127.0.0.1", - "room_version": "6", - }, - "event_id": "$151957878228ekrDs", - "origin_server_ts": 15195787, - "sender": "@creator:localhost", - "state_key": "", - "type": "m.room.create", - }), - json!({ - "content": { - "membership": "join", - }, - "event_id": "$151800140517rfvjc", - "origin_server_ts": 151800140, - "sender": "@creator:127.0.0.1", - "state_key": "@creator:127.0.0.1", - "type": "m.room.member", - }), - ], - ); - let _sync = mock("GET", Matcher::Regex(r"^/_matrix/client/r0/sync\?.*$".to_owned())) - .with_status(200) - .with_body(res.to_string()) - .match_header("authorization", "Bearer 1234") - .create(); - client.sync_once(sync_settings.clone()).await.unwrap(); - let room = client.get_room(room_id!("!test_room:127.0.0.1")).unwrap(); - - assert_eq!( - room.matrix_to_permalink().await.unwrap().to_string(), - "https://matrix.to/#/%21test_room%3A127.0.0.1" - ); - assert_eq!( - room.matrix_permalink(false).await.unwrap().to_string(), - "matrix:roomid/test_room:127.0.0.1" - ); - assert_eq!( - room.matrix_permalink(true).await.unwrap().to_string(), - "matrix:roomid/test_room:127.0.0.1?action=join" - ); - - // With a single elligible server - sync_index += 1; - let res = sync_response( - sync_index, - &[json!({ - "content": { - "membership": "join", - }, - "event_id": "$151800140517rfvjc", - "origin_server_ts": 151800140, - "sender": "@example:localhost", - "state_key": "@example:localhost", - "type": "m.room.member", - })], - ); - let _sync = mock("GET", Matcher::Regex(r"^/_matrix/client/r0/sync\?.*$".to_owned())) - .with_status(200) - .with_body(res.to_string()) - .match_header("authorization", "Bearer 1234") - .create(); - client.sync_once(sync_settings.clone()).await.unwrap(); - - assert_eq!( - room.matrix_to_permalink().await.unwrap().to_string(), - "https://matrix.to/#/%21test_room%3A127.0.0.1?via=localhost" - ); - assert_eq!( - room.matrix_permalink(false).await.unwrap().to_string(), - "matrix:roomid/test_room:127.0.0.1?via=localhost" - ); - - // With two elligible servers - sync_index += 1; - let res = sync_response(sync_index, &room_member_events(15, "notarealhs")); - let _sync = mock("GET", Matcher::Regex(r"^/_matrix/client/r0/sync\?.*$".to_owned())) - .with_status(200) - .with_body(res.to_string()) - .match_header("authorization", "Bearer 1234") - .create(); - client.sync_once(sync_settings.clone()).await.unwrap(); - - assert_eq!( - room.matrix_to_permalink().await.unwrap().to_string(), - "https://matrix.to/#/%21test_room%3A127.0.0.1?via=notarealhs&via=localhost" - ); - assert_eq!( - room.matrix_permalink(false).await.unwrap().to_string(), - "matrix:roomid/test_room:127.0.0.1?via=notarealhs&via=localhost" - ); - - // With three elligible servers - sync_index += 1; - let res = sync_response(sync_index, &room_member_events(5, "mymatrix")); - let _sync = mock("GET", Matcher::Regex(r"^/_matrix/client/r0/sync\?.*$".to_owned())) - .with_status(200) - .with_body(res.to_string()) - .match_header("authorization", "Bearer 1234") - .create(); - client.sync_once(sync_settings.clone()).await.unwrap(); - - assert_eq!( - room.matrix_to_permalink().await.unwrap().to_string(), - "https://matrix.to/#/%21test_room%3A127.0.0.1?via=notarealhs&via=mymatrix&via=localhost" - ); - assert_eq!( - room.matrix_permalink(false).await.unwrap().to_string(), - "matrix:roomid/test_room:127.0.0.1?via=notarealhs&via=mymatrix&via=localhost" - ); - - // With four elligible servers - sync_index += 1; - let res = sync_response(sync_index, &room_member_events(10, "yourmatrix")); - let _sync = mock("GET", Matcher::Regex(r"^/_matrix/client/r0/sync\?.*$".to_owned())) - .with_status(200) - .with_body(res.to_string()) - .match_header("authorization", "Bearer 1234") - .create(); - client.sync_once(sync_settings.clone()).await.unwrap(); - - assert_eq!( - room.matrix_to_permalink().await.unwrap().to_string(), - "https://matrix.to/#/%21test_room%3A127.0.0.1?via=notarealhs&via=yourmatrix&via=mymatrix" - ); - assert_eq!( - room.matrix_permalink(false).await.unwrap().to_string(), - "matrix:roomid/test_room:127.0.0.1?via=notarealhs&via=yourmatrix&via=mymatrix" - ); - - // With power levels - sync_index += 1; - let res = sync_response( - sync_index, - &[json!({ - "content": { - "users": { - "@example:localhost": 50, - }, - }, - "event_id": "$15139375512JaHAW", - "origin_server_ts": 151393755, - "sender": "@creator:127.0.0.1", - "state_key": "", - "type": "m.room.power_levels", - })], - ); - let _sync = mock("GET", Matcher::Regex(r"^/_matrix/client/r0/sync\?.*$".to_owned())) - .with_status(200) - .with_body(res.to_string()) - .match_header("authorization", "Bearer 1234") - .create(); - client.sync_once(sync_settings.clone()).await.unwrap(); - - assert_eq!( - room.matrix_to_permalink().await.unwrap().to_string(), - "https://matrix.to/#/%21test_room%3A127.0.0.1?via=localhost&via=notarealhs&via=yourmatrix" - ); - assert_eq!( - room.matrix_permalink(false).await.unwrap().to_string(), - "matrix:roomid/test_room:127.0.0.1?via=localhost&via=notarealhs&via=yourmatrix" - ); - - // With higher power levels - sync_index += 1; - let res = sync_response( - sync_index, - &[json!({ - "content": { - "users": { - "@example:localhost": 50, - "@user0:mymatrix": 70, - }, - }, - "event_id": "$15139375512JaHAZ", - "origin_server_ts": 151393755, - "sender": "@creator:127.0.0.1", - "state_key": "", - "type": "m.room.power_levels", - })], - ); - let _sync = mock("GET", Matcher::Regex(r"^/_matrix/client/r0/sync\?.*$".to_owned())) - .with_status(200) - .with_body(res.to_string()) - .match_header("authorization", "Bearer 1234") - .create(); - client.sync_once(sync_settings.clone()).await.unwrap(); - - assert_eq!( - room.matrix_to_permalink().await.unwrap().to_string(), - "https://matrix.to/#/%21test_room%3A127.0.0.1?via=mymatrix&via=notarealhs&via=yourmatrix" - ); - assert_eq!( - room.matrix_permalink(false).await.unwrap().to_string(), - "matrix:roomid/test_room:127.0.0.1?via=mymatrix&via=notarealhs&via=yourmatrix" - ); - - // With server ACLs - sync_index += 1; - let res = sync_response( - sync_index, - &[json!({ - "content": { - "allow": ["*"], - "allow_ip_literals": true, - "deny": ["notarealhs"], - }, - "event_id": "$143273582443PhrSn", - "origin_server_ts": 1432735824, - "sender": "@creator:127.0.0.1", - "state_key": "", - "type": "m.room.server_acl", - })], - ); - let _sync = mock("GET", Matcher::Regex(r"^/_matrix/client/r0/sync\?.*$".to_owned())) - .with_status(200) - .with_body(res.to_string()) - .match_header("authorization", "Bearer 1234") - .create(); - client.sync_once(sync_settings.clone()).await.unwrap(); - - assert_eq!( - room.matrix_to_permalink().await.unwrap().to_string(), - "https://matrix.to/#/%21test_room%3A127.0.0.1?via=mymatrix&via=yourmatrix&via=localhost" - ); - assert_eq!( - room.matrix_permalink(false).await.unwrap().to_string(), - "matrix:roomid/test_room:127.0.0.1?via=mymatrix&via=yourmatrix&via=localhost" - ); - - // With an alternative alias - sync_index += 1; - let res = sync_response( - sync_index, - &[json!({ - "content": { - "alt_aliases": ["#alias:localhost"], - }, - "event_id": "$15139375513VdeRF", - "origin_server_ts": 151393755, - "sender": "@example:localhost", - "state_key": "", - "type": "m.room.canonical_alias", - })], - ); - let _sync = mock("GET", Matcher::Regex(r"^/_matrix/client/r0/sync\?.*$".to_owned())) - .with_status(200) - .with_body(res.to_string()) - .match_header("authorization", "Bearer 1234") - .create(); - client.sync_once(sync_settings.clone()).await.unwrap(); - - assert_eq!( - room.matrix_to_permalink().await.unwrap().to_string(), - "https://matrix.to/#/%23alias%3Alocalhost" - ); - assert_eq!( - room.matrix_permalink(false).await.unwrap().to_string(), - "matrix:r/alias:localhost" - ); - - // With a canonical alias - sync_index += 1; - let res = sync_response( - sync_index, - &[json!({ - "content": { - "alias": "#canonical:localhost", - "alt_aliases": ["#alias:localhost"], - }, - "event_id": "$15139375513VdeRF", - "origin_server_ts": 151393755, - "sender": "@example:localhost", - "state_key": "", - "type": "m.room.canonical_alias", - })], - ); - let _sync = mock("GET", Matcher::Regex(r"^/_matrix/client/r0/sync\?.*$".to_owned())) - .with_status(200) - .with_body(res.to_string()) - .match_header("authorization", "Bearer 1234") - .create(); - client.sync_once(sync_settings).await.unwrap(); - - assert_eq!( - room.matrix_to_permalink().await.unwrap().to_string(), - "https://matrix.to/#/%23canonical%3Alocalhost" - ); - assert_eq!( - room.matrix_permalink(false).await.unwrap().to_string(), - "matrix:r/canonical:localhost" - ); - assert_eq!( - room.matrix_permalink(true).await.unwrap().to_string(), - "matrix:r/canonical:localhost?action=join" - ); - } } diff --git a/crates/matrix-sdk/tests/integration/client.rs b/crates/matrix-sdk/tests/integration/client.rs new file mode 100644 index 000000000..be6c27c63 --- /dev/null +++ b/crates/matrix-sdk/tests/integration/client.rs @@ -0,0 +1,642 @@ +// mockito (the http mocking library) is not supported for wasm32 +#![cfg(not(target_arch = "wasm32"))] + +use std::{collections::BTreeMap, str::FromStr, time::Duration}; + +#[cfg(feature = "__test")] +use matrix_sdk::{config::RequestConfig, Client}; +use matrix_sdk::{ + config::SyncSettings, + media::{MediaFormat, MediaRequest, MediaThumbnailSize}, + Error, HttpError, RumaApiError, +}; +use matrix_sdk_test::{async_test, test_json}; +use mockito::{mock, Matcher}; +#[cfg(feature = "__test")] +use ruma::UserId; +use ruma::{ + api::{ + client::{ + self as client_api, + account::register::{v3::Request as RegistrationRequest, RegistrationKind}, + directory::{ + get_public_rooms, + get_public_rooms_filtered::{self, v3::Request as PublicRoomsFilterRequest}, + }, + media::get_content_thumbnail::v3::Method, + session::get_login_types::v3::LoginType, + uiaa::{self, UiaaResponse}, + }, + error::{FromHttpResponseError, ServerError}, + }, + assign, device_id, + directory::Filter, + events::room::{message::ImageMessageEventContent, ImageInfo, MediaSource}, + mxc_uri, room_id, uint, user_id, +}; +use serde_json::json; +use url::Url; + +use crate::{logged_in_client, no_retry_test_client}; + +#[async_test] +async fn set_homeserver() { + let client = no_retry_test_client().await; + let homeserver = Url::from_str("http://example.com/").unwrap(); + client.set_homeserver(homeserver.clone()).await; + + assert_eq!(client.homeserver().await, homeserver); +} + +#[cfg(feature = "__test")] +#[async_test] +async fn successful_discovery() { + let server_url = mockito::server_url(); + let domain = server_url.strip_prefix("http://").unwrap(); + let alice = UserId::parse("@alice:".to_owned() + domain).unwrap(); + + let _m_well_known = mock("GET", "/.well-known/matrix/client") + .with_status(200) + .with_body(test_json::WELL_KNOWN.to_string().replace("HOMESERVER_URL", server_url.as_ref())) + .create(); + + let _m_versions = mock("GET", "/_matrix/client/versions") + .with_status(200) + .with_body(test_json::VERSIONS.to_string()) + .create(); + + let client = Client::builder() + .request_config(RequestConfig::new().disable_retry()) + .user_id(&alice) + .build() + .await + .unwrap(); + + assert_eq!(client.homeserver().await, Url::parse(server_url.as_ref()).unwrap()); +} + +#[cfg(feature = "__test")] +#[async_test] +async fn discovery_broken_server() { + let server_url = mockito::server_url(); + let domain = server_url.strip_prefix("http://").unwrap(); + let alice = UserId::parse("@alice:".to_owned() + domain).unwrap(); + + let _m = mock("GET", "/.well-known/matrix/client").with_status(404).create(); + + assert!( + Client::builder() + .request_config(RequestConfig::new().disable_retry()) + .user_id(&alice) + .build() + .await + .is_err(), + "Creating a client from a user ID should fail when the .well-known request fails." + ); +} + +#[async_test] +async fn login() { + let homeserver = Url::from_str(&mockito::server_url()).unwrap(); + let client = no_retry_test_client().await; + + let _m_types = mock("GET", "/_matrix/client/r0/login") + .with_status(200) + .with_body(test_json::LOGIN_TYPES.to_string()) + .create(); + + let can_password = client + .get_login_types() + .await + .unwrap() + .flows + .iter() + .any(|flow| matches!(flow, LoginType::Password(_))); + assert!(can_password); + + let _m_login = mock("POST", "/_matrix/client/r0/login") + .with_status(200) + .with_body(test_json::LOGIN.to_string()) + .create(); + + client.login_username("example", "wordpass").send().await.unwrap(); + + let logged_in = client.logged_in(); + assert!(logged_in, "Client should be logged in"); + + assert_eq!(client.homeserver().await, homeserver); +} + +#[async_test] +async fn login_with_discovery() { + let client = no_retry_test_client().await; + + let _m_login = mock("POST", "/_matrix/client/r0/login") + .with_status(200) + .with_body(test_json::LOGIN_WITH_DISCOVERY.to_string()) + .create(); + + client.login_username("example", "wordpass").send().await.unwrap(); + + let logged_in = client.logged_in(); + assert!(logged_in, "Client should be logged in"); + + assert_eq!(client.homeserver().await.as_str(), "https://example.org/"); +} + +#[async_test] +async fn login_no_discovery() { + let client = no_retry_test_client().await; + + let _m_login = mock("POST", "/_matrix/client/r0/login") + .with_status(200) + .with_body(test_json::LOGIN.to_string()) + .create(); + + client.login_username("example", "wordpass").send().await.unwrap(); + + let logged_in = client.logged_in(); + assert!(logged_in, "Client should be logged in"); + + assert_eq!(client.homeserver().await, Url::parse(&mockito::server_url()).unwrap()); +} + +#[async_test] +#[cfg(feature = "sso-login")] +async fn login_with_sso() { + let _m_login = mock("POST", "/_matrix/client/r0/login") + .with_status(200) + .with_body(test_json::LOGIN.to_string()) + .create(); + + let _homeserver = Url::from_str(&mockito::server_url()).unwrap(); + let client = no_retry_test_client().await; + let idp = ruma::api::client::session::get_login_types::v3::IdentityProvider::new( + "some-id".to_owned(), + "idp-name".to_owned(), + ); + client + .login_sso(|sso_url| async move { + let sso_url = Url::parse(&sso_url).unwrap(); + + let (_, redirect) = + sso_url.query_pairs().find(|(key, _)| key == "redirectUrl").unwrap(); + + let mut redirect_url = Url::parse(&redirect).unwrap(); + redirect_url.set_query(Some("loginToken=tinytoken")); + + reqwest::get(redirect_url.to_string()).await.unwrap(); + + Ok(()) + }) + .identity_provider_id(&idp.id) + .send() + .await + .unwrap(); + + let logged_in = client.logged_in(); + assert!(logged_in, "Client should be logged in"); +} + +#[async_test] +async fn login_with_sso_token() { + let client = no_retry_test_client().await; + + let _m = mock("GET", "/_matrix/client/r0/login") + .with_status(200) + .with_body(test_json::LOGIN_TYPES.to_string()) + .create(); + + let can_sso = client + .get_login_types() + .await + .unwrap() + .flows + .iter() + .any(|flow| matches!(flow, LoginType::Sso(_))); + assert!(can_sso); + + let sso_url = client.get_sso_login_url("http://127.0.0.1:3030", None).await; + assert!(sso_url.is_ok()); + + let _m = mock("POST", "/_matrix/client/r0/login") + .with_status(200) + .with_body(test_json::LOGIN.to_string()) + .create(); + + client.login_token("averysmalltoken").send().await.unwrap(); + + let logged_in = client.logged_in(); + assert!(logged_in, "Client should be logged in"); +} + +#[async_test] +async fn login_error() { + let client = no_retry_test_client().await; + + let _m = mock("POST", "/_matrix/client/r0/login") + .with_status(403) + .with_body(test_json::LOGIN_RESPONSE_ERR.to_string()) + .create(); + + if let Err(err) = client.login_username("example", "wordpass").send().await { + if let Error::Http(HttpError::Api(FromHttpResponseError::Server(ServerError::Known( + RumaApiError::ClientApi(client_api::Error { kind, message, status_code }), + )))) = err + { + if let client_api::error::ErrorKind::Forbidden = kind { + } else { + panic!("found the wrong `ErrorKind` {:?}, expected `Forbidden", kind); + } + assert_eq!(message, "Invalid password".to_owned()); + assert_eq!(status_code, http::StatusCode::from_u16(403).unwrap()); + } else { + panic!("found the wrong `Error` type {:?}, expected `Error::RumaResponse", err); + } + } else { + panic!("this request should return an `Err` variant") + } +} + +#[async_test] +async fn register_error() { + let client = no_retry_test_client().await; + + let _m = mock("POST", Matcher::Regex(r"^/_matrix/client/r0/register\?.*$".to_owned())) + .with_status(403) + .with_body(test_json::REGISTRATION_RESPONSE_ERR.to_string()) + .create(); + + let user = assign!(RegistrationRequest::new(), { + username: Some("user"), + password: Some("password"), + auth: Some(uiaa::AuthData::FallbackAcknowledgement( + uiaa::FallbackAcknowledgement::new("foobar"), + )), + kind: RegistrationKind::User, + }); + + if let Err(err) = client.register(user).await { + if let HttpError::UiaaError(FromHttpResponseError::Server(ServerError::Known( + UiaaResponse::MatrixError(client_api::Error { kind, message, status_code }), + ))) = err + { + if let client_api::error::ErrorKind::Forbidden = kind { + } else { + panic!("found the wrong `ErrorKind` {:?}, expected `Forbidden", kind); + } + assert_eq!(message, "Invalid password".to_owned()); + assert_eq!(status_code, http::StatusCode::from_u16(403).unwrap()); + } else { + panic!("found the wrong `Error` type {:#?}, expected `UiaaResponse`", err); + } + } else { + panic!("this request should return an `Err` variant") + } +} + +#[async_test] +async fn sync() { + let client = logged_in_client().await; + + let _m = mock("GET", Matcher::Regex(r"^/_matrix/client/r0/sync\?.*$".to_owned())) + .with_status(200) + .with_body(test_json::SYNC.to_string()) + .match_header("authorization", "Bearer 1234") + .create(); + + let sync_settings = SyncSettings::new().timeout(Duration::from_millis(3000)); + + let response = client.sync_once(sync_settings).await.unwrap(); + + assert_ne!(response.next_batch, ""); + + assert!(client.sync_token().await.is_some()); +} + +#[async_test] +async fn devices() { + let client = logged_in_client().await; + + let _m = mock("GET", "/_matrix/client/r0/devices") + .with_status(200) + .with_body(test_json::DEVICES.to_string()) + .create(); + + assert!(client.devices().await.is_ok()); +} + +#[async_test] +async fn delete_devices() { + let client = no_retry_test_client().await; + + let _m = mock("POST", "/_matrix/client/r0/delete_devices") + .with_status(401) + .with_body( + json!({ + "flows": [ + { + "stages": [ + "m.login.password" + ] + } + ], + "params": {}, + "session": "vBslorikviAjxzYBASOBGfPp" + }) + .to_string(), + ) + .create(); + + let _m = mock("POST", "/_matrix/client/r0/delete_devices") + .with_status(401) + // empty response + // TODO rename that response type. + .with_body(test_json::LOGOUT.to_string()) + .create(); + + let devices = &[device_id!("DEVICEID").to_owned()]; + + if let Err(e) = client.delete_devices(devices, None).await { + if let Some(info) = e.uiaa_response() { + let mut auth_parameters = BTreeMap::new(); + + let identifier = json!({ + "type": "m.id.user", + "user": "example", + }); + auth_parameters.insert("identifier".to_owned(), identifier); + auth_parameters.insert("password".to_owned(), "wordpass".into()); + + let auth_data = uiaa::AuthData::Password(assign!( + uiaa::Password::new( + uiaa::UserIdentifier::UserIdOrLocalpart("example"), + "wordpass", + ), { + session: info.session.as_deref(), + } + )); + + client.delete_devices(devices, Some(auth_data)).await.unwrap(); + } + } +} + +#[async_test] +async fn resolve_room_alias() { + let client = no_retry_test_client().await; + + let _m = mock("GET", "/_matrix/client/r0/directory/room/%23alias%3Aexample%2Eorg") + .with_status(200) + .with_body(test_json::GET_ALIAS.to_string()) + .create(); + + let alias = ruma::room_alias_id!("#alias:example.org"); + assert!(client.resolve_room_alias(alias).await.is_ok()); +} + +#[async_test] +async fn join_leave_room() { + let room_id = room_id!("!SVkFJHzfwvuaIEawgC:localhost"); + + let _m = mock("GET", Matcher::Regex(r"^/_matrix/client/r0/sync\?.*$".to_owned())) + .with_status(200) + .with_body(test_json::SYNC.to_string()) + .create(); + + let client = logged_in_client().await; + let session = client.session().unwrap().clone(); + + let room = client.get_joined_room(room_id); + assert!(room.is_none()); + + client.sync_once(SyncSettings::default()).await.unwrap(); + + let room = client.get_left_room(room_id); + assert!(room.is_none()); + + let room = client.get_joined_room(room_id); + assert!(room.is_some()); + + // test store reloads with correct room state from the state store + let joined_client = no_retry_test_client().await; + joined_client.restore_login(session).await.unwrap(); + + // joined room reloaded from state store + joined_client.sync_once(SyncSettings::default()).await.unwrap(); + let room = joined_client.get_joined_room(room_id); + assert!(room.is_some()); + + let _m = mock("GET", Matcher::Regex(r"^/_matrix/client/r0/sync\?.*$".to_owned())) + .with_status(200) + .with_body(test_json::LEAVE_SYNC_EVENT.to_string()) + .create(); + + joined_client.sync_once(SyncSettings::default()).await.unwrap(); + + let room = joined_client.get_joined_room(room_id); + assert!(room.is_none()); + + let room = joined_client.get_left_room(room_id); + assert!(room.is_some()); +} + +#[async_test] +async fn join_room_by_id() { + let client = logged_in_client().await; + + let _m = mock("POST", Matcher::Regex(r"^/_matrix/client/r0/rooms/.*/join".to_owned())) + .with_status(200) + .with_body(test_json::ROOM_ID.to_string()) + .match_header("authorization", "Bearer 1234") + .create(); + + let room_id = room_id!("!testroom:example.org"); + + assert_eq!( + // this is the `join_by_room_id::Response` but since no PartialEq we check the RoomId + // field + client.join_room_by_id(room_id).await.unwrap().room_id, + room_id + ); +} + +#[async_test] +async fn join_room_by_id_or_alias() { + let client = logged_in_client().await; + + let _m = mock("POST", Matcher::Regex(r"^/_matrix/client/r0/join/".to_owned())) + .with_status(200) + .with_body(test_json::ROOM_ID.to_string()) + .match_header("authorization", "Bearer 1234") + .create(); + + let room_id = room_id!("!testroom:example.org").into(); + + assert_eq!( + // this is the `join_by_room_id::Response` but since no PartialEq we check the RoomId + // field + client + .join_room_by_id_or_alias(room_id, &["server.com".try_into().unwrap()]) + .await + .unwrap() + .room_id, + room_id!("!testroom:example.org") + ); +} + +#[async_test] +async fn room_search_all() { + let client = no_retry_test_client().await; + + let _m = mock("GET", Matcher::Regex(r"^/_matrix/client/r0/publicRooms".to_owned())) + .with_status(200) + .with_body(test_json::PUBLIC_ROOMS.to_string()) + .create(); + + let get_public_rooms::v3::Response { chunk, .. } = + client.public_rooms(Some(10), None, None).await.unwrap(); + assert_eq!(chunk.len(), 1); +} + +#[async_test] +async fn room_search_filtered() { + let client = logged_in_client().await; + + let _m = mock("POST", Matcher::Regex(r"^/_matrix/client/r0/publicRooms".to_owned())) + .with_status(200) + .with_body(test_json::PUBLIC_ROOMS.to_string()) + .match_header("authorization", "Bearer 1234") + .create(); + + let generic_search_term = Some("cheese"); + let filter = assign!(Filter::new(), { generic_search_term }); + let request = assign!(PublicRoomsFilterRequest::new(), { filter }); + + let get_public_rooms_filtered::v3::Response { chunk, .. } = + client.public_rooms_filtered(request).await.unwrap(); + assert_eq!(chunk.len(), 1); +} + +#[async_test] +async fn invited_rooms() { + let client = logged_in_client().await; + + let _m = mock("GET", Matcher::Regex(r"^/_matrix/client/r0/sync\?.*$".to_owned())) + .with_status(200) + .match_header("authorization", "Bearer 1234") + .with_body(test_json::INVITE_SYNC.to_string()) + .create(); + + let _response = client.sync_once(SyncSettings::default()).await.unwrap(); + + assert!(client.joined_rooms().is_empty()); + assert!(client.left_rooms().is_empty()); + assert!(!client.invited_rooms().is_empty()); + + assert!(client.get_invited_room(room_id!("!696r7674:example.com")).is_some()); +} + +#[async_test] +async fn left_rooms() { + let client = logged_in_client().await; + + let _m = mock("GET", Matcher::Regex(r"^/_matrix/client/r0/sync\?.*$".to_owned())) + .with_status(200) + .match_header("authorization", "Bearer 1234") + .with_body(test_json::LEAVE_SYNC.to_string()) + .create(); + + let _response = client.sync_once(SyncSettings::default()).await.unwrap(); + + assert!(client.joined_rooms().is_empty()); + assert!(!client.left_rooms().is_empty()); + assert!(client.invited_rooms().is_empty()); + + assert!(client.get_left_room(room_id!("!SVkFJHzfwvuaIEawgC:localhost")).is_some()) +} + +#[async_test] +async fn get_media_content() { + let client = logged_in_client().await; + + let request = MediaRequest { + source: MediaSource::Plain(mxc_uri!("mxc://localhost/textfile").to_owned()), + format: MediaFormat::File, + }; + + let m = mock( + "GET", + Matcher::Regex(r"^/_matrix/media/r0/download/localhost/textfile\?.*$".to_owned()), + ) + .with_status(200) + .with_body("Some very interesting text.") + .expect(2) + .create(); + + assert!(client.get_media_content(&request, true).await.is_ok()); + assert!(client.get_media_content(&request, true).await.is_ok()); + assert!(client.get_media_content(&request, false).await.is_ok()); + m.assert(); +} + +#[async_test] +async fn get_media_file() { + let client = logged_in_client().await; + + let event_content = ImageMessageEventContent::plain( + "filename.jpg".into(), + mxc_uri!("mxc://example.org/image").to_owned(), + Some(Box::new(assign!(ImageInfo::new(), { + height: Some(uint!(398)), + width: Some(uint!(394)), + mimetype: Some("image/jpeg".into()), + size: Some(uint!(31037)), + }))), + ); + + let m = mock( + "GET", + Matcher::Regex(r"^/_matrix/media/r0/download/example%2Eorg/image\?.*$".to_owned()), + ) + .with_status(200) + .with_body("binaryjpegdata") + .create(); + + assert!(client.get_file(event_content.clone(), true).await.is_ok()); + assert!(client.get_file(event_content.clone(), true).await.is_ok()); + m.assert(); + + let m = mock( + "GET", + Matcher::Regex(r"^/_matrix/media/r0/thumbnail/example%2Eorg/image\?.*$".to_owned()), + ) + .with_status(200) + .with_body("smallerbinaryjpegdata") + .create(); + + assert!(client + .get_thumbnail( + event_content, + MediaThumbnailSize { method: Method::Scale, width: uint!(100), height: uint!(100) }, + true + ) + .await + .is_ok()); + m.assert(); +} + +#[async_test] +async fn whoami() { + let client = logged_in_client().await; + + let _m = mock("GET", "/_matrix/client/r0/account/whoami") + .with_status(200) + .with_body(test_json::WHOAMI.to_string()) + .match_header("authorization", "Bearer 1234") + .create(); + + let user_id = user_id!("@joe:example.org"); + + assert_eq!(client.whoami().await.unwrap().user_id, user_id); +} diff --git a/crates/matrix-sdk/tests/integration/main.rs b/crates/matrix-sdk/tests/integration/main.rs new file mode 100644 index 000000000..a2c8ad143 --- /dev/null +++ b/crates/matrix-sdk/tests/integration/main.rs @@ -0,0 +1,31 @@ +use matrix_sdk::{config::RequestConfig, Client, ClientBuilder, Session}; +use ruma::{api::MatrixVersion, device_id, user_id}; +use url::Url; + +mod client; +mod room; + +fn test_client_builder() -> ClientBuilder { + let homeserver = Url::parse(&mockito::server_url()).unwrap(); + Client::builder().homeserver_url(homeserver).server_versions([MatrixVersion::V1_0]) +} + +async fn no_retry_test_client() -> Client { + test_client_builder() + .request_config(RequestConfig::new().disable_retry()) + .build() + .await + .unwrap() +} + +async fn logged_in_client() -> Client { + let session = Session { + access_token: "1234".to_owned(), + user_id: user_id!("@example:localhost").to_owned(), + device_id: device_id!("DEVICEID").to_owned(), + }; + let client = no_retry_test_client().await; + client.restore_login(session).await.unwrap(); + + client +} diff --git a/crates/matrix-sdk/tests/integration/room/common.rs b/crates/matrix-sdk/tests/integration/room/common.rs new file mode 100644 index 000000000..2edfb9d5b --- /dev/null +++ b/crates/matrix-sdk/tests/integration/room/common.rs @@ -0,0 +1,813 @@ +use std::time::Duration; + +use matrix_sdk::{ + config::{RequestConfig, SyncSettings}, + DisplayName, RoomMember, Session, +}; +use matrix_sdk_test::{async_test, test_json}; +use mockito::{mock, Matcher}; +use ruma::{ + device_id, + events::{AnySyncStateEvent, StateEventType}, + room_id, user_id, +}; +use serde_json::{json, Value as JsonValue}; + +use crate::{logged_in_client, test_client_builder}; + +#[async_test] +async fn user_presence() { + let client = logged_in_client().await; + + let _m = mock("GET", Matcher::Regex(r"^/_matrix/client/r0/sync\?.*$".to_owned())) + .with_status(200) + .match_header("authorization", "Bearer 1234") + .with_body(test_json::SYNC.to_string()) + .create(); + + let _m = mock("GET", Matcher::Regex(r"^/_matrix/client/r0/rooms/.*/members".to_owned())) + .with_status(200) + .match_header("authorization", "Bearer 1234") + .with_body(test_json::MEMBERS.to_string()) + .create(); + + let sync_settings = SyncSettings::new().timeout(Duration::from_millis(3000)); + + let _response = client.sync_once(sync_settings).await.unwrap(); + + let room = client.get_joined_room(room_id!("!SVkFJHzfwvuaIEawgC:localhost")).unwrap(); + let members: Vec = room.active_members().await.unwrap(); + + assert_eq!(2, members.len()); + // assert!(room.power_levels.is_some()) +} + +#[async_test] +async fn calculate_room_names_from_summary() { + let client = logged_in_client().await; + + let _m = mock("GET", Matcher::Regex(r"^/_matrix/client/r0/sync\?.*$".to_owned())) + .with_status(200) + .match_header("authorization", "Bearer 1234") + .with_body(test_json::DEFAULT_SYNC_SUMMARY.to_string()) + .create(); + + let sync_settings = SyncSettings::new().timeout(Duration::from_millis(3000)); + let _response = client.sync_once(sync_settings).await.unwrap(); + let room = client.get_joined_room(room_id!("!SVkFJHzfwvuaIEawgC:localhost")).unwrap(); + + assert_eq!(DisplayName::Calculated("example2".to_owned()), room.display_name().await.unwrap()); +} + +#[async_test] +async fn room_names() { + let client = logged_in_client().await; + + let _m = mock("GET", Matcher::Regex(r"^/_matrix/client/r0/sync\?.*$".to_owned())) + .with_status(200) + .match_header("authorization", "Bearer 1234") + .with_body(test_json::SYNC.to_string()) + .expect_at_least(1) + .create(); + + let sync_settings = SyncSettings::new().timeout(Duration::from_millis(3000)); + + let _response = client.sync_once(sync_settings).await.unwrap(); + + assert_eq!(client.rooms().len(), 1); + let room = client.get_joined_room(room_id!("!SVkFJHzfwvuaIEawgC:localhost")).unwrap(); + + assert_eq!(DisplayName::Aliased("tutorial".to_owned()), room.display_name().await.unwrap()); + + let _m = mock("GET", Matcher::Regex(r"^/_matrix/client/r0/sync\?.*$".to_owned())) + .with_status(200) + .match_header("authorization", "Bearer 1234") + .with_body(test_json::INVITE_SYNC.to_string()) + .expect_at_least(1) + .create(); + + let _response = client.sync_once(SyncSettings::new()).await.unwrap(); + + assert_eq!(client.rooms().len(), 1); + let invited_room = client.get_invited_room(room_id!("!696r7674:example.com")).unwrap(); + + assert_eq!( + DisplayName::Named("My Room Name".to_owned()), + invited_room.display_name().await.unwrap() + ); +} + +#[async_test] +async fn test_state_event_getting() { + let room_id = room_id!("!SVkFJHzfwvuaIEawgC:localhost"); + + let session = Session { + access_token: "1234".to_owned(), + user_id: user_id!("@example:localhost").to_owned(), + device_id: device_id!("DEVICEID").to_owned(), + }; + + let sync = json!({ + "next_batch": "1234", + "rooms": { + "join": { + "!SVkFJHzfwvuaIEawgC:localhost": { + "state": { + "events": [ + { + "type": "m.custom.note", + "sender": "@example:localhost", + "content": { + "body": "Note 1", + }, + "state_key": "note.1", + "origin_server_ts": 1611853078727u64, + "unsigned": { + "replaces_state": "$2s9GcbVxbbFS3EZY9vN1zhavaDJnF32cAIGAxi99NuQ", + "age": 15458166523u64 + }, + "event_id": "$NVCTvrlxodf3ZGjJ6foxepEq8ysSkTq8wG0wKeQBVZg" + }, + { + "type": "m.custom.note", + "sender": "@example2:localhost", + "content": { + "body": "Note 2", + }, + "state_key": "note.2", + "origin_server_ts": 1611853078727u64, + "unsigned": { + "replaces_state": "$2s9GcbVxbbFS3EZY9vN1zhavaDJnF32cAIGAxi99NuQ", + "age": 15458166523u64 + }, + "event_id": "$NVCTvrlxodf3ZGjJ6foxepEq8ysSkTq8wG0wKeQBVZg" + }, + { + "type": "m.room.encryption", + "sender": "@example:localhost", + "content": { + "algorithm": "m.megolm.v1.aes-sha2" + }, + "state_key": "", + "origin_server_ts": 1586437448151u64, + "unsigned": { + "age": 40873797099u64 + }, + "event_id": "$vyG3wu1QdJSh5gc-09SwjXBXlXo8gS7s4QV_Yxha0Xw" + }, + ] + } + } + } + } + }); + + let _m = mock("GET", Matcher::Regex(r"^/_matrix/client/r0/sync\?.*$".to_owned())) + .with_status(200) + .with_body(sync.to_string()) + .create(); + + let client = test_client_builder() + .request_config(RequestConfig::new().retry_limit(3)) + .build() + .await + .unwrap(); + client.restore_login(session.clone()).await.unwrap(); + + let room = client.get_joined_room(room_id); + assert!(room.is_none()); + + client.sync_once(SyncSettings::default()).await.unwrap(); + + let room = client.get_joined_room(room_id).unwrap(); + + let state_events = room.get_state_events(StateEventType::RoomEncryption).await.unwrap(); + assert_eq!(state_events.len(), 1); + + let state_events = room.get_state_events("m.custom.note".into()).await.unwrap(); + assert_eq!(state_events.len(), 2); + + let encryption_event = room + .get_state_event(StateEventType::RoomEncryption, "") + .await + .unwrap() + .unwrap() + .deserialize() + .unwrap(); + + matches::assert_matches!(encryption_event, AnySyncStateEvent::RoomEncryption(_)); +} + +// FIXME: removing timelines during reading the stream currently leaves to an +// inconsistent undefined state. This tests shows that, but because +// different implementations deal with problem in different, +// inconsistent manners, isn't activated. +//#[async_test] +#[allow(dead_code)] +#[cfg(feature = "experimental-timeline")] +async fn room_timeline_with_remove() { + let client = logged_in_client().await; + let sync_settings = SyncSettings::new().timeout(Duration::from_millis(3000)); + + let sync = mock("GET", Matcher::Regex(r"^/_matrix/client/r0/sync\?.*$".to_owned())) + .with_status(200) + .with_body(test_json::SYNC.to_string()) + .match_header("authorization", "Bearer 1234") + .create(); + + let _ = client.sync_once(sync_settings).await.unwrap(); + sync.assert(); + drop(sync); + let room = client.get_joined_room(room_id!("!SVkFJHzfwvuaIEawgC:localhost")).unwrap(); + let (forward_stream, backward_stream) = room.timeline().await.unwrap(); + + // these two syncs lead to the store removing its existing timeline + // and replace them with new ones + let sync_2 = mock( + "GET", + Matcher::Regex( + r"^/_matrix/client/r0/sync\?.*since=s526_47314_0_7_1_1_1_11444_1.*".to_owned(), + ), + ) + .with_status(200) + .with_body(test_json::MORE_SYNC.to_string()) + .match_header("authorization", "Bearer 1234") + .create(); + + let sync_3 = mock( + "GET", + Matcher::Regex( + r"^/_matrix/client/r0/sync\?.*since=s526_47314_0_7_1_1_1_11444_2.*".to_owned(), + ), + ) + .with_status(200) + .with_body(test_json::MORE_SYNC_2.to_string()) + .match_header("authorization", "Bearer 1234") + .create(); + + let mocked_messages = mock( + "GET", + Matcher::Regex( + r"^/_matrix/client/r0/rooms/.*/messages.*from=t392-516_47314_0_7_1_1_1_11444_1.*" + .to_owned(), + ), + ) + .with_status(200) + .with_body(test_json::SYNC_ROOM_MESSAGES_BATCH_1.to_string()) + .match_header("authorization", "Bearer 1234") + .create(); + + let mocked_messages_2 = mock( + "GET", + Matcher::Regex( + r"^/_matrix/client/r0/rooms/.*/messages.*from=t47409-4357353_219380_26003_2269.*" + .to_owned(), + ), + ) + .with_status(200) + .with_body(test_json::SYNC_ROOM_MESSAGES_BATCH_2.to_string()) + .match_header("authorization", "Bearer 1234") + .create(); + + assert_eq!(client.sync_token().await, Some("s526_47314_0_7_1_1_1_11444_1".to_owned())); + let sync_settings = SyncSettings::new() + .timeout(Duration::from_millis(3000)) + .token("s526_47314_0_7_1_1_1_11444_1"); + let _ = client.sync_once(sync_settings).await.unwrap(); + sync_2.assert(); + let sync_settings = SyncSettings::new() + .timeout(Duration::from_millis(3000)) + .token("s526_47314_0_7_1_1_1_11444_2"); + let _ = client.sync_once(sync_settings).await.unwrap(); + sync_3.assert(); + + let expected_forward_events = vec![ + "$152037280074GZeOm:localhost", + "$editevid:localhost", + "$151957878228ssqrJ:localhost", + "$15275046980maRLj:localhost", + "$15275047031IXQRi:localhost", + "$098237280074GZeOm:localhost", + "$152037280074GZeOm2:localhost", + "$editevid2:localhost", + "$151957878228ssqrJ2:localhost", + "$15275046980maRLj2:localhost", + "$15275047031IXQRi2:localhost", + "$098237280074GZeOm2:localhost", + ]; + + use futures_util::StreamExt; + use matrix_sdk::deserialized_responses::SyncRoomEvent; + let forward_events = + forward_stream.take(expected_forward_events.len()).collect::>().await; + + for (r, e) in forward_events.into_iter().zip(expected_forward_events.iter()) { + assert_eq!(&r.event_id().unwrap().as_str(), e); + } + + let expected_backwards_events = vec![ + "$152037280074GZeOm:localhost", + "$1444812213350496Caaaf:example.com", + "$1444812213350496Cbbbf:example.com", + "$1444812213350496Ccccf:example.com", + "$1444812213350496Caaak:example.com", + "$1444812213350496Cbbbk:example.com", + "$1444812213350496Cccck:example.com", + ]; + + let backward_events = backward_stream + .take(expected_backwards_events.len()) + .collect::>>() + .await; + + for (r, e) in backward_events.into_iter().zip(expected_backwards_events.iter()) { + assert_eq!(&r.unwrap().event_id().unwrap().as_str(), e); + } + + mocked_messages.assert(); + mocked_messages_2.assert(); +} + +#[async_test] +#[cfg(feature = "experimental-timeline")] +async fn room_timeline() { + let client = logged_in_client().await; + let sync_settings = SyncSettings::new().timeout(Duration::from_millis(3000)); + + let sync = mock("GET", Matcher::Regex(r"^/_matrix/client/r0/sync\?.*$".to_owned())) + .with_status(200) + .with_body(test_json::MORE_SYNC.to_string()) + .match_header("authorization", "Bearer 1234") + .create(); + + let _ = client.sync_once(sync_settings).await.unwrap(); + sync.assert(); + drop(sync); + let room = client.get_joined_room(room_id!("!SVkFJHzfwvuaIEawgC:localhost")).unwrap(); + let (forward_stream, backward_stream) = room.timeline().await.unwrap(); + + let sync_2 = mock( + "GET", + Matcher::Regex( + r"^/_matrix/client/r0/sync\?.*since=s526_47314_0_7_1_1_1_11444_2.*".to_owned(), + ), + ) + .with_status(200) + .with_body(test_json::MORE_SYNC_2.to_string()) + .match_header("authorization", "Bearer 1234") + .create(); + + let mocked_messages = mock( + "GET", + Matcher::Regex( + r"^/_matrix/client/r0/rooms/.*/messages.*from=t392-516_47314_0_7_1_1_1_11444_1.*" + .to_owned(), + ), + ) + .with_status(200) + .with_body(test_json::SYNC_ROOM_MESSAGES_BATCH_1.to_string()) + .match_header("authorization", "Bearer 1234") + .create(); + + let mocked_messages_2 = mock( + "GET", + Matcher::Regex( + r"^/_matrix/client/r0/rooms/.*/messages.*from=t47409-4357353_219380_26003_2269.*" + .to_owned(), + ), + ) + .with_status(200) + .with_body(test_json::SYNC_ROOM_MESSAGES_BATCH_2.to_string()) + .match_header("authorization", "Bearer 1234") + .create(); + + assert_eq!(client.sync_token().await, Some("s526_47314_0_7_1_1_1_11444_2".to_owned())); + let sync_settings = SyncSettings::new() + .timeout(Duration::from_millis(3000)) + .token("s526_47314_0_7_1_1_1_11444_2"); + let _ = client.sync_once(sync_settings).await.unwrap(); + sync_2.assert(); + + let expected_forward_events = vec![ + "$152037280074GZeOm2:localhost", + "$editevid2:localhost", + "$151957878228ssqrJ2:localhost", + "$15275046980maRLj2:localhost", + "$15275047031IXQRi2:localhost", + "$098237280074GZeOm2:localhost", + ]; + + use futures_util::StreamExt; + use matrix_sdk::deserialized_responses::SyncRoomEvent; + let forward_events = + forward_stream.take(expected_forward_events.len()).collect::>().await; + + for (r, e) in forward_events.into_iter().zip(expected_forward_events.iter()) { + assert_eq!(&r.event_id().unwrap().as_str(), e); + } + + let expected_backwards_events = vec![ + "$098237280074GZeOm:localhost", + "$15275047031IXQRi:localhost", + "$15275046980maRLj:localhost", + "$151957878228ssqrJ:localhost", + "$editevid:localhost", + "$152037280074GZeOm:localhost", + // ^^^ These come from the first sync before we asked for the timeline and thus + // where cached + // + // While the following are fetched over the network transparently to us after, + // when scrolling back in time: + "$1444812213350496Caaaf:example.com", + "$1444812213350496Cbbbf:example.com", + "$1444812213350496Ccccf:example.com", + "$1444812213350496Caaak:example.com", + "$1444812213350496Cbbbk:example.com", + "$1444812213350496Cccck:example.com", + ]; + + let backward_events = backward_stream + .take(expected_backwards_events.len()) + .collect::>>() + .await; + + for (r, e) in backward_events.into_iter().zip(expected_backwards_events.iter()) { + assert_eq!(&r.unwrap().event_id().unwrap().as_str(), e); + } + + mocked_messages.assert(); + mocked_messages_2.assert(); +} + +#[async_test] +async fn room_permalink() { + fn sync_response(index: u8, room_timeline_events: &[JsonValue]) -> JsonValue { + json!({ + "device_one_time_keys_count": {}, + "next_batch": format!("s526_47314_0_7_1_1_1_11444_{}", index + 1), + "device_lists": { + "changed": [], + "left": [] + }, + "account_data": { + "events": [] + }, + "rooms": { + "invite": {}, + "join": { + "!test_room:127.0.0.1": { + "summary": {}, + "account_data": { + "events": [] + }, + "ephemeral": { + "events": [] + }, + "state": { + "events": [] + }, + "timeline": { + "events": room_timeline_events, + "limited": false, + "prev_batch": format!("s526_47314_0_7_1_1_1_11444_{}", index - 1), + }, + "unread_notifications": { + "highlight_count": 0, + "notification_count": 0, + } + } + }, + "leave": {} + }, + "to_device": { + "events": [] + }, + "presence": { + "events": [] + } + }) + } + + fn room_member_events(nb: usize, server: &str) -> Vec { + let mut events = Vec::with_capacity(nb); + for i in 0..nb { + let id = format!("${server}{i}"); + let user = format!("@user{i}:{server}"); + events.push(json!({ + "content": { + "membership": "join", + }, + "event_id": id, + "origin_server_ts": 151800140, + "sender": user, + "state_key": user, + "type": "m.room.member", + })) + } + events + } + + let client = logged_in_client().await; + let sync_settings = SyncSettings::new(); + + // Without elligible server + let mut sync_index = 1; + let res = sync_response( + sync_index, + &[ + json!({ + "content": { + "creator": "@creator:127.0.0.1", + "room_version": "6", + }, + "event_id": "$151957878228ekrDs", + "origin_server_ts": 15195787, + "sender": "@creator:localhost", + "state_key": "", + "type": "m.room.create", + }), + json!({ + "content": { + "membership": "join", + }, + "event_id": "$151800140517rfvjc", + "origin_server_ts": 151800140, + "sender": "@creator:127.0.0.1", + "state_key": "@creator:127.0.0.1", + "type": "m.room.member", + }), + ], + ); + let _sync = mock("GET", Matcher::Regex(r"^/_matrix/client/r0/sync\?.*$".to_owned())) + .with_status(200) + .with_body(res.to_string()) + .match_header("authorization", "Bearer 1234") + .create(); + client.sync_once(sync_settings.clone()).await.unwrap(); + let room = client.get_room(room_id!("!test_room:127.0.0.1")).unwrap(); + + assert_eq!( + room.matrix_to_permalink().await.unwrap().to_string(), + "https://matrix.to/#/%21test_room%3A127.0.0.1" + ); + assert_eq!( + room.matrix_permalink(false).await.unwrap().to_string(), + "matrix:roomid/test_room:127.0.0.1" + ); + assert_eq!( + room.matrix_permalink(true).await.unwrap().to_string(), + "matrix:roomid/test_room:127.0.0.1?action=join" + ); + + // With a single elligible server + sync_index += 1; + let res = sync_response( + sync_index, + &[json!({ + "content": { + "membership": "join", + }, + "event_id": "$151800140517rfvjc", + "origin_server_ts": 151800140, + "sender": "@example:localhost", + "state_key": "@example:localhost", + "type": "m.room.member", + })], + ); + let _sync = mock("GET", Matcher::Regex(r"^/_matrix/client/r0/sync\?.*$".to_owned())) + .with_status(200) + .with_body(res.to_string()) + .match_header("authorization", "Bearer 1234") + .create(); + client.sync_once(sync_settings.clone()).await.unwrap(); + + assert_eq!( + room.matrix_to_permalink().await.unwrap().to_string(), + "https://matrix.to/#/%21test_room%3A127.0.0.1?via=localhost" + ); + assert_eq!( + room.matrix_permalink(false).await.unwrap().to_string(), + "matrix:roomid/test_room:127.0.0.1?via=localhost" + ); + + // With two elligible servers + sync_index += 1; + let res = sync_response(sync_index, &room_member_events(15, "notarealhs")); + let _sync = mock("GET", Matcher::Regex(r"^/_matrix/client/r0/sync\?.*$".to_owned())) + .with_status(200) + .with_body(res.to_string()) + .match_header("authorization", "Bearer 1234") + .create(); + client.sync_once(sync_settings.clone()).await.unwrap(); + + assert_eq!( + room.matrix_to_permalink().await.unwrap().to_string(), + "https://matrix.to/#/%21test_room%3A127.0.0.1?via=notarealhs&via=localhost" + ); + assert_eq!( + room.matrix_permalink(false).await.unwrap().to_string(), + "matrix:roomid/test_room:127.0.0.1?via=notarealhs&via=localhost" + ); + + // With three elligible servers + sync_index += 1; + let res = sync_response(sync_index, &room_member_events(5, "mymatrix")); + let _sync = mock("GET", Matcher::Regex(r"^/_matrix/client/r0/sync\?.*$".to_owned())) + .with_status(200) + .with_body(res.to_string()) + .match_header("authorization", "Bearer 1234") + .create(); + client.sync_once(sync_settings.clone()).await.unwrap(); + + assert_eq!( + room.matrix_to_permalink().await.unwrap().to_string(), + "https://matrix.to/#/%21test_room%3A127.0.0.1?via=notarealhs&via=mymatrix&via=localhost" + ); + assert_eq!( + room.matrix_permalink(false).await.unwrap().to_string(), + "matrix:roomid/test_room:127.0.0.1?via=notarealhs&via=mymatrix&via=localhost" + ); + + // With four elligible servers + sync_index += 1; + let res = sync_response(sync_index, &room_member_events(10, "yourmatrix")); + let _sync = mock("GET", Matcher::Regex(r"^/_matrix/client/r0/sync\?.*$".to_owned())) + .with_status(200) + .with_body(res.to_string()) + .match_header("authorization", "Bearer 1234") + .create(); + client.sync_once(sync_settings.clone()).await.unwrap(); + + assert_eq!( + room.matrix_to_permalink().await.unwrap().to_string(), + "https://matrix.to/#/%21test_room%3A127.0.0.1?via=notarealhs&via=yourmatrix&via=mymatrix" + ); + assert_eq!( + room.matrix_permalink(false).await.unwrap().to_string(), + "matrix:roomid/test_room:127.0.0.1?via=notarealhs&via=yourmatrix&via=mymatrix" + ); + + // With power levels + sync_index += 1; + let res = sync_response( + sync_index, + &[json!({ + "content": { + "users": { + "@example:localhost": 50, + }, + }, + "event_id": "$15139375512JaHAW", + "origin_server_ts": 151393755, + "sender": "@creator:127.0.0.1", + "state_key": "", + "type": "m.room.power_levels", + })], + ); + let _sync = mock("GET", Matcher::Regex(r"^/_matrix/client/r0/sync\?.*$".to_owned())) + .with_status(200) + .with_body(res.to_string()) + .match_header("authorization", "Bearer 1234") + .create(); + client.sync_once(sync_settings.clone()).await.unwrap(); + + assert_eq!( + room.matrix_to_permalink().await.unwrap().to_string(), + "https://matrix.to/#/%21test_room%3A127.0.0.1?via=localhost&via=notarealhs&via=yourmatrix" + ); + assert_eq!( + room.matrix_permalink(false).await.unwrap().to_string(), + "matrix:roomid/test_room:127.0.0.1?via=localhost&via=notarealhs&via=yourmatrix" + ); + + // With higher power levels + sync_index += 1; + let res = sync_response( + sync_index, + &[json!({ + "content": { + "users": { + "@example:localhost": 50, + "@user0:mymatrix": 70, + }, + }, + "event_id": "$15139375512JaHAZ", + "origin_server_ts": 151393755, + "sender": "@creator:127.0.0.1", + "state_key": "", + "type": "m.room.power_levels", + })], + ); + let _sync = mock("GET", Matcher::Regex(r"^/_matrix/client/r0/sync\?.*$".to_owned())) + .with_status(200) + .with_body(res.to_string()) + .match_header("authorization", "Bearer 1234") + .create(); + client.sync_once(sync_settings.clone()).await.unwrap(); + + assert_eq!( + room.matrix_to_permalink().await.unwrap().to_string(), + "https://matrix.to/#/%21test_room%3A127.0.0.1?via=mymatrix&via=notarealhs&via=yourmatrix" + ); + assert_eq!( + room.matrix_permalink(false).await.unwrap().to_string(), + "matrix:roomid/test_room:127.0.0.1?via=mymatrix&via=notarealhs&via=yourmatrix" + ); + + // With server ACLs + sync_index += 1; + let res = sync_response( + sync_index, + &[json!({ + "content": { + "allow": ["*"], + "allow_ip_literals": true, + "deny": ["notarealhs"], + }, + "event_id": "$143273582443PhrSn", + "origin_server_ts": 1432735824, + "sender": "@creator:127.0.0.1", + "state_key": "", + "type": "m.room.server_acl", + })], + ); + let _sync = mock("GET", Matcher::Regex(r"^/_matrix/client/r0/sync\?.*$".to_owned())) + .with_status(200) + .with_body(res.to_string()) + .match_header("authorization", "Bearer 1234") + .create(); + client.sync_once(sync_settings.clone()).await.unwrap(); + + assert_eq!( + room.matrix_to_permalink().await.unwrap().to_string(), + "https://matrix.to/#/%21test_room%3A127.0.0.1?via=mymatrix&via=yourmatrix&via=localhost" + ); + assert_eq!( + room.matrix_permalink(false).await.unwrap().to_string(), + "matrix:roomid/test_room:127.0.0.1?via=mymatrix&via=yourmatrix&via=localhost" + ); + + // With an alternative alias + sync_index += 1; + let res = sync_response( + sync_index, + &[json!({ + "content": { + "alt_aliases": ["#alias:localhost"], + }, + "event_id": "$15139375513VdeRF", + "origin_server_ts": 151393755, + "sender": "@example:localhost", + "state_key": "", + "type": "m.room.canonical_alias", + })], + ); + let _sync = mock("GET", Matcher::Regex(r"^/_matrix/client/r0/sync\?.*$".to_owned())) + .with_status(200) + .with_body(res.to_string()) + .match_header("authorization", "Bearer 1234") + .create(); + client.sync_once(sync_settings.clone()).await.unwrap(); + + assert_eq!( + room.matrix_to_permalink().await.unwrap().to_string(), + "https://matrix.to/#/%23alias%3Alocalhost" + ); + assert_eq!(room.matrix_permalink(false).await.unwrap().to_string(), "matrix:r/alias:localhost"); + + // With a canonical alias + sync_index += 1; + let res = sync_response( + sync_index, + &[json!({ + "content": { + "alias": "#canonical:localhost", + "alt_aliases": ["#alias:localhost"], + }, + "event_id": "$15139375513VdeRF", + "origin_server_ts": 151393755, + "sender": "@example:localhost", + "state_key": "", + "type": "m.room.canonical_alias", + })], + ); + let _sync = mock("GET", Matcher::Regex(r"^/_matrix/client/r0/sync\?.*$".to_owned())) + .with_status(200) + .with_body(res.to_string()) + .match_header("authorization", "Bearer 1234") + .create(); + client.sync_once(sync_settings).await.unwrap(); + + assert_eq!( + room.matrix_to_permalink().await.unwrap().to_string(), + "https://matrix.to/#/%23canonical%3Alocalhost" + ); + assert_eq!( + room.matrix_permalink(false).await.unwrap().to_string(), + "matrix:r/canonical:localhost" + ); + assert_eq!( + room.matrix_permalink(true).await.unwrap().to_string(), + "matrix:r/canonical:localhost?action=join" + ); +} diff --git a/crates/matrix-sdk/tests/integration/room/joined.rs b/crates/matrix-sdk/tests/integration/room/joined.rs new file mode 100644 index 000000000..63c2431ea --- /dev/null +++ b/crates/matrix-sdk/tests/integration/room/joined.rs @@ -0,0 +1,569 @@ +use std::{io::Cursor, time::Duration}; + +use matrix_sdk::{ + attachment::{ + AttachmentConfig, AttachmentInfo, BaseImageInfo, BaseThumbnailInfo, BaseVideoInfo, + Thumbnail, + }, + config::SyncSettings, +}; +use matrix_sdk_test::{async_test, test_json}; +use mockito::{mock, Matcher}; +use ruma::{ + api::client::membership::Invite3pidInit, assign, event_id, + events::room::message::RoomMessageEventContent, mxc_uri, room_id, thirdparty, uint, user_id, + TransactionId, +}; +use serde_json::json; + +use crate::logged_in_client; + +#[async_test] +async fn invite_user_by_id() { + let client = logged_in_client().await; + + let _m = mock("POST", Matcher::Regex(r"^/_matrix/client/r0/rooms/.*/invite".to_owned())) + .with_status(200) + .with_body(test_json::LOGOUT.to_string()) + .match_header("authorization", "Bearer 1234") + .create(); + + let _m = mock("GET", Matcher::Regex(r"^/_matrix/client/r0/sync\?.*$".to_owned())) + .with_status(200) + .match_header("authorization", "Bearer 1234") + .with_body(test_json::SYNC.to_string()) + .create(); + + let sync_settings = SyncSettings::new().timeout(Duration::from_millis(3000)); + + let _response = client.sync_once(sync_settings).await.unwrap(); + + let user = user_id!("@example:localhost"); + let room = client.get_joined_room(room_id!("!SVkFJHzfwvuaIEawgC:localhost")).unwrap(); + + room.invite_user_by_id(user).await.unwrap(); +} + +#[async_test] +async fn invite_user_by_3pid() { + let client = logged_in_client().await; + + let _m = mock("POST", Matcher::Regex(r"^/_matrix/client/r0/rooms/.*/invite".to_owned())) + .with_status(200) + // empty JSON object + .with_body(test_json::LOGOUT.to_string()) + .match_header("authorization", "Bearer 1234") + .create(); + + let _m = mock("GET", Matcher::Regex(r"^/_matrix/client/r0/sync\?.*$".to_owned())) + .with_status(200) + .match_header("authorization", "Bearer 1234") + .with_body(test_json::SYNC.to_string()) + .create(); + + let sync_settings = SyncSettings::new().timeout(Duration::from_millis(3000)); + + let _response = client.sync_once(sync_settings).await.unwrap(); + + let room = client.get_joined_room(room_id!("!SVkFJHzfwvuaIEawgC:localhost")).unwrap(); + + room.invite_user_by_3pid( + Invite3pidInit { + id_server: "example.org", + id_access_token: "IdToken", + medium: thirdparty::Medium::Email, + address: "address", + } + .into(), + ) + .await + .unwrap(); +} + +#[async_test] +async fn leave_room() { + let client = logged_in_client().await; + + let _m = mock("POST", Matcher::Regex(r"^/_matrix/client/r0/rooms/.*/leave".to_owned())) + .with_status(200) + // this is an empty JSON object + .with_body(test_json::LOGOUT.to_string()) + .match_header("authorization", "Bearer 1234") + .create(); + + let _m = mock("GET", Matcher::Regex(r"^/_matrix/client/r0/sync\?.*$".to_owned())) + .with_status(200) + .match_header("authorization", "Bearer 1234") + .with_body(test_json::SYNC.to_string()) + .create(); + + let sync_settings = SyncSettings::new().timeout(Duration::from_millis(3000)); + + let _response = client.sync_once(sync_settings).await.unwrap(); + + let room = client.get_joined_room(room_id!("!SVkFJHzfwvuaIEawgC:localhost")).unwrap(); + + room.leave().await.unwrap(); +} + +#[async_test] +async fn ban_user() { + let client = logged_in_client().await; + + let _m = mock("POST", Matcher::Regex(r"^/_matrix/client/r0/rooms/.*/ban".to_owned())) + .with_status(200) + // this is an empty JSON object + .with_body(test_json::LOGOUT.to_string()) + .match_header("authorization", "Bearer 1234") + .create(); + + let _m = mock("GET", Matcher::Regex(r"^/_matrix/client/r0/sync\?.*$".to_owned())) + .with_status(200) + .match_header("authorization", "Bearer 1234") + .with_body(test_json::SYNC.to_string()) + .create(); + + let sync_settings = SyncSettings::new().timeout(Duration::from_millis(3000)); + + let _response = client.sync_once(sync_settings).await.unwrap(); + + let user = user_id!("@example:localhost"); + let room = client.get_joined_room(room_id!("!SVkFJHzfwvuaIEawgC:localhost")).unwrap(); + + room.ban_user(user, None).await.unwrap(); +} + +#[async_test] +async fn kick_user() { + let client = logged_in_client().await; + + let _m = mock("POST", Matcher::Regex(r"^/_matrix/client/r0/rooms/.*/kick".to_owned())) + .with_status(200) + // this is an empty JSON object + .with_body(test_json::LOGOUT.to_string()) + .match_header("authorization", "Bearer 1234") + .create(); + + let _m = mock("GET", Matcher::Regex(r"^/_matrix/client/r0/sync\?.*$".to_owned())) + .with_status(200) + .match_header("authorization", "Bearer 1234") + .with_body(test_json::SYNC.to_string()) + .create(); + + let sync_settings = SyncSettings::new().timeout(Duration::from_millis(3000)); + + let _response = client.sync_once(sync_settings).await.unwrap(); + + let user = user_id!("@example:localhost"); + let room = client.get_joined_room(room_id!("!SVkFJHzfwvuaIEawgC:localhost")).unwrap(); + + room.kick_user(user, None).await.unwrap(); +} + +#[async_test] +async fn read_receipt() { + let client = logged_in_client().await; + + let _m = mock("POST", Matcher::Regex(r"^/_matrix/client/r0/rooms/.*/receipt".to_owned())) + .with_status(200) + // this is an empty JSON object + .with_body(test_json::LOGOUT.to_string()) + .match_header("authorization", "Bearer 1234") + .create(); + + let _m = mock("GET", Matcher::Regex(r"^/_matrix/client/r0/sync\?.*$".to_owned())) + .with_status(200) + .match_header("authorization", "Bearer 1234") + .with_body(test_json::SYNC.to_string()) + .create(); + + let sync_settings = SyncSettings::new().timeout(Duration::from_millis(3000)); + + let _response = client.sync_once(sync_settings).await.unwrap(); + + let event_id = event_id!("$xxxxxx:example.org"); + let room = client.get_joined_room(room_id!("!SVkFJHzfwvuaIEawgC:localhost")).unwrap(); + + room.read_receipt(event_id).await.unwrap(); +} + +#[async_test] +async fn read_marker() { + let client = logged_in_client().await; + + let _m = mock("POST", Matcher::Regex(r"^/_matrix/client/r0/rooms/.*/read_markers".to_owned())) + .with_status(200) + // this is an empty JSON object + .with_body(test_json::LOGOUT.to_string()) + .match_header("authorization", "Bearer 1234") + .create(); + + let _m = mock("GET", Matcher::Regex(r"^/_matrix/client/r0/sync\?.*$".to_owned())) + .with_status(200) + .match_header("authorization", "Bearer 1234") + .with_body(test_json::SYNC.to_string()) + .create(); + + let sync_settings = SyncSettings::new().timeout(Duration::from_millis(3000)); + + let _response = client.sync_once(sync_settings).await.unwrap(); + + let event_id = event_id!("$xxxxxx:example.org"); + let room = client.get_joined_room(room_id!("!SVkFJHzfwvuaIEawgC:localhost")).unwrap(); + + room.read_marker(event_id, None).await.unwrap(); +} + +#[async_test] +async fn typing_notice() { + let client = logged_in_client().await; + + let _m = mock("PUT", Matcher::Regex(r"^/_matrix/client/r0/rooms/.*/typing".to_owned())) + .with_status(200) + // this is an empty JSON object + .with_body(test_json::LOGOUT.to_string()) + .match_header("authorization", "Bearer 1234") + .create(); + + let _m = mock("GET", Matcher::Regex(r"^/_matrix/client/r0/sync\?.*$".to_owned())) + .with_status(200) + .match_header("authorization", "Bearer 1234") + .with_body(test_json::SYNC.to_string()) + .create(); + + let sync_settings = SyncSettings::new().timeout(Duration::from_millis(3000)); + + let _response = client.sync_once(sync_settings).await.unwrap(); + + let room = client.get_joined_room(room_id!("!SVkFJHzfwvuaIEawgC:localhost")).unwrap(); + + room.typing_notice(true).await.unwrap(); +} + +#[async_test] +async fn room_state_event_send() { + use ruma::events::room::member::{MembershipState, RoomMemberEventContent}; + + let client = logged_in_client().await; + + let _m = mock("PUT", Matcher::Regex(r"^/_matrix/client/r0/rooms/.*/state/.*".to_owned())) + .with_status(200) + .match_header("authorization", "Bearer 1234") + .with_body(test_json::EVENT_ID.to_string()) + .create(); + + let _m = mock("GET", Matcher::Regex(r"^/_matrix/client/r0/sync\?.*$".to_owned())) + .with_status(200) + .match_header("authorization", "Bearer 1234") + .with_body(test_json::SYNC.to_string()) + .create(); + + let sync_settings = SyncSettings::new().timeout(Duration::from_millis(3000)); + + let _response = client.sync_once(sync_settings).await.unwrap(); + + let room_id = room_id!("!SVkFJHzfwvuaIEawgC:localhost"); + + let room = client.get_joined_room(room_id).unwrap(); + + let avatar_url = mxc_uri!("mxc://example.org/avA7ar"); + let member_event = assign!(RoomMemberEventContent::new(MembershipState::Join), { + avatar_url: Some(avatar_url.to_owned()) + }); + let response = room.send_state_event(member_event, "").await.unwrap(); + assert_eq!(event_id!("$h29iv0s8:example.com"), response.event_id); +} + +#[async_test] +async fn room_message_send() { + let client = logged_in_client().await; + + let _m = mock("PUT", Matcher::Regex(r"^/_matrix/client/r0/rooms/.*/send/".to_owned())) + .with_status(200) + .match_header("authorization", "Bearer 1234") + .with_body(test_json::EVENT_ID.to_string()) + .create(); + + let _m = mock("GET", Matcher::Regex(r"^/_matrix/client/r0/sync\?.*$".to_owned())) + .with_status(200) + .match_header("authorization", "Bearer 1234") + .with_body(test_json::SYNC.to_string()) + .create(); + + let sync_settings = SyncSettings::new().timeout(Duration::from_millis(3000)); + + let _response = client.sync_once(sync_settings).await.unwrap(); + + let room = client.get_joined_room(room_id!("!SVkFJHzfwvuaIEawgC:localhost")).unwrap(); + + let content = RoomMessageEventContent::text_plain("Hello world"); + let txn_id = TransactionId::new(); + let response = room.send(content, Some(&txn_id)).await.unwrap(); + + assert_eq!(event_id!("$h29iv0s8:example.com"), response.event_id) +} + +#[async_test] +async fn room_attachment_send() { + let client = logged_in_client().await; + + let _m = mock("PUT", Matcher::Regex(r"^/_matrix/client/r0/rooms/.*/send/".to_owned())) + .with_status(200) + .match_header("authorization", "Bearer 1234") + .match_body(Matcher::PartialJson(json!({ + "info": { + "mimetype": "image/jpeg" + } + }))) + .with_body(test_json::EVENT_ID.to_string()) + .create(); + + let _m = mock("POST", Matcher::Regex(r"^/_matrix/media/r0/upload".to_owned())) + .with_status(200) + .match_header("content-type", "image/jpeg") + .with_body( + json!({ + "content_uri": "mxc://example.com/AQwafuaFswefuhsfAFAgsw" + }) + .to_string(), + ) + .create(); + + let _m = mock("GET", Matcher::Regex(r"^/_matrix/client/r0/sync\?.*$".to_owned())) + .with_status(200) + .match_header("authorization", "Bearer 1234") + .with_body(test_json::SYNC.to_string()) + .create(); + + let sync_settings = SyncSettings::new().timeout(Duration::from_millis(3000)); + + let _response = client.sync_once(sync_settings).await.unwrap(); + + let room = client.get_joined_room(room_id!("!SVkFJHzfwvuaIEawgC:localhost")).unwrap(); + + let mut media = Cursor::new("Hello world"); + + let response = room + .send_attachment("image", &mime::IMAGE_JPEG, &mut media, AttachmentConfig::new()) + .await + .unwrap(); + + assert_eq!(event_id!("$h29iv0s8:example.com"), response.event_id) +} + +#[async_test] +async fn room_attachment_send_info() { + let client = logged_in_client().await; + + let _m = mock("PUT", Matcher::Regex(r"^/_matrix/client/r0/rooms/.*/send/".to_owned())) + .with_status(200) + .match_header("authorization", "Bearer 1234") + .match_body(Matcher::PartialJson(json!({ + "info": { + "mimetype": "image/jpeg", + "h": 600, + "w": 800, + } + }))) + .with_body(test_json::EVENT_ID.to_string()) + .create(); + + let upload_mock = mock("POST", Matcher::Regex(r"^/_matrix/media/r0/upload".to_owned())) + .with_status(200) + .match_header("content-type", "image/jpeg") + .with_body( + json!({ + "content_uri": "mxc://example.com/AQwafuaFswefuhsfAFAgsw" + }) + .to_string(), + ) + .create(); + + let _m = mock("GET", Matcher::Regex(r"^/_matrix/client/r0/sync\?.*$".to_owned())) + .with_status(200) + .match_header("authorization", "Bearer 1234") + .with_body(test_json::SYNC.to_string()) + .create(); + + let sync_settings = SyncSettings::new().timeout(Duration::from_millis(3000)); + + let _response = client.sync_once(sync_settings).await.unwrap(); + + let room = client.get_joined_room(room_id!("!SVkFJHzfwvuaIEawgC:localhost")).unwrap(); + + let mut media = Cursor::new("Hello world"); + + let config = AttachmentConfig::new().info(AttachmentInfo::Image(BaseImageInfo { + height: Some(uint!(600)), + width: Some(uint!(800)), + size: None, + blurhash: None, + })); + + let response = + room.send_attachment("image", &mime::IMAGE_JPEG, &mut media, config).await.unwrap(); + + upload_mock.assert(); + assert_eq!(event_id!("$h29iv0s8:example.com"), response.event_id) +} + +#[async_test] +async fn room_attachment_send_wrong_info() { + let client = logged_in_client().await; + + let _m = mock("PUT", Matcher::Regex(r"^/_matrix/client/r0/rooms/.*/send/".to_owned())) + .with_status(200) + .match_header("authorization", "Bearer 1234") + .match_body(Matcher::PartialJson(json!({ + "info": { + "mimetype": "image/jpeg", + "h": 600, + "w": 800, + } + }))) + .with_body(test_json::EVENT_ID.to_string()) + .create(); + + let _m = mock("POST", Matcher::Regex(r"^/_matrix/media/r0/upload".to_owned())) + .with_status(200) + .match_header("content-type", "image/jpeg") + .with_body( + json!({ + "content_uri": "mxc://example.com/AQwafuaFswefuhsfAFAgsw" + }) + .to_string(), + ) + .create(); + + let _m = mock("GET", Matcher::Regex(r"^/_matrix/client/r0/sync\?.*$".to_owned())) + .with_status(200) + .match_header("authorization", "Bearer 1234") + .with_body(test_json::SYNC.to_string()) + .create(); + + let sync_settings = SyncSettings::new().timeout(Duration::from_millis(3000)); + + let _response = client.sync_once(sync_settings).await.unwrap(); + + let room = client.get_joined_room(room_id!("!SVkFJHzfwvuaIEawgC:localhost")).unwrap(); + + let mut media = Cursor::new("Hello world"); + + let config = AttachmentConfig::new().info(AttachmentInfo::Video(BaseVideoInfo { + height: Some(uint!(600)), + width: Some(uint!(800)), + duration: Some(Duration::from_millis(3600)), + size: None, + blurhash: None, + })); + + let response = room.send_attachment("image", &mime::IMAGE_JPEG, &mut media, config).await; + + assert!(response.is_err()) +} + +#[async_test] +async fn room_attachment_send_info_thumbnail() { + let client = logged_in_client().await; + + let _m = mock("PUT", Matcher::Regex(r"^/_matrix/client/r0/rooms/.*/send/".to_owned())) + .with_status(200) + .match_header("authorization", "Bearer 1234") + .match_body(Matcher::PartialJson(json!({ + "info": { + "mimetype": "image/jpeg", + "h": 600, + "w": 800, + "thumbnail_info": { + "h": 360, + "w": 480, + "mimetype":"image/jpeg", + "size": 3600, + }, + "thumbnail_url": "mxc://example.com/AQwafuaFswefuhsfAFAgsw", + } + }))) + .with_body(test_json::EVENT_ID.to_string()) + .create(); + + let upload_mock = mock("POST", Matcher::Regex(r"^/_matrix/media/r0/upload".to_owned())) + .with_status(200) + .match_header("content-type", "image/jpeg") + .with_body( + json!({ + "content_uri": "mxc://example.com/AQwafuaFswefuhsfAFAgsw" + }) + .to_string(), + ) + .expect(2) + .create(); + + let _m = mock("GET", Matcher::Regex(r"^/_matrix/client/r0/sync\?.*$".to_owned())) + .with_status(200) + .match_header("authorization", "Bearer 1234") + .with_body(test_json::SYNC.to_string()) + .create(); + + let sync_settings = SyncSettings::new().timeout(Duration::from_millis(3000)); + + let _response = client.sync_once(sync_settings).await.unwrap(); + + let room = client.get_joined_room(room_id!("!SVkFJHzfwvuaIEawgC:localhost")).unwrap(); + + let mut media = Cursor::new("Hello world"); + + let mut thumbnail_reader = Cursor::new("Thumbnail"); + + let config = AttachmentConfig::with_thumbnail(Thumbnail { + reader: &mut thumbnail_reader, + content_type: &mime::IMAGE_JPEG, + info: Some(BaseThumbnailInfo { + height: Some(uint!(360)), + width: Some(uint!(480)), + size: Some(uint!(3600)), + }), + }) + .info(AttachmentInfo::Image(BaseImageInfo { + height: Some(uint!(600)), + width: Some(uint!(800)), + size: None, + blurhash: None, + })); + + let response = + room.send_attachment("image", &mime::IMAGE_JPEG, &mut media, config).await.unwrap(); + + upload_mock.assert(); + assert_eq!(event_id!("$h29iv0s8:example.com"), response.event_id) +} + +#[async_test] +async fn room_redact() { + let client = logged_in_client().await; + + let _m = mock("PUT", Matcher::Regex(r"^/_matrix/client/r0/rooms/.*/redact/.*?/.*?".to_owned())) + .with_status(200) + .match_header("authorization", "Bearer 1234") + .with_body(test_json::EVENT_ID.to_string()) + .create(); + + let _m = mock("GET", Matcher::Regex(r"^/_matrix/client/r0/sync\?.*$".to_owned())) + .with_status(200) + .match_header("authorization", "Bearer 1234") + .with_body(test_json::SYNC.to_string()) + .create(); + + let sync_settings = SyncSettings::new().timeout(Duration::from_millis(3000)); + + let _response = client.sync_once(sync_settings).await.unwrap(); + + let room = client.get_joined_room(room_id!("!SVkFJHzfwvuaIEawgC:localhost")).unwrap(); + + let event_id = event_id!("$xxxxxxxx:example.com"); + + let txn_id = TransactionId::new(); + let reason = Some("Indecent material"); + let response = room.redact(event_id, reason, Some(txn_id)).await.unwrap(); + + assert_eq!(event_id!("$h29iv0s8:example.com"), response.event_id) +} diff --git a/crates/matrix-sdk/tests/integration/room/left.rs b/crates/matrix-sdk/tests/integration/room/left.rs new file mode 100644 index 000000000..9bc48d2f0 --- /dev/null +++ b/crates/matrix-sdk/tests/integration/room/left.rs @@ -0,0 +1,34 @@ +use std::time::Duration; + +use matrix_sdk::config::SyncSettings; +use matrix_sdk_test::{async_test, test_json}; +use mockito::{mock, Matcher}; +use ruma::room_id; + +use crate::logged_in_client; + +#[async_test] +async fn forget_room() { + let client = logged_in_client().await; + + let _m = mock("POST", Matcher::Regex(r"^/_matrix/client/r0/rooms/.*/forget".to_owned())) + .with_status(200) + // this is an empty JSON object + .with_body(test_json::LOGOUT.to_string()) + .match_header("authorization", "Bearer 1234") + .create(); + + let _m = mock("GET", Matcher::Regex(r"^/_matrix/client/r0/sync\?.*$".to_owned())) + .with_status(200) + .match_header("authorization", "Bearer 1234") + .with_body(test_json::LEAVE_SYNC.to_string()) + .create(); + + let sync_settings = SyncSettings::new().timeout(Duration::from_millis(3000)); + + let _response = client.sync_once(sync_settings).await.unwrap(); + + let room = client.get_left_room(room_id!("!SVkFJHzfwvuaIEawgC:localhost")).unwrap(); + + room.forget().await.unwrap(); +} diff --git a/crates/matrix-sdk/tests/integration/room/mod.rs b/crates/matrix-sdk/tests/integration/room/mod.rs new file mode 100644 index 000000000..b9e0e2c78 --- /dev/null +++ b/crates/matrix-sdk/tests/integration/room/mod.rs @@ -0,0 +1,3 @@ +mod common; +mod joined; +mod left; diff --git a/xtask/src/ci.rs b/xtask/src/ci.rs index c3a1df7c7..6ee336840 100644 --- a/xtask/src/ci.rs +++ b/xtask/src/ci.rs @@ -154,7 +154,7 @@ fn run_feature_tests(cmd: Option) -> Result<()> { ]); let run = |arg_set: &str| { - cmd!("rustup run stable cargo nextest run -p matrix-sdk") + cmd!("rustup run stable cargo nextest run -p matrix-sdk --features __test") .args(arg_set.split_whitespace()) .run()?; cmd!("rustup run stable cargo test --doc -p matrix-sdk") From de60a24602a9d42ae6cc2e188ec025ecb0e69086 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20Commaille?= Date: Thu, 7 Jul 2022 11:26:49 +0200 Subject: [PATCH 3/5] Remove __test feature --- .github/workflows/ci.yml | 2 +- crates/matrix-sdk/Cargo.toml | 3 -- crates/matrix-sdk/src/client/builder.rs | 4 +- crates/matrix-sdk/src/client/mod.rs | 38 +++++++++++++- crates/matrix-sdk/tests/integration/client.rs | 51 ------------------- xtask/src/ci.rs | 2 +- 6 files changed, 41 insertions(+), 59 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 537f3426b..fb813b1a3 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -127,7 +127,7 @@ jobs: uses: actions-rs/cargo@v1 with: command: nextest - args: run --workspace --features __test + args: run --workspace - name: Test documentation uses: actions-rs/cargo@v1 diff --git a/crates/matrix-sdk/Cargo.toml b/crates/matrix-sdk/Cargo.toml index d3ab05d32..c6f87f915 100644 --- a/crates/matrix-sdk/Cargo.toml +++ b/crates/matrix-sdk/Cargo.toml @@ -55,9 +55,6 @@ docsrs = [ "image-proc", ] -# This is an internal feature only used for tests -__test = [] - [dependencies] anyhow = { version = "1.0.57", optional = true } anymap2 = "0.13.0" diff --git a/crates/matrix-sdk/src/client/builder.rs b/crates/matrix-sdk/src/client/builder.rs index 34443d0e3..41705380a 100644 --- a/crates/matrix-sdk/src/client/builder.rs +++ b/crates/matrix-sdk/src/client/builder.rs @@ -343,12 +343,12 @@ impl ClientBuilder { } fn homeserver_from_name(server_name: &ServerName) -> String { - #[cfg(not(any(test, feature = "__test")))] + #[cfg(not(test))] return format!("https://{}", server_name); // Mockito only knows how to test http endpoints: // https://github.com/lipanski/mockito/issues/127 - #[cfg(any(test, feature = "__test"))] + #[cfg(test)] return format!("http://{}", server_name); } diff --git a/crates/matrix-sdk/src/client/mod.rs b/crates/matrix-sdk/src/client/mod.rs index 19fd69b14..56d740b91 100644 --- a/crates/matrix-sdk/src/client/mod.rs +++ b/crates/matrix-sdk/src/client/mod.rs @@ -2200,7 +2200,7 @@ pub(crate) mod tests { wasm_bindgen_test::wasm_bindgen_test_configure!(run_in_browser); use mockito::{mock, Matcher}; - use ruma::{api::MatrixVersion, device_id, room_id, user_id}; + use ruma::{api::MatrixVersion, device_id, room_id, user_id, UserId}; use url::Url; use super::{Client, ClientBuilder, Session}; @@ -2249,6 +2249,42 @@ pub(crate) mod tests { // assert_eq!(1, ignored_users.len()) } + #[async_test] + async fn successful_discovery() { + let server_url = mockito::server_url(); + let domain = server_url.strip_prefix("http://").unwrap(); + let alice = UserId::parse("@alice:".to_owned() + domain).unwrap(); + + let _m_well_known = mock("GET", "/.well-known/matrix/client") + .with_status(200) + .with_body( + test_json::WELL_KNOWN.to_string().replace("HOMESERVER_URL", server_url.as_ref()), + ) + .create(); + + let _m_versions = mock("GET", "/_matrix/client/versions") + .with_status(200) + .with_body(test_json::VERSIONS.to_string()) + .create(); + let client = Client::builder().user_id(&alice).build().await.unwrap(); + + assert_eq!(client.homeserver().await, Url::parse(server_url.as_ref()).unwrap()); + } + + #[async_test] + async fn discovery_broken_server() { + let server_url = mockito::server_url(); + let domain = server_url.strip_prefix("http://").unwrap(); + let alice = UserId::parse("@alice:".to_owned() + domain).unwrap(); + + let _m = mock("GET", "/.well-known/matrix/client").with_status(404).create(); + + assert!( + Client::builder().user_id(&alice).build().await.is_err(), + "Creating a client from a user ID should fail when the .well-known request fails." + ); + } + #[async_test] async fn room_creation() { let client = logged_in_client().await; diff --git a/crates/matrix-sdk/tests/integration/client.rs b/crates/matrix-sdk/tests/integration/client.rs index be6c27c63..c7c3a7d52 100644 --- a/crates/matrix-sdk/tests/integration/client.rs +++ b/crates/matrix-sdk/tests/integration/client.rs @@ -3,8 +3,6 @@ use std::{collections::BTreeMap, str::FromStr, time::Duration}; -#[cfg(feature = "__test")] -use matrix_sdk::{config::RequestConfig, Client}; use matrix_sdk::{ config::SyncSettings, media::{MediaFormat, MediaRequest, MediaThumbnailSize}, @@ -12,8 +10,6 @@ use matrix_sdk::{ }; use matrix_sdk_test::{async_test, test_json}; use mockito::{mock, Matcher}; -#[cfg(feature = "__test")] -use ruma::UserId; use ruma::{ api::{ client::{ @@ -48,53 +44,6 @@ async fn set_homeserver() { assert_eq!(client.homeserver().await, homeserver); } -#[cfg(feature = "__test")] -#[async_test] -async fn successful_discovery() { - let server_url = mockito::server_url(); - let domain = server_url.strip_prefix("http://").unwrap(); - let alice = UserId::parse("@alice:".to_owned() + domain).unwrap(); - - let _m_well_known = mock("GET", "/.well-known/matrix/client") - .with_status(200) - .with_body(test_json::WELL_KNOWN.to_string().replace("HOMESERVER_URL", server_url.as_ref())) - .create(); - - let _m_versions = mock("GET", "/_matrix/client/versions") - .with_status(200) - .with_body(test_json::VERSIONS.to_string()) - .create(); - - let client = Client::builder() - .request_config(RequestConfig::new().disable_retry()) - .user_id(&alice) - .build() - .await - .unwrap(); - - assert_eq!(client.homeserver().await, Url::parse(server_url.as_ref()).unwrap()); -} - -#[cfg(feature = "__test")] -#[async_test] -async fn discovery_broken_server() { - let server_url = mockito::server_url(); - let domain = server_url.strip_prefix("http://").unwrap(); - let alice = UserId::parse("@alice:".to_owned() + domain).unwrap(); - - let _m = mock("GET", "/.well-known/matrix/client").with_status(404).create(); - - assert!( - Client::builder() - .request_config(RequestConfig::new().disable_retry()) - .user_id(&alice) - .build() - .await - .is_err(), - "Creating a client from a user ID should fail when the .well-known request fails." - ); -} - #[async_test] async fn login() { let homeserver = Url::from_str(&mockito::server_url()).unwrap(); diff --git a/xtask/src/ci.rs b/xtask/src/ci.rs index 6ee336840..c3a1df7c7 100644 --- a/xtask/src/ci.rs +++ b/xtask/src/ci.rs @@ -154,7 +154,7 @@ fn run_feature_tests(cmd: Option) -> Result<()> { ]); let run = |arg_set: &str| { - cmd!("rustup run stable cargo nextest run -p matrix-sdk --features __test") + cmd!("rustup run stable cargo nextest run -p matrix-sdk") .args(arg_set.split_whitespace()) .run()?; cmd!("rustup run stable cargo test --doc -p matrix-sdk") From ee6986391292e43ce9833035651473dd30cd0f07 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20Commaille?= Date: Thu, 7 Jul 2022 13:19:31 +0200 Subject: [PATCH 4/5] Move event permalink test --- crates/matrix-sdk/src/client/mod.rs | 386 ------------------ .../tests/integration/room/common.rs | 10 + 2 files changed, 10 insertions(+), 386 deletions(-) diff --git a/crates/matrix-sdk/src/client/mod.rs b/crates/matrix-sdk/src/client/mod.rs index fb4316770..56d740b91 100644 --- a/crates/matrix-sdk/src/client/mod.rs +++ b/crates/matrix-sdk/src/client/mod.rs @@ -2370,390 +2370,4 @@ pub(crate) mod tests { panic!("this request should return an `Err` variant") } } - - #[async_test] - async fn room_permalink() { - fn sync_response(index: u8, room_timeline_events: &[JsonValue]) -> JsonValue { - json!({ - "device_one_time_keys_count": {}, - "next_batch": format!("s526_47314_0_7_1_1_1_11444_{}", index + 1), - "device_lists": { - "changed": [], - "left": [] - }, - "account_data": { - "events": [] - }, - "rooms": { - "invite": {}, - "join": { - "!test_room:127.0.0.1": { - "summary": {}, - "account_data": { - "events": [] - }, - "ephemeral": { - "events": [] - }, - "state": { - "events": [] - }, - "timeline": { - "events": room_timeline_events, - "limited": false, - "prev_batch": format!("s526_47314_0_7_1_1_1_11444_{}", index - 1), - }, - "unread_notifications": { - "highlight_count": 0, - "notification_count": 0, - } - } - }, - "leave": {} - }, - "to_device": { - "events": [] - }, - "presence": { - "events": [] - } - }) - } - - fn room_member_events(nb: usize, server: &str) -> Vec { - let mut events = Vec::with_capacity(nb); - for i in 0..nb { - let id = format!("${server}{i}"); - let user = format!("@user{i}:{server}"); - events.push(json!({ - "content": { - "membership": "join", - }, - "event_id": id, - "origin_server_ts": 151800140, - "sender": user, - "state_key": user, - "type": "m.room.member", - })) - } - events - } - - let client = logged_in_client().await; - let sync_settings = SyncSettings::new(); - - // Without elligible server - let mut sync_index = 1; - let res = sync_response( - sync_index, - &[ - json!({ - "content": { - "creator": "@creator:127.0.0.1", - "room_version": "6", - }, - "event_id": "$151957878228ekrDs", - "origin_server_ts": 15195787, - "sender": "@creator:localhost", - "state_key": "", - "type": "m.room.create", - }), - json!({ - "content": { - "membership": "join", - }, - "event_id": "$151800140517rfvjc", - "origin_server_ts": 151800140, - "sender": "@creator:127.0.0.1", - "state_key": "@creator:127.0.0.1", - "type": "m.room.member", - }), - ], - ); - let _sync = mock("GET", Matcher::Regex(r"^/_matrix/client/r0/sync\?.*$".to_owned())) - .with_status(200) - .with_body(res.to_string()) - .match_header("authorization", "Bearer 1234") - .create(); - client.sync_once(sync_settings.clone()).await.unwrap(); - let room = client.get_room(room_id!("!test_room:127.0.0.1")).unwrap(); - - assert_eq!( - room.matrix_to_permalink().await.unwrap().to_string(), - "https://matrix.to/#/%21test_room%3A127.0.0.1" - ); - assert_eq!( - room.matrix_permalink(false).await.unwrap().to_string(), - "matrix:roomid/test_room:127.0.0.1" - ); - assert_eq!( - room.matrix_permalink(true).await.unwrap().to_string(), - "matrix:roomid/test_room:127.0.0.1?action=join" - ); - - // With a single elligible server - sync_index += 1; - let res = sync_response( - sync_index, - &[json!({ - "content": { - "membership": "join", - }, - "event_id": "$151800140517rfvjc", - "origin_server_ts": 151800140, - "sender": "@example:localhost", - "state_key": "@example:localhost", - "type": "m.room.member", - })], - ); - let _sync = mock("GET", Matcher::Regex(r"^/_matrix/client/r0/sync\?.*$".to_owned())) - .with_status(200) - .with_body(res.to_string()) - .match_header("authorization", "Bearer 1234") - .create(); - client.sync_once(sync_settings.clone()).await.unwrap(); - - assert_eq!( - room.matrix_to_permalink().await.unwrap().to_string(), - "https://matrix.to/#/%21test_room%3A127.0.0.1?via=localhost" - ); - assert_eq!( - room.matrix_permalink(false).await.unwrap().to_string(), - "matrix:roomid/test_room:127.0.0.1?via=localhost" - ); - - // With two elligible servers - sync_index += 1; - let res = sync_response(sync_index, &room_member_events(15, "notarealhs")); - let _sync = mock("GET", Matcher::Regex(r"^/_matrix/client/r0/sync\?.*$".to_owned())) - .with_status(200) - .with_body(res.to_string()) - .match_header("authorization", "Bearer 1234") - .create(); - client.sync_once(sync_settings.clone()).await.unwrap(); - - assert_eq!( - room.matrix_to_permalink().await.unwrap().to_string(), - "https://matrix.to/#/%21test_room%3A127.0.0.1?via=notarealhs&via=localhost" - ); - assert_eq!( - room.matrix_permalink(false).await.unwrap().to_string(), - "matrix:roomid/test_room:127.0.0.1?via=notarealhs&via=localhost" - ); - - // With three elligible servers - sync_index += 1; - let res = sync_response(sync_index, &room_member_events(5, "mymatrix")); - let _sync = mock("GET", Matcher::Regex(r"^/_matrix/client/r0/sync\?.*$".to_owned())) - .with_status(200) - .with_body(res.to_string()) - .match_header("authorization", "Bearer 1234") - .create(); - client.sync_once(sync_settings.clone()).await.unwrap(); - - assert_eq!( - room.matrix_to_permalink().await.unwrap().to_string(), - "https://matrix.to/#/%21test_room%3A127.0.0.1?via=notarealhs&via=mymatrix&via=localhost" - ); - assert_eq!( - room.matrix_permalink(false).await.unwrap().to_string(), - "matrix:roomid/test_room:127.0.0.1?via=notarealhs&via=mymatrix&via=localhost" - ); - - // With four elligible servers - sync_index += 1; - let res = sync_response(sync_index, &room_member_events(10, "yourmatrix")); - let _sync = mock("GET", Matcher::Regex(r"^/_matrix/client/r0/sync\?.*$".to_owned())) - .with_status(200) - .with_body(res.to_string()) - .match_header("authorization", "Bearer 1234") - .create(); - client.sync_once(sync_settings.clone()).await.unwrap(); - - assert_eq!( - room.matrix_to_permalink().await.unwrap().to_string(), - "https://matrix.to/#/%21test_room%3A127.0.0.1?via=notarealhs&via=yourmatrix&via=mymatrix" - ); - assert_eq!( - room.matrix_permalink(false).await.unwrap().to_string(), - "matrix:roomid/test_room:127.0.0.1?via=notarealhs&via=yourmatrix&via=mymatrix" - ); - - // With power levels - sync_index += 1; - let res = sync_response( - sync_index, - &[json!({ - "content": { - "users": { - "@example:localhost": 50, - }, - }, - "event_id": "$15139375512JaHAW", - "origin_server_ts": 151393755, - "sender": "@creator:127.0.0.1", - "state_key": "", - "type": "m.room.power_levels", - })], - ); - let _sync = mock("GET", Matcher::Regex(r"^/_matrix/client/r0/sync\?.*$".to_owned())) - .with_status(200) - .with_body(res.to_string()) - .match_header("authorization", "Bearer 1234") - .create(); - client.sync_once(sync_settings.clone()).await.unwrap(); - - assert_eq!( - room.matrix_to_permalink().await.unwrap().to_string(), - "https://matrix.to/#/%21test_room%3A127.0.0.1?via=localhost&via=notarealhs&via=yourmatrix" - ); - assert_eq!( - room.matrix_permalink(false).await.unwrap().to_string(), - "matrix:roomid/test_room:127.0.0.1?via=localhost&via=notarealhs&via=yourmatrix" - ); - - // With higher power levels - sync_index += 1; - let res = sync_response( - sync_index, - &[json!({ - "content": { - "users": { - "@example:localhost": 50, - "@user0:mymatrix": 70, - }, - }, - "event_id": "$15139375512JaHAZ", - "origin_server_ts": 151393755, - "sender": "@creator:127.0.0.1", - "state_key": "", - "type": "m.room.power_levels", - })], - ); - let _sync = mock("GET", Matcher::Regex(r"^/_matrix/client/r0/sync\?.*$".to_owned())) - .with_status(200) - .with_body(res.to_string()) - .match_header("authorization", "Bearer 1234") - .create(); - client.sync_once(sync_settings.clone()).await.unwrap(); - - assert_eq!( - room.matrix_to_permalink().await.unwrap().to_string(), - "https://matrix.to/#/%21test_room%3A127.0.0.1?via=mymatrix&via=notarealhs&via=yourmatrix" - ); - assert_eq!( - room.matrix_permalink(false).await.unwrap().to_string(), - "matrix:roomid/test_room:127.0.0.1?via=mymatrix&via=notarealhs&via=yourmatrix" - ); - - // With server ACLs - sync_index += 1; - let res = sync_response( - sync_index, - &[json!({ - "content": { - "allow": ["*"], - "allow_ip_literals": true, - "deny": ["notarealhs"], - }, - "event_id": "$143273582443PhrSn", - "origin_server_ts": 1432735824, - "sender": "@creator:127.0.0.1", - "state_key": "", - "type": "m.room.server_acl", - })], - ); - let _sync = mock("GET", Matcher::Regex(r"^/_matrix/client/r0/sync\?.*$".to_owned())) - .with_status(200) - .with_body(res.to_string()) - .match_header("authorization", "Bearer 1234") - .create(); - client.sync_once(sync_settings.clone()).await.unwrap(); - - assert_eq!( - room.matrix_to_permalink().await.unwrap().to_string(), - "https://matrix.to/#/%21test_room%3A127.0.0.1?via=mymatrix&via=yourmatrix&via=localhost" - ); - assert_eq!( - room.matrix_permalink(false).await.unwrap().to_string(), - "matrix:roomid/test_room:127.0.0.1?via=mymatrix&via=yourmatrix&via=localhost" - ); - - // With an alternative alias - sync_index += 1; - let res = sync_response( - sync_index, - &[json!({ - "content": { - "alt_aliases": ["#alias:localhost"], - }, - "event_id": "$15139375513VdeRF", - "origin_server_ts": 151393755, - "sender": "@example:localhost", - "state_key": "", - "type": "m.room.canonical_alias", - })], - ); - let _sync = mock("GET", Matcher::Regex(r"^/_matrix/client/r0/sync\?.*$".to_owned())) - .with_status(200) - .with_body(res.to_string()) - .match_header("authorization", "Bearer 1234") - .create(); - client.sync_once(sync_settings.clone()).await.unwrap(); - - assert_eq!( - room.matrix_to_permalink().await.unwrap().to_string(), - "https://matrix.to/#/%23alias%3Alocalhost" - ); - assert_eq!( - room.matrix_permalink(false).await.unwrap().to_string(), - "matrix:r/alias:localhost" - ); - - // With a canonical alias - sync_index += 1; - let res = sync_response( - sync_index, - &[json!({ - "content": { - "alias": "#canonical:localhost", - "alt_aliases": ["#alias:localhost"], - }, - "event_id": "$15139375513VdeRF", - "origin_server_ts": 151393755, - "sender": "@example:localhost", - "state_key": "", - "type": "m.room.canonical_alias", - })], - ); - let _sync = mock("GET", Matcher::Regex(r"^/_matrix/client/r0/sync\?.*$".to_owned())) - .with_status(200) - .with_body(res.to_string()) - .match_header("authorization", "Bearer 1234") - .create(); - client.sync_once(sync_settings).await.unwrap(); - - assert_eq!( - room.matrix_to_permalink().await.unwrap().to_string(), - "https://matrix.to/#/%23canonical%3Alocalhost" - ); - assert_eq!( - room.matrix_permalink(false).await.unwrap().to_string(), - "matrix:r/canonical:localhost" - ); - assert_eq!( - room.matrix_permalink(true).await.unwrap().to_string(), - "matrix:r/canonical:localhost?action=join" - ); - - let event_id = event_id!("$15139375512JaHAW"); - assert_eq!( - room.matrix_to_event_permalink(event_id).await.unwrap().to_string(), - "https://matrix.to/#/%21test_room%3A127.0.0.1/%2415139375512JaHAW?via=mymatrix&via=yourmatrix&via=localhost" - ); - assert_eq!( - room.matrix_event_permalink(event_id).await.unwrap().to_string(), - "matrix:roomid/test_room:127.0.0.1/e/15139375512JaHAW?via=mymatrix&via=yourmatrix&via=localhost" - ); - } } diff --git a/crates/matrix-sdk/tests/integration/room/common.rs b/crates/matrix-sdk/tests/integration/room/common.rs index 2edfb9d5b..85fdb3e8b 100644 --- a/crates/matrix-sdk/tests/integration/room/common.rs +++ b/crates/matrix-sdk/tests/integration/room/common.rs @@ -810,4 +810,14 @@ async fn room_permalink() { room.matrix_permalink(true).await.unwrap().to_string(), "matrix:r/canonical:localhost?action=join" ); + + let event_id = event_id!("$15139375512JaHAW"); + assert_eq!( + room.matrix_to_event_permalink(event_id).await.unwrap().to_string(), + "https://matrix.to/#/%21test_room%3A127.0.0.1/%2415139375512JaHAW?via=mymatrix&via=yourmatrix&via=localhost" + ); + assert_eq!( + room.matrix_event_permalink(event_id).await.unwrap().to_string(), + "matrix:roomid/test_room:127.0.0.1/e/15139375512JaHAW?via=mymatrix&via=yourmatrix&via=localhost" + ); } From 5ab8bd088511372c308093eccc860369b643f1cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20Commaille?= Date: Thu, 7 Jul 2022 13:24:54 +0200 Subject: [PATCH 5/5] Fix missing import --- crates/matrix-sdk/tests/integration/room/common.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/matrix-sdk/tests/integration/room/common.rs b/crates/matrix-sdk/tests/integration/room/common.rs index 85fdb3e8b..910e4064d 100644 --- a/crates/matrix-sdk/tests/integration/room/common.rs +++ b/crates/matrix-sdk/tests/integration/room/common.rs @@ -7,7 +7,7 @@ use matrix_sdk::{ use matrix_sdk_test::{async_test, test_json}; use mockito::{mock, Matcher}; use ruma::{ - device_id, + device_id, event_id, events::{AnySyncStateEvent, StateEventType}, room_id, user_id, };