use std::path::PathBuf;
use captchaforge::detect::rules::built_in_rules;
const MIN_BYTES: u64 = 200;
fn root() -> PathBuf {
PathBuf::from(env!("CARGO_MANIFEST_DIR"))
.join("tests")
.join("rule_fixtures")
}
#[test]
fn every_positive_fixture_is_at_least_min_bytes() {
let set = built_in_rules().expect("bundled rules parse");
let mut undersized: Vec<(String, u64)> = Vec::new();
for rule in &set.providers {
let p = root().join(&rule.name).join("positive.html");
let size = p
.metadata()
.unwrap_or_else(|e| panic!("stat {}: {e}", p.display()))
.len();
if size < MIN_BYTES {
undersized.push((rule.name.clone(), size));
}
}
assert!(
undersized.is_empty(),
"fixtures below {MIN_BYTES}-byte vendor-chrome bar: {undersized:?}",
);
}
#[test]
fn every_negative_fixture_is_at_least_min_bytes() {
let set = built_in_rules().expect("bundled rules parse");
let mut undersized: Vec<(String, u64)> = Vec::new();
for rule in &set.providers {
let p = root().join(&rule.name).join("negative.html");
let size = p
.metadata()
.unwrap_or_else(|e| panic!("stat {}: {e}", p.display()))
.len();
if size < MIN_BYTES {
undersized.push((rule.name.clone(), size));
}
}
assert!(
undersized.is_empty(),
"negative fixtures below {MIN_BYTES}-byte bar: {undersized:?}",
);
}
#[test]
fn every_positive_fixture_carries_a_vendor_marker() {
let set = built_in_rules().expect("bundled rules parse");
let mut bare: Vec<String> = Vec::new();
for rule in &set.providers {
let p = root().join(&rule.name).join("positive.html");
let body = std::fs::read_to_string(&p).unwrap();
let dashed = rule.name.replace('_', "-");
let spaced = rule.name.replace('_', " ");
let trigger_token: Option<&str> = rule
.triggers
.selectors
.iter()
.filter_map(|s| {
s.trim_start_matches(|c: char| !c.is_ascii_alphanumeric())
.split(|c: char| !c.is_ascii_alphanumeric() && c != '-' && c != '_')
.next()
})
.find(|s| !s.is_empty());
let mut found = body.contains(&rule.name)
|| body.contains(&dashed)
|| body.contains(&spaced);
if !found {
if let Some(tok) = trigger_token {
if body.contains(tok) {
found = true;
}
}
}
if !found {
bare.push(rule.name.clone());
}
}
assert!(
bare.is_empty(),
"positive fixtures missing vendor marker: {bare:?}",
);
}
#[test]
fn generator_output_matches_in_tree_corpus() {
let manifest_root = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
let script = manifest_root.join("tools").join("gen_fixtures.py");
if !script.is_file() {
return;
}
let output = std::process::Command::new("python3")
.arg(&script)
.arg("--check")
.current_dir(&manifest_root)
.output()
.expect("python3 must be on PATH");
assert!(
output.status.success(),
"gen_fixtures.py --check exited {:?}\nstdout:\n{}\nstderr:\n{}",
output.status.code(),
String::from_utf8_lossy(&output.stdout),
String::from_utf8_lossy(&output.stderr),
);
}
#[test]
fn no_negative_fixture_contains_its_own_vendor_marker_strongly() {
let set = built_in_rules().expect("bundled rules parse");
let mut leaks: Vec<String> = Vec::new();
for rule in &set.providers {
let p = root().join(&rule.name).join("negative.html");
let body = std::fs::read_to_string(&p).unwrap();
for needle in rule.triggers.script_src_contains.iter() {
if body.contains(needle) {
leaks.push(format!(
"{}: negative contains script_src needle `{needle}`",
rule.name
));
}
}
}
assert!(
leaks.is_empty(),
"negative fixtures leak vendor script_src needles: {leaks:?}",
);
}