#[cfg(test)]
mod enhanced_metric_registry_tests {
use crate::metrics::definitions::MetricScorer;
use crate::metrics::{
create_enhanced_scorer, get_all_metrics, MetricCategory, MetricRegistryOps, MetricValue,
ProfileMetricConfig, ReadabilityScorer, ScoringFunction, ThresholdSet, WordCountScorer,
};
#[test]
fn test_metric_registry_initialization() {
let word_count_def = MetricRegistryOps::get_metric_definition("word_count");
assert!(
word_count_def.is_ok(),
"word_count metric should be registered"
);
let definition = word_count_def.unwrap();
assert_eq!(definition.field_name, "word_count");
assert_eq!(definition.category, MetricCategory::Content);
assert!(
definition.profile_configurations.len() > 0,
"Should have profile configurations"
);
}
#[test]
fn test_profile_specific_configurations() {
let content_config = MetricRegistryOps::get_profile_config("word_count", "content_article");
let product_config = MetricRegistryOps::get_profile_config("word_count", "product");
assert!(content_config.is_ok() && product_config.is_ok());
let content_cfg = content_config.unwrap();
let product_cfg = product_config.unwrap();
assert!(
content_cfg.thresholds.excellent > product_cfg.thresholds.excellent,
"Content articles should have higher word count thresholds"
);
assert!(
content_cfg.weight > product_cfg.weight,
"Content articles should have higher word count weight"
);
assert!(
content_cfg.penalty_multiplier > product_cfg.penalty_multiplier,
"Content articles should penalize low word count more heavily"
);
}
#[test]
fn test_profile_aware_scoring() {
let word_count_scorer = WordCountScorer;
let word_count = MetricValue::Usize(300);
let content_config = ProfileMetricConfig {
weight: 0.25,
enabled: true,
thresholds: ThresholdSet {
excellent: 1500.0,
good: 800.0,
fair: 400.0,
poor: 100.0,
},
penalty_multiplier: 2.5,
bonus_conditions: Vec::new(),
scoring_function_override: None,
};
let product_config = ProfileMetricConfig {
weight: 0.08,
enabled: true,
thresholds: ThresholdSet {
excellent: 300.0,
good: 150.0,
fair: 75.0,
poor: 25.0,
},
penalty_multiplier: 0.8,
bonus_conditions: Vec::new(),
scoring_function_override: None,
};
let content_score = word_count_scorer.score_metric(word_count.clone(), &content_config);
let product_score = word_count_scorer.score_metric(word_count, &product_config);
assert!(product_score > content_score + 20.0,
"Product profile should score 300 words much higher than content profile. Content: {}, Product: {}",
content_score, product_score);
assert!(
product_score >= 90.0,
"300 words should be excellent for product pages"
);
assert!(
content_score <= 70.0,
"300 words should be poor for content articles"
);
}
#[test]
fn test_readability_reverse_scoring() {
let readability_scorer = ReadabilityScorer;
let config = ProfileMetricConfig {
weight: 0.20,
enabled: true,
thresholds: ThresholdSet {
excellent: 8.0, good: 12.0,
fair: 16.0,
poor: 20.0,
},
penalty_multiplier: 1.5,
bonus_conditions: Vec::new(),
scoring_function_override: None,
};
let excellent_readability =
readability_scorer.score_metric(MetricValue::OptionF32(Some(6.0)), &config);
let poor_readability =
readability_scorer.score_metric(MetricValue::OptionF32(Some(25.0)), &config);
assert!(
excellent_readability > poor_readability + 50.0,
"Lower FK scores should rate much higher. FK 6.0: {}, FK 25.0: {}",
excellent_readability,
poor_readability
);
assert!(excellent_readability >= 90.0, "FK 6.0 should be excellent");
assert!(poor_readability <= 30.0, "FK 25.0 should be poor");
}
#[test]
fn test_registry_operations() {
let content_metrics =
MetricRegistryOps::get_category_metrics(MetricCategory::Content, "content_article");
assert!(
!content_metrics.is_empty(),
"Should have content metrics for content_article profile"
);
for (metric_def, _) in &content_metrics {
assert_eq!(metric_def.category, MetricCategory::Content);
}
let profile_metrics = MetricRegistryOps::get_profile_metrics("content_article");
assert!(
profile_metrics.contains_key("word_count"),
"Should include word_count for content_article"
);
assert!(
profile_metrics.contains_key("readability_fk"),
"Should include readability_fk for content_article"
);
}
#[test]
fn test_profile_availability() {
let available_profiles = MetricRegistryOps::list_available_profiles();
assert!(available_profiles.contains(&"content_article".to_string()));
assert!(available_profiles.contains(&"news".to_string()));
assert!(available_profiles.contains(&"product".to_string()));
assert!(available_profiles.contains(&"portfolio".to_string()));
let mut sorted_profiles = available_profiles.clone();
sorted_profiles.sort();
assert_eq!(
available_profiles, sorted_profiles,
"Profiles should be sorted"
);
}
#[test]
fn test_registry_stats() {
let stats = MetricRegistryOps::get_registry_stats();
assert!(stats.total_metrics > 0, "Should have metrics registered");
assert!(
stats.configurable_metrics > 0,
"Should have configurable metrics"
);
assert!(
stats
.metrics_by_category
.contains_key(&MetricCategory::Content),
"Should have content metrics"
);
assert!(
stats
.profiles_with_overrides
.contains_key("content_article"),
"Should track content_article overrides"
);
}
#[test]
fn test_metric_validation() {
let word_count_scorer = WordCountScorer;
let validation_rules = vec![crate::metrics::ValidationRule {
rule_type: crate::metrics::ValidationRuleType::MinValue(0.0),
error_message: "Word count cannot be negative",
}];
let valid_result =
word_count_scorer.validate_metric_value(&MetricValue::Usize(100), &validation_rules);
assert!(
valid_result.is_ok(),
"Valid word count should pass validation"
);
let zero_result =
word_count_scorer.validate_metric_value(&MetricValue::Usize(0), &validation_rules);
assert!(
zero_result.is_ok(),
"Zero word count should pass validation"
);
}
#[test]
fn test_bonus_conditions() {
let word_count_scorer = WordCountScorer;
let bonus_conditions = vec![crate::metrics::BonusCondition {
condition_type: crate::metrics::BonusConditionType::ValueBetween {
min: 1500.0,
max: 3000.0,
},
bonus_points: 5.0,
description: "Optimal content length".to_string(),
}];
let base_score = 85.0;
let bonus_score = word_count_scorer.apply_bonuses(
base_score,
MetricValue::Usize(2000),
&bonus_conditions,
);
assert_eq!(
bonus_score, 90.0,
"Should apply 5 point bonus for optimal length"
);
let no_bonus_score =
word_count_scorer.apply_bonuses(base_score, MetricValue::Usize(500), &bonus_conditions);
assert_eq!(
no_bonus_score, 85.0,
"Should not apply bonus for non-optimal length"
);
}
#[test]
fn test_enhanced_scorer_factory() {
let word_count_scorer = create_enhanced_scorer(
&ScoringFunction::Linear {
min_value: 0.0,
max_value: 3000.0,
reverse_scoring: false,
},
"word_count",
);
let readability_scorer = create_enhanced_scorer(
&ScoringFunction::CustomFunction {
function_name: "readability_scorer".to_string(),
},
"readability_fk",
);
let config = ProfileMetricConfig::default();
let word_score = word_count_scorer.score_metric(MetricValue::Usize(1000), &config);
let readability_score =
readability_scorer.score_metric(MetricValue::OptionF32(Some(10.0)), &config);
assert!(
word_score > 0.0 && word_score <= 100.0,
"Word count score should be in valid range"
);
assert!(
readability_score > 0.0 && readability_score <= 100.0,
"Readability score should be in valid range"
);
}
#[test]
fn test_profile_compilation() {
let all_metrics = get_all_metrics();
let word_count_metric = all_metrics.iter().find(|m| m.field_name == "word_count");
assert!(word_count_metric.is_some(), "word_count should exist");
if let Some(metric) = word_count_metric {
assert_eq!(
metric.category,
MetricCategory::Content,
"word_count should be in Content category"
);
assert!(
metric.default_weight > 0.0,
"word_count should have a positive default weight"
);
}
}
#[test]
fn test_dramatic_score_differences() {
let test_scenarios = vec![
(50, "content_article", (0.0, 20.0)), (50, "product", (35.0, 55.0)), (1500, "content_article", (80.0, 100.0)), (1500, "product", (85.0, 100.0)), (300, "news", (70.0, 95.0)), (300, "content_article", (45.0, 75.0)), ];
let word_count_scorer = WordCountScorer;
for (word_count, profile, (min_expected, max_expected)) in test_scenarios {
let config = MetricRegistryOps::get_profile_config("word_count", profile).unwrap();
let score = word_count_scorer.score_metric(MetricValue::Usize(word_count), &config);
assert!(
score >= min_expected && score <= max_expected,
"Profile '{}' with {} words should score between {}-{}, got {}",
profile,
word_count,
min_expected,
max_expected,
score
);
}
}
}
#[cfg(test)]
mod phase1_integration_tests {
use super::*;
use crate::metrics::*;
#[test]
fn test_complete_profile_metric_pipeline() {
let metric_def = MetricRegistryOps::get_metric_definition("word_count").unwrap();
assert_eq!(metric_def.field_name, "word_count");
let content_config =
MetricRegistryOps::get_profile_config("word_count", "content_article").unwrap();
assert!(content_config.enabled);
assert!(content_config.weight > 0.0);
let scorer = create_enhanced_scorer(&metric_def.scoring_function, "word_count");
let test_value = MetricValue::Usize(800);
let score = scorer.score_metric(test_value, &content_config);
assert!(
score >= 0.0 && score <= 100.0,
"Score should be in valid range"
);
assert!(
score > 50.0,
"800 words should score reasonably well for content articles"
);
let product_config =
MetricRegistryOps::get_profile_config("word_count", "product").unwrap();
let product_score = scorer.score_metric(MetricValue::Usize(800), &product_config);
assert!(
product_score != score,
"Different profiles should produce different scores"
);
}
#[test]
fn test_registry_completeness() {
let essential_metrics = vec![
"word_count",
"readability_fk",
"main_text_ratio",
"headings_count",
"title_len",
"images_count",
];
for metric_name in essential_metrics {
assert!(
registry_utils::metric_exists(metric_name),
"Essential metric '{}' should exist in registry",
metric_name
);
}
let profiles = vec!["content_article", "news", "product", "portfolio"];
for profile in profiles {
let profile_metrics = MetricRegistryOps::get_profile_metrics(profile);
assert!(
!profile_metrics.is_empty(),
"Profile '{}' should have metric configurations",
profile
);
}
}
}