Skip to content

Commit 8ee134d

Browse files
fix and add tests
1 parent 95f82c5 commit 8ee134d

File tree

2 files changed

+74
-12
lines changed

2 files changed

+74
-12
lines changed

src/service.rs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,8 @@ use std::pin::Pin;
88

99
type BoxFuture<T> = Pin<Box<dyn Future<Output = T> + Send + 'static>>;
1010

11-
/// Wraps an S3 service and short-circuits `GET /` and `GET /health` requests,
12-
/// returning `200 OK` with a plain-text `"Status OK"` body without forwarding
13-
/// them to the S3 layer or requiring authentication.
11+
/// Wraps an S3 service and short-circuits `GET /health` and `GET /upstream-health`
12+
/// requests without forwarding them to the S3 layer or requiring authentication.
1413
#[derive(Clone)]
1514
pub struct S3CachingServiceProxy<S> {
1615
inner: S,

tests/integration_health.rs

Lines changed: 72 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,15 @@ use std::net::SocketAddr;
55
use tokio::net::TcpStream;
66

77
async fn start_test_server() -> (SocketAddr, tokio::sync::oneshot::Sender<()>) {
8+
start_test_server_with_upstream("http://127.0.0.1:1").await
9+
}
10+
11+
async fn start_test_server_with_upstream(
12+
upstream: &str,
13+
) -> (SocketAddr, tokio::sync::oneshot::Sender<()>) {
814
let config = s3_cache::Config {
915
listen_addr: "127.0.0.1:0".parse().unwrap(),
10-
upstream_endpoint: "http://127.0.0.1:1".to_string(),
16+
upstream_endpoint: upstream.to_string(),
1117
upstream_access_key_id: "test".to_string(),
1218
upstream_secret_access_key: "test".to_string(),
1319
upstream_region: "us-east-1".to_string(),
@@ -60,6 +66,37 @@ async fn http_get(addr: SocketAddr, path: &str) -> (u16, String) {
6066
(status, String::from_utf8_lossy(&body).to_string())
6167
}
6268

69+
async fn start_fake_upstream(status: u16) -> (SocketAddr, tokio::sync::oneshot::Sender<()>) {
70+
let listener = tokio::net::TcpListener::bind("127.0.0.1:0").await.unwrap();
71+
let addr = listener.local_addr().unwrap();
72+
73+
let (shutdown_tx, mut shutdown_rx) = tokio::sync::oneshot::channel::<()>();
74+
75+
tokio::spawn(async move {
76+
loop {
77+
let (stream, _) = tokio::select! {
78+
result = listener.accept() => result.unwrap(),
79+
_ = &mut shutdown_rx => break,
80+
};
81+
tokio::spawn(async move {
82+
let service = hyper::service::service_fn(move |_req| {
83+
let resp = hyper::Response::builder()
84+
.status(status)
85+
.body(http_body_util::Empty::<Bytes>::new())
86+
.unwrap();
87+
std::future::ready(Ok::<_, std::convert::Infallible>(resp))
88+
});
89+
hyper::server::conn::http1::Builder::new()
90+
.serve_connection(TokioIo::new(stream), service)
91+
.await
92+
.ok();
93+
});
94+
}
95+
});
96+
97+
(addr, shutdown_tx)
98+
}
99+
63100
// MARK: - Health
64101

65102
#[tokio::test(flavor = "multi_thread")]
@@ -69,25 +106,51 @@ async fn health_check_ok() {
69106
let (status, body) = http_get(addr, "/health").await;
70107

71108
assert_eq!(status, 200);
72-
assert_eq!(body, "Status OK");
109+
assert_eq!(body, "");
73110
}
74111

75112
#[tokio::test(flavor = "multi_thread")]
76-
async fn health_check_root_ok() {
113+
async fn health_check_does_not_require_auth() {
77114
let (addr, _shutdown) = start_test_server().await;
78115

79-
let (status, body) = http_get(addr, "/").await;
116+
// No Authorization header — must still succeed
117+
let (status, _) = http_get(addr, "/health").await;
80118

81119
assert_eq!(status, 200);
82-
assert_eq!(body, "Status OK");
83120
}
84121

85122
#[tokio::test(flavor = "multi_thread")]
86-
async fn health_check_does_not_require_auth() {
87-
let (addr, _shutdown) = start_test_server().await;
123+
async fn upstream_health_returns_ok_when_upstream_is_healthy() {
124+
let (upstream_addr, _upstream_shutdown) = start_fake_upstream(200).await;
125+
let upstream_url = format!("http://{upstream_addr}");
88126

89-
// No Authorization header — must still succeed
90-
let (status, _) = http_get(addr, "/health").await;
127+
let (addr, _shutdown) = start_test_server_with_upstream(&upstream_url).await;
128+
let (status, _) = http_get(addr, "/upstream-health").await;
91129

92130
assert_eq!(status, 200);
93131
}
132+
133+
#[tokio::test(flavor = "multi_thread")]
134+
async fn upstream_health_returns_same_status_code_as_upstream() {
135+
let random_http_status_code = 201;
136+
let (upstream_addr, _upstream_shutdown) = start_fake_upstream(random_http_status_code).await;
137+
let upstream_url = format!("http://{upstream_addr}");
138+
139+
let (addr, _shutdown) = start_test_server_with_upstream(&upstream_url).await;
140+
let (status, _) = http_get(addr, "/upstream-health").await;
141+
142+
assert_eq!(status, random_http_status_code);
143+
}
144+
145+
#[tokio::test(flavor = "multi_thread")]
146+
async fn upstream_health_returns_500_when_upstream_is_unreachable() {
147+
let listener = tokio::net::TcpListener::bind("127.0.0.1:0").await.unwrap();
148+
let dead_addr = listener.local_addr().unwrap();
149+
drop(listener);
150+
151+
let upstream_url = format!("http://{dead_addr}");
152+
let (addr, _shutdown) = start_test_server_with_upstream(&upstream_url).await;
153+
let (status, _) = http_get(addr, "/upstream-health").await;
154+
155+
assert_eq!(status, 500);
156+
}

0 commit comments

Comments
 (0)