mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-08-21 00:02:23 +00:00
0389d9601c
* 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>
251 lines
7.3 KiB
Rust
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(())
|
|
}
|