chore: migriere ping-feature Modul auf Module Structure Blueprint, füge Fake-Repository und neue Integrationstests hinzu, dokumentiere Änderungen

This commit is contained in:
2026-04-19 17:46:23 +02:00
parent 691861a706
commit a645bb4dbc
4 changed files with 287 additions and 0 deletions
@@ -0,0 +1,173 @@
package at.mocode.frontend.features.ping.data
import at.mocode.ping.api.EnhancedPingResponse
import at.mocode.ping.api.HealthResponse
import at.mocode.ping.api.PingResponse
import io.ktor.client.*
import io.ktor.client.engine.mock.*
import io.ktor.client.plugins.contentnegotiation.*
import io.ktor.http.*
import io.ktor.serialization.kotlinx.json.*
import kotlinx.coroutines.test.runTest
import kotlinx.serialization.json.Json
import kotlin.test.Test
import kotlin.test.assertEquals
class PingApiKoinClientTest {
// Hilfe zur Erstellung eines testbaren Clients mithilfe der neuen DI-freundlichen Implementierung
private fun createTestClient(mockEngine: MockEngine): PingApiKoinClient {
val client = HttpClient(mockEngine) {
install(ContentNegotiation) {
json(Json {
prettyPrint = true
isLenient = true
ignoreUnknownKeys = true
})
}
}
return PingApiKoinClient(client)
}
@Test
fun `simplePing should return correct response`() = runTest {
// Given
val expectedResponse = PingResponse(
status = "OK",
timestamp = "2025-09-27T21:27:00Z",
service = "ping-service"
)
val mockEngine = MockEngine { request ->
assertEquals("/api/ping/simple", request.url.encodedPath)
assertEquals(HttpMethod.Get, request.method)
respond(
content = Json.encodeToString(PingResponse.serializer(), expectedResponse),
status = HttpStatusCode.OK,
headers = headersOf(HttpHeaders.ContentType, "application/json")
)
}
// When
val apiClient = createTestClient(mockEngine)
val response = apiClient.simplePing()
// Then
assertEquals(expectedResponse, response)
}
@Test
fun `enhancedPing should include simulate parameter`() = runTest {
// Given
val expectedResponse = EnhancedPingResponse(
status = "OK",
timestamp = "2025-09-27T21:27:00Z",
service = "ping-service",
circuitBreakerState = "CLOSED",
responseTime = 42L
)
val mockEngine = MockEngine { request ->
assertEquals("/api/ping/enhanced", request.url.encodedPath)
assertEquals("true", request.url.parameters["simulate"])
assertEquals(HttpMethod.Get, request.method)
respond(
content = Json.encodeToString(EnhancedPingResponse.serializer(), expectedResponse),
status = HttpStatusCode.OK,
headers = headersOf(HttpHeaders.ContentType, "application/json")
)
}
// When
val apiClient = createTestClient(mockEngine)
val response = apiClient.enhancedPing(simulate = true)
// Then
assertEquals(expectedResponse, response)
}
@Test
fun `healthCheck should return health response`() = runTest {
// Given
val expectedResponse = HealthResponse(
status = "UP",
timestamp = "2025-09-27T21:27:00Z",
service = "ping-service",
healthy = true
)
val mockEngine = MockEngine { request ->
assertEquals("/api/ping/health", request.url.encodedPath)
assertEquals(HttpMethod.Get, request.method)
respond(
content = Json.encodeToString(HealthResponse.serializer(), expectedResponse),
status = HttpStatusCode.OK,
headers = headersOf(HttpHeaders.ContentType, "application/json")
)
}
// When
val apiClient = createTestClient(mockEngine)
val response = apiClient.healthCheck()
// Then
assertEquals(expectedResponse, response)
}
@Test
fun `JSON serialization should work correctly`() {
// Given
val pingResponse = PingResponse(
status = "OK",
timestamp = "2025-09-27T21:27:00Z",
service = "test-service"
)
// When
val json = Json.encodeToString(PingResponse.serializer(), pingResponse)
val deserializedResponse = Json.decodeFromString(PingResponse.serializer(), json)
// Then
assertEquals(pingResponse, deserializedResponse)
}
@Test
fun `Enhanced ping response serialization should work correctly`() {
// Given
val enhancedResponse = EnhancedPingResponse(
status = "OK",
timestamp = "2025-09-27T21:27:00Z",
service = "test-service",
circuitBreakerState = "CLOSED",
responseTime = 123L
)
// When
val json = Json.encodeToString(EnhancedPingResponse.serializer(), enhancedResponse)
val deserializedResponse = Json.decodeFromString(EnhancedPingResponse.serializer(), json)
// Then
assertEquals(enhancedResponse, deserializedResponse)
}
@Test
fun `Health response serialization should work correctly`() {
// Given
val healthResponse = HealthResponse(
status = "UP",
timestamp = "2025-09-27T21:27:00Z",
service = "test-service",
healthy = true
)
// When
val json = Json.encodeToString(HealthResponse.serializer(), healthResponse)
val deserializedResponse = Json.decodeFromString(HealthResponse.serializer(), json)
// Then
assertEquals(healthResponse, deserializedResponse)
}
}
@@ -0,0 +1,15 @@
package at.mocode.frontend.features.ping.integration
import at.mocode.frontend.core.sync.SyncableRepository
import at.mocode.ping.api.PingEvent
class FakePingEventRepository : SyncableRepository<PingEvent> {
val storedEvents = mutableListOf<PingEvent>()
var latestSince: String? = null
override suspend fun getLatestSince(): String? = latestSince
override suspend fun upsert(items: List<PingEvent>) {
storedEvents.addAll(items)
}
}
@@ -0,0 +1,70 @@
package at.mocode.frontend.features.ping.integration
import at.mocode.frontend.core.sync.SyncManager
import at.mocode.frontend.features.ping.domain.PingSyncServiceImpl
import at.mocode.ping.api.PingEvent
import io.ktor.client.*
import io.ktor.client.engine.mock.*
import io.ktor.client.plugins.contentnegotiation.*
import io.ktor.http.*
import io.ktor.serialization.kotlinx.json.*
import kotlinx.coroutines.test.runTest
import kotlinx.serialization.json.Json
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertTrue
class PingSyncIntegrationTest {
@Test
fun `syncPings should fetch data from API and store in repository`() = runTest {
// Given
val fakeRepo = FakePingEventRepository()
// Mock API Response
val mockEngine = MockEngine { request ->
assertEquals("/api/ping/sync", request.url.encodedPath)
val responseBody = """
[
{
"id": "event-1",
"message": "Ping 1",
"lastModified": 1000
},
{
"id": "event-2",
"message": "Ping 2",
"lastModified": 2000
}
]
""".trimIndent()
respond(
content = responseBody,
status = HttpStatusCode.OK,
headers = headersOf(HttpHeaders.ContentType, "application/json")
)
}
val httpClient = HttpClient(mockEngine) {
install(ContentNegotiation) {
json(Json {
ignoreUnknownKeys = true
isLenient = true
})
}
}
val syncManager = SyncManager(httpClient)
val syncService = PingSyncServiceImpl(syncManager, fakeRepo)
// When
syncService.syncPings()
// Then
assertEquals(2, fakeRepo.storedEvents.size)
assertTrue(fakeRepo.storedEvents.any { it.id == "event-1" && it.message == "Ping 1" })
assertTrue(fakeRepo.storedEvents.any { it.id == "event-2" && it.message == "Ping 2" })
}
}