Files
windmill/backend/windmill-api-integration-tests/tests/jobs_unauthed.rs
Ruben Fiszel 0389d9601c chore: upgrade axum 0.7 to 0.8 (#8539)
* chore: upgrade axum 0.7 to 0.8 and related dependencies

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* test: add route reachability tests for ~80 previously untested endpoints

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: switch feature-gated trigger handlers from axum::async_trait to async_trait crate

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: update new trash routes to axum 0.8 path syntax

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* chore: update ee-repo-ref to latest EE commit

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* test: upgrade route tests to assert 2xx responses with proper data setup

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* test: restore npm_proxy and ai_routes tests using local echo servers

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: gate workspace fork test behind enterprise feature flag

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* test: add ~40 more endpoint tests (jobs authed, health, favorites, ACLs, reachability)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: address review findings from axum 0.8 upgrade

- Use cookie value_trimmed() instead of value() for cookie 0.18 compat
- Update comments still referencing old :workspace_id syntax

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* chore: update ee-repo-ref

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* chore: update ee-repo-ref to 61ae055ea31481f1899953e9d5f65566b8c707b1

This commit updates the EE repository reference after PR #486 was merged in windmill-ee-private.

Previous ee-repo-ref: 0059d175a6fdddf52998b183bf91059b224704ac

New ee-repo-ref: 61ae055ea31481f1899953e9d5f65566b8c707b1

Automated by sync-ee-ref workflow.

* test: add test for new get_imports endpoint

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: remove unused import in raw_apps test

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: windmill-internal-app[bot] <windmill-internal-app[bot]@users.noreply.github.com>
2026-03-27 09:55:04 +00:00

251 lines
7.3 KiB
Rust

use serde_json::json;
use sqlx::{Pool, Postgres};
use uuid::Uuid;
use windmill_test_utils::*;
fn client() -> reqwest::Client {
reqwest::Client::new()
}
fn authed(builder: reqwest::RequestBuilder) -> reqwest::RequestBuilder {
builder.header("Authorization", "Bearer SECRET_TOKEN")
}
fn assert_2xx(status: u16, body: &str, endpoint: &str) {
assert!(
(200..300).contains(&status),
"{endpoint} returned {status}: {body}",
);
}
/// Insert a minimal completed job directly into the database for testing.
async fn insert_completed_job(db: &Pool<Postgres>) -> Uuid {
let id = Uuid::new_v4();
sqlx::query(
"INSERT INTO v2_job (id, workspace_id, created_by, permissioned_as, kind, tag, args)
VALUES ($1, 'test-workspace', 'test-user', 'u/test-user', 'script', 'deno', '{}'::jsonb)",
)
.bind(id)
.execute(db)
.await
.unwrap();
sqlx::query(
"INSERT INTO v2_job_completed (id, workspace_id, duration_ms, result, status)
VALUES ($1, 'test-workspace', 100, '42'::jsonb, 'success')",
)
.bind(id)
.execute(db)
.await
.unwrap();
id
}
#[sqlx::test(migrations = "../migrations", fixtures("base"))]
async fn test_jobs_unauthed_endpoints(db: Pool<Postgres>) -> 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/jobs_u");
let job_id = insert_completed_job(&db).await;
// --- No-data endpoints ---
let resp = authed(client().post(format!("{base}/queue/get_started_at_by_ids")))
.json(&json!([]))
.send()
.await?;
assert_2xx(
resp.status().as_u16(),
&resp.text().await?,
"POST /queue/get_started_at_by_ids",
);
// --- Completed job endpoints (unauthed service, with auth header) ---
let resp = authed(client().get(format!("{base}/get/{job_id}")))
.send()
.await?;
assert_2xx(resp.status().as_u16(), &resp.text().await?, "GET /get");
let resp = authed(client().get(format!("{base}/get_logs/{job_id}")))
.send()
.await?;
assert_2xx(resp.status().as_u16(), &resp.text().await?, "GET /get_logs");
let resp = authed(client().get(format!("{base}/get_completed_logs_tail/{job_id}")))
.send()
.await?;
assert_2xx(
resp.status().as_u16(),
&resp.text().await?,
"GET /get_completed_logs_tail",
);
let resp = authed(client().get(format!("{base}/get_args/{job_id}")))
.send()
.await?;
assert_2xx(resp.status().as_u16(), &resp.text().await?, "GET /get_args");
let resp = authed(client().get(format!("{base}/completed/get/{job_id}")))
.send()
.await?;
assert_2xx(
resp.status().as_u16(),
&resp.text().await?,
"GET /completed/get",
);
let resp = authed(client().get(format!("{base}/completed/get_result/{job_id}")))
.send()
.await?;
assert_2xx(
resp.status().as_u16(),
&resp.text().await?,
"GET /completed/get_result",
);
let resp = authed(client().get(format!("{base}/completed/get_result_maybe/{job_id}")))
.send()
.await?;
assert_2xx(
resp.status().as_u16(),
&resp.text().await?,
"GET /completed/get_result_maybe",
);
let resp = authed(client().get(format!("{base}/completed/get_timing/{job_id}")))
.send()
.await?;
assert_2xx(
resp.status().as_u16(),
&resp.text().await?,
"GET /completed/get_timing",
);
let resp = authed(client().get(format!("{base}/getupdate/{job_id}")))
.send()
.await?;
assert_2xx(
resp.status().as_u16(),
&resp.text().await?,
"GET /getupdate",
);
Ok(())
}
const FAKE_UUID: &str = "00000000-0000-0000-0000-000000000000";
const FAKE_SECRET: &str = "aabb";
/// Reachability tests for endpoints that need complex runtime.
/// These just verify the route matches (handler runs), not 2xx.
fn assert_route_reachable(status: u16, body: &str, endpoint: &str) {
assert!(
status != 404 || !body.is_empty(),
"Router-level 404 for {endpoint}",
);
}
#[sqlx::test(migrations = "../migrations", fixtures("base"))]
async fn test_jobs_unauthed_complex_reachability(db: Pool<Postgres>) -> 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/jobs_u");
let resp = authed(client().get(format!("{base}/resume/{FAKE_UUID}/1/{FAKE_SECRET}")))
.send()
.await?;
assert_route_reachable(resp.status().as_u16(), &resp.text().await?, "GET /resume");
let resp = authed(client().post(format!("{base}/cancel/{FAKE_UUID}/1/{FAKE_SECRET}")))
.send()
.await?;
assert_route_reachable(resp.status().as_u16(), &resp.text().await?, "POST /cancel");
let resp = authed(client().get(format!("{base}/get_flow/{FAKE_UUID}/1/{FAKE_SECRET}")))
.send()
.await?;
assert_route_reachable(resp.status().as_u16(), &resp.text().await?, "GET /get_flow");
let resp = authed(client().post(format!("{base}/queue/cancel/{FAKE_UUID}")))
.json(&serde_json::json!({"reason": "test"}))
.send()
.await?;
assert_route_reachable(
resp.status().as_u16(),
&resp.text().await?,
"POST /queue/cancel",
);
let resp = authed(client().post(format!("{base}/queue/force_cancel/{FAKE_UUID}")))
.json(&serde_json::json!({"reason": "test"}))
.send()
.await?;
assert_route_reachable(
resp.status().as_u16(),
&resp.text().await?,
"POST /queue/force_cancel",
);
let resp = authed(client().post(format!("{base}/flow/resume_suspended/{FAKE_UUID}")))
.send()
.await?;
assert_route_reachable(
resp.status().as_u16(),
&resp.text().await?,
"POST /flow/resume_suspended",
);
let resp = authed(client().get(format!("{base}/flow/approval_info/{FAKE_UUID}")))
.send()
.await?;
assert_route_reachable(
resp.status().as_u16(),
&resp.text().await?,
"GET /flow/approval_info",
);
let resp = authed(client().get(format!("{base}/get_root_job_id/{FAKE_UUID}")))
.send()
.await?;
assert_route_reachable(
resp.status().as_u16(),
&resp.text().await?,
"GET /get_root_job_id",
);
let resp = authed(client().get(format!("{base}/get_flow_debug_info/{FAKE_UUID}")))
.send()
.await?;
assert_route_reachable(
resp.status().as_u16(),
&resp.text().await?,
"GET /get_flow_debug_info",
);
let resp = authed(client().get(format!("{base}/get_log_file/{FAKE_UUID}/test.txt")))
.send()
.await?;
assert_route_reachable(
resp.status().as_u16(),
&resp.text().await?,
"GET /get_log_file",
);
let resp = authed(client().post(format!("{base}/queue/cancel_persistent/u/test-user/fake")))
.json(&serde_json::json!({"reason": "test"}))
.send()
.await?;
assert_route_reachable(
resp.status().as_u16(),
&resp.text().await?,
"POST /queue/cancel_persistent",
);
Ok(())
}