diff --git a/backend/tests/apps.rs b/backend/tests/apps.rs index 08b29d71fa..1fce48a373 100644 --- a/backend/tests/apps.rs +++ b/backend/tests/apps.rs @@ -122,6 +122,26 @@ async fn test_app_endpoints(db: Pool) -> anyhow::Result<()> { let history = resp.json::>().await?; assert!(!history.is_empty()); + // --- get by version --- + let version = &history[0]["version"]; + let resp = authed(client().get(format!( + "http://localhost:{port}/api/w/test-workspace/apps/get/v/{version}" + ))) + .send() + .await + .unwrap(); + assert_eq!(resp.status(), 200); + + // --- custom_path_exists --- + let resp = authed(client().get(format!( + "http://localhost:{port}/api/w/test-workspace/apps/custom_path_exists/nonexistent" + ))) + .send() + .await + .unwrap(); + assert_eq!(resp.status(), 200); + assert_eq!(resp.json::().await?, false); + // --- get_latest_version --- let resp = authed_get(port, "get_latest_version", "u/test-user/test_app").await; assert_eq!(resp.status(), 200); diff --git a/backend/tests/drafts.rs b/backend/tests/drafts.rs new file mode 100644 index 0000000000..e479d0b97c --- /dev/null +++ b/backend/tests/drafts.rs @@ -0,0 +1,106 @@ +use serde_json::json; +use sqlx::{Pool, Postgres}; + +mod common; +use common::*; + +fn client() -> reqwest::Client { + reqwest::Client::new() +} + +fn authed(builder: reqwest::RequestBuilder) -> reqwest::RequestBuilder { + builder.header("Authorization", "Bearer SECRET_TOKEN") +} + +#[sqlx::test(fixtures("base"))] +async fn test_draft_endpoints(db: Pool) -> anyhow::Result<()> { + initialize_tracing().await; + let server = ApiServer::start(db.clone()).await?; + let port = server.addr.port(); + let base = format!("http://localhost:{port}/api/w/test-workspace/drafts"); + + // create a script first so the draft has a valid path + let resp = authed(client().post(format!( + "http://localhost:{port}/api/w/test-workspace/scripts/create" + ))) + .json(&json!({ + "path": "u/test-user/draft_script", + "summary": "Script for draft test", + "description": "", + "content": "export async function main() { return 1; }", + "language": "deno", + "schema": { + "$schema": "https://json-schema.org/draft/2020-12/schema", + "type": "object", + "properties": {}, + "required": [] + } + })) + .send() + .await + .unwrap(); + assert_eq!(resp.status(), 201, "create script: {}", resp.text().await?); + + // --- create draft --- + let resp = authed(client().post(format!("{base}/create"))) + .json(&json!({ + "path": "u/test-user/draft_script", + "typ": "script", + "value": { + "content": "export async function main() { return 2; }", + "language": "deno" + } + })) + .send() + .await + .unwrap(); + assert_eq!(resp.status(), 201, "create draft: {}", resp.text().await?); + + // verify draft exists via script get/draft endpoint + let resp = authed(client().get(format!( + "http://localhost:{port}/api/w/test-workspace/scripts/get/draft/u/test-user/draft_script" + ))) + .send() + .await + .unwrap(); + assert_eq!(resp.status(), 200); + let body = resp.json::().await?; + assert!(body["draft"].is_object(), "expected draft to be present"); + + // --- update draft (create with same path overwrites) --- + let resp = authed(client().post(format!("{base}/create"))) + .json(&json!({ + "path": "u/test-user/draft_script", + "typ": "script", + "value": { + "content": "export async function main() { return 3; }", + "language": "deno" + } + })) + .send() + .await + .unwrap(); + assert_eq!(resp.status(), 201); + + // --- delete draft --- + let resp = authed(client().delete(format!( + "{base}/delete/script/u/test-user/draft_script" + ))) + .send() + .await + .unwrap(); + assert_eq!(resp.status(), 200); + + // verify draft is gone + let resp = authed(client().get(format!( + "http://localhost:{port}/api/w/test-workspace/scripts/get/draft/u/test-user/draft_script" + ))) + .send() + .await + .unwrap(); + assert_eq!(resp.status(), 200); + let body = resp.json::().await?; + assert!(body["draft"].is_null(), "expected draft to be deleted"); + + Ok(()) +} diff --git a/backend/tests/fixtures/base.sql b/backend/tests/fixtures/base.sql index 5e96b75b99..2dbcf6b609 100644 --- a/backend/tests/fixtures/base.sql +++ b/backend/tests/fixtures/base.sql @@ -15,6 +15,9 @@ INSERT INTO workspace_key(workspace_id, kind, key) VALUES INSERT INTO workspace_settings (workspace_id) VALUES ('test-workspace'); +INSERT INTO password(email, password_hash, login_type, super_admin, verified, name) + VALUES ('test@windmill.dev', 'not-a-real-hash', 'password', true, true, 'Test User'); + insert INTO token(token, email, label, super_admin) VALUES ('SECRET_TOKEN', 'test@windmill.dev', 'test token', true); GRANT ALL PRIVILEGES ON TABLE workspace_key TO windmill_admin; diff --git a/backend/tests/flows.rs b/backend/tests/flows.rs index d1eb1f9e21..cc8b1e30b0 100644 --- a/backend/tests/flows.rs +++ b/backend/tests/flows.rs @@ -135,6 +135,18 @@ async fn test_flow_endpoints(db: Pool) -> anyhow::Result<()> { let resp = authed_get(port, "deployment_status/p", "u/test-user/test_flow").await; assert_eq!(resp.status(), 200); + // --- get by version --- + let version = &history[0]["id"]; + let resp = authed(client().get(format!( + "http://localhost:{port}/api/w/test-workspace/flows/get/v/{version}" + ))) + .send() + .await + .unwrap(); + assert_eq!(resp.status(), 200); + let body = resp.json::().await?; + assert_eq!(body["path"], "u/test-user/test_flow"); + // --- update --- let resp = authed(client().post(flow_url(port, "update", "u/test-user/test_flow"))) .json(&new_flow("u/test-user/test_flow", "Updated flow")) diff --git a/backend/tests/folders.rs b/backend/tests/folders.rs new file mode 100644 index 0000000000..ee27a0e5c5 --- /dev/null +++ b/backend/tests/folders.rs @@ -0,0 +1,157 @@ +use serde_json::json; +use sqlx::{Pool, Postgres}; + +mod common; +use common::*; + +fn folder_url(port: u16, endpoint: &str, name: &str) -> String { + format!("http://localhost:{port}/api/w/test-workspace/folders/{endpoint}/{name}") +} + +fn client() -> reqwest::Client { + reqwest::Client::new() +} + +fn authed(builder: reqwest::RequestBuilder) -> reqwest::RequestBuilder { + builder.header("Authorization", "Bearer SECRET_TOKEN") +} + +#[sqlx::test(fixtures("base"))] +async fn test_folder_endpoints(db: Pool) -> anyhow::Result<()> { + initialize_tracing().await; + let server = ApiServer::start(db.clone()).await?; + let port = server.addr.port(); + let base = format!("http://localhost:{port}/api/w/test-workspace/folders"); + + // --- create --- + let resp = authed(client().post(format!("{base}/create"))) + .json(&json!({ + "name": "test_folder", + "summary": "A test folder", + "display_name": "Test Folder" + })) + .send() + .await + .unwrap(); + assert_eq!(resp.status(), 200, "create: {}", resp.text().await?); + + // create second folder + let resp = authed(client().post(format!("{base}/create"))) + .json(&json!({ + "name": "another_folder", + "summary": "Another folder" + })) + .send() + .await + .unwrap(); + assert_eq!(resp.status(), 200, "create another: {}", resp.text().await?); + + // --- exists --- + let resp = authed(client().get(folder_url(port, "exists", "test_folder"))) + .send() + .await + .unwrap(); + assert_eq!(resp.status(), 200); + assert_eq!(resp.json::().await?, true); + + let resp = authed(client().get(folder_url(port, "exists", "nonexistent"))) + .send() + .await + .unwrap(); + assert_eq!(resp.status(), 200); + assert_eq!(resp.json::().await?, false); + + // --- get --- + let resp = authed(client().get(folder_url(port, "get", "test_folder"))) + .send() + .await + .unwrap(); + assert_eq!(resp.status(), 200); + let body = resp.json::().await?; + assert_eq!(body["name"], "test_folder"); + assert_eq!(body["summary"], "A test folder"); + + // --- list --- + let resp = authed(client().get(format!("{base}/list"))) + .send() + .await + .unwrap(); + assert_eq!(resp.status(), 200); + let list = resp.json::>().await?; + assert!(list.iter().any(|f| f["name"] == "test_folder")); + + // --- listnames --- + let resp = authed(client().get(format!("{base}/listnames"))) + .send() + .await + .unwrap(); + assert_eq!(resp.status(), 200); + let names = resp.json::>().await?; + assert!(names.contains(&"test_folder".to_string())); + + // --- update --- + let resp = authed(client().post(folder_url(port, "update", "test_folder"))) + .json(&json!({"summary": "Updated summary"})) + .send() + .await + .unwrap(); + assert_eq!(resp.status(), 200); + + let resp = authed(client().get(folder_url(port, "get", "test_folder"))) + .send() + .await + .unwrap(); + let body = resp.json::().await?; + assert_eq!(body["summary"], "Updated summary"); + + // --- addowner --- + let resp = authed(client().post(folder_url(port, "addowner", "test_folder"))) + .json(&json!({"owner": "u/test-user"})) + .send() + .await + .unwrap(); + assert_eq!(resp.status(), 200, "addowner: {}", resp.text().await?); + + // verify ownership + let resp = authed(client().get(folder_url(port, "get", "test_folder"))) + .send() + .await + .unwrap(); + let body = resp.json::().await?; + let owners = body["owners"].as_array().unwrap(); + assert!( + owners.iter().any(|o| o.as_str() == Some("u/test-user")), + "expected u/test-user in owners, got: {:?}", + owners + ); + + // --- removeowner --- + let resp = authed(client().post(folder_url(port, "removeowner", "test_folder"))) + .json(&json!({"owner": "u/test-user"})) + .send() + .await + .unwrap(); + assert_eq!(resp.status(), 200); + + // --- getusage --- + let resp = authed(client().get(folder_url(port, "getusage", "test_folder"))) + .send() + .await + .unwrap(); + assert_eq!(resp.status(), 200); + + // --- delete --- + let resp = authed(client().delete(folder_url(port, "delete", "another_folder"))) + .send() + .await + .unwrap(); + assert_eq!(resp.status(), 200); + + let resp = authed(client().get(folder_url(port, "exists", "another_folder"))) + .send() + .await + .unwrap(); + assert_eq!(resp.json::().await?, false); + + Ok(()) +} diff --git a/backend/tests/groups.rs b/backend/tests/groups.rs new file mode 100644 index 0000000000..c996c4c629 --- /dev/null +++ b/backend/tests/groups.rs @@ -0,0 +1,147 @@ +use serde_json::json; +use sqlx::{Pool, Postgres}; + +mod common; +use common::*; + +fn group_url(port: u16, endpoint: &str, name: &str) -> String { + format!("http://localhost:{port}/api/w/test-workspace/groups/{endpoint}/{name}") +} + +fn client() -> reqwest::Client { + reqwest::Client::new() +} + +fn authed(builder: reqwest::RequestBuilder) -> reqwest::RequestBuilder { + builder.header("Authorization", "Bearer SECRET_TOKEN") +} + +#[sqlx::test(fixtures("base"))] +async fn test_group_endpoints(db: Pool) -> anyhow::Result<()> { + initialize_tracing().await; + let server = ApiServer::start(db.clone()).await?; + let port = server.addr.port(); + let base = format!("http://localhost:{port}/api/w/test-workspace/groups"); + + // --- create --- + let resp = authed(client().post(format!("{base}/create"))) + .json(&json!({ + "name": "test_group", + "summary": "A test group" + })) + .send() + .await + .unwrap(); + assert_eq!(resp.status(), 200, "create: {}", resp.text().await?); + + // create second group + let resp = authed(client().post(format!("{base}/create"))) + .json(&json!({ + "name": "another_group", + "summary": "Another group" + })) + .send() + .await + .unwrap(); + assert_eq!(resp.status(), 200, "create another: {}", resp.text().await?); + + // create duplicate -> error + let resp = authed(client().post(format!("{base}/create"))) + .json(&json!({ + "name": "test_group", + "summary": "Duplicate" + })) + .send() + .await + .unwrap(); + assert_eq!(resp.status(), 400); + + // --- get --- + let resp = authed(client().get(group_url(port, "get", "test_group"))) + .send() + .await + .unwrap(); + assert_eq!(resp.status(), 200); + let body = resp.json::().await?; + assert_eq!(body["name"], "test_group"); + assert_eq!(body["summary"], "A test group"); + + // --- list --- + let resp = authed(client().get(format!("{base}/list"))) + .send() + .await + .unwrap(); + assert_eq!(resp.status(), 200); + let list = resp.json::>().await?; + assert!(list.iter().any(|g| g["name"] == "test_group")); + + // --- listnames --- + let resp = authed(client().get(format!("{base}/listnames"))) + .send() + .await + .unwrap(); + assert_eq!(resp.status(), 200); + let names = resp.json::>().await?; + assert!(names.contains(&"test_group".to_string())); + + // --- update --- + let resp = authed(client().post(group_url(port, "update", "test_group"))) + .json(&json!({"summary": "Updated summary"})) + .send() + .await + .unwrap(); + assert_eq!(resp.status(), 200); + + let resp = authed(client().get(group_url(port, "get", "test_group"))) + .send() + .await + .unwrap(); + let body = resp.json::().await?; + assert_eq!(body["summary"], "Updated summary"); + + // --- adduser --- + let resp = authed(client().post(group_url(port, "adduser", "test_group"))) + .json(&json!({"username": "test-user"})) + .send() + .await + .unwrap(); + assert_eq!(resp.status(), 200, "adduser: {}", resp.text().await?); + + // verify membership + let resp = authed(client().get(group_url(port, "get", "test_group"))) + .send() + .await + .unwrap(); + let body = resp.json::().await?; + let members = body["members"].as_array().unwrap(); + assert!( + members.iter().any(|m| m.as_str() == Some("test-user")), + "expected test-user in members, got: {:?}", + members + ); + + // --- removeuser --- + let resp = authed(client().post(group_url(port, "removeuser", "test_group"))) + .json(&json!({"username": "test-user"})) + .send() + .await + .unwrap(); + assert_eq!(resp.status(), 200); + + // --- delete --- + let resp = authed(client().delete(group_url(port, "delete", "another_group"))) + .send() + .await + .unwrap(); + assert_eq!(resp.status(), 200); + + // verify deleted - get should 404 or the group shouldn't appear in list + let resp = authed(client().get(format!("{base}/listnames"))) + .send() + .await + .unwrap(); + let names = resp.json::>().await?; + assert!(!names.contains(&"another_group".to_string())); + + Ok(()) +} diff --git a/backend/tests/resources.rs b/backend/tests/resources.rs index 77e215af54..d44d21b4ed 100644 --- a/backend/tests/resources.rs +++ b/backend/tests/resources.rs @@ -183,6 +183,14 @@ async fn test_resource_endpoints(db: Pool) -> anyhow::Result<()> { let list = resp.json::>().await?; assert!(!list.is_empty()); + // --- list_names --- + let resp = authed(client().get(format!("{base}/list_names/object"))) + .send() + .await + .unwrap(); + assert_eq!(resp.status(), 200); + resp.json::>().await?; + // --- create --- let resp = authed(client().post(format!("{base}/create"))) .json(&json!({ diff --git a/backend/tests/schedules.rs b/backend/tests/schedules.rs new file mode 100644 index 0000000000..a7d594a721 --- /dev/null +++ b/backend/tests/schedules.rs @@ -0,0 +1,192 @@ +use serde_json::json; +use sqlx::{Pool, Postgres}; + +mod common; +use common::*; + +fn schedule_url(port: u16, endpoint: &str, path: &str) -> String { + format!("http://localhost:{port}/api/w/test-workspace/schedules/{endpoint}/{path}") +} + +fn client() -> reqwest::Client { + reqwest::Client::new() +} + +fn authed(builder: reqwest::RequestBuilder) -> reqwest::RequestBuilder { + builder.header("Authorization", "Bearer SECRET_TOKEN") +} + +async fn authed_get(port: u16, endpoint: &str, path: &str) -> reqwest::Response { + authed(client().get(schedule_url(port, endpoint, path))) + .send() + .await + .unwrap() +} + +#[sqlx::test(fixtures("base"))] +async fn test_schedule_endpoints(db: Pool) -> anyhow::Result<()> { + initialize_tracing().await; + let server = ApiServer::start(db.clone()).await?; + let port = server.addr.port(); + let base = format!("http://localhost:{port}/api/w/test-workspace/schedules"); + + // create a script for the schedule to reference + let resp = authed(client().post(format!( + "http://localhost:{port}/api/w/test-workspace/scripts/create" + ))) + .json(&json!({ + "path": "u/test-user/scheduled_script", + "summary": "Scheduled script", + "description": "", + "content": "export async function main() { return 1; }", + "language": "deno", + "schema": { + "$schema": "https://json-schema.org/draft/2020-12/schema", + "type": "object", + "properties": {}, + "required": [] + } + })) + .send() + .await + .unwrap(); + assert_eq!(resp.status(), 201, "create script: {}", resp.text().await?); + + // --- create --- + let resp = authed(client().post(format!("{base}/create"))) + .json(&json!({ + "path": "u/test-user/test_schedule", + "schedule": "0 0 */6 * * *", + "timezone": "UTC", + "script_path": "u/test-user/scheduled_script", + "is_flow": false, + "enabled": false + })) + .send() + .await + .unwrap(); + assert_eq!(resp.status(), 200, "create: {}", resp.text().await?); + + // create second schedule + let resp = authed(client().post(format!("{base}/create"))) + .json(&json!({ + "path": "u/test-user/another_schedule", + "schedule": "0 0 0 * * *", + "timezone": "America/New_York", + "script_path": "u/test-user/scheduled_script", + "is_flow": false, + "enabled": false + })) + .send() + .await + .unwrap(); + assert_eq!(resp.status(), 200, "create another: {}", resp.text().await?); + + // --- exists --- + let resp = authed_get(port, "exists", "u/test-user/test_schedule").await; + assert_eq!(resp.status(), 200); + assert_eq!(resp.json::().await?, true); + + let resp = authed_get(port, "exists", "u/test-user/nonexistent").await; + assert_eq!(resp.status(), 200); + assert_eq!(resp.json::().await?, false); + + // --- get --- + let resp = authed_get(port, "get", "u/test-user/test_schedule").await; + assert_eq!(resp.status(), 200); + let body = resp.json::().await?; + assert_eq!(body["path"], "u/test-user/test_schedule"); + assert_eq!(body["schedule"], "0 0 */6 * * *"); + assert_eq!(body["timezone"], "UTC"); + assert_eq!(body["script_path"], "u/test-user/scheduled_script"); + assert_eq!(body["is_flow"], false); + assert_eq!(body["enabled"], false); + + // --- list --- + let resp = authed(client().get(format!("{base}/list"))) + .send() + .await + .unwrap(); + assert_eq!(resp.status(), 200); + let list = resp.json::>().await?; + assert!( + list.len() >= 2, + "expected at least 2 schedules, got {}", + list.len() + ); + assert!(list.iter().any(|s| s["path"] == "u/test-user/test_schedule")); + + // --- list_with_jobs --- + let resp = authed(client().get(format!("{base}/list_with_jobs"))) + .send() + .await + .unwrap(); + assert_eq!(resp.status(), 200); + let list = resp.json::>().await?; + assert!(!list.is_empty()); + + // --- update --- + let resp = authed(client().post(schedule_url( + port, + "update", + "u/test-user/test_schedule", + ))) + .json(&json!({ + "schedule": "0 0 */12 * * *", + "timezone": "Europe/Paris" + })) + .send() + .await + .unwrap(); + assert_eq!(resp.status(), 200, "update: {}", resp.text().await?); + + // verify update + let resp = authed_get(port, "get", "u/test-user/test_schedule").await; + let body = resp.json::().await?; + assert_eq!(body["schedule"], "0 0 */12 * * *"); + assert_eq!(body["timezone"], "Europe/Paris"); + + // --- setenabled --- + let resp = authed(client().post(schedule_url( + port, + "setenabled", + "u/test-user/test_schedule", + ))) + .json(&json!({"enabled": true})) + .send() + .await + .unwrap(); + assert_eq!(resp.status(), 200); + + let resp = authed_get(port, "get", "u/test-user/test_schedule").await; + let body = resp.json::().await?; + assert_eq!(body["enabled"], true); + + // disable it back + let resp = authed(client().post(schedule_url( + port, + "setenabled", + "u/test-user/test_schedule", + ))) + .json(&json!({"enabled": false})) + .send() + .await + .unwrap(); + assert_eq!(resp.status(), 200); + + // --- delete --- + let resp = authed(client().delete(schedule_url( + port, + "delete", + "u/test-user/another_schedule", + ))) + .send() + .await + .unwrap(); + assert_eq!(resp.status(), 200); + + let resp = authed_get(port, "exists", "u/test-user/another_schedule").await; + assert_eq!(resp.json::().await?, false); + + Ok(()) +} diff --git a/backend/tests/scripts.rs b/backend/tests/scripts.rs index 873320d089..84fa85665a 100644 --- a/backend/tests/scripts.rs +++ b/backend/tests/scripts.rs @@ -205,7 +205,7 @@ async fn test_script_endpoints(db: Pool) -> anyhow::Result<()> { history.len() ); - // --- archive --- + // --- archive by path --- let resp = authed(client().post(script_url( port, "archive/p", @@ -221,19 +221,34 @@ async fn test_script_endpoints(db: Pool) -> anyhow::Result<()> { assert_eq!(resp.status(), 200); let body = resp.json::().await?; assert_eq!(body["archived"], true); + let another_hash = body["hash"].as_str().unwrap().to_string(); - // --- delete --- + // --- archive by hash --- + let resp = authed(client().post(script_url(port, "archive/h", &another_hash))) + .send() + .await + .unwrap(); + assert_eq!(resp.status(), 200); + + // --- delete by hash --- + let resp = authed(client().post(script_url(port, "delete/h", &another_hash))) + .send() + .await + .unwrap(); + assert_eq!(resp.status(), 200); + + // --- delete by path --- let resp = authed(client().post(script_url( port, "delete/p", - "u/test-user/another_script", + "u/test-user/test_script", ))) .send() .await .unwrap(); assert_eq!(resp.status(), 200); - let resp = authed_get(port, "exists/p", "u/test-user/another_script").await; + let resp = authed_get(port, "exists/p", "u/test-user/test_script").await; assert_eq!(resp.json::().await?, false); Ok(()) diff --git a/backend/tests/users.rs b/backend/tests/users.rs new file mode 100644 index 0000000000..0a8bb3b9de --- /dev/null +++ b/backend/tests/users.rs @@ -0,0 +1,92 @@ +use sqlx::{Pool, Postgres}; + +mod common; +use common::*; + +fn user_url(port: u16, endpoint: &str, name: &str) -> String { + format!("http://localhost:{port}/api/w/test-workspace/users/{endpoint}/{name}") +} + +fn client() -> reqwest::Client { + reqwest::Client::new() +} + +fn authed(builder: reqwest::RequestBuilder) -> reqwest::RequestBuilder { + builder.header("Authorization", "Bearer SECRET_TOKEN") +} + +#[sqlx::test(fixtures("base"))] +async fn test_user_endpoints(db: Pool) -> anyhow::Result<()> { + initialize_tracing().await; + let server = ApiServer::start(db.clone()).await?; + let port = server.addr.port(); + let base = format!("http://localhost:{port}/api/w/test-workspace/users"); + + // --- whoami --- + let resp = authed(client().get(format!("{base}/whoami"))) + .send() + .await + .unwrap(); + assert_eq!(resp.status(), 200); + let body = resp.json::().await?; + assert_eq!(body["username"], "test-user"); + assert_eq!(body["email"], "test@windmill.dev"); + assert_eq!(body["is_admin"], true); + + // --- list --- + let resp = authed(client().get(format!("{base}/list"))) + .send() + .await + .unwrap(); + assert_eq!(resp.status(), 200); + let list = resp.json::>().await?; + assert!(!list.is_empty()); + assert!(list.iter().any(|u| u["username"] == "test-user")); + + // --- list_usernames --- + let resp = authed(client().get(format!("{base}/list_usernames"))) + .send() + .await + .unwrap(); + assert_eq!(resp.status(), 200); + let usernames = resp.json::>().await?; + assert!(usernames.contains(&"test-user".to_string())); + + // --- get --- + let resp = authed(client().get(user_url(port, "get", "test-user"))) + .send() + .await + .unwrap(); + assert_eq!(resp.status(), 200); + let body = resp.json::().await?; + assert_eq!(body["username"], "test-user"); + + // --- whois --- + let resp = authed(client().get(user_url(port, "whois", "test-user"))) + .send() + .await + .unwrap(); + assert_eq!(resp.status(), 200); + let body = resp.json::().await?; + assert_eq!(body["username"], "test-user"); + + // --- username_to_email --- + let resp = authed(client().get(user_url(port, "username_to_email", "test-user"))) + .send() + .await + .unwrap(); + assert_eq!(resp.status(), 200); + let email = resp.text().await?; + assert_eq!(email, "test@windmill.dev"); + + // --- is_owner --- + let resp = authed(client().get(format!( + "http://localhost:{port}/api/w/test-workspace/users/is_owner/u/test-user/test" + ))) + .send() + .await + .unwrap(); + assert_eq!(resp.status(), 200); + + Ok(()) +}