Files
windmill/backend/tests/ci_tests.rs
Diego Imbert ae42cbf4a8 feat(backend): remove draft_only, move never-deployed items into the draft table
Never-deployed "draft only" scripts/flows/apps used to live as a stub row in
their own table (draft_only = true) alongside a draft row. They now live solely
in the `draft` table.

Migration `remove_draft_only`:
- ensures every draft_only stub has a matching draft row (ON CONFLICT DO
  NOTHING preserves the real draft; synthesises one matching the frontend
  draft JSON shape only for stubs that lost their draft), with email = NULL
- deletes the stub rows (FKs to *_version / dependency tables cascade)
- drops the draft_only column from script/flow/app
The down migration re-adds the (nullable) columns; it is fully reversible
schema-wise (deleted stubs are not resurrected — their content lives in draft).

Removes every draft_only / include_draft_only reference from the backend:
handlers, list filters (draft-only items no longer exist in those tables so the
filters are dropped), INSERT/SELECT column lists, NewScript/NewFlow and
*WithDraft structs, the deploy no-op comparison, the delete-permission check
(deleting always requires admin now), git-sync's draft-only skip, and openapi.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-03 16:12:12 +02:00

116 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(),
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(())
}
}