mirror of
https://github.com/Dicklesworthstone/coding_agent_session_search.git
synced 2026-09-22 11:59:25 +08:00
2938b1e90b
- Enhance full-text query parser, boolean filtering, and rank scoring in src/search/query.rs - Optimize SQLite session index schema, query execution plans, and fast batch inserts in src/storage/sqlite.rs - Add reproduction capsule bundling and export support in src/repro_capsule.rs - Update CLI library exports, documentation contract tests, and Cargo.lock dependencies
319 lines
11 KiB
Rust
319 lines
11 KiB
Rust
//! README documentation accuracy tests.
|
|
//!
|
|
//! These tests verify that generated README.md content accurately reflects
|
|
//! the actual archive data and system configuration.
|
|
//!
|
|
//! Run with:
|
|
//! cargo test --test docs
|
|
|
|
use chrono::{TimeZone, Utc};
|
|
use coding_agent_search::pages::docs::{DocConfig, DocLocation, DocumentationGenerator};
|
|
use coding_agent_search::pages::summary::{
|
|
AgentSummaryItem, KeySlotSummary, KeySlotType, PrePublishSummary, ScanReportSummary,
|
|
};
|
|
use regex::Regex;
|
|
|
|
// =============================================================================
|
|
// Test Helpers
|
|
// =============================================================================
|
|
|
|
/// Create a test summary with specified parameters.
|
|
fn create_test_summary(conversation_count: usize, agents: Vec<(&str, usize)>) -> PrePublishSummary {
|
|
let earliest = Utc.with_ymd_and_hms(2024, 1, 1, 0, 0, 0).unwrap();
|
|
let latest = Utc.with_ymd_and_hms(2024, 12, 31, 23, 59, 59).unwrap();
|
|
|
|
let total_agent_convs: usize = agents.iter().map(|(_, c)| c).sum();
|
|
|
|
PrePublishSummary {
|
|
total_conversations: conversation_count,
|
|
total_messages: conversation_count * 20,
|
|
total_characters: conversation_count * 5000,
|
|
estimated_size_bytes: conversation_count * 1000,
|
|
earliest_timestamp: Some(earliest),
|
|
latest_timestamp: Some(latest),
|
|
date_histogram: Vec::new(),
|
|
workspaces: Vec::new(),
|
|
agents: agents
|
|
.into_iter()
|
|
.map(|(name, count)| AgentSummaryItem {
|
|
name: name.to_string(),
|
|
conversation_count: count,
|
|
message_count: count * 20,
|
|
percentage: if total_agent_convs > 0 {
|
|
(count as f64 / total_agent_convs as f64) * 100.0
|
|
} else {
|
|
0.0
|
|
},
|
|
included: true,
|
|
})
|
|
.collect(),
|
|
key_slots: vec![KeySlotSummary {
|
|
slot_index: 0,
|
|
slot_type: KeySlotType::Password,
|
|
hint: None,
|
|
created_at: Some(Utc::now()),
|
|
}],
|
|
secret_scan: ScanReportSummary {
|
|
total_findings: 0,
|
|
by_severity: Default::default(),
|
|
has_critical: false,
|
|
truncated: false,
|
|
status_message: "No secrets found".to_string(),
|
|
},
|
|
encryption_config: None,
|
|
generated_at: Utc::now(),
|
|
}
|
|
}
|
|
|
|
/// Parse conversation count from README content.
|
|
#[allow(dead_code)]
|
|
fn parse_conversation_count(readme: &str) -> Option<usize> {
|
|
// Look for pattern like "123 conversations" or "Total conversations: 123"
|
|
let re = Regex::new(r"(\d+)\s+conversations?|conversations?[:\s]+(\d+)").ok()?;
|
|
if let Some(caps) = re.captures(readme) {
|
|
caps.get(1)
|
|
.or_else(|| caps.get(2))
|
|
.and_then(|m| m.as_str().parse().ok())
|
|
} else {
|
|
None
|
|
}
|
|
}
|
|
|
|
/// Parse date range from README content.
|
|
#[allow(dead_code)]
|
|
fn parse_date_range(readme: &str) -> Option<(String, String)> {
|
|
// Look for date patterns like "2024-01-01 to 2024-12-31"
|
|
let re = Regex::new(r"(\d{4}-\d{2}-\d{2})[^0-9]+(\d{4}-\d{2}-\d{2})").ok()?;
|
|
re.captures(readme)
|
|
.map(|caps| (caps[1].to_string(), caps[2].to_string()))
|
|
}
|
|
|
|
/// Check if an agent is listed in the README.
|
|
fn agent_listed(readme: &str, agent_name: &str) -> bool {
|
|
readme.contains(agent_name)
|
|
}
|
|
|
|
// =============================================================================
|
|
// README Accuracy Tests
|
|
// =============================================================================
|
|
|
|
/// Verify conversation count in README matches summary.
|
|
#[test]
|
|
fn test_readme_conversation_count_accurate() {
|
|
let summary = create_test_summary(1234, vec![("Claude", 500), ("GPT-4", 734)]);
|
|
let config = DocConfig::new().with_url("https://example.com/archive");
|
|
|
|
let generator = DocumentationGenerator::new(config, summary);
|
|
let readme_doc = generator.generate_readme();
|
|
|
|
assert_eq!(readme_doc.filename, "README.md");
|
|
assert_eq!(readme_doc.location, DocLocation::RepoRoot);
|
|
|
|
// Verify the conversation count appears in the README
|
|
assert!(
|
|
readme_doc.content.contains("1234") || readme_doc.content.contains("1,234"),
|
|
"README should contain the conversation count (1234)"
|
|
);
|
|
}
|
|
|
|
/// Verify all agents are listed in README.
|
|
#[test]
|
|
fn test_readme_agents_listed() {
|
|
let summary = create_test_summary(
|
|
100,
|
|
vec![
|
|
("Claude Code", 30),
|
|
("GitHub Copilot", 25),
|
|
("Cursor", 20),
|
|
("Gemini", 15),
|
|
("ChatGPT", 10),
|
|
],
|
|
);
|
|
let config = DocConfig::new();
|
|
|
|
let generator = DocumentationGenerator::new(config, summary);
|
|
let readme_doc = generator.generate_readme();
|
|
|
|
assert!(
|
|
agent_listed(&readme_doc.content, "Claude Code"),
|
|
"Claude Code should be listed"
|
|
);
|
|
assert!(
|
|
agent_listed(&readme_doc.content, "GitHub Copilot"),
|
|
"GitHub Copilot should be listed"
|
|
);
|
|
assert!(
|
|
agent_listed(&readme_doc.content, "Cursor"),
|
|
"Cursor should be listed"
|
|
);
|
|
assert!(
|
|
agent_listed(&readme_doc.content, "Gemini"),
|
|
"Gemini should be listed"
|
|
);
|
|
assert!(
|
|
agent_listed(&readme_doc.content, "ChatGPT"),
|
|
"ChatGPT should be listed"
|
|
);
|
|
}
|
|
|
|
/// Verify date range in README matches summary timestamps.
|
|
#[test]
|
|
fn test_readme_date_range_accurate() {
|
|
let summary = create_test_summary(50, vec![("Test Agent", 50)]);
|
|
let config = DocConfig::new();
|
|
|
|
let generator = DocumentationGenerator::new(config, summary);
|
|
let readme_doc = generator.generate_readme();
|
|
|
|
// Should contain the start and end dates
|
|
assert!(
|
|
readme_doc.content.contains("2024-01-01"),
|
|
"README should contain start date"
|
|
);
|
|
assert!(
|
|
readme_doc.content.contains("2024-12-31"),
|
|
"README should contain end date"
|
|
);
|
|
}
|
|
|
|
/// Verify deployment URL appears in README when configured.
|
|
#[test]
|
|
fn test_readme_url_included() {
|
|
let summary = create_test_summary(10, vec![("Agent", 10)]);
|
|
let config = DocConfig::new().with_url("https://my-archive.example.com");
|
|
|
|
let generator = DocumentationGenerator::new(config, summary);
|
|
let readme_doc = generator.generate_readme();
|
|
|
|
assert!(
|
|
readme_doc
|
|
.content
|
|
.contains("https://my-archive.example.com"),
|
|
"README should include configured URL"
|
|
);
|
|
}
|
|
|
|
/// Verify Argon2 parameters are documented.
|
|
#[test]
|
|
fn test_readme_argon_params_included() {
|
|
let summary = create_test_summary(10, vec![("Agent", 10)]);
|
|
let config = DocConfig::new().with_argon_params(65536, 3, 4);
|
|
|
|
let generator = DocumentationGenerator::new(config, summary);
|
|
let readme_doc = generator.generate_readme();
|
|
|
|
// Should mention Argon2 parameters
|
|
assert!(
|
|
readme_doc.content.contains("65536") || readme_doc.content.contains("64MB"),
|
|
"README should include memory parameter"
|
|
);
|
|
}
|
|
|
|
// =============================================================================
|
|
// README Completeness Tests
|
|
// =============================================================================
|
|
|
|
/// Verify README has all required sections.
|
|
#[test]
|
|
fn test_readme_has_required_sections() {
|
|
let summary = create_test_summary(100, vec![("Claude", 100)]);
|
|
let config = DocConfig::new().with_url("https://example.com");
|
|
|
|
let generator = DocumentationGenerator::new(config, summary);
|
|
let readme_doc = generator.generate_readme();
|
|
let content = &readme_doc.content;
|
|
|
|
// Check for standard README sections (case-insensitive)
|
|
let has_archive_section = content.to_lowercase().contains("archive");
|
|
let has_security_section =
|
|
content.to_lowercase().contains("security") || content.to_lowercase().contains("encrypt");
|
|
let has_usage_section = content.to_lowercase().contains("usage")
|
|
|| content.to_lowercase().contains("access")
|
|
|| content.to_lowercase().contains("how to");
|
|
|
|
assert!(
|
|
has_archive_section,
|
|
"README should have archive information"
|
|
);
|
|
assert!(
|
|
has_security_section,
|
|
"README should mention security/encryption"
|
|
);
|
|
assert!(has_usage_section, "README should have usage instructions");
|
|
}
|
|
|
|
/// Verify version information is included.
|
|
#[test]
|
|
fn test_readme_includes_version() {
|
|
let summary = create_test_summary(10, vec![("Agent", 10)]);
|
|
let config = DocConfig::new();
|
|
|
|
let generator = DocumentationGenerator::new(config, summary);
|
|
let readme_doc = generator.generate_readme();
|
|
|
|
// Should contain version number (semantic versioning pattern)
|
|
let version_re = Regex::new(r"\d+\.\d+\.\d+").unwrap();
|
|
assert!(
|
|
version_re.is_match(&readme_doc.content),
|
|
"README should include version number"
|
|
);
|
|
}
|
|
|
|
// =============================================================================
|
|
// Edge Cases
|
|
// =============================================================================
|
|
|
|
/// Test README generation with no agents.
|
|
#[test]
|
|
fn test_readme_empty_agents() {
|
|
let summary = create_test_summary(0, vec![]);
|
|
let config = DocConfig::new();
|
|
|
|
let generator = DocumentationGenerator::new(config, summary);
|
|
let readme_doc = generator.generate_readme();
|
|
|
|
// Should still generate valid README
|
|
assert!(!readme_doc.content.is_empty(), "README should not be empty");
|
|
assert!(
|
|
readme_doc.content.contains("0") || readme_doc.content.contains("No"),
|
|
"README should indicate no conversations"
|
|
);
|
|
}
|
|
|
|
/// Test README generation with single agent.
|
|
#[test]
|
|
fn test_readme_single_agent() {
|
|
let summary = create_test_summary(42, vec![("Claude Code", 42)]);
|
|
let config = DocConfig::new();
|
|
|
|
let generator = DocumentationGenerator::new(config, summary);
|
|
let readme_doc = generator.generate_readme();
|
|
|
|
assert!(
|
|
readme_doc.content.contains("Claude Code"),
|
|
"Single agent should be listed"
|
|
);
|
|
assert!(
|
|
readme_doc.content.contains("42"),
|
|
"Conversation count should appear"
|
|
);
|
|
}
|
|
|
|
/// Test README generation with very large numbers.
|
|
#[test]
|
|
fn test_readme_large_counts() {
|
|
let summary = create_test_summary(999999, vec![("Agent A", 500000), ("Agent B", 499999)]);
|
|
let config = DocConfig::new();
|
|
|
|
let generator = DocumentationGenerator::new(config, summary);
|
|
let readme_doc = generator.generate_readme();
|
|
|
|
// Should handle large numbers without panic
|
|
assert!(!readme_doc.content.is_empty());
|
|
// The count might be formatted with commas or plain
|
|
assert!(
|
|
readme_doc.content.contains("999999") || readme_doc.content.contains("999,999"),
|
|
"README should contain the large count"
|
|
);
|
|
}
|