Merge pull request #18
* MP-19 Refactoring: Einführung der "Registry" & "Masterdata" Trennung … * MP-19 Refactoring: Frontend Tabula Rasa * MP-19 Refactoring: Frontend Tabula Rasa * refactoring: * MP-20 fix(docker/clients): include `:domains` module in web/desktop b… * MP-20 fix(web-app build): resolve JS compile error and add dev/prod b… * MP-20 fix(web-app): remove vendor.js reference and harden JS bootstra… * MP-20 fixing: clients * MP-20 fixing: clients
This commit is contained in:
@@ -6,111 +6,115 @@ import org.jetbrains.kotlin.gradle.dsl.JvmTarget
|
||||
* und den UI-Baukasten (common-ui), aber es kennt keine anderen Features.
|
||||
*/
|
||||
plugins {
|
||||
alias(libs.plugins.kotlinMultiplatform)
|
||||
alias(libs.plugins.composeMultiplatform)
|
||||
alias(libs.plugins.composeCompiler)
|
||||
alias(libs.plugins.kotlinSerialization)
|
||||
alias(libs.plugins.kotlinMultiplatform)
|
||||
alias(libs.plugins.composeMultiplatform)
|
||||
alias(libs.plugins.composeCompiler)
|
||||
alias(libs.plugins.kotlinSerialization)
|
||||
}
|
||||
|
||||
group = "at.mocode.clients"
|
||||
version = "1.0.0"
|
||||
|
||||
kotlin {
|
||||
val enableWasm = providers.gradleProperty("enableWasm").orNull == "true"
|
||||
val enableWasm = providers.gradleProperty("enableWasm").orNull == "true"
|
||||
|
||||
jvmToolchain(21)
|
||||
jvmToolchain(21)
|
||||
|
||||
jvm()
|
||||
jvm()
|
||||
|
||||
js {
|
||||
browser {
|
||||
testTask {
|
||||
enabled = false
|
||||
}
|
||||
}
|
||||
binaries.executable()
|
||||
}
|
||||
|
||||
// WASM, nur wenn explizit aktiviert
|
||||
if (enableWasm) {
|
||||
@OptIn(org.jetbrains.kotlin.gradle.ExperimentalWasmDsl::class)
|
||||
wasmJs {
|
||||
browser()
|
||||
binaries.executable()
|
||||
}
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
commonMain.dependencies {
|
||||
// Contract from backend
|
||||
implementation(projects.services.ping.pingApi)
|
||||
|
||||
// UI Kit
|
||||
implementation(project(":clients:shared:common-ui"))
|
||||
|
||||
// Shared Konfig & Utilities
|
||||
implementation(project(":clients:shared"))
|
||||
|
||||
// Compose dependencies
|
||||
implementation(compose.runtime)
|
||||
implementation(compose.foundation)
|
||||
implementation(compose.material3)
|
||||
implementation(compose.ui)
|
||||
implementation(compose.components.resources)
|
||||
implementation(compose.materialIconsExtended)
|
||||
|
||||
// Ktor client for HTTP calls
|
||||
implementation(libs.bundles.ktor.client.common)
|
||||
|
||||
// Coroutines and serialization
|
||||
implementation(libs.bundles.kotlinx.core)
|
||||
|
||||
// ViewModel lifecycle
|
||||
implementation(libs.bundles.compose.common)
|
||||
|
||||
js {
|
||||
browser {
|
||||
testTask {
|
||||
enabled = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// WASM, nur wenn explizit aktiviert
|
||||
commonTest.dependencies {
|
||||
implementation(libs.kotlin.test)
|
||||
implementation(libs.kotlinx.coroutines.test)
|
||||
implementation(libs.ktor.client.mock)
|
||||
|
||||
}
|
||||
|
||||
jvmTest.dependencies {
|
||||
implementation(libs.mockk)
|
||||
implementation(projects.platform.platformTesting)
|
||||
implementation(libs.bundles.testing.jvm)
|
||||
}
|
||||
|
||||
jvmMain.dependencies {
|
||||
implementation(libs.ktor.client.cio)
|
||||
// Auth-Models Zugriff (nur für JVM)
|
||||
//implementation(project(":infrastructure:auth:auth-client"))
|
||||
}
|
||||
|
||||
jsMain.dependencies {
|
||||
implementation(libs.ktor.client.js)
|
||||
|
||||
}
|
||||
|
||||
// WASM SourceSet, nur wenn aktiviert
|
||||
if (enableWasm) {
|
||||
@OptIn(org.jetbrains.kotlin.gradle.ExperimentalWasmDsl::class)
|
||||
wasmJs { browser() }
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
commonMain.dependencies {
|
||||
// Contract from backend
|
||||
implementation(projects.services.ping.pingApi)
|
||||
|
||||
// UI Kit
|
||||
implementation(project(":clients:shared:common-ui"))
|
||||
|
||||
// Shared Konfig & Utilities
|
||||
implementation(project(":clients:shared"))
|
||||
|
||||
// Compose dependencies
|
||||
implementation(compose.runtime)
|
||||
implementation(compose.foundation)
|
||||
implementation(compose.material3)
|
||||
implementation(compose.ui)
|
||||
implementation(compose.components.resources)
|
||||
implementation(compose.materialIconsExtended)
|
||||
|
||||
// Ktor client for HTTP calls
|
||||
implementation(libs.bundles.ktor.client.common)
|
||||
|
||||
// Coroutines and serialization
|
||||
implementation(libs.bundles.kotlinx.core)
|
||||
|
||||
// ViewModel lifecycle
|
||||
implementation(libs.bundles.compose.common)
|
||||
|
||||
}
|
||||
|
||||
commonTest.dependencies {
|
||||
implementation(libs.kotlin.test)
|
||||
implementation(libs.kotlinx.coroutines.test)
|
||||
implementation(libs.ktor.client.mock)
|
||||
|
||||
}
|
||||
|
||||
jvmTest.dependencies {
|
||||
implementation(libs.mockk)
|
||||
implementation(projects.platform.platformTesting)
|
||||
implementation(libs.bundles.testing.jvm)
|
||||
}
|
||||
|
||||
jvmMain.dependencies {
|
||||
implementation(libs.ktor.client.cio)
|
||||
// Auth-Models Zugriff (nur für JVM)
|
||||
//implementation(project(":infrastructure:auth:auth-client"))
|
||||
}
|
||||
|
||||
jsMain.dependencies {
|
||||
implementation(libs.ktor.client.js)
|
||||
|
||||
}
|
||||
|
||||
// WASM SourceSet, nur wenn aktiviert
|
||||
if (enableWasm) {
|
||||
val wasmJsMain = getByName("wasmJsMain")
|
||||
wasmJsMain.dependencies {
|
||||
implementation(libs.ktor.client.js) // WASM verwendet JS-Client [cite: 7]
|
||||
|
||||
// ✅ HINZUFÜGEN: Compose für shared UI components für WASM
|
||||
implementation(compose.runtime)
|
||||
implementation(compose.foundation)
|
||||
implementation(compose.material3)
|
||||
}
|
||||
}
|
||||
val wasmJsMain = getByName("wasmJsMain")
|
||||
wasmJsMain.dependencies {
|
||||
implementation(libs.ktor.client.js) // WASM verwendet JS-Client [cite: 7]
|
||||
|
||||
// ✅ HINZUFÜGEN: Compose für shared UI components für WASM
|
||||
implementation(compose.runtime)
|
||||
implementation(compose.foundation)
|
||||
implementation(compose.material3)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// KMP Compile-Optionen
|
||||
tasks.withType<org.jetbrains.kotlin.gradle.tasks.KotlinCompile> {
|
||||
compilerOptions {
|
||||
jvmTarget.set(JvmTarget.JVM_21)
|
||||
freeCompilerArgs.addAll(
|
||||
"-opt-in=kotlin.RequiresOptIn"
|
||||
)
|
||||
}
|
||||
compilerOptions {
|
||||
jvmTarget.set(JvmTarget.JVM_21)
|
||||
freeCompilerArgs.addAll(
|
||||
"-opt-in=kotlin.RequiresOptIn"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
+21
-21
@@ -4,7 +4,7 @@ import at.mocode.ping.api.PingApi
|
||||
import at.mocode.ping.api.PingResponse
|
||||
import at.mocode.ping.api.EnhancedPingResponse
|
||||
import at.mocode.ping.api.HealthResponse
|
||||
import at.mocode.clients.shared.AppConfig
|
||||
import at.mocode.clients.shared.core.AppConstants
|
||||
import io.ktor.client.*
|
||||
import io.ktor.client.call.*
|
||||
import io.ktor.client.plugins.contentnegotiation.*
|
||||
@@ -13,30 +13,30 @@ import io.ktor.serialization.kotlinx.json.*
|
||||
import kotlinx.serialization.json.Json
|
||||
|
||||
class PingApiClient(
|
||||
private val baseUrl: String = AppConfig.GATEWAY_URL
|
||||
private val baseUrl: String = AppConstants.GATEWAY_URL
|
||||
) : PingApi {
|
||||
|
||||
private val client = HttpClient {
|
||||
install(ContentNegotiation) {
|
||||
json(Json {
|
||||
prettyPrint = true
|
||||
isLenient = true
|
||||
ignoreUnknownKeys = true
|
||||
})
|
||||
}
|
||||
private val client = HttpClient {
|
||||
install(ContentNegotiation) {
|
||||
json(Json {
|
||||
prettyPrint = true
|
||||
isLenient = true
|
||||
ignoreUnknownKeys = true
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun simplePing(): PingResponse {
|
||||
return client.get("$baseUrl/api/ping/simple").body()
|
||||
}
|
||||
override suspend fun simplePing(): PingResponse {
|
||||
return client.get("$baseUrl/api/ping/simple").body()
|
||||
}
|
||||
|
||||
override suspend fun enhancedPing(simulate: Boolean): EnhancedPingResponse {
|
||||
return client.get("$baseUrl/api/ping/enhanced") {
|
||||
parameter("simulate", simulate)
|
||||
}.body()
|
||||
}
|
||||
override suspend fun enhancedPing(simulate: Boolean): EnhancedPingResponse {
|
||||
return client.get("$baseUrl/api/ping/enhanced") {
|
||||
parameter("simulate", simulate)
|
||||
}.body()
|
||||
}
|
||||
|
||||
override suspend fun healthCheck(): HealthResponse {
|
||||
return client.get("$baseUrl/api/ping/health").body()
|
||||
}
|
||||
override suspend fun healthCheck(): HealthResponse {
|
||||
return client.get("$baseUrl/api/ping/health").body()
|
||||
}
|
||||
}
|
||||
|
||||
+249
-249
@@ -21,288 +21,288 @@ import at.mocode.clients.pingfeature.model.RoleCategory
|
||||
|
||||
@Composable
|
||||
fun PingScreen(viewModel: PingViewModel) {
|
||||
val uiState = viewModel.uiState
|
||||
val uiState = viewModel.uiState
|
||||
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.padding(16.dp)
|
||||
.verticalScroll(rememberScrollState()),
|
||||
verticalArrangement = Arrangement.spacedBy(16.dp)
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.padding(16.dp)
|
||||
.verticalScroll(rememberScrollState()),
|
||||
verticalArrangement = Arrangement.spacedBy(16.dp)
|
||||
) {
|
||||
Text(
|
||||
text = "Ping Service",
|
||||
style = MaterialTheme.typography.headlineMedium,
|
||||
fontWeight = FontWeight.Bold
|
||||
)
|
||||
|
||||
// Action Buttons
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
horizontalArrangement = Arrangement.spacedBy(8.dp)
|
||||
) {
|
||||
Text(
|
||||
text = "Ping Service",
|
||||
style = MaterialTheme.typography.headlineMedium,
|
||||
fontWeight = FontWeight.Bold
|
||||
)
|
||||
Button(
|
||||
onClick = { viewModel.performSimplePing() },
|
||||
enabled = !uiState.isLoading,
|
||||
modifier = Modifier.weight(1f)
|
||||
) {
|
||||
Text("Simple Ping")
|
||||
}
|
||||
|
||||
// Action Buttons
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
horizontalArrangement = Arrangement.spacedBy(8.dp)
|
||||
) {
|
||||
Button(
|
||||
onClick = { viewModel.performSimplePing() },
|
||||
enabled = !uiState.isLoading,
|
||||
modifier = Modifier.weight(1f)
|
||||
) {
|
||||
Text("Simple Ping")
|
||||
}
|
||||
Button(
|
||||
onClick = { viewModel.performEnhancedPing() },
|
||||
enabled = !uiState.isLoading,
|
||||
modifier = Modifier.weight(1f)
|
||||
) {
|
||||
Text("Enhanced Ping")
|
||||
}
|
||||
|
||||
Button(
|
||||
onClick = { viewModel.performEnhancedPing() },
|
||||
enabled = !uiState.isLoading,
|
||||
modifier = Modifier.weight(1f)
|
||||
) {
|
||||
Text("Enhanced Ping")
|
||||
}
|
||||
|
||||
Button(
|
||||
onClick = { viewModel.performHealthCheck() },
|
||||
enabled = !uiState.isLoading,
|
||||
modifier = Modifier.weight(1f)
|
||||
) {
|
||||
Text("Health Check")
|
||||
}
|
||||
}
|
||||
|
||||
// Loading indicator
|
||||
if (uiState.isLoading) {
|
||||
Box(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
CircularProgressIndicator()
|
||||
}
|
||||
}
|
||||
|
||||
// Error message
|
||||
uiState.errorMessage?.let { error ->
|
||||
Card(
|
||||
colors = CardDefaults.cardColors(
|
||||
containerColor = MaterialTheme.colorScheme.errorContainer
|
||||
)
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier.padding(16.dp)
|
||||
) {
|
||||
Text(
|
||||
text = "Error",
|
||||
style = MaterialTheme.typography.titleMedium,
|
||||
color = MaterialTheme.colorScheme.onErrorContainer,
|
||||
fontWeight = FontWeight.Bold
|
||||
)
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
Text(
|
||||
text = error,
|
||||
color = MaterialTheme.colorScheme.onErrorContainer
|
||||
)
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
Button(
|
||||
onClick = { viewModel.clearError() }
|
||||
) {
|
||||
Text("Dismiss")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Simple Ping Response
|
||||
uiState.simplePingResponse?.let { response ->
|
||||
ResponseCard(
|
||||
title = "Simple Ping Response",
|
||||
status = response.status,
|
||||
timestamp = response.timestamp,
|
||||
service = response.service
|
||||
)
|
||||
}
|
||||
|
||||
// Enhanced Ping Response
|
||||
uiState.enhancedPingResponse?.let { response ->
|
||||
ResponseCard(
|
||||
title = "Enhanced Ping Response",
|
||||
status = response.status,
|
||||
timestamp = response.timestamp,
|
||||
service = response.service,
|
||||
additionalInfo = mapOf(
|
||||
"Circuit Breaker State" to response.circuitBreakerState,
|
||||
"Response Time" to "${response.responseTime}ms"
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
// Health Response
|
||||
uiState.healthResponse?.let { response ->
|
||||
ResponseCard(
|
||||
title = "Health Check Response",
|
||||
status = response.status,
|
||||
timestamp = response.timestamp,
|
||||
service = response.service,
|
||||
additionalInfo = mapOf(
|
||||
"Healthy" to response.healthy.toString()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
// Neue Reitsport-Authentication-Sektion
|
||||
Spacer(modifier = Modifier.height(24.dp))
|
||||
|
||||
ReitsportTestingSection(
|
||||
viewModel = viewModel,
|
||||
uiState = uiState
|
||||
)
|
||||
Button(
|
||||
onClick = { viewModel.performHealthCheck() },
|
||||
enabled = !uiState.isLoading,
|
||||
modifier = Modifier.weight(1f)
|
||||
) {
|
||||
Text("Health Check")
|
||||
}
|
||||
}
|
||||
|
||||
// Loading indicator
|
||||
if (uiState.isLoading) {
|
||||
Box(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
CircularProgressIndicator()
|
||||
}
|
||||
}
|
||||
|
||||
// Error message
|
||||
uiState.errorMessage?.let { error ->
|
||||
Card(
|
||||
colors = CardDefaults.cardColors(
|
||||
containerColor = MaterialTheme.colorScheme.errorContainer
|
||||
)
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier.padding(16.dp)
|
||||
) {
|
||||
Text(
|
||||
text = "Error",
|
||||
style = MaterialTheme.typography.titleMedium,
|
||||
color = MaterialTheme.colorScheme.onErrorContainer,
|
||||
fontWeight = FontWeight.Bold
|
||||
)
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
Text(
|
||||
text = error,
|
||||
color = MaterialTheme.colorScheme.onErrorContainer
|
||||
)
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
Button(
|
||||
onClick = { viewModel.clearError() }
|
||||
) {
|
||||
Text("Dismiss")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Simple Ping Response
|
||||
uiState.simplePingResponse?.let { response ->
|
||||
ResponseCard(
|
||||
title = "Simple Ping Response",
|
||||
status = response.status,
|
||||
timestamp = response.timestamp,
|
||||
service = response.service
|
||||
)
|
||||
}
|
||||
|
||||
// Enhanced Ping Response
|
||||
uiState.enhancedPingResponse?.let { response ->
|
||||
ResponseCard(
|
||||
title = "Enhanced Ping Response",
|
||||
status = response.status,
|
||||
timestamp = response.timestamp,
|
||||
service = response.service,
|
||||
additionalInfo = mapOf(
|
||||
"Circuit Breaker State" to response.circuitBreakerState,
|
||||
"Response Time" to "${response.responseTime}ms"
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
// Health Response
|
||||
uiState.healthResponse?.let { response ->
|
||||
ResponseCard(
|
||||
title = "Health Check Response",
|
||||
status = response.status,
|
||||
timestamp = response.timestamp,
|
||||
service = response.service,
|
||||
additionalInfo = mapOf(
|
||||
"Healthy" to response.healthy.toString()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
// Neue Reitsport-Authentication-Sektion
|
||||
Spacer(modifier = Modifier.height(24.dp))
|
||||
|
||||
ReitsportTestingSection(
|
||||
viewModel = viewModel,
|
||||
uiState = uiState
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ResponseCard(
|
||||
title: String,
|
||||
status: String,
|
||||
timestamp: String,
|
||||
service: String,
|
||||
additionalInfo: Map<String, String> = emptyMap()
|
||||
title: String,
|
||||
status: String,
|
||||
timestamp: String,
|
||||
service: String,
|
||||
additionalInfo: Map<String, String> = emptyMap()
|
||||
) {
|
||||
Card(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
colors = CardDefaults.cardColors(
|
||||
containerColor = MaterialTheme.colorScheme.surfaceVariant
|
||||
)
|
||||
Card(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
colors = CardDefaults.cardColors(
|
||||
containerColor = MaterialTheme.colorScheme.surfaceVariant
|
||||
)
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier.padding(16.dp),
|
||||
verticalArrangement = Arrangement.spacedBy(8.dp)
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier.padding(16.dp),
|
||||
verticalArrangement = Arrangement.spacedBy(8.dp)
|
||||
) {
|
||||
Text(
|
||||
text = title,
|
||||
style = MaterialTheme.typography.titleMedium,
|
||||
fontWeight = FontWeight.Bold
|
||||
)
|
||||
Text(
|
||||
text = title,
|
||||
style = MaterialTheme.typography.titleMedium,
|
||||
fontWeight = FontWeight.Bold
|
||||
)
|
||||
|
||||
InfoRow("Status", status)
|
||||
InfoRow("Timestamp", timestamp)
|
||||
InfoRow("Service", service)
|
||||
InfoRow("Status", status)
|
||||
InfoRow("Timestamp", timestamp)
|
||||
InfoRow("Service", service)
|
||||
|
||||
additionalInfo.forEach { (key, value) ->
|
||||
InfoRow(key, value)
|
||||
}
|
||||
}
|
||||
additionalInfo.forEach { (key, value) ->
|
||||
InfoRow(key, value)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun InfoRow(label: String, value: String) {
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
horizontalArrangement = Arrangement.SpaceBetween
|
||||
) {
|
||||
Text(
|
||||
text = "$label:",
|
||||
fontWeight = FontWeight.Medium
|
||||
)
|
||||
Text(text = value)
|
||||
}
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
horizontalArrangement = Arrangement.SpaceBetween
|
||||
) {
|
||||
Text(
|
||||
text = "$label:",
|
||||
fontWeight = FontWeight.Medium
|
||||
)
|
||||
Text(text = value)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ReitsportTestingSection(
|
||||
viewModel: PingViewModel,
|
||||
uiState: PingUiState
|
||||
viewModel: PingViewModel,
|
||||
uiState: PingUiState
|
||||
) {
|
||||
Card(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
colors = CardDefaults.cardColors(
|
||||
containerColor = MaterialTheme.colorScheme.secondaryContainer
|
||||
)
|
||||
Card(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
colors = CardDefaults.cardColors(
|
||||
containerColor = MaterialTheme.colorScheme.secondaryContainer
|
||||
)
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier.padding(16.dp),
|
||||
verticalArrangement = Arrangement.spacedBy(12.dp)
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier.padding(16.dp),
|
||||
verticalArrangement = Arrangement.spacedBy(12.dp)
|
||||
) {
|
||||
// Header
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Text(
|
||||
text = "🐎",
|
||||
style = MaterialTheme.typography.headlineMedium
|
||||
)
|
||||
Spacer(modifier = Modifier.width(8.dp))
|
||||
Text(
|
||||
text = "Reitsport-Authentication-Testing",
|
||||
style = MaterialTheme.typography.headlineSmall,
|
||||
fontWeight = FontWeight.Bold
|
||||
)
|
||||
}
|
||||
// Header
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Text(
|
||||
text = "🐎",
|
||||
style = MaterialTheme.typography.headlineMedium
|
||||
)
|
||||
Spacer(modifier = Modifier.width(8.dp))
|
||||
Text(
|
||||
text = "Reitsport-Authentication-Testing",
|
||||
style = MaterialTheme.typography.headlineSmall,
|
||||
fontWeight = FontWeight.Bold
|
||||
)
|
||||
}
|
||||
|
||||
Text(
|
||||
text = "Teste verschiedene Benutzerrollen und ihre Berechtigungen im Meldestelle_Pro System",
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
color = MaterialTheme.colorScheme.onSecondaryContainer.copy(alpha = 0.8f)
|
||||
)
|
||||
Text(
|
||||
text = "Teste verschiedene Benutzerrollen und ihre Berechtigungen im Meldestelle_Pro System",
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
color = MaterialTheme.colorScheme.onSecondaryContainer.copy(alpha = 0.8f)
|
||||
)
|
||||
|
||||
// Rollen-Grid
|
||||
LazyVerticalGrid(
|
||||
columns = GridCells.Adaptive(minSize = 120.dp),
|
||||
verticalArrangement = Arrangement.spacedBy(8.dp),
|
||||
horizontalArrangement = Arrangement.spacedBy(8.dp),
|
||||
modifier = Modifier.height(200.dp) // Feste Höhe für 2 Reihen
|
||||
) {
|
||||
items(ReitsportRoles.ALL_ROLES) { role ->
|
||||
RoleTestButton(
|
||||
role = role,
|
||||
onClick = { viewModel.testReitsportRole(role) },
|
||||
isLoading = uiState.isLoading
|
||||
)
|
||||
}
|
||||
}
|
||||
// Rollen-Grid
|
||||
LazyVerticalGrid(
|
||||
columns = GridCells.Adaptive(minSize = 120.dp),
|
||||
verticalArrangement = Arrangement.spacedBy(8.dp),
|
||||
horizontalArrangement = Arrangement.spacedBy(8.dp),
|
||||
modifier = Modifier.height(200.dp) // Feste Höhe für 2 Reihen
|
||||
) {
|
||||
items(ReitsportRoles.ALL_ROLES) { role ->
|
||||
RoleTestButton(
|
||||
role = role,
|
||||
onClick = { viewModel.testReitsportRole(role) },
|
||||
isLoading = uiState.isLoading
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun RoleTestButton(
|
||||
role: ReitsportRole,
|
||||
onClick: () -> Unit,
|
||||
isLoading: Boolean
|
||||
role: ReitsportRole,
|
||||
onClick: () -> Unit,
|
||||
isLoading: Boolean
|
||||
) {
|
||||
OutlinedButton(
|
||||
onClick = onClick,
|
||||
enabled = !isLoading,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.height(80.dp),
|
||||
colors = ButtonDefaults.outlinedButtonColors(
|
||||
containerColor = Color.Transparent,
|
||||
contentColor = when (role.category) {
|
||||
RoleCategory.SYSTEM -> Color(0xFFFF5722)
|
||||
RoleCategory.OFFICIAL -> Color(0xFF3F51B5)
|
||||
RoleCategory.ACTIVE -> Color(0xFF4CAF50)
|
||||
RoleCategory.PASSIVE -> Color(0xFF9E9E9E)
|
||||
}
|
||||
)
|
||||
OutlinedButton(
|
||||
onClick = onClick,
|
||||
enabled = !isLoading,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.height(80.dp),
|
||||
colors = ButtonDefaults.outlinedButtonColors(
|
||||
containerColor = Color.Transparent,
|
||||
contentColor = when (role.category) {
|
||||
RoleCategory.SYSTEM -> Color(0xFFFF5722)
|
||||
RoleCategory.OFFICIAL -> Color(0xFF3F51B5)
|
||||
RoleCategory.ACTIVE -> Color(0xFF4CAF50)
|
||||
RoleCategory.PASSIVE -> Color(0xFF9E9E9E)
|
||||
}
|
||||
)
|
||||
) {
|
||||
Column(
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
verticalArrangement = Arrangement.Center
|
||||
) {
|
||||
Column(
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
verticalArrangement = Arrangement.Center
|
||||
) {
|
||||
Text(
|
||||
text = role.icon,
|
||||
fontSize = 20.sp
|
||||
)
|
||||
Text(
|
||||
text = role.displayName.split(" ").first(), // Erstes Wort nur
|
||||
fontSize = 10.sp,
|
||||
fontWeight = FontWeight.Medium,
|
||||
textAlign = TextAlign.Center,
|
||||
maxLines = 1
|
||||
)
|
||||
Text(
|
||||
text = "${role.permissions.size} Rechte",
|
||||
fontSize = 8.sp,
|
||||
color = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.6f),
|
||||
textAlign = TextAlign.Center
|
||||
)
|
||||
}
|
||||
Text(
|
||||
text = role.icon,
|
||||
fontSize = 20.sp
|
||||
)
|
||||
Text(
|
||||
text = role.displayName.split(" ").first(), // Erstes Wort nur
|
||||
fontSize = 10.sp,
|
||||
fontWeight = FontWeight.Medium,
|
||||
textAlign = TextAlign.Center,
|
||||
maxLines = 1
|
||||
)
|
||||
Text(
|
||||
text = "${role.permissions.size} Rechte",
|
||||
fontSize = 8.sp,
|
||||
color = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.6f),
|
||||
textAlign = TextAlign.Center
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+124
-124
@@ -15,136 +15,136 @@ import at.mocode.ping.api.PingResponse
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
data class PingUiState(
|
||||
val isLoading: Boolean = false,
|
||||
val simplePingResponse: PingResponse? = null,
|
||||
val enhancedPingResponse: EnhancedPingResponse? = null,
|
||||
val healthResponse: HealthResponse? = null,
|
||||
val errorMessage: String? = null
|
||||
val isLoading: Boolean = false,
|
||||
val simplePingResponse: PingResponse? = null,
|
||||
val enhancedPingResponse: EnhancedPingResponse? = null,
|
||||
val healthResponse: HealthResponse? = null,
|
||||
val errorMessage: String? = null
|
||||
)
|
||||
|
||||
class PingViewModel(
|
||||
private val apiClient: PingApi = PingApiClient()
|
||||
private val apiClient: PingApi = PingApiClient()
|
||||
) : ViewModel() {
|
||||
|
||||
var uiState by mutableStateOf(PingUiState())
|
||||
private set
|
||||
var uiState by mutableStateOf(PingUiState())
|
||||
private set
|
||||
|
||||
fun performSimplePing() {
|
||||
viewModelScope.launch {
|
||||
uiState = uiState.copy(isLoading = true, errorMessage = null)
|
||||
try {
|
||||
val response = apiClient.simplePing()
|
||||
uiState = uiState.copy(
|
||||
isLoading = false,
|
||||
simplePingResponse = response
|
||||
)
|
||||
} catch (e: Exception) {
|
||||
uiState = uiState.copy(
|
||||
isLoading = false,
|
||||
errorMessage = "Simple ping failed: ${e.message}"
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun performEnhancedPing(simulate: Boolean = false) {
|
||||
viewModelScope.launch {
|
||||
uiState = uiState.copy(isLoading = true, errorMessage = null)
|
||||
try {
|
||||
val response = apiClient.enhancedPing(simulate)
|
||||
uiState = uiState.copy(
|
||||
isLoading = false,
|
||||
enhancedPingResponse = response
|
||||
)
|
||||
} catch (e: Exception) {
|
||||
uiState = uiState.copy(
|
||||
isLoading = false,
|
||||
errorMessage = "Enhanced ping failed: ${e.message}"
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun performHealthCheck() {
|
||||
viewModelScope.launch {
|
||||
uiState = uiState.copy(isLoading = true, errorMessage = null)
|
||||
try {
|
||||
val response = apiClient.healthCheck()
|
||||
uiState = uiState.copy(
|
||||
isLoading = false,
|
||||
healthResponse = response
|
||||
)
|
||||
} catch (e: Exception) {
|
||||
uiState = uiState.copy(
|
||||
isLoading = false,
|
||||
errorMessage = "Health check failed: ${e.message}"
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun clearError() {
|
||||
uiState = uiState.copy(errorMessage = null)
|
||||
}
|
||||
|
||||
/**
|
||||
* Erweiterte Methode: Echte API-Tests für Reitsport-Rollen
|
||||
*/
|
||||
fun testReitsportRole(role: ReitsportRole) {
|
||||
viewModelScope.launch {
|
||||
uiState = uiState.copy(
|
||||
isLoading = true,
|
||||
errorMessage = null
|
||||
)
|
||||
|
||||
try {
|
||||
// Echte API-Tests durchführen
|
||||
val apiClient = ReitsportTestApi()
|
||||
val testResults = apiClient.testRole(role)
|
||||
|
||||
// Erfolgs-Statistiken berechnen
|
||||
val successful = testResults.count { it.success }
|
||||
val total = testResults.size
|
||||
val successRate = if (total > 0) (successful * 100 / total) else 0
|
||||
|
||||
// Test-Summary erstellen
|
||||
val summary = buildString {
|
||||
appendLine("🎯 ${role.displayName} - Test Abgeschlossen")
|
||||
appendLine("📊 Erfolgsrate: $successful/$total Tests ($successRate%)")
|
||||
appendLine("⏱️ Durchschnittsdauer: ${testResults.map { it.duration }.average().toInt()}ms")
|
||||
appendLine("🔑 Berechtigungen: ${role.permissions.size}")
|
||||
appendLine("")
|
||||
appendLine("📋 Test-Ergebnisse:")
|
||||
|
||||
testResults.forEach { result ->
|
||||
val icon = if (result.success) "✅" else "❌"
|
||||
val status = if (result.responseCode != null) " (${result.responseCode})" else ""
|
||||
appendLine("$icon ${result.scenarioName}$status - ${result.duration}ms")
|
||||
}
|
||||
}
|
||||
|
||||
// Mock-Response für Anzeige
|
||||
val mockResponse = PingResponse(
|
||||
status = summary,
|
||||
timestamp = DateTimeHelper.formatDateTime(DateTimeHelper.now()),
|
||||
service = "Reitsport-Auth-Test"
|
||||
)
|
||||
|
||||
uiState = uiState.copy(
|
||||
isLoading = false,
|
||||
simplePingResponse = mockResponse
|
||||
)
|
||||
|
||||
println("[DEBUG] Reitsport-API-Test: ${role.displayName}")
|
||||
println("[DEBUG] Ergebnisse: $successful/$total erfolgreich")
|
||||
|
||||
} catch (e: Exception) {
|
||||
uiState = uiState.copy(
|
||||
isLoading = false,
|
||||
errorMessage = "Reitsport-API-Test fehlgeschlagen: ${e.message}"
|
||||
)
|
||||
println("[ERROR] Reitsport-Test-Fehler: ${e.message}")
|
||||
}
|
||||
fun performSimplePing() {
|
||||
viewModelScope.launch {
|
||||
uiState = uiState.copy(isLoading = true, errorMessage = null)
|
||||
try {
|
||||
val response = apiClient.simplePing()
|
||||
uiState = uiState.copy(
|
||||
isLoading = false,
|
||||
simplePingResponse = response
|
||||
)
|
||||
} catch (e: Exception) {
|
||||
uiState = uiState.copy(
|
||||
isLoading = false,
|
||||
errorMessage = "Simple ping failed: ${e.message}"
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun performEnhancedPing(simulate: Boolean = false) {
|
||||
viewModelScope.launch {
|
||||
uiState = uiState.copy(isLoading = true, errorMessage = null)
|
||||
try {
|
||||
val response = apiClient.enhancedPing(simulate)
|
||||
uiState = uiState.copy(
|
||||
isLoading = false,
|
||||
enhancedPingResponse = response
|
||||
)
|
||||
} catch (e: Exception) {
|
||||
uiState = uiState.copy(
|
||||
isLoading = false,
|
||||
errorMessage = "Enhanced ping failed: ${e.message}"
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun performHealthCheck() {
|
||||
viewModelScope.launch {
|
||||
uiState = uiState.copy(isLoading = true, errorMessage = null)
|
||||
try {
|
||||
val response = apiClient.healthCheck()
|
||||
uiState = uiState.copy(
|
||||
isLoading = false,
|
||||
healthResponse = response
|
||||
)
|
||||
} catch (e: Exception) {
|
||||
uiState = uiState.copy(
|
||||
isLoading = false,
|
||||
errorMessage = "Health check failed: ${e.message}"
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun clearError() {
|
||||
uiState = uiState.copy(errorMessage = null)
|
||||
}
|
||||
|
||||
/**
|
||||
* Erweiterte Methode: Echte API-Tests für Reitsport-Rollen
|
||||
*/
|
||||
fun testReitsportRole(role: ReitsportRole) {
|
||||
viewModelScope.launch {
|
||||
uiState = uiState.copy(
|
||||
isLoading = true,
|
||||
errorMessage = null
|
||||
)
|
||||
|
||||
try {
|
||||
// Echte API-Tests durchführen
|
||||
val apiClient = ReitsportTestApi()
|
||||
val testResults = apiClient.testRole(role)
|
||||
|
||||
// Erfolgs-Statistiken berechnen
|
||||
val successful = testResults.count { it.success }
|
||||
val total = testResults.size
|
||||
val successRate = if (total > 0) (successful * 100 / total) else 0
|
||||
|
||||
// Test-Summary erstellen
|
||||
val summary = buildString {
|
||||
appendLine("🎯 ${role.displayName} - Test Abgeschlossen")
|
||||
appendLine("📊 Erfolgsrate: $successful/$total Tests ($successRate%)")
|
||||
appendLine("⏱️ Durchschnittsdauer: ${testResults.map { it.duration }.average().toInt()}ms")
|
||||
appendLine("🔑 Berechtigungen: ${role.permissions.size}")
|
||||
appendLine("")
|
||||
appendLine("📋 Test-Ergebnisse:")
|
||||
|
||||
testResults.forEach { result ->
|
||||
val icon = if (result.success) "✅" else "❌"
|
||||
val status = if (result.responseCode != null) " (${result.responseCode})" else ""
|
||||
appendLine("$icon ${result.scenarioName}$status - ${result.duration}ms")
|
||||
}
|
||||
}
|
||||
|
||||
// Mock-Response für Anzeige
|
||||
val mockResponse = PingResponse(
|
||||
status = summary,
|
||||
timestamp = DateTimeHelper.formatDateTime(DateTimeHelper.now()),
|
||||
service = "Reitsport-Auth-Test"
|
||||
)
|
||||
|
||||
uiState = uiState.copy(
|
||||
isLoading = false,
|
||||
simplePingResponse = mockResponse
|
||||
)
|
||||
|
||||
println("[DEBUG] Reitsport-API-Test: ${role.displayName}")
|
||||
println("[DEBUG] Ergebnisse: $successful/$total erfolgreich")
|
||||
|
||||
} catch (e: Exception) {
|
||||
uiState = uiState.copy(
|
||||
isLoading = false,
|
||||
errorMessage = "Reitsport-API-Test fehlgeschlagen: ${e.message}"
|
||||
)
|
||||
println("[ERROR] Reitsport-Test-Fehler: ${e.message}")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+226
-222
@@ -8,254 +8,258 @@ import kotlinx.coroutines.delay
|
||||
|
||||
/**
|
||||
* API-Client für Reitsport-Authentication-Testing
|
||||
* Testet verschiedene Services mit rollenbasierten Tokens
|
||||
* testet verschiedene Services mit rollenbasierten Tokens
|
||||
*/
|
||||
class ReitsportTestApi {
|
||||
|
||||
companion object {
|
||||
// URLs der verfügbaren Services
|
||||
private const val PING_SERVICE_URL = "http://localhost:8082"
|
||||
private const val GATEWAY_URL = "http://localhost:8081"
|
||||
companion object {
|
||||
// URLs der verfügbaren Services
|
||||
private const val PING_SERVICE_URL = "http://localhost:8082"
|
||||
private const val GATEWAY_URL = "http://localhost:8081"
|
||||
|
||||
// Mock URLs für auskommentierte Services
|
||||
private const val MEMBERS_SERVICE_URL = "http://localhost:8083" // Auskommentiert
|
||||
private const val HORSES_SERVICE_URL = "http://localhost:8084" // Auskommentiert
|
||||
private const val EVENTS_SERVICE_URL = "http://localhost:8085" // Auskommentiert
|
||||
// Mock URLs für auskommentierte Services
|
||||
private const val MEMBERS_SERVICE_URL = "http://localhost:8083" // Auskommentiert
|
||||
private const val HORSES_SERVICE_URL = "http://localhost:8084" // Auskommentiert
|
||||
private const val EVENTS_SERVICE_URL = "http://localhost:8085" // Auskommentiert
|
||||
}
|
||||
|
||||
/**
|
||||
* Teste eine Rolle gegen verfügbare Services
|
||||
*/
|
||||
suspend fun testRole(role: ReitsportRole): List<ApiTestResult> {
|
||||
val results = mutableListOf<ApiTestResult>()
|
||||
|
||||
// 1. Test Ping-Service (immer verfügbar)
|
||||
results.add(testPingService(role))
|
||||
|
||||
// 2. Test Gateway Health (immer verfügbar)
|
||||
results.add(testGatewayHealth(role))
|
||||
|
||||
// 3. Test rollenspezifische Services
|
||||
when (role.roleType) {
|
||||
RolleE.ADMIN, RolleE.VEREINS_ADMIN -> {
|
||||
results.add(testMembersService(role))
|
||||
results.add(testSystemAccess(role))
|
||||
}
|
||||
|
||||
RolleE.FUNKTIONAER -> {
|
||||
results.add(testEventsService(role))
|
||||
results.add(testMembersService(role))
|
||||
}
|
||||
|
||||
RolleE.TIERARZT, RolleE.TRAINER -> {
|
||||
results.add(testHorsesService(role))
|
||||
}
|
||||
|
||||
RolleE.REITER -> {
|
||||
results.add(testMembersService(role))
|
||||
}
|
||||
|
||||
RolleE.RICHTER, RolleE.ZUSCHAUER, RolleE.GAST -> {
|
||||
results.add(testPublicAccess(role))
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Teste eine Rolle gegen verfügbare Services
|
||||
*/
|
||||
suspend fun testRole(role: ReitsportRole): List<ApiTestResult> {
|
||||
val results = mutableListOf<ApiTestResult>()
|
||||
return results
|
||||
}
|
||||
|
||||
// 1. Test Ping-Service (immer verfügbar)
|
||||
results.add(testPingService(role))
|
||||
/**
|
||||
* Test 1: Ping-Service (verfügbar)
|
||||
*/
|
||||
private suspend fun testPingService(role: ReitsportRole): ApiTestResult {
|
||||
val startTime = DateTimeHelper.now()
|
||||
|
||||
// 2. Test Gateway Health (immer verfügbar)
|
||||
results.add(testGatewayHealth(role))
|
||||
return try {
|
||||
// Simuliere HTTP-Call zum Ping-Service
|
||||
delay(200)
|
||||
|
||||
// 3. Test rollenspezifische Services
|
||||
when (role.roleType) {
|
||||
RolleE.ADMIN, RolleE.VEREINS_ADMIN -> {
|
||||
results.add(testMembersService(role))
|
||||
results.add(testSystemAccess(role))
|
||||
}
|
||||
RolleE.FUNKTIONAER -> {
|
||||
results.add(testEventsService(role))
|
||||
results.add(testMembersService(role))
|
||||
}
|
||||
RolleE.TIERARZT, RolleE.TRAINER -> {
|
||||
results.add(testHorsesService(role))
|
||||
}
|
||||
RolleE.REITER -> {
|
||||
results.add(testMembersService(role))
|
||||
}
|
||||
RolleE.RICHTER, RolleE.ZUSCHAUER, RolleE.GAST -> {
|
||||
results.add(testPublicAccess(role))
|
||||
}
|
||||
}
|
||||
val duration = DateTimeHelper.now() - startTime
|
||||
val endpoint = "$PING_SERVICE_URL/health"
|
||||
|
||||
return results
|
||||
ApiTestResult(
|
||||
scenarioId = "ping-health",
|
||||
scenarioName = "Ping Service Health",
|
||||
endpoint = endpoint,
|
||||
method = "GET",
|
||||
expectedResult = "Service erreichbar",
|
||||
actualResult = "✅ Ping-Service läuft (HTTP 200)",
|
||||
success = true,
|
||||
responseCode = 200,
|
||||
duration = duration,
|
||||
token = generateMockToken(role),
|
||||
responseData = """{"status":"pong","service":"ping-service","healthy":true}"""
|
||||
)
|
||||
} catch (e: Exception) {
|
||||
ApiTestResult(
|
||||
scenarioId = "ping-health",
|
||||
scenarioName = "Ping Service Health",
|
||||
endpoint = "$PING_SERVICE_URL/health",
|
||||
method = "GET",
|
||||
expectedResult = "Service erreichbar",
|
||||
actualResult = "❌ Fehler: ${e.message}",
|
||||
success = false,
|
||||
duration = DateTimeHelper.now() - startTime,
|
||||
errorMessage = e.message
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test 1: Ping-Service (verfügbar)
|
||||
*/
|
||||
private suspend fun testPingService(role: ReitsportRole): ApiTestResult {
|
||||
val startTime = DateTimeHelper.now()
|
||||
/**
|
||||
* Test 2: Gateway Health (verfügbar)
|
||||
*/
|
||||
private suspend fun testGatewayHealth(role: ReitsportRole): ApiTestResult {
|
||||
val startTime = DateTimeHelper.now()
|
||||
|
||||
return try {
|
||||
// Simuliere HTTP-Call zum Ping-Service
|
||||
delay(200)
|
||||
return try {
|
||||
delay(150)
|
||||
|
||||
val duration = DateTimeHelper.now() - startTime
|
||||
val endpoint = "$PING_SERVICE_URL/health"
|
||||
val duration = DateTimeHelper.now() - startTime
|
||||
val endpoint = "$GATEWAY_URL/actuator/health"
|
||||
|
||||
ApiTestResult(
|
||||
scenarioId = "ping-health",
|
||||
scenarioName = "Ping Service Health",
|
||||
endpoint = endpoint,
|
||||
method = "GET",
|
||||
expectedResult = "Service erreichbar",
|
||||
actualResult = "✅ Ping-Service läuft (HTTP 200)",
|
||||
success = true,
|
||||
responseCode = 200,
|
||||
duration = duration,
|
||||
token = generateMockToken(role),
|
||||
responseData = """{"status":"pong","service":"ping-service","healthy":true}"""
|
||||
)
|
||||
} catch (e: Exception) {
|
||||
ApiTestResult(
|
||||
scenarioId = "ping-health",
|
||||
scenarioName = "Ping Service Health",
|
||||
endpoint = "$PING_SERVICE_URL/health",
|
||||
method = "GET",
|
||||
expectedResult = "Service erreichbar",
|
||||
actualResult = "❌ Fehler: ${e.message}",
|
||||
success = false,
|
||||
duration = DateTimeHelper.now() - startTime,
|
||||
errorMessage = e.message
|
||||
)
|
||||
}
|
||||
ApiTestResult(
|
||||
scenarioId = "gateway-health",
|
||||
scenarioName = "API Gateway Health",
|
||||
endpoint = endpoint,
|
||||
method = "GET",
|
||||
expectedResult = "Gateway gesund",
|
||||
actualResult = "✅ Gateway erreichbar, Service Discovery aktiv",
|
||||
success = true,
|
||||
responseCode = 200,
|
||||
duration = duration,
|
||||
token = generateMockToken(role),
|
||||
responseData = """{"status":"UP","components":{"consul":{"status":"UP"}}}"""
|
||||
)
|
||||
} catch (e: Exception) {
|
||||
ApiTestResult(
|
||||
scenarioId = "gateway-health",
|
||||
scenarioName = "API Gateway Health",
|
||||
endpoint = "$GATEWAY_URL/actuator/health",
|
||||
method = "GET",
|
||||
expectedResult = "Gateway gesund",
|
||||
actualResult = "❌ Gateway nicht erreichbar: ${e.message}",
|
||||
success = false,
|
||||
duration = DateTimeHelper.now() - startTime,
|
||||
errorMessage = e.message
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test 2: Gateway Health (verfügbar)
|
||||
*/
|
||||
private suspend fun testGatewayHealth(role: ReitsportRole): ApiTestResult {
|
||||
val startTime = DateTimeHelper.now()
|
||||
/**
|
||||
* Test 3: Members-Service (auskommentiert - Graceful Degradation)
|
||||
*/
|
||||
private suspend fun testMembersService(role: ReitsportRole): ApiTestResult {
|
||||
val startTime = DateTimeHelper.now()
|
||||
delay(100)
|
||||
|
||||
return try {
|
||||
delay(150)
|
||||
return ApiTestResult(
|
||||
scenarioId = "members-unavailable",
|
||||
scenarioName = "Members Service",
|
||||
endpoint = "$MEMBERS_SERVICE_URL/api/members",
|
||||
method = "GET",
|
||||
expectedResult = "Mitglieder-Daten abrufen",
|
||||
actualResult = "⚠️ Service temporär deaktiviert (in settings.gradle.kts auskommentiert)",
|
||||
success = false,
|
||||
responseCode = 503, // Service Unavailable
|
||||
duration = DateTimeHelper.now() - startTime,
|
||||
token = generateMockToken(role),
|
||||
errorMessage = "Service ist in der aktuellen Konfiguration nicht verfügbar"
|
||||
)
|
||||
}
|
||||
|
||||
val duration = DateTimeHelper.now() - startTime
|
||||
val endpoint = "$GATEWAY_URL/actuator/health"
|
||||
/**
|
||||
* Test 4: Horses-Service (auskommentiert)
|
||||
*/
|
||||
private suspend fun testHorsesService(role: ReitsportRole): ApiTestResult {
|
||||
val startTime = DateTimeHelper.now()
|
||||
delay(100)
|
||||
|
||||
ApiTestResult(
|
||||
scenarioId = "gateway-health",
|
||||
scenarioName = "API Gateway Health",
|
||||
endpoint = endpoint,
|
||||
method = "GET",
|
||||
expectedResult = "Gateway gesund",
|
||||
actualResult = "✅ Gateway erreichbar, Service Discovery aktiv",
|
||||
success = true,
|
||||
responseCode = 200,
|
||||
duration = duration,
|
||||
token = generateMockToken(role),
|
||||
responseData = """{"status":"UP","components":{"consul":{"status":"UP"}}}"""
|
||||
)
|
||||
} catch (e: Exception) {
|
||||
ApiTestResult(
|
||||
scenarioId = "gateway-health",
|
||||
scenarioName = "API Gateway Health",
|
||||
endpoint = "$GATEWAY_URL/actuator/health",
|
||||
method = "GET",
|
||||
expectedResult = "Gateway gesund",
|
||||
actualResult = "❌ Gateway nicht erreichbar: ${e.message}",
|
||||
success = false,
|
||||
duration = DateTimeHelper.now() - startTime,
|
||||
errorMessage = e.message
|
||||
)
|
||||
}
|
||||
}
|
||||
return ApiTestResult(
|
||||
scenarioId = "horses-unavailable",
|
||||
scenarioName = "Horses Service",
|
||||
endpoint = "$HORSES_SERVICE_URL/api/horses",
|
||||
method = "GET",
|
||||
expectedResult = "Pferde-Daten abrufen",
|
||||
actualResult = "⚠️ Service temporär deaktiviert (in settings.gradle.kts auskommentiert)",
|
||||
success = false,
|
||||
responseCode = 503,
|
||||
duration = DateTimeHelper.now() - startTime,
|
||||
token = generateMockToken(role),
|
||||
errorMessage = "Service wird später aktiviert"
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Test 3: Members-Service (auskommentiert - Graceful Degradation)
|
||||
*/
|
||||
private suspend fun testMembersService(role: ReitsportRole): ApiTestResult {
|
||||
val startTime = DateTimeHelper.now()
|
||||
delay(100)
|
||||
/**
|
||||
* Test 5: Events-Service (auskommentiert)
|
||||
*/
|
||||
private suspend fun testEventsService(role: ReitsportRole): ApiTestResult {
|
||||
val startTime = DateTimeHelper.now()
|
||||
delay(100)
|
||||
|
||||
return ApiTestResult(
|
||||
scenarioId = "members-unavailable",
|
||||
scenarioName = "Members Service",
|
||||
endpoint = "$MEMBERS_SERVICE_URL/api/members",
|
||||
method = "GET",
|
||||
expectedResult = "Mitglieder-Daten abrufen",
|
||||
actualResult = "⚠️ Service temporär deaktiviert (in settings.gradle.kts auskommentiert)",
|
||||
success = false,
|
||||
responseCode = 503, // Service Unavailable
|
||||
duration = DateTimeHelper.now() - startTime,
|
||||
token = generateMockToken(role),
|
||||
errorMessage = "Service ist in der aktuellen Konfiguration nicht verfügbar"
|
||||
)
|
||||
}
|
||||
return ApiTestResult(
|
||||
scenarioId = "events-unavailable",
|
||||
scenarioName = "Events Service",
|
||||
endpoint = "$EVENTS_SERVICE_URL/api/events",
|
||||
method = "GET",
|
||||
expectedResult = "Veranstaltungs-Daten abrufen",
|
||||
actualResult = "⚠️ Service temporär deaktiviert (in settings.gradle.kts auskommentiert)",
|
||||
success = false,
|
||||
responseCode = 503,
|
||||
duration = DateTimeHelper.now() - startTime,
|
||||
token = generateMockToken(role),
|
||||
errorMessage = "Service in Entwicklung"
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Test 4: Horses-Service (auskommentiert)
|
||||
*/
|
||||
private suspend fun testHorsesService(role: ReitsportRole): ApiTestResult {
|
||||
val startTime = DateTimeHelper.now()
|
||||
delay(100)
|
||||
/**
|
||||
* Test 6: System-Zugriff (für Admins)
|
||||
*/
|
||||
private suspend fun testSystemAccess(role: ReitsportRole): ApiTestResult {
|
||||
val startTime = DateTimeHelper.now()
|
||||
delay(300)
|
||||
|
||||
return ApiTestResult(
|
||||
scenarioId = "horses-unavailable",
|
||||
scenarioName = "Horses Service",
|
||||
endpoint = "$HORSES_SERVICE_URL/api/horses",
|
||||
method = "GET",
|
||||
expectedResult = "Pferde-Daten abrufen",
|
||||
actualResult = "⚠️ Service temporär deaktiviert (in settings.gradle.kts auskommentiert)",
|
||||
success = false,
|
||||
responseCode = 503,
|
||||
duration = DateTimeHelper.now() - startTime,
|
||||
token = generateMockToken(role),
|
||||
errorMessage = "Service wird später aktiviert"
|
||||
)
|
||||
}
|
||||
val hasSystemAccess = role.roleType == RolleE.ADMIN
|
||||
|
||||
/**
|
||||
* Test 5: Events-Service (auskommentiert)
|
||||
*/
|
||||
private suspend fun testEventsService(role: ReitsportRole): ApiTestResult {
|
||||
val startTime = DateTimeHelper.now()
|
||||
delay(100)
|
||||
return ApiTestResult(
|
||||
scenarioId = "system-access",
|
||||
scenarioName = "System-Administration",
|
||||
endpoint = "$GATEWAY_URL/actuator/info",
|
||||
method = "GET",
|
||||
expectedResult = if (hasSystemAccess) "System-Info verfügbar" else "Zugriff verweigert",
|
||||
actualResult = if (hasSystemAccess) "✅ System-Informationen zugänglich" else "❌ Insufficient permissions",
|
||||
success = hasSystemAccess,
|
||||
responseCode = if (hasSystemAccess) 200 else 403,
|
||||
duration = DateTimeHelper.now() - startTime,
|
||||
token = generateMockToken(role)
|
||||
)
|
||||
}
|
||||
|
||||
return ApiTestResult(
|
||||
scenarioId = "events-unavailable",
|
||||
scenarioName = "Events Service",
|
||||
endpoint = "$EVENTS_SERVICE_URL/api/events",
|
||||
method = "GET",
|
||||
expectedResult = "Veranstaltungs-Daten abrufen",
|
||||
actualResult = "⚠️ Service temporär deaktiviert (in settings.gradle.kts auskommentiert)",
|
||||
success = false,
|
||||
responseCode = 503,
|
||||
duration = DateTimeHelper.now() - startTime,
|
||||
token = generateMockToken(role),
|
||||
errorMessage = "Service in Entwicklung"
|
||||
)
|
||||
}
|
||||
/**
|
||||
* Test 7: Öffentlicher Zugriff
|
||||
*/
|
||||
private suspend fun testPublicAccess(role: ReitsportRole): ApiTestResult {
|
||||
val startTime = DateTimeHelper.now()
|
||||
delay(150)
|
||||
|
||||
/**
|
||||
* Test 6: System-Zugriff (für Admins)
|
||||
*/
|
||||
private suspend fun testSystemAccess(role: ReitsportRole): ApiTestResult {
|
||||
val startTime = DateTimeHelper.now()
|
||||
delay(300)
|
||||
return ApiTestResult(
|
||||
scenarioId = "public-access",
|
||||
scenarioName = "Öffentliche Informationen",
|
||||
endpoint = "$GATEWAY_URL/api/public/info",
|
||||
method = "GET",
|
||||
expectedResult = "Öffentliche Daten verfügbar",
|
||||
actualResult = "✅ Öffentliche Informationen zugänglich (kein Token erforderlich)",
|
||||
success = true,
|
||||
responseCode = 200,
|
||||
duration = DateTimeHelper.now() - startTime,
|
||||
token = null // Kein Token für öffentlichen Zugriff
|
||||
)
|
||||
}
|
||||
|
||||
val hasSystemAccess = role.roleType == RolleE.ADMIN
|
||||
|
||||
return ApiTestResult(
|
||||
scenarioId = "system-access",
|
||||
scenarioName = "System-Administration",
|
||||
endpoint = "$GATEWAY_URL/actuator/info",
|
||||
method = "GET",
|
||||
expectedResult = if (hasSystemAccess) "System-Info verfügbar" else "Zugriff verweigert",
|
||||
actualResult = if (hasSystemAccess) "✅ System-Informationen zugänglich" else "❌ Insufficient permissions",
|
||||
success = hasSystemAccess,
|
||||
responseCode = if (hasSystemAccess) 200 else 403,
|
||||
duration = DateTimeHelper.now() - startTime,
|
||||
token = generateMockToken(role)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Test 7: Öffentlicher Zugriff
|
||||
*/
|
||||
private suspend fun testPublicAccess(role: ReitsportRole): ApiTestResult {
|
||||
val startTime = DateTimeHelper.now()
|
||||
delay(150)
|
||||
|
||||
return ApiTestResult(
|
||||
scenarioId = "public-access",
|
||||
scenarioName = "Öffentliche Informationen",
|
||||
endpoint = "$GATEWAY_URL/api/public/info",
|
||||
method = "GET",
|
||||
expectedResult = "Öffentliche Daten verfügbar",
|
||||
actualResult = "✅ Öffentliche Informationen zugänglich (kein Token erforderlich)",
|
||||
success = true,
|
||||
responseCode = 200,
|
||||
duration = DateTimeHelper.now() - startTime,
|
||||
token = null // Kein Token für öffentlichen Zugriff
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Generiere Mock-Token für Tests
|
||||
*/
|
||||
private fun generateMockToken(role: ReitsportRole): String {
|
||||
// Phase 3: Mock-Token (später echte Keycloak-Integration)
|
||||
val mockPayload = """{"role":"${role.roleType}","permissions":${role.permissions.size}}"""
|
||||
return "mock.token.${DateTimeHelper.now()}.${role.roleType}"
|
||||
}
|
||||
/**
|
||||
* Generiere Mock-Token für Tests
|
||||
*/
|
||||
private fun generateMockToken(role: ReitsportRole): String {
|
||||
// Phase 3: Mock-Token (später echte Keycloak-Integration)
|
||||
val mockPayload = """{"role":"${role.roleType}","permissions":${role.permissions.size}}"""
|
||||
return "mock.token.${DateTimeHelper.now()}.${role.roleType}"
|
||||
}
|
||||
}
|
||||
|
||||
+29
-29
@@ -8,15 +8,15 @@ import kotlinx.serialization.Serializable
|
||||
*/
|
||||
@Serializable
|
||||
enum class RolleE {
|
||||
ADMIN, // System administrator
|
||||
VEREINS_ADMIN, // Club administrator
|
||||
FUNKTIONAER, // Official/functionary
|
||||
REITER, // Rider
|
||||
TRAINER, // Trainer
|
||||
RICHTER, // Judge
|
||||
TIERARZT, // Veterinarian
|
||||
ZUSCHAUER, // Spectator
|
||||
GAST // Guest
|
||||
ADMIN, // System administrator
|
||||
VEREINS_ADMIN, // Club administrator
|
||||
FUNKTIONAER, // Official/functionary
|
||||
REITER, // Rider
|
||||
TRAINER, // Trainer
|
||||
RICHTER, // Judge
|
||||
TIERARZT, // Veterinarian
|
||||
ZUSCHAUER, // Spectator
|
||||
GAST // Guest
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -25,27 +25,27 @@ enum class RolleE {
|
||||
*/
|
||||
@Serializable
|
||||
enum class BerechtigungE {
|
||||
// Person management
|
||||
PERSON_READ,
|
||||
PERSON_CREATE,
|
||||
PERSON_UPDATE,
|
||||
PERSON_DELETE,
|
||||
// Person management
|
||||
PERSON_READ,
|
||||
PERSON_CREATE,
|
||||
PERSON_UPDATE,
|
||||
PERSON_DELETE,
|
||||
|
||||
// Club management
|
||||
VEREIN_READ,
|
||||
VEREIN_CREATE,
|
||||
VEREIN_UPDATE,
|
||||
VEREIN_DELETE,
|
||||
// Club management
|
||||
VEREIN_READ,
|
||||
VEREIN_CREATE,
|
||||
VEREIN_UPDATE,
|
||||
VEREIN_DELETE,
|
||||
|
||||
// Event management
|
||||
VERANSTALTUNG_READ,
|
||||
VERANSTALTUNG_CREATE,
|
||||
VERANSTALTUNG_UPDATE,
|
||||
VERANSTALTUNG_DELETE,
|
||||
// Event management
|
||||
VERANSTALTUNG_READ,
|
||||
VERANSTALTUNG_CREATE,
|
||||
VERANSTALTUNG_UPDATE,
|
||||
VERANSTALTUNG_DELETE,
|
||||
|
||||
// Horse management
|
||||
PFERD_READ,
|
||||
PFERD_CREATE,
|
||||
PFERD_UPDATE,
|
||||
PFERD_DELETE
|
||||
// Horse management
|
||||
PFERD_READ,
|
||||
PFERD_CREATE,
|
||||
PFERD_UPDATE,
|
||||
PFERD_DELETE
|
||||
}
|
||||
|
||||
-93
@@ -1,93 +0,0 @@
|
||||
package at.mocode.clients.pingfeature.model
|
||||
|
||||
/**
|
||||
* Phase 1 Validierung für Reitsport-Authentication-Testing
|
||||
* Testet alle Erfolgs-Kriterien aus der Aufgabenstellung
|
||||
*/
|
||||
object Phase1Validation {
|
||||
|
||||
/**
|
||||
* Führt alle Phase 1 Validierungen durch
|
||||
*/
|
||||
fun validatePhase1(): String {
|
||||
val results = mutableListOf<String>()
|
||||
|
||||
// ✅ Test 1: Anzahl Rollen (erwartet: 9)
|
||||
val roleCount = ReitsportRoles.ALL_ROLES.size
|
||||
results.add("✅ Rollen-Anzahl: $roleCount (erwartet: 9) - ${if (roleCount == 9) "ERFOLG" else "FEHLER"}")
|
||||
|
||||
// ✅ Test 2: Admin-Rolle verfügbar
|
||||
val adminRole = ReitsportRoles.ADMIN
|
||||
results.add("✅ Admin-Rolle: ${adminRole.displayName} - ERFOLG")
|
||||
|
||||
// ✅ Test 3: Alle Kategorien verfügbar
|
||||
val categories = ReitsportRoles.ROLES_BY_CATEGORY.keys
|
||||
results.add("✅ Kategorien: $categories - ERFOLG")
|
||||
results.add(" - SYSTEM: ${ReitsportRoles.ROLES_BY_CATEGORY[RoleCategory.SYSTEM]?.size ?: 0} Rollen")
|
||||
results.add(" - OFFICIAL: ${ReitsportRoles.ROLES_BY_CATEGORY[RoleCategory.OFFICIAL]?.size ?: 0} Rollen")
|
||||
results.add(" - ACTIVE: ${ReitsportRoles.ROLES_BY_CATEGORY[RoleCategory.ACTIVE]?.size ?: 0} Rollen")
|
||||
results.add(" - PASSIVE: ${ReitsportRoles.ROLES_BY_CATEGORY[RoleCategory.PASSIVE]?.size ?: 0} Rollen")
|
||||
|
||||
// ✅ Test 4: DateTime funktioniert
|
||||
val timestamp = DateTimeHelper.now()
|
||||
results.add("✅ DateTime funktioniert: $timestamp - ERFOLG")
|
||||
|
||||
// ✅ Test 5: Test-ID generiert
|
||||
val testId = getTimeMillis().toString()
|
||||
results.add("✅ Test-ID generiert: $testId - ERFOLG")
|
||||
|
||||
// ✅ Test 6: Enum-Zugriff funktioniert
|
||||
results.add("✅ RolleE Enum: ${RolleE.entries.size} Einträge - ERFOLG")
|
||||
results.add("✅ BerechtigungE Enum: ${BerechtigungE.entries.size} Einträge - ERFOLG")
|
||||
|
||||
// ✅ Test 7: Alle 9 Rollen einzeln prüfen
|
||||
results.add("✅ Alle Rollen-Definitionen:")
|
||||
ReitsportRoles.ALL_ROLES.forEachIndexed { index, role ->
|
||||
results.add(" ${index + 1}. ${role.displayName} (${role.roleType}) - ${role.permissions.size} Berechtigungen")
|
||||
}
|
||||
|
||||
// ✅ Test 8: Berechtigungen-Zuordnung testen
|
||||
val adminPermissions = ReitsportRoles.ADMIN.permissions.size
|
||||
val guestPermissions = ReitsportRoles.GAST.permissions.size
|
||||
results.add("✅ Admin-Berechtigungen: $adminPermissions (max)")
|
||||
results.add("✅ Gast-Berechtigungen: $guestPermissions (min)")
|
||||
|
||||
// ✅ Test 9: Hilfsfunktionen testen
|
||||
val roleByType = ReitsportRoles.getRoleByType(RolleE.RICHTER)
|
||||
results.add("✅ Rolle per Type: ${roleByType?.displayName} - ERFOLG")
|
||||
|
||||
val rolesWithRead = ReitsportRoles.getRolesWithPermission(BerechtigungE.PERSON_READ)
|
||||
results.add("✅ Rollen mit PERSON_READ: ${rolesWithRead.size} - ERFOLG")
|
||||
|
||||
return results.joinToString("\n")
|
||||
}
|
||||
|
||||
/**
|
||||
* Führt Performance-Test durch
|
||||
*/
|
||||
fun performanceTest(): String {
|
||||
val start = DateTimeHelper.now()
|
||||
|
||||
// Simuliere mehrere Rollen-Abfragen
|
||||
repeat(100) {
|
||||
ReitsportRoles.getAllRoles()
|
||||
ReitsportRoles.getRoleByType(RolleE.ADMIN)
|
||||
ReitsportRoles.getRolesWithPermission(BerechtigungE.PERSON_READ)
|
||||
}
|
||||
|
||||
val end = DateTimeHelper.now()
|
||||
val duration = end - start
|
||||
|
||||
return "✅ Performance-Test: $duration Zeiteinheiten für 300 Operationen - ERFOLG"
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Hilfsfunktion für externe Zeitabfrage
|
||||
*/
|
||||
private fun getTimeMillis(): Long = DateTimeHelper.now()
|
||||
|
||||
/**
|
||||
* Extension für einfacheren Zugriff
|
||||
*/
|
||||
private fun ReitsportRoles.getAllRoles() = ALL_ROLES
|
||||
+148
-148
@@ -13,27 +13,27 @@ import kotlinx.serialization.Serializable
|
||||
*/
|
||||
@Serializable
|
||||
data class ReitsportRole(
|
||||
val roleType: RolleE,
|
||||
val displayName: String,
|
||||
val description: String,
|
||||
val icon: String,
|
||||
val permissions: List<BerechtigungE>,
|
||||
val priority: Int, // Für Sortierung in UI (1 = höchste Priorität)
|
||||
val category: RoleCategory
|
||||
val roleType: RolleE,
|
||||
val displayName: String,
|
||||
val description: String,
|
||||
val icon: String,
|
||||
val permissions: List<BerechtigungE>,
|
||||
val priority: Int, // Für Sortierung in UI (1 = höchste Priorität)
|
||||
val category: RoleCategory
|
||||
) {
|
||||
/**
|
||||
* Hilfsfunktion: Prüft, ob diese Rolle eine bestimmte Berechtigung hat
|
||||
*/
|
||||
fun hasPermission(permission: BerechtigungE): Boolean {
|
||||
return permissions.contains(permission)
|
||||
}
|
||||
/**
|
||||
* Hilfsfunktion: Prüft, ob diese Rolle eine bestimmte Berechtigung hat
|
||||
*/
|
||||
fun hasPermission(permission: BerechtigungE): Boolean {
|
||||
return permissions.contains(permission)
|
||||
}
|
||||
|
||||
/**
|
||||
* Hilfsfunktion: Gibt alle fehlenden Berechtigungen für eine Liste zurück
|
||||
*/
|
||||
fun getMissingPermissions(requiredPermissions: List<BerechtigungE>): List<BerechtigungE> {
|
||||
return requiredPermissions.filter { !permissions.contains(it) }
|
||||
}
|
||||
/**
|
||||
* Hilfsfunktion: Gibt alle fehlenden Berechtigungen für eine Liste zurück
|
||||
*/
|
||||
fun getMissingPermissions(requiredPermissions: List<BerechtigungE>): List<BerechtigungE> {
|
||||
return requiredPermissions.filter { !permissions.contains(it) }
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -41,10 +41,10 @@ data class ReitsportRole(
|
||||
*/
|
||||
@Serializable
|
||||
enum class RoleCategory(val displayName: String, val color: String) {
|
||||
SYSTEM("System-Verwaltung", "#FF5722"), // Rot
|
||||
OFFICIAL("Offizielle Funktionen", "#3F51B5"), // Indigo
|
||||
ACTIVE("Aktive Teilnahme", "#4CAF50"), // Grün
|
||||
PASSIVE("Information & Zugang", "#9E9E9E") // Grau
|
||||
SYSTEM("System-Verwaltung", "#FF5722"), // Rot
|
||||
OFFICIAL("Offizielle Funktionen", "#3F51B5"), // Indigo
|
||||
ACTIVE("Aktive Teilnahme", "#4CAF50"), // Grün
|
||||
PASSIVE("Information & Zugang", "#9E9E9E") // Grau
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -52,17 +52,17 @@ enum class RoleCategory(val displayName: String, val color: String) {
|
||||
*/
|
||||
@Serializable
|
||||
data class AuthTestScenario(
|
||||
val id: String,
|
||||
val name: String,
|
||||
val businessProcess: String,
|
||||
val description: String,
|
||||
val expectedBehavior: String,
|
||||
val requiredRole: RolleE,
|
||||
val requiredPermissions: List<BerechtigungE>,
|
||||
val testEndpoint: String,
|
||||
val testMethod: String = "GET",
|
||||
val priority: TestPriority = TestPriority.NORMAL,
|
||||
val category: ScenarioCategory
|
||||
val id: String,
|
||||
val name: String,
|
||||
val businessProcess: String,
|
||||
val description: String,
|
||||
val expectedBehavior: String,
|
||||
val requiredRole: RolleE,
|
||||
val requiredPermissions: List<BerechtigungE>,
|
||||
val testEndpoint: String,
|
||||
val testMethod: String = "GET",
|
||||
val priority: TestPriority = TestPriority.NORMAL,
|
||||
val category: ScenarioCategory
|
||||
)
|
||||
|
||||
/**
|
||||
@@ -70,37 +70,37 @@ data class AuthTestScenario(
|
||||
*/
|
||||
@Serializable
|
||||
enum class ScenarioCategory(val displayName: String, val icon: String) {
|
||||
// Kern-Geschäftsprozesse
|
||||
VERANSTALTUNG_SETUP("Veranstaltungs-Einrichtung", "🏟️"),
|
||||
TURNIER_MANAGEMENT("Turnier-Verwaltung", "🎪"),
|
||||
BEWERB_KONFIGURATION("Bewerb-Konfiguration", "🏇"),
|
||||
// Kern-Geschäftsprozesse
|
||||
VERANSTALTUNG_SETUP("Veranstaltungs-Einrichtung", "🏟️"),
|
||||
TURNIER_MANAGEMENT("Turnier-Verwaltung", "🎪"),
|
||||
BEWERB_KONFIGURATION("Bewerb-Konfiguration", "🏇"),
|
||||
|
||||
// Finanzen
|
||||
KASSABUCH("Kassabuch-Führung", "💰"),
|
||||
ABRECHNUNG("Turnier-Abrechnung", "🧾"),
|
||||
// Finanzen
|
||||
KASSABUCH("Kassabuch-Führung", "💰"),
|
||||
ABRECHNUNG("Turnier-Abrechnung", "🧾"),
|
||||
|
||||
// Nennsystem
|
||||
NENNUNG_WEBFORMULAR("Nenn-Web-Formular", "📝"),
|
||||
NENNUNG_MOBILE("Mobile Nennung", "📱"),
|
||||
NENNTAUSCH("Nenntausch-System", "🔄"),
|
||||
// Nennsystem
|
||||
NENNUNG_WEBFORMULAR("Nenn-Web-Formular", "📝"),
|
||||
NENNUNG_MOBILE("Mobile Nennung", "📱"),
|
||||
NENNTAUSCH("Nenntausch-System", "🔄"),
|
||||
|
||||
// Startlisten & Zeitplan
|
||||
ZEITPLAN_ERSTELLUNG("Zeitplan-Erstellung", "⏰"),
|
||||
STARTERLISTE_FLEXIBEL("Flexible Starterlisten", "📋"),
|
||||
RICHTER_VALIDATION("Richter-Lizenz-Validierung", "⚖️"),
|
||||
// Startlisten & Zeitplan
|
||||
ZEITPLAN_ERSTELLUNG("Zeitplan-Erstellung", "⏰"),
|
||||
STARTERLISTE_FLEXIBEL("Flexible Starterlisten", "📋"),
|
||||
RICHTER_VALIDATION("Richter-Lizenz-Validierung", "⚖️"),
|
||||
|
||||
// Ergebnisse
|
||||
ERGEBNIS_DRESSUR("Ergebnis-Erfassung Dressur", "🎭"),
|
||||
ERGEBNIS_SPRINGEN("Ergebnis-Erfassung Springen", "🚀"),
|
||||
ERGEBNIS_VIELSEITIGKEIT("Ergebnis-Erfassung Vielseitigkeit", "🎯"),
|
||||
// Ergebnisse
|
||||
ERGEBNIS_DRESSUR("Ergebnis-Erfassung Dressur", "🎭"),
|
||||
ERGEBNIS_SPRINGEN("Ergebnis-Erfassung Springen", "🚀"),
|
||||
ERGEBNIS_VIELSEITIGKEIT("Ergebnis-Erfassung Vielseitigkeit", "🎯"),
|
||||
|
||||
// OEPS Integration
|
||||
OEPS_SYNC("OEPS-Synchronisation", "🔗"),
|
||||
TURNIER_NUMMER("Turnier-Nummer-Verwaltung", "🔢"),
|
||||
// OEPS Integration
|
||||
OEPS_SYNC("OEPS-Synchronisation", "🔗"),
|
||||
TURNIER_NUMMER("Turnier-Nummer-Verwaltung", "🔢"),
|
||||
|
||||
// System
|
||||
SYSTEM_ADMIN("System-Administration", "🔧"),
|
||||
BENUTZER_VERWALTUNG("Benutzer-Verwaltung", "👥")
|
||||
// System
|
||||
SYSTEM_ADMIN("System-Administration", "🔧"),
|
||||
BENUTZER_VERWALTUNG("Benutzer-Verwaltung", "👥")
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -108,29 +108,29 @@ enum class ScenarioCategory(val displayName: String, val icon: String) {
|
||||
*/
|
||||
@Serializable
|
||||
data class ComplexAuthTestScenario(
|
||||
val id: String,
|
||||
val name: String,
|
||||
val businessProcess: String,
|
||||
val description: String,
|
||||
val subProcesses: List<String>, // Multi-Step-Prozesse
|
||||
val requiredRole: RolleE,
|
||||
val requiredPermissions: List<BerechtigungE>,
|
||||
val testEndpoints: List<TestEndpoint>, // Mehrere API-Calls
|
||||
val mockData: Map<String, String> = emptyMap(),
|
||||
val expectedOutcome: String,
|
||||
val priority: TestPriority = TestPriority.NORMAL,
|
||||
val category: ScenarioCategory,
|
||||
val oepsIntegrationRequired: Boolean = false
|
||||
val id: String,
|
||||
val name: String,
|
||||
val businessProcess: String,
|
||||
val description: String,
|
||||
val subProcesses: List<String>, // Multi-Step-Prozesse
|
||||
val requiredRole: RolleE,
|
||||
val requiredPermissions: List<BerechtigungE>,
|
||||
val testEndpoints: List<TestEndpoint>, // Mehrere API-Calls
|
||||
val mockData: Map<String, String> = emptyMap(),
|
||||
val expectedOutcome: String,
|
||||
val priority: TestPriority = TestPriority.NORMAL,
|
||||
val category: ScenarioCategory,
|
||||
val oepsIntegrationRequired: Boolean = false
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class TestEndpoint(
|
||||
val name: String,
|
||||
val url: String,
|
||||
val method: String = "GET",
|
||||
val payload: String? = null,
|
||||
val expectedResponseCode: Int = 200,
|
||||
val description: String
|
||||
val name: String,
|
||||
val url: String,
|
||||
val method: String = "GET",
|
||||
val payload: String? = null,
|
||||
val expectedResponseCode: Int = 200,
|
||||
val description: String
|
||||
)
|
||||
|
||||
/**
|
||||
@@ -138,10 +138,10 @@ data class TestEndpoint(
|
||||
*/
|
||||
@Serializable
|
||||
enum class TestPriority(val displayName: String, val level: Int) {
|
||||
CRITICAL("Kritisch", 1),
|
||||
HIGH("Hoch", 2),
|
||||
NORMAL("Normal", 3),
|
||||
LOW("Niedrig", 4)
|
||||
CRITICAL("Kritisch", 1),
|
||||
HIGH("Hoch", 2),
|
||||
NORMAL("Normal", 3),
|
||||
LOW("Niedrig", 4)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -149,29 +149,29 @@ enum class TestPriority(val displayName: String, val level: Int) {
|
||||
*/
|
||||
@Serializable
|
||||
data class ApiTestResult(
|
||||
val scenarioId: String,
|
||||
val scenarioName: String,
|
||||
val endpoint: String,
|
||||
val method: String,
|
||||
val expectedResult: String,
|
||||
val actualResult: String,
|
||||
val success: Boolean,
|
||||
val responseCode: Int? = null,
|
||||
val duration: Long, // in Millisekunden
|
||||
val timestamp: Long = getTimeMillis(),
|
||||
val token: String? = null, // Gekürzte Token-Info für Debugging
|
||||
val errorMessage: String? = null,
|
||||
val responseData: String? = null
|
||||
val scenarioId: String,
|
||||
val scenarioName: String,
|
||||
val endpoint: String,
|
||||
val method: String,
|
||||
val expectedResult: String,
|
||||
val actualResult: String,
|
||||
val success: Boolean,
|
||||
val responseCode: Int? = null,
|
||||
val duration: Long, // in Millisekunden
|
||||
val timestamp: Long = getTimeMillis(),
|
||||
val token: String? = null, // Gekürzte Token-Info für Debugging
|
||||
val errorMessage: String? = null,
|
||||
val responseData: String? = null
|
||||
) {
|
||||
/**
|
||||
* Hilfsfunktion: Formatiert die Dauer für UI-Anzeige
|
||||
*/
|
||||
fun formatDuration(): String = "${duration}ms"
|
||||
/**
|
||||
* Hilfsfunktion: Formatiert die Dauer für UI-Anzeige
|
||||
*/
|
||||
fun formatDuration(): String = "${duration}ms"
|
||||
|
||||
/**
|
||||
* Hilfsfunktion: Status-Icon für UI
|
||||
*/
|
||||
fun getStatusIcon(): String = if (success) "✅" else "❌"
|
||||
/**
|
||||
* Hilfsfunktion: Status-Icon für UI
|
||||
*/
|
||||
fun getStatusIcon(): String = if (success) "✅" else "❌"
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -179,33 +179,33 @@ data class ApiTestResult(
|
||||
*/
|
||||
@Serializable
|
||||
data class ReitsportTestResult(
|
||||
val testId: String = getTimeMillis().toString(),
|
||||
val role: ReitsportRole,
|
||||
val scenarios: List<AuthTestScenario>,
|
||||
val apiResults: List<ApiTestResult>,
|
||||
val startTime: Long,
|
||||
val endTime: Long? = null,
|
||||
val overallSuccess: Boolean = false,
|
||||
val summary: TestSummary? = null
|
||||
val testId: String = getTimeMillis().toString(),
|
||||
val role: ReitsportRole,
|
||||
val scenarios: List<AuthTestScenario>,
|
||||
val apiResults: List<ApiTestResult>,
|
||||
val startTime: Long,
|
||||
val endTime: Long? = null,
|
||||
val overallSuccess: Boolean = false,
|
||||
val summary: TestSummary? = null
|
||||
) {
|
||||
/**
|
||||
* Berechnet die Gesamtdauer des Tests
|
||||
*/
|
||||
fun getTotalDuration(): Long = (endTime ?: getTimeMillis()) - startTime
|
||||
/**
|
||||
* Berechnet die Gesamtdauer des Tests
|
||||
*/
|
||||
fun getTotalDuration(): Long = (endTime ?: getTimeMillis()) - startTime
|
||||
|
||||
/**
|
||||
* Berechnet Erfolgsrate in Prozent
|
||||
*/
|
||||
fun getSuccessRate(): Double {
|
||||
if (apiResults.isEmpty()) return 0.0
|
||||
val successful = apiResults.count { it.success }
|
||||
return (successful.toDouble() / apiResults.size) * 100
|
||||
}
|
||||
/**
|
||||
* Berechnet Erfolgsrate in Prozent
|
||||
*/
|
||||
fun getSuccessRate(): Double {
|
||||
if (apiResults.isEmpty()) return 0.0
|
||||
val successful = apiResults.count { it.success }
|
||||
return (successful.toDouble() / apiResults.size) * 100
|
||||
}
|
||||
|
||||
/**
|
||||
* Gibt alle fehlgeschlagenen Tests zurück
|
||||
*/
|
||||
fun getFailedTests(): List<ApiTestResult> = apiResults.filter { !it.success }
|
||||
/**
|
||||
* Gibt alle fehlgeschlagenen Tests zurück
|
||||
*/
|
||||
fun getFailedTests(): List<ApiTestResult> = apiResults.filter { !it.success }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -213,15 +213,15 @@ data class ReitsportTestResult(
|
||||
*/
|
||||
@Serializable
|
||||
data class TestSummary(
|
||||
val totalTests: Int,
|
||||
val successfulTests: Int,
|
||||
val failedTests: Int,
|
||||
val averageDuration: Long,
|
||||
val criticalFailures: List<String> = emptyList(),
|
||||
val recommendations: List<String> = emptyList()
|
||||
val totalTests: Int,
|
||||
val successfulTests: Int,
|
||||
val failedTests: Int,
|
||||
val averageDuration: Long,
|
||||
val criticalFailures: List<String> = emptyList(),
|
||||
val recommendations: List<String> = emptyList()
|
||||
) {
|
||||
val successRate: Double
|
||||
get() = if (totalTests > 0) (successfulTests.toDouble() / totalTests) * 100 else 0.0
|
||||
val successRate: Double
|
||||
get() = if (totalTests > 0) (successfulTests.toDouble() / totalTests) * 100 else 0.0
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -229,17 +229,17 @@ data class TestSummary(
|
||||
*/
|
||||
@Serializable
|
||||
data class TestNennung(
|
||||
val reiterId: String,
|
||||
val pferdId: String,
|
||||
val bewerbId: String,
|
||||
val nennungsDatum: Long = getTimeMillis()
|
||||
val reiterId: String,
|
||||
val pferdId: String,
|
||||
val bewerbId: String,
|
||||
val nennungsDatum: Long = getTimeMillis()
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class TestStartbereitschaft(
|
||||
val nennungId: String,
|
||||
val confirmed: Boolean = true,
|
||||
val confirmationTime: Long = getTimeMillis()
|
||||
val nennungId: String,
|
||||
val confirmed: Boolean = true,
|
||||
val confirmationTime: Long = getTimeMillis()
|
||||
)
|
||||
|
||||
/**
|
||||
@@ -247,14 +247,14 @@ data class TestStartbereitschaft(
|
||||
* Temporäre Lösung für Phase 1 mit incrementellem Counter
|
||||
*/
|
||||
object DateTimeHelper {
|
||||
private var counter = 1000000000L // Start mit einer realistischen Timestamp
|
||||
private var counter = 1000000000L // Start mit einer realistischen Timestamp
|
||||
|
||||
fun now(): Long = counter++
|
||||
fun now(): Long = counter++
|
||||
|
||||
fun formatDateTime(timestamp: Long): String {
|
||||
// Einfache ISO-ähnliche Formatierung ohne kotlinx-datetime
|
||||
return "Timestamp: $timestamp" // Temporäre Lösung für Phase 1
|
||||
}
|
||||
fun formatDateTime(timestamp: Long): String {
|
||||
// Einfache ISO-ähnliche Formatierung ohne kotlinx-datetime
|
||||
return "Timestamp: $timestamp" // Temporäre Lösung für Phase 1
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+198
-198
@@ -6,215 +6,215 @@ package at.mocode.clients.pingfeature.model
|
||||
*/
|
||||
object ReitsportRoles {
|
||||
|
||||
/**
|
||||
* System-Administrator - Vollzugriff auf alle Bounded Contexts
|
||||
*/
|
||||
val ADMIN = ReitsportRole(
|
||||
roleType = RolleE.ADMIN,
|
||||
displayName = "System-Administrator",
|
||||
description = "Vollzugriff auf alle Microservices und System-Konfiguration",
|
||||
icon = "🔧",
|
||||
permissions = BerechtigungE.entries, // Alle verfügbaren Berechtigungen
|
||||
priority = 1,
|
||||
category = RoleCategory.SYSTEM
|
||||
)
|
||||
/**
|
||||
* System-Administrator - Vollzugriff auf alle Bounded Contexts
|
||||
*/
|
||||
val ADMIN = ReitsportRole(
|
||||
roleType = RolleE.ADMIN,
|
||||
displayName = "System-Administrator",
|
||||
description = "Vollzugriff auf alle Microservices und System-Konfiguration",
|
||||
icon = "🔧",
|
||||
permissions = BerechtigungE.entries, // Alle verfügbaren Berechtigungen
|
||||
priority = 1,
|
||||
category = RoleCategory.SYSTEM
|
||||
)
|
||||
|
||||
/**
|
||||
* Vereins-Administrator - Vereins-Bounded-Context
|
||||
*/
|
||||
val VEREINS_ADMIN = ReitsportRole(
|
||||
roleType = RolleE.VEREINS_ADMIN,
|
||||
displayName = "Vereins-Administrator",
|
||||
description = "Vereinsverwaltung und Mitglieder-Management",
|
||||
icon = "🏛️",
|
||||
permissions = listOf(
|
||||
// Personen (Mitglieder)
|
||||
BerechtigungE.PERSON_READ,
|
||||
BerechtigungE.PERSON_CREATE,
|
||||
BerechtigungE.PERSON_UPDATE,
|
||||
BerechtigungE.PERSON_DELETE,
|
||||
// Verein
|
||||
BerechtigungE.VEREIN_READ,
|
||||
BerechtigungE.VEREIN_UPDATE,
|
||||
// Veranstaltungen organisieren
|
||||
BerechtigungE.VERANSTALTUNG_READ,
|
||||
BerechtigungE.VERANSTALTUNG_CREATE,
|
||||
BerechtigungE.VERANSTALTUNG_UPDATE,
|
||||
// Pferde (für Vereinsmitglieder)
|
||||
BerechtigungE.PFERD_READ
|
||||
),
|
||||
priority = 2,
|
||||
category = RoleCategory.SYSTEM
|
||||
)
|
||||
/**
|
||||
* Vereins-Administrator - Vereins-Bounded-Context
|
||||
*/
|
||||
val VEREINS_ADMIN = ReitsportRole(
|
||||
roleType = RolleE.VEREINS_ADMIN,
|
||||
displayName = "Vereins-Administrator",
|
||||
description = "Vereinsverwaltung und Mitglieder-Management",
|
||||
icon = "🏛️",
|
||||
permissions = listOf(
|
||||
// Personen (Mitglieder)
|
||||
BerechtigungE.PERSON_READ,
|
||||
BerechtigungE.PERSON_CREATE,
|
||||
BerechtigungE.PERSON_UPDATE,
|
||||
BerechtigungE.PERSON_DELETE,
|
||||
// Verein
|
||||
BerechtigungE.VEREIN_READ,
|
||||
BerechtigungE.VEREIN_UPDATE,
|
||||
// Veranstaltungen organisieren
|
||||
BerechtigungE.VERANSTALTUNG_READ,
|
||||
BerechtigungE.VERANSTALTUNG_CREATE,
|
||||
BerechtigungE.VERANSTALTUNG_UPDATE,
|
||||
// Pferde (für Vereinsmitglieder)
|
||||
BerechtigungE.PFERD_READ
|
||||
),
|
||||
priority = 2,
|
||||
category = RoleCategory.SYSTEM
|
||||
)
|
||||
|
||||
/**
|
||||
* Funktionär - Event-Management-Bounded-Context
|
||||
*/
|
||||
val FUNKTIONAER = ReitsportRole(
|
||||
roleType = RolleE.FUNKTIONAER,
|
||||
displayName = "Funktionär (Meldestelle)",
|
||||
description = "Turnierorganisation: Nennungen, Starterlisten, Meldestellen-Workflows",
|
||||
icon = "⚖️",
|
||||
permissions = listOf(
|
||||
// Lesen aller relevanten Daten
|
||||
BerechtigungE.PERSON_READ,
|
||||
BerechtigungE.PFERD_READ,
|
||||
BerechtigungE.VERANSTALTUNG_READ,
|
||||
BerechtigungE.VERANSTALTUNG_UPDATE, // Turnier-Management
|
||||
// Erweiterte Rechte in Veranstaltungs-Context
|
||||
// (Hier werden später Nennung-, Startlisten-Berechtigungen hinzugefügt)
|
||||
),
|
||||
priority = 3,
|
||||
category = RoleCategory.OFFICIAL
|
||||
)
|
||||
/**
|
||||
* Funktionär - Event-Management-Bounded-Context
|
||||
*/
|
||||
val FUNKTIONAER = ReitsportRole(
|
||||
roleType = RolleE.FUNKTIONAER,
|
||||
displayName = "Funktionär (Meldestelle)",
|
||||
description = "Turnierorganisation: Nennungen, Starterlisten, Meldestellen-Workflows",
|
||||
icon = "⚖️",
|
||||
permissions = listOf(
|
||||
// Lesen aller relevanten Daten
|
||||
BerechtigungE.PERSON_READ,
|
||||
BerechtigungE.PFERD_READ,
|
||||
BerechtigungE.VERANSTALTUNG_READ,
|
||||
BerechtigungE.VERANSTALTUNG_UPDATE, // Turnier-Management
|
||||
// Erweiterte Rechte in Veranstaltungs-Context
|
||||
// (Hier werden später Nennung-, Startlisten-Berechtigungen hinzugefügt)
|
||||
),
|
||||
priority = 3,
|
||||
category = RoleCategory.OFFICIAL
|
||||
)
|
||||
|
||||
/**
|
||||
* Richter - Spezialisierte Bewertungs-Rolle
|
||||
*/
|
||||
val RICHTER = ReitsportRole(
|
||||
roleType = RolleE.RICHTER,
|
||||
displayName = "Richter",
|
||||
description = "Prüfungs-Bewertung und Ergebnis-Eingabe (ReadOnly-Zugriff auf Stammdaten)",
|
||||
icon = "⚖️",
|
||||
permissions = listOf(
|
||||
// Nur Lese-Zugriff auf relevante Daten
|
||||
BerechtigungE.PERSON_READ, // Starter-Info
|
||||
BerechtigungE.PFERD_READ, // Pferde-Info
|
||||
BerechtigungE.VERANSTALTUNG_READ // Prüfungs-Details
|
||||
// Ergebnis-Eingabe wird später als eigener Bounded Context hinzugefügt
|
||||
),
|
||||
priority = 4,
|
||||
category = RoleCategory.OFFICIAL
|
||||
)
|
||||
/**
|
||||
* Richter - Spezialisierte Bewertungs-Rolle
|
||||
*/
|
||||
val RICHTER = ReitsportRole(
|
||||
roleType = RolleE.RICHTER,
|
||||
displayName = "Richter",
|
||||
description = "Prüfungs-Bewertung und Ergebnis-Eingabe (ReadOnly-Zugriff auf Stammdaten)",
|
||||
icon = "⚖️",
|
||||
permissions = listOf(
|
||||
// Nur Lese-Zugriff auf relevante Daten
|
||||
BerechtigungE.PERSON_READ, // Starter-Info
|
||||
BerechtigungE.PFERD_READ, // Pferde-Info
|
||||
BerechtigungE.VERANSTALTUNG_READ // Prüfungs-Details
|
||||
// Ergebnis-Eingabe wird später als eigener Bounded Context hinzugefügt
|
||||
),
|
||||
priority = 4,
|
||||
category = RoleCategory.OFFICIAL
|
||||
)
|
||||
|
||||
/**
|
||||
* Tierarzt - Veterinär-Bounded-Context
|
||||
*/
|
||||
val TIERARZT = ReitsportRole(
|
||||
roleType = RolleE.TIERARZT,
|
||||
displayName = "Tierarzt",
|
||||
description = "Veterinärkontrollen und Pferde-Gesundheits-Management",
|
||||
icon = "🩺",
|
||||
permissions = listOf(
|
||||
BerechtigungE.PFERD_READ,
|
||||
BerechtigungE.PFERD_UPDATE, // Gesundheitsdaten, Vet-Checks
|
||||
BerechtigungE.PERSON_READ, // Besitzer-Kontakt
|
||||
BerechtigungE.VERANSTALTUNG_READ // Turnier-Context für Kontrollen
|
||||
),
|
||||
priority = 5,
|
||||
category = RoleCategory.OFFICIAL
|
||||
)
|
||||
/**
|
||||
* Tierarzt - Veterinär-Bounded-Context
|
||||
*/
|
||||
val TIERARZT = ReitsportRole(
|
||||
roleType = RolleE.TIERARZT,
|
||||
displayName = "Tierarzt",
|
||||
description = "Veterinärkontrollen und Pferde-Gesundheits-Management",
|
||||
icon = "🩺",
|
||||
permissions = listOf(
|
||||
BerechtigungE.PFERD_READ,
|
||||
BerechtigungE.PFERD_UPDATE, // Gesundheitsdaten, Vet-Checks
|
||||
BerechtigungE.PERSON_READ, // Besitzer-Kontakt
|
||||
BerechtigungE.VERANSTALTUNG_READ // Turnier-Context für Kontrollen
|
||||
),
|
||||
priority = 5,
|
||||
category = RoleCategory.OFFICIAL
|
||||
)
|
||||
|
||||
/**
|
||||
* Trainer - Training-Bounded-Context (zukünftig)
|
||||
*/
|
||||
val TRAINER = ReitsportRole(
|
||||
roleType = RolleE.TRAINER,
|
||||
displayName = "Trainer",
|
||||
description = "Schützlings-Betreuung und Training-Management",
|
||||
icon = "🏃♂️",
|
||||
permissions = listOf(
|
||||
BerechtigungE.PERSON_READ, // Schützlinge
|
||||
BerechtigungE.PFERD_READ, // Trainingspferde
|
||||
BerechtigungE.VERANSTALTUNG_READ // Turnier-Planung für Schützlinge
|
||||
// Training-spezifische Berechtigungen kommen später
|
||||
),
|
||||
priority = 6,
|
||||
category = RoleCategory.ACTIVE
|
||||
)
|
||||
/**
|
||||
* Trainer - Training-Bounded-Context (zukünftig)
|
||||
*/
|
||||
val TRAINER = ReitsportRole(
|
||||
roleType = RolleE.TRAINER,
|
||||
displayName = "Trainer",
|
||||
description = "Schützlings-Betreuung und Training-Management",
|
||||
icon = "🏃♂️",
|
||||
permissions = listOf(
|
||||
BerechtigungE.PERSON_READ, // Schützlinge
|
||||
BerechtigungE.PFERD_READ, // Trainingspferde
|
||||
BerechtigungE.VERANSTALTUNG_READ // Turnier-Planung für Schützlinge
|
||||
// Training-spezifische Berechtigungen kommen später
|
||||
),
|
||||
priority = 6,
|
||||
category = RoleCategory.ACTIVE
|
||||
)
|
||||
|
||||
/**
|
||||
* Reiter - Persönlicher Bounded Context
|
||||
*/
|
||||
val REITER = ReitsportRole(
|
||||
roleType = RolleE.REITER,
|
||||
displayName = "Reiter",
|
||||
description = "Persönliche Daten, eigene Pferde und Turnier-Teilnahme",
|
||||
icon = "🐎",
|
||||
permissions = listOf(
|
||||
BerechtigungE.PERSON_READ, // Nur eigene Daten
|
||||
BerechtigungE.PFERD_READ, // Nur eigene Pferde
|
||||
BerechtigungE.VERANSTALTUNG_READ // Öffentliche Turnier-Infos
|
||||
// Eigene Daten ändern: Später als PERSON_UPDATE_OWN, PFERD_UPDATE_OWN
|
||||
),
|
||||
priority = 7,
|
||||
category = RoleCategory.ACTIVE
|
||||
)
|
||||
/**
|
||||
* Reiter - Persönlicher Bounded Context
|
||||
*/
|
||||
val REITER = ReitsportRole(
|
||||
roleType = RolleE.REITER,
|
||||
displayName = "Reiter",
|
||||
description = "Persönliche Daten, eigene Pferde und Turnier-Teilnahme",
|
||||
icon = "🐎",
|
||||
permissions = listOf(
|
||||
BerechtigungE.PERSON_READ, // Nur eigene Daten
|
||||
BerechtigungE.PFERD_READ, // Nur eigene Pferde
|
||||
BerechtigungE.VERANSTALTUNG_READ // Öffentliche Turnier-Infos
|
||||
// Eigene Daten ändern: Später als PERSON_UPDATE_OWN, PFERD_UPDATE_OWN
|
||||
),
|
||||
priority = 7,
|
||||
category = RoleCategory.ACTIVE
|
||||
)
|
||||
|
||||
/**
|
||||
* Zuschauer - Public-Read-Only Bounded Context
|
||||
*/
|
||||
val ZUSCHAUER = ReitsportRole(
|
||||
roleType = RolleE.ZUSCHAUER,
|
||||
displayName = "Zuschauer",
|
||||
description = "Öffentliche Informationen: Starterlisten, Ergebnisse, Zeitpläne",
|
||||
icon = "👁️",
|
||||
permissions = listOf(
|
||||
BerechtigungE.VERANSTALTUNG_READ // Nur öffentliche Turnier-Daten
|
||||
// Später: STARTERLISTE_READ_PUBLIC, ERGEBNIS_READ_PUBLIC
|
||||
),
|
||||
priority = 8,
|
||||
category = RoleCategory.PASSIVE
|
||||
)
|
||||
/**
|
||||
* Zuschauer - Public-Read-Only Bounded Context
|
||||
*/
|
||||
val ZUSCHAUER = ReitsportRole(
|
||||
roleType = RolleE.ZUSCHAUER,
|
||||
displayName = "Zuschauer",
|
||||
description = "Öffentliche Informationen: Starterlisten, Ergebnisse, Zeitpläne",
|
||||
icon = "👁️",
|
||||
permissions = listOf(
|
||||
BerechtigungE.VERANSTALTUNG_READ // Nur öffentliche Turnier-Daten
|
||||
// Später: STARTERLISTE_READ_PUBLIC, ERGEBNIS_READ_PUBLIC
|
||||
),
|
||||
priority = 8,
|
||||
category = RoleCategory.PASSIVE
|
||||
)
|
||||
|
||||
/**
|
||||
* Gast - Keine Authentifizierung erforderlich
|
||||
*/
|
||||
val GAST = ReitsportRole(
|
||||
roleType = RolleE.GAST,
|
||||
displayName = "Gast",
|
||||
description = "Öffentliche Basis-Informationen ohne Registrierung",
|
||||
icon = "🔓",
|
||||
permissions = emptyList(), // Nur völlig öffentliche Endpunkte
|
||||
priority = 9,
|
||||
category = RoleCategory.PASSIVE
|
||||
)
|
||||
/**
|
||||
* Gast - Keine Authentifizierung erforderlich
|
||||
*/
|
||||
val GAST = ReitsportRole(
|
||||
roleType = RolleE.GAST,
|
||||
displayName = "Gast",
|
||||
description = "Öffentliche Basis-Informationen ohne Registrierung",
|
||||
icon = "🔓",
|
||||
permissions = emptyList(), // Nur völlig öffentliche Endpunkte
|
||||
priority = 9,
|
||||
category = RoleCategory.PASSIVE
|
||||
)
|
||||
|
||||
/**
|
||||
* Alle definierten Rollen in organisatorischer Reihenfolge
|
||||
*/
|
||||
val ALL_ROLES = listOf(
|
||||
ADMIN,
|
||||
VEREINS_ADMIN,
|
||||
FUNKTIONAER,
|
||||
RICHTER,
|
||||
TIERARZT,
|
||||
TRAINER,
|
||||
REITER,
|
||||
ZUSCHAUER,
|
||||
GAST
|
||||
)
|
||||
/**
|
||||
* Alle definierten Rollen in organisatorischer Reihenfolge
|
||||
*/
|
||||
val ALL_ROLES = listOf(
|
||||
ADMIN,
|
||||
VEREINS_ADMIN,
|
||||
FUNKTIONAER,
|
||||
RICHTER,
|
||||
TIERARZT,
|
||||
TRAINER,
|
||||
REITER,
|
||||
ZUSCHAUER,
|
||||
GAST
|
||||
)
|
||||
|
||||
/**
|
||||
* Rollen nach Bounded Context / Microservice gruppiert
|
||||
*/
|
||||
val ROLES_BY_BOUNDED_CONTEXT = mapOf(
|
||||
"System Management" to listOf(ADMIN),
|
||||
"Vereins-Service" to listOf(VEREINS_ADMIN),
|
||||
"Event-Service" to listOf(FUNKTIONAER),
|
||||
"Bewertungs-Service" to listOf(RICHTER),
|
||||
"Vet-Service" to listOf(TIERARZT),
|
||||
"Training-Service" to listOf(TRAINER),
|
||||
"Member-Service" to listOf(REITER),
|
||||
"Public-Service" to listOf(ZUSCHAUER, GAST)
|
||||
)
|
||||
/**
|
||||
* Rollen nach Bounded Context / Microservice gruppiert
|
||||
*/
|
||||
val ROLES_BY_BOUNDED_CONTEXT = mapOf(
|
||||
"System Management" to listOf(ADMIN),
|
||||
"Vereins-Service" to listOf(VEREINS_ADMIN),
|
||||
"Event-Service" to listOf(FUNKTIONAER),
|
||||
"Bewertungs-Service" to listOf(RICHTER),
|
||||
"Vet-Service" to listOf(TIERARZT),
|
||||
"Training-Service" to listOf(TRAINER),
|
||||
"Member-Service" to listOf(REITER),
|
||||
"Public-Service" to listOf(ZUSCHAUER, GAST)
|
||||
)
|
||||
|
||||
/**
|
||||
* Rollen nach UI-Kategorie (für Ping-Dashboard)
|
||||
*/
|
||||
val ROLES_BY_CATEGORY = ALL_ROLES.groupBy { it.category }
|
||||
/**
|
||||
* Rollen nach UI-Kategorie (für Ping-Dashboard)
|
||||
*/
|
||||
val ROLES_BY_CATEGORY = ALL_ROLES.groupBy { it.category }
|
||||
|
||||
/**
|
||||
* Hilfsfunktion: Rolle nach RolleE-Typ finden
|
||||
*/
|
||||
fun getRoleByType(roleType: RolleE): ReitsportRole? {
|
||||
return ALL_ROLES.find { it.roleType == roleType }
|
||||
}
|
||||
/**
|
||||
* Hilfsfunktion: Rolle nach RolleE-Typ finden
|
||||
*/
|
||||
fun getRoleByType(roleType: RolleE): ReitsportRole? {
|
||||
return ALL_ROLES.find { it.roleType == roleType }
|
||||
}
|
||||
|
||||
/**
|
||||
* Hilfsfunktion: Alle Rollen mit einer bestimmten Berechtigung
|
||||
*/
|
||||
fun getRolesWithPermission(permission: BerechtigungE): List<ReitsportRole> {
|
||||
return ALL_ROLES.filter { it.hasPermission(permission) }
|
||||
}
|
||||
/**
|
||||
* Hilfsfunktion: Alle Rollen mit einer bestimmten Berechtigung
|
||||
*/
|
||||
fun getRolesWithPermission(permission: BerechtigungE): List<ReitsportRole> {
|
||||
return ALL_ROLES.filter { it.hasPermission(permission) }
|
||||
}
|
||||
}
|
||||
|
||||
+156
-156
@@ -12,190 +12,190 @@ import kotlin.test.assertEquals
|
||||
|
||||
class PingApiClientTest {
|
||||
|
||||
private fun createMockApiClient(mockEngine: MockEngine): PingApiClient {
|
||||
return PingApiClient("http://localhost:8081")
|
||||
private fun createMockApiClient(mockEngine: MockEngine): PingApiClient {
|
||||
return PingApiClient("http://localhost:8081")
|
||||
}
|
||||
|
||||
@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("http://localhost:8081/api/ping/simple", request.url.toString())
|
||||
assertEquals(HttpMethod.Get, request.method)
|
||||
|
||||
respond(
|
||||
content = Json.encodeToString(PingResponse.serializer(), expectedResponse),
|
||||
status = HttpStatusCode.OK,
|
||||
headers = headersOf(HttpHeaders.ContentType, "application/json")
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `simplePing should return correct response`() = runTest {
|
||||
// Given
|
||||
val expectedResponse = PingResponse(
|
||||
status = "OK",
|
||||
timestamp = "2025-09-27T21:27:00Z",
|
||||
service = "ping-service"
|
||||
)
|
||||
// When
|
||||
val apiClient = PingApiClient("http://localhost:8081")
|
||||
// Note: This is a limitation - we can't easily inject the mock engine
|
||||
// This test demonstrates the structure but would need refactoring of PingApiClient
|
||||
// to accept HttpClient as dependency for full testability
|
||||
}
|
||||
|
||||
val mockEngine = MockEngine { request ->
|
||||
assertEquals("http://localhost:8081/api/ping/simple", request.url.toString())
|
||||
assertEquals(HttpMethod.Get, request.method)
|
||||
@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
|
||||
)
|
||||
|
||||
respond(
|
||||
content = Json.encodeToString(PingResponse.serializer(), expectedResponse),
|
||||
status = HttpStatusCode.OK,
|
||||
headers = headersOf(HttpHeaders.ContentType, "application/json")
|
||||
)
|
||||
}
|
||||
val mockEngine = MockEngine { request ->
|
||||
assertEquals("http://localhost:8081/api/ping/enhanced", request.url.encodedPath)
|
||||
assertEquals("true", request.url.parameters["simulate"])
|
||||
assertEquals(HttpMethod.Get, request.method)
|
||||
|
||||
// When
|
||||
val apiClient = PingApiClient("http://localhost:8081")
|
||||
// Note: This is a limitation - we can't easily inject the mock engine
|
||||
// This test demonstrates the structure but would need refactoring of PingApiClient
|
||||
// to accept HttpClient as dependency for full testability
|
||||
respond(
|
||||
content = Json.encodeToString(EnhancedPingResponse.serializer(), expectedResponse),
|
||||
status = HttpStatusCode.OK,
|
||||
headers = headersOf(HttpHeaders.ContentType, "application/json")
|
||||
)
|
||||
}
|
||||
|
||||
@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
|
||||
)
|
||||
// When - This test shows the intended structure
|
||||
// val apiClient = PingApiClient(httpClient = HttpClient(mockEngine))
|
||||
// val response = apiClient.enhancedPing(simulate = true)
|
||||
|
||||
val mockEngine = MockEngine { request ->
|
||||
assertEquals("http://localhost:8081/api/ping/enhanced", request.url.encodedPath)
|
||||
assertEquals("true", request.url.parameters["simulate"])
|
||||
assertEquals(HttpMethod.Get, request.method)
|
||||
// Then
|
||||
// assertEquals(expectedResponse, response)
|
||||
}
|
||||
|
||||
respond(
|
||||
content = Json.encodeToString(EnhancedPingResponse.serializer(), expectedResponse),
|
||||
status = HttpStatusCode.OK,
|
||||
headers = headersOf(HttpHeaders.ContentType, "application/json")
|
||||
)
|
||||
}
|
||||
@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
|
||||
)
|
||||
|
||||
// When - This test shows the intended structure
|
||||
// val apiClient = PingApiClient(httpClient = HttpClient(mockEngine))
|
||||
// val response = apiClient.enhancedPing(simulate = true)
|
||||
val mockEngine = MockEngine { request ->
|
||||
assertEquals("http://localhost:8081/api/ping/health", request.url.toString())
|
||||
assertEquals(HttpMethod.Get, request.method)
|
||||
|
||||
// Then
|
||||
// assertEquals(expectedResponse, response)
|
||||
respond(
|
||||
content = Json.encodeToString(HealthResponse.serializer(), expectedResponse),
|
||||
status = HttpStatusCode.OK,
|
||||
headers = headersOf(HttpHeaders.ContentType, "application/json")
|
||||
)
|
||||
}
|
||||
|
||||
@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
|
||||
)
|
||||
// When - Test structure demonstration
|
||||
// val apiClient = PingApiClient(httpClient = HttpClient(mockEngine))
|
||||
// val response = apiClient.healthCheck()
|
||||
|
||||
val mockEngine = MockEngine { request ->
|
||||
assertEquals("http://localhost:8081/api/ping/health", request.url.toString())
|
||||
assertEquals(HttpMethod.Get, request.method)
|
||||
// Then
|
||||
// assertEquals(expectedResponse, response)
|
||||
}
|
||||
|
||||
respond(
|
||||
content = Json.encodeToString(HealthResponse.serializer(), expectedResponse),
|
||||
status = HttpStatusCode.OK,
|
||||
headers = headersOf(HttpHeaders.ContentType, "application/json")
|
||||
)
|
||||
}
|
||||
|
||||
// When - Test structure demonstration
|
||||
// val apiClient = PingApiClient(httpClient = HttpClient(mockEngine))
|
||||
// val response = apiClient.healthCheck()
|
||||
|
||||
// Then
|
||||
// assertEquals(expectedResponse, response)
|
||||
@Test
|
||||
fun `API client should handle HTTP errors correctly`() = runTest {
|
||||
val mockEngine = MockEngine { request ->
|
||||
respond(
|
||||
content = """{"error": "Internal Server Error"}""",
|
||||
status = HttpStatusCode.InternalServerError,
|
||||
headers = headersOf(HttpHeaders.ContentType, "application/json")
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `API client should handle HTTP errors correctly`() = runTest {
|
||||
val mockEngine = MockEngine { request ->
|
||||
respond(
|
||||
content = """{"error": "Internal Server Error"}""",
|
||||
status = HttpStatusCode.InternalServerError,
|
||||
headers = headersOf(HttpHeaders.ContentType, "application/json")
|
||||
)
|
||||
}
|
||||
// Test structure for error handling
|
||||
// val apiClient = PingApiClient(httpClient = HttpClient(mockEngine))
|
||||
// assertFailsWith<Exception> {
|
||||
// apiClient.simplePing()
|
||||
// }
|
||||
}
|
||||
|
||||
// Test structure for error handling
|
||||
// val apiClient = PingApiClient(httpClient = HttpClient(mockEngine))
|
||||
// assertFailsWith<Exception> {
|
||||
// apiClient.simplePing()
|
||||
// }
|
||||
@Test
|
||||
fun `API client should handle network errors`() = runTest {
|
||||
val mockEngine = MockEngine { request ->
|
||||
throw Exception("Network unreachable")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `API client should handle network errors`() = runTest {
|
||||
val mockEngine = MockEngine { request ->
|
||||
throw Exception("Network unreachable")
|
||||
}
|
||||
// Test structure for network error handling
|
||||
// val apiClient = PingApiClient(httpClient = HttpClient(mockEngine))
|
||||
// assertFailsWith<Exception> {
|
||||
// apiClient.simplePing()
|
||||
// }
|
||||
}
|
||||
|
||||
// Test structure for network error handling
|
||||
// val apiClient = PingApiClient(httpClient = HttpClient(mockEngine))
|
||||
// assertFailsWith<Exception> {
|
||||
// apiClient.simplePing()
|
||||
// }
|
||||
}
|
||||
@Test
|
||||
fun `JSON serialization should work correctly`() {
|
||||
// Given
|
||||
val pingResponse = PingResponse(
|
||||
status = "OK",
|
||||
timestamp = "2025-09-27T21:27:00Z",
|
||||
service = "test-service"
|
||||
)
|
||||
|
||||
@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)
|
||||
|
||||
// When
|
||||
val json = Json.encodeToString(PingResponse.serializer(), pingResponse)
|
||||
val deserializedResponse = Json.decodeFromString(PingResponse.serializer(), json)
|
||||
// Then
|
||||
assertEquals(pingResponse, deserializedResponse)
|
||||
}
|
||||
|
||||
// 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
|
||||
)
|
||||
|
||||
@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)
|
||||
|
||||
// When
|
||||
val json = Json.encodeToString(EnhancedPingResponse.serializer(), enhancedResponse)
|
||||
val deserializedResponse = Json.decodeFromString(EnhancedPingResponse.serializer(), json)
|
||||
// Then
|
||||
assertEquals(enhancedResponse, deserializedResponse)
|
||||
}
|
||||
|
||||
// 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
|
||||
)
|
||||
|
||||
@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)
|
||||
|
||||
// When
|
||||
val json = Json.encodeToString(HealthResponse.serializer(), healthResponse)
|
||||
val deserializedResponse = Json.decodeFromString(HealthResponse.serializer(), json)
|
||||
// Then
|
||||
assertEquals(healthResponse, deserializedResponse)
|
||||
}
|
||||
|
||||
// Then
|
||||
assertEquals(healthResponse, deserializedResponse)
|
||||
}
|
||||
|
||||
// Note: The HTTP request tests above demonstrate the test structure but are commented out
|
||||
// because the current PingApiClient implementation doesn't support dependency injection
|
||||
// of HttpClient. To make these tests fully functional, PingApiClient would need to be
|
||||
// refactored to accept HttpClient as a constructor parameter:
|
||||
//
|
||||
// class PingApiClient(
|
||||
// private val baseUrl: String = "http://localhost:8081",
|
||||
// private val httpClient: HttpClient = HttpClient { ... }
|
||||
// )
|
||||
//
|
||||
// This would enable full HTTP mocking and testing capabilities.
|
||||
// Note: The HTTP request tests above demonstrate the test structure but are commented out
|
||||
// because the current PingApiClient implementation doesn't support dependency injection
|
||||
// of HttpClient. To make these tests fully functional, PingApiClient would need to be
|
||||
// refactored to accept HttpClient as a constructor parameter:
|
||||
//
|
||||
// class PingApiClient(
|
||||
// private val baseUrl: String = "http://localhost:8081",
|
||||
// private val httpClient: HttpClient = HttpClient { ... }
|
||||
// )
|
||||
//
|
||||
// This would enable full HTTP mocking and testing capabilities.
|
||||
}
|
||||
|
||||
+237
-237
@@ -10,253 +10,253 @@ import kotlin.test.*
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
class PingViewModelTest {
|
||||
|
||||
private lateinit var viewModel: PingViewModel
|
||||
private lateinit var testApiClient: TestPingApiClient
|
||||
private val testDispatcher = StandardTestDispatcher()
|
||||
private lateinit var viewModel: PingViewModel
|
||||
private lateinit var testApiClient: TestPingApiClient
|
||||
private val testDispatcher = StandardTestDispatcher()
|
||||
|
||||
@BeforeTest
|
||||
fun setup() {
|
||||
Dispatchers.setMain(testDispatcher)
|
||||
testApiClient = TestPingApiClient()
|
||||
viewModel = PingViewModel(testApiClient)
|
||||
@BeforeTest
|
||||
fun setup() {
|
||||
Dispatchers.setMain(testDispatcher)
|
||||
testApiClient = TestPingApiClient()
|
||||
viewModel = PingViewModel(testApiClient)
|
||||
}
|
||||
|
||||
@AfterTest
|
||||
fun tearDown() {
|
||||
Dispatchers.resetMain()
|
||||
testApiClient.reset()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `initial state should be empty`() {
|
||||
// Given & When - initial state
|
||||
val initialState = viewModel.uiState
|
||||
|
||||
// Then
|
||||
assertFalse(initialState.isLoading)
|
||||
assertNull(initialState.simplePingResponse)
|
||||
assertNull(initialState.enhancedPingResponse)
|
||||
assertNull(initialState.healthResponse)
|
||||
assertNull(initialState.errorMessage)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `performSimplePing should update state with success response`() = runTest(testDispatcher) {
|
||||
// Given
|
||||
val expectedResponse = PingResponse(
|
||||
status = "OK",
|
||||
timestamp = "2025-09-27T21:27:00Z",
|
||||
service = "test-service"
|
||||
)
|
||||
testApiClient.simplePingResponse = expectedResponse
|
||||
|
||||
// When
|
||||
viewModel.performSimplePing()
|
||||
testDispatcher.scheduler.advanceUntilIdle()
|
||||
|
||||
// Then
|
||||
val finalState = viewModel.uiState
|
||||
assertFalse(finalState.isLoading)
|
||||
assertEquals(expectedResponse, finalState.simplePingResponse)
|
||||
assertNull(finalState.errorMessage)
|
||||
assertTrue(testApiClient.simplePingCalled)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `performSimplePing should set loading state during execution`() = runTest(testDispatcher) {
|
||||
// Given
|
||||
testApiClient.simulateDelay = true
|
||||
testApiClient.delayMs = 100
|
||||
|
||||
// When
|
||||
viewModel.performSimplePing()
|
||||
testDispatcher.scheduler.advanceTimeBy(1) // Allow the coroutine to start
|
||||
|
||||
// Then - should be loading during execution
|
||||
assertTrue(viewModel.uiState.isLoading)
|
||||
assertNull(viewModel.uiState.errorMessage)
|
||||
|
||||
// When - complete the operation
|
||||
testDispatcher.scheduler.advanceUntilIdle()
|
||||
|
||||
// Then - should not be loading anymore
|
||||
assertFalse(viewModel.uiState.isLoading)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `performSimplePing should handle error and update state`() = runTest(testDispatcher) {
|
||||
// Given
|
||||
val errorMessage = "Network error"
|
||||
testApiClient.shouldThrowException = true
|
||||
testApiClient.exceptionMessage = errorMessage
|
||||
|
||||
// When
|
||||
viewModel.performSimplePing()
|
||||
testDispatcher.scheduler.advanceUntilIdle()
|
||||
|
||||
// Then
|
||||
val finalState = viewModel.uiState
|
||||
assertFalse(finalState.isLoading)
|
||||
assertNull(finalState.simplePingResponse)
|
||||
assertEquals("Simple ping failed: $errorMessage", finalState.errorMessage)
|
||||
assertTrue(testApiClient.simplePingCalled)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `performEnhancedPing should update state with success response`() = runTest(testDispatcher) {
|
||||
// Given
|
||||
val expectedResponse = EnhancedPingResponse(
|
||||
status = "OK",
|
||||
timestamp = "2025-09-27T21:27:00Z",
|
||||
service = "test-service",
|
||||
circuitBreakerState = "CLOSED",
|
||||
responseTime = 42L
|
||||
)
|
||||
testApiClient.enhancedPingResponse = expectedResponse
|
||||
|
||||
// When
|
||||
viewModel.performEnhancedPing(simulate = false)
|
||||
testDispatcher.scheduler.advanceUntilIdle()
|
||||
|
||||
// Then
|
||||
val finalState = viewModel.uiState
|
||||
assertFalse(finalState.isLoading)
|
||||
assertEquals(expectedResponse, finalState.enhancedPingResponse)
|
||||
assertNull(finalState.errorMessage)
|
||||
assertEquals(false, testApiClient.enhancedPingCalledWith)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `performEnhancedPing should handle simulate parameter correctly`() = runTest(testDispatcher) {
|
||||
// When
|
||||
viewModel.performEnhancedPing(simulate = true)
|
||||
testDispatcher.scheduler.advanceUntilIdle()
|
||||
|
||||
// Then
|
||||
assertEquals(true, testApiClient.enhancedPingCalledWith)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `performEnhancedPing should handle error and update state`() = runTest(testDispatcher) {
|
||||
// Given
|
||||
val errorMessage = "Enhanced ping error"
|
||||
testApiClient.shouldThrowException = true
|
||||
testApiClient.exceptionMessage = errorMessage
|
||||
|
||||
// When
|
||||
viewModel.performEnhancedPing()
|
||||
testDispatcher.scheduler.advanceUntilIdle()
|
||||
|
||||
// Then
|
||||
val finalState = viewModel.uiState
|
||||
assertFalse(finalState.isLoading)
|
||||
assertNull(finalState.enhancedPingResponse)
|
||||
assertEquals("Enhanced ping failed: $errorMessage", finalState.errorMessage)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `performHealthCheck should update state with success response`() = runTest(testDispatcher) {
|
||||
// Given
|
||||
val expectedResponse = HealthResponse(
|
||||
status = "UP",
|
||||
timestamp = "2025-09-27T21:27:00Z",
|
||||
service = "test-service",
|
||||
healthy = true
|
||||
)
|
||||
testApiClient.healthResponse = expectedResponse
|
||||
|
||||
// When
|
||||
viewModel.performHealthCheck()
|
||||
testDispatcher.scheduler.advanceUntilIdle()
|
||||
|
||||
// Then
|
||||
val finalState = viewModel.uiState
|
||||
assertFalse(finalState.isLoading)
|
||||
assertEquals(expectedResponse, finalState.healthResponse)
|
||||
assertNull(finalState.errorMessage)
|
||||
assertTrue(testApiClient.healthCheckCalled)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `performHealthCheck should handle error and update state`() = runTest(testDispatcher) {
|
||||
// Given
|
||||
val errorMessage = "Health check error"
|
||||
testApiClient.shouldThrowException = true
|
||||
testApiClient.exceptionMessage = errorMessage
|
||||
|
||||
// When
|
||||
viewModel.performHealthCheck()
|
||||
testDispatcher.scheduler.advanceUntilIdle()
|
||||
|
||||
// Then
|
||||
val finalState = viewModel.uiState
|
||||
assertFalse(finalState.isLoading)
|
||||
assertNull(finalState.healthResponse)
|
||||
assertEquals("Health check failed: $errorMessage", finalState.errorMessage)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `clearError should remove error message from state`() {
|
||||
// Given - set up an error state by simulating an error
|
||||
testApiClient.shouldThrowException = true
|
||||
runTest(testDispatcher) {
|
||||
viewModel.performSimplePing()
|
||||
testDispatcher.scheduler.advanceUntilIdle()
|
||||
}
|
||||
|
||||
@AfterTest
|
||||
fun tearDown() {
|
||||
Dispatchers.resetMain()
|
||||
testApiClient.reset()
|
||||
}
|
||||
// Verify error is present
|
||||
assertNotNull(viewModel.uiState.errorMessage)
|
||||
|
||||
@Test
|
||||
fun `initial state should be empty`() {
|
||||
// Given & When - initial state
|
||||
val initialState = viewModel.uiState
|
||||
// When
|
||||
viewModel.clearError()
|
||||
|
||||
// Then
|
||||
assertFalse(initialState.isLoading)
|
||||
assertNull(initialState.simplePingResponse)
|
||||
assertNull(initialState.enhancedPingResponse)
|
||||
assertNull(initialState.healthResponse)
|
||||
assertNull(initialState.errorMessage)
|
||||
}
|
||||
// Then
|
||||
assertNull(viewModel.uiState.errorMessage)
|
||||
assertFalse(viewModel.uiState.isLoading)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `performSimplePing should update state with success response`() = runTest(testDispatcher) {
|
||||
// Given
|
||||
val expectedResponse = PingResponse(
|
||||
status = "OK",
|
||||
timestamp = "2025-09-27T21:27:00Z",
|
||||
service = "test-service"
|
||||
)
|
||||
testApiClient.simplePingResponse = expectedResponse
|
||||
@Test
|
||||
fun `multiple operations should clear previous error messages`() = runTest(testDispatcher) {
|
||||
// Given - first operation fails
|
||||
testApiClient.shouldThrowException = true
|
||||
viewModel.performSimplePing()
|
||||
testDispatcher.scheduler.advanceUntilIdle()
|
||||
assertNotNull(viewModel.uiState.errorMessage)
|
||||
|
||||
// When
|
||||
viewModel.performSimplePing()
|
||||
testDispatcher.scheduler.advanceUntilIdle()
|
||||
// When - second operation succeeds
|
||||
testApiClient.shouldThrowException = false
|
||||
val successResponse = PingResponse("SUCCESS", "2025-09-27T21:27:00Z", "test-service")
|
||||
testApiClient.simplePingResponse = successResponse
|
||||
viewModel.performSimplePing()
|
||||
testDispatcher.scheduler.advanceUntilIdle()
|
||||
|
||||
// Then
|
||||
val finalState = viewModel.uiState
|
||||
assertFalse(finalState.isLoading)
|
||||
assertEquals(expectedResponse, finalState.simplePingResponse)
|
||||
assertNull(finalState.errorMessage)
|
||||
assertTrue(testApiClient.simplePingCalled)
|
||||
}
|
||||
// Then - error should be cleared
|
||||
assertNull(viewModel.uiState.errorMessage)
|
||||
assertEquals(successResponse, viewModel.uiState.simplePingResponse)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `performSimplePing should set loading state during execution`() = runTest(testDispatcher) {
|
||||
// Given
|
||||
testApiClient.simulateDelay = true
|
||||
testApiClient.delayMs = 100
|
||||
@Test
|
||||
fun `loading state should be false after successful operation`() = runTest(testDispatcher) {
|
||||
// Given
|
||||
viewModel.performSimplePing()
|
||||
testDispatcher.scheduler.advanceUntilIdle()
|
||||
|
||||
// When
|
||||
viewModel.performSimplePing()
|
||||
testDispatcher.scheduler.advanceTimeBy(1) // Allow the coroutine to start
|
||||
// Then
|
||||
assertFalse(viewModel.uiState.isLoading)
|
||||
}
|
||||
|
||||
// Then - should be loading during execution
|
||||
assertTrue(viewModel.uiState.isLoading)
|
||||
assertNull(viewModel.uiState.errorMessage)
|
||||
@Test
|
||||
fun `all operations should call respective API methods`() = runTest(testDispatcher) {
|
||||
// When
|
||||
viewModel.performSimplePing()
|
||||
viewModel.performEnhancedPing(true)
|
||||
viewModel.performHealthCheck()
|
||||
testDispatcher.scheduler.advanceUntilIdle()
|
||||
|
||||
// When - complete the operation
|
||||
testDispatcher.scheduler.advanceUntilIdle()
|
||||
|
||||
// Then - should not be loading anymore
|
||||
assertFalse(viewModel.uiState.isLoading)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `performSimplePing should handle error and update state`() = runTest(testDispatcher) {
|
||||
// Given
|
||||
val errorMessage = "Network error"
|
||||
testApiClient.shouldThrowException = true
|
||||
testApiClient.exceptionMessage = errorMessage
|
||||
|
||||
// When
|
||||
viewModel.performSimplePing()
|
||||
testDispatcher.scheduler.advanceUntilIdle()
|
||||
|
||||
// Then
|
||||
val finalState = viewModel.uiState
|
||||
assertFalse(finalState.isLoading)
|
||||
assertNull(finalState.simplePingResponse)
|
||||
assertEquals("Simple ping failed: $errorMessage", finalState.errorMessage)
|
||||
assertTrue(testApiClient.simplePingCalled)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `performEnhancedPing should update state with success response`() = runTest(testDispatcher) {
|
||||
// Given
|
||||
val expectedResponse = EnhancedPingResponse(
|
||||
status = "OK",
|
||||
timestamp = "2025-09-27T21:27:00Z",
|
||||
service = "test-service",
|
||||
circuitBreakerState = "CLOSED",
|
||||
responseTime = 42L
|
||||
)
|
||||
testApiClient.enhancedPingResponse = expectedResponse
|
||||
|
||||
// When
|
||||
viewModel.performEnhancedPing(simulate = false)
|
||||
testDispatcher.scheduler.advanceUntilIdle()
|
||||
|
||||
// Then
|
||||
val finalState = viewModel.uiState
|
||||
assertFalse(finalState.isLoading)
|
||||
assertEquals(expectedResponse, finalState.enhancedPingResponse)
|
||||
assertNull(finalState.errorMessage)
|
||||
assertEquals(false, testApiClient.enhancedPingCalledWith)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `performEnhancedPing should handle simulate parameter correctly`() = runTest(testDispatcher) {
|
||||
// When
|
||||
viewModel.performEnhancedPing(simulate = true)
|
||||
testDispatcher.scheduler.advanceUntilIdle()
|
||||
|
||||
// Then
|
||||
assertEquals(true, testApiClient.enhancedPingCalledWith)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `performEnhancedPing should handle error and update state`() = runTest(testDispatcher) {
|
||||
// Given
|
||||
val errorMessage = "Enhanced ping error"
|
||||
testApiClient.shouldThrowException = true
|
||||
testApiClient.exceptionMessage = errorMessage
|
||||
|
||||
// When
|
||||
viewModel.performEnhancedPing()
|
||||
testDispatcher.scheduler.advanceUntilIdle()
|
||||
|
||||
// Then
|
||||
val finalState = viewModel.uiState
|
||||
assertFalse(finalState.isLoading)
|
||||
assertNull(finalState.enhancedPingResponse)
|
||||
assertEquals("Enhanced ping failed: $errorMessage", finalState.errorMessage)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `performHealthCheck should update state with success response`() = runTest(testDispatcher) {
|
||||
// Given
|
||||
val expectedResponse = HealthResponse(
|
||||
status = "UP",
|
||||
timestamp = "2025-09-27T21:27:00Z",
|
||||
service = "test-service",
|
||||
healthy = true
|
||||
)
|
||||
testApiClient.healthResponse = expectedResponse
|
||||
|
||||
// When
|
||||
viewModel.performHealthCheck()
|
||||
testDispatcher.scheduler.advanceUntilIdle()
|
||||
|
||||
// Then
|
||||
val finalState = viewModel.uiState
|
||||
assertFalse(finalState.isLoading)
|
||||
assertEquals(expectedResponse, finalState.healthResponse)
|
||||
assertNull(finalState.errorMessage)
|
||||
assertTrue(testApiClient.healthCheckCalled)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `performHealthCheck should handle error and update state`() = runTest(testDispatcher) {
|
||||
// Given
|
||||
val errorMessage = "Health check error"
|
||||
testApiClient.shouldThrowException = true
|
||||
testApiClient.exceptionMessage = errorMessage
|
||||
|
||||
// When
|
||||
viewModel.performHealthCheck()
|
||||
testDispatcher.scheduler.advanceUntilIdle()
|
||||
|
||||
// Then
|
||||
val finalState = viewModel.uiState
|
||||
assertFalse(finalState.isLoading)
|
||||
assertNull(finalState.healthResponse)
|
||||
assertEquals("Health check failed: $errorMessage", finalState.errorMessage)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `clearError should remove error message from state`() {
|
||||
// Given - set up an error state by simulating an error
|
||||
testApiClient.shouldThrowException = true
|
||||
runTest(testDispatcher) {
|
||||
viewModel.performSimplePing()
|
||||
testDispatcher.scheduler.advanceUntilIdle()
|
||||
}
|
||||
|
||||
// Verify error is present
|
||||
assertNotNull(viewModel.uiState.errorMessage)
|
||||
|
||||
// When
|
||||
viewModel.clearError()
|
||||
|
||||
// Then
|
||||
assertNull(viewModel.uiState.errorMessage)
|
||||
assertFalse(viewModel.uiState.isLoading)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `multiple operations should clear previous error messages`() = runTest(testDispatcher) {
|
||||
// Given - first operation fails
|
||||
testApiClient.shouldThrowException = true
|
||||
viewModel.performSimplePing()
|
||||
testDispatcher.scheduler.advanceUntilIdle()
|
||||
assertNotNull(viewModel.uiState.errorMessage)
|
||||
|
||||
// When - second operation succeeds
|
||||
testApiClient.shouldThrowException = false
|
||||
val successResponse = PingResponse("SUCCESS", "2025-09-27T21:27:00Z", "test-service")
|
||||
testApiClient.simplePingResponse = successResponse
|
||||
viewModel.performSimplePing()
|
||||
testDispatcher.scheduler.advanceUntilIdle()
|
||||
|
||||
// Then - error should be cleared
|
||||
assertNull(viewModel.uiState.errorMessage)
|
||||
assertEquals(successResponse, viewModel.uiState.simplePingResponse)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `loading state should be false after successful operation`() = runTest(testDispatcher) {
|
||||
// Given
|
||||
viewModel.performSimplePing()
|
||||
testDispatcher.scheduler.advanceUntilIdle()
|
||||
|
||||
// Then
|
||||
assertFalse(viewModel.uiState.isLoading)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `all operations should call respective API methods`() = runTest(testDispatcher) {
|
||||
// When
|
||||
viewModel.performSimplePing()
|
||||
viewModel.performEnhancedPing(true)
|
||||
viewModel.performHealthCheck()
|
||||
testDispatcher.scheduler.advanceUntilIdle()
|
||||
|
||||
// Then
|
||||
assertTrue(testApiClient.simplePingCalled)
|
||||
assertEquals(true, testApiClient.enhancedPingCalledWith)
|
||||
assertTrue(testApiClient.healthCheckCalled)
|
||||
assertEquals(3, testApiClient.callCount)
|
||||
}
|
||||
// Then
|
||||
assertTrue(testApiClient.simplePingCalled)
|
||||
assertEquals(true, testApiClient.enhancedPingCalledWith)
|
||||
assertTrue(testApiClient.healthCheckCalled)
|
||||
assertEquals(3, testApiClient.callCount)
|
||||
}
|
||||
}
|
||||
|
||||
+78
-78
@@ -11,95 +11,95 @@ import at.mocode.ping.api.HealthResponse
|
||||
*/
|
||||
class TestPingApiClient : PingApi {
|
||||
|
||||
// Test configuration properties
|
||||
var shouldThrowException = false
|
||||
var exceptionMessage = "Test exception"
|
||||
var simulateDelay = false
|
||||
var delayMs = 100L
|
||||
// Test configuration properties
|
||||
var shouldThrowException = false
|
||||
var exceptionMessage = "Test exception"
|
||||
var simulateDelay = false
|
||||
var delayMs = 100L
|
||||
|
||||
// Response configuration
|
||||
var simplePingResponse: PingResponse? = null
|
||||
var enhancedPingResponse: EnhancedPingResponse? = null
|
||||
var healthResponse: HealthResponse? = null
|
||||
// Response configuration
|
||||
var simplePingResponse: PingResponse? = null
|
||||
var enhancedPingResponse: EnhancedPingResponse? = null
|
||||
var healthResponse: HealthResponse? = null
|
||||
|
||||
// Call tracking
|
||||
var simplePingCalled = false
|
||||
var enhancedPingCalledWith: Boolean? = null
|
||||
var healthCheckCalled = false
|
||||
var callCount = 0
|
||||
// Call tracking
|
||||
var simplePingCalled = false
|
||||
var enhancedPingCalledWith: Boolean? = null
|
||||
var healthCheckCalled = false
|
||||
var callCount = 0
|
||||
|
||||
override suspend fun simplePing(): PingResponse {
|
||||
simplePingCalled = true
|
||||
callCount++
|
||||
override suspend fun simplePing(): PingResponse {
|
||||
simplePingCalled = true
|
||||
callCount++
|
||||
|
||||
if (simulateDelay) {
|
||||
kotlinx.coroutines.delay(delayMs)
|
||||
}
|
||||
|
||||
if (shouldThrowException) {
|
||||
throw Exception(exceptionMessage)
|
||||
}
|
||||
|
||||
return simplePingResponse ?: PingResponse(
|
||||
status = "OK",
|
||||
timestamp = "2025-09-27T21:27:00Z",
|
||||
service = "test-ping-service"
|
||||
)
|
||||
if (simulateDelay) {
|
||||
kotlinx.coroutines.delay(delayMs)
|
||||
}
|
||||
|
||||
override suspend fun enhancedPing(simulate: Boolean): EnhancedPingResponse {
|
||||
enhancedPingCalledWith = simulate
|
||||
callCount++
|
||||
|
||||
if (simulateDelay) {
|
||||
kotlinx.coroutines.delay(delayMs)
|
||||
}
|
||||
|
||||
if (shouldThrowException) {
|
||||
throw Exception(exceptionMessage)
|
||||
}
|
||||
|
||||
return enhancedPingResponse ?: EnhancedPingResponse(
|
||||
status = "OK",
|
||||
timestamp = "2025-09-27T21:27:00Z",
|
||||
service = "test-ping-service",
|
||||
circuitBreakerState = "CLOSED",
|
||||
responseTime = 42L
|
||||
)
|
||||
if (shouldThrowException) {
|
||||
throw Exception(exceptionMessage)
|
||||
}
|
||||
|
||||
override suspend fun healthCheck(): HealthResponse {
|
||||
healthCheckCalled = true
|
||||
callCount++
|
||||
return simplePingResponse ?: PingResponse(
|
||||
status = "OK",
|
||||
timestamp = "2025-09-27T21:27:00Z",
|
||||
service = "test-ping-service"
|
||||
)
|
||||
}
|
||||
|
||||
if (simulateDelay) {
|
||||
kotlinx.coroutines.delay(delayMs)
|
||||
}
|
||||
override suspend fun enhancedPing(simulate: Boolean): EnhancedPingResponse {
|
||||
enhancedPingCalledWith = simulate
|
||||
callCount++
|
||||
|
||||
if (shouldThrowException) {
|
||||
throw Exception(exceptionMessage)
|
||||
}
|
||||
|
||||
return healthResponse ?: HealthResponse(
|
||||
status = "UP",
|
||||
timestamp = "2025-09-27T21:27:00Z",
|
||||
service = "test-ping-service",
|
||||
healthy = true
|
||||
)
|
||||
if (simulateDelay) {
|
||||
kotlinx.coroutines.delay(delayMs)
|
||||
}
|
||||
|
||||
// Test utilities
|
||||
fun reset() {
|
||||
shouldThrowException = false
|
||||
exceptionMessage = "Test exception"
|
||||
simulateDelay = false
|
||||
delayMs = 100L
|
||||
simplePingResponse = null
|
||||
enhancedPingResponse = null
|
||||
healthResponse = null
|
||||
simplePingCalled = false
|
||||
enhancedPingCalledWith = null
|
||||
healthCheckCalled = false
|
||||
callCount = 0
|
||||
if (shouldThrowException) {
|
||||
throw Exception(exceptionMessage)
|
||||
}
|
||||
|
||||
return enhancedPingResponse ?: EnhancedPingResponse(
|
||||
status = "OK",
|
||||
timestamp = "2025-09-27T21:27:00Z",
|
||||
service = "test-ping-service",
|
||||
circuitBreakerState = "CLOSED",
|
||||
responseTime = 42L
|
||||
)
|
||||
}
|
||||
|
||||
override suspend fun healthCheck(): HealthResponse {
|
||||
healthCheckCalled = true
|
||||
callCount++
|
||||
|
||||
if (simulateDelay) {
|
||||
kotlinx.coroutines.delay(delayMs)
|
||||
}
|
||||
|
||||
if (shouldThrowException) {
|
||||
throw Exception(exceptionMessage)
|
||||
}
|
||||
|
||||
return healthResponse ?: HealthResponse(
|
||||
status = "UP",
|
||||
timestamp = "2025-09-27T21:27:00Z",
|
||||
service = "test-ping-service",
|
||||
healthy = true
|
||||
)
|
||||
}
|
||||
|
||||
// Test utilities
|
||||
fun reset() {
|
||||
shouldThrowException = false
|
||||
exceptionMessage = "Test exception"
|
||||
simulateDelay = false
|
||||
delayMs = 100L
|
||||
simplePingResponse = null
|
||||
enhancedPingResponse = null
|
||||
healthResponse = null
|
||||
simplePingCalled = false
|
||||
enhancedPingCalledWith = null
|
||||
healthCheckCalled = false
|
||||
callCount = 0
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user