stemedb/crates/stemedb-api/tests/http_basic.rs
jordan 58594bc7b9 feat: add feed endpoint, dashboard feed panel, and FindMyHealth app
- Add /v1/feed API endpoint with handler and tests
- Remove health endpoint rate limiting (behind firewall, caused spurious 429s)
- Add dashboard feed panel with list, row, empty state, and loading skeleton
- Update home page to show feed instead of redirecting to skeptic
- Improve API key auth middleware and DTO create/query params
- Add OpenAPI conceptual guide (api-intro.md) with semaglutide examples
- Add FindMyHealth application scaffolding (vision, architecture, prototypes)
- Add FindMyHealth designer/writer and Aphoria founder-CEO agents
- Update roadmap with current progress

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-16 17:16:17 -07:00

98 lines
3.2 KiB
Rust

//! HTTP integration tests for basic endpoint functionality.
//!
//! Coverage:
//! - GET /v1/health - Health check endpoint
//! - Basic response structure validation
//! - Server availability tests
//! - Real TCP listener tests (ConnectInfo injection)
#![allow(clippy::expect_used)]
mod common;
use axum::{
body::Body,
http::{Request, StatusCode},
};
use tower::ServiceExt;
use stemedb_api::create_router;
// ============================================================================
// Health Check Tests
// ============================================================================
#[tokio::test]
async fn test_health_check() {
let env = common::create_test_env().await;
let app = create_router(env.state);
let request =
Request::builder().uri("/v1/health").method("GET").body(Body::empty()).expect("Request");
let response = app.oneshot(request).await.expect("Request");
assert_eq!(response.status(), StatusCode::OK);
let body = axum::body::to_bytes(response.into_body(), usize::MAX).await.expect("Body");
let json: serde_json::Value = serde_json::from_slice(&body).expect("JSON");
assert_eq!(json["status"], "healthy");
assert!(json["version"].is_string());
assert_eq!(json["assertions_count"], 0);
}
/// Test health check over a real TCP connection.
///
/// This catches the ConnectInfo<SocketAddr> injection bug where
/// rate_limit_middleware requires ConnectInfo but the server didn't
/// provide it via into_make_service_with_connect_info().
#[tokio::test]
async fn test_health_check_over_tcp() {
use std::net::SocketAddr;
use tokio::io::{AsyncReadExt, AsyncWriteExt};
let env = common::create_test_env().await;
let app = create_router(env.state);
// Bind to a random available port
let listener = tokio::net::TcpListener::bind("127.0.0.1:0").await.expect("bind");
let addr = listener.local_addr().expect("local_addr");
// Serve with ConnectInfo injection (the fix for the 500 bug)
tokio::spawn(async move {
axum::serve(
listener,
app.into_make_service_with_connect_info::<SocketAddr>(),
)
.await
.expect("server");
});
// Give the server a moment to start
tokio::time::sleep(std::time::Duration::from_millis(50)).await;
// Make a raw HTTP/1.1 request over TCP
let mut stream = tokio::net::TcpStream::connect(addr).await.expect("connect");
let request = format!(
"GET /v1/health HTTP/1.1\r\nHost: {}\r\nConnection: close\r\n\r\n",
addr
);
stream.write_all(request.as_bytes()).await.expect("write");
let mut response = String::new();
stream.read_to_string(&mut response).await.expect("read");
// Verify we got 200 OK, not 500
assert!(
response.starts_with("HTTP/1.1 200"),
"health check over TCP should return 200, not 500 (ConnectInfo must be injected). Got: {}",
response.lines().next().unwrap_or("empty")
);
// Extract JSON body (after the blank line separating headers from body)
let body = response.split("\r\n\r\n").nth(1).expect("response body");
let json: serde_json::Value = serde_json::from_str(body).expect("json parse");
assert_eq!(json["status"], "healthy");
}