#[cfg(feature = "server")]
mod server_tests {
use std::path::PathBuf;
use std::time::Duration;
use synheart_sensor_agent::gateway::GatewayConfig;
use synheart_sensor_agent::server::{run, ServerConfig};
fn test_state_dir() -> PathBuf {
std::env::temp_dir().join("synheart-server-test")
}
#[tokio::test]
async fn test_health_endpoint() {
let gateway_config = GatewayConfig::new("127.0.0.1", 9999, "test-token".to_string());
let config = ServerConfig::new(0, gateway_config, test_state_dir());
let (addr, shutdown_tx) = run(config).await.expect("Failed to start server");
tokio::time::sleep(Duration::from_millis(100)).await;
let client = reqwest::Client::new();
let response = client
.get(format!("http://{addr}/health"))
.send()
.await
.expect("Failed to send request");
assert!(response.status().is_success());
let body: serde_json::Value = response.json().await.expect("Failed to parse JSON");
assert_eq!(body["status"], "ok");
assert!(body["version"].as_str().is_some());
let _ = shutdown_tx.send(());
}
#[tokio::test]
async fn test_ingest_endpoint_structure() {
let gateway_config = GatewayConfig::new("127.0.0.1", 9999, "test-token".to_string());
let config = ServerConfig::new(0, gateway_config, test_state_dir());
let (addr, shutdown_tx) = run(config).await.expect("Failed to start server");
tokio::time::sleep(Duration::from_millis(100)).await;
let sample_session = serde_json::json!({
"session": {
"session_id": "TEST-123",
"device_id": "chrome-test",
"timezone": "America/Los_Angeles",
"start_time": "2024-01-22T10:00:00Z",
"end_time": "2024-01-22T10:00:10Z",
"events": [
{
"timestamp": "2024-01-22T10:00:01Z",
"event_type": "typing",
"typing": {
"key_category": "alphanumeric",
"hold_ms": 100,
"flight_ms": 50,
"is_backspace_burst": false,
"burst_count": 0
}
},
{
"timestamp": "2024-01-22T10:00:02Z",
"event_type": "scroll",
"scroll": {
"velocity": 150.0,
"direction": "down",
"direction_reversal": false
}
}
],
"meta": {
"source": "synheart-behavior-chrome",
"version": "2.0",
"event_count": 2
}
}
});
let client = reqwest::Client::new();
let response = client
.post(format!("http://{addr}/ingest"))
.header("Content-Type", "application/json")
.header("Authorization", "Bearer test-token")
.json(&sample_session)
.send()
.await
.expect("Failed to send request");
let status = response.status();
let body: serde_json::Value = response.json().await.expect("Failed to parse JSON");
assert!(
status.is_success() || status == reqwest::StatusCode::BAD_GATEWAY,
"Unexpected status: {status} - body: {body:?}"
);
if status == reqwest::StatusCode::BAD_GATEWAY {
assert!(body["code"].as_str().unwrap_or("").contains("GATEWAY"));
}
let _ = shutdown_tx.send(());
}
#[tokio::test]
async fn test_cors_headers() {
let gateway_config = GatewayConfig::new("127.0.0.1", 9999, "test-token".to_string());
let config = ServerConfig::new(0, gateway_config, test_state_dir());
let (addr, shutdown_tx) = run(config).await.expect("Failed to start server");
tokio::time::sleep(Duration::from_millis(100)).await;
let client = reqwest::Client::new();
let response = client
.request(reqwest::Method::OPTIONS, format!("http://{addr}/ingest"))
.header("Origin", "http://localhost")
.header("Access-Control-Request-Method", "POST")
.send()
.await
.expect("Failed to send request");
assert!(
response.status().is_success() || response.status() == reqwest::StatusCode::NO_CONTENT,
"CORS preflight failed: {}",
response.status()
);
let _ = shutdown_tx.send(());
}
}