From 1cb565598a09b2c64c1082d7d8d8ea0df0cb33b8 Mon Sep 17 00:00:00 2001 From: Ruben Fiszel Date: Sun, 8 Feb 2026 22:35:36 +0000 Subject: [PATCH] test: add hub, auth, critical alerts, and fork/tarball endpoint coverage Co-Authored-By: Claude Opus 4.5 --- backend/tests/apps.rs | 41 +++++++++++++ backend/tests/flows.rs | 28 +++++++++ backend/tests/scripts.rs | 54 +++++++++++++++++ backend/tests/users.rs | 64 ++++++++++++++++++++ backend/tests/workspaces.rs | 115 ++++++++++++++++++++++++++++++++++++ 5 files changed, 302 insertions(+) diff --git a/backend/tests/apps.rs b/backend/tests/apps.rs index fd11db2d7b..869858a265 100644 --- a/backend/tests/apps.rs +++ b/backend/tests/apps.rs @@ -237,5 +237,46 @@ async fn test_app_endpoints(db: Pool) -> anyhow::Result<()> { let resp = authed_get(port, "exists", "u/test-user/another_app").await; assert_eq!(resp.json::().await?, false); + // ===== Hub endpoints (require external network, expect 500 or 200) ===== + + // --- hub/list --- + let resp = authed(client().get(format!( + "http://localhost:{port}/api/apps/hub/list" + ))) + .send() + .await + .unwrap(); + assert!( + resp.status() == 200 || resp.status() == 500, + "hub/list: unexpected status {}", + resp.status() + ); + + // --- hub/get --- + let resp = authed(client().get(format!( + "http://localhost:{port}/api/apps/hub/get/1" + ))) + .send() + .await + .unwrap(); + assert!( + resp.status() == 200 || resp.status() == 500, + "hub/get: unexpected status {}", + resp.status() + ); + + // --- hub/get_raw --- + let resp = authed(client().get(format!( + "http://localhost:{port}/api/apps/hub/get_raw/1" + ))) + .send() + .await + .unwrap(); + assert!( + resp.status() == 200 || resp.status() == 500, + "hub/get_raw: unexpected status {}", + resp.status() + ); + Ok(()) } diff --git a/backend/tests/flows.rs b/backend/tests/flows.rs index d63f9ffeab..848231f2fc 100644 --- a/backend/tests/flows.rs +++ b/backend/tests/flows.rs @@ -257,5 +257,33 @@ async fn test_flow_endpoints(db: Pool) -> anyhow::Result<()> { let resp = authed_get(port, "exists", "u/test-user/another_flow").await; assert_eq!(resp.json::().await?, false); + // ===== Hub endpoints (require external network, expect 500 or 200) ===== + + // --- hub/list --- + let resp = authed(client().get(format!( + "http://localhost:{port}/api/flows/hub/list" + ))) + .send() + .await + .unwrap(); + assert!( + resp.status() == 200 || resp.status() == 500, + "hub/list: unexpected status {}", + resp.status() + ); + + // --- hub/get --- + let resp = authed(client().get(format!( + "http://localhost:{port}/api/flows/hub/get/1" + ))) + .send() + .await + .unwrap(); + assert!( + resp.status() == 200 || resp.status() == 500, + "hub/get: unexpected status {}", + resp.status() + ); + Ok(()) } diff --git a/backend/tests/scripts.rs b/backend/tests/scripts.rs index 3825980350..3f55f36a64 100644 --- a/backend/tests/scripts.rs +++ b/backend/tests/scripts.rs @@ -331,5 +331,59 @@ async fn test_script_endpoints(db: Pool) -> anyhow::Result<()> { let body = resp.text().await?; assert!(body.is_empty(), "expected empty string, got: {body}"); + // ===== Hub endpoints (require external network, expect 500 or 200) ===== + + // --- hub/top --- + let resp = authed(client().get(format!( + "http://localhost:{port}/api/scripts/hub/top" + ))) + .send() + .await + .unwrap(); + assert!( + resp.status() == 200 || resp.status() == 500, + "hub/top: unexpected status {}", + resp.status() + ); + + // --- hub/get (raw script by path, needs hub/ prefix in path) --- + let resp = authed(client().get(format!( + "http://localhost:{port}/api/scripts/hub/get/hub/1/hello" + ))) + .send() + .await + .unwrap(); + assert!( + resp.status() == 200 || resp.status() == 500, + "hub/get: unexpected status {}", + resp.status() + ); + + // --- hub/get_full (full script by path, needs hub/ prefix in path) --- + let resp = authed(client().get(format!( + "http://localhost:{port}/api/scripts/hub/get_full/hub/1/hello" + ))) + .send() + .await + .unwrap(); + assert!( + resp.status() == 200 || resp.status() == 500, + "hub/get_full: unexpected status {}", + resp.status() + ); + + // --- integrations hub/list --- + let resp = authed(client().get(format!( + "http://localhost:{port}/api/integrations/hub/list" + ))) + .send() + .await + .unwrap(); + assert!( + resp.status() == 200 || resp.status() == 500, + "integrations hub/list: unexpected status {}", + resp.status() + ); + Ok(()) } diff --git a/backend/tests/users.rs b/backend/tests/users.rs index 79307d9f1e..107d2bd676 100644 --- a/backend/tests/users.rs +++ b/backend/tests/users.rs @@ -285,6 +285,70 @@ async fn test_user_endpoints(db: Pool) -> anyhow::Result<()> { ); } + // ===== Auth (unauthed) endpoints ===== + let auth_base = format!("http://localhost:{port}/api/auth"); + + // --- login (will fail: password hash in fixture is fake) --- + let resp = client() + .post(format!("{auth_base}/login")) + .json(&json!({"email": "test@windmill.dev", "password": "wrong-password"})) + .send() + .await + .unwrap(); + assert!( + resp.status() == 401 || resp.status() == 500, + "login: unexpected status {}", + resp.status() + ); + + // --- logout (POST, with auth token) --- + let resp = authed(client().post(format!("{auth_base}/logout"))) + .send() + .await + .unwrap(); + assert!( + resp.status() == 200 || resp.status() == 303, + "logout POST: unexpected status {}", + resp.status() + ); + + // --- logout (GET, with auth token) --- + let resp = authed(client().get(format!("{auth_base}/logout"))) + .send() + .await + .unwrap(); + assert!( + resp.status() == 200 || resp.status() == 303, + "logout GET: unexpected status {}", + resp.status() + ); + + // --- request_password_reset (returns 400 if SMTP not configured) --- + let resp = client() + .post(format!("{auth_base}/request_password_reset")) + .json(&json!({"email": "test@windmill.dev"})) + .send() + .await + .unwrap(); + assert!( + resp.status() == 200 || resp.status() == 400, + "request_password_reset: unexpected status {}", + resp.status() + ); + + // --- reset_password (EE-gated, invalid token) --- + let resp = client() + .post(format!("{auth_base}/reset_password")) + .json(&json!({"token": "invalid-token", "new_password": "new-pass"})) + .send() + .await + .unwrap(); + assert!( + resp.status() == 400 || resp.status() == 500, + "reset_password: unexpected status {}", + resp.status() + ); + // ===== Workspace-scoped endpoints ===== // --- whoami --- diff --git a/backend/tests/workspaces.rs b/backend/tests/workspaces.rs index c5ce424514..4a7507b155 100644 --- a/backend/tests/workspaces.rs +++ b/backend/tests/workspaces.rs @@ -557,6 +557,121 @@ async fn test_workspace_endpoints(db: Pool) -> anyhow::Result<()> { resp.text().await? ); + // ===== Critical alerts (EE-gated, returns 404 in OSS) ===== + + // --- get critical_alerts --- + let resp = authed(client().get(format!("{base}/critical_alerts"))) + .send() + .await + .unwrap(); + assert!( + resp.status() == 200 || resp.status() == 404, + "critical_alerts: unexpected status {}", + resp.status() + ); + + // --- acknowledge critical alert (nonexistent id) --- + let resp = authed(client().post(format!("{base}/critical_alerts/1/acknowledge"))) + .send() + .await + .unwrap(); + assert!( + resp.status() == 200 || resp.status() == 404, + "acknowledge_critical_alert: unexpected status {}", + resp.status() + ); + + // --- acknowledge_all critical alerts --- + let resp = authed(client().post(format!("{base}/critical_alerts/acknowledge_all"))) + .send() + .await + .unwrap(); + assert!( + resp.status() == 200 || resp.status() == 404, + "acknowledge_all_critical_alerts: unexpected status {}", + resp.status() + ); + + // --- mute critical alerts --- + let resp = authed(client().post(format!("{base}/critical_alerts/mute"))) + .json(&json!({"mute_critical_alerts": false})) + .send() + .await + .unwrap(); + assert!( + resp.status() == 200 || resp.status() == 404, + "mute_critical_alerts: unexpected status {}", + resp.status() + ); + + // ===== Tarball export ===== + + // --- tarball (download workspace as tar archive) --- + let resp = authed(client().get(format!("{base}/tarball"))) + .send() + .await + .unwrap(); + assert_eq!(resp.status(), 200, "tarball: {}", resp.status()); + + // ===== Fork operations (on the newly created workspace) ===== + + // --- create_fork (workspace-scoped, from new-test-ws) --- + let new_ws_base = format!("http://localhost:{port}/api/w/new-test-ws/workspaces"); + let resp = authed(client().post(format!("{new_ws_base}/create_fork"))) + .json(&json!({ + "id": "wm-fork-test-ws", + "name": "Forked Test Workspace" + })) + .send() + .await + .unwrap(); + assert_eq!( + resp.status(), + 200, + "create_fork: {}", + resp.text().await? + ); + + // verify fork exists + let resp = authed(client().post(format!("{global_base}/exists"))) + .json(&json!({"id": "wm-fork-test-ws"})) + .send() + .await + .unwrap(); + assert_eq!(resp.json::().await?, true); + + // --- change_workspace_id --- + let fork_ws_base = format!("http://localhost:{port}/api/w/wm-fork-test-ws/workspaces"); + let resp = authed(client().post(format!("{fork_ws_base}/change_workspace_id"))) + .json(&json!({ + "new_id": "wm-fork-renamed", + "new_name": "Renamed Fork" + })) + .send() + .await + .unwrap(); + assert_eq!( + resp.status(), + 200, + "change_workspace_id: {}", + resp.text().await? + ); + + // verify renamed workspace exists + let resp = authed(client().post(format!("{global_base}/exists"))) + .json(&json!({"id": "wm-fork-renamed"})) + .send() + .await + .unwrap(); + assert_eq!(resp.json::().await?, true); + + // clean up renamed fork + let resp = authed(client().delete(format!("{global_base}/delete/wm-fork-renamed"))) + .send() + .await + .unwrap(); + assert_eq!(resp.status(), 200); + // --- archive workspace (on the newly created one, not our main test workspace) --- let new_ws_base = format!("http://localhost:{port}/api/w/new-test-ws/workspaces"); let resp = authed(client().post(format!("{new_ws_base}/archive")))