use crawlberg::{BrowserMode, CrawlConfig, CrawlError, DispatchProfile, EscalationStrategy, create_engine, scrape};
use wiremock::matchers::{method, path};
use wiremock::{Mock, MockServer, ResponseTemplate};
fn engine_with(config: CrawlConfig) -> crawlberg::CrawlEngineHandle {
create_engine(Some(config)).expect("engine build must not fail")
}
#[tokio::test]
async fn browser_only_strategy_browser_never_returns_forbidden_error() {
let mock = MockServer::start().await;
Mock::given(method("GET"))
.and(path("/forbidden"))
.respond_with(ResponseTemplate::new(403).set_body_string("nope"))
.mount(&mock)
.await;
let handle = engine_with(CrawlConfig {
dispatch: Some(DispatchProfile {
strategy: EscalationStrategy::BrowserOnly,
..DispatchProfile::default()
}),
browser: crawlberg::BrowserConfig {
mode: BrowserMode::Never,
..Default::default()
},
..CrawlConfig::default()
});
let url = format!("{}/forbidden", mock.uri());
let result = scrape(&handle, &url).await;
assert!(result.is_err(), "expected Err, got Ok: {result:?}");
let err = result.unwrap_err();
assert!(
matches!(err, CrawlError::Forbidden(_) | CrawlError::WafBlocked { .. }),
"expected Forbidden or WafBlocked, got: {err:?}"
);
}
#[tokio::test]
async fn browser_only_strategy_browser_never_returns_waf_blocked_error() {
let mock = MockServer::start().await;
Mock::given(method("GET"))
.and(path("/cf"))
.respond_with(
ResponseTemplate::new(403)
.insert_header("server", "cloudflare")
.set_body_string("<html><head><title>Just a moment...</title></head></html>"),
)
.mount(&mock)
.await;
let handle = engine_with(CrawlConfig {
dispatch: Some(DispatchProfile {
strategy: EscalationStrategy::BrowserOnly,
..DispatchProfile::default()
}),
browser: crawlberg::BrowserConfig {
mode: BrowserMode::Never,
..Default::default()
},
..CrawlConfig::default()
});
let url = format!("{}/cf", mock.uri());
let result = scrape(&handle, &url).await;
assert!(result.is_err(), "expected Err, got Ok: {result:?}");
let err = result.unwrap_err();
assert!(
matches!(err, CrawlError::Forbidden(_) | CrawlError::WafBlocked { .. }),
"expected Forbidden or WafBlocked, got: {err:?}"
);
}
#[tokio::test]
async fn none_strategy_browser_auto_returns_forbidden_error() {
let mock = MockServer::start().await;
Mock::given(method("GET"))
.and(path("/forbidden"))
.respond_with(ResponseTemplate::new(403).set_body_string("access denied"))
.mount(&mock)
.await;
let handle = engine_with(CrawlConfig {
dispatch: Some(DispatchProfile {
strategy: EscalationStrategy::None,
..DispatchProfile::default()
}),
browser: crawlberg::BrowserConfig {
mode: BrowserMode::Never,
..Default::default()
},
..CrawlConfig::default()
});
let url = format!("{}/forbidden", mock.uri());
let result = scrape(&handle, &url).await;
assert!(result.is_err(), "expected Err, got Ok: {result:?}");
assert!(
matches!(
result.unwrap_err(),
CrawlError::Forbidden(_) | CrawlError::WafBlocked { .. }
),
"expected Forbidden or WafBlocked"
);
}
#[tokio::test]
async fn none_strategy_returns_waf_blocked_not_ok() {
let mock = MockServer::start().await;
Mock::given(method("GET"))
.and(path("/akamai"))
.respond_with(
ResponseTemplate::new(403)
.insert_header("server", "AkamaiGHost")
.set_body_string("<html><body>Access Denied</body></html>"),
)
.mount(&mock)
.await;
let handle = engine_with(CrawlConfig {
dispatch: Some(DispatchProfile {
strategy: EscalationStrategy::None,
..DispatchProfile::default()
}),
browser: crawlberg::BrowserConfig {
mode: BrowserMode::Never,
..Default::default()
},
..CrawlConfig::default()
});
let url = format!("{}/akamai", mock.uri());
let result = scrape(&handle, &url).await;
assert!(result.is_err(), "expected Err, got Ok: {result:?}");
let err = result.unwrap_err();
assert!(
matches!(err, CrawlError::Forbidden(_) | CrawlError::WafBlocked { .. }),
"expected Forbidden or WafBlocked, got: {err:?}"
);
}