Files
foxhunt/testing/integration/smoke_tests/infrastructure_health.rs
jgrusewski 9c3d741a08 refactor: restructure repo — crates/, bin/, testing/ layout
Move 17 library crates into crates/, CLI binary into bin/fxt,
consolidate 10 test crates into testing/, split config crate
from deployment config files.

Root directory reduced from 38+ to ~17 directories.
All Cargo.toml paths and build.rs proto refs updated.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 11:56:00 +01:00

294 lines
8.8 KiB
Rust

//! Infrastructure Health Smoke Tests
//!
//! Tests to validate that core infrastructure services are available and functional.
//! These tests run first to ensure the foundation is solid before testing application services.
use super::common::*;
#[tokio::test]
async fn test_postgres_connection() {
let result = with_timeout(async {
let pool = sqlx::PgPool::connect(&database_url())
.await
.map_err(|e| Box::new(e) as Box<dyn std::error::Error>)?;
// Verify database is responsive
let row: (i32,) = sqlx::query_as("SELECT 1")
.fetch_one(&pool)
.await
.map_err(|e| Box::new(e) as Box<dyn std::error::Error>)?;
assert_eq!(row.0, 1, "Database query returned unexpected value");
// Check TimescaleDB extension
let has_timescale: (bool,) = sqlx::query_as(
"SELECT COUNT(*) > 0 FROM pg_extension WHERE extname = 'timescaledb'"
)
.fetch_one(&pool)
.await
.map_err(|e| Box::new(e) as Box<dyn std::error::Error>)?;
pool.close().await;
println!("✅ PostgreSQL connection successful (TimescaleDB: {})", has_timescale.0);
Ok(())
}).await;
skip_if_unavailable!("PostgreSQL", { result });
}
#[tokio::test]
async fn test_postgres_schema_exists() {
let result = with_timeout(async {
let pool = sqlx::PgPool::connect(&database_url())
.await
.map_err(|e| Box::new(e) as Box<dyn std::error::Error>)?;
// Check for key tables
let tables = vec!["orders", "positions", "market_data", "audit_log"];
for table in tables {
let exists: (bool,) = sqlx::query_as(
"SELECT EXISTS (SELECT FROM information_schema.tables WHERE table_name = $1)"
)
.bind(table)
.fetch_one(&pool)
.await
.map_err(|e| Box::new(e) as Box<dyn std::error::Error>)?;
assert!(exists.0, "Table '{}' does not exist", table);
println!(" ✓ Table '{}' exists", table);
}
pool.close().await;
println!("✅ PostgreSQL schema validation successful");
Ok(())
}).await;
skip_if_unavailable!("PostgreSQL", { result });
}
#[tokio::test]
async fn test_redis_connection() {
let result = with_timeout(async {
use redis::AsyncCommands;
let client = redis::Client::open(redis_url().as_str())
.map_err(|e| Box::new(e) as Box<dyn std::error::Error>)?;
let mut con = client.get_multiplexed_async_connection()
.await
.map_err(|e| Box::new(e) as Box<dyn std::error::Error>)?;
// Test PING
let pong: String = redis::cmd("PING")
.query_async(&mut con)
.await
.map_err(|e| Box::new(e) as Box<dyn std::error::Error>)?;
assert_eq!(pong, "PONG", "Redis PING returned unexpected value");
// Test SET/GET
let test_key = "smoke_test:redis_health";
let test_value = "healthy";
con.set::<_, _, ()>(test_key, test_value)
.await
.map_err(|e| Box::new(e) as Box<dyn std::error::Error>)?;
let retrieved: String = con.get(test_key)
.await
.map_err(|e| Box::new(e) as Box<dyn std::error::Error>)?;
assert_eq!(retrieved, test_value, "Redis GET returned unexpected value");
// Cleanup
con.del::<_, ()>(test_key)
.await
.map_err(|e| Box::new(e) as Box<dyn std::error::Error>)?;
println!("✅ Redis connection and operations successful");
Ok(())
}).await;
skip_if_unavailable!("Redis", { result });
}
#[tokio::test]
async fn test_vault_connectivity() {
let result = with_timeout(async {
let client = reqwest::Client::new();
let vault_url = vault_url();
// Test Vault health endpoint
let response = client
.get(format!("{}/v1/sys/health", vault_url))
.send()
.await
.map_err(|e| Box::new(e) as Box<dyn std::error::Error>)?;
// Vault health returns 200 (initialized and unsealed), 429 (unsealed and standby),
// 472 (data recovery mode), 473 (performance standby), or 501 (not initialized)
let status = response.status();
assert!(
status == 200 || status == 429 || status == 473,
"Vault health check returned unexpected status: {}",
status
);
println!("✅ Vault connectivity successful (status: {})", status);
Ok(())
}).await;
skip_if_unavailable!("Vault", { result });
}
#[tokio::test]
async fn test_influxdb_connectivity() {
let result = with_timeout(async {
let client = reqwest::Client::new();
let influxdb_url = influxdb_url();
// Test InfluxDB ping endpoint
let response = client
.get(format!("{}/ping", influxdb_url))
.send()
.await
.map_err(|e| Box::new(e) as Box<dyn std::error::Error>)?;
assert!(
response.status().is_success(),
"InfluxDB ping failed with status: {}",
response.status()
);
println!("✅ InfluxDB connectivity successful");
Ok(())
}).await;
skip_if_unavailable!("InfluxDB", { result });
}
#[tokio::test]
async fn test_prometheus_connectivity() {
let result = with_timeout(async {
let client = reqwest::Client::new();
let prometheus_url = prometheus_url();
// Test Prometheus health endpoint
let response = client
.get(format!("{}/-/healthy", prometheus_url))
.send()
.await
.map_err(|e| Box::new(e) as Box<dyn std::error::Error>)?;
assert!(
response.status().is_success(),
"Prometheus health check failed with status: {}",
response.status()
);
println!("✅ Prometheus connectivity successful");
Ok(())
}).await;
skip_if_unavailable!("Prometheus", { result });
}
#[tokio::test]
async fn test_grafana_connectivity() {
let result = with_timeout(async {
let client = reqwest::Client::new();
let grafana_url = grafana_url();
// Test Grafana API health endpoint
let response = client
.get(format!("{}/api/health", grafana_url))
.send()
.await
.map_err(|e| Box::new(e) as Box<dyn std::error::Error>)?;
assert!(
response.status().is_success(),
"Grafana health check failed with status: {}",
response.status()
);
println!("✅ Grafana connectivity successful");
Ok(())
}).await;
skip_if_unavailable!("Grafana", { result });
}
#[tokio::test]
async fn test_infrastructure_all_healthy() {
println!("🏥 Running comprehensive infrastructure health check...");
let mut checks_passed = 0;
let mut checks_failed = 0;
// PostgreSQL
match sqlx::PgPool::connect(&database_url()).await {
Ok(pool) => {
pool.close().await;
println!(" ✓ PostgreSQL");
checks_passed += 1;
}
Err(e) => {
eprintln!(" ✗ PostgreSQL: {}", e);
checks_failed += 1;
}
}
// Redis
match redis::Client::open(redis_url().as_str()) {
Ok(client) => {
match client.get_multiplexed_async_connection().await {
Ok(_) => {
println!(" ✓ Redis");
checks_passed += 1;
}
Err(e) => {
eprintln!(" ✗ Redis: {}", e);
checks_failed += 1;
}
}
}
Err(e) => {
eprintln!(" ✗ Redis: {}", e);
checks_failed += 1;
}
}
// Vault (optional - don't fail if unavailable)
let client = reqwest::Client::new();
match client.get(format!("{}/v1/sys/health", vault_url())).send().await {
Ok(response) if response.status() == 200 || response.status() == 429 => {
println!(" ✓ Vault");
checks_passed += 1;
}
_ => {
println!(" ⚠ Vault (not critical)");
}
}
// InfluxDB (optional - don't fail if unavailable)
match client.get(format!("{}/ping", influxdb_url())).send().await {
Ok(response) if response.status().is_success() => {
println!(" ✓ InfluxDB");
checks_passed += 1;
}
_ => {
println!(" ⚠ InfluxDB (not critical)");
}
}
println!("\n📊 Infrastructure Health Summary:");
println!(" Passed: {}", checks_passed);
println!(" Failed: {}", checks_failed);
// Require at least PostgreSQL and Redis
assert!(checks_passed >= 2, "Critical infrastructure services are down");
println!("✅ Infrastructure health check complete");
}