use reputation_core::Calculator;
use reputation_types::{AgentData, AgentDataBuilder};
use chrono::{Duration, Utc, DateTime};
use serde::{Serialize, Deserialize};
use std::collections::HashMap;
use std::fs;
use std::path::Path;
#[derive(Debug, Serialize, Deserialize)]
pub struct TestVectorFile {
pub version: String,
pub algorithm: String,
pub generated: DateTime<Utc>,
pub calculator_config: CalculatorConfig,
pub test_categories: HashMap<String, String>,
pub test_cases: Vec<TestCase>,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct CalculatorConfig {
pub confidence_k: f64,
pub prior_base: f64,
pub prior_max: f64,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct TestCase {
pub id: String,
pub category: String,
pub description: String,
pub input: TestInput,
pub expected: ExpectedScore,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct TestInput {
pub did: String,
pub created_at: DateTime<Utc>,
pub mcp_level: Option<u8>,
pub identity_verified: bool,
pub security_audit_passed: bool,
pub open_source: bool,
pub total_interactions: u32,
pub total_reviews: u32,
pub average_rating: Option<f64>,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct ExpectedScore {
pub score: f64,
pub confidence: f64,
pub level: String,
pub components: ScoreComponents,
pub is_provisional: bool,
pub data_points: u32,
pub algorithm_version: String,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct ScoreComponents {
pub prior_score: f64,
pub prior_breakdown: PriorBreakdown,
pub empirical_score: f64,
pub confidence_value: f64,
pub confidence_level: String,
pub prior_weight: f64,
pub empirical_weight: f64,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct PriorBreakdown {
pub base_score: f64,
pub mcp_bonus: f64,
pub identity_bonus: f64,
pub security_audit_bonus: f64,
pub open_source_bonus: f64,
pub age_bonus: f64,
pub total: f64,
}
fn main() {
println!("🔧 Generating test vectors for KnowThat Reputation Engine...\n");
let mut vectors = TestVectorFile {
version: "1.0.0".to_string(),
algorithm: "bayesian_reputation_v1".to_string(),
generated: Utc::now(),
calculator_config: CalculatorConfig {
confidence_k: 15.0,
prior_base: 50.0,
prior_max: 80.0,
},
test_categories: HashMap::from([
("new_agents".to_string(), "Agents with no interaction history".to_string()),
("verified_agents".to_string(), "Agents with identity verification and credentials".to_string()),
("high_interaction".to_string(), "Agents with significant activity".to_string()),
("edge_cases".to_string(), "Boundary conditions and limits".to_string()),
("confidence_levels".to_string(), "Different confidence level examples".to_string()),
("review_patterns".to_string(), "Various rating distributions".to_string()),
]),
test_cases: Vec::new(),
};
let calc = Calculator::default();
let mut case_id = 1;
println!("📝 Generating new agent test cases...");
add_test_case(&mut vectors, &calc, &mut case_id, "new_agents",
"Brand new agent with no history",
AgentDataBuilder::new("did:test:new-agent").build().unwrap());
add_test_case(&mut vectors, &calc, &mut case_id, "new_agents",
"Week-old agent with no activity",
AgentDataBuilder::new("did:test:week-old")
.created_at(Utc::now() - Duration::days(7))
.build().unwrap());
add_test_case(&mut vectors, &calc, &mut case_id, "new_agents",
"Month-old agent with no activity",
AgentDataBuilder::new("did:test:month-old")
.created_at(Utc::now() - Duration::days(30))
.build().unwrap());
println!("📝 Generating verified agent test cases...");
for level in 1..=3 {
add_test_case(&mut vectors, &calc, &mut case_id, "verified_agents",
&format!("Agent with MCP Level {}", level),
AgentDataBuilder::new(&format!("did:test:mcp-{}", level))
.mcp_level(level)
.created_at(Utc::now() - Duration::days(90))
.build().unwrap());
}
add_test_case(&mut vectors, &calc, &mut case_id, "verified_agents",
"Agent with identity verification",
AgentDataBuilder::new("did:test:identity-verified")
.identity_verified(true)
.created_at(Utc::now() - Duration::days(60))
.build().unwrap());
add_test_case(&mut vectors, &calc, &mut case_id, "verified_agents",
"Fully verified agent with all credentials",
AgentDataBuilder::new("did:test:fully-verified")
.mcp_level(3)
.identity_verified(true)
.security_audit_passed(true)
.open_source(true)
.created_at(Utc::now() - Duration::days(180))
.build().unwrap());
println!("📝 Generating review pattern test cases...");
for rating in 1..=5 {
add_test_case(&mut vectors, &calc, &mut case_id, "review_patterns",
&format!("Agent with {} star rating", rating),
AgentDataBuilder::new(&format!("did:test:rating-{}", rating))
.total_interactions(100)
.with_reviews(50, rating as f64)
.created_at(Utc::now() - Duration::days(30))
.build().unwrap());
}
add_test_case(&mut vectors, &calc, &mut case_id, "review_patterns",
"Agent with mixed reviews (3.5 stars)",
AgentDataBuilder::new("did:test:mixed-reviews")
.total_interactions(200)
.with_reviews(100, 3.5)
.created_at(Utc::now() - Duration::days(60))
.build().unwrap());
println!("📝 Generating confidence level test cases...");
let confidence_cases = [
(1, "Low confidence (1 interaction)"),
(10, "Low-Medium confidence (10 interactions)"),
(50, "Medium confidence (50 interactions)"),
(200, "High confidence (200 interactions)"),
(1000, "Very high confidence (1000 interactions)"),
];
for (interactions, desc) in confidence_cases {
let reviews = (interactions / 2).max(1).min(interactions); let mut builder = AgentDataBuilder::new(&format!("did:test:conf-{}", interactions))
.total_interactions(interactions)
.created_at(Utc::now() - Duration::days(90));
if interactions > 0 {
builder = builder.with_reviews(reviews, 4.0);
}
add_test_case(&mut vectors, &calc, &mut case_id, "confidence_levels",
desc, builder.build().unwrap());
}
println!("📝 Generating edge case test cases...");
add_test_case(&mut vectors, &calc, &mut case_id, "edge_cases",
"Many interactions but no reviews",
AgentDataBuilder::new("did:test:no-reviews")
.total_interactions(500)
.created_at(Utc::now() - Duration::days(30))
.build().unwrap());
add_test_case(&mut vectors, &calc, &mut case_id, "edge_cases",
"Maximum allowed values",
AgentDataBuilder::new("did:test:max-values")
.total_interactions(1_000_000)
.with_reviews(500_000, 5.0)
.mcp_level(3)
.identity_verified(true)
.security_audit_passed(true)
.open_source(true)
.created_at(Utc::now() - Duration::days(730))
.build().unwrap());
add_test_case(&mut vectors, &calc, &mut case_id, "edge_cases",
"Single perfect review",
AgentDataBuilder::new("did:test:one-review")
.total_interactions(1)
.with_reviews(1, 5.0)
.created_at(Utc::now() - Duration::days(1))
.build().unwrap());
add_test_case(&mut vectors, &calc, &mut case_id, "edge_cases",
"Agent reaching prior score cap",
AgentDataBuilder::new("did:test:prior-cap")
.mcp_level(3)
.identity_verified(true)
.security_audit_passed(true)
.open_source(true)
.created_at(Utc::now() - Duration::days(365))
.build().unwrap());
add_test_case(&mut vectors, &calc, &mut case_id, "edge_cases",
"Boundary confidence level (~0.21)",
AgentDataBuilder::new("did:test:boundary-conf")
.total_interactions(4) .created_at(Utc::now() - Duration::days(7))
.build().unwrap());
println!("📝 Generating high interaction test cases...");
let interaction_levels = [100, 500, 1000, 5000, 10000];
for interactions in interaction_levels {
add_test_case(&mut vectors, &calc, &mut case_id, "high_interaction",
&format!("Agent with {} interactions", interactions),
AgentDataBuilder::new(&format!("did:test:high-int-{}", interactions))
.total_interactions(interactions)
.with_reviews((interactions as f64 * 0.3) as u32, 4.2)
.created_at(Utc::now() - Duration::days(180))
.build().unwrap());
}
println!("📝 Generating additional test cases...");
let credential_combos = [
(Some(1), false, false, false, "MCP Level 1 only"),
(None, true, false, false, "Identity verified only"),
(None, false, true, false, "Security audit only"),
(None, false, false, true, "Open source only"),
(Some(2), true, false, false, "MCP Level 2 + Identity"),
(Some(3), true, true, true, "All credentials maxed"),
];
for (mcp, id, sec, os, desc) in credential_combos {
let mut builder = AgentDataBuilder::new(&format!("did:test:cred-{}", case_id))
.created_at(Utc::now() - Duration::days(90))
.total_interactions(50);
builder = builder.with_reviews(25, 4.0);
if let Some(level) = mcp {
builder = builder.mcp_level(level);
}
builder = builder
.identity_verified(id)
.security_audit_passed(sec)
.open_source(os);
add_test_case(&mut vectors, &calc, &mut case_id, "verified_agents", desc, builder.build().unwrap());
}
let age_days = [0, 30, 60, 90, 180, 365];
for days in age_days {
add_test_case(&mut vectors, &calc, &mut case_id, "edge_cases",
&format!("Agent aged {} days", days),
AgentDataBuilder::new(&format!("did:test:age-{}", days))
.created_at(Utc::now() - Duration::days(days))
.build().unwrap());
}
println!("\n📁 Saving test vectors...");
let json = serde_json::to_string_pretty(&vectors).unwrap();
let paths = [
Path::new("test_vectors.json"),
Path::new("docs/test_vectors.json"),
];
for path in &paths {
match fs::write(path, &json) {
Ok(_) => println!("✅ Saved to: {}", path.display()),
Err(e) => eprintln!("❌ Failed to save to {}: {}", path.display(), e),
}
}
println!("\n📊 Summary:");
println!(" Total test cases: {}", vectors.test_cases.len());
let mut category_counts = HashMap::new();
for case in &vectors.test_cases {
*category_counts.entry(case.category.clone()).or_insert(0) += 1;
}
println!(" Test cases by category:");
for (category, desc) in &vectors.test_categories {
let count = category_counts.get(category).unwrap_or(&0);
println!(" {} ({}): {}", category, count, desc);
}
println!("\n✨ Test vector generation complete!");
}
fn add_test_case(
vectors: &mut TestVectorFile,
calc: &Calculator,
case_id: &mut usize,
category: &str,
description: &str,
agent: AgentData,
) {
let score = match calc.calculate(&agent) {
Ok(s) => s,
Err(e) => {
eprintln!("Error calculating score for {}: {:?}", description, e);
eprintln!("Agent: {:?}", agent);
panic!("Test vector generation failed");
}
};
vectors.test_cases.push(TestCase {
id: format!("TC{:03}", case_id),
category: category.to_string(),
description: description.to_string(),
input: TestInput {
did: agent.did.clone(),
created_at: agent.created_at,
mcp_level: agent.mcp_level,
identity_verified: agent.identity_verified,
security_audit_passed: agent.security_audit_passed,
open_source: agent.open_source,
total_interactions: agent.total_interactions,
total_reviews: agent.total_reviews,
average_rating: agent.average_rating,
},
expected: ExpectedScore {
score: score.score,
confidence: score.confidence,
level: format!("{:?}", score.level),
components: ScoreComponents {
prior_score: score.components.prior_score,
prior_breakdown: PriorBreakdown {
base_score: score.components.prior_breakdown.base_score,
mcp_bonus: score.components.prior_breakdown.mcp_bonus,
identity_bonus: score.components.prior_breakdown.identity_bonus,
security_audit_bonus: score.components.prior_breakdown.security_audit_bonus,
open_source_bonus: score.components.prior_breakdown.open_source_bonus,
age_bonus: score.components.prior_breakdown.age_bonus,
total: score.components.prior_breakdown.total,
},
empirical_score: score.components.empirical_score,
confidence_value: score.components.confidence_value,
confidence_level: format!("{:?}", score.components.confidence_level),
prior_weight: score.components.prior_weight,
empirical_weight: score.components.empirical_weight,
},
is_provisional: score.is_provisional,
data_points: score.data_points,
algorithm_version: score.algorithm_version.clone(),
},
});
*case_id += 1;
}