Fundamental_Analysis/crates/workflow-context/tests/worker_runtime_tests.rs
Lv, Qi efd2c42775 feat(workflow): Implement Worker Runtime with Context Shell
- Implemented `ContextShell` trait providing `ls`, `find`, `grep`, `cat`, `patch`, `wc` primitives.
- Added `WorkerContext` struct acting as the shell interface for LLM workers.
- Implemented `get_tool_definitions` to export OpenAI-compatible tool schemas.
- Added `patch` command for atomic, exact-match text replacement.
- Added comprehensive tests covering happy paths and edge cases (ambiguity, unicode, regex errors).
- Updated design documentation.
2025-11-27 01:50:48 +08:00

143 lines
5.1 KiB
Rust

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"));
}
}