//! Route-level tests driven through `tower::ServiceExt::oneshot`. use axum::body::{Body, to_bytes}; use axum::http::{Request, StatusCode, header}; use ferrenium::config::Config; use ferrenium::server; use ferrenium::state::{AppState, Overrides}; use tower::ServiceExt; fn app_with(toml: &str) -> (axum::Router, std::sync::Arc) { let mut config = Config::parse(toml).expect("parse"); config.finish().expect("valid"); let state = AppState::new(config, Overrides::default()); (server::app(state.clone()), state) } async fn get(app: &axum::Router, uri: &str) -> (StatusCode, axum::http::HeaderMap, String) { let res = app .clone() .oneshot(Request::get(uri).body(Body::empty()).unwrap()) .await .unwrap(); let status = res.status(); let headers = res.headers().clone(); let body = to_bytes(res.into_body(), usize::MAX).await.unwrap(); (status, headers, String::from_utf8_lossy(&body).into_owned()) } #[tokio::test] async fn index_renders_styled_empty_page() { let (app, state) = app_with("[site]\ntitle = \"krolyxon\"\ndescription = \"Personal \"\n"); let (status, headers, body) = get(&app, "/").await; assert_eq!(status, StatusCode::OK); assert_eq!(headers[header::CONTENT_TYPE], "text/html; charset=utf-8"); assert!( headers[header::CONTENT_SECURITY_POLICY] .to_str() .unwrap() .contains("script-src 'self'") ); assert_eq!(headers[header::X_CONTENT_TYPE_OPTIONS], "nosniff"); let static_base = format!("/-/static/{}/", state.assets.hash()); assert!( body.contains(&format!("{static_base}css/app.css")), "{body}" ); assert!(body.contains(&format!("{static_base}js/theme.js"))); assert!(body.contains(r#"data-default-theme="auto""#)); assert!(!body.contains("data-theme=\"")); // auto: OS decides assert!(body.contains("krolyxon")); // Escaped by askama. assert!(body.contains("Personal <repos>") || body.contains("Personal <repos>")); assert!(body.contains("No repositories")); // No inline scripts or styles (CSP). assert!(!body.contains("