use workflow_context::{WorkerContext, ContextShell, OutputFormat, FindOptions, Vgcs, ContextStore}; use tempfile::TempDir; const ZERO_OID: &str = "0000000000000000000000000000000000000000"; fn setup_env() -> (TempDir, String, String) { let temp_dir = TempDir::new().unwrap(); let data_path = temp_dir.path().to_str().unwrap().to_string(); let req_id = "req-shell-test".to_string(); // Init Repo let vgcs = Vgcs::new(&data_path); vgcs.init_repo(&req_id).unwrap(); (temp_dir, data_path, req_id) } #[test] fn test_shell_comprehensive() -> anyhow::Result<()> { let (_tmp, data_path, req_id) = setup_env(); // 1. Setup Initial Context let mut ctx = WorkerContext::new(&data_path, &req_id, ZERO_OID); ctx.write_file("README.md", "Project Root\n\nIntroduction here.")?; ctx.write_file("src/main.rs", "fn main() {\n println!(\"Hello\");\n println!(\"Hello\");\n}")?; // Double Hello for ambiguity test ctx.write_file("src/util.rs", "pub fn util() -> i32 { 42 }")?; ctx.write_file("data/config.json", "{\n \"key\": \"value\",\n \"retries\": 3\n}")?; ctx.write_file("文档/说明.txt", "这是一个中文文件。")?; // Unicode Path & Content let commit_1 = ctx.commit("Init")?; let mut ctx = WorkerContext::new(&data_path, &req_id, &commit_1); // --- Find Tests --- println!("Testing Find..."); // Test: Recursive vs Non-recursive // Note: Includes directories (src, data, 文档) + files (5) = 8 let all_nodes = ctx.find("**/*", FindOptions { recursive: true, ..Default::default() })?; assert_eq!(all_nodes.len(), 8); // Test: Only Files let only_files = ctx.find("**/*", FindOptions { recursive: true, type_filter: Some("File".to_string()), ..Default::default() })?; assert_eq!(only_files.len(), 5); // Test: Non-recursive (Top level) let root_nodes = ctx.find("*", FindOptions { recursive: false, ..Default::default() })?; // Expect README.md, src(dir), data(dir), 文档(dir) assert!(root_nodes.iter().any(|f| f.path == "README.md")); assert!(root_nodes.iter().any(|f| f.path == "src")); // Test: Type Filter (Dir) let dirs = ctx.find("**/*", FindOptions { recursive: true, type_filter: Some("Dir".to_string()), ..Default::default() })?; assert!(dirs.iter().any(|d| d.path == "src")); assert!(dirs.iter().any(|d| d.path == "data")); assert!(dirs.iter().any(|d| d.path == "文档")); assert!(!dirs.iter().any(|d| d.path == "README.md")); // --- Grep Tests --- println!("Testing Grep..."); // Test: Regex Match let matches = ctx.grep(r"fn \w+\(\)", None)?; assert_eq!(matches.len(), 2); // main() and util() // Test: Unicode Content let zh_matches = ctx.grep("中文", None)?; assert_eq!(zh_matches.len(), 1); assert_eq!(zh_matches[0].path, "文档/说明.txt"); // Test: Invalid Regex let bad_regex = ctx.grep("(", None); assert!(bad_regex.is_err()); // --- Patch Tests --- println!("Testing Patch..."); // Test: Ambiguous Match (Safety Check) // src/main.rs has two "println!(\"Hello\");" let res = ctx.patch("src/main.rs", "println!(\"Hello\");", "println!(\"World\");"); assert!(res.is_err(), "Should fail on ambiguous match"); let err_msg = res.unwrap_err().to_string(); assert!(err_msg.contains("Ambiguous match"), "Error message mismatch: {}", err_msg); // Test: Unique Match // Patch "Introduction here." to "Intro v2." in README.md ctx.patch("README.md", "Introduction here.", "Intro v2.")?; ctx.commit("Patch 1")?; // Must commit to verify via read (if read uses committed state) // Verify let readme = ctx.read_text("README.md")?; assert!(readme.contains("Intro v2.")); // Test: Special Characters (Literal Match) // Let's try to patch JSON which has braces and quotes ctx.patch("data/config.json", "\"retries\": 3", "\"retries\": 5")?; ctx.commit("Patch 2")?; let config = ctx.read_text("data/config.json")?; assert!(config.contains("\"retries\": 5")); // Test: Cross-line Patch // Replace the whole function body in util.rs let old_block = "pub fn util() -> i32 { 42 }"; let new_block = "pub fn util() -> i32 {\n return 100;\n}"; ctx.patch("src/util.rs", old_block, new_block)?; ctx.commit("Patch 3")?; let util = ctx.read_text("src/util.rs")?; assert!(util.contains("return 100;")); // Test: Patch non-existent file let res = ctx.patch("ghost.txt", "foo", "bar"); assert!(res.is_err()); Ok(()) } #[test] fn test_tool_schema_validity() { let defs = WorkerContext::get_tool_definitions(); assert!(defs.is_array()); let arr = defs.as_array().unwrap(); // Verify critical fields exist for OpenAI for tool in arr { let obj = tool.as_object().unwrap(); assert_eq!(obj["type"], "function"); let func = obj["function"].as_object().unwrap(); assert!(func.contains_key("name")); assert!(func.contains_key("description")); assert!(func.contains_key("parameters")); } }