Skip to content

Commit f5b7288

Browse files
committed
Test: Add comprehensive unit tests for tier-aware prompt selection (Phase 2B.14)
Added 3 unit tests to verify prompt selector behavior: 1. test_tier_to_verbosity_prompt_content(): - Verifies each tier (Small/Medium/Large/Massive) gets appropriate verbosity prompts (TERSE/BALANCED/DETAILED/EXPLORATORY) - Checks for correct max steps constraints in prompt content 2. test_all_prompts_enforce_zero_heuristics(): - Validates all 28 prompts (7 analysis types × 4 tiers) enforce zero-heuristics principle - Verifies all prompts require JSON response format with "reasoning" and "tool_call" fields 3. test_specialized_prompts_for_all_analysis_types(): - Confirms all analysis types have specialized (non-generic) prompts - Checks for relevant domain keywords in each prompt type - Ensures prompts are properly customized for their purpose Test Coverage: - All 7 analysis types (code_search, dependency_analysis, call_chain_analysis, architecture_analysis, api_surface_analysis, context_builder, semantic_question) - All 4 context tiers (Small, Medium, Large, Massive) - Total: 28 unique prompt configurations validated Phase 2B.14 Complete
1 parent f93e16a commit f5b7288

File tree

1 file changed

+127
-1
lines changed

1 file changed

+127
-1
lines changed

crates/codegraph-mcp/src/prompt_selector.rs

Lines changed: 127 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -357,11 +357,137 @@ mod tests {
357357
.select_prompt(analysis_type, tier)
358358
.expect("Should have prompt");
359359
assert!(!prompt.is_empty());
360-
assert!(prompt.contains(analysis_type.as_str()) || prompt.contains("NOTE"));
360+
// Prompts should not contain placeholder text anymore
361+
assert!(!prompt.contains("placeholder"));
362+
assert!(!prompt.contains("Phase 2B"));
361363
}
362364
}
363365
}
364366

367+
#[test]
368+
fn test_tier_to_verbosity_prompt_content() {
369+
let selector = PromptSelector::new();
370+
371+
// Test that Small tier gets TERSE prompts with appropriate constraints
372+
let terse_prompt = selector
373+
.select_prompt(AnalysisType::ArchitectureAnalysis, ContextTier::Small)
374+
.expect("Should have terse prompt");
375+
assert!(
376+
terse_prompt.contains("TERSE")
377+
|| terse_prompt.contains("5 STEPS")
378+
|| terse_prompt.contains("MAX 5")
379+
);
380+
381+
// Test that Medium tier gets BALANCED prompts
382+
let balanced_prompt = selector
383+
.select_prompt(AnalysisType::ArchitectureAnalysis, ContextTier::Medium)
384+
.expect("Should have balanced prompt");
385+
assert!(
386+
balanced_prompt.contains("BALANCED")
387+
|| balanced_prompt.contains("10 STEPS")
388+
|| balanced_prompt.contains("MAX 10")
389+
);
390+
391+
// Test that Large tier gets DETAILED prompts
392+
let detailed_prompt = selector
393+
.select_prompt(AnalysisType::ArchitectureAnalysis, ContextTier::Large)
394+
.expect("Should have detailed prompt");
395+
assert!(
396+
detailed_prompt.contains("DETAILED")
397+
|| detailed_prompt.contains("15 STEPS")
398+
|| detailed_prompt.contains("MAX 15")
399+
);
400+
401+
// Test that Massive tier gets EXPLORATORY prompts
402+
let exploratory_prompt = selector
403+
.select_prompt(AnalysisType::ArchitectureAnalysis, ContextTier::Massive)
404+
.expect("Should have exploratory prompt");
405+
assert!(
406+
exploratory_prompt.contains("EXPLORATORY")
407+
|| exploratory_prompt.contains("20 STEPS")
408+
|| exploratory_prompt.contains("MAX 20")
409+
);
410+
}
411+
412+
#[test]
413+
fn test_all_prompts_enforce_zero_heuristics() {
414+
let selector = PromptSelector::new();
415+
416+
// Every prompt should enforce zero heuristics principle
417+
for analysis_type in AnalysisType::all() {
418+
for tier in [
419+
ContextTier::Small,
420+
ContextTier::Medium,
421+
ContextTier::Large,
422+
ContextTier::Massive,
423+
] {
424+
let prompt = selector
425+
.select_prompt(analysis_type, tier)
426+
.expect("Should have prompt");
427+
428+
// Should contain zero heuristics guidance
429+
assert!(
430+
prompt.contains("ZERO HEURISTIC")
431+
|| prompt.contains("NO HEURISTIC")
432+
|| prompt.contains("ONLY structured")
433+
|| prompt.contains("NO assumptions"),
434+
"Prompt for {:?}/{:?} should enforce zero heuristics",
435+
analysis_type,
436+
tier
437+
);
438+
439+
// Should enforce JSON response format
440+
assert!(
441+
prompt.contains("\"reasoning\"") && prompt.contains("\"tool_call\""),
442+
"Prompt for {:?}/{:?} should specify JSON response format",
443+
analysis_type,
444+
tier
445+
);
446+
}
447+
}
448+
}
449+
450+
#[test]
451+
fn test_specialized_prompts_for_all_analysis_types() {
452+
let selector = PromptSelector::new();
453+
454+
// Test that we have specialized (non-generic) prompts for all analysis types
455+
let test_cases = vec![
456+
(AnalysisType::CodeSearch, "code", "search"),
457+
(
458+
AnalysisType::DependencyAnalysis,
459+
"dependency",
460+
"dependencies",
461+
),
462+
(AnalysisType::CallChainAnalysis, "call", "chain"),
463+
(
464+
AnalysisType::ArchitectureAnalysis,
465+
"architecture",
466+
"architectural",
467+
),
468+
(AnalysisType::ApiSurfaceAnalysis, "API", "surface"),
469+
(AnalysisType::ContextBuilder, "context", "build"),
470+
(AnalysisType::SemanticQuestion, "semantic", "question"),
471+
];
472+
473+
for (analysis_type, keyword1, keyword2) in test_cases {
474+
let prompt = selector
475+
.select_prompt(analysis_type, ContextTier::Medium)
476+
.expect("Should have prompt");
477+
478+
// Each prompt should contain keywords relevant to its analysis type
479+
let lowercase_prompt = prompt.to_lowercase();
480+
assert!(
481+
lowercase_prompt.contains(keyword1) || lowercase_prompt.contains(keyword2),
482+
"Prompt for {:?} should contain '{}' or '{}' but got: {}...",
483+
analysis_type,
484+
keyword1,
485+
keyword2,
486+
&prompt[..200.min(prompt.len())]
487+
);
488+
}
489+
}
490+
365491
#[test]
366492
fn test_recommended_max_steps() {
367493
let selector = PromptSelector::new();

0 commit comments

Comments
 (0)