Files
freedak f7a720204a Update: 将子项目从 submodule 转为完整内容
- 移除 GovAI, nomifun-tauri, 算力盒子 的 submodule 引用
- 添加所有子项目的完整源代码
- 保留原始 .git 为 .git.bak 备份
2026-07-04 19:20:46 +08:00

376 lines
11 KiB
Rust

//! Integration tests for system prompt tool usage guidance (TC-4.3-01 through TC-4.3-08).
//!
//! Black-box tests verifying the "# Using your tools" section is correctly
//! assembled into the system prompt with proper content and ordering.
use std::fs;
use nomi_agent::context::{SystemPromptCache, build_system_prompt};
use nomi_skills::types::{ExecutionContext, LoadedFrom, SkillMetadata, SkillSource};
fn make_skill(name: &str, description: &str) -> SkillMetadata {
SkillMetadata {
name: name.to_string(),
display_name: None,
description: description.to_string(),
has_user_specified_description: false,
allowed_tools: vec![],
argument_hint: None,
argument_names: vec![],
when_to_use: None,
version: None,
model: None,
disable_model_invocation: false,
user_invocable: true,
execution_context: ExecutionContext::Inline,
agent: None,
effort: None,
shell: None,
paths: vec![],
hooks_raw: None,
source: SkillSource::User,
loaded_from: LoadedFrom::Skills,
content: String::new(),
content_length: 0,
skill_root: None,
}
}
// ---------------------------------------------------------------------------
// TC-4.3-01: Tool guidance section exists
// ---------------------------------------------------------------------------
#[test]
fn tc_4_3_01_tool_guidance_section_exists() {
let result = build_system_prompt(
&mut SystemPromptCache::new(),
None,
"/tmp",
"test-model",
&[],
None,
None,
false,
false,
false, // browser_enabled
);
assert!(
result.contains("# Using your tools"),
"system prompt should contain the tool guidance section heading"
);
}
// ---------------------------------------------------------------------------
// TC-4.3-02: Bash prohibition list with dedicated tool alternatives
// ---------------------------------------------------------------------------
#[test]
fn tc_4_3_02_bash_prohibition_list() {
let result = build_system_prompt(
&mut SystemPromptCache::new(),
None,
"/tmp",
"test-model",
&[],
None,
None,
false,
false,
false, // browser_enabled
);
// Glob replaces find/ls
assert!(
result.contains("Glob") && result.contains("find"),
"should map Glob as replacement for find"
);
// Grep replaces grep/rg
assert!(
result.contains("Grep") && result.contains("grep"),
"should map Grep as replacement for grep"
);
// Read replaces cat/head/tail
assert!(
result.contains("Read") && result.contains("cat"),
"should map Read as replacement for cat"
);
// Edit replaces sed/awk
assert!(
result.contains("Edit") && result.contains("sed"),
"should map Edit as replacement for sed"
);
// Write replaces echo/heredoc
assert!(
result.contains("Write") && result.contains("echo"),
"should map Write as replacement for echo"
);
}
// ---------------------------------------------------------------------------
// TC-4.3-03: Parallel call guidance
// ---------------------------------------------------------------------------
#[test]
fn tc_4_3_03_parallel_call_guidance() {
let result = build_system_prompt(
&mut SystemPromptCache::new(),
None,
"/tmp",
"test-model",
&[],
None,
None,
false,
false,
false, // browser_enabled
);
assert!(
result.contains("parallel"),
"should contain parallel call guidance"
);
assert!(
result.contains("sequentially"),
"should explain when to run sequentially (dependencies)"
);
}
// ---------------------------------------------------------------------------
// TC-4.3-04: Edit-over-Write and Read-before-Edit rules
// ---------------------------------------------------------------------------
#[test]
fn tc_4_3_04_edit_write_read_rules() {
let result = build_system_prompt(
&mut SystemPromptCache::new(),
None,
"/tmp",
"test-model",
&[],
None,
None,
false,
false,
false, // browser_enabled
);
assert!(
result.contains("Prefer Edit over Write"),
"should contain Edit-over-Write preference"
);
assert!(
result.contains("Read a file before editing"),
"should contain Read-before-Edit rule"
);
}
// ---------------------------------------------------------------------------
// TC-4.3-05: Section order — guidance after intro, before custom prompt
// ---------------------------------------------------------------------------
#[test]
fn tc_4_3_05_order_after_intro_before_custom() {
let result = build_system_prompt(
&mut SystemPromptCache::new(),
Some("CUSTOM_PROMPT_MARKER"),
"/tmp",
"test-model",
&[],
None,
None,
false,
false,
false, // browser_enabled
);
let intro_pos = result
.find("Working directory")
.expect("intro should contain 'Working directory'");
let guidance_pos = result
.find("# Using your tools")
.expect("tool guidance section should exist");
let custom_pos = result
.find("CUSTOM_PROMPT_MARKER")
.expect("custom prompt should exist");
assert!(
guidance_pos > intro_pos,
"tool guidance should appear after the base intro"
);
assert!(
guidance_pos < custom_pos,
"tool guidance should appear before custom prompt"
);
}
// ---------------------------------------------------------------------------
// TC-4.3-06: Section order — guidance before skills and memory
// ---------------------------------------------------------------------------
#[test]
fn tc_4_3_06_order_before_skills() {
let skills = vec![make_skill("order-test-skill", "Order test")];
let result = build_system_prompt(
&mut SystemPromptCache::new(),
None,
"/tmp",
"test-model",
&skills,
None,
None,
false,
false,
false, // browser_enabled
);
let guidance_pos = result
.find("# Using your tools")
.expect("tool guidance should exist");
let skills_pos = result
.find("order-test-skill")
.expect("skill should be listed");
assert!(
guidance_pos < skills_pos,
"tool guidance should appear before skills listing"
);
}
#[test]
fn tc_4_3_06_order_before_memory() {
let tmp = tempfile::TempDir::new().unwrap();
let mem_dir = tmp.path().join("memory");
fs::create_dir_all(&mem_dir).unwrap();
fs::write(
mem_dir.join("MEMORY.md"),
"- [Note](note.md) \u{2014} some note\n",
)
.unwrap();
let result = build_system_prompt(
&mut SystemPromptCache::new(),
None,
"/tmp",
"test-model",
&[],
None,
Some(&mem_dir),
false,
false,
false, // browser_enabled
);
let guidance_pos = result
.find("# Using your tools")
.expect("tool guidance should exist");
let memory_pos = result
.find("auto memory")
.expect("memory section should exist");
assert!(
guidance_pos < memory_pos,
"tool guidance should appear before memory section"
);
}
// ---------------------------------------------------------------------------
// TC-4.3-07: All sections coexist with correct ordering
// ---------------------------------------------------------------------------
#[test]
fn tc_4_3_07_all_sections_coexist() {
let tmp = tempfile::TempDir::new().unwrap();
let cwd = tmp.path();
// Create AGENTS.md
fs::write(cwd.join("AGENTS.md"), "PROJECT_RULES_COEXIST").unwrap();
// Create memory
let mem_dir = tmp.path().join("memory");
fs::create_dir_all(&mem_dir).unwrap();
fs::write(
mem_dir.join("MEMORY.md"),
"- [Item](item.md) \u{2014} coexist test\n",
)
.unwrap();
let skills = vec![make_skill("coexist-skill", "Coexist test")];
let result = build_system_prompt(
&mut SystemPromptCache::new(),
Some("CUSTOM_COEXIST"),
&cwd.to_string_lossy(),
"test-model",
&skills,
None,
Some(&mem_dir),
true, // plan mode active
false,
false, // browser_enabled
);
// All sections should exist
assert!(result.contains("Working directory"), "intro should exist");
assert!(
result.contains("# Using your tools"),
"tool guidance should exist"
);
assert!(
result.contains("CUSTOM_COEXIST"),
"custom prompt should exist"
);
assert!(
result.contains("PROJECT_RULES_COEXIST"),
"AGENTS.md should exist"
);
assert!(
result.contains("auto memory"),
"memory section should exist"
);
assert!(
result.contains("coexist-skill"),
"skills listing should exist"
);
// Verify ordering: intro < guidance < custom < agents.md < memory < skills
let intro_pos = result.find("Working directory").unwrap();
let guidance_pos = result.find("# Using your tools").unwrap();
let custom_pos = result.find("CUSTOM_COEXIST").unwrap();
let agents_pos = result.find("PROJECT_RULES_COEXIST").unwrap();
let memory_pos = result.find("auto memory").unwrap();
let skills_pos = result.find("coexist-skill").unwrap();
assert!(guidance_pos > intro_pos, "guidance after intro");
assert!(custom_pos > guidance_pos, "custom after guidance");
assert!(agents_pos > custom_pos, "agents.md after custom");
assert!(memory_pos > agents_pos, "memory after agents.md");
assert!(skills_pos > memory_pos, "skills after memory");
}
// ---------------------------------------------------------------------------
// TC-4.3-08: Tool guidance present in plan mode
// ---------------------------------------------------------------------------
#[test]
fn tc_4_3_08_guidance_in_plan_mode() {
let result = build_system_prompt(
&mut SystemPromptCache::new(),
None,
"/tmp",
"test-model",
&[],
None,
None,
true,
false,
false, // browser_enabled
);
assert!(
result.contains("# Using your tools"),
"tool guidance should be present even in plan mode"
);
// Plan mode instructions should also be present
assert!(
result.contains("plan") || result.contains("Plan"),
"plan mode instructions should coexist with tool guidance"
);
}