fix(server, shared): TODO

This commit is contained in:
2025-05-07 01:34:50 +02:00
parent d74b47cbf5
commit d97df11832
55 changed files with 4338 additions and 735 deletions
-134
View File
@@ -1,134 +0,0 @@
# Projektanalyse: Meldestelle
## Projektübersicht
Dieses Projekt ist eine Kotlin Multiplatform-Anwendung, die aus drei Hauptmodulen besteht:
1. **shared** - Gemeinsam genutzte Klassen und Funktionen für alle Plattformen
2. **server** - Ktor-basierter Backend-Server mit PostgreSQL-Datenbankanbindung
3. **composeApp** - Compose Multiplatform UI-Anwendung für Desktop und Web (WASM/JS)
## Shared Modul
### Gemeinsame Klassen und Interfaces
- **Platform** (Interface)
- Eigenschaften: `name: String`
- Zweck: Abstraktion für plattformspezifische Implementierungen
- **Greeting** (Klasse)
- Methoden: `greet(): String`
- Zweck: Einfache Beispielklasse, die eine plattformspezifische Begrüßung zurückgibt
- **Constants** (Klasse)
- Aktuell leer, vermutlich für zukünftige Konstanten vorgesehen
### Datenmodelle
- **Turnier** (Data Class)
- Eigenschaften:
- `id: String` - Eindeutige ID für das Turnier
- `name: String` - Name des Turniers
- `datum: String` - Datum oder Zeitraum als Text
- `logoUrl: String?` - Optionaler Link zum Logo
- `ausschreibungUrl: String?` - Optionaler Link zur Ausschreibungs-PDF
- Annotationen: `@Serializable` für JSON-Serialisierung
- **Nennung** (Data Class)
- Eigenschaften:
- `turnierId: String` - Referenz zum zugehörigen Turnier
- `riderName: String` - Name des Reiters
- `horseName: String` - Name des Pferdes
- `email: String` - E-Mail-Adresse
- `comments: String?` - Optionale Kommentare
- Annotationen: `@Serializable` für JSON-Serialisierung
### Plattformspezifische Implementierungen
- **JVMPlatform** (Klasse, JVM-spezifisch)
- Implementiert: `Platform`
- Eigenschaften: `name = "Java ${System.getProperty("java.version")}"`
- **WasmPlatform** (Klasse, WASM/JS-spezifisch)
- Implementiert: `Platform`
- Eigenschaften: `name = "Web with Kotlin/Wasm"`
- **getPlatform()** (Expect/Actual Funktion)
- Rückgabetyp: `Platform`
- Implementierungen für JVM und WASM/JS
## Server Modul
### Hauptanwendung
- **main** (Funktion)
- Parameter: `args: Array<String>`
- Zweck: Startet den Ktor-Server mit Netty-Engine
- **module** (Erweiterungsfunktion für Application)
- Konfiguriert die Datenbank
- Definiert Routing:
- GET "/" - Zeigt eine HTML-Seite mit Turnieren aus der Datenbank
### Plugins
- **configureDatabase** (Funktion)
- Konfiguriert die Datenbankverbindung mit HikariCP
- Liest Konfiguration aus Umgebungsvariablen
- Initialisiert das Datenbankschema
### Datenbanktabellen
- **TurniereTable** (Object, erbt von Table)
- Tabellenname: "turniere"
- Spalten:
- `id: Column<String>` - Primärschlüssel
- `name: Column<String>` - Name des Turniers
- `datum: Column<String>` - Datum als Text
- `logoUrl: Column<String?>` - Optionaler Logo-URL
- `ausschreibungUrl: Column<String?>` - Optionaler Ausschreibungs-URL
### Tests
- **ApplicationTest** (Klasse)
- Testmethoden:
- `testRootRouteShowsTournamentList()` - Testet die Root-Route und Datenbankinteraktion
- Prüft HTTP-Status, HTML-Inhalt und Anzeige von Turnierdaten
## ComposeApp Modul
### UI-Komponenten
- **App** (Composable Funktion)
- Einfache UI mit Button, der bei Klick einen Begrüßungstext und das Compose-Logo anzeigt
- Verwendet die Greeting-Klasse aus dem Shared-Modul
### Plattformspezifische Implementierungen
- **Desktop** (JVM)
- Verwendet Compose for Desktop's Window API
- Setzt Fenstertitel auf "Meldestelle"
- **Web** (WASM/JS)
- Verwendet Compose für Web
- Generiert composeApp.js für Browser-Ausführung
## Datenbankintegration
- **PostgreSQL** mit **Exposed ORM**
- Verbindungspooling mit HikariCP
- Transaktionsbasierte Datenbankoperationen
- Schema-Initialisierung mit SchemaUtils
## Stärken und Verbesserungspotenzial
### Stärken
1. **Multiplatform-Architektur**: Effektive Codewiederverwendung zwischen Plattformen
2. **Datenbankintegration**: Solide Implementierung mit Connection Pooling und ORM
3. **Modularisierung**: Klare Trennung zwischen Shared, Server und UI-Code
4. **Serialisierung**: Konsistente Datenmodelle mit kotlinx.serialization
### Verbesserungspotenzial
1. **Testabdeckung**: Bisher nur grundlegende Tests für den Server
2. **Fehlerbehandlung**: Minimale Fehlerbehandlung in Datenbankoperationen
3. **Dokumentation**: Begrenzte Inline-Dokumentation
4. **Client-Server-Kommunikation**: Noch keine API-Endpunkte für CRUD-Operationen
## Zusammenfassung
Das Projekt implementiert eine Multiplatform-Anwendung für die Verwaltung von Turnieren und Nennungen. Es besteht aus:
- **5 Klassen**: Greeting, JVMPlatform, WasmPlatform, Turnier, Nennung
- **1 Interface**: Platform
- **1 Datenbanktabelle**: TurniereTable
- **4 Hauptfunktionen**: main, module, configureDatabase, App
- **1 Testklasse** mit 1 Testmethode
Die Anwendung befindet sich in einem frühen Entwicklungsstadium mit grundlegender Funktionalität für die Anzeige von Turnieren aus einer Datenbank. Die Modellklassen und Datenbankstruktur sind für zukünftige Erweiterungen vorbereitet, wie in den Kommentaren im Code angedeutet.
+4 -4
View File
@@ -1,8 +1,8 @@
plugins {
// this is necessary to avoid the plugins to be loaded multiple times
// in each subproject's classloader
alias(libs.plugins.composeMultiplatform) apply false
alias(libs.plugins.composeCompiler) apply false
alias(libs.plugins.kotlinJvm) apply false
alias(libs.plugins.kotlinMultiplatform) apply false
}
alias(libs.plugins.composeMultiplatform) apply false
alias(libs.plugins.kotlinJvm) apply false
alias(libs.plugins.composeCompiler) apply false
}
+5 -4
View File
@@ -1,3 +1,5 @@
@file:OptIn(ExperimentalWasmDsl::class)
import org.jetbrains.compose.desktop.application.dsl.TargetFormat
import org.jetbrains.kotlin.gradle.ExperimentalWasmDsl
import org.jetbrains.kotlin.gradle.targets.js.webpack.KotlinWebpackConfig
@@ -10,8 +12,7 @@ plugins {
kotlin {
jvm("desktop")
@OptIn(ExperimentalWasmDsl::class)
wasmJs {
outputModuleName = "composeApp"
browser {
@@ -30,10 +31,10 @@ kotlin {
}
binaries.executable()
}
sourceSets {
val desktopMain by getting
commonMain.dependencies {
implementation(compose.runtime)
implementation(compose.foundation)
@@ -1,4 +1,4 @@
package at.mocode
package at.mocode.server
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.foundation.Image
@@ -34,4 +34,4 @@ fun App() {
}
}
}
}
}
@@ -1,4 +1,4 @@
package at.mocode
package at.mocode.server
import androidx.compose.ui.window.Window
import androidx.compose.ui.window.application
@@ -10,4 +10,4 @@ fun main() = application {
) {
App()
}
}
}
@@ -1,4 +1,4 @@
package at.mocode
package at.mocode.server
import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.compose.ui.window.ComposeViewport
@@ -9,4 +9,4 @@ fun main() {
ComposeViewport(document.body!!) {
App()
}
}
}
+9 -2
View File
@@ -4,6 +4,7 @@ kotlin = "2.1.20"
kotlinx-coroutines = "1.10.1"
kotlinx-serialization = "1.8.1"
kotlinx-datetime = "0.6.1"
compose-plugin = "2.1.20"
# UI frameworks
compose-multiplatform = "1.7.3"
@@ -28,6 +29,7 @@ junit-jupiter = "5.12.0"
# Utilities
uuid = "0.8.4"
bignum = "0.3.10"
#junit-jupiter-version = "5.8.1"
[libraries]
# Kotlin and related libraries
@@ -67,11 +69,16 @@ junit-jupiter = { group = "org.junit.jupiter", name = "junit-jupiter", version.r
# Utilities
uuid = { group = "com.benasher44", name = "uuid", version.ref = "uuid" }
bignum = { group = "com.ionspin.kotlin", name = "bignum", version.ref = "bignum" }
#jupiter-junit-jupiter = { group = "org.junit.jupiter", name = "junit-jupiter", version.ref = "junit-jupiter-version" }
[plugins]
kotlinJvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" }
kotlinMultiplatform = { id = "org.jetbrains.kotlin.multiplatform", version.ref = "kotlin" }
kotlinSerialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin" }
composeMultiplatform = { id = "org.jetbrains.compose", version.ref = "compose-multiplatform" }
composeCompiler = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" }
ktor = { id = "io.ktor.plugin", version.ref = "ktor" }
jetbrainsCompose = { id = "org.jetbrains.compose", version.ref = "compose-plugin" }
kotlinMultiplatform = { id = "org.jetbrains.kotlin.multiplatform", version.ref = "kotlin" }
composeCompiler = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" }
+60 -20
View File
@@ -1,3 +1,5 @@
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
plugins {
alias(libs.plugins.kotlinJvm)
alias(libs.plugins.ktor)
@@ -6,41 +8,79 @@ plugins {
group = "at.mocode"
version = "1.0.0"
// Enable Gradle caching and parallel execution for better build performance
tasks.withType<org.jetbrains.kotlin.gradle.tasks.KotlinCompile> {
compilerOptions {
jvmTarget.set(JvmTarget.JVM_21) // Set appropriate JVM target
freeCompilerArgs.set(listOf("-Xjsr305=strict", "-opt-in=kotlin.RequiresOptIn"))
}
}
// Configure application
application {
mainClass.set("at.mocode.ApplicationKt")
applicationDefaultJvmArgs = listOf("-Dio.ktor.development=${extra["io.ktor.development"] ?: "false"}")
mainClass.set("at.mocode.server.ApplicationKt")
applicationDefaultJvmArgs = listOf(
"-Dio.ktor.development=${extra["io.ktor.development"] ?: "false"}",
// "-XX:+UseG1GC", // Use G1 Garbage Collector
// "-XX:MaxGCPauseMillis=100", // Target max GC pause time
// "-Djava.awt.headless=true" // Headless mode for server
)
}
// Configure tests
tasks.withType<Test> {
useJUnitPlatform() // Use JUnit 5 platform
testLogging {
events("passed", "skipped", "failed")
}
// Parallel test execution if tests are independent
maxParallelForks = (Runtime.getRuntime().availableProcessors() / 2).takeIf { it > 0 } ?: 1
}
dependencies {
implementation(project(":shared"))
implementation(libs.logback)
implementation(libs.ktor.server.core)
implementation(libs.ktor.server.netty)
implementation(libs.ktor.server.config.yaml)
implementation(libs.ktor.server.html.builder)
// Project dependencies
implementation(projects.shared)
// Kotlin and related libraries
implementation(libs.kotlinx.serialization.json)
implementation(libs.kotlinx.datetime)
implementation(libs.uuid)
implementation(libs.bignum)
testImplementation(libs.ktor.server.tests)
testImplementation(libs.kotlin.test.junit)
testImplementation(libs.junit.jupiter)
// Ktor server components
implementation(libs.ktor.server.core)
implementation(libs.ktor.server.netty)
implementation(libs.ktor.server.config.yaml)
implementation(libs.ktor.server.html.builder)
// Exposed für Datenbankzugriff (Core, DAO-Pattern, JDBC-Implementierung)
// Ktor server plugins
implementation("io.ktor:ktor-server-content-negotiation:${libs.versions.ktor.get()}")
implementation("io.ktor:ktor-serialization-kotlinx-json:${libs.versions.ktor.get()}")
implementation("io.ktor:ktor-server-cors:${libs.versions.ktor.get()}")
implementation("io.ktor:ktor-server-call-logging:${libs.versions.ktor.get()}")
implementation("io.ktor:ktor-server-default-headers:${libs.versions.ktor.get()}")
implementation("io.ktor:ktor-server-status-pages:${libs.versions.ktor.get()}")
implementation("io.ktor:ktor-server-auth:${libs.versions.ktor.get()}")
implementation("io.ktor:ktor-server-auth-jwt:${libs.versions.ktor.get()}")
// Database - Exposed ORM
implementation(libs.exposed.core)
implementation(libs.exposed.dao)
implementation(libs.exposed.jdbc)
implementation(libs.exposed.kotlin.datetime)
// JDBC Treiber für PostgreSQL (nur zur Laufzeit benötigt)
runtimeOnly(libs.postgresql.driver)
// H2 Datenbank für Tests und lokale Entwicklung
runtimeOnly(libs.h2.driver)
// HikariCP für Connection Pooling
// Connection pooling
implementation(libs.hikari.cp)
// Logging
implementation(libs.logback)
// Database drivers
runtimeOnly(libs.postgresql.driver) // Production
runtimeOnly(libs.h2.driver) // Development and testing
// Testing
testImplementation(libs.ktor.server.tests)
testImplementation(libs.kotlin.test.junit)
testImplementation(libs.junit.jupiter)
}
@@ -1,89 +0,0 @@
package at.mocode
import at.mocode.plugins.configureDatabase
import io.ktor.server.application.*
import io.ktor.server.netty.*
fun main(args: Array<String>) {
EngineMain.main(args)
}
fun Application.module() {
// Als Erstes die Datenbank konfigurieren:
configureDatabase()
// Danach deine anderen Konfigurationen (Routing etc.):
// routing {
// get("/") {
// // Logger holen (optional, aber nützlich)
// val log = LoggerFactory.getLogger("RootRoute")
// // --- Datenbankoperationen ---
// // alle DB-Zugriffe mit Exposed sollten in einer Transaktion stattfinden
// val turniereFromDb = transaction {
// // Optional: Füge ein Test-Turnier hinzu, WENN die Tabelle leer ist.
// // Das ist nur für den ersten Test praktisch.
// if (TurniereTable.selectAll().count() == 0L) {
// log.info("Turnier table is empty, inserting dummy tournament...")
// TurniereTable.insert {
// it[id] = "dummy-01" // Eindeutige ID
//
// }
// }
//
// // Lese ALLE Einträge aus der TurniereTable
// log.info("Fetching all tournaments from database...")
// TurniereTable.selectAll().map { row ->
// // Wandle jede Datenbank-Zeile (row) wieder in ein Turnier-Objekt um
// Turnier(
// id = row[TurniereTable.id],
//
// )
// } // Das Ergebnis ist eine List<Turnier>
// } // Ende der Transaktion
//
// // --- HTML-Antwort generieren ---
// call.respondHtml(HttpStatusCode.OK) {
// head {
// title { +"Meldestelle Portal" }
// }
// body {
// h1 { +"Willkommen beim Meldestelle Portal!" }
// p { +"Datenbankverbindung erfolgreich!" } // Kleine Bestätigung
// hr()
// h2 { +"Aktuelle Turniere (aus Datenbank):" } // Geänderte Überschrift
//
// // Gib die Turnierliste aus der Datenbank aus
// ul {
// if (turniereFromDb.isEmpty()) {
// li { +"Keine Turniere in der Datenbank gefunden." }
// } else {
// // Schleife über die Liste aus der DB
// turniereFromDb.forEach { turnier ->
// li {
// strong { +turnier.name }
// +" (${turnier.datum})"
// // Füge die Buttons wieder hinzu
// +" "
// if (turnier.ausschreibungUrl != null) {
// a(href = turnier.ausschreibungUrl, target = "_blank") {
// button { +"Ausschreibung" }
// }
// +" "
// }
// a(href = "/nennung/${turnier.id}") {
// button { +"Online Nennen" }
// }
// }
// }
// }
// }
// // Link zum (noch nicht funktionierenden) Admin-Bereich
// hr()
// p { a(href = "/admin/tournaments") { +"Zur Turnierverwaltung (TODO)" } }
// }
// } // <--- HIER endet der respondHtml-Block
// } // Ende get("/")
// }
}
@@ -1,115 +0,0 @@
package at.mocode.plugins
import at.mocode.tables.*
import com.zaxxer.hikari.HikariConfig
import com.zaxxer.hikari.HikariDataSource
import org.jetbrains.exposed.sql.Database
import org.jetbrains.exposed.sql.SchemaUtils
import org.jetbrains.exposed.sql.transactions.transaction
import org.slf4j.LoggerFactory
fun configureDatabase() {
val log = LoggerFactory.getLogger("DatabaseInitialization")
var connectionSuccessful = false // Flag: Wurde irgendeine Verbindung hergestellt?
// Prüfen, ob wir in einer Testumgebung sind (z.B. über System Property)
val isTestEnvironment = System.getProperty("isTestEnvironment")?.toBoolean() ?: false
if (isTestEnvironment) {
log.info("Test environment detected, using in-memory H2 database (test)...")
try {
// H2 im PostgreSQL-Kompatibilitätsmodus starten, kann helfen
Database.connect("jdbc:h2:mem:test;DB_CLOSE_DELAY=-1;MODE=PostgreSQL", driver = "org.h2.Driver")
log.info("Connected to H2 (test) successfully.")
connectionSuccessful = true
} catch (e: Exception) {
log.error("Failed to connect to H2 (test)!", e)
throw e // Fehler weiterwerfen, Test soll fehlschlagen
}
} else {
// Prüfen, ob wir in IDEA laufen (keine Docker Umgebungsvariablen gesetzt)
// wir prüfen nur eine Variable, das reicht meistens
val dbHostFromEnv = System.getenv("DB_HOST")
val isIdeaEnvironment = (dbHostFromEnv == null)
if (isIdeaEnvironment) {
log.info("IDEA environment detected (missing DB_HOST), using in-memory H2 database (dev)...")
try {
Database.connect("jdbc:h2:mem:dev;DB_CLOSE_DELAY=-1;MODE=PostgreSQL", driver = "org.h2.Driver")
log.info("Connected to H2 (dev) successfully.")
connectionSuccessful = true
} catch (e: Exception) {
log.error("Failed to connect to H2 (dev)!", e)
// Hier vielleicht nicht werfen, damit App in IDE trotzdem startet? Oder doch? → Aktuell wirft es.
throw e
}
} else {
// Normale Docker/Produktionsumgebung -> PostgreSQL verwenden
log.info("Production/Docker environment detected, connecting to PostgreSQL...")
try {
// Lese Konfiguration direkt aus Umgebungsvariablen
val dbHost = dbHostFromEnv // Sicherer Fallback
val dbPort = System.getenv("DB_PORT") ?: "5432"
val dbName = System.getenv("DB_NAME") ?: error("DB_NAME not set in environment")
val dbUser = System.getenv("DB_USER") ?: error("DB_USER not set in environment")
val dbPassword = System.getenv("DB_PASSWORD") ?: error("DB_PASSWORD not set in environment")
val driverClassName = "org.postgresql.Driver"
val maxPoolSize = System.getenv("DB_POOL_SIZE")?.toIntOrNull() ?: 10
val jdbcURL = "jdbc:postgresql://$dbHost:$dbPort/$dbName"
log.info("Attempting to connect to PostgreSQL at URL: {}", jdbcURL)
val hikariConfig = HikariConfig().apply {
this.driverClassName = driverClassName
this.jdbcUrl = jdbcURL
this.username = dbUser
this.password = dbPassword
this.maximumPoolSize = maxPoolSize
this.validate()
}
val dataSource = HikariDataSource(hikariConfig)
Database.connect(dataSource)
log.info("PostgreSQL connection pool initialized successfully!")
connectionSuccessful = true
} catch (e: Exception) {
log.error("Failed to initialize PostgreSQL connection pool!", e)
throw e // Fehler weiterwerfen, App soll nicht starten ohne DB in Prod
}
}
}
// --- Schema Initialisierung (JETZT ZENTRALISIERT) ---
// Führe dies nur aus, wenn *irgendeine* DB-Verbindung erfolgreich war
transaction { // Führe Schema-Operationen in einer Transaktion aus
log.info("Initializing/Verifying database schema...")
try {
// Erstellt die Tabelle(n), falls sie noch nicht existieren
SchemaUtils.create(TurniereTable)
// Füge hier später weitere Tabellen hinzu:
// SchemaUtils.create(TurniereTable, NennungenTable, ...)
log.info("Database schema initialized successfully (tables created/verified).")
} catch (e: Exception) {
log.error("Failed to initialize database schema!", e)
// Hier könntest du entscheiden, ob ein Fehler beim Schema kritisch ist
// throw e // Auskommentiert: App startet evtl. trotzdem, auch wenn Schema fehlt/falsch ist
}
}
// --- TODO für den NÄCHSTEN Schritt ---
// Hier kommt später die Logik zum Erstellen der Tabellen hin,
// z.B. innerhalb einer Transaktion:
transaction {
SchemaUtils.create(
VereineTable,
PersonenTable,
PferdeTable,
VeranstaltungenTable, // NEU
TurniereTable,
ArtikelTable,
PlaetzeTable // NEU
// ... weitere Tabellen ...
)
}
// ------------------------------------
}
@@ -0,0 +1,144 @@
package at.mocode.server
import at.mocode.server.plugins.configureDatabase
import io.ktor.server.application.*
import io.ktor.server.netty.*
import io.ktor.server.response.*
import io.ktor.server.routing.*
import org.slf4j.LoggerFactory
/**
* Main entry point for the application.
* Uses Ktor's EngineMain to start the server with configuration from application.yaml
*/
fun main(args: Array<String>) {
EngineMain.main(args)
}
/**
* Application module configuration.
* This is where all server plugins and routes are configured.
*/
fun Application.module() {
val log = LoggerFactory.getLogger("Application")
log.info("Initializing application...")
// Configure database
configureDatabase()
// Configure plugins
// configurePlugins()
// Configure routing
configureRouting()
log.info("Application initialized successfully")
}
/**
* Configures all Ktor plugins for the application
*/
//private fun Application.configurePlugins() {
// val log = LoggerFactory.getLogger("ApplicationPlugins")
// // Add default headers to all responses
// install(DefaultHeaders) {
// header("X-Engine", "Ktor")
// header("X-Content-Type-Options", "nosniff")
// }
//
// // Configure call logging
// install(CallLogging) {
// level = Level.INFO
// }
//
// // Configure content negotiation with JSON
// install(ContentNegotiation) {
// json(Json {
// prettyPrint = true
// isLenient = true
// ignoreUnknownKeys = true
// })
// }
//
// // Configure CORS
// install(CORS) {
// // Default CORS configuration
// anyHost()
// allowMethod(HttpMethod.Options)
// allowMethod(HttpMethod.Get)
// allowMethod(HttpMethod.Post)
// allowMethod(HttpMethod.Put)
// allowMethod(HttpMethod.Delete)
// allowHeader(HttpHeaders.ContentType)
// allowHeader(HttpHeaders.Authorization)
//
// // Try to read from config to override defaults
// try {
// val appEnv = this@configurePlugins.environment.config
// if (appEnv.propertyOrNull("cors") != null) {
// val corsConfig = appEnv.config("cors")
//
// // Clear default anyHost if we have specific hosts
// if (corsConfig.propertyOrNull("allowedHosts") != null) {
// val hosts = corsConfig.property("allowedHosts").getList()
// if (hosts.isNotEmpty()) {
// hosts.forEach { host ->
// allowHost(host)
// }
// }
// }
//
// // Allow credentials if configured
// if (corsConfig.propertyOrNull("allowCredentials") != null) {
// allowCredentials = corsConfig.property("allowCredentials").getString().toBoolean()
// }
// }
// } catch (e: Exception) {
// // Log the error but continue with default configuration
// this@configurePlugins.log.warn("Failed to configure CORS from config, using defaults: ${e.message}")
// }
// }
//
// // Configure status pages for error handling
// install(StatusPages) {
// exception<Throwable> { call, cause ->
// call.respondText(
// text = "500: ${cause.message ?: "Internal Server Error"}",
// status = HttpStatusCode.InternalServerError
// )
// }
//
// status(HttpStatusCode.NotFound) { call, _ ->
// call.respondText(
// text = "404: Page Not Found",
// status = HttpStatusCode.NotFound
// )
// }
// }
//}
/**
* Configures all routes for the application
*/
private fun Application.configureRouting() {
routing {
// Health check endpoint
get("/health") {
call.respondText("OK")
}
// Root endpoint with basic information
get("/") {
// Read application info from config if available
val appName = application.environment.config.propertyOrNull("application.name")?.getString() ?: "Meldestelle API Server"
val appVersion = application.environment.config.propertyOrNull("application.version")?.getString() ?: "1.0.0"
val appEnv = application.environment.config.propertyOrNull("application.environment")?.getString() ?: "development"
call.respondText("$appName v$appVersion - Running in $appEnv mode")
}
// API routes can be organized in separate files and included here
// Example: registerUserRoutes()
}
}
@@ -0,0 +1,204 @@
package at.mocode.server.plugins
import at.mocode.server.tables.ArtikelTable
import at.mocode.server.tables.LizenzenTable
import at.mocode.server.tables.PersonenTable
import at.mocode.server.tables.PferdeTable
import at.mocode.server.tables.PlaetzeTable
import at.mocode.server.tables.TurniereTable
import at.mocode.server.tables.VeranstaltungenTable
import at.mocode.server.tables.VereineTable
import com.zaxxer.hikari.HikariConfig
import com.zaxxer.hikari.HikariDataSource
import io.ktor.server.application.*
import io.ktor.server.config.*
import org.jetbrains.exposed.sql.Database
import org.jetbrains.exposed.sql.SchemaUtils
import org.jetbrains.exposed.sql.transactions.transaction
import org.slf4j.LoggerFactory
import java.util.concurrent.TimeUnit
/**
* Configures the database connection based on the environment.
* Supports three environments:
* 1. Test environment - Uses in-memory H2 database
* 2. Development environment - Uses in-memory H2 database
* 3. Production environment - Uses PostgreSQL database
*
* @param application The Ktor application instance to read configuration from
*/
fun Application.configureDatabase() {
val log = LoggerFactory.getLogger("DatabaseInitialization")
var connectionSuccessful = false
// Environment detection
val isTestEnvironment = System.getProperty("isTestEnvironment")?.toBoolean() ?: false
val dbHostFromEnv = System.getenv("DB_HOST")
val isIdeaEnvironment = (dbHostFromEnv == null)
// Get database configuration from application.yaml if available
val dbConfig = try {
environment.config.config("database")
} catch (e: ApplicationConfigurationException) {
log.warn("No database configuration found in application.yaml, using environment variables")
null
}
when {
isTestEnvironment -> {
configureTestDatabase(log)
connectionSuccessful = true
}
isIdeaEnvironment -> {
configureDevelopmentDatabase(log)
connectionSuccessful = true
}
else -> {
connectionSuccessful = configureProductionDatabase(log, dbConfig)
}
}
// Initialize schema if connection was successful
if (connectionSuccessful) {
initializeSchema(log, isTestEnvironment, isIdeaEnvironment)
} else {
log.error("No database connection established. Schema initialization skipped.")
}
}
/**
* Configures an in-memory H2 database for testing
*/
private fun configureTestDatabase(log: org.slf4j.Logger): Boolean {
log.info("Test environment detected, using in-memory H2 database (test)...")
return try {
Database.connect(
url = "jdbc:h2:mem:test;DB_CLOSE_DELAY=-1;MODE=PostgreSQL",
driver = "org.h2.Driver",
user = "sa",
password = ""
)
log.info("Connected to H2 (test) successfully.")
true
} catch (e: Exception) {
log.error("Failed to connect to H2 (test)!", e)
throw e // Rethrow to fail the test
}
}
/**
* Configures an in-memory H2 database for development
*/
private fun configureDevelopmentDatabase(log: org.slf4j.Logger): Boolean {
log.info("Development environment detected, using in-memory H2 database (dev)...")
return try {
Database.connect(
url = "jdbc:h2:mem:dev;DB_CLOSE_DELAY=-1;MODE=PostgreSQL",
driver = "org.h2.Driver",
user = "sa",
password = ""
)
log.info("Connected to H2 (dev) successfully.")
true
} catch (e: Exception) {
log.error("Failed to connect to H2 (dev)!", e)
throw e
}
}
/**
* Configures a PostgreSQL database for production
*/
private fun configureProductionDatabase(log: org.slf4j.Logger, dbConfig: ApplicationConfig?): Boolean {
log.info("Production environment detected, connecting to PostgreSQL...")
// Get database configuration from application.yaml or environment variables
val dbHost = dbConfig?.propertyOrNull("host")?.getString() ?: System.getenv("DB_HOST")
?: error("Database host not configured")
val dbPort = dbConfig?.propertyOrNull("port")?.getString() ?: System.getenv("DB_PORT") ?: "5432"
val dbName = dbConfig?.propertyOrNull("name")?.getString() ?: System.getenv("DB_NAME")
?: error("Database name not configured")
val dbUser = dbConfig?.propertyOrNull("user")?.getString() ?: System.getenv("DB_USER")
?: error("Database user not configured")
val dbPassword = dbConfig?.propertyOrNull("password")?.getString() ?: System.getenv("DB_PASSWORD")
?: error("Database password not configured")
// Connection pool configuration
val maxPoolSize = dbConfig?.propertyOrNull("pool.maxSize")?.getString()?.toIntOrNull()
?: System.getenv("DB_POOL_SIZE")?.toIntOrNull() ?: 10
val minIdle = dbConfig?.propertyOrNull("pool.minIdle")?.getString()?.toIntOrNull() ?: 2
val idleTimeout = dbConfig?.propertyOrNull("pool.idleTimeout")?.getString()?.toLongOrNull() ?: 10000L
val connectionTimeout = dbConfig?.propertyOrNull("pool.connectionTimeout")?.getString()?.toLongOrNull() ?: 5000L
val maxLifetime = dbConfig?.propertyOrNull("pool.maxLifetime")?.getString()?.toLongOrNull() ?: 1800000L
val jdbcURL = "jdbc:postgresql://$dbHost:$dbPort/$dbName"
log.info("Attempting to connect to PostgreSQL at URL: {}", jdbcURL)
return try {
val hikariConfig = HikariConfig().apply {
driverClassName = "org.postgresql.Driver"
jdbcUrl = jdbcURL
username = dbUser
password = dbPassword
maximumPoolSize = maxPoolSize
minimumIdle = minIdle
this.idleTimeout = idleTimeout
this.connectionTimeout = connectionTimeout
this.maxLifetime = maxLifetime
// Additional security and performance settings
addDataSourceProperty("cachePrepStmts", "true")
addDataSourceProperty("prepStmtCacheSize", "250")
addDataSourceProperty("prepStmtCacheSqlLimit", "2048")
addDataSourceProperty("useServerPrepStmts", "true")
// Connection validation
connectionTestQuery = "SELECT 1"
validationTimeout = TimeUnit.SECONDS.toMillis(5)
// Leak detection
leakDetectionThreshold = TimeUnit.SECONDS.toMillis(60)
validate()
}
val dataSource = HikariDataSource(hikariConfig)
Database.connect(dataSource)
log.info("PostgreSQL connection pool initialized successfully!")
true
} catch (e: Exception) {
log.error("Failed to initialize PostgreSQL connection pool!", e)
throw e // Rethrow in production
}
}
/**
* Initializes the database schema
*/
private fun initializeSchema(log: org.slf4j.Logger, isTestEnvironment: Boolean, isIdeaEnvironment: Boolean) {
transaction {
log.info("Initializing/Verifying database schema...")
try {
// Create all tables if they don't exist
SchemaUtils.create(
VereineTable,
PersonenTable,
PferdeTable,
VeranstaltungenTable,
TurniereTable,
ArtikelTable,
PlaetzeTable,
LizenzenTable
// Add more tables here if needed
)
log.info("Database schema initialized successfully.")
} catch (e: Exception) {
log.error("Failed to initialize database schema!", e)
// In production, a schema initialization failure is critical
if (!isTestEnvironment && !isIdeaEnvironment) {
throw e
}
// In test/development, just log the error
}
}
}
@@ -0,0 +1,26 @@
package at.mocode.server.tables
import org.jetbrains.exposed.sql.Table
import org.jetbrains.exposed.sql.kotlin.datetime.timestamp
/**
* Optimized version of ArtikelTable
* Changes:
* - Changed unique index on bezeichnung to non-unique
* - Added init block for defining indexes
*/
object ArtikelTable : Table(name = "artikel") {
val id = uuid(name = "id")
val bezeichnung = varchar(name = "bezeichnung", length = 255)
val preis = varchar(name = "preis", length = 50)
val einheit = varchar(name = "einheit", length = 50)
val istVerbandsabgabe = bool(name = "ist_verbandsabgabe").default(defaultValue = false)
val createdAt = timestamp(name = "created_at")
val updatedAt = timestamp(name = "updated_at")
override val primaryKey = PrimaryKey(id)
init {
index(isUnique = false, bezeichnung)
}
}
@@ -0,0 +1,30 @@
package at.mocode.server.tables
import at.mocode.server.enums.LizenzTyp
import at.mocode.server.enums.Sparte
import org.jetbrains.exposed.sql.Table
import org.jetbrains.exposed.sql.kotlin.datetime.date
/**
* Optimized version of LizenzenTable
* Changes:
* - Added proper imports for enums
* - Uncommented the sparte field
* - Added index for lizenzTyp and gueltigBisJahr
*/
object LizenzenTable : Table(name = "lizenzen") {
val id = uuid(name = "id")
val personId = uuid(name = "person_id").references(PersonenTable.id)
val lizenzTyp = enumerationByName(name = "lizenz_typ", length = 50, klass = LizenzTyp::class)
val stufe = varchar(name = "stufe", 20).nullable()
val sparte = enumerationByName(name = "sparte", length = 50, klass = Sparte::class).nullable()
val gueltigBisJahr = integer(name = "gueltig_bis_jahr").nullable()
val ausgestelltAm = date(name = "ausgestellt_am").nullable()
override val primaryKey = PrimaryKey(firstColumn = id)
init {
index(isUnique = false, personId)
index(isUnique = false, lizenzTyp, gueltigBisJahr)
}
}
@@ -0,0 +1,51 @@
package at.mocode.server.tables
import at.mocode.server.enums.Geschlecht
import org.jetbrains.exposed.sql.Table
import org.jetbrains.exposed.sql.kotlin.datetime.date
import org.jetbrains.exposed.sql.kotlin.datetime.timestamp
/**
* Optimized version of PersonenTable
* Changes:
* - Added proper imports for enums
* - Replaced inline comments with KDoc
* - Fixed the unique index on nachname+vorname to be non-unique
* - Added indexes for email and stammVereinId for common queries
*/
object PersonenTable : Table(name = "personen") {
val id = uuid(name = "id")
val oepsSatzNr = varchar(name = "oeps_satz_nr", length = 10).uniqueIndex().nullable()
val nachname = varchar(name = "nachname", length = 100)
val vorname = varchar(name = "vorname", length = 100)
val titel = varchar(name = "titel", length = 50).nullable()
val geburtsdatum = date(name = "geburtsdatum").nullable()
val geschlecht = enumerationByName(name = "geschlecht", length = 10, klass = Geschlecht::class).nullable()
val nationalitaet = varchar(name = "nationalitaet", length = 3).nullable()
val email = varchar(name = "email", length = 255).nullable()
val telefon = varchar(name = "telefon", length = 50).nullable()
val adresse = varchar(name = "adresse", length = 255).nullable()
val plz = varchar(name = "plz", length = 10).nullable()
val ort = varchar(name = "ort", length = 100).nullable()
val stammVereinId = uuid(name = "stamm_verein_id").references(ref = VereineTable.id).nullable()
val mitgliedsNummerIntern = varchar(name = "mitglieds_nr_intern", length = 50).nullable()
val letzteZahlungJahr = integer(name = "letzte_zahlung_jahr").nullable()
val feiId = varchar(name = "fei_id", length = 20).nullable()
val istGesperrt = bool(name = "ist_gesperrt").default(defaultValue = false)
val sperrGrund = text(name = "sperr_grund").nullable()
val rollenCsv = text(name = "rollen_csv").nullable()
val qualifikationenRichterCsv = text(name = "qualifikationen_richter_csv").nullable()
val qualifikationenParcoursbauerCsv = text(name = "qualifikationen_parcoursbauer_csv").nullable()
val istAktiv = bool(name = "ist_aktiv").default(true)
val createdAt = timestamp(name = "created_at")
val updatedAt = timestamp(name = "updated_at")
override val primaryKey = PrimaryKey(firstColumn = id)
init {
index(isUnique = false, nachname, vorname)
index(isUnique = false, nachname)
index(isUnique = false, email)
index(isUnique = false, stammVereinId)
}
}
@@ -0,0 +1,46 @@
package at.mocode.server.tables
import at.mocode.server.enums.GeschlechtPferd
import org.jetbrains.exposed.sql.Table
import org.jetbrains.exposed.sql.kotlin.datetime.timestamp
/**
* Optimized version of PferdeTable
* Changes:
* - Added proper imports for enums
* - Added indexes for foreign key fields
* - Added index for common search fields (name, rasse)
*/
object PferdeTable : Table(name = "pferde") {
val id = uuid(name = "id")
val oepsKopfNr = varchar(name = "oeps_kopf_nr", length = 10).uniqueIndex().nullable()
val oepsSatzNr = varchar(name = "oeps_satz_nr", length = 15).uniqueIndex().nullable()
val name = varchar(name = "name", length = 255)
val lebensnummer = varchar(name = "lebensnummer", length = 20).nullable()
val feiPassNr = varchar(name = "fei_pass_nr", length = 20).nullable()
val geschlecht = enumerationByName(name = "geschlecht", length = 10, klass = GeschlechtPferd::class).nullable()
val geburtsjahr = integer(name = "geburtsjahr").nullable()
val rasse = varchar(name = "rasse", length = 100).nullable()
val farbe = varchar(name = "farbe", length = 50).nullable()
val vaterName = varchar(name = "vater_name", length = 255).nullable()
val mutterName = varchar(name = "mutter_name", length = 255).nullable()
val mutterVaterName = varchar(name = "mutter_vater_name", length = 255).nullable()
val besitzerId = uuid(name = "besitzer_id").references(ref = PersonenTable.id).nullable()
val verantwortlichePersonId = uuid(name = "verantwortliche_person_id").references(ref = PersonenTable.id).nullable()
val heimatVereinId = uuid(name = "heimat_verein_id").references(ref = VereineTable.id).nullable()
val letzteZahlungJahrOeps = integer(name = "letzte_zahlung_jahr_oeps").nullable()
val stockmassCm = integer(name = "stockmass_cm").nullable()
val istAktiv = bool(name = "ist_aktiv").default(defaultValue = true)
val createdAt = timestamp(name = "created_at")
val updatedAt = timestamp(name = "updated_at")
override val primaryKey = PrimaryKey(firstColumn = id)
init {
index(isUnique = false, name)
index(isUnique = false, rasse)
index(isUnique = false, besitzerId)
index(isUnique = false, verantwortlichePersonId)
index(isUnique = false, heimatVereinId)
}
}
@@ -0,0 +1,27 @@
package at.mocode.server.tables
import at.mocode.server.enums.PlatzTyp
import org.jetbrains.exposed.sql.Table
/**
* Optimized version of PlaetzeTable
* Changes:
* - Added proper imports for enums
* - Added index for name field
*/
object PlaetzeTable : Table(name = "plaetze") {
val id = uuid(name = "id")
val turnierId = uuid(name = "turnier_id").references(ref = TurniereTable.id)
val name = varchar(name = "name", length = 100)
val dimension = varchar(name = "dimension", length = 50).nullable()
val boden = varchar(name = "boden", length = 100).nullable()
val typ = enumerationByName(name = "typ", length = 20, klass = PlatzTyp::class)
override val primaryKey = PrimaryKey(firstColumn = id)
init {
index(isUnique = false, turnierId)
index(isUnique = false, name)
index(isUnique = false, typ)
}
}
@@ -0,0 +1,53 @@
package at.mocode.server.tables
import org.jetbrains.exposed.sql.Table
import org.jetbrains.exposed.sql.kotlin.datetime.date // Für kotlinx-datetime LocalDate
import org.jetbrains.exposed.sql.kotlin.datetime.datetime // Für kotlinx-datetime LocalDateTime
import org.jetbrains.exposed.sql.kotlin.datetime.timestamp // Für kotlinx-datetime Instant
/**
* Optimized version of TurniereTable
* Changes:
* - Added proper imports for enums
* - Added indexes for foreign key fields and common search fields
* - Added init block for defining indexes
*/
object TurniereTable : Table(name = "turniere") { // Name der Tabelle in PostgreSQL
val id = uuid(name = "id")
val veranstaltungId = uuid(name = "veranstaltung_id").references(ref = VeranstaltungenTable.id)
val oepsTurnierNr = varchar(name = "oeps_turnier_nr", length = 15).uniqueIndex()
val titel = varchar(name = "titel", length = 255)
val untertitel = varchar(name = "untertitel", length = 500).nullable()
val datumVon = date(name = "datum_von")
val datumBis = date(name = "datum_bis")
val nennungsschluss = datetime(name = "nennungsschluss").nullable()
val nennungsArtCsv = text(name = "nennungs_art_csv").nullable()
val nennungsHinweis = text(name = "nennungs_hinweis").nullable()
val eigenesNennsystemUrl = varchar(name = "eigenes_nennsystem_url", length = 500).nullable()
val nenngeld = varchar(name = "nenngeld", length = 50).nullable()
val startgeldStandard = varchar(name = "startgeld_standard", length = 50).nullable()
val turnierleiterId = uuid(name = "turnierleiter_id").references(ref = PersonenTable.id).nullable()
val turnierbeauftragterId = uuid(name = "turnierbeauftragter_id").references(ref = PersonenTable.id).nullable()
val richterIdsCsv = text(name = "richter_ids_csv").nullable()
val parcoursbauerIdsCsv = text(name = "parcoursbauer_ids_csv").nullable()
val parcoursAssistentIdsCsv = text(name = "parcours_assistent_ids_csv").nullable()
val tierarztInfos = text(name = "tierarzt_infos").nullable()
val hufschmiedInfo = text(name = "hufschmied_info").nullable()
val meldestelleVerantwortlicherId = uuid(name = "meldestelle_verantwortlicher_id").references(ref = PersonenTable.id).nullable()
val meldestelleTelefon = varchar(name = "meldestelle_telefon", length = 50).nullable()
val meldestelleOeffnungszeiten = varchar(name = "meldestelle_oeffnungszeiten", length = 255).nullable()
val ergebnislistenUrl = varchar(name = "ergebnislisten_url", length = 500).nullable()
val createdAt = timestamp(name = "created_at")
val updatedAt = timestamp(name = "updated_at")
override val primaryKey = PrimaryKey(firstColumn = id)
init {
index(isUnique = false, veranstaltungId)
index(isUnique = false, datumVon, datumBis)
index(isUnique = false, titel)
index(isUnique = false, turnierleiterId)
index(isUnique = false, turnierbeauftragterId)
index(isUnique = false, meldestelleVerantwortlicherId)
}
}
@@ -0,0 +1,49 @@
package at.mocode.server.tables
import at.mocode.server.enums.VeranstalterTyp
import org.jetbrains.exposed.sql.Table
import org.jetbrains.exposed.sql.kotlin.datetime.date
import org.jetbrains.exposed.sql.kotlin.datetime.timestamp
/**
* Optimized version of VeranstaltungenTable
* Changes:
* - Added proper imports for enums
* - Added indexes for common search fields
* - Added init block for defining indexes
*/
object VeranstaltungenTable : Table(name = "veranstaltungen") {
val id = uuid(name = "id")
val name = varchar(name = "name", length = 255)
val datumVon = date(name = "datum_von")
val datumBis = date(name = "datum_bis")
val veranstalterName = varchar(name = "veranstalter_name", length = 255)
val veranstalterOepsNummer = varchar(name = "veranstalter_oeps_nr", length = 10).nullable()
val veranstalterTyp =
enumerationByName(name = "veranstalter_typ", length = 20, klass = VeranstalterTyp::class).default(
VeranstalterTyp.UNBEKANNT
)
val veranstaltungsortName = varchar(name = "veranstaltungsort_name", length = 255)
val veranstaltungsortAdresse = varchar(name = "veranstaltungsort_adresse", length = 500)
val kontaktpersonName = varchar(name = "kontaktperson_name", length = 200).nullable()
val kontaktTelefon = varchar(name = "kontakt_telefon", length = 50).nullable()
val kontaktEmail = varchar(name = "kontakt_email", length = 255).nullable()
val webseite = varchar(name = "webseite", length = 500).nullable()
val logoUrl = varchar(name = "logo_url", length = 500).nullable()
val anfahrtsplanInfo = text(name = "anfahrtsplan_info").nullable()
val sponsorInfosCsv = text(name = "sponsor_infos_csv").nullable()
val dsgvoText = text(name = "dsgvo_text").nullable()
val haftungsText = text(name = "haftungs_text").nullable()
val sonstigeBesondereBestimmungen = text(name = "sonstige_bestimmungen").nullable()
val createdAt = timestamp("created_at")
val updatedAt = timestamp("updated_at")
override val primaryKey = PrimaryKey(id)
init {
index(isUnique = false, name)
index(isUnique = false, datumVon, datumBis)
index(isUnique = false, veranstalterName)
index(isUnique = false, veranstaltungsortName)
}
}
@@ -0,0 +1,35 @@
package at.mocode.server.tables
import org.jetbrains.exposed.sql.Table
import org.jetbrains.exposed.sql.kotlin.datetime.timestamp
/**
* Optimized version of VereineTable
* Changes:
* - Added indexes for common search fields (name, bundesland)
* - Added init block for defining indexes
*/
object VereineTable : Table(name = "vereine") {
val id = uuid(name = "id")
val oepsVereinsNr = varchar(name = "oeps_vereins_nr", length = 10).uniqueIndex()
val name = varchar(name = "name", length = 255)
val kuerzel = varchar(name = "kuerzel", length = 50).nullable()
val bundesland = varchar(name = "bundesland", length = 10).nullable()
val adresse = varchar(name = "adresse", length = 255).nullable()
val plz = varchar(name = "plz", length = 10).nullable()
val ort = varchar(name = "ort", length = 100).nullable()
val email = varchar(name = "email", length = 255).nullable()
val telefon = varchar(name = "telefon", length = 50).nullable()
val webseite = varchar(name = "webseite", length = 500).nullable()
val istAktiv = bool(name = "ist_aktiv").default(defaultValue = true)
val createdAt = timestamp(name = "created_at")
val updatedAt = timestamp(name = "updated_at")
override val primaryKey = PrimaryKey(firstColumn = id)
init {
index(isUnique = false, name)
index(isUnique = false, bundesland)
index(isUnique = false, ort)
}
}
@@ -1,19 +0,0 @@
package at.mocode.tables
import org.jetbrains.exposed.sql.Table
import org.jetbrains.exposed.sql.kotlin.datetime.timestamp
// --- Tabelle für Artikel (falls noch nicht vorhanden) ---
object ArtikelTable : Table("artikel") {
val id = uuid("id")
val bezeichnung = varchar("bezeichnung", 255).uniqueIndex() // Bezeichnung sollte eindeutig sein?
// Preis als Varchar speichern wegen KMP BigDecimal
val preis = varchar("preis", 50)
val einheit = varchar("einheit", 50)
val istVerbandsabgabe = bool("ist_verbandsabgabe").default(false)
val createdAt = timestamp("created_at")
val updatedAt = timestamp("updated_at")
override val primaryKey = PrimaryKey(id)
}
@@ -1,24 +0,0 @@
package at.mocode.tables
import at.mocode.model.enums.LizenzTyp
import org.jetbrains.exposed.sql.Table
import org.jetbrains.exposed.sql.kotlin.datetime.date
// --- Tabelle für Lizenzen (Beispiel für Normalisierung von List<LizenzInfo>) ---
// Diese Tabelle wäre die "sauberere" Lösung für die Speicherung der Lizenzen aus Person.
// Statt sie als JSON/Text in PersonenTable zu speichern.
object LizenzenTable : Table("lizenzen") {
val id = uuid("id")
val personId = uuid("person_id").references(PersonenTable.id) // FK zur Person
val lizenzTyp = enumerationByName("lizenz_typ", 50, LizenzTyp::class)
val stufe = varchar("stufe", 20).nullable()
// val sparte = enumerationByName("sparte", 50, Sparte::class).nullable() // Sparte Enum nötig
val gueltigBisJahr = integer("gueltig_bis_jahr").nullable()
val ausgestelltAm = date("ausgestellt_am").nullable()
override val primaryKey = PrimaryKey(id)
init {
index(false, personId) // Index auf personId für schnelle Suche
}
}
@@ -1,51 +0,0 @@
package at.mocode.tables
import at.mocode.model.enums.Geschlecht
import org.jetbrains.exposed.sql.Table
import org.jetbrains.exposed.sql.kotlin.datetime.date
import org.jetbrains.exposed.sql.kotlin.datetime.timestamp
// --- Tabelle für Personen (Reiter, Richter, Funktionäre etc.) ---
object PersonenTable : Table("personen") {
val id = uuid("id")
val oepsSatzNr = varchar("oeps_satz_nr", 10).uniqueIndex().nullable() // OEPS SatzNr ist eindeutig, wenn vorhanden
val nachname = varchar("nachname", 100)
val vorname = varchar("vorname", 100)
val titel = varchar("titel", 50).nullable()
val geburtsdatum = date("geburtsdatum").nullable() // kotlinx.datetime.LocalDate
// Speichert den Enum-Namen als String, max 10 Zeichen lang
val geschlecht = enumerationByName("geschlecht", 10, Geschlecht::class).nullable()
val nationalitaet = varchar("nationalitaet", 3).nullable() // AUT, GER, ...
val email = varchar("email", 255).nullable()
val telefon = varchar("telefon", 50).nullable()
val adresse = varchar("adresse", 255).nullable()
val plz = varchar("plz", 10).nullable()
val ort = varchar("ort", 100).nullable()
// Fremdschlüssel zur Vereine Tabelle für die Stamm-Mitgliedschaft
val stammVereinId = uuid("stamm_verein_id").references(VereineTable.id).nullable()
val mitgliedsNummerIntern = varchar("mitglieds_nr_intern", 50).nullable()
val letzteZahlungJahr = integer("letzte_zahlung_jahr").nullable()
val feiId = varchar("fei_id", 20).nullable()
val istGesperrt = bool("ist_gesperrt").default(false)
val sperrGrund = text("sperr_grund").nullable() // Längerer Text möglich
// Listen/Sets -> Als Text speichern für Einfachheit, später evtl. normalisieren
// Rollen (Set<FunktionaerRolle>) -> CSV oder JSON in Textfeld
val rollenCsv = text("rollen_csv").nullable()
// Lizenzen (List<LizenzInfo>) -> Eigene Tabelle "LizenzenTable" wäre besser! Vorerst hier weglassen oder als JSONB.
// val lizenzenJson = jsonb("lizenzen", ...) // Benötigt spezielle Exposed/Postgres Konfiguration
// Qualifikationen (List<String>) -> CSV
val qualifikationenRichterCsv = text("qualifikationen_richter_csv").nullable()
val qualifikationenParcoursbauerCsv = text("qualifikationen_parcoursbauer_csv").nullable()
val istAktiv = bool("ist_aktiv").default(true)
val createdAt = timestamp("created_at")
val updatedAt = timestamp("updated_at")
override val primaryKey = PrimaryKey(id)
// Index für schnelles Suchen nach Namen
init {
index(true, nachname, vorname) // Eindeutiger Index auf Nachname+Vorname? Eher nicht. Normaler Index: index(false, ...)
index(false, nachname) // Index auf Nachname allein
}
}
@@ -1,37 +0,0 @@
package at.mocode.tables
import at.mocode.model.enums.GeschlechtPferd
import org.jetbrains.exposed.sql.Table
import org.jetbrains.exposed.sql.kotlin.datetime.timestamp
// --- Tabelle für Pferde ---
object PferdeTable : Table("pferde") {
val id = uuid("id")
val oepsKopfNr = varchar("oeps_kopf_nr", 10).uniqueIndex().nullable() // KopfNr sollte eindeutig sein, wenn vorhanden
val oepsSatzNr = varchar("oeps_satz_nr", 15).uniqueIndex().nullable() // 10-stellige Nr, Puffer; Eindeutig wenn vorhanden
val name = varchar("name", 255)
val lebensnummer = varchar("lebensnummer", 20).nullable() // UELN etc.
val feiPassNr = varchar("fei_pass_nr", 20).nullable()
val geschlecht = enumerationByName("geschlecht", 10, GeschlechtPferd::class).nullable()
val geburtsjahr = integer("geburtsjahr").nullable()
val rasse = varchar("rasse", 100).nullable()
val farbe = varchar("farbe", 50).nullable()
val vaterName = varchar("vater_name", 255).nullable()
val mutterName = varchar("mutter_name", 255).nullable()
val mutterVaterName = varchar("mutter_vater_name", 255).nullable()
// Fremdschlüssel zu Personen (Besitzer, Verantwortlicher) und Vereine (Heimatverein)
val besitzerId = uuid("besitzer_id").references(PersonenTable.id).nullable()
val verantwortlichePersonId = uuid("verantwortliche_person_id").references(PersonenTable.id).nullable()
val heimatVereinId = uuid("heimat_verein_id").references(VereineTable.id).nullable()
val letzteZahlungJahrOeps = integer("letzte_zahlung_jahr_oeps").nullable()
val stockmassCm = integer("stockmass_cm").nullable()
val istAktiv = bool("ist_aktiv").default(true)
val createdAt = timestamp("created_at")
val updatedAt = timestamp("updated_at")
override val primaryKey = PrimaryKey(id)
// Index für Pferdenamen
init {
index(false, name)
}
}
@@ -1,26 +0,0 @@
package at.mocode.tables
import at.mocode.model.enums.PlatzTyp
import org.jetbrains.exposed.sql.Table
// --- Tabelle für Plätze (Austragungs- & Vorbereitungsplätze) ---
// Wichtig: Ein Platz gehört immer zu einem spezifischen Turnier!
object PlaetzeTable : Table("plaetze") {
val id = uuid("id")
// Fremdschlüssel zur Turniere Tabelle
val turnierId = uuid("turnier_id").references(TurniereTable.id) // Annahme: TurniereTable existiert
val name = varchar("name", 100) // z.B. "Sandplatz Austragung", "Halle Vorbereitung"
val dimension = varchar("dimension", 50).nullable() // z.B. "20x40m", "50x100m"
val boden = varchar("boden", 100).nullable() // z.B. "Sand", "Gras", "Sand/Vlies"
// Typ des Platzes (Austragung, Vorbereitung etc.)
val typ = enumerationByName("typ", 20, PlatzTyp::class)
override val primaryKey = PrimaryKey(id)
init {
index(false, turnierId) // Index auf turnierId für schnelle Abfragen pro Turnier
}
}
@@ -1,84 +0,0 @@
package at.mocode.tables
import org.jetbrains.exposed.sql.Table
import org.jetbrains.exposed.sql.kotlin.datetime.date // Für kotlinx-datetime LocalDate
import org.jetbrains.exposed.sql.kotlin.datetime.datetime // Für kotlinx-datetime LocalDateTime
import org.jetbrains.exposed.sql.kotlin.datetime.timestamp // Für kotlinx-datetime Instant
// Annahme: Es gibt bereits oder wird geben:
// object VeranstaltungenTable : Table("veranstaltungen") { val id = uuid("id") /* ... */ }
// object PersonenTable : Table("personen") { val id = uuid("id") /* ... */ }
// Diese sind für die Foreign Key Constraints notwendig.
/**
* Exposed Table Definition für die Turnier-Entität.
* Spiegelt die Struktur von shared/.../Turnier.kt wider.
*/
object TurniereTable : Table("turniere") { // Name der Tabelle in PostgreSQL
// Primärschlüssel (KMP Uuid -> DB UUID)
val id = uuid("id") // Exposed bietet uuid() für UUIDs
// Foreign Key zur Veranstaltungstabelle
val veranstaltungId = uuid("veranstaltung_id").references(VeranstaltungenTable.id)
// OEPS Turniernummer (kann Buchstaben enthalten? Besser Varchar)
val oepsTurnierNr = varchar("oeps_turnier_nr", 15).uniqueIndex() // Eindeutig machen?
// Titel und Untertitel
val titel = varchar("titel", 255)
val untertitel = varchar("untertitel", 500).nullable()
// Datumswerte (kotlinx -> DB Date/Timestamp)
val datumVon = date("datum_von")
val datumBis = date("datum_bis")
val nennungsschluss = datetime("nennungsschluss").nullable()
// NennungsArt Liste -> Einfache Speicherung als CSV-String für den Anfang
// Bessere Lösung später: Eigene Zwischentabelle (TurnierNennungsArtMapping)
val nennungsArtCsv = text("nennungs_art_csv").nullable() // Z.B. "EIGENES_ONLINE,DIREKT_VERANSTALTER_TELEFON"
val nennungsHinweis = text("nennungs_hinweis").nullable()
val eigenesNennsystemUrl = varchar("eigenes_nennsystem_url", 500).nullable()
// Geldwerte (KMP BigDecimal -> DB Varchar)
// Konvertierung muss im Code (Service-Schicht) erfolgen!
// Alternative: decimal("nenngeld", 10, 2).nullable() - erfordert Konvertierungslogik KMP<->JVM BigDecimal
val nenngeld = varchar("nenngeld", 50).nullable()
val startgeldStandard = varchar("startgeld_standard", 50).nullable()
// Plätze (List<Platz>) -> Besser in eigener Tabelle "PlaetzeTable" mit FK zu Turnier.
// Hier *nicht* direkt speichern.
// Personen-Referenzen (FKs)
val turnierleiterId = uuid("turnierleiter_id").references(PersonenTable.id).nullable()
val turnierbeauftragterId = uuid("turnierbeauftragter_id").references(PersonenTable.id).nullable()
// Listen von Personen-IDs -> Einfache Speicherung als CSV-String für den Anfang
// Bessere Lösung später: Eigene Zwischentabellen (TurnierRichterMapping, TurnierParcoursbauerMapping etc.)
val richterIdsCsv = text("richter_ids_csv").nullable() // z.B. "uuid1,uuid2,uuid3"
val parcoursbauerIdsCsv = text("parcoursbauer_ids_csv").nullable()
val parcoursAssistentIdsCsv = text("parcours_assistent_ids_csv").nullable()
// Info-Texte
val tierarztInfos = text("tierarzt_infos").nullable()
val hufschmiedInfo = text("hufschmied_info").nullable()
// Meldestelle
val meldestelleVerantwortlicherId = uuid("meldestelle_verantwortlicher_id").references(PersonenTable.id).nullable()
val meldestelleTelefon = varchar("meldestelle_telefon", 50).nullable()
val meldestelleOeffnungszeiten = varchar("meldestelle_oeffnungszeiten", 255).nullable()
val ergebnislistenUrl = varchar("ergebnislisten_url", 500).nullable()
// Komplexe Listen -> Besser eigene Tabellen oder JSONB (PostgreSQL)
// Hier *nicht* direkt speichern:
// - verfuegbareArtikel: List<Artikel> -> Eigene Tabelle TurnierArtikelMapping
// - meisterschaftRefs: List<MeisterschaftReferenz> -> Eigene Tabelle TurnierMeisterschaftMapping
// Timestamps (kotlinx Instant -> DB Timestamp mit Zeitzone)
val createdAt = timestamp("created_at")
val updatedAt = timestamp("updated_at")
// Primärschlüssel definieren
override val primaryKey = PrimaryKey(id)
}
@@ -1,48 +0,0 @@
package at.mocode.tables
import at.mocode.model.enums.VeranstalterTyp
import org.jetbrains.exposed.sql.Table
import org.jetbrains.exposed.sql.kotlin.datetime.date
import org.jetbrains.exposed.sql.kotlin.datetime.timestamp
// --- Tabelle für Veranstaltungen ---
object VeranstaltungenTable : Table("veranstaltungen") {
val id = uuid("id") // KMP Uuid -> DB UUID
val name = varchar("name", 255)
val datumVon = date("datum_von") // kotlinx.datetime.LocalDate
val datumBis = date("datum_bis") // kotlinx.datetime.LocalDate
// Veranstalter Infos
val veranstalterName = varchar("veranstalter_name", 255)
val veranstalterOepsNummer = varchar("veranstalter_oeps_nr", 10).nullable()
val veranstalterTyp =
enumerationByName("veranstalter_typ", 20, VeranstalterTyp::class).default(VeranstalterTyp.UNBEKANNT)
// Ort Infos
val veranstaltungsortName = varchar("veranstaltungsort_name", 255)
val veranstaltungsortAdresse = varchar("veranstaltungsort_adresse", 500)
// Kontakt Infos
val kontaktpersonName = varchar("kontaktperson_name", 200).nullable()
val kontaktTelefon = varchar("kontakt_telefon", 50).nullable()
val kontaktEmail = varchar("kontakt_email", 255).nullable()
// Weitere Infos
val webseite = varchar("webseite", 500).nullable()
val logoUrl = varchar("logo_url", 500).nullable()
val anfahrtsplanInfo = text("anfahrtsplan_info").nullable()
// Sponsoren als einfacher Text (CSV oder ähnlich)
val sponsorInfosCsv = text("sponsor_infos_csv").nullable()
// Rechtliche Texte
val dsgvoText = text("dsgvo_text").nullable()
val haftungsText = text("haftungs_text").nullable()
val sonstigeBesondereBestimmungen = text("sonstige_bestimmungen").nullable()
// Timestamps
val createdAt = timestamp("created_at") // kotlinx.datetime.Instant
val updatedAt = timestamp("updated_at") // kotlinx.datetime.Instant
override val primaryKey = PrimaryKey(id)
}
@@ -1,24 +0,0 @@
package at.mocode.tables
import org.jetbrains.exposed.sql.Table
import org.jetbrains.exposed.sql.kotlin.datetime.timestamp
// --- Tabelle für Vereine ---
object VereineTable : Table("vereine") { // PostgreSQL Tabellenname
val id = uuid("id") // KMP Uuid -> DB UUID
val oepsVereinsNr = varchar("oeps_vereins_nr", 10).uniqueIndex() // Ist die OEPS Nummer eindeutig? Ja.
val name = varchar("name", 255)
val kuerzel = varchar("kuerzel", 50).nullable()
val bundesland = varchar("bundesland", 10).nullable() // Kürzel wie NÖ, W, ST etc.
val adresse = varchar("adresse", 255).nullable()
val plz = varchar("plz", 10).nullable()
val ort = varchar("ort", 100).nullable()
val email = varchar("email", 255).nullable()
val telefon = varchar("telefon", 50).nullable()
val webseite = varchar("webseite", 500).nullable()
val istAktiv = bool("ist_aktiv").default(true)
val createdAt = timestamp("created_at") // kotlinx.datetime.Instant
val updatedAt = timestamp("updated_at") // kotlinx.datetime.Instant
override val primaryKey = PrimaryKey(id)
}
+68 -9
View File
@@ -1,14 +1,73 @@
# Grundkonfiguration für Ktor in YAML
# Meldestelle Server Configuration
ktor:
deployment:
# Der Port, auf dem der Server lauschen soll
# Server port configuration
port: 8081
# port: ${PORT:8080} # Alternative: Nutzt Env-Variable PORT, sonst 8080
# Optional für Entwicklung: Server bei Änderungen neu laden
# watch:
# - classes
# - resources
# Connection timeout in seconds
connectionTimeout: 30
# Maximum number of concurrent connections
maxConnections: 1000
# Enable development mode with hot-reload (only for development)
watch:
- classes
- resources
application:
# Hier wird Ktor gesagt, welche Funktion die Konfiguration enthält
modules:
- at.mocode.ApplicationKt.module
- at.mocode.server.ApplicationKt.module
# Database Configuration
database:
# Database driver (postgresql for production, h2 for development)
driver: "${DB_DRIVER:postgresql}"
# Database connection settings
host: "${DB_HOST:localhost}"
port: "${DB_PORT:5432}"
name: "${DB_NAME:meldestelle}"
user: "${DB_USER:postgres}"
password: "${DB_PASSWORD:postgres}"
# Connection pool settings
pool:
maxSize: "${DB_POOL_SIZE:10}"
minIdle: "${DB_POOL_MIN_IDLE:2}"
idleTimeout: 10000
connectionTimeout: 5000
maxLifetime: 1800000
# Security Configuration
security:
# JWT configuration
jwt:
issuer: "meldestelle-server"
audience: "meldestelle-clients"
realm: "meldestelle"
# Secret should be set via environment variable in production
secret: "${JWT_SECRET:dev-secret-key-change-in-production}"
# Token validity duration in milliseconds (24 hours)
validity: 86400000
# CORS Configuration
cors:
# Allow requests from these origins
allowedHosts:
- "localhost:3000"
- "127.0.0.1:3000"
- "meldestelle.mocode.at"
# Allow these HTTP methods
allowedMethods:
- GET
- POST
- PUT
- DELETE
- OPTIONS
# Allow credentials (cookies, auth headers)
allowCredentials: true
# Application-specific settings
application:
name: "Meldestelle Server"
version: "1.0.0"
environment: "${ENVIRONMENT:development}"
# Feature flags
features:
enableRegistration: true
enableEmailNotifications: "${ENABLE_EMAIL:false}"
+28 -2
View File
@@ -1,12 +1,38 @@
<configuration>
<!-- Console appender configuration -->
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{YYYY-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>
<root level="trace">
<!-- File appender for important logs -->
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>logs/meldestelle.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!-- daily rollover -->
<fileNamePattern>logs/meldestelle.%d{yyyy-MM-dd}.log</fileNamePattern>
<!-- keep 30 days' worth of history -->
<maxHistory>30</maxHistory>
</rollingPolicy>
<encoder>
<pattern>%d{YYYY-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>
<!-- Set default log level to INFO for production use -->
<root level="INFO">
<appender-ref ref="STDOUT"/>
<appender-ref ref="FILE"/>
</root>
<!-- Application-specific logger configuration -->
<logger name="at.mocode" level="DEBUG"/>
<!-- Third-party library configurations -->
<logger name="org.eclipse.jetty" level="INFO"/>
<logger name="io.netty" level="INFO"/>
</configuration>
<logger name="org.hibernate.SQL" level="INFO"/>
<logger name="com.zaxxer.hikari" level="INFO"/>
<logger name="org.jetbrains.exposed" level="INFO"/>
</configuration>
@@ -0,0 +1,48 @@
package at.mocode.server
import org.junit.jupiter.api.Assertions.*
import org.junit.jupiter.api.Test
import org.slf4j.LoggerFactory
import java.io.File
/**
* Basic tests for the application
*/
class ApplicationTest {
private val logger = LoggerFactory.getLogger(ApplicationTest::class.java)
@Test
fun testEnvironmentSetup() {
// Set test environment flag
System.setProperty("isTestEnvironment", "true")
// Verify the flag is set correctly
assertTrue(System.getProperty("isTestEnvironment").toBoolean())
logger.info("Test environment flag set successfully")
}
@Test
fun testApplicationFilesExist() {
// Verify the Application.kt file exists
val applicationFile = File("src/main/kotlin/at/mocode/server/Application.kt")
assertTrue(applicationFile.exists() || File("server/" + applicationFile.path).exists(),
"Application.kt file should exist")
// Verify the Database.kt file exists
val databaseFile = File("src/main/kotlin/at/mocode/server/plugins/Database.kt")
assertTrue(databaseFile.exists() || File("server/" + databaseFile.path).exists(),
"Database.kt file should exist")
logger.info("Application files exist")
}
@Test
fun testConfigurationFileExists() {
// Verify the application.yaml file exists
val configFile = File("src/main/resources/application.yaml")
assertTrue(configFile.exists() || File("server/" + configFile.path).exists(),
"application.yaml file should exist")
logger.info("Configuration file exists")
}
}
@@ -0,0 +1,223 @@
package at.mocode.server.plugins
import at.mocode.server.tables.*
import io.ktor.server.config.*
import org.jetbrains.exposed.sql.Database
import org.jetbrains.exposed.sql.SchemaUtils
import org.jetbrains.exposed.sql.transactions.transaction
import org.junit.jupiter.api.AfterEach
import org.junit.jupiter.api.Assertions.*
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.io.TempDir
import org.slf4j.LoggerFactory
import java.io.File
/**
* Tests for the Database.kt file
*/
class DatabaseTest {
private val logger = LoggerFactory.getLogger(DatabaseTest::class.java)
// Create a temporary directory for test resources
@TempDir
lateinit var tempDir: File
@BeforeEach
fun setUp() {
// Clear any system properties that might affect the tests
System.clearProperty("isTestEnvironment")
// Clear environment variables by setting them to null
// Note: This is a workaround since we can't actually clear environment variables in Java
System.getProperties().remove("DB_HOST")
System.getProperties().remove("DB_NAME")
System.getProperties().remove("DB_USER")
System.getProperties().remove("DB_PASSWORD")
}
@AfterEach
fun tearDown() {
// Clear any system properties set during tests
System.clearProperty("isTestEnvironment")
System.getProperties().remove("DB_HOST")
System.getProperties().remove("DB_NAME")
System.getProperties().remove("DB_USER")
System.getProperties().remove("DB_PASSWORD")
}
@Test
fun testTestDatabaseConfiguration() {
// Set test environment flag
System.setProperty("isTestEnvironment", "true")
// Create a direct database connection for testing
val db = Database.connect(
url = "jdbc:h2:mem:test;DB_CLOSE_DELAY=-1;MODE=PostgreSQL",
driver = "org.h2.Driver",
user = "sa",
password = ""
)
// Verify that we can execute a simple query
transaction(db) {
// If this doesn't throw an exception, the connection is working
exec("SELECT 1") { rs ->
assertTrue(rs.next())
assertEquals(1, rs.getInt(1))
true
}
logger.info("Test database connection verified")
}
}
@Test
fun testDevelopmentDatabaseConfiguration() {
// Ensure test environment flag is not set
System.clearProperty("isTestEnvironment")
// Create a direct database connection for testing
val db = Database.connect(
url = "jdbc:h2:mem:dev;DB_CLOSE_DELAY=-1;MODE=PostgreSQL",
driver = "org.h2.Driver",
user = "sa",
password = ""
)
// Verify that we can execute a simple query
transaction(db) {
// If this doesn't throw an exception, the connection is working
exec("SELECT 1") { rs ->
assertTrue(rs.next())
assertEquals(1, rs.getInt(1))
true
}
logger.info("Development database connection verified")
}
}
@Test
fun testSchemaInitialization() {
// Set test environment flag
System.setProperty("isTestEnvironment", "true")
// Create a direct database connection for testing
val db = Database.connect(
url = "jdbc:h2:mem:test_schema;DB_CLOSE_DELAY=-1;MODE=PostgreSQL",
driver = "org.h2.Driver",
user = "sa",
password = ""
)
// Initialize schema
transaction(db) {
SchemaUtils.create(
VereineTable,
PersonenTable,
PferdeTable,
VeranstaltungenTable,
TurniereTable,
ArtikelTable,
PlaetzeTable,
LizenzenTable
)
}
// Verify that tables were created
transaction(db) {
// Check if tables exist by querying the H2 metadata
val tables = listOf(
VereineTable,
PersonenTable,
PferdeTable,
VeranstaltungenTable,
TurniereTable,
ArtikelTable,
PlaetzeTable,
LizenzenTable
)
for (table in tables) {
val tableName = table.tableName.uppercase()
val result = exec("SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = '$tableName'") { rs ->
rs.next()
rs.getInt(1)
}
assertEquals(1, result, "Table $tableName should exist")
}
logger.info("Schema initialization verified")
}
}
@Test
fun testErrorHandlingInTestEnvironment() {
// Set test environment flag
System.setProperty("isTestEnvironment", "true")
// Create a test application with a broken database URL
try {
// Use reflection to access the private function
val method = this::class.java.classLoader
.loadClass("at.mocode.server.plugins.DatabaseKt")
.getDeclaredMethod("configureTestDatabase", org.slf4j.Logger::class.java)
method.isAccessible = true
// Create a mock Database object that throws an exception when connect is called
val originalConnect = Database::class.java.getDeclaredMethod("connect",
String::class.java, String::class.java, String::class.java, String::class.java)
// Store the original method
val originalAccessible = originalConnect.canAccess(originalConnect)
originalConnect.isAccessible = true
try {
// Call the method with an invalid URL to trigger an exception
assertThrows(Exception::class.java) {
method.invoke(null, logger)
}
logger.info("Error handling in test environment verified")
} finally {
// Restore the original method
originalConnect.isAccessible = originalAccessible
}
} catch (e: Exception) {
// If we can't use reflection, just log a message
logger.warn("Could not test error handling using reflection: ${e.message}")
}
}
@Test
fun testProductionDatabaseConfigurationValidation() {
// Ensure test environment flag is not set
System.clearProperty("isTestEnvironment")
// Set DB_HOST to trigger production configuration but leave other required variables unset
System.setProperty("DB_HOST", "localhost")
System.getProperties().remove("DB_NAME")
System.getProperties().remove("DB_USER")
System.getProperties().remove("DB_PASSWORD")
// Create a logger to pass to the function
val log = LoggerFactory.getLogger("TestLogger")
// Call the production database configuration function directly
val method = this::class.java.classLoader
.loadClass("at.mocode.server.plugins.DatabaseKt")
.getDeclaredMethod("configureProductionDatabase", org.slf4j.Logger::class.java, ApplicationConfig::class.java)
method.isAccessible = true
// This should throw an exception because we don't have all required environment variables
try {
method.invoke(null, log, null)
fail("Expected an exception to be thrown")
} catch (e: java.lang.reflect.InvocationTargetException) {
// The actual exception is wrapped in an InvocationTargetException
val cause = e.cause
assertTrue(cause is IllegalStateException, "Expected IllegalStateException but got ${cause?.javaClass?.name}")
logger.info("Production database configuration validation verified: ${cause?.message}")
}
}
}
+7 -1
View File
@@ -1,4 +1,4 @@
import org.jetbrains.kotlin.gradle.ExperimentalWasmDsl
import org.jetbrains.kotlin.gradle.targets.js.dsl.ExperimentalWasmDsl
import org.jetbrains.kotlin.gradle.targets.js.webpack.KotlinWebpackConfig
plugins {
@@ -37,6 +37,12 @@ kotlin {
}
}
val commonTest by getting {
dependencies {
implementation(kotlin("test"))
}
}
// val jvmMain by getting {
// dependsOn(commonMain)
// }
@@ -1,8 +1,8 @@
package at.mocode.model.entitaeten
package at.mocode.shared.model.entitaeten
import at.mocode.model.serializers.BigDecimalSerializer
import at.mocode.model.serializers.KotlinInstantSerializer
import at.mocode.model.serializers.UuidSerializer
import at.mocode.shared.model.serializers.BigDecimalSerializer
import at.mocode.shared.model.serializers.KotlinInstantSerializer
import at.mocode.shared.model.serializers.UuidSerializer
import com.benasher44.uuid.Uuid
import com.benasher44.uuid.uuid4
import com.ionspin.kotlin.bignum.decimal.BigDecimal
@@ -1,6 +1,6 @@
package at.mocode.model.entitaeten
package at.mocode.shared.model.entitaeten
import at.mocode.model.serializers.UuidSerializer
import at.mocode.shared.model.serializers.UuidSerializer
import com.benasher44.uuid.Uuid
import com.benasher44.uuid.uuid4
import kotlinx.serialization.Serializable
@@ -1,7 +1,7 @@
package at.mocode.model.entitaeten
package at.mocode.shared.model.entitaeten
import at.mocode.model.enums.PlatzTyp
import at.mocode.model.serializers.UuidSerializer
import at.mocode.shared.model.enums.PlatzTyp
import at.mocode.shared.model.serializers.UuidSerializer
import com.benasher44.uuid.Uuid
import com.benasher44.uuid.uuid4
import kotlinx.serialization.Serializable
@@ -1,7 +1,12 @@
package at.mocode.model.entitaeten
package at.mocode.shared.model.entitaeten
import at.mocode.model.enums.NennungsArt
import at.mocode.model.serializers.*
import at.mocode.shared.model.enums.NennungsArt
import at.mocode.shared.model.serializers.BigDecimalSerializer
import at.mocode.shared.model.serializers.KotlinInstantSerializer
import at.mocode.shared.model.serializers.KotlinLocalDateSerializer
import at.mocode.shared.model.serializers.KotlinLocalDateTimeSerializer
import at.mocode.shared.model.serializers.UuidSerializer
import com.benasher44.uuid.Uuid
import com.benasher44.uuid.uuid4
import com.ionspin.kotlin.bignum.decimal.BigDecimal
@@ -1,9 +1,9 @@
package at.mocode.model.entitaeten
package at.mocode.shared.model.entitaeten
import at.mocode.model.enums.VeranstalterTyp
import at.mocode.model.serializers.KotlinInstantSerializer
import at.mocode.model.serializers.KotlinLocalDateSerializer
import at.mocode.model.serializers.UuidSerializer
import at.mocode.shared.model.enums.VeranstalterTyp
import at.mocode.shared.model.serializers.KotlinInstantSerializer
import at.mocode.shared.model.serializers.KotlinLocalDateSerializer
import at.mocode.shared.model.serializers.UuidSerializer
import com.benasher44.uuid.Uuid
import com.benasher44.uuid.uuid4
import kotlinx.datetime.Clock
@@ -1,4 +1,4 @@
package at.mocode.model.enums
package at.mocode.shared.model.enums
import kotlinx.serialization.Serializable
@@ -1,4 +1,4 @@
package at.mocode.model.serializers
package at.mocode.shared.model.serializers
import com.benasher44.uuid.Uuid
import com.benasher44.uuid.uuidFrom
@@ -1,8 +1,8 @@
package at.mocode.model.stammdaten
package at.mocode.shared.model.stammdaten
import at.mocode.model.enums.LizenzTyp
import at.mocode.model.enums.Sparte
import at.mocode.model.serializers.KotlinLocalDateSerializer
import at.mocode.shared.model.enums.LizenzTyp
import at.mocode.shared.model.enums.Sparte
import at.mocode.shared.model.serializers.KotlinLocalDateSerializer
import kotlinx.datetime.LocalDate
import kotlinx.serialization.Serializable
@@ -1,10 +1,10 @@
package at.mocode.model.stammdaten
package at.mocode.shared.model.stammdaten
import at.mocode.model.enums.FunktionaerRolle
import at.mocode.model.enums.Geschlecht
import at.mocode.model.serializers.KotlinInstantSerializer
import at.mocode.model.serializers.KotlinLocalDateSerializer
import at.mocode.model.serializers.UuidSerializer
import at.mocode.shared.model.enums.FunktionaerRolle
import at.mocode.shared.model.enums.Geschlecht
import at.mocode.shared.model.serializers.KotlinInstantSerializer
import at.mocode.shared.model.serializers.KotlinLocalDateSerializer
import at.mocode.shared.model.serializers.UuidSerializer
import com.benasher44.uuid.Uuid
import com.benasher44.uuid.uuid4
import kotlinx.datetime.Clock
@@ -1,8 +1,8 @@
package at.mocode.model.stammdaten
package at.mocode.shared.model.stammdaten
import at.mocode.model.enums.GeschlechtPferd
import at.mocode.model.serializers.KotlinInstantSerializer
import at.mocode.model.serializers.UuidSerializer
import at.mocode.shared.model.enums.GeschlechtPferd
import at.mocode.shared.model.serializers.KotlinInstantSerializer
import at.mocode.shared.model.serializers.UuidSerializer
import com.benasher44.uuid.Uuid
import com.benasher44.uuid.uuid4
import kotlinx.datetime.Clock
@@ -1,7 +1,7 @@
package at.mocode.model.stammdaten
package at.mocode.shared.model.stammdaten
import at.mocode.model.serializers.KotlinInstantSerializer
import at.mocode.model.serializers.UuidSerializer
import at.mocode.shared.model.serializers.KotlinInstantSerializer
import at.mocode.shared.model.serializers.UuidSerializer
import com.benasher44.uuid.Uuid
import com.benasher44.uuid.uuid4
import kotlinx.datetime.Clock
@@ -0,0 +1,161 @@
package at.mocode.shared.model.entitaeten
import at.mocode.shared.model.serializers.BigDecimalSerializer
import at.mocode.shared.model.serializers.KotlinInstantSerializer
import at.mocode.shared.model.serializers.UuidSerializer
import com.benasher44.uuid.Uuid
import com.benasher44.uuid.uuid4
import com.ionspin.kotlin.bignum.decimal.BigDecimal
import kotlinx.datetime.Clock
import kotlinx.datetime.Instant
import kotlinx.serialization.decodeFromString
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertNotEquals
import kotlin.test.assertNotNull
import kotlin.test.assertTrue
class ArtikelTest {
@Test
fun testCreateArtikel() {
// Create an Artikel with minimal required parameters
val artikel = Artikel(
bezeichnung = "Test Artikel",
preis = BigDecimal.parseString("10.50"),
einheit = "Stück"
)
// Verify required fields
assertEquals("Test Artikel", artikel.bezeichnung)
assertEquals(BigDecimal.parseString("10.50"), artikel.preis)
assertEquals("Stück", artikel.einheit)
// Verify default values
assertNotNull(artikel.id)
assertEquals(false, artikel.istVerbandsabgabe)
assertNotNull(artikel.createdAt)
assertNotNull(artikel.updatedAt)
}
@Test
fun testCreateArtikelWithAllParameters() {
// Create an Artikel with all parameters
val id = uuid4()
val now = Clock.System.now()
val artikel = Artikel(
id = id,
bezeichnung = "Vollständiger Artikel",
preis = BigDecimal.parseString("99.99"),
einheit = "Paket",
istVerbandsabgabe = true,
createdAt = now,
updatedAt = now
)
// Verify all fields
assertEquals(id, artikel.id)
assertEquals("Vollständiger Artikel", artikel.bezeichnung)
assertEquals(BigDecimal.parseString("99.99"), artikel.preis)
assertEquals("Paket", artikel.einheit)
assertEquals(true, artikel.istVerbandsabgabe)
assertEquals(now, artikel.createdAt)
assertEquals(now, artikel.updatedAt)
}
@Test
fun testModifyArtikel() {
// Create an Artikel
val artikel = Artikel(
bezeichnung = "Original Artikel",
preis = BigDecimal.parseString("10.00"),
einheit = "Stück"
)
val originalUpdatedAt = artikel.updatedAt
// Modify properties
artikel.bezeichnung = "Geänderter Artikel"
artikel.preis = BigDecimal.parseString("15.00")
artikel.einheit = "Box"
artikel.istVerbandsabgabe = true
artikel.updatedAt = Clock.System.now()
// Verify modifications
assertEquals("Geänderter Artikel", artikel.bezeichnung)
assertEquals(BigDecimal.parseString("15.00"), artikel.preis)
assertEquals("Box", artikel.einheit)
assertEquals(true, artikel.istVerbandsabgabe)
assertNotEquals(originalUpdatedAt, artikel.updatedAt)
}
@Test
fun testSerializationDeserialization() {
// Create an Artikel
val artikel = Artikel(
bezeichnung = "Serialisierter Artikel",
preis = BigDecimal.parseString("25.75"),
einheit = "Einheit",
istVerbandsabgabe = true
)
// Serialize to JSON
val json = Json {
prettyPrint = true
encodeDefaults = true
}
val jsonString = json.encodeToString(artikel)
// Verify JSON contains expected fields
assertTrue(jsonString.contains("\"bezeichnung\""), "JSON should contain bezeichnung field")
assertTrue(jsonString.contains("\"Serialisierter Artikel\""), "JSON should contain value Serialisierter Artikel")
assertTrue(jsonString.contains("\"preis\""), "JSON should contain preis field")
assertTrue(jsonString.contains("\"25.75\""), "JSON should contain value 25.75")
assertTrue(jsonString.contains("\"einheit\""), "JSON should contain einheit field")
assertTrue(jsonString.contains("\"Einheit\""), "JSON should contain value Einheit")
assertTrue(jsonString.contains("\"istVerbandsabgabe\""), "JSON should contain istVerbandsabgabe field")
assertTrue(jsonString.contains("true"), "JSON should contain value true")
// Deserialize from JSON
val deserializedArtikel = json.decodeFromString<Artikel>(jsonString)
// Verify deserialized object matches original
assertEquals(artikel.id, deserializedArtikel.id)
assertEquals(artikel.bezeichnung, deserializedArtikel.bezeichnung)
assertEquals(artikel.preis, deserializedArtikel.preis)
assertEquals(artikel.einheit, deserializedArtikel.einheit)
assertEquals(artikel.istVerbandsabgabe, deserializedArtikel.istVerbandsabgabe)
assertEquals(artikel.createdAt, deserializedArtikel.createdAt)
assertEquals(artikel.updatedAt, deserializedArtikel.updatedAt)
}
@Test
fun testCopyArtikel() {
// Create an Artikel
val original = Artikel(
bezeichnung = "Original Artikel",
preis = BigDecimal.parseString("10.00"),
einheit = "Stück"
)
// Create a copy with some modified properties
val copy = original.copy(
bezeichnung = "Kopierter Artikel",
preis = BigDecimal.parseString("12.50")
)
// Verify copied properties
assertEquals(original.id, copy.id)
assertEquals(original.einheit, copy.einheit)
assertEquals(original.istVerbandsabgabe, copy.istVerbandsabgabe)
assertEquals(original.createdAt, copy.createdAt)
assertEquals(original.updatedAt, copy.updatedAt)
// Verify modified properties
assertEquals("Kopierter Artikel", copy.bezeichnung)
assertEquals(BigDecimal.parseString("12.50"), copy.preis)
}
}
@@ -0,0 +1,169 @@
package at.mocode.shared.model.entitaeten
import at.mocode.shared.model.serializers.UuidSerializer
import com.benasher44.uuid.Uuid
import com.benasher44.uuid.uuid4
import kotlinx.serialization.decodeFromString
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertNotEquals
import kotlin.test.assertNotNull
import kotlin.test.assertTrue
class MeisterschaftReferenzTest {
@Test
fun testCreateMeisterschaftReferenz() {
// Create a MeisterschaftReferenz with minimal required parameters
val meisterschaftId = uuid4()
val meisterschaftReferenz = MeisterschaftReferenz(
meisterschaftId = meisterschaftId,
name = "Test Meisterschaft",
betrifftBewerbNummern = listOf(1, 2, 3),
berechnungsstrategie = null,
reglementUrl = null
)
// Verify required fields
assertEquals(meisterschaftId, meisterschaftReferenz.meisterschaftId)
assertEquals("Test Meisterschaft", meisterschaftReferenz.name)
assertEquals(listOf(1, 2, 3), meisterschaftReferenz.betrifftBewerbNummern)
// Verify default values
assertNotNull(meisterschaftReferenz.id)
assertEquals(null, meisterschaftReferenz.berechnungsstrategie)
assertEquals(null, meisterschaftReferenz.reglementUrl)
}
@Test
fun testCreateMeisterschaftReferenzWithAllParameters() {
// Create a MeisterschaftReferenz with all parameters
val id = uuid4()
val meisterschaftId = uuid4()
val meisterschaftReferenz = MeisterschaftReferenz(
id = id,
meisterschaftId = meisterschaftId,
name = "Vollständige Meisterschaft",
betrifftBewerbNummern = listOf(10, 20, 30),
berechnungsstrategie = "Punktesystem A",
reglementUrl = "https://example.com/reglement"
)
// Verify all fields
assertEquals(id, meisterschaftReferenz.id)
assertEquals(meisterschaftId, meisterschaftReferenz.meisterschaftId)
assertEquals("Vollständige Meisterschaft", meisterschaftReferenz.name)
assertEquals(listOf(10, 20, 30), meisterschaftReferenz.betrifftBewerbNummern)
assertEquals("Punktesystem A", meisterschaftReferenz.berechnungsstrategie)
assertEquals("https://example.com/reglement", meisterschaftReferenz.reglementUrl)
}
@Test
fun testModifyMeisterschaftReferenz() {
// Create a MeisterschaftReferenz
val meisterschaftId = uuid4()
val meisterschaftReferenz = MeisterschaftReferenz(
meisterschaftId = meisterschaftId,
name = "Original Meisterschaft",
betrifftBewerbNummern = listOf(1, 2, 3),
berechnungsstrategie = null,
reglementUrl = null
)
val newMeisterschaftId = uuid4()
// Modify properties
meisterschaftReferenz.meisterschaftId = newMeisterschaftId
meisterschaftReferenz.name = "Geänderte Meisterschaft"
meisterschaftReferenz.betrifftBewerbNummern = listOf(4, 5, 6)
meisterschaftReferenz.berechnungsstrategie = "Neues Punktesystem"
meisterschaftReferenz.reglementUrl = "https://example.com/neues-reglement"
// Verify modifications
assertEquals(newMeisterschaftId, meisterschaftReferenz.meisterschaftId)
assertEquals("Geänderte Meisterschaft", meisterschaftReferenz.name)
assertEquals(listOf(4, 5, 6), meisterschaftReferenz.betrifftBewerbNummern)
assertEquals("Neues Punktesystem", meisterschaftReferenz.berechnungsstrategie)
assertEquals("https://example.com/neues-reglement", meisterschaftReferenz.reglementUrl)
}
@Test
fun testSerializationDeserialization() {
// Create a MeisterschaftReferenz
val meisterschaftId = uuid4()
val meisterschaftReferenz = MeisterschaftReferenz(
meisterschaftId = meisterschaftId,
name = "Serialisierte Meisterschaft",
betrifftBewerbNummern = listOf(7, 8, 9),
berechnungsstrategie = "Punktesystem B",
reglementUrl = "https://example.com/serialisiert"
)
// Serialize to JSON
val json = Json {
prettyPrint = true
encodeDefaults = true
}
val jsonString = json.encodeToString(meisterschaftReferenz)
// Verify JSON contains expected fields
assertTrue(jsonString.contains("\"meisterschaftId\""), "JSON should contain meisterschaftId field")
assertTrue(jsonString.contains(meisterschaftId.toString()), "JSON should contain meisterschaftId value")
assertTrue(jsonString.contains("\"name\""), "JSON should contain name field")
assertTrue(jsonString.contains("\"Serialisierte Meisterschaft\""), "JSON should contain name value")
assertTrue(jsonString.contains("\"betrifftBewerbNummern\""), "JSON should contain betrifftBewerbNummern field")
assertTrue(jsonString.contains("7"), "JSON should contain betrifftBewerbNummern value 7")
assertTrue(jsonString.contains("8"), "JSON should contain betrifftBewerbNummern value 8")
assertTrue(jsonString.contains("9"), "JSON should contain betrifftBewerbNummern value 9")
assertTrue(jsonString.contains("\"berechnungsstrategie\""), "JSON should contain berechnungsstrategie field")
assertTrue(jsonString.contains("\"Punktesystem B\""), "JSON should contain berechnungsstrategie value")
assertTrue(jsonString.contains("\"reglementUrl\""), "JSON should contain reglementUrl field")
assertTrue(jsonString.contains("\"https://example.com/serialisiert\""), "JSON should contain reglementUrl value")
// Deserialize from JSON
val deserializedMeisterschaftReferenz = json.decodeFromString<MeisterschaftReferenz>(jsonString)
// Verify deserialized object matches original
assertEquals(meisterschaftReferenz.id, deserializedMeisterschaftReferenz.id)
assertEquals(meisterschaftReferenz.meisterschaftId, deserializedMeisterschaftReferenz.meisterschaftId)
assertEquals(meisterschaftReferenz.name, deserializedMeisterschaftReferenz.name)
assertEquals(meisterschaftReferenz.betrifftBewerbNummern, deserializedMeisterschaftReferenz.betrifftBewerbNummern)
assertEquals(meisterschaftReferenz.berechnungsstrategie, deserializedMeisterschaftReferenz.berechnungsstrategie)
assertEquals(meisterschaftReferenz.reglementUrl, deserializedMeisterschaftReferenz.reglementUrl)
}
@Test
fun testCopyMeisterschaftReferenz() {
// Create a MeisterschaftReferenz
val meisterschaftId = uuid4()
val original = MeisterschaftReferenz(
meisterschaftId = meisterschaftId,
name = "Original Meisterschaft",
betrifftBewerbNummern = listOf(1, 2, 3),
berechnungsstrategie = "Original Strategie",
reglementUrl = null
)
val newMeisterschaftId = uuid4()
// Create a copy with some modified properties
val copy = original.copy(
name = "Kopierte Meisterschaft",
meisterschaftId = newMeisterschaftId,
reglementUrl = "https://example.com/kopie"
)
// Verify copied properties
assertEquals(original.id, copy.id)
assertEquals(original.betrifftBewerbNummern, copy.betrifftBewerbNummern)
assertEquals(original.berechnungsstrategie, copy.berechnungsstrategie)
// Verify modified properties
assertEquals("Kopierte Meisterschaft", copy.name)
assertEquals(newMeisterschaftId, copy.meisterschaftId)
assertEquals("https://example.com/kopie", copy.reglementUrl)
}
}
@@ -0,0 +1,145 @@
package at.mocode.shared.model.entitaeten
import at.mocode.shared.model.enums.PlatzTyp
import at.mocode.shared.model.serializers.UuidSerializer
import com.benasher44.uuid.Uuid
import com.benasher44.uuid.uuid4
import kotlinx.serialization.decodeFromString
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertNotEquals
import kotlin.test.assertNotNull
import kotlin.test.assertTrue
class PlatzTest {
@Test
fun testCreatePlatz() {
// Create a Platz with minimal required parameters
val platz = Platz(
name = "Hauptplatz",
dimension = null,
boden = null,
typ = PlatzTyp.AUSTRAGUNG
)
// Verify required fields
assertEquals("Hauptplatz", platz.name)
assertEquals(PlatzTyp.AUSTRAGUNG, platz.typ)
// Verify default values
assertNotNull(platz.id)
assertEquals(null, platz.dimension)
assertEquals(null, platz.boden)
}
@Test
fun testCreatePlatzWithAllParameters() {
// Create a Platz with all parameters
val id = uuid4()
val platz = Platz(
id = id,
name = "Vollständiger Platz",
dimension = "60x20m",
boden = "Sand",
typ = PlatzTyp.VORBEREITUNG
)
// Verify all fields
assertEquals(id, platz.id)
assertEquals("Vollständiger Platz", platz.name)
assertEquals("60x20m", platz.dimension)
assertEquals("Sand", platz.boden)
assertEquals(PlatzTyp.VORBEREITUNG, platz.typ)
}
@Test
fun testModifyPlatz() {
// Create a Platz
val platz = Platz(
name = "Original Platz",
dimension = null,
boden = null,
typ = PlatzTyp.AUSTRAGUNG
)
// Modify properties
platz.name = "Geänderter Platz"
platz.dimension = "80x40m"
platz.boden = "Gras"
platz.typ = PlatzTyp.LONGIEREN
// Verify modifications
assertEquals("Geänderter Platz", platz.name)
assertEquals("80x40m", platz.dimension)
assertEquals("Gras", platz.boden)
assertEquals(PlatzTyp.LONGIEREN, platz.typ)
}
@Test
fun testSerializationDeserialization() {
// Create a Platz
val platz = Platz(
name = "Serialisierter Platz",
dimension = "70x30m",
boden = "Kunstrasen",
typ = PlatzTyp.SONSTIGES
)
// Serialize to JSON
val json = Json {
prettyPrint = true
encodeDefaults = true
}
val jsonString = json.encodeToString(platz)
// Verify JSON contains expected fields
assertTrue(jsonString.contains("\"name\""), "JSON should contain name field")
assertTrue(jsonString.contains("\"Serialisierter Platz\""), "JSON should contain name value")
assertTrue(jsonString.contains("\"dimension\""), "JSON should contain dimension field")
assertTrue(jsonString.contains("\"70x30m\""), "JSON should contain dimension value")
assertTrue(jsonString.contains("\"boden\""), "JSON should contain boden field")
assertTrue(jsonString.contains("\"Kunstrasen\""), "JSON should contain boden value")
assertTrue(jsonString.contains("\"typ\""), "JSON should contain typ field")
assertTrue(jsonString.contains("\"SONSTIGES\""), "JSON should contain typ value")
// Deserialize from JSON
val deserializedPlatz = json.decodeFromString<Platz>(jsonString)
// Verify deserialized object matches original
assertEquals(platz.id, deserializedPlatz.id)
assertEquals(platz.name, deserializedPlatz.name)
assertEquals(platz.dimension, deserializedPlatz.dimension)
assertEquals(platz.boden, deserializedPlatz.boden)
assertEquals(platz.typ, deserializedPlatz.typ)
}
@Test
fun testCopyPlatz() {
// Create a Platz
val original = Platz(
name = "Original Platz",
dimension = "50x25m",
boden = "Holzspäne",
typ = PlatzTyp.VORBEREITUNG
)
// Create a copy with some modified properties
val copy = original.copy(
name = "Kopierter Platz",
typ = PlatzTyp.LONGIEREN
)
// Verify copied properties
assertEquals(original.id, copy.id)
assertEquals(original.dimension, copy.dimension)
assertEquals(original.boden, copy.boden)
// Verify modified properties
assertEquals("Kopierter Platz", copy.name)
assertEquals(PlatzTyp.LONGIEREN, copy.typ)
}
}
@@ -0,0 +1,441 @@
package at.mocode.shared.model.entitaeten
import at.mocode.shared.model.enums.NennungsArt
import at.mocode.shared.model.enums.PlatzTyp
import at.mocode.shared.model.serializers.BigDecimalSerializer
import at.mocode.shared.model.serializers.KotlinInstantSerializer
import at.mocode.shared.model.serializers.KotlinLocalDateSerializer
import at.mocode.shared.model.serializers.KotlinLocalDateTimeSerializer
import at.mocode.shared.model.serializers.UuidSerializer
import com.benasher44.uuid.Uuid
import com.benasher44.uuid.uuid4
import com.ionspin.kotlin.bignum.decimal.BigDecimal
import kotlinx.datetime.Clock
import kotlinx.datetime.Instant
import kotlinx.datetime.LocalDate
import kotlinx.datetime.LocalDateTime
import kotlinx.serialization.decodeFromString
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertNotEquals
import kotlin.test.assertNotNull
import kotlin.test.assertTrue
class TurnierTest {
@Test
fun testCreateTurnier() {
// Create a Turnier with minimal required parameters
val veranstaltungId = uuid4()
val datumVon = LocalDate(2023, 6, 1)
val datumBis = LocalDate(2023, 6, 3)
val turnier = Turnier(
veranstaltungId = veranstaltungId,
oepsTurnierNr = "T12345",
titel = "Test Turnier",
untertitel = null,
datumVon = datumVon,
datumBis = datumBis,
nennungsschluss = null,
nennungsHinweis = null,
eigenesNennsystemUrl = null,
nenngeld = null,
startgeldStandard = null,
turnierleiterId = null,
turnierbeauftragterId = null,
tierarztInfos = null,
hufschmiedInfo = null,
meldestelleVerantwortlicherId = null,
meldestelleTelefon = null,
meldestelleOeffnungszeiten = null,
ergebnislistenUrl = null
)
// Verify required fields
assertEquals(veranstaltungId, turnier.veranstaltungId)
assertEquals("T12345", turnier.oepsTurnierNr)
assertEquals("Test Turnier", turnier.titel)
assertEquals(datumVon, turnier.datumVon)
assertEquals(datumBis, turnier.datumBis)
// Verify default values
assertNotNull(turnier.id)
assertEquals(null, turnier.untertitel)
assertEquals(null, turnier.nennungsschluss)
assertTrue(turnier.nennungsArt.isEmpty())
assertEquals(null, turnier.nennungsHinweis)
assertEquals(null, turnier.eigenesNennsystemUrl)
assertEquals(null, turnier.nenngeld)
assertEquals(null, turnier.startgeldStandard)
assertTrue(turnier.austragungsplaetze.isEmpty())
assertTrue(turnier.vorbereitungsplaetze.isEmpty())
assertEquals(null, turnier.turnierleiterId)
assertEquals(null, turnier.turnierbeauftragterId)
assertTrue(turnier.richterIds.isEmpty())
assertTrue(turnier.parcoursbauerIds.isEmpty())
assertTrue(turnier.parcoursAssistentIds.isEmpty())
assertEquals(null, turnier.tierarztInfos)
assertEquals(null, turnier.hufschmiedInfo)
assertEquals(null, turnier.meldestelleVerantwortlicherId)
assertEquals(null, turnier.meldestelleTelefon)
assertEquals(null, turnier.meldestelleOeffnungszeiten)
assertEquals(null, turnier.ergebnislistenUrl)
assertTrue(turnier.verfuegbareArtikel.isEmpty())
assertTrue(turnier.meisterschaftRefs.isEmpty())
assertNotNull(turnier.createdAt)
assertNotNull(turnier.updatedAt)
}
@Test
fun testCreateTurnierWithAllParameters() {
// Create a Turnier with all parameters
val id = uuid4()
val veranstaltungId = uuid4()
val datumVon = LocalDate(2023, 7, 15)
val datumBis = LocalDate(2023, 7, 17)
val nennungsschluss = LocalDateTime(2023, 7, 10, 23, 59, 59)
val turnierleiterId = uuid4()
val turnierbeauftragterId = uuid4()
val meldestelleVerantwortlicherId = uuid4()
val now = Clock.System.now()
// Create some test objects for lists
val nennungsArt = listOf(NennungsArt.OEPS_ZNS, NennungsArt.EIGENES_ONLINE)
val austragungsplatz = Platz(name = "Hauptplatz", dimension = "60x20m", boden = "Sand", typ = PlatzTyp.AUSTRAGUNG)
val vorbereitungsplatz = Platz(name = "Abreiteplatz", dimension = "40x20m", boden = "Sand", typ = PlatzTyp.VORBEREITUNG)
val richterIds = listOf(uuid4(), uuid4())
val parcoursbauerIds = listOf(uuid4())
val parcoursAssistentIds = listOf(uuid4())
val artikel = Artikel(bezeichnung = "Startgebühr", preis = BigDecimal.parseString("25.00"), einheit = "Start")
val meisterschaftRef = MeisterschaftReferenz(
meisterschaftId = uuid4(),
name = "Landesmeisterschaft",
betrifftBewerbNummern = listOf(1, 2, 3),
berechnungsstrategie = "Standard",
reglementUrl = null
)
val turnier = Turnier(
id = id,
veranstaltungId = veranstaltungId,
oepsTurnierNr = "T67890",
titel = "Vollständiges Turnier",
untertitel = "Mit allen Details",
datumVon = datumVon,
datumBis = datumBis,
nennungsschluss = nennungsschluss,
nennungsArt = nennungsArt,
nennungsHinweis = "Bitte rechtzeitig nennen",
eigenesNennsystemUrl = "https://example.com/nennung",
nenngeld = BigDecimal.parseString("50.00"),
startgeldStandard = BigDecimal.parseString("25.00"),
austragungsplaetze = listOf(austragungsplatz),
vorbereitungsplaetze = listOf(vorbereitungsplatz),
turnierleiterId = turnierleiterId,
turnierbeauftragterId = turnierbeauftragterId,
richterIds = richterIds,
parcoursbauerIds = parcoursbauerIds,
parcoursAssistentIds = parcoursAssistentIds,
tierarztInfos = "Dr. Vet, Tel: 12345",
hufschmiedInfo = "Hans Schmidt, Tel: 67890",
meldestelleVerantwortlicherId = meldestelleVerantwortlicherId,
meldestelleTelefon = "+43 123 456789",
meldestelleOeffnungszeiten = "8-18 Uhr",
ergebnislistenUrl = "https://example.com/ergebnisse",
verfuegbareArtikel = listOf(artikel),
meisterschaftRefs = listOf(meisterschaftRef),
createdAt = now,
updatedAt = now
)
// Verify all fields
assertEquals(id, turnier.id)
assertEquals(veranstaltungId, turnier.veranstaltungId)
assertEquals("T67890", turnier.oepsTurnierNr)
assertEquals("Vollständiges Turnier", turnier.titel)
assertEquals("Mit allen Details", turnier.untertitel)
assertEquals(datumVon, turnier.datumVon)
assertEquals(datumBis, turnier.datumBis)
assertEquals(nennungsschluss, turnier.nennungsschluss)
assertEquals(nennungsArt, turnier.nennungsArt)
assertEquals("Bitte rechtzeitig nennen", turnier.nennungsHinweis)
assertEquals("https://example.com/nennung", turnier.eigenesNennsystemUrl)
assertEquals(BigDecimal.parseString("50.00"), turnier.nenngeld)
assertEquals(BigDecimal.parseString("25.00"), turnier.startgeldStandard)
assertEquals(1, turnier.austragungsplaetze.size)
assertEquals(austragungsplatz.name, turnier.austragungsplaetze[0].name)
assertEquals(1, turnier.vorbereitungsplaetze.size)
assertEquals(vorbereitungsplatz.name, turnier.vorbereitungsplaetze[0].name)
assertEquals(turnierleiterId, turnier.turnierleiterId)
assertEquals(turnierbeauftragterId, turnier.turnierbeauftragterId)
assertEquals(richterIds, turnier.richterIds)
assertEquals(parcoursbauerIds, turnier.parcoursbauerIds)
assertEquals(parcoursAssistentIds, turnier.parcoursAssistentIds)
assertEquals("Dr. Vet, Tel: 12345", turnier.tierarztInfos)
assertEquals("Hans Schmidt, Tel: 67890", turnier.hufschmiedInfo)
assertEquals(meldestelleVerantwortlicherId, turnier.meldestelleVerantwortlicherId)
assertEquals("+43 123 456789", turnier.meldestelleTelefon)
assertEquals("8-18 Uhr", turnier.meldestelleOeffnungszeiten)
assertEquals("https://example.com/ergebnisse", turnier.ergebnislistenUrl)
assertEquals(1, turnier.verfuegbareArtikel.size)
assertEquals(artikel.bezeichnung, turnier.verfuegbareArtikel[0].bezeichnung)
assertEquals(1, turnier.meisterschaftRefs.size)
assertEquals(meisterschaftRef.name, turnier.meisterschaftRefs[0].name)
assertEquals(now, turnier.createdAt)
assertEquals(now, turnier.updatedAt)
}
@Test
fun testModifyTurnier() {
// Create a Turnier
val veranstaltungId = uuid4()
val datumVon = LocalDate(2023, 8, 1)
val datumBis = LocalDate(2023, 8, 3)
val turnier = Turnier(
veranstaltungId = veranstaltungId,
oepsTurnierNr = "T12345",
titel = "Original Turnier",
untertitel = null,
datumVon = datumVon,
datumBis = datumBis,
nennungsschluss = null,
nennungsHinweis = null,
eigenesNennsystemUrl = null,
nenngeld = null,
startgeldStandard = null,
turnierleiterId = null,
turnierbeauftragterId = null,
tierarztInfos = null,
hufschmiedInfo = null,
meldestelleVerantwortlicherId = null,
meldestelleTelefon = null,
meldestelleOeffnungszeiten = null,
ergebnislistenUrl = null
)
val originalUpdatedAt = turnier.updatedAt
val newVeranstaltungId = uuid4()
val newDatumVon = LocalDate(2023, 9, 1)
val newDatumBis = LocalDate(2023, 9, 3)
val newNennungsschluss = LocalDateTime(2023, 8, 25, 23, 59, 59)
val newTurnierleiterId = uuid4()
val newTurnierbeauftragterId = uuid4()
val newMeldestelleVerantwortlicherId = uuid4()
// Create some test objects for lists
val nennungsArt = listOf(NennungsArt.DIREKT_VERANSTALTER_EMAIL)
val austragungsplatz = Platz(name = "Neuer Hauptplatz", dimension = "70x30m", boden = "Gras", typ = PlatzTyp.AUSTRAGUNG)
val richterIds = listOf(uuid4())
val artikel = Artikel(bezeichnung = "Neue Startgebühr", preis = BigDecimal.parseString("30.00"), einheit = "Start")
// Modify properties
turnier.veranstaltungId = newVeranstaltungId
turnier.oepsTurnierNr = "T54321"
turnier.titel = "Geändertes Turnier"
turnier.untertitel = "Mit Untertitel"
turnier.datumVon = newDatumVon
turnier.datumBis = newDatumBis
turnier.nennungsschluss = newNennungsschluss
turnier.nennungsArt = nennungsArt
turnier.nennungsHinweis = "Neuer Hinweis"
turnier.eigenesNennsystemUrl = "https://example.com/neues-system"
turnier.nenngeld = BigDecimal.parseString("60.00")
turnier.startgeldStandard = BigDecimal.parseString("35.00")
turnier.austragungsplaetze = listOf(austragungsplatz)
turnier.turnierleiterId = newTurnierleiterId
turnier.turnierbeauftragterId = newTurnierbeauftragterId
turnier.richterIds = richterIds
turnier.tierarztInfos = "Neuer Tierarzt"
turnier.hufschmiedInfo = "Neuer Hufschmied"
turnier.meldestelleVerantwortlicherId = newMeldestelleVerantwortlicherId
turnier.meldestelleTelefon = "+43 987 654321"
turnier.meldestelleOeffnungszeiten = "9-17 Uhr"
turnier.ergebnislistenUrl = "https://example.com/neue-ergebnisse"
turnier.verfuegbareArtikel = listOf(artikel)
turnier.updatedAt = Clock.System.now()
// Verify modifications
assertEquals(newVeranstaltungId, turnier.veranstaltungId)
assertEquals("T54321", turnier.oepsTurnierNr)
assertEquals("Geändertes Turnier", turnier.titel)
assertEquals("Mit Untertitel", turnier.untertitel)
assertEquals(newDatumVon, turnier.datumVon)
assertEquals(newDatumBis, turnier.datumBis)
assertEquals(newNennungsschluss, turnier.nennungsschluss)
assertEquals(nennungsArt, turnier.nennungsArt)
assertEquals("Neuer Hinweis", turnier.nennungsHinweis)
assertEquals("https://example.com/neues-system", turnier.eigenesNennsystemUrl)
assertEquals(BigDecimal.parseString("60.00"), turnier.nenngeld)
assertEquals(BigDecimal.parseString("35.00"), turnier.startgeldStandard)
assertEquals(1, turnier.austragungsplaetze.size)
assertEquals("Neuer Hauptplatz", turnier.austragungsplaetze[0].name)
assertEquals(newTurnierleiterId, turnier.turnierleiterId)
assertEquals(newTurnierbeauftragterId, turnier.turnierbeauftragterId)
assertEquals(richterIds, turnier.richterIds)
assertEquals("Neuer Tierarzt", turnier.tierarztInfos)
assertEquals("Neuer Hufschmied", turnier.hufschmiedInfo)
assertEquals(newMeldestelleVerantwortlicherId, turnier.meldestelleVerantwortlicherId)
assertEquals("+43 987 654321", turnier.meldestelleTelefon)
assertEquals("9-17 Uhr", turnier.meldestelleOeffnungszeiten)
assertEquals("https://example.com/neue-ergebnisse", turnier.ergebnislistenUrl)
assertEquals(1, turnier.verfuegbareArtikel.size)
assertEquals("Neue Startgebühr", turnier.verfuegbareArtikel[0].bezeichnung)
assertNotEquals(originalUpdatedAt, turnier.updatedAt)
}
@Test
fun testSerializationDeserialization() {
// Create a simplified Turnier for serialization testing
val veranstaltungId = uuid4()
val datumVon = LocalDate(2023, 10, 1)
val datumBis = LocalDate(2023, 10, 3)
val turnierleiterId = uuid4()
val turnier = Turnier(
veranstaltungId = veranstaltungId,
oepsTurnierNr = "T12345",
titel = "Serialisiertes Turnier",
untertitel = "Für Test",
datumVon = datumVon,
datumBis = datumBis,
nennungsschluss = null,
nennungsArt = listOf(NennungsArt.OEPS_ZNS),
nennungsHinweis = "Hinweis",
eigenesNennsystemUrl = null,
nenngeld = BigDecimal.parseString("40.00"),
startgeldStandard = null,
turnierleiterId = turnierleiterId,
turnierbeauftragterId = null,
tierarztInfos = "Tierarzt Info",
hufschmiedInfo = null,
meldestelleVerantwortlicherId = null,
meldestelleTelefon = "+43 123 456789",
meldestelleOeffnungszeiten = null,
ergebnislistenUrl = null
)
// Serialize to JSON
val json = Json {
prettyPrint = true
encodeDefaults = true
}
val jsonString = json.encodeToString(turnier)
// Verify JSON contains expected fields
assertTrue(jsonString.contains("\"veranstaltungId\""), "JSON should contain veranstaltungId field")
assertTrue(jsonString.contains(veranstaltungId.toString()), "JSON should contain veranstaltungId value")
assertTrue(jsonString.contains("\"oepsTurnierNr\""), "JSON should contain oepsTurnierNr field")
assertTrue(jsonString.contains("\"T12345\""), "JSON should contain oepsTurnierNr value")
assertTrue(jsonString.contains("\"titel\""), "JSON should contain titel field")
assertTrue(jsonString.contains("\"Serialisiertes Turnier\""), "JSON should contain titel value")
assertTrue(jsonString.contains("\"datumVon\""), "JSON should contain datumVon field")
assertTrue(jsonString.contains("\"2023-10-01\""), "JSON should contain datumVon value")
assertTrue(jsonString.contains("\"nennungsArt\""), "JSON should contain nennungsArt field")
assertTrue(jsonString.contains("\"OEPS_ZNS\""), "JSON should contain nennungsArt value")
assertTrue(jsonString.contains("\"nenngeld\""), "JSON should contain nenngeld field")
assertTrue(jsonString.contains("40"), "JSON should contain nenngeld value")
assertTrue(jsonString.contains("\"turnierleiterId\""), "JSON should contain turnierleiterId field")
assertTrue(jsonString.contains(turnierleiterId.toString()), "JSON should contain turnierleiterId value")
// Deserialize from JSON
val deserializedTurnier = json.decodeFromString<Turnier>(jsonString)
// Verify deserialized object matches original
assertEquals(turnier.id, deserializedTurnier.id)
assertEquals(turnier.veranstaltungId, deserializedTurnier.veranstaltungId)
assertEquals(turnier.oepsTurnierNr, deserializedTurnier.oepsTurnierNr)
assertEquals(turnier.titel, deserializedTurnier.titel)
assertEquals(turnier.untertitel, deserializedTurnier.untertitel)
assertEquals(turnier.datumVon, deserializedTurnier.datumVon)
assertEquals(turnier.datumBis, deserializedTurnier.datumBis)
assertEquals(turnier.nennungsArt, deserializedTurnier.nennungsArt)
assertEquals(turnier.nennungsHinweis, deserializedTurnier.nennungsHinweis)
assertEquals(turnier.nenngeld, deserializedTurnier.nenngeld)
assertEquals(turnier.turnierleiterId, deserializedTurnier.turnierleiterId)
assertEquals(turnier.tierarztInfos, deserializedTurnier.tierarztInfos)
assertEquals(turnier.meldestelleTelefon, deserializedTurnier.meldestelleTelefon)
assertEquals(turnier.createdAt, deserializedTurnier.createdAt)
assertEquals(turnier.updatedAt, deserializedTurnier.updatedAt)
}
@Test
fun testCopyTurnier() {
// Create a Turnier
val veranstaltungId = uuid4()
val datumVon = LocalDate(2023, 11, 1)
val datumBis = LocalDate(2023, 11, 3)
val original = Turnier(
veranstaltungId = veranstaltungId,
oepsTurnierNr = "T12345",
titel = "Original Turnier",
untertitel = "Original Untertitel",
datumVon = datumVon,
datumBis = datumBis,
nennungsschluss = null,
nennungsHinweis = "Original Hinweis",
eigenesNennsystemUrl = null,
nenngeld = BigDecimal.parseString("50.00"),
startgeldStandard = null,
turnierleiterId = null,
turnierbeauftragterId = null,
tierarztInfos = null,
hufschmiedInfo = null,
meldestelleVerantwortlicherId = null,
meldestelleTelefon = null,
meldestelleOeffnungszeiten = null,
ergebnislistenUrl = null
)
val newVeranstaltungId = uuid4()
val newDatumVon = LocalDate(2023, 12, 1)
val newDatumBis = LocalDate(2023, 12, 3)
// Create a copy with some modified properties
val copy = original.copy(
veranstaltungId = newVeranstaltungId,
titel = "Kopiertes Turnier",
datumVon = newDatumVon,
datumBis = newDatumBis,
nenngeld = BigDecimal.parseString("60.00")
)
// Verify copied properties
assertEquals(original.id, copy.id)
assertEquals(original.oepsTurnierNr, copy.oepsTurnierNr)
assertEquals(original.untertitel, copy.untertitel)
assertEquals(original.nennungsschluss, copy.nennungsschluss)
assertEquals(original.nennungsArt, copy.nennungsArt)
assertEquals(original.nennungsHinweis, copy.nennungsHinweis)
assertEquals(original.eigenesNennsystemUrl, copy.eigenesNennsystemUrl)
assertEquals(original.startgeldStandard, copy.startgeldStandard)
assertEquals(original.austragungsplaetze, copy.austragungsplaetze)
assertEquals(original.vorbereitungsplaetze, copy.vorbereitungsplaetze)
assertEquals(original.turnierleiterId, copy.turnierleiterId)
assertEquals(original.turnierbeauftragterId, copy.turnierbeauftragterId)
assertEquals(original.richterIds, copy.richterIds)
assertEquals(original.parcoursbauerIds, copy.parcoursbauerIds)
assertEquals(original.parcoursAssistentIds, copy.parcoursAssistentIds)
assertEquals(original.tierarztInfos, copy.tierarztInfos)
assertEquals(original.hufschmiedInfo, copy.hufschmiedInfo)
assertEquals(original.meldestelleVerantwortlicherId, copy.meldestelleVerantwortlicherId)
assertEquals(original.meldestelleTelefon, copy.meldestelleTelefon)
assertEquals(original.meldestelleOeffnungszeiten, copy.meldestelleOeffnungszeiten)
assertEquals(original.ergebnislistenUrl, copy.ergebnislistenUrl)
assertEquals(original.verfuegbareArtikel, copy.verfuegbareArtikel)
assertEquals(original.meisterschaftRefs, copy.meisterschaftRefs)
assertEquals(original.createdAt, copy.createdAt)
assertEquals(original.updatedAt, copy.updatedAt)
// Verify modified properties
assertEquals(newVeranstaltungId, copy.veranstaltungId)
assertEquals("Kopiertes Turnier", copy.titel)
assertEquals(newDatumVon, copy.datumVon)
assertEquals(newDatumBis, copy.datumBis)
assertEquals(BigDecimal.parseString("60.00"), copy.nenngeld)
}
}
@@ -0,0 +1,341 @@
package at.mocode.shared.model.entitaeten
import at.mocode.shared.model.enums.VeranstalterTyp
import at.mocode.shared.model.serializers.KotlinInstantSerializer
import at.mocode.shared.model.serializers.KotlinLocalDateSerializer
import at.mocode.shared.model.serializers.UuidSerializer
import com.benasher44.uuid.Uuid
import com.benasher44.uuid.uuid4
import kotlinx.datetime.Clock
import kotlinx.datetime.Instant
import kotlinx.datetime.LocalDate
import kotlinx.serialization.decodeFromString
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertNotEquals
import kotlin.test.assertNotNull
import kotlin.test.assertTrue
class VeranstaltungTest {
@Test
fun testCreateVeranstaltung() {
// Create a Veranstaltung with minimal required parameters
val datumVon = LocalDate(2023, 6, 1)
val datumBis = LocalDate(2023, 6, 3)
val veranstaltung = Veranstaltung(
name = "Test Veranstaltung",
datumVon = datumVon,
datumBis = datumBis,
veranstalterName = "Test Veranstalter",
veranstalterOepsNummer = null,
veranstaltungsortName = "Test Ort",
veranstaltungsortAdresse = "Test Adresse",
kontaktpersonName = null,
kontaktTelefon = null,
kontaktEmail = null,
webseite = null,
logoUrl = null,
anfahrtsplanInfo = null,
dsgvoText = null,
haftungsText = null,
sonstigeBesondereBestimmungen = null
)
// Verify required fields
assertEquals("Test Veranstaltung", veranstaltung.name)
assertEquals(datumVon, veranstaltung.datumVon)
assertEquals(datumBis, veranstaltung.datumBis)
assertEquals("Test Veranstalter", veranstaltung.veranstalterName)
assertEquals("Test Ort", veranstaltung.veranstaltungsortName)
assertEquals("Test Adresse", veranstaltung.veranstaltungsortAdresse)
// Verify default values
assertNotNull(veranstaltung.id)
assertEquals(VeranstalterTyp.UNBEKANNT, veranstaltung.veranstalterTyp)
assertEquals(null, veranstaltung.veranstalterOepsNummer)
assertEquals(null, veranstaltung.kontaktpersonName)
assertEquals(null, veranstaltung.kontaktTelefon)
assertEquals(null, veranstaltung.kontaktEmail)
assertEquals(null, veranstaltung.webseite)
assertEquals(null, veranstaltung.logoUrl)
assertEquals(null, veranstaltung.anfahrtsplanInfo)
assertTrue(veranstaltung.sponsorInfos.isEmpty())
assertEquals(null, veranstaltung.dsgvoText)
assertEquals(null, veranstaltung.haftungsText)
assertEquals(null, veranstaltung.sonstigeBesondereBestimmungen)
assertNotNull(veranstaltung.createdAt)
assertNotNull(veranstaltung.updatedAt)
}
@Test
fun testCreateVeranstaltungWithAllParameters() {
// Create a Veranstaltung with all parameters
val id = uuid4()
val datumVon = LocalDate(2023, 7, 15)
val datumBis = LocalDate(2023, 7, 17)
val now = Clock.System.now()
val sponsorInfos = listOf("Sponsor 1", "Sponsor 2")
val veranstaltung = Veranstaltung(
id = id,
name = "Vollständige Veranstaltung",
datumVon = datumVon,
datumBis = datumBis,
veranstalterName = "Vollständiger Veranstalter",
veranstalterOepsNummer = "V12345",
veranstalterTyp = VeranstalterTyp.VEREIN,
veranstaltungsortName = "Vollständiger Ort",
veranstaltungsortAdresse = "Vollständige Adresse",
kontaktpersonName = "Max Mustermann",
kontaktTelefon = "+43 123 456789",
kontaktEmail = "max@example.com",
webseite = "https://example.com",
logoUrl = "https://example.com/logo.png",
anfahrtsplanInfo = "Anfahrtsplan Info",
sponsorInfos = sponsorInfos,
dsgvoText = "DSGVO Text",
haftungsText = "Haftungs Text",
sonstigeBesondereBestimmungen = "Besondere Bestimmungen",
createdAt = now,
updatedAt = now
)
// Verify all fields
assertEquals(id, veranstaltung.id)
assertEquals("Vollständige Veranstaltung", veranstaltung.name)
assertEquals(datumVon, veranstaltung.datumVon)
assertEquals(datumBis, veranstaltung.datumBis)
assertEquals("Vollständiger Veranstalter", veranstaltung.veranstalterName)
assertEquals("V12345", veranstaltung.veranstalterOepsNummer)
assertEquals(VeranstalterTyp.VEREIN, veranstaltung.veranstalterTyp)
assertEquals("Vollständiger Ort", veranstaltung.veranstaltungsortName)
assertEquals("Vollständige Adresse", veranstaltung.veranstaltungsortAdresse)
assertEquals("Max Mustermann", veranstaltung.kontaktpersonName)
assertEquals("+43 123 456789", veranstaltung.kontaktTelefon)
assertEquals("max@example.com", veranstaltung.kontaktEmail)
assertEquals("https://example.com", veranstaltung.webseite)
assertEquals("https://example.com/logo.png", veranstaltung.logoUrl)
assertEquals("Anfahrtsplan Info", veranstaltung.anfahrtsplanInfo)
assertEquals(sponsorInfos, veranstaltung.sponsorInfos)
assertEquals("DSGVO Text", veranstaltung.dsgvoText)
assertEquals("Haftungs Text", veranstaltung.haftungsText)
assertEquals("Besondere Bestimmungen", veranstaltung.sonstigeBesondereBestimmungen)
assertEquals(now, veranstaltung.createdAt)
assertEquals(now, veranstaltung.updatedAt)
}
@Test
fun testModifyVeranstaltung() {
// Create a Veranstaltung
val datumVon = LocalDate(2023, 8, 1)
val datumBis = LocalDate(2023, 8, 3)
val veranstaltung = Veranstaltung(
name = "Original Veranstaltung",
datumVon = datumVon,
datumBis = datumBis,
veranstalterName = "Original Veranstalter",
veranstalterOepsNummer = null,
veranstaltungsortName = "Original Ort",
veranstaltungsortAdresse = "Original Adresse",
kontaktpersonName = null,
kontaktTelefon = null,
kontaktEmail = null,
webseite = null,
logoUrl = null,
anfahrtsplanInfo = null,
dsgvoText = null,
haftungsText = null,
sonstigeBesondereBestimmungen = null
)
val originalUpdatedAt = veranstaltung.updatedAt
val newDatumVon = LocalDate(2023, 9, 1)
val newDatumBis = LocalDate(2023, 9, 3)
val sponsorInfos = listOf("Neuer Sponsor")
// Modify properties
veranstaltung.name = "Geänderte Veranstaltung"
veranstaltung.datumVon = newDatumVon
veranstaltung.datumBis = newDatumBis
veranstaltung.veranstalterName = "Geänderter Veranstalter"
veranstaltung.veranstalterOepsNummer = "V54321"
veranstaltung.veranstalterTyp = VeranstalterTyp.FIRMA
veranstaltung.veranstaltungsortName = "Geänderter Ort"
veranstaltung.veranstaltungsortAdresse = "Geänderte Adresse"
veranstaltung.kontaktpersonName = "Maria Musterfrau"
veranstaltung.kontaktTelefon = "+43 987 654321"
veranstaltung.kontaktEmail = "maria@example.com"
veranstaltung.webseite = "https://example.com/neu"
veranstaltung.logoUrl = "https://example.com/neues-logo.png"
veranstaltung.anfahrtsplanInfo = "Neue Anfahrtsplan Info"
veranstaltung.sponsorInfos = sponsorInfos
veranstaltung.dsgvoText = "Neuer DSGVO Text"
veranstaltung.haftungsText = "Neuer Haftungs Text"
veranstaltung.sonstigeBesondereBestimmungen = "Neue Besondere Bestimmungen"
veranstaltung.updatedAt = Clock.System.now()
// Verify modifications
assertEquals("Geänderte Veranstaltung", veranstaltung.name)
assertEquals(newDatumVon, veranstaltung.datumVon)
assertEquals(newDatumBis, veranstaltung.datumBis)
assertEquals("Geänderter Veranstalter", veranstaltung.veranstalterName)
assertEquals("V54321", veranstaltung.veranstalterOepsNummer)
assertEquals(VeranstalterTyp.FIRMA, veranstaltung.veranstalterTyp)
assertEquals("Geänderter Ort", veranstaltung.veranstaltungsortName)
assertEquals("Geänderte Adresse", veranstaltung.veranstaltungsortAdresse)
assertEquals("Maria Musterfrau", veranstaltung.kontaktpersonName)
assertEquals("+43 987 654321", veranstaltung.kontaktTelefon)
assertEquals("maria@example.com", veranstaltung.kontaktEmail)
assertEquals("https://example.com/neu", veranstaltung.webseite)
assertEquals("https://example.com/neues-logo.png", veranstaltung.logoUrl)
assertEquals("Neue Anfahrtsplan Info", veranstaltung.anfahrtsplanInfo)
assertEquals(sponsorInfos, veranstaltung.sponsorInfos)
assertEquals("Neuer DSGVO Text", veranstaltung.dsgvoText)
assertEquals("Neuer Haftungs Text", veranstaltung.haftungsText)
assertEquals("Neue Besondere Bestimmungen", veranstaltung.sonstigeBesondereBestimmungen)
assertNotEquals(originalUpdatedAt, veranstaltung.updatedAt)
}
@Test
fun testSerializationDeserialization() {
// Create a Veranstaltung
val datumVon = LocalDate(2023, 10, 1)
val datumBis = LocalDate(2023, 10, 3)
val sponsorInfos = listOf("Sponsor A", "Sponsor B")
val veranstaltung = Veranstaltung(
name = "Serialisierte Veranstaltung",
datumVon = datumVon,
datumBis = datumBis,
veranstalterName = "Serialisierter Veranstalter",
veranstalterOepsNummer = "V12345",
veranstalterTyp = VeranstalterTyp.VEREIN,
veranstaltungsortName = "Serialisierter Ort",
veranstaltungsortAdresse = "Serialisierte Adresse",
kontaktpersonName = "Kontakt Person",
kontaktTelefon = "+43 123 456789",
kontaktEmail = "kontakt@example.com",
webseite = "https://example.com",
logoUrl = "https://example.com/logo.png",
anfahrtsplanInfo = "Anfahrtsplan Info",
sponsorInfos = sponsorInfos,
dsgvoText = "DSGVO Text",
haftungsText = "Haftungs Text",
sonstigeBesondereBestimmungen = "Besondere Bestimmungen"
)
// Serialize to JSON
val json = Json {
prettyPrint = true
encodeDefaults = true
}
val jsonString = json.encodeToString(veranstaltung)
// Verify JSON contains expected fields
assertTrue(jsonString.contains("\"name\""), "JSON should contain name field")
assertTrue(jsonString.contains("\"Serialisierte Veranstaltung\""), "JSON should contain name value")
assertTrue(jsonString.contains("\"datumVon\""), "JSON should contain datumVon field")
assertTrue(jsonString.contains("\"2023-10-01\""), "JSON should contain datumVon value")
assertTrue(jsonString.contains("\"veranstalterName\""), "JSON should contain veranstalterName field")
assertTrue(jsonString.contains("\"Serialisierter Veranstalter\""), "JSON should contain veranstalterName value")
assertTrue(jsonString.contains("\"veranstalterTyp\""), "JSON should contain veranstalterTyp field")
assertTrue(jsonString.contains("\"VEREIN\""), "JSON should contain veranstalterTyp value")
assertTrue(jsonString.contains("\"sponsorInfos\""), "JSON should contain sponsorInfos field")
assertTrue(jsonString.contains("\"Sponsor A\""), "JSON should contain sponsorInfos value")
assertTrue(jsonString.contains("\"Sponsor B\""), "JSON should contain sponsorInfos value")
// Deserialize from JSON
val deserializedVeranstaltung = json.decodeFromString<Veranstaltung>(jsonString)
// Verify deserialized object matches original
assertEquals(veranstaltung.id, deserializedVeranstaltung.id)
assertEquals(veranstaltung.name, deserializedVeranstaltung.name)
assertEquals(veranstaltung.datumVon, deserializedVeranstaltung.datumVon)
assertEquals(veranstaltung.datumBis, deserializedVeranstaltung.datumBis)
assertEquals(veranstaltung.veranstalterName, deserializedVeranstaltung.veranstalterName)
assertEquals(veranstaltung.veranstalterOepsNummer, deserializedVeranstaltung.veranstalterOepsNummer)
assertEquals(veranstaltung.veranstalterTyp, deserializedVeranstaltung.veranstalterTyp)
assertEquals(veranstaltung.veranstaltungsortName, deserializedVeranstaltung.veranstaltungsortName)
assertEquals(veranstaltung.veranstaltungsortAdresse, deserializedVeranstaltung.veranstaltungsortAdresse)
assertEquals(veranstaltung.kontaktpersonName, deserializedVeranstaltung.kontaktpersonName)
assertEquals(veranstaltung.kontaktTelefon, deserializedVeranstaltung.kontaktTelefon)
assertEquals(veranstaltung.kontaktEmail, deserializedVeranstaltung.kontaktEmail)
assertEquals(veranstaltung.webseite, deserializedVeranstaltung.webseite)
assertEquals(veranstaltung.logoUrl, deserializedVeranstaltung.logoUrl)
assertEquals(veranstaltung.anfahrtsplanInfo, deserializedVeranstaltung.anfahrtsplanInfo)
assertEquals(veranstaltung.sponsorInfos, deserializedVeranstaltung.sponsorInfos)
assertEquals(veranstaltung.dsgvoText, deserializedVeranstaltung.dsgvoText)
assertEquals(veranstaltung.haftungsText, deserializedVeranstaltung.haftungsText)
assertEquals(veranstaltung.sonstigeBesondereBestimmungen, deserializedVeranstaltung.sonstigeBesondereBestimmungen)
assertEquals(veranstaltung.createdAt, deserializedVeranstaltung.createdAt)
assertEquals(veranstaltung.updatedAt, deserializedVeranstaltung.updatedAt)
}
@Test
fun testCopyVeranstaltung() {
// Create a Veranstaltung
val datumVon = LocalDate(2023, 11, 1)
val datumBis = LocalDate(2023, 11, 3)
val original = Veranstaltung(
name = "Original Veranstaltung",
datumVon = datumVon,
datumBis = datumBis,
veranstalterName = "Original Veranstalter",
veranstalterOepsNummer = "V12345",
veranstalterTyp = VeranstalterTyp.VEREIN,
veranstaltungsortName = "Original Ort",
veranstaltungsortAdresse = "Original Adresse",
kontaktpersonName = "Original Kontakt",
kontaktTelefon = null,
kontaktEmail = null,
webseite = null,
logoUrl = null,
anfahrtsplanInfo = null,
dsgvoText = null,
haftungsText = null,
sonstigeBesondereBestimmungen = null
)
val newDatumVon = LocalDate(2023, 12, 1)
val newDatumBis = LocalDate(2023, 12, 3)
// Create a copy with some modified properties
val copy = original.copy(
name = "Kopierte Veranstaltung",
datumVon = newDatumVon,
datumBis = newDatumBis,
veranstalterTyp = VeranstalterTyp.FIRMA
)
// Verify copied properties
assertEquals(original.id, copy.id)
assertEquals(original.veranstalterName, copy.veranstalterName)
assertEquals(original.veranstalterOepsNummer, copy.veranstalterOepsNummer)
assertEquals(original.veranstaltungsortName, copy.veranstaltungsortName)
assertEquals(original.veranstaltungsortAdresse, copy.veranstaltungsortAdresse)
assertEquals(original.kontaktpersonName, copy.kontaktpersonName)
assertEquals(original.kontaktTelefon, copy.kontaktTelefon)
assertEquals(original.kontaktEmail, copy.kontaktEmail)
assertEquals(original.webseite, copy.webseite)
assertEquals(original.logoUrl, copy.logoUrl)
assertEquals(original.anfahrtsplanInfo, copy.anfahrtsplanInfo)
assertEquals(original.sponsorInfos, copy.sponsorInfos)
assertEquals(original.dsgvoText, copy.dsgvoText)
assertEquals(original.haftungsText, copy.haftungsText)
assertEquals(original.sonstigeBesondereBestimmungen, copy.sonstigeBesondereBestimmungen)
assertEquals(original.createdAt, copy.createdAt)
assertEquals(original.updatedAt, copy.updatedAt)
// Verify modified properties
assertEquals("Kopierte Veranstaltung", copy.name)
assertEquals(newDatumVon, copy.datumVon)
assertEquals(newDatumBis, copy.datumBis)
assertEquals(VeranstalterTyp.FIRMA, copy.veranstalterTyp)
}
}
@@ -0,0 +1,575 @@
package at.mocode.shared.model.enums
import kotlinx.serialization.Serializable
import kotlinx.serialization.decodeFromString
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertNotEquals
import kotlin.test.assertTrue
class EnumsTest {
@Test
fun testVeranstalterTypEnum() {
// Test all enum values
val values = VeranstalterTyp.entries.toTypedArray()
assertEquals(5, values.size)
// Test specific enum values
assertEquals(VeranstalterTyp.VEREIN, values[0])
assertEquals(VeranstalterTyp.FIRMA, values[1])
assertEquals(VeranstalterTyp.PRIVATPERSON, values[2])
assertEquals(VeranstalterTyp.SONSTIGE, values[3])
assertEquals(VeranstalterTyp.UNBEKANNT, values[4])
// Test serialization and deserialization
testEnumSerialization(VeranstalterTyp.VEREIN)
testEnumSerialization(VeranstalterTyp.FIRMA)
testEnumSerialization(VeranstalterTyp.PRIVATPERSON)
testEnumSerialization(VeranstalterTyp.SONSTIGE)
testEnumSerialization(VeranstalterTyp.UNBEKANNT)
// Test comparison
assertEquals(VeranstalterTyp.VEREIN, VeranstalterTyp.VEREIN)
assertNotEquals(VeranstalterTyp.VEREIN, VeranstalterTyp.FIRMA)
}
@Test
fun testPlatzTypEnum() {
// Test all enum values
val values = PlatzTyp.entries.toTypedArray()
assertEquals(4, values.size)
// Test specific enum values
assertEquals(PlatzTyp.AUSTRAGUNG, values[0])
assertEquals(PlatzTyp.VORBEREITUNG, values[1])
assertEquals(PlatzTyp.LONGIEREN, values[2])
assertEquals(PlatzTyp.SONSTIGES, values[3])
// Test serialization and deserialization
testEnumSerialization(PlatzTyp.AUSTRAGUNG)
testEnumSerialization(PlatzTyp.VORBEREITUNG)
testEnumSerialization(PlatzTyp.LONGIEREN)
testEnumSerialization(PlatzTyp.SONSTIGES)
// Test comparison
assertEquals(PlatzTyp.AUSTRAGUNG, PlatzTyp.AUSTRAGUNG)
assertNotEquals(PlatzTyp.AUSTRAGUNG, PlatzTyp.VORBEREITUNG)
}
@Test
fun testNennungsArtEnum() {
// Test all enum values
val values = NennungsArt.entries.toTypedArray()
assertEquals(6, values.size)
// Test specific enum values
assertEquals(NennungsArt.OEPS_ZNS, values[0])
assertEquals(NennungsArt.EIGENES_ONLINE, values[1])
assertEquals(NennungsArt.DIREKT_VERANSTALTER_EMAIL, values[2])
assertEquals(NennungsArt.DIREKT_VERANSTALTER_TELEFON, values[3])
assertEquals(NennungsArt.DIREKT_VERANSTALTER_WHATSAPP, values[4])
assertEquals(NennungsArt.SONSTIGE, values[5])
// Test serialization and deserialization
testEnumSerialization(NennungsArt.OEPS_ZNS)
testEnumSerialization(NennungsArt.EIGENES_ONLINE)
testEnumSerialization(NennungsArt.DIREKT_VERANSTALTER_EMAIL)
testEnumSerialization(NennungsArt.DIREKT_VERANSTALTER_TELEFON)
testEnumSerialization(NennungsArt.DIREKT_VERANSTALTER_WHATSAPP)
testEnumSerialization(NennungsArt.SONSTIGE)
// Test comparison
assertEquals(NennungsArt.OEPS_ZNS, NennungsArt.OEPS_ZNS)
assertNotEquals(NennungsArt.OEPS_ZNS, NennungsArt.EIGENES_ONLINE)
}
@Test
fun testSparteEnum() {
// Test all enum values
val values = Sparte.entries.toTypedArray()
assertEquals(12, values.size)
// Test specific enum values
assertEquals(Sparte.DRESSUR, values[0])
assertEquals(Sparte.SPRINGEN, values[1])
assertEquals(Sparte.VIELSEITIGKEIT, values[2])
assertEquals(Sparte.FAHREN, values[3])
assertEquals(Sparte.VOLTIGIEREN, values[4])
assertEquals(Sparte.WESTERN, values[5])
assertEquals(Sparte.DISTANZ, values[6])
assertEquals(Sparte.ISLAND, values[7])
assertEquals(Sparte.PFERDESPORT_SPIEL, values[8])
assertEquals(Sparte.BASIS, values[9])
assertEquals(Sparte.KOMBINIERT, values[10])
assertEquals(Sparte.SONSTIGES, values[11])
// Test serialization and deserialization
testEnumSerialization(Sparte.DRESSUR)
testEnumSerialization(Sparte.SPRINGEN)
testEnumSerialization(Sparte.VIELSEITIGKEIT)
testEnumSerialization(Sparte.FAHREN)
testEnumSerialization(Sparte.VOLTIGIEREN)
testEnumSerialization(Sparte.WESTERN)
testEnumSerialization(Sparte.DISTANZ)
testEnumSerialization(Sparte.ISLAND)
testEnumSerialization(Sparte.PFERDESPORT_SPIEL)
testEnumSerialization(Sparte.BASIS)
testEnumSerialization(Sparte.KOMBINIERT)
testEnumSerialization(Sparte.SONSTIGES)
// Test comparison
assertEquals(Sparte.DRESSUR, Sparte.DRESSUR)
assertNotEquals(Sparte.DRESSUR, Sparte.SPRINGEN)
}
@Test
fun testBewerbStatusEnum() {
// Test all enum values
val values = BewerbStatus.entries.toTypedArray()
assertEquals(6, values.size)
// Test specific enum values
assertEquals(BewerbStatus.GEPLANT, values[0])
assertEquals(BewerbStatus.OFFEN_FUER_NENNUNG, values[1])
assertEquals(BewerbStatus.GESCHLOSSEN_FUER_NENNUNG, values[2])
assertEquals(BewerbStatus.LAEUFT, values[3])
assertEquals(BewerbStatus.ABGESCHLOSSEN, values[4])
assertEquals(BewerbStatus.ABGESAGT, values[5])
// Test serialization and deserialization
testEnumSerialization(BewerbStatus.GEPLANT)
testEnumSerialization(BewerbStatus.OFFEN_FUER_NENNUNG)
testEnumSerialization(BewerbStatus.GESCHLOSSEN_FUER_NENNUNG)
testEnumSerialization(BewerbStatus.LAEUFT)
testEnumSerialization(BewerbStatus.ABGESCHLOSSEN)
testEnumSerialization(BewerbStatus.ABGESAGT)
// Test comparison
assertEquals(BewerbStatus.GEPLANT, BewerbStatus.GEPLANT)
assertNotEquals(BewerbStatus.GEPLANT, BewerbStatus.ABGESAGT)
}
@Test
fun testBedingungstypEnum() {
// Test all enum values
val values = Bedingungstyp.entries.toTypedArray()
assertEquals(9, values.size)
// Test specific enum values
assertEquals(Bedingungstyp.LIZENZ_REITER, values[0])
assertEquals(Bedingungstyp.LIZENZ_FAHRER, values[1])
assertEquals(Bedingungstyp.ALTER_PFERD, values[2])
assertEquals(Bedingungstyp.ALTER_REITER, values[3])
assertEquals(Bedingungstyp.RASSE_PFERD, values[4])
assertEquals(Bedingungstyp.GESCHLECHT_PFERD, values[5])
assertEquals(Bedingungstyp.GESCHLECHT_REITER, values[6])
assertEquals(Bedingungstyp.STARTKARTE, values[7])
assertEquals(Bedingungstyp.SONSTIGES, values[8])
// Test serialization and deserialization
testEnumSerialization(Bedingungstyp.LIZENZ_REITER)
testEnumSerialization(Bedingungstyp.LIZENZ_FAHRER)
testEnumSerialization(Bedingungstyp.ALTER_PFERD)
testEnumSerialization(Bedingungstyp.ALTER_REITER)
testEnumSerialization(Bedingungstyp.RASSE_PFERD)
testEnumSerialization(Bedingungstyp.GESCHLECHT_PFERD)
testEnumSerialization(Bedingungstyp.GESCHLECHT_REITER)
testEnumSerialization(Bedingungstyp.STARTKARTE)
testEnumSerialization(Bedingungstyp.SONSTIGES)
// Test comparison
assertEquals(Bedingungstyp.LIZENZ_REITER, Bedingungstyp.LIZENZ_REITER)
assertNotEquals(Bedingungstyp.LIZENZ_REITER, Bedingungstyp.LIZENZ_FAHRER)
}
@Test
fun testOperatorEnum() {
// Test all enum values
val values = Operator.entries.toTypedArray()
assertEquals(7, values.size)
// Test specific enum values
assertEquals(Operator.GLEICH, values[0])
assertEquals(Operator.UNGLEICH, values[1])
assertEquals(Operator.MINDESTENS, values[2])
assertEquals(Operator.MAXIMAL, values[3])
assertEquals(Operator.ZWISCHEN, values[4])
assertEquals(Operator.IN_LISTE, values[5])
assertEquals(Operator.NICHT_IN_LISTE, values[6])
// Test serialization and deserialization
testEnumSerialization(Operator.GLEICH)
testEnumSerialization(Operator.UNGLEICH)
testEnumSerialization(Operator.MINDESTENS)
testEnumSerialization(Operator.MAXIMAL)
testEnumSerialization(Operator.ZWISCHEN)
testEnumSerialization(Operator.IN_LISTE)
testEnumSerialization(Operator.NICHT_IN_LISTE)
// Test comparison
assertEquals(Operator.GLEICH, Operator.GLEICH)
assertNotEquals(Operator.GLEICH, Operator.UNGLEICH)
}
@Test
fun testFunktionaerRolleEnum() {
// Test all enum values
val values = FunktionaerRolle.entries.toTypedArray()
assertEquals(12, values.size)
// Test specific enum values
assertEquals(FunktionaerRolle.RICHTER, values[0])
assertEquals(FunktionaerRolle.PARCOURSBAUER, values[1])
assertEquals(FunktionaerRolle.PARCOURSBAU_ASSISTENT, values[2])
assertEquals(FunktionaerRolle.TECHN_DELEGIERTER, values[3])
assertEquals(FunktionaerRolle.TURNIERBEAUFTRAGTER, values[4])
assertEquals(FunktionaerRolle.STEWARD, values[5])
assertEquals(FunktionaerRolle.ZEITNEHMER, values[6])
assertEquals(FunktionaerRolle.SCHREIBER, values[7])
assertEquals(FunktionaerRolle.VERANSTALTER_KONTAKT, values[8])
assertEquals(FunktionaerRolle.TURNIERLEITER, values[9])
assertEquals(FunktionaerRolle.HELFER, values[10])
assertEquals(FunktionaerRolle.SONSTIGE, values[11])
// Test serialization and deserialization
testEnumSerialization(FunktionaerRolle.RICHTER)
testEnumSerialization(FunktionaerRolle.PARCOURSBAUER)
testEnumSerialization(FunktionaerRolle.PARCOURSBAU_ASSISTENT)
testEnumSerialization(FunktionaerRolle.TECHN_DELEGIERTER)
testEnumSerialization(FunktionaerRolle.TURNIERBEAUFTRAGTER)
testEnumSerialization(FunktionaerRolle.STEWARD)
testEnumSerialization(FunktionaerRolle.ZEITNEHMER)
testEnumSerialization(FunktionaerRolle.SCHREIBER)
testEnumSerialization(FunktionaerRolle.VERANSTALTER_KONTAKT)
testEnumSerialization(FunktionaerRolle.TURNIERLEITER)
testEnumSerialization(FunktionaerRolle.HELFER)
testEnumSerialization(FunktionaerRolle.SONSTIGE)
// Test comparison
assertEquals(FunktionaerRolle.RICHTER, FunktionaerRolle.RICHTER)
assertNotEquals(FunktionaerRolle.RICHTER, FunktionaerRolle.PARCOURSBAUER)
}
@Test
fun testRichterPositionEnum() {
// Test all enum values
val values = RichterPosition.entries.toTypedArray()
assertEquals(8, values.size)
// Test specific enum values
assertEquals(RichterPosition.BEI_C, values[0])
assertEquals(RichterPosition.BEI_E, values[1])
assertEquals(RichterPosition.BEI_H, values[2])
assertEquals(RichterPosition.BEI_M, values[3])
assertEquals(RichterPosition.BEI_B, values[4])
assertEquals(RichterPosition.VORSITZ, values[5])
assertEquals(RichterPosition.SEITENRICHTER, values[6])
assertEquals(RichterPosition.SONSTIGE, values[7])
// Test serialization and deserialization
testEnumSerialization(RichterPosition.BEI_C)
testEnumSerialization(RichterPosition.BEI_E)
testEnumSerialization(RichterPosition.BEI_H)
testEnumSerialization(RichterPosition.BEI_M)
testEnumSerialization(RichterPosition.BEI_B)
testEnumSerialization(RichterPosition.VORSITZ)
testEnumSerialization(RichterPosition.SEITENRICHTER)
testEnumSerialization(RichterPosition.SONSTIGE)
// Test comparison
assertEquals(RichterPosition.BEI_C, RichterPosition.BEI_C)
assertNotEquals(RichterPosition.BEI_C, RichterPosition.BEI_E)
}
@Test
fun testGeschlechtEnum() {
// Test all enum values
val values = Geschlecht.entries.toTypedArray()
assertEquals(4, values.size)
// Test specific enum values
assertEquals(Geschlecht.M, values[0])
assertEquals(Geschlecht.W, values[1])
assertEquals(Geschlecht.D, values[2])
assertEquals(Geschlecht.UNBEKANNT, values[3])
// Test serialization and deserialization
testEnumSerialization(Geschlecht.M)
testEnumSerialization(Geschlecht.W)
testEnumSerialization(Geschlecht.D)
testEnumSerialization(Geschlecht.UNBEKANNT)
// Test comparison
assertEquals(Geschlecht.M, Geschlecht.M)
assertNotEquals(Geschlecht.M, Geschlecht.W)
}
@Test
fun testLizenzTypEnum() {
// Test all enum values
val values = LizenzTyp.entries.toTypedArray()
assertEquals(15, values.size)
// Test specific enum values
assertEquals(LizenzTyp.REITER, values[0])
assertEquals(LizenzTyp.FAHRER, values[1])
assertEquals(LizenzTyp.VOLTIGIERER, values[2])
assertEquals(LizenzTyp.WESTERN, values[3])
assertEquals(LizenzTyp.WORKING_EQUITATION, values[4])
assertEquals(LizenzTyp.POLO, values[5])
assertEquals(LizenzTyp.STARTKARTE_ALLG, values[6])
assertEquals(LizenzTyp.STARTKARTE_VOLTIGIEREN, values[7])
assertEquals(LizenzTyp.STARTKARTE_WESTERN, values[8])
assertEquals(LizenzTyp.STARTKARTE_ISLAND, values[9])
assertEquals(LizenzTyp.STARTKARTE_FAHREN_JUGEND, values[10])
assertEquals(LizenzTyp.STARTKARTE_HORSEBALL, values[11])
assertEquals(LizenzTyp.STARTKARTE_POLO, values[12])
assertEquals(LizenzTyp.PARAEQUESTRIAN, values[13])
assertEquals(LizenzTyp.SONSTIGE, values[14])
// Test serialization and deserialization
testEnumSerialization(LizenzTyp.REITER)
testEnumSerialization(LizenzTyp.FAHRER)
testEnumSerialization(LizenzTyp.VOLTIGIERER)
testEnumSerialization(LizenzTyp.WESTERN)
testEnumSerialization(LizenzTyp.WORKING_EQUITATION)
testEnumSerialization(LizenzTyp.POLO)
testEnumSerialization(LizenzTyp.STARTKARTE_ALLG)
testEnumSerialization(LizenzTyp.STARTKARTE_VOLTIGIEREN)
testEnumSerialization(LizenzTyp.STARTKARTE_WESTERN)
testEnumSerialization(LizenzTyp.STARTKARTE_ISLAND)
testEnumSerialization(LizenzTyp.STARTKARTE_FAHREN_JUGEND)
testEnumSerialization(LizenzTyp.STARTKARTE_HORSEBALL)
testEnumSerialization(LizenzTyp.STARTKARTE_POLO)
testEnumSerialization(LizenzTyp.PARAEQUESTRIAN)
testEnumSerialization(LizenzTyp.SONSTIGE)
// Test comparison
assertEquals(LizenzTyp.REITER, LizenzTyp.REITER)
assertNotEquals(LizenzTyp.REITER, LizenzTyp.FAHRER)
}
@Test
fun testGeschlechtPferdEnum() {
// Test all enum values
val values = GeschlechtPferd.entries.toTypedArray()
assertEquals(4, values.size)
// Test specific enum values
assertEquals(GeschlechtPferd.HENGST, values[0])
assertEquals(GeschlechtPferd.STUTE, values[1])
assertEquals(GeschlechtPferd.WALLACH, values[2])
assertEquals(GeschlechtPferd.UNBEKANNT, values[3])
// Test serialization and deserialization
testEnumSerialization(GeschlechtPferd.HENGST)
testEnumSerialization(GeschlechtPferd.STUTE)
testEnumSerialization(GeschlechtPferd.WALLACH)
testEnumSerialization(GeschlechtPferd.UNBEKANNT)
// Test comparison
assertEquals(GeschlechtPferd.HENGST, GeschlechtPferd.HENGST)
assertNotEquals(GeschlechtPferd.HENGST, GeschlechtPferd.STUTE)
}
@Test
fun testEnumCollections() {
// Test using enums in collections
val enumSet = setOf(
Geschlecht.M,
Geschlecht.W,
FunktionaerRolle.RICHTER,
FunktionaerRolle.PARCOURSBAUER
)
assertTrue(enumSet.contains(Geschlecht.M))
assertTrue(enumSet.contains(FunktionaerRolle.RICHTER))
assertEquals(4, enumSet.size)
// Test serialization of collections with enums
@Serializable
data class TestClass(
val geschlecht: Geschlecht,
val rollen: Set<FunktionaerRolle>
)
val testObject = TestClass(
geschlecht = Geschlecht.M,
rollen = setOf(FunktionaerRolle.RICHTER, FunktionaerRolle.PARCOURSBAUER)
)
val json = Json { prettyPrint = true }
val jsonString = json.encodeToString(testObject)
// Verify serialization
assertTrue(jsonString.contains("\"geschlecht\""))
assertTrue(jsonString.contains("\"M\""))
assertTrue(jsonString.contains("\"rollen\""))
assertTrue(jsonString.contains("\"RICHTER\""))
assertTrue(jsonString.contains("\"PARCOURSBAUER\""))
// Verify deserialization
val deserializedObject = json.decodeFromString<TestClass>(jsonString)
assertEquals(testObject.geschlecht, deserializedObject.geschlecht)
assertEquals(testObject.rollen, deserializedObject.rollen)
}
// Test serialization for a specific enum value
private fun testEnumSerialization(value: VeranstalterTyp) {
val json = Json { prettyPrint = true }
val jsonString = json.encodeToString(value)
// Verify serialization
assertEquals("\"${value.name}\"", jsonString)
// Verify deserialization
val deserializedValue = json.decodeFromString<VeranstalterTyp>(jsonString)
assertEquals(value, deserializedValue)
}
// Test serialization for a specific enum value
private fun testEnumSerialization(value: PlatzTyp) {
val json = Json { prettyPrint = true }
val jsonString = json.encodeToString(value)
// Verify serialization
assertEquals("\"${value.name}\"", jsonString)
// Verify deserialization
val deserializedValue = json.decodeFromString<PlatzTyp>(jsonString)
assertEquals(value, deserializedValue)
}
// Test serialization for a specific enum value
private fun testEnumSerialization(value: NennungsArt) {
val json = Json { prettyPrint = true }
val jsonString = json.encodeToString(value)
// Verify serialization
assertEquals("\"${value.name}\"", jsonString)
// Verify deserialization
val deserializedValue = json.decodeFromString<NennungsArt>(jsonString)
assertEquals(value, deserializedValue)
}
// Test serialization for a specific enum value
private fun testEnumSerialization(value: Sparte) {
val json = Json { prettyPrint = true }
val jsonString = json.encodeToString(value)
// Verify serialization
assertEquals("\"${value.name}\"", jsonString)
// Verify deserialization
val deserializedValue = json.decodeFromString<Sparte>(jsonString)
assertEquals(value, deserializedValue)
}
// Test serialization for a specific enum value
private fun testEnumSerialization(value: BewerbStatus) {
val json = Json { prettyPrint = true }
val jsonString = json.encodeToString(value)
// Verify serialization
assertEquals("\"${value.name}\"", jsonString)
// Verify deserialization
val deserializedValue = json.decodeFromString<BewerbStatus>(jsonString)
assertEquals(value, deserializedValue)
}
// Test serialization for a specific enum value
private fun testEnumSerialization(value: Bedingungstyp) {
val json = Json { prettyPrint = true }
val jsonString = json.encodeToString(value)
// Verify serialization
assertEquals("\"${value.name}\"", jsonString)
// Verify deserialization
val deserializedValue = json.decodeFromString<Bedingungstyp>(jsonString)
assertEquals(value, deserializedValue)
}
// Test serialization for a specific enum value
private fun testEnumSerialization(value: Operator) {
val json = Json { prettyPrint = true }
val jsonString = json.encodeToString(value)
// Verify serialization
assertEquals("\"${value.name}\"", jsonString)
// Verify deserialization
val deserializedValue = json.decodeFromString<Operator>(jsonString)
assertEquals(value, deserializedValue)
}
// Test serialization for a specific enum value
private fun testEnumSerialization(value: FunktionaerRolle) {
val json = Json { prettyPrint = true }
val jsonString = json.encodeToString(value)
// Verify serialization
assertEquals("\"${value.name}\"", jsonString)
// Verify deserialization
val deserializedValue = json.decodeFromString<FunktionaerRolle>(jsonString)
assertEquals(value, deserializedValue)
}
// Test serialization for a specific enum value
private fun testEnumSerialization(value: RichterPosition) {
val json = Json { prettyPrint = true }
val jsonString = json.encodeToString(value)
// Verify serialization
assertEquals("\"${value.name}\"", jsonString)
// Verify deserialization
val deserializedValue = json.decodeFromString<RichterPosition>(jsonString)
assertEquals(value, deserializedValue)
}
// Test serialization for a specific enum value
private fun testEnumSerialization(value: Geschlecht) {
val json = Json { prettyPrint = true }
val jsonString = json.encodeToString(value)
// Verify serialization
assertEquals("\"${value.name}\"", jsonString)
// Verify deserialization
val deserializedValue = json.decodeFromString<Geschlecht>(jsonString)
assertEquals(value, deserializedValue)
}
// Test serialization for a specific enum value
private fun testEnumSerialization(value: LizenzTyp) {
val json = Json { prettyPrint = true }
val jsonString = json.encodeToString(value)
// Verify serialization
assertEquals("\"${value.name}\"", jsonString)
// Verify deserialization
val deserializedValue = json.decodeFromString<LizenzTyp>(jsonString)
assertEquals(value, deserializedValue)
}
// Test serialization for a specific enum value
private fun testEnumSerialization(value: GeschlechtPferd) {
val json = Json { prettyPrint = true }
val jsonString = json.encodeToString(value)
// Verify serialization
assertEquals("\"${value.name}\"", jsonString)
// Verify deserialization
val deserializedValue = json.decodeFromString<GeschlechtPferd>(jsonString)
assertEquals(value, deserializedValue)
}
}
@@ -0,0 +1,231 @@
package at.mocode.shared.model.serializers
import com.benasher44.uuid.Uuid
import com.benasher44.uuid.uuid4
import com.benasher44.uuid.uuidFrom
import com.ionspin.kotlin.bignum.decimal.BigDecimal
import kotlinx.datetime.Clock
import kotlinx.datetime.Instant
import kotlinx.datetime.LocalDate
import kotlinx.datetime.LocalDateTime
import kotlinx.serialization.Serializable
import kotlinx.serialization.decodeFromString
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertTrue
class SerializationTest {
@Test
fun testBigDecimalSerializer() {
// Test regular values
testBigDecimalSerialization(BigDecimal.fromInt(0))
testBigDecimalSerialization(BigDecimal.fromInt(42))
testBigDecimalSerialization(BigDecimal.fromInt(-42))
testBigDecimalSerialization(BigDecimal.parseString("123.456"))
testBigDecimalSerialization(BigDecimal.parseString("-123.456"))
// Test edge cases
testBigDecimalSerialization(BigDecimal.fromInt(Int.MAX_VALUE))
testBigDecimalSerialization(BigDecimal.fromInt(Int.MIN_VALUE))
testBigDecimalSerialization(BigDecimal.parseString("9999999999999999999.9999999999"))
testBigDecimalSerialization(BigDecimal.parseString("-9999999999999999999.9999999999"))
}
private fun testBigDecimalSerialization(value: BigDecimal) {
@Serializable
data class TestClass(
@Serializable(with = BigDecimalSerializer::class)
val value: BigDecimal
)
val testObject = TestClass(value)
val json = Json { prettyPrint = true }
val jsonString = json.encodeToString(testObject)
// Verify serialization
assertTrue(jsonString.contains("\"value\""))
assertTrue(jsonString.contains(value.toStringExpanded()))
// Verify deserialization
val deserializedObject = json.decodeFromString<TestClass>(jsonString)
assertEquals(value, deserializedObject.value)
}
@Test
fun testUuidSerializer() {
// Test regular UUIDs
testUuidSerialization(uuid4())
testUuidSerialization(uuidFrom("00000000-0000-0000-0000-000000000000"))
testUuidSerialization(uuidFrom("ffffffff-ffff-ffff-ffff-ffffffffffff"))
// Test specific UUID formats
testUuidSerialization(uuidFrom("12345678-1234-5678-1234-567812345678"))
}
private fun testUuidSerialization(value: Uuid) {
@Serializable
data class TestClass(
@Serializable(with = UuidSerializer::class)
val id: Uuid
)
val testObject = TestClass(value)
val json = Json { prettyPrint = true }
val jsonString = json.encodeToString(testObject)
// Verify serialization
assertTrue(jsonString.contains("\"id\""))
assertTrue(jsonString.contains(value.toString()))
// Verify deserialization
val deserializedObject = json.decodeFromString<TestClass>(jsonString)
assertEquals(value, deserializedObject.id)
}
@Test
fun testKotlinInstantSerializer() {
// Test current time
testInstantSerialization(Clock.System.now())
// Test specific instants
testInstantSerialization(Instant.parse("2023-01-01T00:00:00Z"))
testInstantSerialization(Instant.parse("1970-01-01T00:00:00Z"))
testInstantSerialization(Instant.parse("2099-12-31T23:59:59.999Z"))
// Test with different time zones
testInstantSerialization(Instant.parse("2023-06-15T12:30:45.123+02:00"))
}
private fun testInstantSerialization(value: Instant) {
@Serializable
data class TestClass(
@Serializable(with = KotlinInstantSerializer::class)
val timestamp: Instant
)
val testObject = TestClass(value)
val json = Json { prettyPrint = true }
val jsonString = json.encodeToString(testObject)
// Verify serialization
assertTrue(jsonString.contains("\"timestamp\""))
assertTrue(jsonString.contains(value.toString()))
// Verify deserialization
val deserializedObject = json.decodeFromString<TestClass>(jsonString)
assertEquals(value, deserializedObject.timestamp)
}
@Test
fun testKotlinLocalDateSerializer() {
// Test regular dates
testLocalDateSerialization(LocalDate(2023, 1, 1))
testLocalDateSerialization(LocalDate(2000, 2, 29)) // Leap year
testLocalDateSerialization(LocalDate(1970, 1, 1))
// Test edge cases
testLocalDateSerialization(LocalDate(1, 1, 1))
testLocalDateSerialization(LocalDate(9999, 12, 31))
}
private fun testLocalDateSerialization(value: LocalDate) {
@Serializable
data class TestClass(
@Serializable(with = KotlinLocalDateSerializer::class)
val date: LocalDate
)
val testObject = TestClass(value)
val json = Json { prettyPrint = true }
val jsonString = json.encodeToString(testObject)
// Verify serialization
assertTrue(jsonString.contains("\"date\""))
assertTrue(jsonString.contains(value.toString()))
// Verify deserialization
val deserializedObject = json.decodeFromString<TestClass>(jsonString)
assertEquals(value, deserializedObject.date)
}
@Test
fun testKotlinLocalDateTimeSerializer() {
// Test regular date-times
testLocalDateTimeSerialization(LocalDateTime(2023, 1, 1, 12, 0, 0))
testLocalDateTimeSerialization(LocalDateTime(2000, 2, 29, 23, 59, 59)) // Leap year
testLocalDateTimeSerialization(LocalDateTime(1970, 1, 1, 0, 0, 0))
// Test with nanoseconds
testLocalDateTimeSerialization(LocalDateTime(2023, 6, 15, 12, 30, 45, 123456789))
// Test edge cases
testLocalDateTimeSerialization(LocalDateTime(1, 1, 1, 0, 0, 0))
testLocalDateTimeSerialization(LocalDateTime(9999, 12, 31, 23, 59, 59, 999999999))
}
private fun testLocalDateTimeSerialization(value: LocalDateTime) {
@Serializable
data class TestClass(
@Serializable(with = KotlinLocalDateTimeSerializer::class)
val dateTime: LocalDateTime
)
val testObject = TestClass(value)
val json = Json { prettyPrint = true }
val jsonString = json.encodeToString(testObject)
// Verify serialization
assertTrue(jsonString.contains("\"dateTime\""))
assertTrue(jsonString.contains(value.toString()))
// Verify deserialization
val deserializedObject = json.decodeFromString<TestClass>(jsonString)
assertEquals(value, deserializedObject.dateTime)
}
@Test
fun testAllSerializersInSingleObject() {
@Serializable
data class ComplexObject(
@Serializable(with = UuidSerializer::class)
val id: Uuid,
@Serializable(with = BigDecimalSerializer::class)
val amount: BigDecimal,
@Serializable(with = KotlinInstantSerializer::class)
val createdAt: Instant,
@Serializable(with = KotlinLocalDateSerializer::class)
val date: LocalDate,
@Serializable(with = KotlinLocalDateTimeSerializer::class)
val dateTime: LocalDateTime
)
val obj = ComplexObject(
id = uuid4(),
amount = BigDecimal.parseString("123.456"),
createdAt = Clock.System.now(),
date = LocalDate(2023, 1, 1),
dateTime = LocalDateTime(2023, 1, 1, 12, 0, 0)
)
val json = Json { prettyPrint = true }
val jsonString = json.encodeToString(obj)
// Verify serialization contains all fields
assertTrue(jsonString.contains("\"id\""))
assertTrue(jsonString.contains("\"amount\""))
assertTrue(jsonString.contains("\"createdAt\""))
assertTrue(jsonString.contains("\"date\""))
assertTrue(jsonString.contains("\"dateTime\""))
// Verify deserialization
val deserializedObj = json.decodeFromString<ComplexObject>(jsonString)
assertEquals(obj.id, deserializedObj.id)
assertEquals(obj.amount, deserializedObj.amount)
assertEquals(obj.createdAt, deserializedObj.createdAt)
assertEquals(obj.date, deserializedObj.date)
assertEquals(obj.dateTime, deserializedObj.dateTime)
}
}
@@ -0,0 +1,170 @@
package at.mocode.shared.model.stammdaten
import at.mocode.shared.model.enums.LizenzTyp
import at.mocode.shared.model.enums.Sparte
import at.mocode.shared.model.serializers.KotlinLocalDateSerializer
import kotlinx.datetime.LocalDate
import kotlinx.serialization.decodeFromString
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertNotNull
import kotlin.test.assertTrue
class LizenzInfoTest {
@Test
fun testCreateLizenzInfoWithMinimalParameters() {
// Create a LizenzInfo with minimal required parameters
val lizenzInfo = LizenzInfo(
lizenzTyp = LizenzTyp.REITER,
stufe = null,
sparte = null,
gueltigBisJahr = null,
ausgestelltAm = null
)
// Verify required fields
assertEquals(LizenzTyp.REITER, lizenzInfo.lizenzTyp)
// Verify optional fields are null
assertEquals(null, lizenzInfo.stufe)
assertEquals(null, lizenzInfo.sparte)
assertEquals(null, lizenzInfo.gueltigBisJahr)
assertEquals(null, lizenzInfo.ausgestelltAm)
}
@Test
fun testCreateLizenzInfoWithAllParameters() {
// Create a LizenzInfo with all parameters
val ausgestelltAm = LocalDate(2023, 1, 15)
val lizenzInfo = LizenzInfo(
lizenzTyp = LizenzTyp.FAHRER,
stufe = "A",
sparte = Sparte.DRESSUR,
gueltigBisJahr = 2024,
ausgestelltAm = ausgestelltAm
)
// Verify all fields
assertEquals(LizenzTyp.FAHRER, lizenzInfo.lizenzTyp)
assertEquals("A", lizenzInfo.stufe)
assertEquals(Sparte.DRESSUR, lizenzInfo.sparte)
assertEquals(2024, lizenzInfo.gueltigBisJahr)
assertEquals(ausgestelltAm, lizenzInfo.ausgestelltAm)
}
@Test
fun testSerializationDeserialization() {
// Create a LizenzInfo with all parameters
val ausgestelltAm = LocalDate(2023, 5, 20)
val lizenzInfo = LizenzInfo(
lizenzTyp = LizenzTyp.VOLTIGIERER,
stufe = "B",
sparte = Sparte.VOLTIGIEREN,
gueltigBisJahr = 2025,
ausgestelltAm = ausgestelltAm
)
// Serialize to JSON
val json = Json {
prettyPrint = true
encodeDefaults = true
}
val jsonString = json.encodeToString(lizenzInfo)
// Verify JSON contains expected fields
assertTrue(jsonString.contains("\"lizenzTyp\""), "JSON should contain lizenzTyp field")
assertTrue(jsonString.contains("\"VOLTIGIERER\""), "JSON should contain value VOLTIGIERER")
assertTrue(jsonString.contains("\"stufe\""), "JSON should contain stufe field")
assertTrue(jsonString.contains("\"B\""), "JSON should contain value B")
assertTrue(jsonString.contains("\"sparte\""), "JSON should contain sparte field")
assertTrue(jsonString.contains("\"VOLTIGIEREN\""), "JSON should contain value VOLTIGIEREN")
assertTrue(jsonString.contains("\"gueltigBisJahr\""), "JSON should contain gueltigBisJahr field")
assertTrue(jsonString.contains("2025"), "JSON should contain value 2025")
assertTrue(jsonString.contains("\"ausgestelltAm\""), "JSON should contain ausgestelltAm field")
assertTrue(jsonString.contains("2023-05-20"), "JSON should contain value 2023-05-20")
// Deserialize from JSON
val deserializedLizenzInfo = json.decodeFromString<LizenzInfo>(jsonString)
// Verify deserialized object matches original
assertEquals(lizenzInfo.lizenzTyp, deserializedLizenzInfo.lizenzTyp)
assertEquals(lizenzInfo.stufe, deserializedLizenzInfo.stufe)
assertEquals(lizenzInfo.sparte, deserializedLizenzInfo.sparte)
assertEquals(lizenzInfo.gueltigBisJahr, deserializedLizenzInfo.gueltigBisJahr)
assertEquals(lizenzInfo.ausgestelltAm, deserializedLizenzInfo.ausgestelltAm)
}
@Test
fun testCopyLizenzInfo() {
// Create a LizenzInfo
val original = LizenzInfo(
lizenzTyp = LizenzTyp.WESTERN,
stufe = "C",
sparte = Sparte.WESTERN,
gueltigBisJahr = 2023,
ausgestelltAm = null
)
// Create a copy with some modified properties
val copy = original.copy(
stufe = "B",
gueltigBisJahr = 2024,
ausgestelltAm = LocalDate(2023, 12, 1)
)
// Verify copied properties
assertEquals(original.lizenzTyp, copy.lizenzTyp)
assertEquals(original.sparte, copy.sparte)
// Verify modified properties
assertEquals("B", copy.stufe)
assertEquals(2024, copy.gueltigBisJahr)
assertEquals(LocalDate(2023, 12, 1), copy.ausgestelltAm)
}
@Test
fun testDifferentLizenzTypes() {
// Test different LizenzTyp values
val lizenzTypes = listOf(
LizenzTyp.REITER,
LizenzTyp.FAHRER,
LizenzTyp.VOLTIGIERER,
LizenzTyp.WESTERN,
LizenzTyp.WORKING_EQUITATION,
LizenzTyp.POLO,
LizenzTyp.STARTKARTE_ALLG,
LizenzTyp.STARTKARTE_VOLTIGIEREN,
LizenzTyp.STARTKARTE_WESTERN,
LizenzTyp.STARTKARTE_ISLAND,
LizenzTyp.STARTKARTE_FAHREN_JUGEND,
LizenzTyp.STARTKARTE_HORSEBALL,
LizenzTyp.STARTKARTE_POLO,
LizenzTyp.PARAEQUESTRIAN,
LizenzTyp.SONSTIGE
)
for (lizenzTyp in lizenzTypes) {
val lizenzInfo = LizenzInfo(
lizenzTyp = lizenzTyp,
stufe = "Test",
sparte = Sparte.SONSTIGES,
gueltigBisJahr = 2024,
ausgestelltAm = null
)
assertEquals(lizenzTyp, lizenzInfo.lizenzTyp)
// Serialize and deserialize to verify enum handling
val json = Json { encodeDefaults = true }
val jsonString = json.encodeToString(lizenzInfo)
val deserializedLizenzInfo = json.decodeFromString<LizenzInfo>(jsonString)
assertEquals(lizenzTyp, deserializedLizenzInfo.lizenzTyp)
}
}
}
@@ -0,0 +1,393 @@
package at.mocode.shared.model.stammdaten
import at.mocode.shared.model.enums.FunktionaerRolle
import at.mocode.shared.model.enums.Geschlecht
import at.mocode.shared.model.enums.LizenzTyp
import at.mocode.shared.model.enums.Sparte
import at.mocode.shared.model.serializers.KotlinInstantSerializer
import at.mocode.shared.model.serializers.KotlinLocalDateSerializer
import at.mocode.shared.model.serializers.UuidSerializer
import com.benasher44.uuid.Uuid
import com.benasher44.uuid.uuid4
import kotlinx.datetime.Clock
import kotlinx.datetime.Instant
import kotlinx.datetime.LocalDate
import kotlinx.serialization.decodeFromString
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertNotEquals
import kotlin.test.assertNotNull
import kotlin.test.assertTrue
class PersonTest {
@Test
fun testCreatePerson() {
// Create a Person with minimal required parameters
val person = Person(
oepsSatzNr = null,
nachname = "Mustermann",
vorname = "Max",
titel = null,
geburtsdatum = null,
geschlecht = null,
nationalitaet = null,
email = null,
telefon = null,
adresse = null,
plz = null,
ort = null,
stammVereinId = null,
mitgliedsNummerIntern = null,
letzteZahlungJahr = null,
feiId = null,
sperrGrund = null
)
// Verify required fields
assertEquals("Mustermann", person.nachname)
assertEquals("Max", person.vorname)
// Verify default values
assertNotNull(person.id)
assertEquals(null, person.geschlecht)
assertEquals(false, person.istGesperrt)
assertTrue(person.rollen.isEmpty())
assertTrue(person.lizenzen.isEmpty())
assertTrue(person.qualifikationenRichter.isEmpty())
assertTrue(person.qualifikationenParcoursbauer.isEmpty())
assertTrue(person.istAktiv)
assertNotNull(person.createdAt)
assertNotNull(person.updatedAt)
// Verify optional fields are null
assertEquals(null, person.oepsSatzNr)
assertEquals(null, person.titel)
assertEquals(null, person.geburtsdatum)
assertEquals(null, person.nationalitaet)
assertEquals(null, person.email)
assertEquals(null, person.telefon)
assertEquals(null, person.adresse)
assertEquals(null, person.plz)
assertEquals(null, person.ort)
assertEquals(null, person.stammVereinId)
assertEquals(null, person.mitgliedsNummerIntern)
assertEquals(null, person.letzteZahlungJahr)
assertEquals(null, person.feiId)
assertEquals(null, person.sperrGrund)
}
@Test
fun testCreatePersonWithAllParameters() {
// Create a Person with all parameters
val id = uuid4()
val stammVereinId = uuid4()
val now = Clock.System.now()
val geburtsdatum = LocalDate(1990, 1, 15)
val rollen = setOf(FunktionaerRolle.RICHTER, FunktionaerRolle.TURNIERLEITER)
val lizenzen = listOf(
LizenzInfo(
lizenzTyp = LizenzTyp.REITER,
stufe = "A",
sparte = Sparte.SPRINGEN,
gueltigBisJahr = 2024,
ausgestelltAm = null
)
)
val qualifikationenRichter = listOf("Springen A", "Dressur B")
val qualifikationenParcoursbauer = listOf("Springen A")
val person = Person(
id = id,
oepsSatzNr = "12345",
nachname = "Vollständig",
vorname = "Victoria",
titel = "Dr.",
geburtsdatum = geburtsdatum,
geschlecht = Geschlecht.W,
nationalitaet = "AUT",
email = "victoria@example.com",
telefon = "+43 123 456789",
adresse = "Musterstraße 1",
plz = "1010",
ort = "Wien",
stammVereinId = stammVereinId,
mitgliedsNummerIntern = "M12345",
letzteZahlungJahr = 2023,
feiId = "FEI12345",
istGesperrt = false,
sperrGrund = null,
rollen = rollen,
lizenzen = lizenzen,
qualifikationenRichter = qualifikationenRichter,
qualifikationenParcoursbauer = qualifikationenParcoursbauer,
istAktiv = true,
createdAt = now,
updatedAt = now
)
// Verify all fields
assertEquals(id, person.id)
assertEquals("12345", person.oepsSatzNr)
assertEquals("Vollständig", person.nachname)
assertEquals("Victoria", person.vorname)
assertEquals("Dr.", person.titel)
assertEquals(geburtsdatum, person.geburtsdatum)
assertEquals(Geschlecht.W, person.geschlecht)
assertEquals("AUT", person.nationalitaet)
assertEquals("victoria@example.com", person.email)
assertEquals("+43 123 456789", person.telefon)
assertEquals("Musterstraße 1", person.adresse)
assertEquals("1010", person.plz)
assertEquals("Wien", person.ort)
assertEquals(stammVereinId, person.stammVereinId)
assertEquals("M12345", person.mitgliedsNummerIntern)
assertEquals(2023, person.letzteZahlungJahr)
assertEquals("FEI12345", person.feiId)
assertEquals(false, person.istGesperrt)
assertEquals(null, person.sperrGrund)
assertEquals(rollen, person.rollen)
assertEquals(lizenzen, person.lizenzen)
assertEquals(qualifikationenRichter, person.qualifikationenRichter)
assertEquals(qualifikationenParcoursbauer, person.qualifikationenParcoursbauer)
assertEquals(true, person.istAktiv)
assertEquals(now, person.createdAt)
assertEquals(now, person.updatedAt)
}
@Test
fun testModifyPerson() {
// Create a Person
val person = Person(
oepsSatzNr = null,
nachname = "Original",
vorname = "Otto",
titel = null,
geburtsdatum = null,
geschlecht = null,
nationalitaet = null,
email = null,
telefon = null,
adresse = null,
plz = null,
ort = null,
stammVereinId = null,
mitgliedsNummerIntern = null,
letzteZahlungJahr = null,
feiId = null,
sperrGrund = null
)
val originalUpdatedAt = person.updatedAt
val stammVereinId = uuid4()
val geburtsdatum = LocalDate(1985, 5, 20)
val rollen = setOf(FunktionaerRolle.PARCOURSBAUER)
val lizenzen = listOf(
LizenzInfo(
lizenzTyp = LizenzTyp.FAHRER,
stufe = "B",
sparte = Sparte.DRESSUR,
gueltigBisJahr = 2025,
ausgestelltAm = null
)
)
// Modify properties
person.oepsSatzNr = "54321"
person.nachname = "Updated"
person.vorname = "Ulrike"
person.titel = "Mag."
person.geburtsdatum = geburtsdatum
person.geschlecht = Geschlecht.W
person.nationalitaet = "GER"
person.email = "ulrike@example.com"
person.telefon = "+49 987 654321"
person.adresse = "Neue Straße 2"
person.plz = "10115"
person.ort = "Berlin"
person.stammVereinId = stammVereinId
person.mitgliedsNummerIntern = "M54321"
person.letzteZahlungJahr = 2024
person.feiId = "FEI54321"
person.istGesperrt = true
person.sperrGrund = "Administrativer Grund"
person.rollen = rollen
person.lizenzen = lizenzen
person.qualifikationenRichter = listOf("Neue Qualifikation")
person.qualifikationenParcoursbauer = listOf("Parcours A", "Parcours B")
person.istAktiv = false
person.updatedAt = Clock.System.now()
// Verify modifications
assertEquals("54321", person.oepsSatzNr)
assertEquals("Updated", person.nachname)
assertEquals("Ulrike", person.vorname)
assertEquals("Mag.", person.titel)
assertEquals(geburtsdatum, person.geburtsdatum)
assertEquals(Geschlecht.W, person.geschlecht)
assertEquals("GER", person.nationalitaet)
assertEquals("ulrike@example.com", person.email)
assertEquals("+49 987 654321", person.telefon)
assertEquals("Neue Straße 2", person.adresse)
assertEquals("10115", person.plz)
assertEquals("Berlin", person.ort)
assertEquals(stammVereinId, person.stammVereinId)
assertEquals("M54321", person.mitgliedsNummerIntern)
assertEquals(2024, person.letzteZahlungJahr)
assertEquals("FEI54321", person.feiId)
assertEquals(true, person.istGesperrt)
assertEquals("Administrativer Grund", person.sperrGrund)
assertEquals(rollen, person.rollen)
assertEquals(lizenzen, person.lizenzen)
assertEquals(listOf("Neue Qualifikation"), person.qualifikationenRichter)
assertEquals(listOf("Parcours A", "Parcours B"), person.qualifikationenParcoursbauer)
assertEquals(false, person.istAktiv)
assertNotEquals(originalUpdatedAt, person.updatedAt)
}
@Test
fun testSerializationDeserialization() {
// Create a Person with all parameters
val stammVereinId = uuid4()
val geburtsdatum = LocalDate(1980, 3, 10)
val rollen = setOf(FunktionaerRolle.RICHTER, FunktionaerRolle.STEWARD)
val lizenzen = listOf(
LizenzInfo(
lizenzTyp = LizenzTyp.REITER,
stufe = "A",
sparte = Sparte.SPRINGEN,
gueltigBisJahr = 2024,
ausgestelltAm = null
)
)
val person = Person(
oepsSatzNr = "12345",
nachname = "Serialization",
vorname = "Samuel",
titel = "Prof.",
geburtsdatum = geburtsdatum,
geschlecht = Geschlecht.M,
nationalitaet = "AUT",
email = "samuel@example.com",
telefon = "+43 123 456789",
adresse = "Testgasse 3",
plz = "8010",
ort = "Graz",
stammVereinId = stammVereinId,
mitgliedsNummerIntern = "M12345",
letzteZahlungJahr = 2023,
feiId = "FEI12345",
istGesperrt = false,
sperrGrund = null,
rollen = rollen,
lizenzen = lizenzen,
qualifikationenRichter = listOf("Springen A"),
qualifikationenParcoursbauer = emptyList(),
istAktiv = true
)
// Serialize to JSON
val json = Json {
prettyPrint = true
encodeDefaults = true
}
val jsonString = json.encodeToString(person)
// Verify JSON contains expected fields
assertTrue(jsonString.contains("\"nachname\""), "JSON should contain nachname field")
assertTrue(jsonString.contains("\"Serialization\""), "JSON should contain value Serialization")
assertTrue(jsonString.contains("\"vorname\""), "JSON should contain vorname field")
assertTrue(jsonString.contains("\"Samuel\""), "JSON should contain value Samuel")
assertTrue(jsonString.contains("\"geschlecht\""), "JSON should contain geschlecht field")
assertTrue(jsonString.contains("\"M\""), "JSON should contain value M")
assertTrue(jsonString.contains("\"istAktiv\""), "JSON should contain istAktiv field")
assertTrue(jsonString.contains("\"stammVereinId\""), "JSON should contain stammVereinId field")
assertTrue(jsonString.contains(stammVereinId.toString()), "JSON should contain stammVereinId value")
assertTrue(jsonString.contains("\"rollen\""), "JSON should contain rollen field")
assertTrue(jsonString.contains("\"RICHTER\""), "JSON should contain value RICHTER")
assertTrue(jsonString.contains("\"STEWARD\""), "JSON should contain value STEWARD")
// Deserialize from JSON
val deserializedPerson = json.decodeFromString<Person>(jsonString)
// Verify deserialized object matches original
assertEquals(person.id, deserializedPerson.id)
assertEquals(person.oepsSatzNr, deserializedPerson.oepsSatzNr)
assertEquals(person.nachname, deserializedPerson.nachname)
assertEquals(person.vorname, deserializedPerson.vorname)
assertEquals(person.titel, deserializedPerson.titel)
assertEquals(person.geburtsdatum, deserializedPerson.geburtsdatum)
assertEquals(person.geschlecht, deserializedPerson.geschlecht)
assertEquals(person.nationalitaet, deserializedPerson.nationalitaet)
assertEquals(person.email, deserializedPerson.email)
assertEquals(person.telefon, deserializedPerson.telefon)
assertEquals(person.adresse, deserializedPerson.adresse)
assertEquals(person.plz, deserializedPerson.plz)
assertEquals(person.ort, deserializedPerson.ort)
assertEquals(person.stammVereinId, deserializedPerson.stammVereinId)
assertEquals(person.mitgliedsNummerIntern, deserializedPerson.mitgliedsNummerIntern)
assertEquals(person.letzteZahlungJahr, deserializedPerson.letzteZahlungJahr)
assertEquals(person.feiId, deserializedPerson.feiId)
assertEquals(person.istGesperrt, deserializedPerson.istGesperrt)
assertEquals(person.sperrGrund, deserializedPerson.sperrGrund)
assertEquals(person.rollen, deserializedPerson.rollen)
assertEquals(person.lizenzen, deserializedPerson.lizenzen)
assertEquals(person.qualifikationenRichter, deserializedPerson.qualifikationenRichter)
assertEquals(person.qualifikationenParcoursbauer, deserializedPerson.qualifikationenParcoursbauer)
assertEquals(person.istAktiv, deserializedPerson.istAktiv)
assertEquals(person.createdAt, deserializedPerson.createdAt)
assertEquals(person.updatedAt, deserializedPerson.updatedAt)
}
@Test
fun testCopyPerson() {
// Create a Person
val original = Person(
oepsSatzNr = null,
nachname = "Original",
vorname = "Otto",
titel = null,
geburtsdatum = null,
geschlecht = null,
nationalitaet = null,
email = null,
telefon = null,
adresse = null,
plz = null,
ort = null,
stammVereinId = null,
mitgliedsNummerIntern = null,
letzteZahlungJahr = null,
feiId = null,
sperrGrund = null
)
val stammVereinId = uuid4()
val geburtsdatum = LocalDate(1975, 8, 30)
// Create a copy with some modified properties
val copy = original.copy(
nachname = "Copy",
vorname = "Clara",
geburtsdatum = geburtsdatum,
stammVereinId = stammVereinId,
rollen = setOf(FunktionaerRolle.ZEITNEHMER)
)
// Verify copied properties
assertEquals(original.id, copy.id)
assertEquals(original.oepsSatzNr, copy.oepsSatzNr)
assertEquals(original.createdAt, copy.createdAt)
assertEquals(original.updatedAt, copy.updatedAt)
// Verify modified properties
assertEquals("Copy", copy.nachname)
assertEquals("Clara", copy.vorname)
assertEquals(geburtsdatum, copy.geburtsdatum)
assertEquals(stammVereinId, copy.stammVereinId)
assertEquals(setOf(FunktionaerRolle.ZEITNEHMER), copy.rollen)
}
}
@@ -0,0 +1,317 @@
package at.mocode.shared.model.stammdaten
import at.mocode.shared.model.enums.GeschlechtPferd
import at.mocode.shared.model.serializers.KotlinInstantSerializer
import at.mocode.shared.model.serializers.UuidSerializer
import com.benasher44.uuid.Uuid
import com.benasher44.uuid.uuid4
import kotlinx.datetime.Clock
import kotlinx.datetime.Instant
import kotlinx.serialization.decodeFromString
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertNotEquals
import kotlin.test.assertNotNull
import kotlin.test.assertTrue
class PferdTest {
@Test
fun testCreatePferd() {
// Create a Pferd with minimal required parameters
val pferd = Pferd(
oepsKopfNr = null,
oepsSatzNr = null,
name = "Test Pferd",
lebensnummer = null,
feiPassNr = null,
geschlecht = null,
geburtsjahr = null,
rasse = null,
farbe = null,
vaterName = null,
mutterName = null,
mutterVaterName = null,
besitzerId = null,
verantwortlichePersonId = null,
heimatVereinId = null,
letzteZahlungJahrOeps = null,
stockmassCm = null
)
// Verify required fields
assertEquals("Test Pferd", pferd.name)
// Verify default values
assertNotNull(pferd.id)
assertTrue(pferd.istAktiv)
assertNotNull(pferd.createdAt)
assertNotNull(pferd.updatedAt)
// Verify optional fields are null
assertEquals(null, pferd.oepsKopfNr)
assertEquals(null, pferd.oepsSatzNr)
assertEquals(null, pferd.lebensnummer)
assertEquals(null, pferd.feiPassNr)
assertEquals(null, pferd.geschlecht)
assertEquals(null, pferd.geburtsjahr)
assertEquals(null, pferd.rasse)
assertEquals(null, pferd.farbe)
assertEquals(null, pferd.vaterName)
assertEquals(null, pferd.mutterName)
assertEquals(null, pferd.mutterVaterName)
assertEquals(null, pferd.besitzerId)
assertEquals(null, pferd.verantwortlichePersonId)
assertEquals(null, pferd.heimatVereinId)
assertEquals(null, pferd.letzteZahlungJahrOeps)
assertEquals(null, pferd.stockmassCm)
}
@Test
fun testCreatePferdWithAllParameters() {
// Create a Pferd with all parameters
val id = uuid4()
val besitzerId = uuid4()
val verantwortlichePersonId = uuid4()
val heimatVereinId = uuid4()
val now = Clock.System.now()
val pferd = Pferd(
id = id,
oepsKopfNr = "K12345",
oepsSatzNr = "S12345",
name = "Vollständiges Pferd",
lebensnummer = "AT123456789",
feiPassNr = "FEI12345",
geschlecht = GeschlechtPferd.WALLACH,
geburtsjahr = 2015,
rasse = "Hannoveraner",
farbe = "Fuchs",
vaterName = "Vater Pferd",
mutterName = "Mutter Pferd",
mutterVaterName = "Muttervater Pferd",
besitzerId = besitzerId,
verantwortlichePersonId = verantwortlichePersonId,
heimatVereinId = heimatVereinId,
letzteZahlungJahrOeps = 2023,
stockmassCm = 168,
istAktiv = true,
createdAt = now,
updatedAt = now
)
// Verify all fields
assertEquals(id, pferd.id)
assertEquals("K12345", pferd.oepsKopfNr)
assertEquals("S12345", pferd.oepsSatzNr)
assertEquals("Vollständiges Pferd", pferd.name)
assertEquals("AT123456789", pferd.lebensnummer)
assertEquals("FEI12345", pferd.feiPassNr)
assertEquals(GeschlechtPferd.WALLACH, pferd.geschlecht)
assertEquals(2015, pferd.geburtsjahr)
assertEquals("Hannoveraner", pferd.rasse)
assertEquals("Fuchs", pferd.farbe)
assertEquals("Vater Pferd", pferd.vaterName)
assertEquals("Mutter Pferd", pferd.mutterName)
assertEquals("Muttervater Pferd", pferd.mutterVaterName)
assertEquals(besitzerId, pferd.besitzerId)
assertEquals(verantwortlichePersonId, pferd.verantwortlichePersonId)
assertEquals(heimatVereinId, pferd.heimatVereinId)
assertEquals(2023, pferd.letzteZahlungJahrOeps)
assertEquals(168, pferd.stockmassCm)
assertEquals(true, pferd.istAktiv)
assertEquals(now, pferd.createdAt)
assertEquals(now, pferd.updatedAt)
}
@Test
fun testModifyPferd() {
// Create a Pferd
val pferd = Pferd(
oepsKopfNr = null,
oepsSatzNr = null,
name = "Test Pferd",
lebensnummer = null,
feiPassNr = null,
geschlecht = null,
geburtsjahr = null,
rasse = null,
farbe = null,
vaterName = null,
mutterName = null,
mutterVaterName = null,
besitzerId = null,
verantwortlichePersonId = null,
heimatVereinId = null,
letzteZahlungJahrOeps = null,
stockmassCm = null
)
val originalUpdatedAt = pferd.updatedAt
val besitzerId = uuid4()
val verantwortlichePersonId = uuid4()
val heimatVereinId = uuid4()
// Modify properties
pferd.oepsKopfNr = "K54321"
pferd.oepsSatzNr = "S54321"
pferd.name = "Updated Pferd"
pferd.lebensnummer = "AT987654321"
pferd.feiPassNr = "FEI54321"
pferd.geschlecht = GeschlechtPferd.STUTE
pferd.geburtsjahr = 2018
pferd.rasse = "Trakehner"
pferd.farbe = "Rappe"
pferd.vaterName = "Neuer Vater"
pferd.mutterName = "Neue Mutter"
pferd.mutterVaterName = "Neuer Muttervater"
pferd.besitzerId = besitzerId
pferd.verantwortlichePersonId = verantwortlichePersonId
pferd.heimatVereinId = heimatVereinId
pferd.letzteZahlungJahrOeps = 2024
pferd.stockmassCm = 165
pferd.istAktiv = false
pferd.updatedAt = Clock.System.now()
// Verify modifications
assertEquals("K54321", pferd.oepsKopfNr)
assertEquals("S54321", pferd.oepsSatzNr)
assertEquals("Updated Pferd", pferd.name)
assertEquals("AT987654321", pferd.lebensnummer)
assertEquals("FEI54321", pferd.feiPassNr)
assertEquals(GeschlechtPferd.STUTE, pferd.geschlecht)
assertEquals(2018, pferd.geburtsjahr)
assertEquals("Trakehner", pferd.rasse)
assertEquals("Rappe", pferd.farbe)
assertEquals("Neuer Vater", pferd.vaterName)
assertEquals("Neue Mutter", pferd.mutterName)
assertEquals("Neuer Muttervater", pferd.mutterVaterName)
assertEquals(besitzerId, pferd.besitzerId)
assertEquals(verantwortlichePersonId, pferd.verantwortlichePersonId)
assertEquals(heimatVereinId, pferd.heimatVereinId)
assertEquals(2024, pferd.letzteZahlungJahrOeps)
assertEquals(165, pferd.stockmassCm)
assertEquals(false, pferd.istAktiv)
assertNotEquals(originalUpdatedAt, pferd.updatedAt)
}
@Test
fun testSerializationDeserialization() {
// Create a Pferd with all parameters
val besitzerId = uuid4()
val verantwortlichePersonId = uuid4()
val heimatVereinId = uuid4()
val pferd = Pferd(
oepsKopfNr = "K12345",
oepsSatzNr = "S12345",
name = "Serialization Pferd",
lebensnummer = "AT123456789",
feiPassNr = "FEI12345",
geschlecht = GeschlechtPferd.HENGST,
geburtsjahr = 2016,
rasse = "Holsteiner",
farbe = "Schimmel",
vaterName = "Vater Pferd",
mutterName = "Mutter Pferd",
mutterVaterName = "Muttervater Pferd",
besitzerId = besitzerId,
verantwortlichePersonId = verantwortlichePersonId,
heimatVereinId = heimatVereinId,
letzteZahlungJahrOeps = 2023,
stockmassCm = 170,
istAktiv = true
)
// Serialize to JSON
val json = Json {
prettyPrint = true
encodeDefaults = true
}
val jsonString = json.encodeToString(pferd)
// Verify JSON contains expected fields
assertTrue(jsonString.contains("\"name\""), "JSON should contain name field")
assertTrue(jsonString.contains("\"Serialization Pferd\""), "JSON should contain value Serialization Pferd")
assertTrue(jsonString.contains("\"geschlecht\""), "JSON should contain geschlecht field")
assertTrue(jsonString.contains("\"HENGST\""), "JSON should contain value HENGST")
assertTrue(jsonString.contains("\"rasse\""), "JSON should contain rasse field")
assertTrue(jsonString.contains("\"Holsteiner\""), "JSON should contain value Holsteiner")
assertTrue(jsonString.contains("\"istAktiv\""), "JSON should contain istAktiv field")
assertTrue(jsonString.contains("\"besitzerId\""), "JSON should contain besitzerId field")
assertTrue(jsonString.contains(besitzerId.toString()), "JSON should contain besitzerId value")
// Deserialize from JSON
val deserializedPferd = json.decodeFromString<Pferd>(jsonString)
// Verify deserialized object matches original
assertEquals(pferd.id, deserializedPferd.id)
assertEquals(pferd.oepsKopfNr, deserializedPferd.oepsKopfNr)
assertEquals(pferd.oepsSatzNr, deserializedPferd.oepsSatzNr)
assertEquals(pferd.name, deserializedPferd.name)
assertEquals(pferd.lebensnummer, deserializedPferd.lebensnummer)
assertEquals(pferd.feiPassNr, deserializedPferd.feiPassNr)
assertEquals(pferd.geschlecht, deserializedPferd.geschlecht)
assertEquals(pferd.geburtsjahr, deserializedPferd.geburtsjahr)
assertEquals(pferd.rasse, deserializedPferd.rasse)
assertEquals(pferd.farbe, deserializedPferd.farbe)
assertEquals(pferd.vaterName, deserializedPferd.vaterName)
assertEquals(pferd.mutterName, deserializedPferd.mutterName)
assertEquals(pferd.mutterVaterName, deserializedPferd.mutterVaterName)
assertEquals(pferd.besitzerId, deserializedPferd.besitzerId)
assertEquals(pferd.verantwortlichePersonId, deserializedPferd.verantwortlichePersonId)
assertEquals(pferd.heimatVereinId, deserializedPferd.heimatVereinId)
assertEquals(pferd.letzteZahlungJahrOeps, deserializedPferd.letzteZahlungJahrOeps)
assertEquals(pferd.stockmassCm, deserializedPferd.stockmassCm)
assertEquals(pferd.istAktiv, deserializedPferd.istAktiv)
assertEquals(pferd.createdAt, deserializedPferd.createdAt)
assertEquals(pferd.updatedAt, deserializedPferd.updatedAt)
}
@Test
fun testCopyPferd() {
// Create a Pferd
val original = Pferd(
oepsKopfNr = null,
oepsSatzNr = null,
name = "Original Pferd",
lebensnummer = null,
feiPassNr = null,
geschlecht = null,
geburtsjahr = null,
rasse = null,
farbe = null,
vaterName = null,
mutterName = null,
mutterVaterName = null,
besitzerId = null,
verantwortlichePersonId = null,
heimatVereinId = null,
letzteZahlungJahrOeps = null,
stockmassCm = null
)
val besitzerId = uuid4()
// Create a copy with some modified properties
val copy = original.copy(
name = "Copy Pferd",
rasse = "Arabisches Vollblut",
besitzerId = besitzerId
)
// Verify copied properties
assertEquals(original.id, copy.id)
assertEquals(original.oepsKopfNr, copy.oepsKopfNr)
assertEquals(original.oepsSatzNr, copy.oepsSatzNr)
assertEquals(original.createdAt, copy.createdAt)
assertEquals(original.updatedAt, copy.updatedAt)
// Verify modified properties
assertEquals("Copy Pferd", copy.name)
assertEquals("Arabisches Vollblut", copy.rasse)
assertEquals(besitzerId, copy.besitzerId)
}
}
@@ -0,0 +1,231 @@
package at.mocode.shared.model.stammdaten
import at.mocode.shared.model.serializers.KotlinInstantSerializer
import at.mocode.shared.model.serializers.UuidSerializer
import com.benasher44.uuid.Uuid
import com.benasher44.uuid.uuid4
import kotlinx.datetime.Clock
import kotlinx.datetime.Instant
import kotlinx.serialization.decodeFromString
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertNotEquals
import kotlin.test.assertNotNull
import kotlin.test.assertTrue
class VereinTest {
@Test
fun testCreateVerein() {
// Create a Verein with minimal required parameters
val verein = Verein(
oepsVereinsNr = "12345",
name = "Test Verein",
kuerzel = null,
bundesland = null,
adresse = null,
plz = null,
ort = null,
email = null,
telefon = null,
webseite = null
)
// Verify required fields
assertEquals("12345", verein.oepsVereinsNr)
assertEquals("Test Verein", verein.name)
// Verify default values
assertNotNull(verein.id)
assertTrue(verein.istAktiv)
assertNotNull(verein.createdAt)
assertNotNull(verein.updatedAt)
// Verify optional fields are null
assertEquals(null, verein.kuerzel)
assertEquals(null, verein.bundesland)
assertEquals(null, verein.adresse)
assertEquals(null, verein.plz)
assertEquals(null, verein.ort)
assertEquals(null, verein.email)
assertEquals(null, verein.telefon)
assertEquals(null, verein.webseite)
}
@Test
fun testCreateVereinWithAllParameters() {
// Create a Verein with all parameters
val id = uuid4()
val now = Clock.System.now()
val verein = Verein(
id = id,
oepsVereinsNr = "12345",
name = "Test Verein",
kuerzel = "TV",
bundesland = "Wien",
adresse = "Teststraße 1",
plz = "1010",
ort = "Wien",
email = "test@verein.at",
telefon = "+43 1 234567",
webseite = "https://testverein.at",
istAktiv = true,
createdAt = now,
updatedAt = now
)
// Verify all fields
assertEquals(id, verein.id)
assertEquals("12345", verein.oepsVereinsNr)
assertEquals("Test Verein", verein.name)
assertEquals("TV", verein.kuerzel)
assertEquals("Wien", verein.bundesland)
assertEquals("Teststraße 1", verein.adresse)
assertEquals("1010", verein.plz)
assertEquals("Wien", verein.ort)
assertEquals("test@verein.at", verein.email)
assertEquals("+43 1 234567", verein.telefon)
assertEquals("https://testverein.at", verein.webseite)
assertEquals(true, verein.istAktiv)
assertEquals(now, verein.createdAt)
assertEquals(now, verein.updatedAt)
}
@Test
fun testModifyVerein() {
// Create a Verein
val verein = Verein(
oepsVereinsNr = "12345",
name = "Test Verein",
kuerzel = null,
bundesland = null,
adresse = null,
plz = null,
ort = null,
email = null,
telefon = null,
webseite = null
)
val originalUpdatedAt = verein.updatedAt
// Modify properties
verein.oepsVereinsNr = "54321"
verein.name = "Updated Verein"
verein.kuerzel = "UV"
verein.bundesland = "Salzburg"
verein.adresse = "Neue Straße 2"
verein.plz = "5020"
verein.ort = "Salzburg"
verein.email = "updated@verein.at"
verein.telefon = "+43 662 123456"
verein.webseite = "https://updatedverein.at"
verein.istAktiv = false
verein.updatedAt = Clock.System.now()
// Verify modifications
assertEquals("54321", verein.oepsVereinsNr)
assertEquals("Updated Verein", verein.name)
assertEquals("UV", verein.kuerzel)
assertEquals("Salzburg", verein.bundesland)
assertEquals("Neue Straße 2", verein.adresse)
assertEquals("5020", verein.plz)
assertEquals("Salzburg", verein.ort)
assertEquals("updated@verein.at", verein.email)
assertEquals("+43 662 123456", verein.telefon)
assertEquals("https://updatedverein.at", verein.webseite)
assertEquals(false, verein.istAktiv)
assertNotEquals(originalUpdatedAt, verein.updatedAt)
}
@Test
fun testSerializationDeserialization() {
// Create a Verein with all parameters
val verein = Verein(
oepsVereinsNr = "12345",
name = "Test Verein",
kuerzel = "TV",
bundesland = "Wien",
adresse = "Teststraße 1",
plz = "1010",
ort = "Wien",
email = "test@verein.at",
telefon = "+43 1 234567",
webseite = "https://testverein.at",
istAktiv = true
)
// Serialize to JSON
val json = Json {
prettyPrint = true
encodeDefaults = true
}
val jsonString = json.encodeToString(verein)
// Verify JSON contains expected fields
assertTrue(jsonString.contains("\"oepsVereinsNr\""), "JSON should contain oepsVereinsNr field")
assertTrue(jsonString.contains("\"12345\""), "JSON should contain value 12345")
assertTrue(jsonString.contains("\"name\""), "JSON should contain name field")
assertTrue(jsonString.contains("\"Test Verein\""), "JSON should contain value Test Verein")
assertTrue(jsonString.contains("\"kuerzel\""), "JSON should contain kuerzel field")
assertTrue(jsonString.contains("\"TV\""), "JSON should contain value TV")
assertTrue(jsonString.contains("\"bundesland\""), "JSON should contain bundesland field")
assertTrue(jsonString.contains("\"Wien\""), "JSON should contain value Wien")
assertTrue(jsonString.contains("\"istAktiv\""), "JSON should contain istAktiv field")
// Deserialize from JSON
val deserializedVerein = json.decodeFromString<Verein>(jsonString)
// Verify deserialized object matches original
assertEquals(verein.id, deserializedVerein.id)
assertEquals(verein.oepsVereinsNr, deserializedVerein.oepsVereinsNr)
assertEquals(verein.name, deserializedVerein.name)
assertEquals(verein.kuerzel, deserializedVerein.kuerzel)
assertEquals(verein.bundesland, deserializedVerein.bundesland)
assertEquals(verein.adresse, deserializedVerein.adresse)
assertEquals(verein.plz, deserializedVerein.plz)
assertEquals(verein.ort, deserializedVerein.ort)
assertEquals(verein.email, deserializedVerein.email)
assertEquals(verein.telefon, deserializedVerein.telefon)
assertEquals(verein.webseite, deserializedVerein.webseite)
assertEquals(verein.istAktiv, deserializedVerein.istAktiv)
assertEquals(verein.createdAt, deserializedVerein.createdAt)
assertEquals(verein.updatedAt, deserializedVerein.updatedAt)
}
@Test
fun testCopyVerein() {
// Create a Verein
val original = Verein(
oepsVereinsNr = "12345",
name = "Test Verein",
kuerzel = null,
bundesland = null,
adresse = null,
plz = null,
ort = null,
email = null,
telefon = null,
webseite = null
)
// Create a copy with some modified properties
val copy = original.copy(
name = "Copy Verein",
bundesland = "Tirol"
)
// Verify copied properties
assertEquals(original.id, copy.id)
assertEquals(original.oepsVereinsNr, copy.oepsVereinsNr)
assertEquals(original.createdAt, copy.createdAt)
assertEquals(original.updatedAt, copy.updatedAt)
// Verify modified properties
assertEquals("Copy Verein", copy.name)
assertEquals("Tirol", copy.bundesland)
}
}