Files
windmill/backend/tests/ci_tests.rs
T
Ruben Fiszel c57c769dea feat: add CI test scripts with auto-trigger on deploy (#8736)
* feat: add CI test scripts with auto-trigger on deploy

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

* fix: fix annotation parser early return and handle renames correctly

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

* fix: move CI test results to top of script/flow detail pages

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

* fix: improve CI test results spacing, icon, and remove pass label

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

* feat: support one-line annotation and use script/path format

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

* feat: move CI test trigger logic to EE

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

* feat: move CI badge next to New badge and add deduplicated CI summary

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

* feat: add CI test e2e tests and fix nullable column annotations

Add integration tests for CI test annotation parsing (creates/removes
ci_test_reference rows) and the CI test results API (single + batch
endpoints). Add backend test for auto-trigger on deploy (private+python).

Fix sqlx LEFT JOIN LATERAL nullable column annotations in
get_ci_test_results and get_ci_test_results_batch queries — sqlx
cannot infer nullability from LATERAL subqueries, causing runtime
decode errors when no matching job exists.

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

* fix build/sqlx

* fix

* feat: CI test improvements and templates

- Fix windmill-dep-map/private feature propagation in worker, api-scripts,
  and api-flows Cargo.toml so CI test triggers actually fire in EE mode
- Clone ci_test_reference rows during workspace fork
- Add polling to CiTestResults component (refetch every 3s while running)
- Add running state and auto-refresh to ForkWorkspaceBanner CI summary
- Add yellow "CI test" badge on script list rows and detail page
- Fix Library badge border color (remove indigo border override)
- Add CI Test TypeScript and CI Test Python templates in ScriptBuilder
- Update sqlx offline cache
- Add debug tracing for CI test trigger in worker_lockfiles

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

* fix: add missing children prop to WorkspaceDeployLayout

Fixes svelte-fast-check type error when passing named snippets as
children content inside the component tag.

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

* fix: address PR review feedback

- Remove empty wrapper divs around CiTestResults, move mb-4 into component
- Add batch endpoint size cap (max 200 items)
- Add ON DELETE CASCADE to ci_test_reference workspace FK (new migration)
- Downgrade CI test trigger logs from info to debug
- Fix false-positive polling: only treat status='running' as running,
  not null status (CiTestResults, CompareWorkspaces, ForkWorkspaceBanner)
- Fix test numbering in integration tests

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>

* chore: update ee-repo-ref to d9d68c2406df0b59f413ea0b2cb24780a9817d04

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

Previous ee-repo-ref: d7ccd9b86da99ec056a0e8708e3637d64290387a

New ee-repo-ref: d9d68c2406df0b59f413ea0b2cb24780a9817d04

Automated by sync-ee-ref workflow.

* fix: treat queued jobs (job_id set, null status) as running

Jobs that have been pushed but not yet picked up by a worker have a
job_id but null status. Treat these as 'running' to avoid showing
misleading 'pass' badges or '0 passing'. Tests that were never
triggered (no job_id, null status) remain neutral/hidden.

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: hugocasa <hugo@casademont.ch>
Co-authored-by: windmill-internal-app[bot] <windmill-internal-app[bot]@users.noreply.github.com>
2026-04-09 17:21:36 +00:00

117 lines
4.0 KiB
Rust

#[cfg(all(feature = "python", feature = "private"))]
mod ci_tests {
use std::collections::HashMap;
use sqlx::{Pool, Postgres};
use tokio_stream::StreamExt;
use windmill_api_client::types::{NewScript, ScriptLang};
use windmill_test_utils::{in_test_worker, init_client, listen_for_completed_jobs};
fn quick_ns(content: &str, path: &str, parent_hash: Option<String>) -> NewScript {
NewScript {
content: content.into(),
language: ScriptLang::Python3,
lock: None,
parent_hash,
path: path.into(),
concurrent_limit: None,
concurrency_time_window_s: None,
cache_ttl: None,
dedicated_worker: None,
description: "".to_string(),
draft_only: None,
envs: vec![],
is_template: None,
kind: None,
summary: "".to_string(),
tag: None,
schema: HashMap::new(),
ws_error_handler_muted: Some(false),
priority: None,
delete_after_secs: None,
timeout: None,
restart_unless_cancelled: None,
deployment_message: None,
concurrency_key: None,
visible_to_runner_only: None,
auto_kind: None,
codebase: None,
has_preprocessor: None,
on_behalf_of_email: None,
assets: vec![],
modules: None,
}
}
/// Test 2: Deploying a script automatically triggers CI test jobs for test scripts
/// that reference it via the `# test:` annotation.
#[sqlx::test(fixtures("base"))]
async fn test_ci_test_trigger_on_deploy(db: Pool<Postgres>) -> anyhow::Result<()> {
let (client, port, _s) = init_client(db.clone()).await;
// Step 1: Create the test script with a CI annotation targeting deploy_target.
// This inserts a ci_test_reference row.
client
.create_script(
"test-workspace",
&quick_ns(
"# test: script/u/test-user/deploy_target\ndef main():\n return True",
"u/test-user/ci_test_for_deploy",
None,
),
)
.await
.unwrap();
// Process the test script's dependency (lock generation) job
let mut completed = listen_for_completed_jobs(&db).await;
in_test_worker(&db, completed.next(), port).await;
// Step 2: Create the target script. Its dependency job, once processed,
// will call trigger_ci_tests_for_item which finds our test script.
client
.create_script(
"test-workspace",
&quick_ns(
"def main():\n return 42",
"u/test-user/deploy_target",
None,
),
)
.await
.unwrap();
// Process the target script's dependency job → CI trigger fires in tokio::spawn
let mut completed = listen_for_completed_jobs(&db).await;
in_test_worker(&db, completed.next(), port).await;
// Give the spawned trigger task time to push the CI test job
tokio::time::sleep(std::time::Duration::from_secs(1)).await;
// Verify a CI test job was queued
let ci_jobs = sqlx::query!(
"SELECT j.id, j.runnable_path, j.trigger_kind::text as trigger_kind \
FROM v2_job j \
WHERE j.workspace_id = 'test-workspace' \
AND j.trigger_kind = 'ci_test'"
)
.fetch_all(&db)
.await?;
assert_eq!(ci_jobs.len(), 1, "expected exactly 1 CI test job");
assert_eq!(
ci_jobs[0].runnable_path.as_deref(),
Some("u/test-user/ci_test_for_deploy"),
"CI test job should run the test script"
);
assert_eq!(
ci_jobs[0].trigger_kind.as_deref(),
Some("ci_test"),
"trigger_kind should be ci_test"
);
Ok(())
}
}