use webpage_quality_analyzer::async_runtime::DefaultRuntime;
use webpage_quality_analyzer::{Analyzer, QualityBand};
const TEST_HTML_SHORT: &str = r#"
<!DOCTYPE html>
<html>
<head>
<title>Short Article</title>
<meta name="description" content="A brief description">
</head>
<body>
<h1>Short Content</h1>
<p>This is a very short article with minimal content.</p>
</body>
</html>
"#;
const TEST_HTML_MEDIUM: &str = r#"
<!DOCTYPE html>
<html>
<head>
<title>Medium Length Article For Testing Purposes</title>
<meta name="description" content="A medium length article for comprehensive testing">
</head>
<body>
<h1>Medium Length Content</h1>
<p>This article has a reasonable amount of content. It contains multiple sentences
to ensure we have enough text for meaningful word count analysis.</p>
<p>The second paragraph adds more substance to the document, making it suitable
for testing threshold boundaries and scoring behavior across different ranges.</p>
<p>A third paragraph ensures we're well within the medium range for most metrics,
providing stable test conditions for threshold customization validation.</p>
</body>
</html>
"#;
const TEST_HTML_LONG: &str = r#"
<!DOCTYPE html>
<html>
<head>
<title>Comprehensive Long Article With Extended Title For Testing</title>
<meta name="description" content="An extensive article with substantial content for thorough threshold testing">
</head>
<body>
<h1>Comprehensive Long Content</h1>
<h2>Introduction</h2>
<p>This is a long article designed to test threshold behaviors at the upper end of metric ranges.
It contains multiple paragraphs, headings, and substantial textual content to ensure comprehensive
coverage of all threshold scenarios.</p>
<h2>Main Content Section</h2>
<p>The main content section provides extensive detail on various topics. Each paragraph is carefully
crafted to contribute to the overall word count while maintaining readability and structure.</p>
<p>We include multiple sentences in each paragraph to ensure natural text flow and realistic
content analysis. This helps validate that our threshold customizations work correctly with
real-world content patterns.</p>
<h2>Additional Details</h2>
<p>Further elaboration on the topic demonstrates the depth of coverage this article provides.
The extensive content allows us to test how custom thresholds affect scoring when metric values
are well above typical optimal ranges.</p>
<p>This comprehensive approach ensures our threshold system can handle content of various lengths
and complexities without issues.</p>
<h2>Conclusion</h2>
<p>In conclusion, this long-form content provides an excellent test case for custom threshold
validation and scoring behavior analysis across the full spectrum of content lengths.</p>
</body>
</html>
"#;
const TEST_URL: &str = "http://example.com/test";
#[tokio::test]
async fn test_set_metric_threshold_basic() {
let analyzer: Analyzer = Analyzer::builder()
.with_profile_name("content_article")
.unwrap()
.set_metric_threshold(
"word_count",
50.0, 200.0, 800.0, 2000.0, )
.unwrap()
.build()
.unwrap();
let report = analyzer
.run(TEST_URL, Some(TEST_HTML_MEDIUM))
.await
.unwrap();
assert!(report.score > 0.0);
assert!(matches!(
report.verdict,
QualityBand::VeryPoor
| QualityBand::Poor
| QualityBand::Fair
| QualityBand::Good
| QualityBand::Excellent
));
}
#[tokio::test]
async fn test_set_simple_threshold() {
let analyzer: Analyzer = Analyzer::builder()
.with_profile_name("content_article")
.unwrap()
.set_simple_threshold(
"title_len",
20.0, 50.0, 100.0, )
.unwrap()
.build()
.unwrap();
let report = analyzer
.run(TEST_URL, Some(TEST_HTML_MEDIUM))
.await
.unwrap();
assert!(report.score > 0.0);
}
#[tokio::test]
async fn test_multiple_threshold_customizations() {
let analyzer: Analyzer = Analyzer::builder()
.with_profile_name("content_article")
.unwrap()
.set_thresholds(vec![
("word_count", 100.0, 300.0, 1000.0, 3000.0),
("title_len", 20.0, 40.0, 70.0, 120.0),
("paragraph_count", 2.0, 4.0, 12.0, 25.0),
])
.unwrap()
.build()
.unwrap();
let report = analyzer.run(TEST_URL, Some(TEST_HTML_LONG)).await.unwrap();
assert!(report.score > 0.0);
}
#[tokio::test]
async fn test_threshold_affects_scoring() {
let analyzer_default: Analyzer = Analyzer::builder()
.with_profile_name("content_article")
.unwrap()
.build()
.unwrap();
let report_default = analyzer_default
.run(TEST_URL, Some(TEST_HTML_SHORT))
.await
.unwrap();
let analyzer_custom: Analyzer = Analyzer::builder()
.with_profile_name("content_article")
.unwrap()
.set_metric_threshold(
"word_count",
10.0, 20.0, 100.0, 500.0, )
.unwrap()
.build()
.unwrap();
let report_custom = analyzer_custom
.run(TEST_URL, Some(TEST_HTML_SHORT))
.await
.unwrap();
assert!(report_default.score >= 0.0);
assert!(report_custom.score >= 0.0);
}
#[tokio::test]
async fn test_threshold_validation_min_greater_than_optimal_min() {
let result = Analyzer::<DefaultRuntime>::builder()
.with_profile_name("content_article")
.unwrap()
.set_metric_threshold(
"word_count",
500.0, 100.0, 800.0, 2000.0, );
assert!(result.is_err(), "Should reject min >= optimal_min");
let err = result.unwrap_err().to_string();
assert!(err.contains("min") && err.contains("optimal_min"));
}
#[tokio::test]
async fn test_threshold_validation_optimal_min_greater_than_optimal_max() {
let result = Analyzer::<DefaultRuntime>::builder()
.with_profile_name("content_article")
.unwrap()
.set_metric_threshold(
"word_count",
100.0, 800.0, 500.0, 2000.0, );
assert!(result.is_err(), "Should reject optimal_min > optimal_max");
let err = result.unwrap_err().to_string();
assert!(err.contains("optimal_min") && err.contains("optimal_max"));
}
#[tokio::test]
async fn test_threshold_validation_optimal_max_greater_than_max() {
let result = Analyzer::<DefaultRuntime>::builder()
.with_profile_name("content_article")
.unwrap()
.set_metric_threshold(
"word_count",
100.0, 500.0, 2000.0, 2000.0, );
assert!(result.is_err(), "Should reject optimal_max >= max");
let err = result.unwrap_err().to_string();
assert!(err.contains("optimal_max") && err.contains("max"));
}
#[tokio::test]
async fn test_threshold_validation_negative_values() {
let result = Analyzer::<DefaultRuntime>::builder()
.with_profile_name("content_article")
.unwrap()
.set_metric_threshold(
"word_count",
-100.0, 500.0,
800.0,
2000.0,
);
assert!(result.is_err(), "Should reject negative values");
let err = result.unwrap_err().to_string();
assert!(err.contains("negative") || err.contains("min"));
}
#[tokio::test]
async fn test_threshold_validation_infinite_values() {
let result = Analyzer::<DefaultRuntime>::builder()
.with_profile_name("content_article")
.unwrap()
.set_metric_threshold(
"word_count",
100.0,
500.0,
f32::INFINITY, 2000.0,
);
assert!(result.is_err(), "Should reject infinite values");
let err = result.unwrap_err().to_string();
assert!(err.contains("finite") || err.contains("optimal_max"));
}
#[tokio::test]
async fn test_threshold_validation_nan_values() {
let result = Analyzer::<DefaultRuntime>::builder()
.with_profile_name("content_article")
.unwrap()
.set_metric_threshold(
"word_count",
100.0,
f32::NAN, 800.0,
2000.0,
);
assert!(result.is_err(), "Should reject NaN values");
let err = result.unwrap_err().to_string();
assert!(err.contains("finite") || err.contains("optimal_min"));
}
#[tokio::test]
async fn test_invalid_metric_name() {
let result = Analyzer::<DefaultRuntime>::builder()
.with_profile_name("content_article")
.unwrap()
.set_metric_threshold("nonexistent_metric_xyz", 100.0, 500.0, 800.0, 2000.0);
assert!(result.is_err(), "Should reject unknown metric names");
let err = result.unwrap_err().to_string();
assert!(err.contains("nonexistent_metric_xyz") || err.contains("Unknown metric"));
}
#[tokio::test]
async fn test_threshold_chaining_with_other_operations() {
let analyzer: Analyzer = Analyzer::builder()
.with_profile_name("content_article")
.unwrap()
.disable_metric("readability_fk")
.unwrap()
.set_metric_threshold("word_count", 100.0, 500.0, 2000.0, 5000.0)
.unwrap()
.enable_metric("paragraph_count")
.unwrap()
.set_simple_threshold("title_len", 20.0, 60.0, 120.0)
.unwrap()
.build()
.unwrap();
let report = analyzer
.run(TEST_URL, Some(TEST_HTML_MEDIUM))
.await
.unwrap();
assert!(report.score > 0.0);
}
#[tokio::test]
async fn test_threshold_override_profile_default() {
let analyzer: Analyzer = Analyzer::builder()
.with_profile_name("news")
.unwrap()
.set_metric_threshold(
"word_count",
50.0, 300.0,
1500.0,
4000.0,
)
.unwrap()
.build()
.unwrap();
let report = analyzer
.run(TEST_URL, Some(TEST_HTML_MEDIUM))
.await
.unwrap();
assert!(report.score > 0.0);
}
#[tokio::test]
async fn test_edge_case_equal_optimal_values() {
let analyzer: Analyzer = Analyzer::builder()
.with_profile_name("content_article")
.unwrap()
.set_metric_threshold(
"word_count",
100.0,
500.0,
500.0, 2000.0,
)
.unwrap()
.build()
.unwrap();
let report = analyzer
.run(TEST_URL, Some(TEST_HTML_MEDIUM))
.await
.unwrap();
assert!(report.score > 0.0);
}
#[tokio::test]
async fn test_edge_case_very_narrow_ranges() {
let analyzer: Analyzer = Analyzer::builder()
.with_profile_name("content_article")
.unwrap()
.set_metric_threshold(
"title_len",
10.0,
10.1, 10.2, 10.3, )
.unwrap()
.build()
.unwrap();
let report = analyzer
.run(TEST_URL, Some(TEST_HTML_MEDIUM))
.await
.unwrap();
assert!(report.score >= 0.0);
}
#[tokio::test]
async fn test_threshold_with_different_profiles() {
let profiles = ["content_article", "news", "blog"];
for profile in &profiles {
let analyzer: Analyzer = Analyzer::builder()
.with_profile_name(profile)
.unwrap()
.set_metric_threshold("word_count", 100.0, 400.0, 1500.0, 4000.0)
.unwrap()
.build()
.unwrap();
let report = analyzer
.run(TEST_URL, Some(TEST_HTML_MEDIUM))
.await
.unwrap();
assert!(
report.score >= 0.0,
"Profile {} should work with custom thresholds",
profile
);
}
}
#[tokio::test]
async fn test_comprehensive_threshold_workflow() {
let analyzer: Analyzer = Analyzer::builder()
.with_profile_name("content_article")
.unwrap()
.set_metric_threshold("word_count", 150.0, 600.0, 2500.0, 6000.0)
.unwrap()
.set_simple_threshold("title_len", 25.0, 55.0, 110.0)
.unwrap()
.set_metric_threshold("paragraph_count", 3.0, 6.0, 18.0, 35.0)
.unwrap()
.disable_metric("readability_fk")
.unwrap()
.enable_metric("headings_count")
.unwrap()
.build()
.unwrap();
let report_short = analyzer.run(TEST_URL, Some(TEST_HTML_SHORT)).await.unwrap();
let report_medium = analyzer
.run(TEST_URL, Some(TEST_HTML_MEDIUM))
.await
.unwrap();
let report_long = analyzer.run(TEST_URL, Some(TEST_HTML_LONG)).await.unwrap();
assert!(report_short.score >= 0.0);
assert!(report_medium.score >= 0.0);
assert!(report_long.score >= 0.0);
println!("✅ Comprehensive threshold workflow test passed!");
println!(" - Short content score: {:.2}", report_short.score);
println!(" - Medium content score: {:.2}", report_medium.score);
println!(" - Long content score: {:.2}", report_long.score);
}