* feat: add script module mode with folder model for Bun and Python Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: add missing modules field to RawCode in bun_executor Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * sqlx * feat: enrich WAC templates with checkpoint and replay semantics Add prominent comments explaining that all computation must happen inside task/step/taskScript or it will be replayed on resume/retry. Clarify that waitForApproval does not hold a worker and that approve/reject URLs are available in the timeline step details. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(cli): script module sync idempotency, per-module hash tracking, and preview support - Fix pull→push idempotency: use `??` instead of `||` for module lock field so empty strings are preserved (matches API's `lock: ""`) - Add per-module hash tracking in wmill-lock.yaml following the flow inline script pattern (SCRIPT_TOP_HASH + per-module subpath hashes) - Selective module lock regeneration: only regenerate locks for modules whose content actually changed, not all modules - Use unfiltered rawWorkspaceDependencies for module hashes to match what updateModuleLocks passes to fetchScriptLock - Show changed module names in stale script output for clarity - Add module support to `script preview` command: read modules from __mod/ folder and pass them in the preview API request - Add preview tests for taskScript pattern (flat and folder layout) - Update test assertion for module stale detection output Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat(frontend): WAC UI improvements — reorder templates, module tab rename, import consolidation - Reorder WAC template buttons: TypeScript before Python in ScriptBuilder, CreateActionsScript, and CreateActionsFlow - Remove dropdown items from +Script button (simplify to direct link) - Move "Import Workflow-as-Code" to +Flow dropdown with dedicated drawer - Add module tab rename: pencil icon on hover opens popover with validation, fixed-width icon container prevents layout shift Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat: remaining module-mode changes from working branch - Backend parser updates for WAC detection - CLI sync/types updates for raw app path and module support - Frontend UI polish (Dev.svelte, ScriptRow, script hash page) - Test fixture updates Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * test(cli): add test for module modification detection in generate-metadata Verifies that modifying a single module file re-triggers stale detection and only the changed module is listed, not all modules. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(backend): critical fixes from PR review - Fix hardcoded dev path in bun_executor.rs WAC v2 wrapper — use "windmill-client" import instead of absolute filesystem path - Fix missed no_main_func → auto_kind rename in parser TS test - Add modules column to clone_script SQL (windmill-common and windmill-api-workspaces) so cloned scripts retain their modules - Add modules: None to RawCode structs in worker tests - Restore complete sqlx cache (merge main's cache + our new queries) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(backend): fix clone warning treated as error in CI Change `.clone()` on double reference to `*k` dereference in scripts.rs hash implementation. Update sqlx cache with new query hashes from modified clone_script SQL. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(frontend): use published parser wasm versions for CI build The local file:// paths for windmill-parser-wasm-py and windmill-parser-wasm-ts don't exist in the Cloudflare Pages build environment. Revert to published npm versions (1.655.0). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(frontend): update parser wasm packages to 1.657.2 Use newly published windmill-parser-wasm-ts and windmill-parser-wasm-py v1.657.2 which include auto_kind/WAC detection changes. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(frontend): regenerate package-lock.json for npm ci compatibility Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(frontend): use main's lockfile as base, update only parser wasm packages Regenerating package-lock.json from scratch pulled different dependency versions causing svelte-check type errors. Instead, start from main's lockfile and only update the two changed packages. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(backend): add modules column to fetch_script_for_update query The Script<SR> struct has a modules field (FromRow), but fetch_script_for_update didn't SELECT modules, causing a runtime error "no column found for name: modules" when the worker processed dependency jobs. This was the root cause of the relock_skip test timeout. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(backend): fix script module execution for Python and Bun - Fix modules not passed through job queue: inject _MODULES into PushArgs.extra when pushing Code jobs so worker can extract them - Fix Python module imports: use relative imports (from .helper) and add sys.path.insert for module directory in wrapper - Fix Python tests: use relative imports and empty lock to prevent pip from resolving module names as packages - Add local file check in Bun loader for module resolution - Ignore Bun module test (bundle mode loader integration tracked separately) - Add missing modules column to fetch_script_for_update query Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(backend): remove unnecessary empty lock in Python module tests Relative imports (from .helper) are not parsed as pip packages, so the empty lock workaround is not needed. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(backend): fix module execution for Python and Bun — all tests pass Python modules: - Use relative imports (from .helper import greet) since scripts run as packages - Add sys.path.insert for module directory in wrapper to ensure local modules take precedence over pip packages with same name Bun modules: - Use bundled output (./out/main.js) as wrapper import when modules are present — the bundled output has module content inlined by Bun.build, avoiding runtime loader resolution issues - Add local file check in loader.bun.js onResolve to short-circuit API URL resolution for module files on disk Job queue: - Inject _MODULES into PushArgs.extra when pushing Code jobs so the worker can extract them at execution time Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * refactor: address PR review — simplify, fix correctness, remove dead code Critical fixes: - Replace all CLI `no_main_func` references with `auto_kind` (string) to match the backend migration and API changes - Remove duplicated `compute_python_module_dir` in worker.rs, use the canonical version from python_executor.rs High priority: - Auto-create `__init__.py` in intermediate directories for nested Python modules so imports like `from .utils.math import add` work without users manually creating __init__.py files - Remove redundant `sys_path_insert` — relative imports use Python's package system, not sys.path Medium: - Fix lock file base name extraction: use regex to strip only the final extension (`.replace(/\.[^.]+$/, '')`) instead of `indexOf(".")` which breaks for files like `helper.test.ts` Simplification: - Remove dead `{#if false}` Popover block in ScriptEditor.svelte - Guard loader.bun.js local file check to only run for relative paths (matching the Windows loader pattern) - Add clarifying comment on Bun dual mechanism (build + run phases) - Add maintenance comment on manual Hash impl for NewScript Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: final review fixes — stale cleanup, baseName, auto_kind export - Fix sync.ts baseName extraction using indexOf(".") → regex (same fix as script.ts/metadata.ts, missed this instance) - Add stale module file cleanup in writeModulesToDisk: removes files from __mod/ that are no longer in the modules map before writing, fixing the pull→push cycle that couldn't delete modules - Log warning when _MODULES serialization fails in job push instead of silently dropping modules - Use strict equality (===) for auto_kind comparison - Exclude auto_kind from workspace export — it is auto-detected by the parser at deploy time from script content Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(cli): remove auto_kind from push, comparison, and metadata auto_kind is auto-detected by the parser at deploy time, so the CLI should not send it, compare it, or write it to script.yaml. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: remove erroneously added backend/backend/.sqlx directory Duplicate .sqlx cache was committed at the wrong nested path. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: address PR review feedback + fix CI dead_code warning Frontend (ScriptEditor.svelte): - Fix switchToMain() missing lastSyncedCode update — prevents stale code sync on external changes while editing a module tab - Fix formatAction saving module code to main script's localStorage draft — now saves main code when on a module tab - Fix non-null assertion on inferModuleLang in renameModule — fall back to original language instead of force unwrap - Remove redundant activeModuleTab truthy check in runTest CLI (script.ts): - Clean up empty directories after removing stale module files in writeModulesToDisk Backend: - Add path traversal guard in write_module_files — reject module paths containing ".." - Fix dead_code warning on auto_kind field in workspace export struct Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat(frontend): improve auto_kind UX + address review findings - Rename "Include without main function" toggle to "Include library scripts" in script list (ItemsList.svelte) - Update NoMainFuncBadge: "No main" → "Library" with clearer tooltip - Filter module file extensions by main script language — Python scripts only allow .py modules, TypeScript only .ts, etc. - Split flushModuleState into flushModuleContent (no UI side-effect) and flushModuleState (flush + reset tab), reducing duplication - Dynamic placeholder and hint text in add module popover based on main script language Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
540 lines
19 KiB
Rust
540 lines
19 KiB
Rust
use sqlx::{Pool, Postgres};
|
|
use tokio_stream::StreamExt;
|
|
|
|
use windmill_api_client::types::NewScript;
|
|
use windmill_test_utils::{
|
|
in_test_worker, init_client, listen_for_completed_jobs, rebuild_dmap, ApiServer,
|
|
};
|
|
|
|
mod dependency_map {
|
|
use super::*;
|
|
fn quick_ns(
|
|
content: &str,
|
|
language: windmill_api_client::types::ScriptLang,
|
|
path: &str,
|
|
lock: Option<String>,
|
|
parent_hash: Option<String>,
|
|
) -> NewScript {
|
|
NewScript {
|
|
content: content.into(),
|
|
language,
|
|
lock,
|
|
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: std::collections::HashMap::new(),
|
|
ws_error_handler_muted: Some(false),
|
|
priority: None,
|
|
delete_after_use: 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,
|
|
}
|
|
}
|
|
async fn init(db: Pool<Postgres>) -> (windmill_api_client::Client, u16, ApiServer) {
|
|
init_client(db).await
|
|
}
|
|
|
|
async fn _clear_dmap(db: &Pool<Postgres>) {
|
|
sqlx::query!("DELETE FROM dependency_map WHERE workspace_id = 'test-workspace'")
|
|
.execute(db)
|
|
.await
|
|
.unwrap();
|
|
}
|
|
|
|
/// Corrects map according to provided replacements.
|
|
/// Only changes importer_path and/or id
|
|
/// Does not affect imported_path nor kind!
|
|
fn corrected_dmap(replacements: Vec<(&str, &str)>) -> Vec<(String, String, String, String)> {
|
|
CORRECT_DMAP
|
|
.clone()
|
|
.into_iter()
|
|
.map(|e| {
|
|
let mut r = (
|
|
e.0.to_owned(),
|
|
e.1.to_owned(),
|
|
e.2.to_owned(),
|
|
e.3.to_owned(),
|
|
);
|
|
for (from, to) in &replacements {
|
|
r = (
|
|
r.0.replace(from, to),
|
|
r.1, // Kind should be immutable
|
|
r.2, // Imported path should be immutable
|
|
// We do not modify script contents in test, so we can assume scripts always import the same path
|
|
// Modification of kind or imported path considered to be incorrect.
|
|
r.3.replace(from, to),
|
|
);
|
|
}
|
|
r
|
|
})
|
|
.collect()
|
|
}
|
|
|
|
async fn assert_dmap(
|
|
db: &Pool<Postgres>,
|
|
importer: Option<String>,
|
|
expected: Vec<(
|
|
impl Into<String>,
|
|
impl Into<String>,
|
|
impl Into<String>,
|
|
impl Into<String>,
|
|
)>,
|
|
) {
|
|
let mut dmap = sqlx::query_as::<_, (String, String, String, String)>(
|
|
"SELECT importer_path, importer_kind::text, imported_path, importer_node_id FROM dependency_map WHERE workspace_id = 'test-workspace' AND ($1::text IS NULL OR importer_path = $1::text)",
|
|
)
|
|
.bind(importer)
|
|
.fetch_all(db)
|
|
.await
|
|
.unwrap();
|
|
|
|
let mut expected = expected
|
|
.into_iter()
|
|
.map(|(f, s, t, fo)| (f.into(), s.into(), t.into(), fo.into()))
|
|
.collect::<Vec<(String, String, String, String)>>();
|
|
|
|
dmap.sort();
|
|
expected.sort();
|
|
|
|
assert_eq!(dmap, expected);
|
|
}
|
|
|
|
lazy_static::lazy_static! {
|
|
pub static ref CORRECT_DMAP: Vec<(&'static str, &'static str, &'static str, &'static str)> = vec![
|
|
("f/rel/root_script", "script", "dependencies/test.requirements.in", ""),
|
|
("f/rel/root_flow", "flow", "dependencies/test.requirements.in", "nstep1"),
|
|
("f/rel/root_app", "app", "dependencies/test.requirements.in", "dontpressmeplz"),
|
|
("f/rel/branch", "script", "f/rel/leaf_1", ""),
|
|
("f/rel/root_script", "script", "f/rel/branch", ""),
|
|
("f/rel/root_script", "script", "f/rel/leaf_1", ""),
|
|
("f/rel/root_script", "script", "f/rel/leaf_2", ""),
|
|
("f/rel/root_app", "app", "f/rel/leaf_2", "dontpressmeplz"),
|
|
("f/rel/root_flow", "flow", "f/rel/leaf_2", "failure"),
|
|
("f/rel/root_flow", "flow", "f/rel/branch", "nstep1"),
|
|
("f/rel/root_flow", "flow", "f/rel/leaf_1", "nstep1"),
|
|
("f/rel/root_flow", "flow", "f/rel/leaf_2", "nstep1"),
|
|
("f/rel/root_flow", "flow", "f/rel/leaf_2", "nstep2_2"),
|
|
("f/rel/root_flow", "flow", "f/rel/branch", "nstep4_1"),
|
|
("f/rel/root_flow", "flow", "f/rel/branch", "nstep5_1"),
|
|
("f/rel/root_flow", "flow", "f/rel/leaf_1", "nstep5_1"),
|
|
("f/rel/root_flow", "flow", "f/rel/leaf_2", "nstep5_1"),
|
|
("f/rel/root_flow", "flow", "f/rel/branch", "preprocessor"),
|
|
("f/rel/root_flow", "flow", "f/rel/leaf_1", "preprocessor"),
|
|
("f/rel/root_flow", "flow", "f/rel/leaf_2", "preprocessor"),
|
|
("f/rel/root_app", "app", "f/rel/branch", "pressmeplz"),
|
|
("f/rel/root_app", "app", "f/rel/leaf_1", "pressmeplz"),
|
|
("f/rel/root_app", "app", "f/rel/leaf_2", "pressmeplz"),
|
|
("f/rel/root_flow", "flow", "f/rel/leaf_2", "qtool1"),
|
|
("f/rel/root_app", "app", "f/rel/branch", "youcanpressme"),
|
|
|
|
// Default
|
|
("f/rel/leaf_1", "script", "dependencies/requirements.in", ""),
|
|
("f/rel/leaf_2", "script", "dependencies/requirements.in", ""),
|
|
("f/rel/branch", "script", "dependencies/requirements.in", ""),
|
|
("f/rel/root_flow", "flow", "dependencies/requirements.in", "failure"),
|
|
("f/rel/root_flow", "flow", "dependencies/package.json", "nstep2_1"),
|
|
("f/rel/root_flow", "flow", "dependencies/package.json", "nstep3_2"),
|
|
("f/rel/root_flow", "flow", "dependencies/requirements.in", "nstep2_2"),
|
|
("f/rel/root_flow", "flow", "dependencies/requirements.in", "nstep3_1"),
|
|
("f/rel/root_flow", "flow", "dependencies/requirements.in", "nstep4_1"),
|
|
("f/rel/root_flow", "flow", "dependencies/requirements.in", "nstep5_1"),
|
|
("f/rel/root_flow", "flow", "dependencies/requirements.in", "preprocessor"),
|
|
("f/rel/root_app", "app", "dependencies/requirements.in", "pressmeplz"),
|
|
("f/rel/root_flow", "flow", "dependencies/requirements.in", "qtool1"),
|
|
("f/rel/root_app", "app", "dependencies/requirements.in", "youcanpressme")
|
|
];
|
|
}
|
|
// TODO:
|
|
// Test that checks that we can run rebuild_dmap multiple times in tests.
|
|
|
|
#[cfg(feature = "python")]
|
|
#[sqlx::test(fixtures("base", "dependency_map"))]
|
|
async fn relative_imports_test_rebuild_correctness(db: Pool<Postgres>) -> anyhow::Result<()> {
|
|
let (client, _port, _s) = init(db.clone()).await;
|
|
assert_dmap(&db, None, CORRECT_DMAP.clone()).await;
|
|
// rebuild map
|
|
assert!(rebuild_dmap(&client).await);
|
|
assert_dmap(&db, None, CORRECT_DMAP.clone()).await;
|
|
Ok(())
|
|
}
|
|
|
|
#[cfg(feature = "python")]
|
|
#[sqlx::test(fixtures("base", "dependency_map"))]
|
|
async fn relative_imports_test_rebuild_lock(db: Pool<Postgres>) -> anyhow::Result<()> {
|
|
let (client, _port, _s) = init(db.clone()).await;
|
|
|
|
// Spawn first rebuild
|
|
let handle = {
|
|
let client = client.clone();
|
|
tokio::spawn(async move { rebuild_dmap(&client).await })
|
|
};
|
|
|
|
// Immidiately spawn another
|
|
let res = client
|
|
.client()
|
|
.post(format!(
|
|
"{}/w/test-workspace/workspaces/rebuild_dependency_map",
|
|
client.baseurl()
|
|
))
|
|
.send()
|
|
.await
|
|
.unwrap()
|
|
.text()
|
|
.await
|
|
.unwrap();
|
|
|
|
// Should tell us there is already rebuilt in progress
|
|
// Or if it is too fast we will be able to trigger it second time
|
|
assert!(&res == "There is already one task pending, try again later." || &res == "Success");
|
|
|
|
assert!(handle.await.unwrap());
|
|
Ok(())
|
|
}
|
|
|
|
// If you deploy from cli and you use raw requirements you don't want the script be included in dmap
|
|
// Otherwise script will be overwritten once any relative import is updated
|
|
// #[cfg(feature = "python")]
|
|
// #[sqlx::test(fixtures("base", "dependency_map"))]
|
|
// async fn relative_imports_test_with_legacy(db: Pool<Postgres>) -> anyhow::Result<()> {
|
|
// let (client, _port, _s) = init(db.clone()).await;
|
|
|
|
// client
|
|
// .create_script(
|
|
// "test-workspace",
|
|
// &quick_ns(
|
|
// "
|
|
// from f.rel.branch import main as br;
|
|
// from f.rel.leaf_1 import main as lf_1;
|
|
// from f.rel.leaf_2 import main as lf_2;
|
|
|
|
// def main():
|
|
// return [br(), lf_1(), lf_2];
|
|
// ",
|
|
// windmill_api_client::types::ScriptLang::Python3,
|
|
// "f/rel/root_script",
|
|
// Some("# from requirements.txt".to_string()),
|
|
// Some("000000000005165B".into()),
|
|
// ),
|
|
// )
|
|
// .await
|
|
// .unwrap();
|
|
|
|
// assert_dmap(
|
|
// &db,
|
|
// Some("f/rel/root_script".into()),
|
|
// vec![
|
|
// ("f/rel/root_script", "script", "f/rel/branch", ""),
|
|
// ("f/rel/root_script", "script", "f/rel/leaf_1", ""),
|
|
// ("f/rel/root_script", "script", "f/rel/leaf_2", ""),
|
|
// ],
|
|
// )
|
|
// .await;
|
|
|
|
// tokio::time::sleep(std::time::Duration::from_secs(13)).await;
|
|
|
|
// assert_dmap(
|
|
// &db,
|
|
// Some("f/rel/root_script".into()),
|
|
// vec![
|
|
// ("f/rel/root_script", "script", "f/rel/branch", ""),
|
|
// ("f/rel/root_script", "script", "f/rel/leaf_1", ""),
|
|
// ("f/rel/root_script", "script", "f/rel/leaf_2", ""),
|
|
// ],
|
|
// )
|
|
// .await;
|
|
|
|
// Ok(())
|
|
// }
|
|
|
|
// Consider simple one. Only referenced directly. No deep connections
|
|
#[cfg(feature = "python")]
|
|
#[sqlx::test(fixtures("base", "dependency_map"))]
|
|
async fn relative_imports_test_rename_leaf_2(db: Pool<Postgres>) -> anyhow::Result<()> {
|
|
let (client, port, _s) = init(db.clone()).await;
|
|
client
|
|
.create_script(
|
|
"test-workspace",
|
|
&quick_ns(
|
|
"
|
|
def main():
|
|
return 'leaf3';
|
|
",
|
|
windmill_api_client::types::ScriptLang::Python3,
|
|
"f/rel/leaf_2_renamed",
|
|
None,
|
|
Some("0000000000051659".into()),
|
|
),
|
|
)
|
|
.await
|
|
.unwrap();
|
|
|
|
let mut completed = listen_for_completed_jobs(&db).await;
|
|
in_test_worker(&db, completed.next(), port).await;
|
|
|
|
// Changing leafs should not change dependency map
|
|
assert_dmap(
|
|
&db,
|
|
None,
|
|
corrected_dmap(vec![("f/rel/leaf_2", "f/rel/leaf_2_renamed")]),
|
|
)
|
|
.await;
|
|
Ok(())
|
|
}
|
|
|
|
// Consider hard one. Referenced deeply and exists in double references.
|
|
#[cfg(feature = "python")]
|
|
#[sqlx::test(fixtures("base", "dependency_map"))]
|
|
async fn relative_imports_test_rename_leaf_1(db: Pool<Postgres>) -> anyhow::Result<()> {
|
|
let (client, port, _s) = init(db.clone()).await;
|
|
client
|
|
.create_script(
|
|
"test-workspace",
|
|
&quick_ns(
|
|
"
|
|
def main():
|
|
return 'leaf1';
|
|
",
|
|
windmill_api_client::types::ScriptLang::Python3,
|
|
"f/rel/leaf_1_renamed",
|
|
None,
|
|
Some("0000000000051658".into()),
|
|
),
|
|
)
|
|
.await
|
|
.unwrap();
|
|
|
|
let mut completed = listen_for_completed_jobs(&db).await;
|
|
in_test_worker(&db, completed.next(), port).await;
|
|
|
|
// Changing leafs should not change dependency map
|
|
assert_dmap(
|
|
&db,
|
|
None,
|
|
corrected_dmap(vec![("f/rel/leaf_1", "f/rel/leaf_1_renamed")]),
|
|
)
|
|
.await;
|
|
Ok(())
|
|
}
|
|
|
|
#[cfg(feature = "python")]
|
|
#[sqlx::test(fixtures("base", "dependency_map"))]
|
|
async fn relative_imports_test_rename_branch(db: Pool<Postgres>) -> anyhow::Result<()> {
|
|
let (client, port, _s) = init(db.clone()).await;
|
|
client
|
|
.create_script(
|
|
"test-workspace",
|
|
&quick_ns(
|
|
"
|
|
from f.rel.leaf_1 import main as lf_1;
|
|
|
|
def main():
|
|
return lf_1();
|
|
",
|
|
windmill_api_client::types::ScriptLang::Python3,
|
|
"f/rel/branch_renamed",
|
|
None,
|
|
Some("000000000005165A".into()),
|
|
),
|
|
)
|
|
.await
|
|
.unwrap();
|
|
|
|
let mut completed = listen_for_completed_jobs(&db).await;
|
|
in_test_worker(&db, completed.next(), port).await;
|
|
|
|
// Changing branches SHOULD change dependency map
|
|
// Though it should only change branch item in dmap when it is importer.
|
|
// All entries when branch is imported should not change.
|
|
let corrected_dmap = CORRECT_DMAP
|
|
.clone()
|
|
.iter_mut()
|
|
.map(|el| {
|
|
if el.0 == "f/rel/branch" {
|
|
el.0 = "f/rel/branch_renamed";
|
|
}
|
|
*el
|
|
})
|
|
.collect::<Vec<_>>();
|
|
assert_dmap(&db, None, corrected_dmap).await;
|
|
Ok(())
|
|
}
|
|
|
|
#[cfg(feature = "python")]
|
|
#[sqlx::test(fixtures("base", "dependency_map"))]
|
|
async fn relative_imports_test_rename_primary_script(db: Pool<Postgres>) -> anyhow::Result<()> {
|
|
let (client, port, _s) = init(db.clone()).await;
|
|
|
|
client
|
|
.create_script(
|
|
"test-workspace",
|
|
&quick_ns(
|
|
"
|
|
# requirements: test
|
|
from f.rel.branch import main as br;
|
|
from f.rel.leaf_1 import main as lf_1;
|
|
from f.rel.leaf_2 import main as lf_2;
|
|
|
|
def main():
|
|
return [br(), lf_1(), lf_2];
|
|
",
|
|
windmill_api_client::types::ScriptLang::Python3,
|
|
"f/rel/root_script_renamed",
|
|
None,
|
|
Some("000000000005165B".into()),
|
|
),
|
|
)
|
|
.await
|
|
.unwrap();
|
|
|
|
let corrected_dmap = corrected_dmap(vec![("root_script", "root_script_renamed")]);
|
|
let mut completed = listen_for_completed_jobs(&db).await;
|
|
in_test_worker(&db, completed.next(), port).await;
|
|
assert_dmap(&db, None, corrected_dmap.clone()).await;
|
|
Ok(())
|
|
}
|
|
|
|
#[cfg(feature = "python")]
|
|
#[sqlx::test(fixtures("base", "dependency_map"))]
|
|
async fn relative_imports_test_rename_primary_flow(db: Pool<Postgres>) -> anyhow::Result<()> {
|
|
use windmill_common::{cache::flow::fetch_version, flows::NewFlow, worker::to_raw_value};
|
|
|
|
let (client, port, _s) = init(db.clone()).await;
|
|
let flow = fetch_version(&db, 1443253234253454).await.unwrap();
|
|
let res = client
|
|
.client()
|
|
.post(format!(
|
|
"{}/w/test-workspace/flows/update/{}",
|
|
client.baseurl(),
|
|
"f/rel/root_flow" // encode_path()
|
|
))
|
|
.json(&NewFlow {
|
|
path: "f/rel/root_flow_renamed".into(),
|
|
summary: "".into(),
|
|
description: None,
|
|
value: to_raw_value(
|
|
&serde_json::from_str::<serde_json::Value>(
|
|
&serde_json::to_string(flow.value())
|
|
.unwrap()
|
|
.replace("nstep1", "Foxes")
|
|
.replace("nstep2_2", "like")
|
|
.replace("nstep_4_1", "Emeralds"),
|
|
)
|
|
.unwrap(),
|
|
),
|
|
schema: None,
|
|
draft_only: None,
|
|
tag: None,
|
|
dedicated_worker: None,
|
|
timeout: None,
|
|
deployment_message: None,
|
|
visible_to_runner_only: None,
|
|
on_behalf_of_email: None,
|
|
preserve_on_behalf_of: None,
|
|
ws_error_handler_muted: None,
|
|
})
|
|
.send()
|
|
.await
|
|
.unwrap();
|
|
|
|
assert_eq!(res.text().await.unwrap(), "f/rel/root_flow_renamed");
|
|
|
|
let mut completed = listen_for_completed_jobs(&db).await;
|
|
in_test_worker(&db, completed.next(), port).await;
|
|
|
|
assert_dmap(
|
|
&db,
|
|
None,
|
|
corrected_dmap(vec![
|
|
("f/rel/root_flow", "f/rel/root_flow_renamed"),
|
|
("nstep1", "Foxes"),
|
|
("nstep2_2", "like"),
|
|
("nstep_4_1", "Emeralds"),
|
|
]),
|
|
)
|
|
.await;
|
|
Ok(())
|
|
}
|
|
|
|
#[cfg(feature = "python")]
|
|
#[sqlx::test(fixtures("base", "dependency_map"))]
|
|
async fn relative_imports_test_rename_primary_app(db: Pool<Postgres>) -> anyhow::Result<()> {
|
|
let (client, port, _s) = init(db.clone()).await;
|
|
|
|
let app_value: String =
|
|
sqlx::query_scalar!("SELECT value::text FROM app_version WHERE id = 0 AND app_id = 2")
|
|
.fetch_one(&db)
|
|
.await
|
|
.unwrap()
|
|
.unwrap();
|
|
|
|
// TODO: There is:
|
|
// 1. update app
|
|
// 2. create app
|
|
// 3. update app raw
|
|
// Ideally all of them should be handled
|
|
let res = client
|
|
.client()
|
|
.post(format!(
|
|
"{}/w/test-workspace/apps/update/{}",
|
|
client.baseurl(),
|
|
"f/rel/root_app" // encode_path()
|
|
))
|
|
.json(&windmill_api::EditApp {
|
|
path: Some("f/rel/root_app_renamed".into()),
|
|
summary: None,
|
|
value: serde_json::from_str(
|
|
&app_value
|
|
.replace("dontpressmeplz", "Apps")
|
|
.replace("youcanpressme", "Work"),
|
|
)
|
|
.unwrap(),
|
|
policy: None,
|
|
deployment_message: None,
|
|
custom_path: None,
|
|
preserve_on_behalf_of: None,
|
|
})
|
|
.send()
|
|
.await
|
|
.unwrap();
|
|
|
|
assert_eq!(
|
|
res.text().await.unwrap(),
|
|
"app f/rel/root_app updated (npath: \"f/rel/root_app_renamed\")"
|
|
);
|
|
|
|
let mut completed = listen_for_completed_jobs(&db).await;
|
|
in_test_worker(&db, completed.next(), port).await;
|
|
|
|
assert_dmap(
|
|
&db,
|
|
None,
|
|
corrected_dmap(vec![
|
|
("f/rel/root_app", "f/rel/root_app_renamed"),
|
|
("dontpressmeplz", "Apps"),
|
|
("youcanpressme", "Work"),
|
|
]),
|
|
)
|
|
.await;
|
|
Ok(())
|
|
}
|
|
}
|