You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
231 lines
8.4 KiB
231 lines
8.4 KiB
#[cfg(feature = "enable_metrics")]
|
|
mod metrics_integration_tests {
|
|
use rocket::local::blocking::Client;
|
|
use rocket::http::{Status, Header, ContentType};
|
|
use rocket::serde::json;
|
|
use vaultwarden::api::core::routes as core_routes;
|
|
use vaultwarden::api::metrics::routes as metrics_routes;
|
|
use vaultwarden::CONFIG;
|
|
use vaultwarden::metrics;
|
|
|
|
fn create_test_rocket() -> rocket::Rocket<rocket::Build> {
|
|
// Initialize metrics for testing
|
|
metrics::init_build_info();
|
|
|
|
rocket::build()
|
|
.mount("/", core_routes())
|
|
.mount("/", metrics_routes())
|
|
.attach(vaultwarden::api::middleware::MetricsFairing)
|
|
}
|
|
|
|
#[test]
|
|
fn test_metrics_endpoint_without_auth() {
|
|
let client = Client::tracked(create_test_rocket()).expect("valid rocket instance");
|
|
|
|
// Test without authorization header
|
|
let response = client.get("/metrics").dispatch();
|
|
|
|
// Should return 401 Unauthorized when metrics token is required
|
|
if CONFIG.metrics_token().is_some() {
|
|
assert_eq!(response.status(), Status::Unauthorized);
|
|
} else {
|
|
// If no token is configured, it should work
|
|
assert_eq!(response.status(), Status::Ok);
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn test_metrics_endpoint_with_bearer_token() {
|
|
let client = Client::tracked(create_test_rocket()).expect("valid rocket instance");
|
|
|
|
// Test with Bearer token
|
|
if let Some(token) = CONFIG.metrics_token() {
|
|
let auth_header = Header::new("Authorization", format!("Bearer {}", token));
|
|
let response = client.get("/metrics").header(auth_header).dispatch();
|
|
|
|
assert_eq!(response.status(), Status::Ok);
|
|
|
|
let body = response.into_string().expect("response body");
|
|
assert!(body.contains("# HELP"));
|
|
assert!(body.contains("# TYPE"));
|
|
assert!(body.contains("vaultwarden_"));
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn test_metrics_endpoint_with_query_parameter() {
|
|
let client = Client::tracked(create_test_rocket()).expect("valid rocket instance");
|
|
|
|
// Test with query parameter
|
|
if let Some(token) = CONFIG.metrics_token() {
|
|
let response = client.get(format!("/metrics?token={}", token)).dispatch();
|
|
|
|
assert_eq!(response.status(), Status::Ok);
|
|
|
|
let body = response.into_string().expect("response body");
|
|
assert!(body.contains("# HELP"));
|
|
assert!(body.contains("# TYPE"));
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn test_metrics_endpoint_with_invalid_token() {
|
|
let client = Client::tracked(create_test_rocket()).expect("valid rocket instance");
|
|
|
|
// Test with invalid Bearer token
|
|
let auth_header = Header::new("Authorization", "Bearer invalid-token");
|
|
let response = client.get("/metrics").header(auth_header).dispatch();
|
|
|
|
assert_eq!(response.status(), Status::Unauthorized);
|
|
}
|
|
|
|
#[test]
|
|
fn test_metrics_content_format() {
|
|
let client = Client::tracked(create_test_rocket()).expect("valid rocket instance");
|
|
|
|
// Setup authorization if needed
|
|
let mut request = client.get("/metrics");
|
|
|
|
if let Some(token) = CONFIG.metrics_token() {
|
|
let auth_header = Header::new("Authorization", format!("Bearer {}", token));
|
|
request = request.header(auth_header);
|
|
}
|
|
|
|
let response = request.dispatch();
|
|
|
|
if response.status() == Status::Ok {
|
|
let body = response.into_string().expect("response body");
|
|
|
|
// Verify Prometheus format
|
|
assert!(body.contains("# HELP"));
|
|
assert!(body.contains("# TYPE"));
|
|
|
|
// Verify expected metrics exist
|
|
assert!(body.contains("vaultwarden_build_info"));
|
|
assert!(body.contains("vaultwarden_uptime_seconds"));
|
|
|
|
// Verify metric types
|
|
assert!(body.contains("TYPE vaultwarden_build_info gauge"));
|
|
assert!(body.contains("TYPE vaultwarden_uptime_seconds gauge"));
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn test_metrics_instrumentation() {
|
|
let client = Client::tracked(create_test_rocket()).expect("valid rocket instance");
|
|
|
|
// Make some requests to generate HTTP metrics
|
|
let _response1 = client.get("/alive").dispatch();
|
|
let _response2 = client.post("/api/accounts/register")
|
|
.header(ContentType::JSON)
|
|
.body(r#"{"email":"test@example.com"}"#)
|
|
.dispatch();
|
|
|
|
// Now check metrics
|
|
let mut metrics_request = client.get("/metrics");
|
|
|
|
if let Some(token) = CONFIG.metrics_token() {
|
|
let auth_header = Header::new("Authorization", format!("Bearer {}", token));
|
|
metrics_request = metrics_request.header(auth_header);
|
|
}
|
|
|
|
let response = metrics_request.dispatch();
|
|
|
|
if response.status() == Status::Ok {
|
|
let body = response.into_string().expect("response body");
|
|
|
|
// Should contain HTTP request metrics
|
|
assert!(body.contains("vaultwarden_http_requests_total"));
|
|
assert!(body.contains("vaultwarden_http_request_duration_seconds"));
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn test_multiple_concurrent_requests() {
|
|
use std::thread;
|
|
use std::sync::Arc;
|
|
|
|
let client = Arc::new(Client::tracked(create_test_rocket()).expect("valid rocket instance"));
|
|
|
|
// Spawn multiple threads making requests
|
|
let handles: Vec<_> = (0..5).map(|_| {
|
|
let client = Arc::clone(&client);
|
|
thread::spawn(move || {
|
|
client.get("/alive").dispatch();
|
|
})
|
|
}).collect();
|
|
|
|
// Wait for all requests to complete
|
|
for handle in handles {
|
|
handle.join().unwrap();
|
|
}
|
|
|
|
// Check that metrics were collected
|
|
let mut metrics_request = client.get("/metrics");
|
|
|
|
if let Some(token) = CONFIG.metrics_token() {
|
|
let auth_header = Header::new("Authorization", format!("Bearer {}", token));
|
|
metrics_request = metrics_request.header(auth_header);
|
|
}
|
|
|
|
let response = metrics_request.dispatch();
|
|
assert!(response.status() == Status::Ok || response.status() == Status::Unauthorized);
|
|
}
|
|
|
|
#[test]
|
|
fn test_metrics_performance() {
|
|
let client = Client::tracked(create_test_rocket()).expect("valid rocket instance");
|
|
|
|
let start = std::time::Instant::now();
|
|
|
|
let mut metrics_request = client.get("/metrics");
|
|
|
|
if let Some(token) = CONFIG.metrics_token() {
|
|
let auth_header = Header::new("Authorization", format!("Bearer {}", token));
|
|
metrics_request = metrics_request.header(auth_header);
|
|
}
|
|
|
|
let response = metrics_request.dispatch();
|
|
let duration = start.elapsed();
|
|
|
|
// Metrics endpoint should respond quickly (under 1 second)
|
|
assert!(duration.as_secs() < 1);
|
|
|
|
if response.status() == Status::Ok {
|
|
let body = response.into_string().expect("response body");
|
|
// Should return meaningful content
|
|
assert!(body.len() > 100);
|
|
}
|
|
}
|
|
}
|
|
|
|
#[cfg(not(feature = "enable_metrics"))]
|
|
mod metrics_disabled_tests {
|
|
use rocket::local::blocking::Client;
|
|
use rocket::http::Status;
|
|
use vaultwarden::api::core::routes as core_routes;
|
|
|
|
fn create_test_rocket() -> rocket::Rocket<rocket::Build> {
|
|
rocket::build()
|
|
.mount("/", core_routes())
|
|
// Note: metrics routes should not be mounted when feature is disabled
|
|
}
|
|
|
|
#[test]
|
|
fn test_metrics_endpoint_not_available() {
|
|
let client = Client::tracked(create_test_rocket()).expect("valid rocket instance");
|
|
|
|
// Metrics endpoint should not exist when feature is disabled
|
|
let response = client.get("/metrics").dispatch();
|
|
assert_eq!(response.status(), Status::NotFound);
|
|
}
|
|
|
|
#[test]
|
|
fn test_normal_endpoints_still_work() {
|
|
let client = Client::tracked(create_test_rocket()).expect("valid rocket instance");
|
|
|
|
// Normal endpoints should still work
|
|
let response = client.get("/alive").dispatch();
|
|
assert_eq!(response.status(), Status::Ok);
|
|
}
|
|
}
|