Files
cli/sunbeam-sdk/tests/test_matrix.rs
Sienna Meridian Satterwhite f06a167496 feat: BuildKit client + integration test suite (651 tests)
BuildKitClient CLI wrapper for buildctl.
Docker compose stack (9 services) for integration testing.
Comprehensive test suite: wiremock tests for Matrix/La Suite/S3/client,
integration tests for Kratos/Hydra/Gitea/OpenSearch/Prometheus/Loki/
Grafana/LiveKit.

Bump: sunbeam-sdk v0.12.0
2026-03-21 20:35:59 +00:00

903 lines
33 KiB
Rust

#![cfg(feature = "integration")]
use sunbeam_sdk::client::{AuthMethod, ServiceClient};
use sunbeam_sdk::matrix::MatrixClient;
use sunbeam_sdk::matrix::types::*;
use wiremock::{MockServer, Mock, ResponseTemplate};
use wiremock::matchers::{method, path, path_regex};
fn client(uri: &str) -> MatrixClient {
MatrixClient::from_parts(uri.to_string(), AuthMethod::Bearer("test-token".into()))
}
fn ok_json(body: serde_json::Value) -> ResponseTemplate {
ResponseTemplate::new(200).set_body_json(body)
}
fn ok_empty() -> ResponseTemplate {
ResponseTemplate::new(200).set_body_json(serde_json::json!({}))
}
// ---------------------------------------------------------------------------
// ServiceClient trait + connect / set_token
// ---------------------------------------------------------------------------
#[tokio::test]
async fn service_client_trait() {
let server = MockServer::start().await;
let mut c = client(&server.uri());
assert_eq!(c.service_name(), "matrix");
assert_eq!(c.base_url(), server.uri());
c.set_token("new-tok");
// just exercises set_token; nothing to assert beyond no panic
let c2 = MatrixClient::connect("example.com");
assert_eq!(c2.base_url(), "https://matrix.example.com/_matrix");
}
// ---------------------------------------------------------------------------
// Auth endpoints
// ---------------------------------------------------------------------------
#[tokio::test]
async fn auth_endpoints() {
let server = MockServer::start().await;
let c = client(&server.uri());
// list_login_types
Mock::given(method("GET")).and(path("/client/v3/login"))
.respond_with(ok_json(serde_json::json!({"flows": []})))
.mount(&server).await;
let r = c.list_login_types().await.unwrap();
assert!(r.flows.is_empty());
// login
Mock::given(method("POST")).and(path("/client/v3/login"))
.respond_with(ok_json(serde_json::json!({
"user_id": "@u:localhost",
"access_token": "tok",
"device_id": "D1"
})))
.mount(&server).await;
let body = LoginRequest {
login_type: "m.login.password".into(),
identifier: None, password: Some("pw".into()), token: None,
device_id: None, initial_device_display_name: None, refresh_token: None,
};
let r = c.login(&body).await.unwrap();
assert_eq!(r.user_id, "@u:localhost");
// refresh
Mock::given(method("POST")).and(path("/client/v3/refresh"))
.respond_with(ok_json(serde_json::json!({
"access_token": "new-tok"
})))
.mount(&server).await;
let r = c.refresh(&RefreshRequest { refresh_token: "rt".into() }).await.unwrap();
assert_eq!(r.access_token, "new-tok");
// logout
Mock::given(method("POST")).and(path("/client/v3/logout"))
.respond_with(ok_empty())
.mount(&server).await;
c.logout().await.unwrap();
// logout_all
Mock::given(method("POST")).and(path("/client/v3/logout/all"))
.respond_with(ok_empty())
.mount(&server).await;
c.logout_all().await.unwrap();
}
// ---------------------------------------------------------------------------
// Account endpoints
// ---------------------------------------------------------------------------
#[tokio::test]
async fn account_endpoints() {
let server = MockServer::start().await;
let c = client(&server.uri());
// register
Mock::given(method("POST")).and(path("/client/v3/register"))
.respond_with(ok_json(serde_json::json!({"user_id": "@new:localhost"})))
.mount(&server).await;
let body = RegisterRequest {
username: Some("new".into()), password: Some("pw".into()),
device_id: None, initial_device_display_name: None,
inhibit_login: None, refresh_token: None, auth: None, kind: None,
};
let r = c.register(&body).await.unwrap();
assert_eq!(r.user_id, "@new:localhost");
// whoami
Mock::given(method("GET")).and(path("/client/v3/account/whoami"))
.respond_with(ok_json(serde_json::json!({"user_id": "@me:localhost"})))
.mount(&server).await;
let r = c.whoami().await.unwrap();
assert_eq!(r.user_id, "@me:localhost");
// list_3pids
Mock::given(method("GET")).and(path("/client/v3/account/3pid"))
.respond_with(ok_json(serde_json::json!({"threepids": []})))
.mount(&server).await;
let r = c.list_3pids().await.unwrap();
assert!(r.threepids.is_empty());
// add_3pid
Mock::given(method("POST")).and(path("/client/v3/account/3pid/add"))
.respond_with(ok_empty())
.mount(&server).await;
c.add_3pid(&Add3pidRequest { client_secret: None, sid: None, auth: None }).await.unwrap();
// delete_3pid
Mock::given(method("POST")).and(path("/client/v3/account/3pid/delete"))
.respond_with(ok_empty())
.mount(&server).await;
c.delete_3pid(&Delete3pidRequest {
medium: "email".into(), address: "a@b.c".into(), id_server: None,
}).await.unwrap();
// change_password
Mock::given(method("POST")).and(path("/client/v3/account/password"))
.respond_with(ok_empty())
.mount(&server).await;
c.change_password(&ChangePasswordRequest {
new_password: "new-pw".into(), logout_devices: None, auth: None,
}).await.unwrap();
// deactivate
Mock::given(method("POST")).and(path("/client/v3/account/deactivate"))
.respond_with(ok_empty())
.mount(&server).await;
c.deactivate(&DeactivateRequest { auth: None, id_server: None, erase: None }).await.unwrap();
}
// ---------------------------------------------------------------------------
// Room endpoints
// ---------------------------------------------------------------------------
#[tokio::test]
async fn room_endpoints() {
let server = MockServer::start().await;
let c = client(&server.uri());
// create_room
Mock::given(method("POST")).and(path("/client/v3/createRoom"))
.respond_with(ok_json(serde_json::json!({"room_id": "!r:localhost"})))
.mount(&server).await;
let body = CreateRoomRequest {
name: Some("test".into()), topic: None, room_alias_name: None,
visibility: None, preset: None, invite: None, is_direct: None,
creation_content: None, initial_state: None, power_level_content_override: None,
};
let r = c.create_room(&body).await.unwrap();
assert_eq!(r.room_id, "!r:localhost");
// list_public_rooms — no params
Mock::given(method("GET")).and(path("/client/v3/publicRooms"))
.respond_with(ok_json(serde_json::json!({"chunk": []})))
.mount(&server).await;
let r = c.list_public_rooms(None, None).await.unwrap();
assert!(r.chunk.is_empty());
// list_public_rooms — with limit + since (exercises query-string branch)
let r = c.list_public_rooms(Some(10), Some("tok")).await.unwrap();
assert!(r.chunk.is_empty());
// search_public_rooms
Mock::given(method("POST")).and(path("/client/v3/publicRooms"))
.respond_with(ok_json(serde_json::json!({"chunk": []})))
.mount(&server).await;
let body = SearchPublicRoomsRequest {
limit: None, since: None, filter: None,
include_all_networks: None, third_party_instance_id: None,
};
let r = c.search_public_rooms(&body).await.unwrap();
assert!(r.chunk.is_empty());
// get_room_visibility
Mock::given(method("GET")).and(path_regex("/client/v3/directory/list/room/.+"))
.respond_with(ok_json(serde_json::json!({"visibility": "public"})))
.mount(&server).await;
let r = c.get_room_visibility("!r:localhost").await.unwrap();
assert_eq!(r.visibility, "public");
// set_room_visibility
Mock::given(method("PUT")).and(path_regex("/client/v3/directory/list/room/.+"))
.respond_with(ok_empty())
.mount(&server).await;
c.set_room_visibility("!r:localhost", &SetRoomVisibilityRequest {
visibility: "private".into(),
}).await.unwrap();
}
// ---------------------------------------------------------------------------
// Membership endpoints
// ---------------------------------------------------------------------------
#[tokio::test]
async fn membership_endpoints() {
let server = MockServer::start().await;
let c = client(&server.uri());
// join_room_by_id
Mock::given(method("POST")).and(path_regex("/client/v3/join/.+"))
.respond_with(ok_empty())
.mount(&server).await;
c.join_room_by_id("!room:localhost").await.unwrap();
// join_room_by_alias — use URL-encoded alias to avoid # being treated as fragment
c.join_room_by_alias("%23alias:localhost").await.unwrap();
// leave_room
Mock::given(method("POST")).and(path_regex("/client/v3/rooms/.+/leave"))
.respond_with(ok_empty())
.mount(&server).await;
c.leave_room("!room:localhost").await.unwrap();
// invite
Mock::given(method("POST")).and(path_regex("/client/v3/rooms/.+/invite"))
.respond_with(ok_empty())
.mount(&server).await;
c.invite("!room:localhost", &InviteRequest {
user_id: "@u:localhost".into(), reason: None,
}).await.unwrap();
// ban
Mock::given(method("POST")).and(path_regex("/client/v3/rooms/.+/ban"))
.respond_with(ok_empty())
.mount(&server).await;
c.ban("!room:localhost", &BanRequest {
user_id: "@u:localhost".into(), reason: None,
}).await.unwrap();
// unban
Mock::given(method("POST")).and(path_regex("/client/v3/rooms/.+/unban"))
.respond_with(ok_empty())
.mount(&server).await;
c.unban("!room:localhost", &UnbanRequest {
user_id: "@u:localhost".into(), reason: None,
}).await.unwrap();
// kick
Mock::given(method("POST")).and(path_regex("/client/v3/rooms/.+/kick"))
.respond_with(ok_empty())
.mount(&server).await;
c.kick("!room:localhost", &KickRequest {
user_id: "@u:localhost".into(), reason: None,
}).await.unwrap();
}
// ---------------------------------------------------------------------------
// State endpoints
// ---------------------------------------------------------------------------
#[tokio::test]
async fn state_endpoints() {
let server = MockServer::start().await;
let c = client(&server.uri());
// get_all_state — use exact path for the room
Mock::given(method("GET")).and(path("/client/v3/rooms/room1/state"))
.respond_with(ok_json(serde_json::json!([])))
.mount(&server).await;
let r = c.get_all_state("room1").await.unwrap();
assert!(r.is_empty());
// get_state_event — the path includes event_type/state_key, trailing slash for empty key
Mock::given(method("GET")).and(path_regex("/client/v3/rooms/.+/state/.+/.*"))
.respond_with(ok_json(serde_json::json!({"name": "Room"})))
.mount(&server).await;
let r = c.get_state_event("room2", "m.room.name", "").await.unwrap();
assert_eq!(r["name"], "Room");
// set_state_event
Mock::given(method("PUT")).and(path_regex("/client/v3/rooms/.+/state/.+/.*"))
.respond_with(ok_json(serde_json::json!({"event_id": "$e1"})))
.mount(&server).await;
let r = c.set_state_event(
"room2", "m.room.name", "",
&serde_json::json!({"name": "New"}),
).await.unwrap();
assert_eq!(r.event_id, "$e1");
}
// ---------------------------------------------------------------------------
// Sync
// ---------------------------------------------------------------------------
#[tokio::test]
async fn sync_endpoints() {
let server = MockServer::start().await;
let c = client(&server.uri());
// sync — no params
Mock::given(method("GET")).and(path("/client/v3/sync"))
.respond_with(ok_json(serde_json::json!({"next_batch": "s1"})))
.mount(&server).await;
let r = c.sync(&SyncParams::default()).await.unwrap();
assert_eq!(r.next_batch, "s1");
// sync — all params populated to cover every query-string branch
let r = c.sync(&SyncParams {
filter: Some("f".into()),
since: Some("s0".into()),
full_state: Some(true),
set_presence: Some("online".into()),
timeout: Some(30000),
}).await.unwrap();
assert_eq!(r.next_batch, "s1");
}
// ---------------------------------------------------------------------------
// Messages / events
// ---------------------------------------------------------------------------
#[tokio::test]
async fn message_endpoints() {
let server = MockServer::start().await;
let c = client(&server.uri());
// send_event
Mock::given(method("PUT")).and(path_regex("/client/v3/rooms/.+/send/.+/.+"))
.respond_with(ok_json(serde_json::json!({"event_id": "$e1"})))
.mount(&server).await;
let r = c.send_event(
"!r:localhost", "m.room.message", "txn1",
&serde_json::json!({"msgtype": "m.text", "body": "hi"}),
).await.unwrap();
assert_eq!(r.event_id, "$e1");
// get_messages — minimal params
Mock::given(method("GET")).and(path_regex("/client/v3/rooms/.+/messages"))
.respond_with(ok_json(serde_json::json!({"start": "s0", "chunk": []})))
.mount(&server).await;
let r = c.get_messages("!r:localhost", &MessagesParams {
dir: "b".into(), from: None, to: None, limit: None, filter: None,
}).await.unwrap();
assert!(r.chunk.is_empty());
// get_messages — all optional params to cover every branch
let r = c.get_messages("!r:localhost", &MessagesParams {
dir: "b".into(),
from: Some("tok1".into()),
to: Some("tok2".into()),
limit: Some(10),
filter: Some("{}".into()),
}).await.unwrap();
assert!(r.chunk.is_empty());
// get_event
Mock::given(method("GET")).and(path_regex("/client/v3/rooms/.+/event/.+"))
.respond_with(ok_json(serde_json::json!({
"type": "m.room.message",
"content": {"body": "hi"}
})))
.mount(&server).await;
let r = c.get_event("!r:localhost", "$ev1").await.unwrap();
assert_eq!(r.event_type, "m.room.message");
// get_context — without limit
Mock::given(method("GET")).and(path_regex("/client/v3/rooms/.+/context/.+"))
.respond_with(ok_json(serde_json::json!({
"start": "s0", "end": "s1",
"events_before": [], "events_after": []
})))
.mount(&server).await;
c.get_context("!r:localhost", "$ev1", None).await.unwrap();
// get_context — with limit to cover the branch
c.get_context("!r:localhost", "$ev1", Some(5)).await.unwrap();
// redact
Mock::given(method("PUT")).and(path_regex("/client/v3/rooms/.+/redact/.+/.+"))
.respond_with(ok_json(serde_json::json!({"event_id": "$re1"})))
.mount(&server).await;
let r = c.redact("!r:localhost", "$ev1", "txn2", &RedactRequest { reason: None }).await.unwrap();
assert_eq!(r.event_id, "$re1");
}
// ---------------------------------------------------------------------------
// Presence
// ---------------------------------------------------------------------------
#[tokio::test]
async fn presence_endpoints() {
let server = MockServer::start().await;
let c = client(&server.uri());
// get_presence
Mock::given(method("GET")).and(path_regex("/client/v3/presence/.+/status"))
.respond_with(ok_json(serde_json::json!({"presence": "online"})))
.mount(&server).await;
let r = c.get_presence("@u:localhost").await.unwrap();
assert_eq!(r.presence, "online");
// set_presence
Mock::given(method("PUT")).and(path_regex("/client/v3/presence/.+/status"))
.respond_with(ok_empty())
.mount(&server).await;
c.set_presence("@u:localhost", &SetPresenceRequest {
presence: "offline".into(), status_msg: None,
}).await.unwrap();
}
// ---------------------------------------------------------------------------
// Typing
// ---------------------------------------------------------------------------
#[tokio::test]
async fn typing_endpoint() {
let server = MockServer::start().await;
let c = client(&server.uri());
Mock::given(method("PUT")).and(path_regex("/client/v3/rooms/.+/typing/.+"))
.respond_with(ok_empty())
.mount(&server).await;
c.send_typing("!r:localhost", "@u:localhost", &TypingRequest {
typing: true, timeout: Some(30000),
}).await.unwrap();
}
// ---------------------------------------------------------------------------
// Receipts
// ---------------------------------------------------------------------------
#[tokio::test]
async fn receipt_endpoint() {
let server = MockServer::start().await;
let c = client(&server.uri());
Mock::given(method("POST")).and(path_regex("/client/v3/rooms/.+/receipt/.+/.+"))
.respond_with(ok_empty())
.mount(&server).await;
c.send_receipt("!r:localhost", "m.read", "$ev1", &ReceiptRequest { thread_id: None }).await.unwrap();
}
// ---------------------------------------------------------------------------
// Profile endpoints
// ---------------------------------------------------------------------------
#[tokio::test]
async fn profile_endpoints() {
let server = MockServer::start().await;
let c = client(&server.uri());
// get_profile
Mock::given(method("GET")).and(path_regex("/client/v3/profile/[^/]+$"))
.respond_with(ok_json(serde_json::json!({"displayname": "Alice"})))
.mount(&server).await;
let r = c.get_profile("@u:localhost").await.unwrap();
assert_eq!(r.displayname.as_deref(), Some("Alice"));
// get_displayname
Mock::given(method("GET")).and(path_regex("/client/v3/profile/.+/displayname"))
.respond_with(ok_json(serde_json::json!({"displayname": "Alice"})))
.mount(&server).await;
let r = c.get_displayname("@u:localhost").await.unwrap();
assert_eq!(r.displayname.as_deref(), Some("Alice"));
// set_displayname
Mock::given(method("PUT")).and(path_regex("/client/v3/profile/.+/displayname"))
.respond_with(ok_empty())
.mount(&server).await;
c.set_displayname("@u:localhost", &SetDisplaynameRequest {
displayname: "Bob".into(),
}).await.unwrap();
// get_avatar_url
Mock::given(method("GET")).and(path_regex("/client/v3/profile/.+/avatar_url"))
.respond_with(ok_json(serde_json::json!({"avatar_url": "mxc://example/abc"})))
.mount(&server).await;
let r = c.get_avatar_url("@u:localhost").await.unwrap();
assert_eq!(r.avatar_url.as_deref(), Some("mxc://example/abc"));
// set_avatar_url
Mock::given(method("PUT")).and(path_regex("/client/v3/profile/.+/avatar_url"))
.respond_with(ok_empty())
.mount(&server).await;
c.set_avatar_url("@u:localhost", &SetAvatarUrlRequest {
avatar_url: "mxc://example/xyz".into(),
}).await.unwrap();
}
// ---------------------------------------------------------------------------
// Alias endpoints
// ---------------------------------------------------------------------------
#[tokio::test]
async fn alias_endpoints() {
let server = MockServer::start().await;
let c = client(&server.uri());
// create_alias
Mock::given(method("PUT")).and(path_regex("/client/v3/directory/room/.+"))
.respond_with(ok_empty())
.mount(&server).await;
c.create_alias("%23test:localhost", &CreateAliasRequest {
room_id: "!r:localhost".into(),
}).await.unwrap();
// resolve_alias
Mock::given(method("GET")).and(path_regex("/client/v3/directory/room/.+"))
.respond_with(ok_json(serde_json::json!({
"room_id": "!r:localhost", "servers": ["localhost"]
})))
.mount(&server).await;
let r = c.resolve_alias("%23test:localhost").await.unwrap();
assert_eq!(r.room_id.as_deref(), Some("!r:localhost"));
// delete_alias
Mock::given(method("DELETE")).and(path_regex("/client/v3/directory/room/.+"))
.respond_with(ok_empty())
.mount(&server).await;
c.delete_alias("%23test:localhost").await.unwrap();
}
// ---------------------------------------------------------------------------
// User directory search
// ---------------------------------------------------------------------------
#[tokio::test]
async fn search_users_endpoint() {
let server = MockServer::start().await;
let c = client(&server.uri());
Mock::given(method("POST")).and(path("/client/v3/user_directory/search"))
.respond_with(ok_json(serde_json::json!({"results": [], "limited": false})))
.mount(&server).await;
let r = c.search_users(&UserSearchRequest {
search_term: "alice".into(), limit: None,
}).await.unwrap();
assert!(r.results.is_empty());
}
// ---------------------------------------------------------------------------
// Media endpoints
// ---------------------------------------------------------------------------
#[tokio::test]
async fn media_upload() {
let server = MockServer::start().await;
let c = client(&server.uri());
// upload_media — success
Mock::given(method("POST")).and(path("/media/v3/upload"))
.respond_with(ok_json(serde_json::json!({"content_uri": "mxc://example/abc"})))
.mount(&server).await;
let r = c.upload_media("image/png", b"fake-png".to_vec()).await.unwrap();
assert_eq!(r.content_uri, "mxc://example/abc");
}
#[tokio::test]
async fn media_upload_http_error() {
let server = MockServer::start().await;
let c = client(&server.uri());
// upload_media — HTTP error branch
Mock::given(method("POST")).and(path("/media/v3/upload"))
.respond_with(ResponseTemplate::new(500).set_body_string("server error"))
.mount(&server).await;
let r = c.upload_media("image/png", b"fake-png".to_vec()).await;
assert!(r.is_err());
}
#[tokio::test]
async fn media_upload_bad_json() {
let server = MockServer::start().await;
let c = client(&server.uri());
// upload_media — invalid JSON response branch
Mock::given(method("POST")).and(path("/media/v3/upload"))
.respond_with(ResponseTemplate::new(200).set_body_string("not-json"))
.mount(&server).await;
let r = c.upload_media("image/png", b"fake-png".to_vec()).await;
assert!(r.is_err());
}
#[tokio::test]
async fn media_download_and_thumbnail() {
let server = MockServer::start().await;
let c = client(&server.uri());
// download_media
Mock::given(method("GET")).and(path_regex("/media/v3/download/.+/.+"))
.respond_with(ResponseTemplate::new(200).set_body_bytes(b"fake-content".to_vec()))
.mount(&server).await;
let r = c.download_media("localhost", "abc123").await.unwrap();
assert_eq!(r.as_ref(), b"fake-content");
// thumbnail — without method param
Mock::given(method("GET")).and(path_regex("/media/v3/thumbnail/.+/.+"))
.respond_with(ResponseTemplate::new(200).set_body_bytes(b"thumb-bytes".to_vec()))
.mount(&server).await;
let r = c.thumbnail("localhost", "abc123", &ThumbnailParams {
width: 64, height: 64, method: None,
}).await.unwrap();
assert_eq!(r.as_ref(), b"thumb-bytes");
// thumbnail — with method param to cover the branch
let r = c.thumbnail("localhost", "abc123", &ThumbnailParams {
width: 64, height: 64, method: Some("crop".into()),
}).await.unwrap();
assert_eq!(r.as_ref(), b"thumb-bytes");
}
// ---------------------------------------------------------------------------
// Device endpoints
// ---------------------------------------------------------------------------
#[tokio::test]
async fn device_endpoints() {
let server = MockServer::start().await;
let c = client(&server.uri());
// list_devices
Mock::given(method("GET")).and(path("/client/v3/devices"))
.respond_with(ok_json(serde_json::json!({"devices": []})))
.mount(&server).await;
let r = c.list_devices().await.unwrap();
assert!(r.devices.is_empty());
// get_device
Mock::given(method("GET")).and(path_regex("/client/v3/devices/.+"))
.respond_with(ok_json(serde_json::json!({"device_id": "D1"})))
.mount(&server).await;
let r = c.get_device("D1").await.unwrap();
assert_eq!(r.device_id, "D1");
// update_device
Mock::given(method("PUT")).and(path_regex("/client/v3/devices/.+"))
.respond_with(ok_empty())
.mount(&server).await;
c.update_device("D1", &UpdateDeviceRequest {
display_name: Some("Phone".into()),
}).await.unwrap();
// delete_device
Mock::given(method("DELETE")).and(path_regex("/client/v3/devices/.+"))
.respond_with(ok_empty())
.mount(&server).await;
c.delete_device("D1", &DeleteDeviceRequest { auth: None }).await.unwrap();
// batch_delete_devices
Mock::given(method("POST")).and(path("/client/v3/delete_devices"))
.respond_with(ok_empty())
.mount(&server).await;
c.batch_delete_devices(&BatchDeleteDevicesRequest {
devices: vec!["D1".into(), "D2".into()], auth: None,
}).await.unwrap();
}
// ---------------------------------------------------------------------------
// E2EE / Keys
// ---------------------------------------------------------------------------
#[tokio::test]
async fn keys_endpoints() {
let server = MockServer::start().await;
let c = client(&server.uri());
// upload_keys
Mock::given(method("POST")).and(path("/client/v3/keys/upload"))
.respond_with(ok_json(serde_json::json!({"one_time_key_counts": {}})))
.mount(&server).await;
let r = c.upload_keys(&KeysUploadRequest {
device_keys: None, one_time_keys: None, fallback_keys: None,
}).await.unwrap();
assert!(r.one_time_key_counts.is_object());
// query_keys
Mock::given(method("POST")).and(path("/client/v3/keys/query"))
.respond_with(ok_json(serde_json::json!({"device_keys": {}})))
.mount(&server).await;
let r = c.query_keys(&KeysQueryRequest {
device_keys: serde_json::json!({}), timeout: None,
}).await.unwrap();
assert!(r.device_keys.is_object());
// claim_keys
Mock::given(method("POST")).and(path("/client/v3/keys/claim"))
.respond_with(ok_json(serde_json::json!({"one_time_keys": {}})))
.mount(&server).await;
let r = c.claim_keys(&KeysClaimRequest {
one_time_keys: serde_json::json!({}), timeout: None,
}).await.unwrap();
assert!(r.one_time_keys.is_object());
}
// ---------------------------------------------------------------------------
// Push endpoints
// ---------------------------------------------------------------------------
#[tokio::test]
async fn push_endpoints() {
let server = MockServer::start().await;
let c = client(&server.uri());
// list_pushers
Mock::given(method("GET")).and(path("/client/v3/pushers"))
.respond_with(ok_json(serde_json::json!({"pushers": []})))
.mount(&server).await;
let r = c.list_pushers().await.unwrap();
assert!(r.pushers.is_empty());
// set_pusher
Mock::given(method("POST")).and(path("/client/v3/pushers/set"))
.respond_with(ok_empty())
.mount(&server).await;
c.set_pusher(&serde_json::json!({})).await.unwrap();
// get_push_rules
Mock::given(method("GET")).and(path("/client/v3/pushrules/"))
.respond_with(ok_json(serde_json::json!({"global": {}})))
.mount(&server).await;
let r = c.get_push_rules().await.unwrap();
assert!(r.global.is_some());
// set_push_rule
Mock::given(method("PUT")).and(path_regex("/client/v3/pushrules/.+/.+/.+"))
.respond_with(ok_empty())
.mount(&server).await;
c.set_push_rule("global", "content", "rule1", &serde_json::json!({})).await.unwrap();
// delete_push_rule
Mock::given(method("DELETE")).and(path_regex("/client/v3/pushrules/.+/.+/.+"))
.respond_with(ok_empty())
.mount(&server).await;
c.delete_push_rule("global", "content", "rule1").await.unwrap();
// get_notifications — no params
Mock::given(method("GET")).and(path("/client/v3/notifications"))
.respond_with(ok_json(serde_json::json!({"notifications": []})))
.mount(&server).await;
let r = c.get_notifications(&NotificationsParams::default()).await.unwrap();
assert!(r.notifications.is_empty());
// get_notifications — all params to cover all branches
let r = c.get_notifications(&NotificationsParams {
from: Some("tok".into()),
limit: Some(5),
only: Some("highlight".into()),
}).await.unwrap();
assert!(r.notifications.is_empty());
}
// ---------------------------------------------------------------------------
// Account data
// ---------------------------------------------------------------------------
#[tokio::test]
async fn account_data_endpoints() {
let server = MockServer::start().await;
let c = client(&server.uri());
// get_account_data
Mock::given(method("GET")).and(path_regex("/client/v3/user/.+/account_data/.+"))
.respond_with(ok_json(serde_json::json!({"key": "val"})))
.mount(&server).await;
let r = c.get_account_data("@u:localhost", "m.some_type").await.unwrap();
assert_eq!(r["key"], "val");
// set_account_data
Mock::given(method("PUT")).and(path_regex("/client/v3/user/.+/account_data/.+"))
.respond_with(ok_empty())
.mount(&server).await;
c.set_account_data("@u:localhost", "m.some_type", &serde_json::json!({"key": "val"})).await.unwrap();
}
// ---------------------------------------------------------------------------
// Tags
// ---------------------------------------------------------------------------
#[tokio::test]
async fn tag_endpoints() {
let server = MockServer::start().await;
let c = client(&server.uri());
// get_tags
Mock::given(method("GET")).and(path_regex("/client/v3/user/.+/rooms/.+/tags$"))
.respond_with(ok_json(serde_json::json!({"tags": {}})))
.mount(&server).await;
let r = c.get_tags("@u:localhost", "!r:localhost").await.unwrap();
assert!(r.tags.is_object());
// set_tag
Mock::given(method("PUT")).and(path_regex("/client/v3/user/.+/rooms/.+/tags/.+"))
.respond_with(ok_empty())
.mount(&server).await;
c.set_tag("@u:localhost", "!r:localhost", "m.favourite", &serde_json::json!({})).await.unwrap();
// delete_tag
Mock::given(method("DELETE")).and(path_regex("/client/v3/user/.+/rooms/.+/tags/.+"))
.respond_with(ok_empty())
.mount(&server).await;
c.delete_tag("@u:localhost", "!r:localhost", "m.favourite").await.unwrap();
}
// ---------------------------------------------------------------------------
// Search
// ---------------------------------------------------------------------------
#[tokio::test]
async fn search_messages_endpoint() {
let server = MockServer::start().await;
let c = client(&server.uri());
Mock::given(method("POST")).and(path("/client/v3/search"))
.respond_with(ok_json(serde_json::json!({"search_categories": {}})))
.mount(&server).await;
let r = c.search_messages(&SearchRequest {
search_categories: serde_json::json!({}),
}).await.unwrap();
assert!(r.search_categories.is_object());
}
// ---------------------------------------------------------------------------
// Filters
// ---------------------------------------------------------------------------
#[tokio::test]
async fn filter_endpoints() {
let server = MockServer::start().await;
let c = client(&server.uri());
// create_filter
Mock::given(method("POST")).and(path_regex("/client/v3/user/.+/filter$"))
.respond_with(ok_json(serde_json::json!({"filter_id": "f1"})))
.mount(&server).await;
let r = c.create_filter("@u:localhost", &serde_json::json!({})).await.unwrap();
assert_eq!(r.filter_id, "f1");
// get_filter
Mock::given(method("GET")).and(path_regex("/client/v3/user/.+/filter/.+"))
.respond_with(ok_json(serde_json::json!({})))
.mount(&server).await;
c.get_filter("@u:localhost", "f1").await.unwrap();
}
// ---------------------------------------------------------------------------
// Spaces
// ---------------------------------------------------------------------------
#[tokio::test]
async fn space_hierarchy_endpoint() {
let server = MockServer::start().await;
let c = client(&server.uri());
Mock::given(method("GET")).and(path_regex("/client/v1/rooms/.+/hierarchy"))
.respond_with(ok_json(serde_json::json!({"rooms": []})))
.mount(&server).await;
// no params
let r = c.get_space_hierarchy("!r:localhost", &SpaceHierarchyParams::default()).await.unwrap();
assert!(r.rooms.is_empty());
// all params to cover all branches
let r = c.get_space_hierarchy("!r:localhost", &SpaceHierarchyParams {
from: Some("tok".into()),
limit: Some(10),
max_depth: Some(3),
suggested_only: Some(true),
}).await.unwrap();
assert!(r.rooms.is_empty());
}
// ---------------------------------------------------------------------------
// Send-to-device
// ---------------------------------------------------------------------------
#[tokio::test]
async fn send_to_device_endpoint() {
let server = MockServer::start().await;
let c = client(&server.uri());
Mock::given(method("PUT")).and(path_regex("/client/v3/sendToDevice/.+/.+"))
.respond_with(ok_empty())
.mount(&server).await;
c.send_to_device("m.room.encrypted", "txn1", &SendToDeviceRequest {
messages: serde_json::json!({}),
}).await.unwrap();
}