529 lines
17 KiB
Rust
529 lines
17 KiB
Rust
|
|
//! Integration tests for sunbeam-sdk service clients.
|
||
|
|
//!
|
||
|
|
//! Requires the test stack running:
|
||
|
|
//! docker compose -f sunbeam-sdk/tests/docker-compose.yml up -d
|
||
|
|
//!
|
||
|
|
//! Run with:
|
||
|
|
//! cargo test -p sunbeam-sdk --features integration --test integration
|
||
|
|
#![cfg(feature = "integration")]
|
||
|
|
|
||
|
|
use sunbeam_sdk::client::{AuthMethod, ServiceClient};
|
||
|
|
|
||
|
|
// ---------------------------------------------------------------------------
|
||
|
|
// Helpers
|
||
|
|
// ---------------------------------------------------------------------------
|
||
|
|
|
||
|
|
/// Poll a URL until it returns 200, or panic after `timeout`.
|
||
|
|
async fn wait_for_healthy(url: &str, timeout: std::time::Duration) {
|
||
|
|
let client = reqwest::Client::new();
|
||
|
|
let deadline = tokio::time::Instant::now() + timeout;
|
||
|
|
loop {
|
||
|
|
if tokio::time::Instant::now() > deadline {
|
||
|
|
panic!("Service at {url} did not become healthy within {timeout:?}");
|
||
|
|
}
|
||
|
|
if let Ok(resp) = client.get(url).send().await {
|
||
|
|
if resp.status().is_success() {
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
tokio::time::sleep(std::time::Duration::from_millis(500)).await;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
const TIMEOUT: std::time::Duration = std::time::Duration::from_secs(60);
|
||
|
|
|
||
|
|
// ---------------------------------------------------------------------------
|
||
|
|
// Kratos
|
||
|
|
// ---------------------------------------------------------------------------
|
||
|
|
|
||
|
|
mod kratos {
|
||
|
|
use super::*;
|
||
|
|
use sunbeam_sdk::identity::KratosClient;
|
||
|
|
|
||
|
|
fn client() -> KratosClient {
|
||
|
|
KratosClient::from_parts("http://localhost:4434".into(), AuthMethod::None)
|
||
|
|
}
|
||
|
|
|
||
|
|
#[tokio::test]
|
||
|
|
async fn health() {
|
||
|
|
wait_for_healthy("http://localhost:4434/health/alive", TIMEOUT).await;
|
||
|
|
let c = client();
|
||
|
|
let status = c.alive().await.unwrap();
|
||
|
|
assert_eq!(status.status, "ok");
|
||
|
|
}
|
||
|
|
|
||
|
|
#[tokio::test]
|
||
|
|
async fn identity_crud() {
|
||
|
|
wait_for_healthy("http://localhost:4434/health/alive", TIMEOUT).await;
|
||
|
|
let c = client();
|
||
|
|
|
||
|
|
// Create
|
||
|
|
let body = sunbeam_sdk::identity::types::CreateIdentityBody {
|
||
|
|
schema_id: "default".into(),
|
||
|
|
traits: serde_json::json!({"email": "integration-test@example.com"}),
|
||
|
|
state: Some("active".into()),
|
||
|
|
metadata_public: None,
|
||
|
|
metadata_admin: None,
|
||
|
|
credentials: None,
|
||
|
|
verifiable_addresses: None,
|
||
|
|
recovery_addresses: None,
|
||
|
|
};
|
||
|
|
let identity = c.create_identity(&body).await.unwrap();
|
||
|
|
assert!(!identity.id.is_empty());
|
||
|
|
let id = identity.id.clone();
|
||
|
|
|
||
|
|
// Get
|
||
|
|
let fetched = c.get_identity(&id).await.unwrap();
|
||
|
|
assert_eq!(fetched.id, id);
|
||
|
|
|
||
|
|
// List
|
||
|
|
let list = c.list_identities(None, None).await.unwrap();
|
||
|
|
assert!(list.iter().any(|i| i.id == id));
|
||
|
|
|
||
|
|
// Update
|
||
|
|
let update = sunbeam_sdk::identity::types::UpdateIdentityBody {
|
||
|
|
schema_id: "default".into(),
|
||
|
|
traits: serde_json::json!({"email": "updated@example.com"}),
|
||
|
|
state: "active".into(),
|
||
|
|
metadata_public: None,
|
||
|
|
metadata_admin: None,
|
||
|
|
credentials: None,
|
||
|
|
};
|
||
|
|
let updated = c.update_identity(&id, &update).await.unwrap();
|
||
|
|
assert_eq!(updated.traits["email"], "updated@example.com");
|
||
|
|
|
||
|
|
// Delete
|
||
|
|
c.delete_identity(&id).await.unwrap();
|
||
|
|
let list = c.list_identities(None, None).await.unwrap();
|
||
|
|
assert!(!list.iter().any(|i| i.id == id));
|
||
|
|
}
|
||
|
|
|
||
|
|
#[tokio::test]
|
||
|
|
async fn schemas() {
|
||
|
|
wait_for_healthy("http://localhost:4434/health/alive", TIMEOUT).await;
|
||
|
|
let c = client();
|
||
|
|
let schemas = c.list_schemas().await.unwrap();
|
||
|
|
assert!(!schemas.is_empty());
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// ---------------------------------------------------------------------------
|
||
|
|
// Hydra
|
||
|
|
// ---------------------------------------------------------------------------
|
||
|
|
|
||
|
|
mod hydra {
|
||
|
|
use super::*;
|
||
|
|
use sunbeam_sdk::auth::hydra::HydraClient;
|
||
|
|
use sunbeam_sdk::auth::hydra::types::OAuth2Client;
|
||
|
|
|
||
|
|
fn client() -> HydraClient {
|
||
|
|
HydraClient::from_parts("http://localhost:4445".into(), AuthMethod::None)
|
||
|
|
}
|
||
|
|
|
||
|
|
#[tokio::test]
|
||
|
|
async fn oauth2_client_crud() {
|
||
|
|
wait_for_healthy("http://localhost:4445/health/alive", TIMEOUT).await;
|
||
|
|
let c = client();
|
||
|
|
|
||
|
|
// Create
|
||
|
|
let body = OAuth2Client {
|
||
|
|
client_name: Some("test-client".into()),
|
||
|
|
grant_types: Some(vec!["authorization_code".into()]),
|
||
|
|
redirect_uris: Some(vec!["http://localhost:9876/callback".into()]),
|
||
|
|
scope: Some("openid email".into()),
|
||
|
|
token_endpoint_auth_method: Some("none".into()),
|
||
|
|
..Default::default()
|
||
|
|
};
|
||
|
|
let created = c.create_client(&body).await.unwrap();
|
||
|
|
let cid = created.client_id.unwrap();
|
||
|
|
assert!(!cid.is_empty());
|
||
|
|
|
||
|
|
// Get
|
||
|
|
let fetched = c.get_client(&cid).await.unwrap();
|
||
|
|
assert_eq!(fetched.client_name, Some("test-client".into()));
|
||
|
|
|
||
|
|
// List
|
||
|
|
let list = c.list_clients(None, None).await.unwrap();
|
||
|
|
assert!(list.iter().any(|cl| cl.client_id.as_deref() == Some(&cid)));
|
||
|
|
|
||
|
|
// Update
|
||
|
|
let mut updated_body = fetched.clone();
|
||
|
|
updated_body.client_name = Some("renamed-client".into());
|
||
|
|
let updated = c.update_client(&cid, &updated_body).await.unwrap();
|
||
|
|
assert_eq!(updated.client_name, Some("renamed-client".into()));
|
||
|
|
|
||
|
|
// Delete
|
||
|
|
c.delete_client(&cid).await.unwrap();
|
||
|
|
let list = c.list_clients(None, None).await.unwrap();
|
||
|
|
assert!(!list.iter().any(|cl| cl.client_id.as_deref() == Some(&cid)));
|
||
|
|
}
|
||
|
|
|
||
|
|
#[tokio::test]
|
||
|
|
async fn token_introspect_inactive() {
|
||
|
|
wait_for_healthy("http://localhost:4445/health/alive", TIMEOUT).await;
|
||
|
|
let c = client();
|
||
|
|
let result = c.introspect_token("bogus-token").await.unwrap();
|
||
|
|
assert!(!result.active);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// ---------------------------------------------------------------------------
|
||
|
|
// Gitea
|
||
|
|
// ---------------------------------------------------------------------------
|
||
|
|
|
||
|
|
mod gitea {
|
||
|
|
use super::*;
|
||
|
|
use sunbeam_sdk::gitea::GiteaClient;
|
||
|
|
|
||
|
|
const ADMIN_USER: &str = "testadmin";
|
||
|
|
const ADMIN_PASS: &str = "testpass123";
|
||
|
|
const ADMIN_EMAIL: &str = "admin@test.local";
|
||
|
|
|
||
|
|
/// Bootstrap admin user + PAT. Returns the PAT string.
|
||
|
|
async fn setup_gitea() -> String {
|
||
|
|
wait_for_healthy("http://localhost:3000/api/v1/version", TIMEOUT).await;
|
||
|
|
let http = reqwest::Client::new();
|
||
|
|
|
||
|
|
// Register user via public API (DISABLE_REGISTRATION=false)
|
||
|
|
let _ = http
|
||
|
|
.post("http://localhost:3000/user/sign_up")
|
||
|
|
.form(&[
|
||
|
|
("user_name", ADMIN_USER),
|
||
|
|
("password", ADMIN_PASS),
|
||
|
|
("retype", ADMIN_PASS),
|
||
|
|
("email", ADMIN_EMAIL),
|
||
|
|
])
|
||
|
|
.send()
|
||
|
|
.await;
|
||
|
|
|
||
|
|
// Create PAT using basic auth
|
||
|
|
let resp = http
|
||
|
|
.post(format!(
|
||
|
|
"http://localhost:3000/api/v1/users/{ADMIN_USER}/tokens"
|
||
|
|
))
|
||
|
|
.basic_auth(ADMIN_USER, Some(ADMIN_PASS))
|
||
|
|
.json(&serde_json::json!({
|
||
|
|
"name": format!("test-{}", std::time::SystemTime::now().duration_since(std::time::UNIX_EPOCH).unwrap().as_millis()),
|
||
|
|
"scopes": ["all"]
|
||
|
|
}))
|
||
|
|
.send()
|
||
|
|
.await
|
||
|
|
.unwrap();
|
||
|
|
|
||
|
|
if !resp.status().is_success() {
|
||
|
|
panic!("PAT creation failed: {}", resp.text().await.unwrap_or_default());
|
||
|
|
}
|
||
|
|
|
||
|
|
let body: serde_json::Value = resp.json().await.unwrap();
|
||
|
|
body["sha1"]
|
||
|
|
.as_str()
|
||
|
|
.or_else(|| body["token"].as_str())
|
||
|
|
.expect("PAT response missing sha1/token field")
|
||
|
|
.to_string()
|
||
|
|
}
|
||
|
|
|
||
|
|
#[tokio::test]
|
||
|
|
async fn repo_crud() {
|
||
|
|
let pat = setup_gitea().await;
|
||
|
|
let c = GiteaClient::from_parts(
|
||
|
|
"http://localhost:3000/api/v1".into(),
|
||
|
|
AuthMethod::Token(pat),
|
||
|
|
);
|
||
|
|
|
||
|
|
// Authenticated user
|
||
|
|
let me = c.get_authenticated_user().await.unwrap();
|
||
|
|
assert_eq!(me.login, ADMIN_USER);
|
||
|
|
|
||
|
|
// Create repo
|
||
|
|
let body = sunbeam_sdk::gitea::types::CreateRepoBody {
|
||
|
|
name: "integration-test".into(),
|
||
|
|
description: Some("test repo".into()),
|
||
|
|
auto_init: Some(true),
|
||
|
|
..Default::default()
|
||
|
|
};
|
||
|
|
let repo = c.create_user_repo(&body).await.unwrap();
|
||
|
|
assert_eq!(repo.name, "integration-test");
|
||
|
|
|
||
|
|
// Get repo
|
||
|
|
let fetched = c.get_repo(ADMIN_USER, "integration-test").await.unwrap();
|
||
|
|
assert_eq!(fetched.full_name, format!("{ADMIN_USER}/integration-test"));
|
||
|
|
|
||
|
|
// Search repos
|
||
|
|
let results = c.search_repos("integration", None).await.unwrap();
|
||
|
|
assert!(!results.data.is_empty());
|
||
|
|
|
||
|
|
// Delete repo
|
||
|
|
c.delete_repo(ADMIN_USER, "integration-test").await.unwrap();
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// ---------------------------------------------------------------------------
|
||
|
|
// OpenSearch
|
||
|
|
// ---------------------------------------------------------------------------
|
||
|
|
|
||
|
|
mod opensearch {
|
||
|
|
use super::*;
|
||
|
|
use sunbeam_sdk::search::OpenSearchClient;
|
||
|
|
|
||
|
|
fn client() -> OpenSearchClient {
|
||
|
|
OpenSearchClient::from_parts("http://localhost:9200".into(), AuthMethod::None)
|
||
|
|
}
|
||
|
|
|
||
|
|
#[tokio::test]
|
||
|
|
async fn cluster_health() {
|
||
|
|
wait_for_healthy("http://localhost:9200/_cluster/health", TIMEOUT).await;
|
||
|
|
let c = client();
|
||
|
|
let health = c.cluster_health().await.unwrap();
|
||
|
|
assert!(!health.cluster_name.is_empty());
|
||
|
|
}
|
||
|
|
|
||
|
|
#[tokio::test]
|
||
|
|
async fn document_crud() {
|
||
|
|
wait_for_healthy("http://localhost:9200/_cluster/health", TIMEOUT).await;
|
||
|
|
let c = client();
|
||
|
|
|
||
|
|
let idx = "integration-test";
|
||
|
|
|
||
|
|
// Create index
|
||
|
|
let _ = c
|
||
|
|
.create_index(idx, &serde_json::json!({"settings": {"number_of_shards": 1, "number_of_replicas": 0}}))
|
||
|
|
.await
|
||
|
|
.unwrap();
|
||
|
|
|
||
|
|
// Index a document
|
||
|
|
let doc = serde_json::json!({"title": "Hello", "body": "World"});
|
||
|
|
let resp = c.index_doc(idx, "doc-1", &doc).await.unwrap();
|
||
|
|
assert_eq!(resp.result.as_deref(), Some("created"));
|
||
|
|
|
||
|
|
// Refresh to make searchable (use a raw reqwest call)
|
||
|
|
let _ = reqwest::Client::new()
|
||
|
|
.post(format!("http://localhost:9200/{idx}/_refresh"))
|
||
|
|
.send()
|
||
|
|
.await;
|
||
|
|
|
||
|
|
// Get document
|
||
|
|
let got = c.get_doc(idx, "doc-1").await.unwrap();
|
||
|
|
assert_eq!(got.source.as_ref().unwrap()["title"], "Hello");
|
||
|
|
|
||
|
|
// Search
|
||
|
|
let query = serde_json::json!({"query": {"match_all": {}}});
|
||
|
|
let results = c.search(idx, &query).await.unwrap();
|
||
|
|
assert!(results.hits.total.value > 0);
|
||
|
|
|
||
|
|
// Delete document
|
||
|
|
let del = c.delete_doc(idx, "doc-1").await.unwrap();
|
||
|
|
assert_eq!(del.result.as_deref(), Some("deleted"));
|
||
|
|
|
||
|
|
// Delete index
|
||
|
|
c.delete_index(idx).await.unwrap();
|
||
|
|
}
|
||
|
|
|
||
|
|
#[tokio::test]
|
||
|
|
async fn cat_indices() {
|
||
|
|
wait_for_healthy("http://localhost:9200/_cluster/health", TIMEOUT).await;
|
||
|
|
let c = client();
|
||
|
|
let _indices = c.cat_indices().await.unwrap();
|
||
|
|
// Just verify it parses without error
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// ---------------------------------------------------------------------------
|
||
|
|
// Prometheus
|
||
|
|
// ---------------------------------------------------------------------------
|
||
|
|
|
||
|
|
mod prometheus {
|
||
|
|
use super::*;
|
||
|
|
use sunbeam_sdk::monitoring::PrometheusClient;
|
||
|
|
|
||
|
|
fn client() -> PrometheusClient {
|
||
|
|
PrometheusClient::from_parts("http://localhost:9090/api/v1".into(), AuthMethod::None)
|
||
|
|
}
|
||
|
|
|
||
|
|
#[tokio::test]
|
||
|
|
async fn query_up() {
|
||
|
|
wait_for_healthy("http://localhost:9090/api/v1/status/buildinfo", TIMEOUT).await;
|
||
|
|
let c = client();
|
||
|
|
|
||
|
|
let result = c.build_info().await.unwrap();
|
||
|
|
assert_eq!(result.status, "success");
|
||
|
|
|
||
|
|
let result = c.query("up", None).await.unwrap();
|
||
|
|
assert_eq!(result.status, "success");
|
||
|
|
}
|
||
|
|
|
||
|
|
#[tokio::test]
|
||
|
|
async fn labels() {
|
||
|
|
wait_for_healthy("http://localhost:9090/api/v1/status/buildinfo", TIMEOUT).await;
|
||
|
|
let c = client();
|
||
|
|
|
||
|
|
let result = c.labels(None, None).await.unwrap();
|
||
|
|
assert_eq!(result.status, "success");
|
||
|
|
}
|
||
|
|
|
||
|
|
#[tokio::test]
|
||
|
|
async fn targets() {
|
||
|
|
wait_for_healthy("http://localhost:9090/api/v1/status/buildinfo", TIMEOUT).await;
|
||
|
|
let c = client();
|
||
|
|
|
||
|
|
let result = c.targets().await.unwrap();
|
||
|
|
assert_eq!(result.status, "success");
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// ---------------------------------------------------------------------------
|
||
|
|
// Loki
|
||
|
|
// ---------------------------------------------------------------------------
|
||
|
|
|
||
|
|
mod loki {
|
||
|
|
use super::*;
|
||
|
|
use sunbeam_sdk::monitoring::LokiClient;
|
||
|
|
|
||
|
|
fn client() -> LokiClient {
|
||
|
|
LokiClient::from_parts("http://localhost:3100/loki/api/v1".into(), AuthMethod::None)
|
||
|
|
}
|
||
|
|
|
||
|
|
#[tokio::test]
|
||
|
|
async fn ready_and_labels() {
|
||
|
|
wait_for_healthy("http://localhost:3100/ready", TIMEOUT).await;
|
||
|
|
let c = client();
|
||
|
|
|
||
|
|
let _status = c.ready().await.unwrap();
|
||
|
|
|
||
|
|
// Loki's ring needs time to settle — retry labels a few times
|
||
|
|
for i in 0..10 {
|
||
|
|
match c.labels(None, None).await {
|
||
|
|
Ok(labels) => {
|
||
|
|
assert_eq!(labels.status, "success");
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
Err(_) if i < 9 => {
|
||
|
|
tokio::time::sleep(std::time::Duration::from_secs(2)).await;
|
||
|
|
}
|
||
|
|
Err(e) => panic!("loki labels failed after retries: {e}"),
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// ---------------------------------------------------------------------------
|
||
|
|
// Grafana
|
||
|
|
// ---------------------------------------------------------------------------
|
||
|
|
|
||
|
|
mod grafana {
|
||
|
|
use super::*;
|
||
|
|
use sunbeam_sdk::monitoring::GrafanaClient;
|
||
|
|
|
||
|
|
fn client() -> GrafanaClient {
|
||
|
|
use base64::Engine;
|
||
|
|
let creds = base64::engine::general_purpose::STANDARD.encode("admin:admin");
|
||
|
|
GrafanaClient::from_parts(
|
||
|
|
"http://localhost:3001/api".into(),
|
||
|
|
AuthMethod::Header {
|
||
|
|
name: "Authorization",
|
||
|
|
value: format!("Basic {creds}"),
|
||
|
|
},
|
||
|
|
)
|
||
|
|
}
|
||
|
|
|
||
|
|
#[tokio::test]
|
||
|
|
async fn org() {
|
||
|
|
wait_for_healthy("http://localhost:3001/api/health", TIMEOUT).await;
|
||
|
|
let c = client();
|
||
|
|
let org = c.get_current_org().await.unwrap();
|
||
|
|
assert!(!org.name.is_empty());
|
||
|
|
}
|
||
|
|
|
||
|
|
#[tokio::test]
|
||
|
|
async fn folder_and_dashboard_crud() {
|
||
|
|
wait_for_healthy("http://localhost:3001/api/health", TIMEOUT).await;
|
||
|
|
let c = client();
|
||
|
|
|
||
|
|
// Create folder
|
||
|
|
let folder = c
|
||
|
|
.create_folder(&serde_json::json!({"title": "Integration Tests"}))
|
||
|
|
.await
|
||
|
|
.unwrap();
|
||
|
|
let folder_uid = folder.uid.clone();
|
||
|
|
assert!(!folder_uid.is_empty());
|
||
|
|
|
||
|
|
// Create dashboard in folder
|
||
|
|
let dash_body = serde_json::json!({
|
||
|
|
"dashboard": {
|
||
|
|
"title": "Test Dashboard",
|
||
|
|
"panels": [],
|
||
|
|
"schemaVersion": 30,
|
||
|
|
},
|
||
|
|
"folderUid": folder_uid,
|
||
|
|
"overwrite": false
|
||
|
|
});
|
||
|
|
let dash = c.create_dashboard(&dash_body).await.unwrap();
|
||
|
|
let dash_uid = dash.uid.clone().unwrap();
|
||
|
|
|
||
|
|
// List dashboards
|
||
|
|
let list = c.list_dashboards().await.unwrap();
|
||
|
|
assert!(list.iter().any(|d| d.uid == dash_uid));
|
||
|
|
|
||
|
|
// Delete dashboard
|
||
|
|
c.delete_dashboard(&dash_uid).await.unwrap();
|
||
|
|
|
||
|
|
// Delete folder
|
||
|
|
c.delete_folder(&folder_uid).await.unwrap();
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// ---------------------------------------------------------------------------
|
||
|
|
// LiveKit
|
||
|
|
// ---------------------------------------------------------------------------
|
||
|
|
|
||
|
|
mod livekit {
|
||
|
|
use super::*;
|
||
|
|
use sunbeam_sdk::media::LiveKitClient;
|
||
|
|
use sunbeam_sdk::media::types::VideoGrants;
|
||
|
|
|
||
|
|
fn client() -> LiveKitClient {
|
||
|
|
let grants = VideoGrants {
|
||
|
|
room_create: Some(true),
|
||
|
|
room_list: Some(true),
|
||
|
|
room_join: Some(true),
|
||
|
|
..Default::default()
|
||
|
|
};
|
||
|
|
let token =
|
||
|
|
LiveKitClient::generate_access_token("devkey", "devsecret", "test-user", &grants, 300)
|
||
|
|
.expect("JWT generation failed");
|
||
|
|
LiveKitClient::from_parts("http://localhost:7880".into(), AuthMethod::Bearer(token))
|
||
|
|
}
|
||
|
|
|
||
|
|
#[tokio::test]
|
||
|
|
async fn room_crud() {
|
||
|
|
wait_for_healthy("http://localhost:7880", TIMEOUT).await;
|
||
|
|
let c = client();
|
||
|
|
|
||
|
|
// List rooms (empty initially)
|
||
|
|
let rooms = c
|
||
|
|
.list_rooms()
|
||
|
|
.await
|
||
|
|
.unwrap();
|
||
|
|
let initial_count = rooms.rooms.len();
|
||
|
|
|
||
|
|
// Create room
|
||
|
|
let room = c
|
||
|
|
.create_room(&serde_json::json!({"name": "integration-test-room"}))
|
||
|
|
.await
|
||
|
|
.unwrap();
|
||
|
|
assert_eq!(room.name, "integration-test-room");
|
||
|
|
|
||
|
|
// List rooms (should have one more)
|
||
|
|
let rooms = c.list_rooms().await.unwrap();
|
||
|
|
assert_eq!(rooms.rooms.len(), initial_count + 1);
|
||
|
|
|
||
|
|
// Delete room
|
||
|
|
c.delete_room(&serde_json::json!({"room": "integration-test-room"}))
|
||
|
|
.await
|
||
|
|
.unwrap();
|
||
|
|
|
||
|
|
// Verify deleted
|
||
|
|
let rooms = c.list_rooms().await.unwrap();
|
||
|
|
assert_eq!(rooms.rooms.len(), initial_count);
|
||
|
|
}
|
||
|
|
}
|