use super::*;
#[test]
fn test_generate_hot_topics_empty_state() {
let tmp = tempfile::tempdir().unwrap();
let output_path = tmp.path().join("hot-topics.md");
let state = HookState::default();
generate_hot_topics(&state, &output_path).unwrap();
let content = std::fs::read_to_string(&output_path).unwrap();
assert!(content.contains("# Hot Topics (auto-generated by bobbin)"));
assert!(content.contains("Based on 0 context injections."));
assert!(content.contains("No injection data yet."));
assert!(content.contains("## Frequently Referenced Code"));
assert!(content.contains("## Most Referenced Files"));
}
#[test]
fn test_generate_hot_topics_with_data() {
let tmp = tempfile::tempdir().unwrap();
let output_path = tmp.path().join("hot-topics.md");
let mut chunk_freqs = HashMap::new();
chunk_freqs.insert(
"src/cli/hook.rs:10:50".to_string(),
ChunkFrequency {
count: 12,
file: "src/cli/hook.rs".to_string(),
name: Some("InjectContextArgs".to_string()),
},
);
chunk_freqs.insert(
"src/config.rs:20:40".to_string(),
ChunkFrequency {
count: 9,
file: "src/config.rs".to_string(),
name: Some("HooksConfig".to_string()),
},
);
chunk_freqs.insert(
"src/search/context.rs:5:30".to_string(),
ChunkFrequency {
count: 7,
file: "src/search/context.rs".to_string(),
name: None,
},
);
let mut file_freqs = HashMap::new();
file_freqs.insert("src/cli/hook.rs".to_string(), 15);
file_freqs.insert("src/config.rs".to_string(), 12);
file_freqs.insert("src/search/context.rs".to_string(), 9);
let state = HookState {
last_session_id: "abc123".to_string(),
last_injected_chunks: vec![],
last_injection_time: "2026-02-08T10:30:00Z".to_string(),
injection_count: 47,
chunk_frequencies: chunk_freqs,
file_frequencies: file_freqs,
hot_topics_generated_at: 40,
};
generate_hot_topics(&state, &output_path).unwrap();
let content = std::fs::read_to_string(&output_path).unwrap();
assert!(content.contains("Based on 47 context injections."));
assert!(content.contains("2026-02-08 10:30 UTC"));
let hook_pos = content.find("InjectContextArgs").unwrap();
let config_pos = content.find("HooksConfig").unwrap();
assert!(
hook_pos < config_pos,
"Higher-count chunk should appear first"
);
assert!(content.contains("| src/cli/hook.rs | 15 |"));
assert!(content.contains("| src/config.rs | 12 |"));
assert!(content.contains("| - |"));
}
#[test]
fn test_generate_hot_topics_truncates_chunks_to_20() {
let tmp = tempfile::tempdir().unwrap();
let output_path = tmp.path().join("hot-topics.md");
let mut chunk_freqs = HashMap::new();
for i in 0..30 {
chunk_freqs.insert(
format!("src/file{}.rs:1:10", i),
ChunkFrequency {
count: 30 - i,
file: format!("src/file{}.rs", i),
name: Some(format!("fn_{}", i)),
},
);
}
let state = HookState {
injection_count: 100,
chunk_frequencies: chunk_freqs,
..Default::default()
};
generate_hot_topics(&state, &output_path).unwrap();
let content = std::fs::read_to_string(&output_path).unwrap();
let chunk_section = content
.split("## Frequently Referenced Code")
.nth(1)
.unwrap()
.split("## Most Referenced Files")
.next()
.unwrap();
let rank_rows: Vec<&str> = chunk_section
.lines()
.filter(|l| l.starts_with("| ") && l.chars().nth(2).map_or(false, |c| c.is_ascii_digit()))
.collect();
assert_eq!(rank_rows.len(), 20);
}
#[test]
fn test_generate_hot_topics_truncates_files_to_10() {
let tmp = tempfile::tempdir().unwrap();
let output_path = tmp.path().join("hot-topics.md");
let mut file_freqs = HashMap::new();
for i in 0..15 {
file_freqs.insert(format!("src/file{}.rs", i), 15 - i as u64);
}
let state = HookState {
injection_count: 50,
file_frequencies: file_freqs,
..Default::default()
};
generate_hot_topics(&state, &output_path).unwrap();
let content = std::fs::read_to_string(&output_path).unwrap();
let file_section = content.split("## Most Referenced Files").nth(1).unwrap();
let row_count = file_section
.lines()
.filter(|l| l.starts_with("| src/"))
.count();
assert_eq!(row_count, 10);
}