fixing ping-service
This commit is contained in:
-87
@@ -1,87 +0,0 @@
|
||||
package at.mocode.ping.service
|
||||
|
||||
import at.mocode.ping.api.PingResponse
|
||||
import io.github.resilience4j.circuitbreaker.CircuitBreakerRegistry
|
||||
import org.springframework.http.MediaType
|
||||
import org.springframework.http.ResponseEntity
|
||||
import org.springframework.web.bind.annotation.*
|
||||
import java.time.OffsetDateTime
|
||||
import java.time.format.DateTimeFormatter
|
||||
import java.util.UUID
|
||||
|
||||
@RestController
|
||||
@CrossOrigin(
|
||||
origins = ["http://localhost:8080", "http://localhost:8083", "http://localhost:4000"],
|
||||
methods = [RequestMethod.GET, RequestMethod.POST, RequestMethod.PUT, RequestMethod.DELETE, RequestMethod.OPTIONS],
|
||||
allowedHeaders = ["*"],
|
||||
allowCredentials = "true"
|
||||
)
|
||||
class LegacyPingController(
|
||||
private val pingService: PingServiceCircuitBreaker,
|
||||
private val circuitBreakerRegistry: CircuitBreakerRegistry
|
||||
) {
|
||||
|
||||
@GetMapping("/ping", produces = [MediaType.APPLICATION_JSON_VALUE])
|
||||
fun legacySimplePing(): ResponseEntity<PingResponse> {
|
||||
val now = OffsetDateTime.now().format(DateTimeFormatter.ISO_OFFSET_DATE_TIME)
|
||||
val resp = PingResponse(
|
||||
status = "pong",
|
||||
timestamp = now,
|
||||
service = "ping-service"
|
||||
)
|
||||
return ResponseEntity
|
||||
.ok()
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.body(resp)
|
||||
}
|
||||
|
||||
@GetMapping("/ping/enhanced")
|
||||
fun legacyEnhanced(@RequestParam(required = false, defaultValue = "false") simulate: Boolean): Map<String, Any> {
|
||||
val dto = pingService.ping(simulate)
|
||||
val map = mutableMapOf<String, Any>(
|
||||
"status" to dto.status,
|
||||
"timestamp" to dto.timestamp,
|
||||
"service" to dto.service,
|
||||
"circuitBreaker" to dto.circuitBreakerState
|
||||
)
|
||||
if (dto.status.equals("fallback", ignoreCase = true) || dto.service.contains("fallback")) {
|
||||
map["message"] = "Service temporarily unavailable"
|
||||
map["error"] = UUID.randomUUID().toString()
|
||||
}
|
||||
return map
|
||||
}
|
||||
|
||||
@GetMapping("/ping/health")
|
||||
fun legacyHealth(): Map<String, Any> {
|
||||
val dto = pingService.healthCheck()
|
||||
val state = circuitBreakerRegistry
|
||||
.circuitBreaker(PingServiceCircuitBreaker.PING_CIRCUIT_BREAKER)
|
||||
.state
|
||||
val cb = if (state.name == "OPEN") "OPEN" else "CLOSED"
|
||||
val map = mutableMapOf<String, Any>(
|
||||
"status" to if (dto.healthy) "UP" else "DOWN",
|
||||
"timestamp" to dto.timestamp,
|
||||
"circuitBreaker" to cb
|
||||
)
|
||||
if (!dto.healthy) {
|
||||
map["message"] = "Health check temporarily unavailable"
|
||||
}
|
||||
return map
|
||||
}
|
||||
|
||||
@GetMapping("/ping/test-failure")
|
||||
fun legacyTestFailure(): Map<String, Any> {
|
||||
val dto = pingService.ping(simulateFailure = true)
|
||||
val map = mutableMapOf<String, Any>(
|
||||
"status" to dto.status,
|
||||
"timestamp" to dto.timestamp,
|
||||
"service" to dto.service,
|
||||
"circuitBreaker" to dto.circuitBreakerState
|
||||
)
|
||||
if (dto.status.equals("fallback", ignoreCase = true) || dto.service.contains("fallback")) {
|
||||
map["message"] = "Service temporarily unavailable"
|
||||
map["error"] = UUID.randomUUID().toString()
|
||||
}
|
||||
return map
|
||||
}
|
||||
}
|
||||
@@ -20,7 +20,7 @@ class PingController(
|
||||
) : PingApi {
|
||||
|
||||
// Contract endpoints
|
||||
@GetMapping("/api/ping/simple")
|
||||
@GetMapping("/ping/simple")
|
||||
override suspend fun simplePing(): PingResponse {
|
||||
val now = OffsetDateTime.now().format(DateTimeFormatter.ISO_OFFSET_DATE_TIME)
|
||||
return PingResponse(
|
||||
@@ -30,11 +30,11 @@ class PingController(
|
||||
)
|
||||
}
|
||||
|
||||
@GetMapping("/api/ping/enhanced")
|
||||
@GetMapping("/ping/enhanced")
|
||||
override suspend fun enhancedPing(
|
||||
@RequestParam(required = false, defaultValue = "false") simulate: Boolean
|
||||
): EnhancedPingResponse = pingService.ping(simulate)
|
||||
|
||||
@GetMapping("/api/ping/health")
|
||||
@GetMapping("/ping/health")
|
||||
override suspend fun healthCheck(): HealthResponse = pingService.healthCheck()
|
||||
}
|
||||
|
||||
+13
-39
@@ -46,7 +46,7 @@ class PingControllerIntegrationTest {
|
||||
@Test
|
||||
fun `should return basic ping response from standard endpoint`() {
|
||||
// When
|
||||
val response = restTemplate.getForEntity(getUrl("/ping"), Map::class.java)
|
||||
val response = restTemplate.getForEntity(getUrl("/ping/simple"), Map::class.java)
|
||||
|
||||
// Then
|
||||
assertThat(response.statusCode).isEqualTo(HttpStatus.OK)
|
||||
@@ -71,7 +71,7 @@ class PingControllerIntegrationTest {
|
||||
val body = response.body!!
|
||||
assertThat(body["status"]).isEqualTo("pong")
|
||||
assertThat(body["service"]).isEqualTo("ping-service")
|
||||
assertThat(body["circuitBreaker"]).isEqualTo("CLOSED")
|
||||
assertThat(body["circuitBreakerState"]).isEqualTo("CLOSED")
|
||||
assertThat(body["timestamp"]).isNotNull()
|
||||
|
||||
logger.info("Enhanced ping response: {}", body)
|
||||
@@ -89,7 +89,7 @@ class PingControllerIntegrationTest {
|
||||
val body = response.body!!
|
||||
assertThat(body["status"]).isEqualTo("pong")
|
||||
assertThat(body["service"]).isEqualTo("ping-service")
|
||||
assertThat(body["circuitBreaker"]).isEqualTo("CLOSED")
|
||||
assertThat(body["circuitBreakerState"]).isEqualTo("CLOSED")
|
||||
|
||||
logger.info("Enhanced ping without simulation: {}", body)
|
||||
}
|
||||
@@ -125,8 +125,9 @@ class PingControllerIntegrationTest {
|
||||
assertThat(response.body).isNotNull
|
||||
|
||||
val body = response.body!!
|
||||
assertThat(body["status"]).isEqualTo("UP")
|
||||
assertThat(body["circuitBreaker"]).isEqualTo("CLOSED")
|
||||
assertThat(body["status"]).isEqualTo("pong")
|
||||
assertThat(body["service"]).isEqualTo("ping-service")
|
||||
assertThat(body["healthy"]).isEqualTo(true)
|
||||
assertThat(body["timestamp"]).isNotNull()
|
||||
|
||||
logger.info("Health check response: {}", body)
|
||||
@@ -146,39 +147,13 @@ class PingControllerIntegrationTest {
|
||||
assertThat(response.body).isNotNull
|
||||
|
||||
val body = response.body!!
|
||||
assertThat(body["status"]).isEqualTo("DOWN")
|
||||
assertThat(body["circuitBreaker"]).isEqualTo("OPEN")
|
||||
assertThat(body["message"]).isEqualTo("Health check temporarily unavailable")
|
||||
assertThat(body["status"]).isEqualTo("down")
|
||||
assertThat(body["service"]).isEqualTo("ping-service")
|
||||
assertThat(body["healthy"]).isEqualTo(false)
|
||||
|
||||
logger.info("Fallback health check response: {}", body)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should return response from test-failure endpoint`() {
|
||||
// When
|
||||
val response = restTemplate.getForEntity(getUrl("/ping/test-failure"), Map::class.java)
|
||||
|
||||
// Then
|
||||
assertThat(response.statusCode).isEqualTo(HttpStatus.OK)
|
||||
assertThat(response.body).isNotNull
|
||||
|
||||
val body = response.body!!
|
||||
|
||||
// Due to 60% failure simulation, we expect either success or fallback
|
||||
assertThat(body["status"]).isIn("pong", "fallback")
|
||||
|
||||
if (body["status"] == "fallback") {
|
||||
assertThat(body["service"]).isEqualTo("ping-service-fallback")
|
||||
assertThat(body["circuitBreaker"]).isEqualTo("OPEN")
|
||||
assertThat(body["message"]).isEqualTo("Service temporarily unavailable")
|
||||
assertThat(body["error"]).isNotNull()
|
||||
} else {
|
||||
assertThat(body["service"]).isEqualTo("ping-service")
|
||||
assertThat(body["circuitBreaker"]).isEqualTo("CLOSED")
|
||||
}
|
||||
|
||||
logger.info("Test failure endpoint response: {}", body)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should handle multiple rapid requests correctly`() {
|
||||
@@ -219,7 +194,7 @@ class PingControllerIntegrationTest {
|
||||
|
||||
// All should return fallback responses while circuit breaker is open
|
||||
assertThat(body["status"]).isEqualTo("fallback")
|
||||
assertThat(body["circuitBreaker"]).isEqualTo("OPEN")
|
||||
assertThat(body["circuitBreakerState"]).isEqualTo("OPEN")
|
||||
|
||||
logger.info("Request {} with OPEN circuit breaker: {}", i + 1, body["status"])
|
||||
}
|
||||
@@ -228,12 +203,11 @@ class PingControllerIntegrationTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should test all endpoints return valid responses`() {
|
||||
fun `should test all existing endpoints return valid responses`() {
|
||||
val endpoints = listOf(
|
||||
"/ping",
|
||||
"/ping/simple",
|
||||
"/ping/enhanced",
|
||||
"/ping/health",
|
||||
"/ping/test-failure"
|
||||
"/ping/health"
|
||||
)
|
||||
|
||||
endpoints.forEach { endpoint ->
|
||||
|
||||
+101
-9
@@ -1,7 +1,11 @@
|
||||
package at.mocode.ping.service
|
||||
|
||||
import at.mocode.ping.api.EnhancedPingResponse
|
||||
import at.mocode.ping.api.HealthResponse
|
||||
import io.mockk.every
|
||||
import io.mockk.mockk
|
||||
import io.github.resilience4j.circuitbreaker.CircuitBreakerRegistry
|
||||
import io.mockk.verify
|
||||
import org.junit.jupiter.api.BeforeEach
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.springframework.beans.factory.annotation.Autowired
|
||||
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest
|
||||
@@ -12,27 +16,115 @@ import org.springframework.test.web.servlet.MockMvc
|
||||
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get
|
||||
import org.springframework.test.web.servlet.result.MockMvcResultMatchers.*
|
||||
|
||||
@WebMvcTest(LegacyPingController::class)
|
||||
/**
|
||||
* Unit tests for PingController
|
||||
* Tests REST endpoints with mocked dependencies
|
||||
*/
|
||||
@WebMvcTest(PingController::class)
|
||||
@Import(PingControllerTest.TestConfig::class)
|
||||
class PingControllerTest {
|
||||
|
||||
@Autowired
|
||||
private lateinit var mockMvc: MockMvc
|
||||
|
||||
@Autowired
|
||||
private lateinit var pingService: PingServiceCircuitBreaker
|
||||
|
||||
@TestConfiguration
|
||||
class TestConfig {
|
||||
@Bean
|
||||
fun pingServiceCircuitBreaker(): PingServiceCircuitBreaker = mockk()
|
||||
fun pingServiceCircuitBreaker(): PingServiceCircuitBreaker = mockk(relaxed = true)
|
||||
}
|
||||
|
||||
@Bean
|
||||
fun circuitBreakerRegistry(): CircuitBreakerRegistry = mockk()
|
||||
@BeforeEach
|
||||
fun setUp() {
|
||||
// Reset mocks before each test
|
||||
io.mockk.clearMocks(pingService)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `ping endpoint should return pong status`() {
|
||||
mockMvc.perform(get("/ping"))
|
||||
fun `should return simple ping response`() {
|
||||
// When & Then
|
||||
mockMvc.perform(get("/ping/simple"))
|
||||
.andExpect(status().isOk)
|
||||
.andExpect(content().contentType("application/json"))
|
||||
.andExpect(jsonPath("$.status").value("pong"))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should return enhanced ping response without simulation`() {
|
||||
// Given
|
||||
val expectedResponse = EnhancedPingResponse(
|
||||
status = "pong",
|
||||
timestamp = "2023-10-01T10:00:00Z",
|
||||
service = "ping-service",
|
||||
circuitBreakerState = "CLOSED",
|
||||
responseTime = 10L
|
||||
)
|
||||
every { pingService.ping(false) } returns expectedResponse
|
||||
|
||||
// When & Then
|
||||
mockMvc.perform(get("/ping/enhanced"))
|
||||
.andExpect(status().isOk)
|
||||
|
||||
// Verify
|
||||
verify { pingService.ping(false) }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should return enhanced ping response with simulation enabled`() {
|
||||
// Given
|
||||
val expectedResponse = EnhancedPingResponse(
|
||||
status = "fallback",
|
||||
timestamp = "2023-10-01T10:00:00Z",
|
||||
service = "ping-service-fallback",
|
||||
circuitBreakerState = "OPEN",
|
||||
responseTime = 5L
|
||||
)
|
||||
every { pingService.ping(true) } returns expectedResponse
|
||||
|
||||
// When & Then
|
||||
mockMvc.perform(get("/ping/enhanced?simulate=true"))
|
||||
.andExpect(status().isOk)
|
||||
|
||||
// Verify
|
||||
verify { pingService.ping(true) }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should return health check response`() {
|
||||
// Given
|
||||
val expectedResponse = HealthResponse(
|
||||
status = "pong",
|
||||
timestamp = "2023-10-01T10:00:00Z",
|
||||
service = "ping-service",
|
||||
healthy = true
|
||||
)
|
||||
every { pingService.healthCheck() } returns expectedResponse
|
||||
|
||||
// When & Then
|
||||
mockMvc.perform(get("/ping/health"))
|
||||
.andExpect(status().isOk)
|
||||
|
||||
// Verify
|
||||
verify { pingService.healthCheck() }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should handle missing simulate parameter with default false`() {
|
||||
// Given
|
||||
val expectedResponse = EnhancedPingResponse(
|
||||
status = "pong",
|
||||
timestamp = "2023-10-01T10:00:00Z",
|
||||
service = "ping-service",
|
||||
circuitBreakerState = "CLOSED",
|
||||
responseTime = 8L
|
||||
)
|
||||
every { pingService.ping(false) } returns expectedResponse
|
||||
|
||||
// When & Then
|
||||
mockMvc.perform(get("/ping/enhanced"))
|
||||
.andExpect(status().isOk)
|
||||
|
||||
// Verify default parameter is used
|
||||
verify { pingService.ping(false) }
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user