use crawlberg::{BrowserMode, CrawlConfig, CrawlError, create_engine, scrape};
use wiremock::matchers::{method, path};
use wiremock::{Mock, MockServer, ResponseTemplate};
fn engine_with_config(mut config: CrawlConfig) -> crawlberg::CrawlEngineHandle {
config.browser.mode = BrowserMode::Never;
create_engine(Some(config)).expect("engine build must not fail")
}
#[tokio::test]
async fn direct_404_raises_when_soft_errors_disabled() {
let mock = MockServer::start().await;
Mock::given(method("GET"))
.and(path("/not-found"))
.respond_with(ResponseTemplate::new(404))
.mount(&mock)
.await;
let handle = engine_with_config(CrawlConfig::default());
let url = format!("{}/not-found", mock.uri());
let result = scrape(&handle, &url).await;
assert!(result.is_err(), "expected Err, got Ok: {result:?}");
assert!(
matches!(result.unwrap_err(), CrawlError::NotFound(_)),
"expected CrawlError::NotFound"
);
}
#[tokio::test]
async fn direct_404_returns_result_when_soft_errors_enabled() {
let mock = MockServer::start().await;
Mock::given(method("GET"))
.and(path("/not-found"))
.respond_with(ResponseTemplate::new(404))
.mount(&mock)
.await;
let handle = engine_with_config(CrawlConfig {
soft_http_errors: true,
..CrawlConfig::default()
});
let url = format!("{}/not-found", mock.uri());
let result = scrape(&handle, &url).await;
assert!(result.is_ok(), "expected Ok, got Err: {:?}", result.err());
let page = result.unwrap();
assert_eq!(page.status_code, 404, "status_code must be 404");
assert!(page.html.is_empty(), "body must be empty for synthesised 404");
}
#[tokio::test]
async fn redirected_404_returns_result_regardless_of_soft_errors() {
let mock = MockServer::start().await;
Mock::given(method("GET"))
.and(path("/start"))
.respond_with(
ResponseTemplate::new(302)
.append_header("location", "/not-found")
.append_header("content-type", "text/html"),
)
.mount(&mock)
.await;
Mock::given(method("GET"))
.and(path("/not-found"))
.respond_with(ResponseTemplate::new(404))
.mount(&mock)
.await;
let handle = engine_with_config(CrawlConfig::default());
let url = format!("{}/start", mock.uri());
let result = scrape(&handle, &url).await;
assert!(
result.is_ok(),
"redirected 404 must return Ok regardless of soft_http_errors: {:?}",
result.err()
);
let page = result.unwrap();
assert_eq!(page.status_code, 404, "status_code must be 404 after redirect chain");
}
#[tokio::test]
async fn direct_403_raises_when_soft_errors_disabled() {
let mock = MockServer::start().await;
Mock::given(method("GET"))
.and(path("/forbidden"))
.respond_with(ResponseTemplate::new(403))
.mount(&mock)
.await;
let handle = engine_with_config(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(_)),
"expected CrawlError::Forbidden"
);
}
#[tokio::test]
async fn direct_403_returns_result_when_soft_errors_enabled() {
let mock = MockServer::start().await;
Mock::given(method("GET"))
.and(path("/forbidden"))
.respond_with(ResponseTemplate::new(403))
.mount(&mock)
.await;
let handle = engine_with_config(CrawlConfig {
soft_http_errors: true,
..CrawlConfig::default()
});
let url = format!("{}/forbidden", mock.uri());
let result = scrape(&handle, &url).await;
assert!(result.is_ok(), "expected Ok, got Err: {:?}", result.err());
let page = result.unwrap();
assert_eq!(page.status_code, 403, "status_code must be 403");
assert!(page.html.is_empty(), "body must be empty for synthesised 403");
}