diff --git a/backend/src/main/java/com/stackup/stackup/system/application/SystemHealthService.java b/backend/src/main/java/com/stackup/stackup/system/application/SystemHealthService.java index b006c3d0..d2ef7a9c 100644 --- a/backend/src/main/java/com/stackup/stackup/system/application/SystemHealthService.java +++ b/backend/src/main/java/com/stackup/stackup/system/application/SystemHealthService.java @@ -15,8 +15,12 @@ @Service public class SystemHealthService { + // 응답 키(name)와 Actuator 컴포넌트 키(actuatorPath)는 다르다. + // Actuator 키는 Spring 이 등록하는 빈 이름에서 접미사를 뗀 값이다 — + // DataSourceHealthContributor→"db", rabbitHealthContributor→"rabbit". + // "rabbitmq" 로 조회하면 항상 null 이라 rabbitmq 컴포넌트가 영구 UNKNOWN 이 된다. private static final ComponentSpec DATABASE = new ComponentSpec("database", "db"); - private static final ComponentSpec RABBITMQ = new ComponentSpec("rabbitmq", "rabbitmq"); + private static final ComponentSpec RABBITMQ = new ComponentSpec("rabbitmq", "rabbit"); private static final ComponentSpec S3 = new ComponentSpec("s3", "s3"); private static final ComponentSpec AI_SERVER = new ComponentSpec("aiServer", "aiServer"); diff --git a/backend/src/test/java/com/stackup/stackup/system/application/SystemHealthServiceTest.java b/backend/src/test/java/com/stackup/stackup/system/application/SystemHealthServiceTest.java index 4d86803d..2a52e7ee 100644 --- a/backend/src/test/java/com/stackup/stackup/system/application/SystemHealthServiceTest.java +++ b/backend/src/test/java/com/stackup/stackup/system/application/SystemHealthServiceTest.java @@ -38,7 +38,7 @@ void live_returnsUpStatus() { void ready_usesDatabaseAndRabbitmqIndicators() { HealthEndpoint healthEndpoint = healthEndpoint(Map.of( "db", indicator(Status.UP, Map.of("connections", 12)), - "rabbitmq", indicator(Status.UP, Map.of("host", "localhost")) + "rabbit", indicator(Status.UP, Map.of("host", "localhost")) // Actuator 실제 키 )); SystemHealthService systemHealthService = new SystemHealthService(healthEndpoint); @@ -54,7 +54,7 @@ void ready_usesDatabaseAndRabbitmqIndicators() { void health_includesDatabaseRabbitmqS3AndAiServer() { HealthEndpoint healthEndpoint = healthEndpoint(Map.of( "db", indicator(Status.UP, Map.of()), - "rabbitmq", indicator(Status.DOWN, Map.of("reachable", false)) + "rabbit", indicator(Status.DOWN, Map.of("reachable", false)) // Actuator 실제 키 )); SystemHealthService systemHealthService = new SystemHealthService(healthEndpoint); @@ -66,6 +66,44 @@ void health_includesDatabaseRabbitmqS3AndAiServer() { assertThat(response.components().get("aiServer").status()).isEqualTo(Status.UNKNOWN.getCode()); } + /** + * 응답 키와 Actuator 컴포넌트 키가 다르다는 사실 자체를 못 박는다. + * + *
운영에서 rabbitmq 가 영구 UNKNOWN 이었던 원인이 여기였다 — Actuator 는
+ * {@code rabbitHealthContributor} 빈을 "rabbit" 으로 등록하는데 "rabbitmq" 로 조회했다.
+ * 기존 테스트는 픽스처를 "rabbitmq" 로 등록해서 같은 실수를 그대로 재현하고 있었다.
+ */
+ @Test
+ void health_readsRabbitFromActuatorKeyNotResponseKey() {
+ HealthEndpoint healthEndpoint = healthEndpoint(Map.of(
+ "db", indicator(Status.UP, Map.of()),
+ "rabbit", indicator(Status.UP, Map.of("version", "3.13"))
+ ));
+ SystemHealthService systemHealthService = new SystemHealthService(healthEndpoint);
+
+ var response = systemHealthService.ready();
+
+ // 응답 키는 그대로 rabbitmq — 공개 계약은 바뀌지 않는다.
+ assertThat(response.components().get("rabbitmq").status()).isEqualTo(Status.UP.getCode());
+ assertThat(response.components().get("rabbitmq").details()).containsEntry("version", "3.13");
+ assertThat(response.status()).isEqualTo(Status.UP.getCode());
+ }
+
+ // Actuator 에 없는 이름으로 조회하면 UNKNOWN 이 된다 — 회귀 시 이 테스트가 아니라
+ // 위 테스트가 깨지도록, 여기서는 '없을 때의 동작'만 확인한다.
+ @Test
+ void health_reportsUnknownWhenActuatorHasNoSuchComponent() {
+ HealthEndpoint healthEndpoint = healthEndpoint(Map.of(
+ "db", indicator(Status.UP, Map.of())
+ ));
+ SystemHealthService systemHealthService = new SystemHealthService(healthEndpoint);
+
+ var response = systemHealthService.ready();
+
+ assertThat(response.components().get("rabbitmq").status()).isEqualTo(Status.UNKNOWN.getCode());
+ assertThat(response.status()).isEqualTo(Status.UNKNOWN.getCode());
+ }
+
private static HealthEndpoint healthEndpoint(Map