diff --git a/src/platform/enterprise.rs b/src/platform/enterprise.rs index 08d8b7f..46acd34 100644 --- a/src/platform/enterprise.rs +++ b/src/platform/enterprise.rs @@ -157,7 +157,14 @@ pub fn build_launch_args( args.push(execute.clone()); } if let Some(c) = &launch.c { - args.push(quoted_c_arg(c)); + // `/C` and its payload must go as two separate argv tokens. + // std::process::Command bypasses the shell on both Linux (execve) and + // Windows (CreateProcess), so any surrounding `"..."` quoting would end + // up inside the value itself — the platform then sees `/C"…"` as a + // single unrecognized key and fails with «Неверные или отсутствующие + // параметры соединения с информационной базой». + args.push("/C".to_owned()); + args.push(c.clone()); } let mut extra_args = Vec::new(); @@ -182,10 +189,6 @@ fn effective_out_path(launch: &LaunchOptions) -> Option<&str> { launch.internal_out.as_deref().or(launch.out.as_deref()) } -fn quoted_c_arg(payload: &str) -> String { - format!("/C\"{payload}\"") -} - fn filtered_raw_launch_args(args: &[String]) -> Vec { let mut filtered = Vec::new(); let mut skip_value = false; @@ -262,9 +265,8 @@ mod tests { assert_eq!(args[1], "/DisableStartupDialogs"); assert!(args.iter().any(|arg| arg == "/TESTMANAGER")); assert!(args - .iter() - .any(|arg| arg == "/C\"RunUnitTests=/tmp/path with space/тест config.json\"")); - assert!(!args.iter().any(|arg| arg == "/C")); + .windows(2) + .any(|pair| pair == ["/C", "RunUnitTests=/tmp/path with space/тест config.json"])); assert!(args.iter().any(|arg| arg == "/Out")); } @@ -288,10 +290,11 @@ mod tests { .iter() .any(|arg| arg == "/tmp/va/vanessa automation.epf")); assert!(args.iter().any(|arg| arg == "/TESTMANAGER")); - assert!(args - .iter() - .any(|arg| arg == "/C\"StartFeaturePlayer;VAParams=/tmp/va/va-params.json\"")); - assert!(!args.iter().any(|arg| arg == "/C")); + assert!(args.windows(2).any(|pair| pair + == [ + "/C", + "StartFeaturePlayer;VAParams=/tmp/va/va-params.json", + ])); } #[test] diff --git a/src/use_cases/launch_app.rs b/src/use_cases/launch_app.rs index d055c11..b3f1ed2 100644 --- a/src/use_cases/launch_app.rs +++ b/src/use_cases/launch_app.rs @@ -559,7 +559,7 @@ mod tests { assert_eq!(result.mcp_port, Some(9874)); let args = fs::read_to_string(args_log).expect("args log"); assert!(args.contains("ENTERPRISE")); - assert!(args.contains("/C\"runMcp;mcpPort=9874\"")); + assert!(args.contains("/C\nrunMcp;mcpPort=9874\n")); assert!(!args.contains("/LoadCfg")); assert!(!args.contains("-Extension")); } diff --git a/src/use_cases/vanessa.rs b/src/use_cases/vanessa.rs index db6ce1e..5ddec7d 100644 --- a/src/use_cases/vanessa.rs +++ b/src/use_cases/vanessa.rs @@ -226,10 +226,12 @@ fn apply_test_overlay(object: &mut Map, artifacts: VanessaTestArt Value::Bool(true), ); object.insert("ДелатьОтчетВФорматеjUnit".to_owned(), Value::Bool(true)); + let junit_dir = Value::String(artifacts.junit_dir.display().to_string()); object.insert( "КаталогВыгрузкиJUnit".to_owned(), - Value::String(artifacts.junit_dir.display().to_string()), + junit_dir.clone(), ); + ensure_object(object, "ОтчетJUnit").insert("КаталогВыгрузкиJUnit".to_owned(), junit_dir); apply_logging_overlay( object, artifacts.runner_log, @@ -237,6 +239,16 @@ fn apply_test_overlay(object: &mut Map, artifacts: VanessaTestArt ); } +fn ensure_object<'a>(object: &'a mut Map, key: &str) -> &'a mut Map { + if !object.get(key).is_some_and(Value::is_object) { + object.insert(key.to_owned(), Value::Object(Map::new())); + } + object + .get_mut(key) + .and_then(Value::as_object_mut) + .expect("object value was just inserted") +} + fn apply_logging_overlay( object: &mut Map, text_log_path: &Path, diff --git a/tests/cli_launch.rs b/tests/cli_launch.rs index 1496ddb..3fc38bb 100644 --- a/tests/cli_launch.rs +++ b/tests/cli_launch.rs @@ -359,7 +359,7 @@ fn launch_ordinary_supports_typed_keys_and_filters_reserved_raw_duplicates() { assert!(args.contains("/UsePrivilegedMode")); assert!(args.contains("/Execute")); assert!(args.contains("/tmp/tool.epf")); - assert!(args.contains("/C\"DoWork\"")); + assert!(args.contains("/C\nDoWork\n")); assert!(args.contains("DoWork")); assert!(args.contains("/WA-")); assert!(args.contains("/tmp/user.out.log")); @@ -411,7 +411,7 @@ fn launch_mcp_va_builds_payload_from_configured_port_and_ordinary_mode() { assert!(args.contains("/RunModeOrdinaryApplication")); assert!(args.contains("/Execute")); assert!(args.contains("vanessa-automation.epf")); - assert!(args.contains("/C\"runMcp=/tmp/mcp conf.json;mcpPort=9874;VAParams=")); + assert!(args.contains("/C\nrunMcp=/tmp/mcp conf.json;mcpPort=9874;VAParams=")); assert!(!args.contains("StartFeaturePlayer")); assert!(args.contains("/TESTMANAGER")); assert!(args.contains("/WA-")); @@ -741,7 +741,7 @@ fn launch_mcp_transport_emits_runmcp_payload_and_mcp_envelope() { assert!(payload["data"]["client_uid"].is_null()); let args = fs::read_to_string(args_log).expect("args log"); - assert!(args.contains("/C\"runMcp;mcpPort=9999\"")); + assert!(args.contains("/C\nrunMcp;mcpPort=9999\n")); assert!(!args.contains("mcpMode=ws")); } @@ -861,7 +861,7 @@ fn launch_mcp_auto_falls_back_to_mcp_when_manager_unreachable() { let payload: Value = serde_json::from_slice(&output.stdout).expect("json"); assert_eq!(payload["data"]["transport"], "mcp"); let args = fs::read_to_string(args_log).expect("args log"); - assert!(args.contains("/C\"runMcp\"")); + assert!(args.contains("/C\nrunMcp\n")); assert!(!args.contains("mcpMode=ws")); } diff --git a/tests/cli_test.rs b/tests/cli_test.rs index 823da66..10fb9af 100644 --- a/tests/cli_test.rs +++ b/tests/cli_test.rs @@ -773,6 +773,10 @@ fn test_va_builds_vanessa_command_and_overlay() { .as_str() .expect("КаталогВыгрузкиJUnit") .contains("/junit")); + assert!(params["ОтчетJUnit"]["КаталогВыгрузкиJUnit"] + .as_str() + .expect("ОтчетJUnit.КаталогВыгрузкиJUnit") + .contains("/junit")); assert_eq!(params["ДелатьЛогВыполненияСценариевВТекстовыйФайл"], true); assert_eq!(params["ВыводитьВЛогВыполнениеШагов"], true); assert_eq!(params["ПодробныйЛогВыполненияСценариев"], 1);