From 84d40ba8fdfb9974428c97d264b703c1dc0341be Mon Sep 17 00:00:00 2001 From: Ruben Fiszel Date: Sun, 8 Feb 2026 22:06:53 +0000 Subject: [PATCH] test: expand workspace endpoint coverage to ~40 endpoints Co-Authored-By: Claude Opus 4.6 --- backend/tests/fixtures/base.sql | 7 +- backend/tests/workspaces.rs | 396 +++++++++++++++++++++++++++++++- 2 files changed, 398 insertions(+), 5 deletions(-) diff --git a/backend/tests/fixtures/base.sql b/backend/tests/fixtures/base.sql index 53ac17bbf9..7db9918fba 100644 --- a/backend/tests/fixtures/base.sql +++ b/backend/tests/fixtures/base.sql @@ -15,8 +15,11 @@ 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 group_ (workspace_id, name, summary, extra_perms) VALUES + ('test-workspace', 'all', 'All users', '{}'); + +INSERT INTO password(email, password_hash, login_type, super_admin, verified, name, username) + VALUES ('test@windmill.dev', 'not-a-real-hash', 'password', true, true, 'Test User', 'test-user'); INSERT INTO password(email, password_hash, login_type, super_admin, verified, name) VALUES ('test2@windmill.dev', 'not-a-real-hash', 'password', false, true, 'Test User 2'); diff --git a/backend/tests/workspaces.rs b/backend/tests/workspaces.rs index e349a845ea..0b70c1955a 100644 --- a/backend/tests/workspaces.rs +++ b/backend/tests/workspaces.rs @@ -68,7 +68,6 @@ async fn test_workspace_endpoints(db: Pool) -> anyhow::Result<()> { assert_eq!(resp.json::().await?, false); // --- exists_username (validates username is available) --- - // existing username -> 400 let resp = authed(client().post(format!("{global_base}/exists_username"))) .json(&json!({"id": "test-workspace", "username": "test-user"})) .send() @@ -76,7 +75,6 @@ async fn test_workspace_endpoints(db: Pool) -> anyhow::Result<()> { .unwrap(); assert_eq!(resp.status(), 400); - // available username -> 200 let resp = authed(client().post(format!("{global_base}/exists_username"))) .json(&json!({"id": "test-workspace", "username": "available-user"})) .send() @@ -84,7 +82,36 @@ async fn test_workspace_endpoints(db: Pool) -> anyhow::Result<()> { .unwrap(); assert_eq!(resp.status(), 200); - // ===== Workspace-scoped endpoints ===== + // --- allowed_domain_auto_invite --- + let resp = authed(client().get(format!( + "{global_base}/allowed_domain_auto_invite" + ))) + .send() + .await + .unwrap(); + assert_eq!(resp.status(), 200); + resp.json::().await?; + + // --- create workspace --- + let resp = authed(client().post(format!("{global_base}/create"))) + .json(&json!({ + "id": "new-test-ws", + "name": "New Test Workspace" + })) + .send() + .await + .unwrap(); + assert_eq!(resp.status(), 200, "create: {}", resp.text().await?); + + // verify it exists + let resp = authed(client().post(format!("{global_base}/exists"))) + .json(&json!({"id": "new-test-ws"})) + .send() + .await + .unwrap(); + assert_eq!(resp.json::().await?, true); + + // ===== Workspace-scoped endpoints (read) ===== // --- get_settings --- let resp = authed(client().get(format!("{base}/get_settings"))) @@ -154,6 +181,339 @@ async fn test_workspace_endpoints(db: Pool) -> anyhow::Result<()> { let body = resp.json::().await?; assert_eq!(body["id"], "test-workspace"); + // --- get_workspace_name --- + let resp = authed(client().get(format!("{base}/get_workspace_name"))) + .send() + .await + .unwrap(); + assert_eq!(resp.status(), 200); + let name = resp.text().await?; + assert_eq!(name, "test-workspace"); + + // --- get_usage --- + let resp = authed(client().get(format!("{base}/usage"))) + .send() + .await + .unwrap(); + assert_eq!(resp.status(), 200); + + // --- get_used_triggers --- + let resp = authed(client().get(format!("{base}/used_triggers"))) + .send() + .await + .unwrap(); + assert_eq!(resp.status(), 200); + resp.json::().await?; + + // --- get_secondary_storage_names --- + let resp = authed(client().get(format!("{base}/get_secondary_storage_names"))) + .send() + .await + .unwrap(); + assert_eq!(resp.status(), 200); + resp.json::>().await?; + + // --- get_dependents (empty, no dependencies exist) --- + let resp = authed(client().get(format!( + "{base}/get_dependents/u/test-user/nonexistent" + ))) + .send() + .await + .unwrap(); + assert_eq!(resp.status(), 200); + let dependents = resp.json::>().await?; + assert!(dependents.is_empty()); + + // --- get_dependents_amounts --- + let resp = authed(client().post(format!("{base}/get_dependents_amounts"))) + .json(&json!(["u/test-user/some_script"])) + .send() + .await + .unwrap(); + assert_eq!(resp.status(), 200); + resp.json::>().await?; + + // --- list_ducklakes --- + let resp = authed(client().get(format!("{base}/list_ducklakes"))) + .send() + .await + .unwrap(); + assert_eq!(resp.status(), 200); + resp.json::>().await?; + + // --- list_datatables --- + let resp = authed(client().get(format!("{base}/list_datatables"))) + .send() + .await + .unwrap(); + assert_eq!(resp.status(), 200); + resp.json::>().await?; + + // --- list_datatable_schemas --- + let resp = authed(client().get(format!("{base}/list_datatable_schemas"))) + .send() + .await + .unwrap(); + assert_eq!(resp.status(), 200); + resp.json::>().await?; + + // ===== Workspace-scoped endpoints (mutations) ===== + + // --- update (edit_workspace) --- + let resp = authed(client().post(format!("{base}/update"))) + .json(&json!({"name": "renamed-workspace", "owner": "test-user"})) + .send() + .await + .unwrap(); + assert_eq!(resp.status(), 200, "update: {}", resp.text().await?); + + // --- change_workspace_name --- + let resp = authed(client().post(format!("{base}/change_workspace_name"))) + .json(&json!({"new_name": "Test Workspace Renamed"})) + .send() + .await + .unwrap(); + assert_eq!( + resp.status(), + 200, + "change_workspace_name: {}", + resp.text().await? + ); + + // verify name changed + let resp = authed(client().get(format!("{base}/get_workspace_name"))) + .send() + .await + .unwrap(); + assert_eq!(resp.text().await?, "Test Workspace Renamed"); + + // --- change_workspace_color --- + let resp = authed(client().post(format!("{base}/change_workspace_color"))) + .json(&json!({"color": "#FF5733"})) + .send() + .await + .unwrap(); + assert_eq!( + resp.status(), + 200, + "change_workspace_color: {}", + resp.text().await? + ); + + // --- edit_webhook --- + let resp = authed(client().post(format!("{base}/edit_webhook"))) + .json(&json!({"webhook": "https://example.com/hook"})) + .send() + .await + .unwrap(); + assert_eq!(resp.status(), 200, "edit_webhook: {}", resp.text().await?); + + // verify in settings + let resp = authed(client().get(format!("{base}/get_settings"))) + .send() + .await + .unwrap(); + let settings = resp.json::().await?; + assert_eq!(settings["webhook"], "https://example.com/hook"); + + // clear webhook + let resp = authed(client().post(format!("{base}/edit_webhook"))) + .json(&json!({"webhook": null})) + .send() + .await + .unwrap(); + assert_eq!(resp.status(), 200); + + // --- edit_auto_invite (EE-gated) --- + let resp = authed(client().post(format!("{base}/edit_auto_invite"))) + .json(&json!({"operator": false, "invite_all": false, "auto_add": false})) + .send() + .await + .unwrap(); + assert!( + resp.status() == 200 || resp.status() == 500, + "edit_auto_invite: unexpected status {}", + resp.status() + ); + + // --- edit_slack_command --- + let resp = authed(client().post(format!("{base}/edit_slack_command"))) + .json(&json!({"slack_command_script": null})) + .send() + .await + .unwrap(); + assert_eq!( + resp.status(), + 200, + "edit_slack_command: {}", + resp.text().await? + ); + + // --- edit_error_handler (new format) --- + let resp = authed(client().post(format!("{base}/edit_error_handler"))) + .json(&json!({"path": null, "extra_args": null})) + .send() + .await + .unwrap(); + assert_eq!( + resp.status(), + 200, + "edit_error_handler: {}", + resp.text().await? + ); + + // --- edit_success_handler (new format) --- + let resp = authed(client().post(format!("{base}/edit_success_handler"))) + .json(&json!({"path": null, "extra_args": null})) + .send() + .await + .unwrap(); + assert_eq!( + resp.status(), + 200, + "edit_success_handler: {}", + resp.text().await? + ); + + // --- edit_default_scripts --- + let resp = authed(client().post(format!("{base}/default_scripts"))) + .json(&json!(null)) + .send() + .await + .unwrap(); + assert_eq!( + resp.status(), + 200, + "edit_default_scripts: {}", + resp.text().await? + ); + + // --- edit_default_app (EE-gated, may return 200 or error) --- + let resp = authed(client().post(format!("{base}/edit_default_app"))) + .json(&json!({})) + .send() + .await + .unwrap(); + assert!( + resp.status() == 200 || resp.status() == 400, + "edit_default_app: unexpected status {}", + resp.status() + ); + + // --- set_environment_variable --- + let resp = authed(client().post(format!("{base}/set_environment_variable"))) + .json(&json!({"name": "TEST_ENV_VAR", "value": "test_value"})) + .send() + .await + .unwrap(); + assert_eq!( + resp.status(), + 200, + "set_environment_variable: {}", + resp.text().await? + ); + + // --- edit_deploy_to (EE-gated) --- + let resp = authed(client().post(format!("{base}/edit_deploy_to"))) + .json(&json!({"deploy_to": null})) + .send() + .await + .unwrap(); + assert!( + resp.status() == 200 || resp.status() == 400, + "edit_deploy_to: unexpected status {}", + resp.status() + ); + + // --- edit_large_file_storage_config --- + let resp = authed(client().post(format!( + "{base}/edit_large_file_storage_config" + ))) + .json(&json!({"large_file_storage": null})) + .send() + .await + .unwrap(); + assert_eq!( + resp.status(), + 200, + "edit_large_file_storage_config: {}", + resp.text().await? + ); + + // --- edit_deploy_ui_config (EE-gated) --- + let resp = authed(client().post(format!("{base}/edit_deploy_ui_config"))) + .json(&json!({"deploy_ui": null})) + .send() + .await + .unwrap(); + assert!( + resp.status() == 200 || resp.status() == 400, + "edit_deploy_ui_config: unexpected status {}", + resp.status() + ); + + // --- edit_git_sync_config (EE-gated) --- + let resp = authed(client().post(format!("{base}/edit_git_sync_config"))) + .json(&json!({"git_sync_settings": null})) + .send() + .await + .unwrap(); + assert!( + resp.status() == 200 || resp.status() == 400, + "edit_git_sync_config: unexpected status {}", + resp.status() + ); + + // --- update_operator_settings --- + let resp = authed(client().post(format!("{base}/operator_settings"))) + .json(&json!({})) + .send() + .await + .unwrap(); + assert_eq!( + resp.status(), + 200, + "update_operator_settings: {}", + resp.text().await? + ); + + // --- edit_public_app_rate_limit --- + let resp = authed(client().post(format!("{base}/public_app_rate_limit"))) + .json(&json!({"public_app_execution_limit_per_minute": null})) + .send() + .await + .unwrap(); + assert_eq!( + resp.status(), + 200, + "edit_public_app_rate_limit: {}", + resp.text().await? + ); + + // --- rebuild_dependency_map --- + let resp = authed(client().post(format!("{base}/rebuild_dependency_map"))) + .send() + .await + .unwrap(); + assert_eq!( + resp.status(), + 200, + "rebuild_dependency_map: {}", + resp.text().await? + ); + + // --- add_user --- + let resp = authed(client().post(format!("{base}/add_user"))) + .json(&json!({ + "email": "newuser@windmill.dev", + "is_admin": false, + "operator": false + })) + .send() + .await + .unwrap(); + assert_eq!(resp.status(), 201, "add_user: {}", resp.text().await?); + // --- invite_user + list_pending_invites + delete_invite --- let resp = authed(client().post(format!("{base}/invite_user"))) .json(&json!({ @@ -197,5 +557,35 @@ async fn test_workspace_endpoints(db: Pool) -> anyhow::Result<()> { resp.text().await? ); + // --- 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"))) + .send() + .await + .unwrap(); + assert_eq!(resp.status(), 200, "archive: {}", resp.text().await?); + + // --- unarchive workspace (global) --- + let resp = authed(client().post(format!("{global_base}/unarchive/new-test-ws"))) + .send() + .await + .unwrap(); + assert_eq!(resp.status(), 200, "unarchive: {}", resp.text().await?); + + // --- delete workspace (global) --- + let resp = authed(client().delete(format!("{global_base}/delete/new-test-ws"))) + .send() + .await + .unwrap(); + assert_eq!(resp.status(), 200, "delete: {}", resp.text().await?); + + // verify deleted + let resp = authed(client().post(format!("{global_base}/exists"))) + .json(&json!({"id": "new-test-ws"})) + .send() + .await + .unwrap(); + assert_eq!(resp.json::().await?, false); + Ok(()) }