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

This commit is contained in:
Stefan Mogeritsch 2026-04-19 17:46:23 +02:00
parent 691861a706
commit a645bb4dbc
4 changed files with 287 additions and 0 deletions

View File

@ -0,0 +1,29 @@
# Journal: Fehlerbehebung PingSyncIntegrationTest nach Blueprint-Migration
**Datum:** 19. April 2026
**Status:** Abgeschlossen
**Agent:** 🧐 [QA Specialist] | 🏗️ [Lead Architect]
## 🎯 Problemstellung
Nach der großflächigen Umbenennung der Pakete und der Migration der Feature-Module auf den neuen Blueprint traten Kompilierfehler im Modul `ping-feature` auf, speziell im `PingSyncIntegrationTest.kt`.
### Fehlermeldungen:
* `Unresolved reference 'FakePingEventRepository'`: Die Mock-Klasse für den Test fehlte.
* `Unresolved reference 'it'`: Typ-Inferenz-Fehler aufgrund der fehlenden Repository-Klasse.
## 🛠️ Durchgeführte Änderungen
### 1. Wiederherstellung der Test-Infrastruktur
* Die Klasse `FakePingEventRepository` wurde im Verzeichnis `frontend/features/ping-feature/src/commonTest/kotlin/at/mocode/frontend/features/ping/integration/` neu erstellt.
* Sie implementiert das `SyncableRepository<PingEvent>` Interface und ermöglicht die Verifizierung der synchronisierten Daten im Integrationstest.
### 2. Korrektur des Integrationstests
* In `PingSyncIntegrationTest.kt` wurden die fehlenden Importe (insbesondere `at.mocode.ping.api.PingEvent`) hinzugefügt.
* Die Lambda-Ausdrücke in den Assertions wurden verifiziert; durch die Anwesenheit der `FakePingEventRepository` Klasse funktioniert die Typ-Inferenz von Kotlin nun wieder korrekt, und die Referenzen auf `it.id` und `it.message` werden aufgelöst.
## ✅ Verifizierung
* `./gradlew :frontend:features:ping-feature:compileTestKotlinJvm`: **ERFOLGREICH**
* `./gradlew :frontend:features:ping-feature:jvmTest`: **ERFOLGREICH** (Alle Tests bestanden)
## 🧹 Fazit
Die Test-Suite für das `ping-feature` ist nun wieder vollständig und blueprint-konform. Die Entkopplung durch das `SyncableRepository` wurde im Test erfolgreich durch das Fake-Repository validiert.

View File

@ -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)
}
}

View File

@ -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)
}
}

View File

@ -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" })
}
}