From c4c8be9af0528afd9b45eb3d2cec2f3b705e7ced Mon Sep 17 00:00:00 2001 From: Ruben Fiszel Date: Fri, 20 Feb 2026 07:04:56 +0000 Subject: [PATCH] test: add unit tests for wmill_scopes and allowed_domains parsing Co-Authored-By: Claude Opus 4.5 --- backend/windmill-queue/src/jobs.rs | 93 +++++++++++++++++++++++++++ backend/windmill-sandbox/src/types.rs | 31 +++++++++ 2 files changed, 124 insertions(+) diff --git a/backend/windmill-queue/src/jobs.rs b/backend/windmill-queue/src/jobs.rs index 08ef1cb4df..b50595bbed 100644 --- a/backend/windmill-queue/src/jobs.rs +++ b/backend/windmill-queue/src/jobs.rs @@ -6275,3 +6275,96 @@ pub async fn get_same_worker_job( )) }) } + +#[cfg(test)] +mod tests { + use super::parse_wmill_scopes; + + #[test] + fn test_parse_wmill_scopes_double_slash() { + let code = "// wmill_scopes: read:resource,read:variable\nconst x = 1;"; + let scopes = parse_wmill_scopes(code).unwrap(); + assert_eq!(scopes, vec!["read:resource", "read:variable"]); + } + + #[test] + fn test_parse_wmill_scopes_hash() { + let code = "# wmill_scopes: run:script,read:resource\nimport os"; + let scopes = parse_wmill_scopes(code).unwrap(); + assert_eq!(scopes, vec!["run:script", "read:resource"]); + } + + #[test] + fn test_parse_wmill_scopes_double_dash() { + let code = "-- wmill_scopes: read:resource\nSELECT 1;"; + let scopes = parse_wmill_scopes(code).unwrap(); + assert_eq!(scopes, vec!["read:resource"]); + } + + #[test] + fn test_parse_wmill_scopes_skips_shebang() { + let code = "#!/bin/bash\n# wmill_scopes: run:script\necho hello"; + let scopes = parse_wmill_scopes(code).unwrap(); + assert_eq!(scopes, vec!["run:script"]); + } + + #[test] + fn test_parse_wmill_scopes_none_when_absent() { + let code = "import os\ndef main():\n pass"; + assert!(parse_wmill_scopes(code).is_none()); + } + + #[test] + fn test_parse_wmill_scopes_none_when_empty() { + let code = "// wmill_scopes:\nconst x = 1;"; + assert!(parse_wmill_scopes(code).is_none()); + } + + #[test] + fn test_parse_wmill_scopes_trims_whitespace() { + let code = "// wmill_scopes: read:resource , write:variable \n"; + let scopes = parse_wmill_scopes(code).unwrap(); + assert_eq!(scopes, vec!["read:resource", "write:variable"]); + } + + #[test] + fn test_parse_wmill_scopes_not_in_first_20_lines() { + let mut code = String::new(); + for _ in 0..25 { + code.push_str("// some comment\n"); + } + code.push_str("// wmill_scopes: read:resource\n"); + assert!(parse_wmill_scopes(&code).is_none()); + } + + #[test] + fn test_parse_wmill_scopes_within_first_20_lines() { + let mut code = String::new(); + for _ in 0..18 { + code.push_str("// some comment\n"); + } + code.push_str("// wmill_scopes: read:resource\n"); + let scopes = parse_wmill_scopes(&code).unwrap(); + assert_eq!(scopes, vec!["read:resource"]); + } + + #[test] + fn test_parse_wmill_scopes_with_leading_whitespace() { + let code = " // wmill_scopes: read:resource\n"; + let scopes = parse_wmill_scopes(code).unwrap(); + assert_eq!(scopes, vec!["read:resource"]); + } + + #[test] + fn test_parse_wmill_scopes_single_scope() { + let code = "# wmill_scopes: read:resource\n"; + let scopes = parse_wmill_scopes(code).unwrap(); + assert_eq!(scopes, vec!["read:resource"]); + } + + #[test] + fn test_parse_wmill_scopes_ignores_non_comment_lines() { + let code = "wmill_scopes: read:resource\ndef main(): pass"; + assert!(parse_wmill_scopes(code).is_none()); + } +} diff --git a/backend/windmill-sandbox/src/types.rs b/backend/windmill-sandbox/src/types.rs index 6f84627cf7..65d341dbf0 100644 --- a/backend/windmill-sandbox/src/types.rs +++ b/backend/windmill-sandbox/src/types.rs @@ -285,6 +285,37 @@ mod tests { assert_eq!(config.allowed_domains, vec!["api.example.com"]); } + #[test] + fn test_parse_allowed_domains_with_sandbox_and_volume() { + let code = "# sandbox: py-env:v1\n\ + # volume: data:/mnt/data\n\ + # allowed_domains: api.example.com, cdn.example.com\n\ + def main(): pass\n"; + let config = parse_sandbox_config(code); + assert!(config.snapshot.is_some()); + assert_eq!(config.volumes.len(), 1); + assert_eq!( + config.allowed_domains, + vec!["api.example.com", "cdn.example.com"] + ); + } + + #[test] + fn test_parse_allowed_domains_last_wins() { + let code = "# allowed_domains: first.com\n# allowed_domains: second.com, third.com\n"; + let config = parse_sandbox_config(code); + assert_eq!(config.allowed_domains, vec!["second.com", "third.com"]); + } + + #[test] + fn test_parse_allowed_domains_standalone() { + let code = "// allowed_domains: api.openai.com\nasync function main() { }"; + let config = parse_sandbox_config(code); + assert!(config.snapshot.is_none()); + assert!(config.volumes.is_empty()); + assert_eq!(config.allowed_domains, vec!["api.openai.com"]); + } + #[test] fn test_parse_ignores_unrelated_comments() { let code = "# This is a normal comment\n\