Files
windmill/backend/tests/script_auto_kind_failure.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

122 lines
3.5 KiB
Rust

use std::collections::HashMap;
use sqlx::{Pool, Postgres};
use windmill_api_client::types::{NewScript, ScriptLang};
use windmill_test_utils::init_client;
fn quick_ns(content: &str, path: &str, kind: Option<&str>) -> NewScript {
NewScript {
content: content.into(),
language: ScriptLang::Bun,
lock: None,
parent_hash: None,
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: kind.map(|s| s.to_string()),
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,
}
}
/// Regression: a `failure`-kind script must never be marked `auto_kind = 'lib'`
/// even if the parser fails to detect a `main` function, because the flow
/// error-handler picker filters out lib scripts and would otherwise hide it.
#[sqlx::test(fixtures("base"))]
async fn failure_kind_script_without_main_is_not_marked_lib(
db: Pool<Postgres>,
) -> anyhow::Result<()> {
let (client, _port, _s) = init_client(db.clone()).await;
// Content with no `main` — TS parser would normally set auto_kind = 'lib'.
client
.create_script(
"test-workspace",
&quick_ns(
"export function notMain() { return 42 }",
"u/test-user/failure_no_main",
Some("failure"),
),
)
.await
.unwrap();
let auto_kind: Option<String> = sqlx::query_scalar(
"SELECT auto_kind FROM script \
WHERE workspace_id = $1 AND path = $2",
)
.bind("test-workspace")
.bind("u/test-user/failure_no_main")
.fetch_one(&db)
.await?;
assert_ne!(
auto_kind.as_deref(),
Some("lib"),
"failure-kind script must not be marked as 'lib' auto_kind, got {:?}",
auto_kind
);
Ok(())
}
/// Sibling: a normal `script` kind WITHOUT main should still be marked `lib`
/// (so it stays hidden from the regular script picker). Guards against an
/// over-broad sanitizer accidentally clearing the value for plain scripts.
#[sqlx::test(fixtures("base"))]
async fn regular_script_without_main_is_still_marked_lib(
db: Pool<Postgres>,
) -> anyhow::Result<()> {
let (client, _port, _s) = init_client(db.clone()).await;
client
.create_script(
"test-workspace",
&quick_ns(
"export function notMain() { return 42 }",
"u/test-user/script_no_main",
Some("script"),
),
)
.await
.unwrap();
let auto_kind: Option<String> = sqlx::query_scalar(
"SELECT auto_kind FROM script \
WHERE workspace_id = $1 AND path = $2",
)
.bind("test-workspace")
.bind("u/test-user/script_no_main")
.fetch_one(&db)
.await?;
assert_eq!(
auto_kind.as_deref(),
Some("lib"),
"regular script without main should be marked 'lib', got {:?}",
auto_kind
);
Ok(())
}