Skip to content

Commit dc57737

Browse files
committed
fix: add RuntimeContext + startupz integration tests
1 parent 32f94b9 commit dc57737

2 files changed

Lines changed: 159 additions & 0 deletions

File tree

src/env.rs

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -392,4 +392,119 @@ mod tests {
392392
| Environment::BareMetal
393393
));
394394
}
395+
396+
// --- RuntimeContext tests ---
397+
398+
#[test]
399+
fn test_runtime_context_detect_does_not_panic() {
400+
let ctx = RuntimeContext::detect();
401+
// Environment is always set
402+
assert!(matches!(
403+
ctx.environment,
404+
Environment::Kubernetes
405+
| Environment::Docker
406+
| Environment::Container
407+
| Environment::BareMetal
408+
));
409+
}
410+
411+
#[test]
412+
fn test_runtime_context_bare_metal_has_no_k8s_fields() {
413+
// On a dev machine (bare metal), K8s fields should be None
414+
// unless POD_NAME etc. env vars happen to be set
415+
let ctx = RuntimeContext::detect();
416+
if ctx.environment.is_bare_metal() {
417+
assert!(
418+
ctx.node_name.is_none(),
419+
"node_name should be None on bare metal"
420+
);
421+
// pod_name might come from HOSTNAME, so we don't assert it's None
422+
}
423+
}
424+
425+
#[test]
426+
fn test_runtime_context_reads_pod_name_env() {
427+
temp_env::with_vars(
428+
[
429+
("POD_NAME", Some("test-pod-123")),
430+
("KUBERNETES_SERVICE_HOST", Some("10.0.0.1")),
431+
],
432+
|| {
433+
let ctx = RuntimeContext::detect();
434+
assert_eq!(ctx.pod_name.as_deref(), Some("test-pod-123"));
435+
},
436+
);
437+
}
438+
439+
#[test]
440+
fn test_runtime_context_reads_namespace_env() {
441+
temp_env::with_var("POD_NAMESPACE", Some("production"), || {
442+
let ctx = RuntimeContext::detect();
443+
assert_eq!(ctx.namespace.as_deref(), Some("production"));
444+
});
445+
}
446+
447+
#[test]
448+
fn test_runtime_context_reads_node_name_env() {
449+
temp_env::with_var("NODE_NAME", Some("node-1"), || {
450+
let ctx = RuntimeContext::detect();
451+
assert_eq!(ctx.node_name.as_deref(), Some("node-1"));
452+
});
453+
}
454+
455+
#[test]
456+
fn test_runtime_context_global_singleton() {
457+
// runtime_context() should return the same instance every time
458+
let ctx1 = runtime_context();
459+
let ctx2 = runtime_context();
460+
assert_eq!(ctx1.environment, ctx2.environment);
461+
assert_eq!(ctx1.pod_name, ctx2.pod_name);
462+
}
463+
464+
#[test]
465+
fn test_runtime_context_is_kubernetes_convenience() {
466+
let mut ctx = RuntimeContext::detect();
467+
ctx.environment = Environment::Kubernetes;
468+
assert!(ctx.is_kubernetes());
469+
assert!(ctx.is_container());
470+
assert!(!ctx.is_bare_metal());
471+
}
472+
473+
#[test]
474+
fn test_runtime_context_is_bare_metal_convenience() {
475+
let mut ctx = RuntimeContext::detect();
476+
ctx.environment = Environment::BareMetal;
477+
assert!(!ctx.is_kubernetes());
478+
assert!(!ctx.is_container());
479+
assert!(ctx.is_bare_metal());
480+
}
481+
482+
// --- cgroup helper tests ---
483+
484+
#[test]
485+
fn test_read_cgroup_memory_limit_returns_option() {
486+
// On bare metal, returns None (no cgroup). On container, returns Some.
487+
let limit = read_cgroup_memory_limit();
488+
// Just verify it doesn't panic — result depends on environment
489+
let _ = limit;
490+
}
491+
492+
#[test]
493+
fn test_read_cgroup_cpu_quota_returns_option() {
494+
let quota = read_cgroup_cpu_quota();
495+
let _ = quota;
496+
}
497+
498+
#[test]
499+
fn test_prestop_delay_default_bare_metal() {
500+
// On bare metal, default pre-stop delay should be 0
501+
temp_env::with_var("PRESTOP_DELAY_SECS", None::<&str>, || {
502+
let ctx = RuntimeContext::detect();
503+
if ctx.environment.is_bare_metal() {
504+
// The prestop_delay_secs function is in shutdown.rs,
505+
// but we can verify the RuntimeContext is bare metal
506+
assert!(!ctx.is_kubernetes());
507+
}
508+
});
509+
}
395510
}

tests/integration/metrics.rs

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -644,3 +644,47 @@ async fn test_18_concurrent_requests_during_shutdown() {
644644
"concurrent requests should not hang during shutdown"
645645
);
646646
}
647+
648+
#[tokio::test]
649+
async fn test_19_startupz_before_and_after_mark_started() {
650+
let _lock = TEST_LOCK.lock().unwrap_or_else(|e| e.into_inner());
651+
init_manager();
652+
653+
let port = find_available_port().await;
654+
let addr = format!("127.0.0.1:{port}");
655+
656+
let mut guard = MANAGER.lock().unwrap_or_else(|e| e.into_inner());
657+
let manager = guard.as_mut().expect("manager not initialised");
658+
659+
manager
660+
.start_server(&addr)
661+
.await
662+
.expect("failed to start server");
663+
tokio::time::sleep(Duration::from_millis(50)).await;
664+
665+
// Before mark_started: /startupz should return 503
666+
let (status, body) = http_get(&addr, "/startupz").await;
667+
assert!(
668+
status.contains("503"),
669+
"expected 503 before mark_started, got: {status}"
670+
);
671+
assert!(
672+
body.contains("starting"),
673+
"expected 'starting' in body: {body}"
674+
);
675+
676+
// After mark_started: /startupz should return 200
677+
manager.mark_started();
678+
let (status, body) = http_get(&addr, "/startupz").await;
679+
assert!(
680+
status.contains("200"),
681+
"expected 200 after mark_started, got: {status}"
682+
);
683+
assert!(
684+
body.contains("started"),
685+
"expected 'started' in body: {body}"
686+
);
687+
688+
// Cleanup
689+
let _ = manager.stop_server().await;
690+
}

0 commit comments

Comments
 (0)