test: add global endpoint coverage for users, groups, schedules, and more

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Ruben Fiszel
2026-02-08 22:20:34 +00:00
parent 3256c34309
commit dbcb5db155
8 changed files with 376 additions and 0 deletions

View File

@@ -206,6 +206,17 @@ async fn test_flow_endpoints(db: Pool<Postgres>) -> anyhow::Result<()> {
.unwrap();
assert_eq!(resp.status(), 200, "history_update: {}", resp.text().await?);
// --- get_triggers_count ---
let resp = authed(client().get(flow_url(
port,
"get_triggers_count",
"u/test-user/test_flow",
)))
.send()
.await
.unwrap();
assert_eq!(resp.status(), 200);
// --- toggle_workspace_error_handler (EE-gated, expect 400 in OSS) ---
let resp = authed(client().post(flow_url(
port,

View File

@@ -140,6 +140,16 @@ async fn test_folder_endpoints(db: Pool<Postgres>) -> anyhow::Result<()> {
.unwrap();
assert_eq!(resp.status(), 200);
// --- is_owner ---
let resp = authed(client().get(format!(
"{base}/is_owner/f/test_folder"
)))
.send()
.await
.unwrap();
assert_eq!(resp.status(), 200);
resp.json::<bool>().await?;
// --- delete ---
let resp = authed(client().delete(folder_url(port, "delete", "another_folder")))
.send()

View File

@@ -151,5 +151,150 @@ async fn test_group_endpoints(db: Pool<Postgres>) -> anyhow::Result<()> {
let names = resp.json::<Vec<String>>().await?;
assert!(!names.contains(&"another_group".to_string()));
// ===== Global (instance group) endpoints =====
let global_base = format!("http://localhost:{port}/api/groups");
// --- create instance group ---
let resp = authed(client().post(format!("{global_base}/create")))
.json(&json!({"name": "test_igroup", "summary": "Test instance group"}))
.send()
.await
.unwrap();
assert_eq!(
resp.status(),
200,
"create igroup: {}",
resp.text().await?
);
// --- list instance groups ---
let resp = authed(client().get(format!("{global_base}/list")))
.send()
.await
.unwrap();
assert_eq!(resp.status(), 200);
let list = resp.json::<Vec<serde_json::Value>>().await?;
assert!(list.iter().any(|g| g["name"] == "test_igroup"));
// --- list_with_workspaces ---
let resp = authed(client().get(format!("{global_base}/list_with_workspaces")))
.send()
.await
.unwrap();
assert_eq!(resp.status(), 200);
resp.json::<Vec<serde_json::Value>>().await?;
// --- get instance group ---
let resp = authed(client().get(format!("{global_base}/get/test_igroup")))
.send()
.await
.unwrap();
assert_eq!(resp.status(), 200);
let body = resp.json::<serde_json::Value>().await?;
assert_eq!(body["name"], "test_igroup");
assert_eq!(body["summary"], "Test instance group");
// --- update instance group ---
let resp = authed(client().post(format!("{global_base}/update/test_igroup")))
.json(&json!({"new_summary": "Updated instance group"}))
.send()
.await
.unwrap();
assert_eq!(
resp.status(),
200,
"update igroup: {}",
resp.text().await?
);
// verify update
let resp = authed(client().get(format!("{global_base}/get/test_igroup")))
.send()
.await
.unwrap();
let body = resp.json::<serde_json::Value>().await?;
assert_eq!(body["summary"], "Updated instance group");
// --- adduser to instance group ---
let resp = authed(client().post(format!("{global_base}/adduser/test_igroup")))
.json(&json!({"email": "test@windmill.dev"}))
.send()
.await
.unwrap();
assert_eq!(
resp.status(),
200,
"adduser igroup: {}",
resp.text().await?
);
// verify membership
let resp = authed(client().get(format!("{global_base}/get/test_igroup")))
.send()
.await
.unwrap();
let body = resp.json::<serde_json::Value>().await?;
let emails = body["emails"].as_array().unwrap();
assert!(
emails
.iter()
.any(|e| e.as_str() == Some("test@windmill.dev")),
"expected test@windmill.dev in emails, got: {:?}",
emails
);
// --- removeuser from instance group ---
let resp = authed(client().post(format!(
"{global_base}/removeuser/test_igroup"
)))
.json(&json!({"email": "test@windmill.dev"}))
.send()
.await
.unwrap();
assert_eq!(resp.status(), 200);
// --- export (EE-gated) ---
let resp = authed(client().get(format!("{global_base}/export")))
.send()
.await
.unwrap();
assert!(
resp.status() == 200 || resp.status() == 400,
"export igroups: unexpected status {}",
resp.status()
);
// --- overwrite (EE-gated) ---
let resp = authed(client().post(format!("{global_base}/overwrite")))
.json(&json!([]))
.send()
.await
.unwrap();
assert!(
resp.status() == 200 || resp.status() == 400,
"overwrite igroups: unexpected status {}",
resp.status()
);
// --- delete instance group ---
let resp = authed(client().delete(format!("{global_base}/delete/test_igroup")))
.send()
.await
.unwrap();
assert_eq!(
resp.status(),
200,
"delete igroup: {}",
resp.text().await?
);
// verify deleted
let resp = authed(client().get(format!("{global_base}/list")))
.send()
.await
.unwrap();
let list = resp.json::<Vec<serde_json::Value>>().await?;
assert!(!list.iter().any(|g| g["name"] == "test_igroup"));
Ok(())
}

View File

@@ -296,6 +296,16 @@ async fn test_resource_endpoints(db: Pool<Postgres>) -> anyhow::Result<()> {
.unwrap();
assert_eq!(resp.status(), 404);
// --- file_resource_type_to_file_ext_map ---
let resp = authed(client().get(format!(
"{base}/file_resource_type_to_file_ext_map"
)))
.send()
.await
.unwrap();
assert_eq!(resp.status(), 200);
resp.json::<serde_json::Value>().await?;
// --- resource types ---
// type/exists

View File

@@ -218,5 +218,20 @@ async fn test_schedule_endpoints(db: Pool<Postgres>) -> anyhow::Result<()> {
let resp = authed_get(port, "exists", "u/test-user/another_schedule").await;
assert_eq!(resp.json::<bool>().await?, false);
// ===== Global endpoints =====
// --- preview ---
let resp = authed(client().post(format!(
"http://localhost:{port}/api/schedules/preview"
)))
.json(&json!({
"schedule": "0 0 */6 * * *",
"timezone": "UTC"
}))
.send()
.await
.unwrap();
assert_eq!(resp.status(), 200, "preview: {}", resp.text().await?);
Ok(())
}

View File

@@ -257,6 +257,10 @@ async fn test_script_endpoints(db: Pool<Postgres>) -> anyhow::Result<()> {
resp.status()
);
// --- get_triggers_count ---
let resp = authed_get(port, "get_triggers_count", "u/test-user/test_script").await;
assert_eq!(resp.status(), 200);
// --- tokened_raw (global unauthed, token in URL) ---
let resp = client()
.get(format!(

View File

@@ -113,6 +113,178 @@ async fn test_user_endpoints(db: Pool<Postgres>) -> anyhow::Result<()> {
assert_eq!(resp.status(), 200);
resp.json::<Vec<serde_json::Value>>().await?;
// --- username_info ---
let resp = authed(client().get(format!(
"{global_base}/username_info/test@windmill.dev"
)))
.send()
.await
.unwrap();
assert_eq!(resp.status(), 200);
let body = resp.json::<serde_json::Value>().await?;
assert_eq!(body["username"], "test-user");
// --- global usage ---
let resp = authed(client().get(format!("{global_base}/usage")))
.send()
.await
.unwrap();
assert_eq!(resp.status(), 200);
// --- tutorial_progress (get, then set, then get again) ---
let resp = authed(client().get(format!("{global_base}/tutorial_progress")))
.send()
.await
.unwrap();
assert_eq!(resp.status(), 200);
resp.json::<serde_json::Value>().await?;
let resp = authed(client().post(format!("{global_base}/tutorial_progress")))
.json(&json!({"progress": 42, "skipped_all": false}))
.send()
.await
.unwrap();
assert_eq!(
resp.status(),
200,
"set tutorial_progress: {}",
resp.text().await?
);
let resp = authed(client().get(format!("{global_base}/tutorial_progress")))
.send()
.await
.unwrap();
let body = resp.json::<serde_json::Value>().await?;
assert_eq!(body["progress"], 42);
// --- global update user ---
let resp = authed(client().post(format!(
"{global_base}/update/test2@windmill.dev"
)))
.json(&json!({"name": "Updated Test User 2"}))
.send()
.await
.unwrap();
assert_eq!(
resp.status(),
200,
"global update user: {}",
resp.text().await?
);
// --- setpassword (EE-gated in OSS) ---
let resp = authed(client().post(format!("{global_base}/setpassword")))
.json(&json!({"password": "new-test-password-123"}))
.send()
.await
.unwrap();
assert!(
resp.status() == 200 || resp.status() == 500,
"setpassword: unexpected status {}",
resp.status()
);
// --- all_runnables ---
let resp = authed(client().get(format!("{global_base}/all_runnables")))
.send()
.await
.unwrap();
assert_eq!(resp.status(), 200);
resp.json::<Vec<serde_json::Value>>().await?;
// --- onboarding (EE-gated in OSS) ---
let resp = authed(client().post(format!("{global_base}/onboarding")))
.json(&json!({"touch_point": "test", "use_case": "testing"}))
.send()
.await
.unwrap();
assert!(
resp.status() == 200 || resp.status() == 500,
"onboarding: unexpected status {}",
resp.status()
);
// --- decline_invite (no pending invite, but endpoint should handle gracefully) ---
let resp = authed(client().post(format!("{global_base}/decline_invite")))
.json(&json!({"workspace_id": "nonexistent-ws"}))
.send()
.await
.unwrap();
assert!(
resp.status() == 200 || resp.status() == 404,
"decline_invite: unexpected status {}",
resp.status()
);
// --- auth: is_first_time_setup (unauthed) ---
let resp = client()
.get(format!("http://localhost:{port}/api/auth/is_first_time_setup"))
.send()
.await
.unwrap();
assert_eq!(resp.status(), 200);
let is_first = resp.json::<bool>().await?;
assert_eq!(is_first, false);
// --- auth: is_smtp_configured (unauthed) ---
let resp = client()
.get(format!("http://localhost:{port}/api/auth/is_smtp_configured"))
.send()
.await
.unwrap();
assert_eq!(resp.status(), 200);
resp.json::<bool>().await?;
// --- create user (global, EE-gated in OSS) ---
let resp = authed(client().post(format!("{global_base}/create")))
.json(&json!({
"email": "newglobaluser@windmill.dev",
"password": "test-password-123",
"super_admin": false,
"name": "New Global User"
}))
.send()
.await
.unwrap();
let create_status = resp.status();
assert!(
create_status == 201 || create_status == 500,
"create user: unexpected status {}",
create_status
);
if create_status == 201 {
// --- rename user (only if create succeeded / EE) ---
let resp = authed(client().post(format!(
"{global_base}/rename/newglobaluser@windmill.dev"
)))
.json(&json!({"new_username": "renamed-user"}))
.send()
.await
.unwrap();
assert_eq!(
resp.status(),
200,
"rename user: {}",
resp.text().await?
);
// --- global delete user ---
let resp = authed(client().delete(format!(
"{global_base}/delete/newglobaluser@windmill.dev"
)))
.send()
.await
.unwrap();
assert_eq!(
resp.status(),
200,
"global delete user: {}",
resp.text().await?
);
}
// ===== Workspace-scoped endpoints =====
// --- whoami ---

View File

@@ -587,5 +587,14 @@ async fn test_workspace_endpoints(db: Pool<Postgres>) -> anyhow::Result<()> {
.unwrap();
assert_eq!(resp.json::<bool>().await?, false);
// --- create_workspace_require_superadmin ---
let resp = authed(client().get(format!(
"{global_base}/create_workspace_require_superadmin"
)))
.send()
.await
.unwrap();
assert_eq!(resp.status(), 200);
Ok(())
}