From 2ad447c9783885c216e61e82713ccdb4461ba3d7 Mon Sep 17 00:00:00 2001 From: StefanMoCoAt Date: Sun, 29 Jun 2025 23:34:04 +0200 Subject: [PATCH] (fix) Server-Modul --- server/build.gradle.kts | 4 +- .../at/mocode/{server => }/Application.kt | 41 +---- .../src/main/kotlin/at/mocode/db/mapping.kt | 2 + .../src/main/kotlin/at/mocode/model/Event.kt | 2 + .../kotlin/at/mocode/model/EventRepository.kt | 4 + .../mocode/model/PostgresEventRepository.kt | 4 + .../mocode/{server => }/plugins/Database.kt | 19 +- .../main/kotlin/at/mocode/plugins/Routing.kt | 32 ++++ .../kotlin/at/mocode/plugins/Serialization.kt | 2 + .../{server => }/tables/ArtikelTable.kt | 2 +- .../{server => }/tables/LizenzenTable.kt | 11 +- .../{server => }/tables/PersonenTable.kt | 6 +- .../mocode/{server => }/tables/PferdeTable.kt | 6 +- .../{server => }/tables/PlaetzeTable.kt | 6 +- .../{server => }/tables/TurniereTable.kt | 2 +- .../tables/VeranstaltungenTable.kt | 8 +- .../{server => }/tables/VereineTable.kt | 2 +- server/src/main/resources/application.yaml | 15 +- server/src/main/resources/static/index.html | 10 ++ .../test/kotlin/at/mocode/ApplicationTest.kt | 166 ++++++++++++++++++ .../src/test/kotlin/at/mocode/SimpleTest.kt | 10 ++ shared/build.gradle.kts | 9 - .../kotlin/at/mocode/model/Turnier.kt | 41 +++++ .../kotlin/at/mocode/shared/enums/Enums.kt | 4 +- .../kotlin/at/mocode/shared/model/Turnier.kt | 3 +- .../at/mocode/shared/stammdaten/LizenzInfo.kt | 4 +- .../at/mocode/shared/stammdaten/Pferd.kt | 4 +- 27 files changed, 321 insertions(+), 98 deletions(-) rename server/src/main/kotlin/at/mocode/{server => }/Application.kt (73%) create mode 100644 server/src/main/kotlin/at/mocode/db/mapping.kt create mode 100644 server/src/main/kotlin/at/mocode/model/Event.kt create mode 100644 server/src/main/kotlin/at/mocode/model/EventRepository.kt create mode 100644 server/src/main/kotlin/at/mocode/model/PostgresEventRepository.kt rename server/src/main/kotlin/at/mocode/{server => }/plugins/Database.kt (92%) create mode 100644 server/src/main/kotlin/at/mocode/plugins/Routing.kt create mode 100644 server/src/main/kotlin/at/mocode/plugins/Serialization.kt rename server/src/main/kotlin/at/mocode/{server => }/tables/ArtikelTable.kt (94%) rename server/src/main/kotlin/at/mocode/{server => }/tables/LizenzenTable.kt (74%) rename server/src/main/kotlin/at/mocode/{server => }/tables/PersonenTable.kt (94%) rename server/src/main/kotlin/at/mocode/{server => }/tables/PferdeTable.kt (93%) rename server/src/main/kotlin/at/mocode/{server => }/tables/PlaetzeTable.kt (77%) rename server/src/main/kotlin/at/mocode/{server => }/tables/TurniereTable.kt (98%) rename server/src/main/kotlin/at/mocode/{server => }/tables/VeranstaltungenTable.kt (92%) rename server/src/main/kotlin/at/mocode/{server => }/tables/VereineTable.kt (96%) create mode 100644 server/src/main/resources/static/index.html create mode 100644 server/src/test/kotlin/at/mocode/ApplicationTest.kt create mode 100644 server/src/test/kotlin/at/mocode/SimpleTest.kt create mode 100644 shared/src/commonMain/kotlin/at/mocode/model/Turnier.kt diff --git a/server/build.gradle.kts b/server/build.gradle.kts index f4e53e52..f2446801 100644 --- a/server/build.gradle.kts +++ b/server/build.gradle.kts @@ -22,7 +22,7 @@ tasks.withType { // Configure application application { - mainClass.set("at.mocode.server.ApplicationKt") + mainClass.set("at.mocode.ApplicationKt") applicationDefaultJvmArgs = listOf( "-Dio.ktor.development=${extra["io.ktor.development"] ?: "false"}", "-XX:+UseG1GC", // Use G1 Garbage Collector @@ -75,7 +75,7 @@ dependencies { // Testing testImplementation(libs.ktor.server.tests) - testImplementation(libs.kotlin.test.junit) + testImplementation(libs.kotlin.test) testImplementation(libs.junitJupiter) } diff --git a/server/src/main/kotlin/at/mocode/server/Application.kt b/server/src/main/kotlin/at/mocode/Application.kt similarity index 73% rename from server/src/main/kotlin/at/mocode/server/Application.kt rename to server/src/main/kotlin/at/mocode/Application.kt index 2236331b..283e50d0 100644 --- a/server/src/main/kotlin/at/mocode/server/Application.kt +++ b/server/src/main/kotlin/at/mocode/Application.kt @@ -1,6 +1,7 @@ -package at.mocode.server +package at.mocode -import at.mocode.server.plugins.configureDatabase +import at.mocode.plugins.configureDatabase +import at.mocode.plugins.configureRouting import io.ktor.http.* import io.ktor.serialization.kotlinx.json.* import io.ktor.server.application.* @@ -11,7 +12,6 @@ import io.ktor.server.plugins.cors.routing.* import io.ktor.server.plugins.defaultheaders.* import io.ktor.server.plugins.statuspages.* import io.ktor.server.response.* -import io.ktor.server.routing.* import kotlinx.serialization.json.Json import org.slf4j.LoggerFactory import org.slf4j.event.Level @@ -22,18 +22,10 @@ fun main(args: Array) { 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") } @@ -93,7 +85,7 @@ private fun Application.configurePlugins() { } } } catch (e: Exception) { - // Log the error but continue with default configuration + // Log the error but continue with the default configuration this@configurePlugins.log.warn("Failed to configure CORS from config, using defaults: ${e.message}") } } @@ -115,28 +107,3 @@ private fun Application.configurePlugins() { } } } - -/** - * 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() - } -} diff --git a/server/src/main/kotlin/at/mocode/db/mapping.kt b/server/src/main/kotlin/at/mocode/db/mapping.kt new file mode 100644 index 00000000..c8669823 --- /dev/null +++ b/server/src/main/kotlin/at/mocode/db/mapping.kt @@ -0,0 +1,2 @@ +package at.mocode.db + diff --git a/server/src/main/kotlin/at/mocode/model/Event.kt b/server/src/main/kotlin/at/mocode/model/Event.kt new file mode 100644 index 00000000..e1ac1a35 --- /dev/null +++ b/server/src/main/kotlin/at/mocode/model/Event.kt @@ -0,0 +1,2 @@ +package at.mocode.model + diff --git a/server/src/main/kotlin/at/mocode/model/EventRepository.kt b/server/src/main/kotlin/at/mocode/model/EventRepository.kt new file mode 100644 index 00000000..7b835e1b --- /dev/null +++ b/server/src/main/kotlin/at/mocode/model/EventRepository.kt @@ -0,0 +1,4 @@ +package at.mocode.model + +interface EventRepository { +} diff --git a/server/src/main/kotlin/at/mocode/model/PostgresEventRepository.kt b/server/src/main/kotlin/at/mocode/model/PostgresEventRepository.kt new file mode 100644 index 00000000..825c37ea --- /dev/null +++ b/server/src/main/kotlin/at/mocode/model/PostgresEventRepository.kt @@ -0,0 +1,4 @@ +package at.mocode.model + +class PostgresEventRepository { +} diff --git a/server/src/main/kotlin/at/mocode/server/plugins/Database.kt b/server/src/main/kotlin/at/mocode/plugins/Database.kt similarity index 92% rename from server/src/main/kotlin/at/mocode/server/plugins/Database.kt rename to server/src/main/kotlin/at/mocode/plugins/Database.kt index 5524765f..d4d7e654 100644 --- a/server/src/main/kotlin/at/mocode/server/plugins/Database.kt +++ b/server/src/main/kotlin/at/mocode/plugins/Database.kt @@ -1,6 +1,5 @@ -package at.mocode.server.plugins +package at.mocode.plugins -import at.mocode.server.tables.* import com.zaxxer.hikari.HikariConfig import com.zaxxer.hikari.HikariDataSource import io.ktor.server.application.* @@ -175,14 +174,14 @@ private fun initializeSchema(log: Logger, isTestEnvironment: Boolean, isIdeaEnvi try { // Create all tables if they don't exist SchemaUtils.create( - VereineTable, - PersonenTable, - PferdeTable, - VeranstaltungenTable, - TurniereTable, - ArtikelTable, - PlaetzeTable, - LizenzenTable + _root_ide_package_.at.mocode.tables.VereineTable, + _root_ide_package_.at.mocode.tables.PersonenTable, + _root_ide_package_.at.mocode.tables.PferdeTable, + _root_ide_package_.at.mocode.tables.VeranstaltungenTable, + _root_ide_package_.at.mocode.tables.TurniereTable, + _root_ide_package_.at.mocode.tables.ArtikelTable, + _root_ide_package_.at.mocode.tables.PlaetzeTable, + _root_ide_package_.at.mocode.tables.LizenzenTable // Add more tables here if needed ) log.info("Database schema initialized successfully.") diff --git a/server/src/main/kotlin/at/mocode/plugins/Routing.kt b/server/src/main/kotlin/at/mocode/plugins/Routing.kt new file mode 100644 index 00000000..e0fb11a0 --- /dev/null +++ b/server/src/main/kotlin/at/mocode/plugins/Routing.kt @@ -0,0 +1,32 @@ +package at.mocode.plugins + +import io.ktor.server.application.Application +import io.ktor.server.response.respondText +import io.ktor.server.routing.application +import io.ktor.server.routing.get +import io.ktor.server.routing.routing + +/** + * Configures all routes for the application + */ +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() + } +} diff --git a/server/src/main/kotlin/at/mocode/plugins/Serialization.kt b/server/src/main/kotlin/at/mocode/plugins/Serialization.kt new file mode 100644 index 00000000..65541218 --- /dev/null +++ b/server/src/main/kotlin/at/mocode/plugins/Serialization.kt @@ -0,0 +1,2 @@ +package at.mocode.plugins + diff --git a/server/src/main/kotlin/at/mocode/server/tables/ArtikelTable.kt b/server/src/main/kotlin/at/mocode/tables/ArtikelTable.kt similarity index 94% rename from server/src/main/kotlin/at/mocode/server/tables/ArtikelTable.kt rename to server/src/main/kotlin/at/mocode/tables/ArtikelTable.kt index 5d9013dd..ad264718 100644 --- a/server/src/main/kotlin/at/mocode/server/tables/ArtikelTable.kt +++ b/server/src/main/kotlin/at/mocode/tables/ArtikelTable.kt @@ -1,4 +1,4 @@ -package at.mocode.server.tables +package at.mocode.tables import org.jetbrains.exposed.sql.Table import org.jetbrains.exposed.sql.kotlin.datetime.timestamp diff --git a/server/src/main/kotlin/at/mocode/server/tables/LizenzenTable.kt b/server/src/main/kotlin/at/mocode/tables/LizenzenTable.kt similarity index 74% rename from server/src/main/kotlin/at/mocode/server/tables/LizenzenTable.kt rename to server/src/main/kotlin/at/mocode/tables/LizenzenTable.kt index f1cd9aab..6a605c0a 100644 --- a/server/src/main/kotlin/at/mocode/server/tables/LizenzenTable.kt +++ b/server/src/main/kotlin/at/mocode/tables/LizenzenTable.kt @@ -1,16 +1,17 @@ -package at.mocode.server.tables +package at.mocode.tables -import at.mocode.shared.model.enums.LizenzTyp -import at.mocode.shared.model.enums.Sparte + +import at.mocode.shared.enums.LizenzTypE +import at.mocode.shared.enums.SparteE import org.jetbrains.exposed.sql.Table import org.jetbrains.exposed.sql.kotlin.datetime.date object LizenzenTable : Table("lizenzen") { val id = uuid("id") val personId = uuid("person_id").references(PersonenTable.id) - val lizenzTyp = enumerationByName("lizenz_typ", 50, LizenzTyp::class) + val lizenzTyp = enumerationByName("lizenz_typ", 50, LizenzTypE::class) val stufe = varchar("stufe", 20).nullable() - val sparte = enumerationByName("sparte", 50, Sparte::class).nullable() + val sparte = enumerationByName("sparte", 50, SparteE::class).nullable() val gueltigBisJahr = integer("gueltig_bis_jahr").nullable() val ausgestelltAm = date("ausgestellt_am").nullable() diff --git a/server/src/main/kotlin/at/mocode/server/tables/PersonenTable.kt b/server/src/main/kotlin/at/mocode/tables/PersonenTable.kt similarity index 94% rename from server/src/main/kotlin/at/mocode/server/tables/PersonenTable.kt rename to server/src/main/kotlin/at/mocode/tables/PersonenTable.kt index e627de21..a0ea8f58 100644 --- a/server/src/main/kotlin/at/mocode/server/tables/PersonenTable.kt +++ b/server/src/main/kotlin/at/mocode/tables/PersonenTable.kt @@ -1,6 +1,6 @@ -package at.mocode.server.tables +package at.mocode.tables -import at.mocode.shared.model.enums.Geschlecht +import at.mocode.shared.enums.GeschlechtE import org.jetbrains.exposed.sql.Table import org.jetbrains.exposed.sql.kotlin.datetime.date import org.jetbrains.exposed.sql.kotlin.datetime.timestamp @@ -12,7 +12,7 @@ object PersonenTable : Table("personen") { val vorname = varchar("vorname", 100) val titel = varchar("titel", 50).nullable() val geburtsdatum = date("geburtsdatum").nullable() - val geschlecht = enumerationByName("geschlecht", 10, Geschlecht::class).nullable() + val geschlecht = enumerationByName("geschlecht", 10, GeschlechtE::class).nullable() val nationalitaet = varchar("nationalitaet", 3).nullable() val email = varchar("email", 255).nullable() val telefon = varchar("telefon", 50).nullable() diff --git a/server/src/main/kotlin/at/mocode/server/tables/PferdeTable.kt b/server/src/main/kotlin/at/mocode/tables/PferdeTable.kt similarity index 93% rename from server/src/main/kotlin/at/mocode/server/tables/PferdeTable.kt rename to server/src/main/kotlin/at/mocode/tables/PferdeTable.kt index d40d6f56..a7894a27 100644 --- a/server/src/main/kotlin/at/mocode/server/tables/PferdeTable.kt +++ b/server/src/main/kotlin/at/mocode/tables/PferdeTable.kt @@ -1,6 +1,6 @@ -package at.mocode.server.tables +package at.mocode.tables -import at.mocode.shared.model.enums.GeschlechtPferd +import at.mocode.shared.enums.GeschlechtPferdE import org.jetbrains.exposed.sql.Table import org.jetbrains.exposed.sql.kotlin.datetime.timestamp @@ -11,7 +11,7 @@ object PferdeTable : Table("pferde") { val name = varchar("name", 255) val lebensnummer = varchar("lebensnummer", 20).nullable() val feiPassNr = varchar("fei_pass_nr", 20).nullable() - val geschlecht = enumerationByName("geschlecht", 10, GeschlechtPferd::class).nullable() + val geschlecht = enumerationByName("geschlecht", 10, GeschlechtPferdE::class).nullable() val geburtsjahr = integer("geburtsjahr").nullable() val rasse = varchar("rasse", 100).nullable() val farbe = varchar("farbe", 50).nullable() diff --git a/server/src/main/kotlin/at/mocode/server/tables/PlaetzeTable.kt b/server/src/main/kotlin/at/mocode/tables/PlaetzeTable.kt similarity index 77% rename from server/src/main/kotlin/at/mocode/server/tables/PlaetzeTable.kt rename to server/src/main/kotlin/at/mocode/tables/PlaetzeTable.kt index ae322438..5b8df50c 100644 --- a/server/src/main/kotlin/at/mocode/server/tables/PlaetzeTable.kt +++ b/server/src/main/kotlin/at/mocode/tables/PlaetzeTable.kt @@ -1,6 +1,6 @@ -package at.mocode.server.tables +package at.mocode.tables -import at.mocode.shared.model.enums.PlatzTyp +import at.mocode.shared.enums.PlatzTypE import org.jetbrains.exposed.sql.Table object PlaetzeTable : Table("plaetze") { @@ -9,7 +9,7 @@ object PlaetzeTable : Table("plaetze") { val name = varchar("name", 100) val dimension = varchar("dimension", 50).nullable() val boden = varchar("boden", 100).nullable() - val typ = enumerationByName("typ", 20, PlatzTyp::class) + val typ = enumerationByName("typ", 20, PlatzTypE::class) override val primaryKey = PrimaryKey(id) diff --git a/server/src/main/kotlin/at/mocode/server/tables/TurniereTable.kt b/server/src/main/kotlin/at/mocode/tables/TurniereTable.kt similarity index 98% rename from server/src/main/kotlin/at/mocode/server/tables/TurniereTable.kt rename to server/src/main/kotlin/at/mocode/tables/TurniereTable.kt index b583dfa4..bd8e19b0 100644 --- a/server/src/main/kotlin/at/mocode/server/tables/TurniereTable.kt +++ b/server/src/main/kotlin/at/mocode/tables/TurniereTable.kt @@ -1,4 +1,4 @@ -package at.mocode.server.tables +package at.mocode.tables import org.jetbrains.exposed.sql.Table import org.jetbrains.exposed.sql.kotlin.datetime.date // Für kotlinx-datetime LocalDate diff --git a/server/src/main/kotlin/at/mocode/server/tables/VeranstaltungenTable.kt b/server/src/main/kotlin/at/mocode/tables/VeranstaltungenTable.kt similarity index 92% rename from server/src/main/kotlin/at/mocode/server/tables/VeranstaltungenTable.kt rename to server/src/main/kotlin/at/mocode/tables/VeranstaltungenTable.kt index 4501a5c4..6c04fecc 100644 --- a/server/src/main/kotlin/at/mocode/server/tables/VeranstaltungenTable.kt +++ b/server/src/main/kotlin/at/mocode/tables/VeranstaltungenTable.kt @@ -1,6 +1,6 @@ -package at.mocode.server.tables +package at.mocode.tables -import at.mocode.shared.model.enums.VeranstalterTyp +import at.mocode.shared.enums.VeranstalterTypE import org.jetbrains.exposed.sql.Table import org.jetbrains.exposed.sql.kotlin.datetime.date import org.jetbrains.exposed.sql.kotlin.datetime.timestamp @@ -13,8 +13,8 @@ object VeranstaltungenTable : Table("veranstaltungen") { 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 + enumerationByName("veranstalter_typ", 20, VeranstalterTypE::class).default( + VeranstalterTypE.UNBEKANNT ) val veranstaltungsortName = varchar("veranstaltungsort_name", 255) val veranstaltungsortAdresse = varchar("veranstaltungsort_adresse", 500) diff --git a/server/src/main/kotlin/at/mocode/server/tables/VereineTable.kt b/server/src/main/kotlin/at/mocode/tables/VereineTable.kt similarity index 96% rename from server/src/main/kotlin/at/mocode/server/tables/VereineTable.kt rename to server/src/main/kotlin/at/mocode/tables/VereineTable.kt index 1e0f66a8..69702029 100644 --- a/server/src/main/kotlin/at/mocode/server/tables/VereineTable.kt +++ b/server/src/main/kotlin/at/mocode/tables/VereineTable.kt @@ -1,4 +1,4 @@ -package at.mocode.server.tables +package at.mocode.tables import org.jetbrains.exposed.sql.Table import org.jetbrains.exposed.sql.kotlin.datetime.timestamp diff --git a/server/src/main/resources/application.yaml b/server/src/main/resources/application.yaml index 2b913604..6ffecd3c 100644 --- a/server/src/main/resources/application.yaml +++ b/server/src/main/resources/application.yaml @@ -2,7 +2,7 @@ ktor: deployment: # Server port configuration - port: 8081 + port: 8080 # Connection timeout in seconds connectionTimeout: 30 # Maximum number of concurrent connections @@ -13,7 +13,7 @@ ktor: - resources application: modules: - - at.mocode.server.ApplicationKt.module + - at.mocode.ApplicationKt.module # Database Configuration database: @@ -40,8 +40,8 @@ security: 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" + # Secret should be set via an environment variable in production + secret: "${JWT_SECRET:dev-secret-key-change-in-production}" # Token validity duration in milliseconds (24 hours) validity: 86400000 @@ -52,13 +52,6 @@ cors: - "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 diff --git a/server/src/main/resources/static/index.html b/server/src/main/resources/static/index.html new file mode 100644 index 00000000..e149a394 --- /dev/null +++ b/server/src/main/resources/static/index.html @@ -0,0 +1,10 @@ + + + + + Title + + + + + diff --git a/server/src/test/kotlin/at/mocode/ApplicationTest.kt b/server/src/test/kotlin/at/mocode/ApplicationTest.kt new file mode 100644 index 00000000..511ea449 --- /dev/null +++ b/server/src/test/kotlin/at/mocode/ApplicationTest.kt @@ -0,0 +1,166 @@ +package at.mocode + +import at.mocode.plugins.configureRouting +import io.ktor.client.request.* +import io.ktor.client.statement.* +import io.ktor.http.* +import io.ktor.serialization.kotlinx.json.* +import io.ktor.server.application.* +import io.ktor.server.plugins.contentnegotiation.* +import io.ktor.server.plugins.cors.routing.* +import io.ktor.server.plugins.defaultheaders.* +import io.ktor.server.response.* +import io.ktor.server.routing.* +import io.ktor.server.testing.* +import kotlinx.serialization.json.Json +import kotlin.test.* + +/** + * Comprehensive test suite for the Meldestelle Server Application. + * + * This test class verifies: + * - Application startup and initialization + * - Core routing functionality (health check, root endpoint) + * - Plugin configuration (CORS, content negotiation, default headers) + * - Error handling + * - Basic HTTP functionality + */ +class ApplicationTest { + + companion object { + init { + // Set test environment property for database configuration + // This ensures the application uses H2 in-memory database for testing + System.setProperty("isTestEnvironment", "true") + } + } + + @Test + fun testApplicationStartup() = testApplication { + application { + module() + } + // Test that the application starts without errors + // This test passes if no exceptions are thrown during module initialization + } + + @Test + fun testHealthEndpoint() = testApplication { + application { + module() + } + client.get("/health").apply { + assertEquals(HttpStatusCode.OK, status) + assertEquals("OK", bodyAsText()) + } + } + + @Test + fun testRootEndpoint() = testApplication { + application { + module() + } + client.get("/").apply { + assertEquals(HttpStatusCode.OK, status) + val responseText = bodyAsText() + // The response format is: "Meldestelle API Server v1.0.0 - Running in development mode" + assertTrue(responseText.contains("Meldestelle API Server"), "Response should contain 'Meldestelle API Server', but was: $responseText") + assertTrue(responseText.contains("v1.0.0"), "Response should contain 'v1.0.0', but was: $responseText") + assertTrue(responseText.contains("development"), "Response should contain 'development', but was: $responseText") + } + } + + @Test + fun testNotFoundEndpoint() = testApplication { + application { + module() + } + client.get("/nonexistent").apply { + assertEquals(HttpStatusCode.NotFound, status) + val responseText = bodyAsText() + assertTrue(responseText.contains("404: Page Not Found")) + } + } + + @Test + fun testDefaultHeaders() = testApplication { + application { + module() + } + client.get("/health").apply { + assertEquals(HttpStatusCode.OK, status) + // Check that default headers are set + assertEquals("Ktor", headers["X-Engine"]) + assertEquals("nosniff", headers["X-Content-Type-Options"]) + } + } + + @Test + fun testCorsConfiguration() = testApplication { + application { + module() + } + client.get("/health") { + header(HttpHeaders.Origin, "http://localhost:3000") + }.apply { + assertEquals(HttpStatusCode.OK, status) + // Check that CORS headers are present + assertNotNull(headers[HttpHeaders.AccessControlAllowOrigin]) + } + } + + @Test + fun testContentNegotiation() = testApplication { + application { + module() + } + client.get("/health") { + header(HttpHeaders.Accept, "application/json") + }.apply { + assertEquals(HttpStatusCode.OK, status) + // The response should still be text for the health endpoint + assertEquals("OK", bodyAsText()) + } + } + + @Test + fun testOptionsRequest() = testApplication { + application { + module() + } + client.options("/health") { + header(HttpHeaders.Origin, "http://localhost:3000") + header(HttpHeaders.AccessControlRequestMethod, "GET") + }.apply { + // OPTIONS requests should be handled by CORS + assertTrue(status.isSuccess() || status == HttpStatusCode.NotFound) + } + } + + @Test + fun testBasicRoutingWithoutDatabase() = testApplication { + application { + // Test routing functionality without full application module + // This isolates routing from database dependencies + install(DefaultHeaders) { + header("X-Engine", "Ktor") + header("X-Content-Type-Options", "nosniff") + } + + install(ContentNegotiation) { + json(Json { + prettyPrint = true + isLenient = true + ignoreUnknownKeys = true + }) + } + + configureRouting() + } + + client.get("/health").apply { + assertEquals(HttpStatusCode.OK, status) + assertEquals("OK", bodyAsText()) + } + } +} diff --git a/server/src/test/kotlin/at/mocode/SimpleTest.kt b/server/src/test/kotlin/at/mocode/SimpleTest.kt new file mode 100644 index 00000000..a693ab96 --- /dev/null +++ b/server/src/test/kotlin/at/mocode/SimpleTest.kt @@ -0,0 +1,10 @@ +package at.mocode + +import kotlin.test.* + +class SimpleTest { + @Test + fun testBasic() { + assertEquals(1, 1) + } +} diff --git a/shared/build.gradle.kts b/shared/build.gradle.kts index 0368520b..44159364 100644 --- a/shared/build.gradle.kts +++ b/shared/build.gradle.kts @@ -51,14 +51,5 @@ kotlin { implementation(kotlin("test")) } } - -// val jvmMain by getting { -// dependsOn(commonMain) -// } -// -// val wasmJsMain by getting { -// dependsOn(commonMain) -// } - } } diff --git a/shared/src/commonMain/kotlin/at/mocode/model/Turnier.kt b/shared/src/commonMain/kotlin/at/mocode/model/Turnier.kt new file mode 100644 index 00000000..2adc8ea6 --- /dev/null +++ b/shared/src/commonMain/kotlin/at/mocode/model/Turnier.kt @@ -0,0 +1,41 @@ +package at.mocode.model + +import kotlinx.serialization.Serializable + +/** + * Represents a tournament (Turnier) with its details and associated competitions (Bewerbe). + * Each tournament can have one or more competitions. + */ +@Serializable +data class Turnier( + /** The name of the tournament, e.g. "CSN-C NEU CSNP-C NEU NEUMARKT/M., OÖ" */ + val name: String, + + /** The date of the tournament as a formatted string, e.g. "7.JUNI 2025" */ + val datum: String, + + /** Unique identifier for the tournament */ + val number: Int, + + /** List of competitions (Bewerbe) associated with this tournament */ + var bewerbe: List +) + +/** + * Represents a competition (Bewerb) within a tournament. + * A competition has specific details like number, title, class, and optional task. + */ +@Serializable +data class Bewerb( + /** Competition number, e.g. 1, 2, etc. */ + val nummer: Int, + + /** Title of the competition, e.g. "Stilspringprüfung" or "Dressurprüfung" */ + val titel: String, + + /** Class/level of the competition, e.g. "60 cm" or "Kl. A" */ + val klasse: String, + + /** Optional task identifier, e.g. "DRA 1" */ + val task: String? +) diff --git a/shared/src/commonMain/kotlin/at/mocode/shared/enums/Enums.kt b/shared/src/commonMain/kotlin/at/mocode/shared/enums/Enums.kt index 4f7b5875..b5eff7d8 100644 --- a/shared/src/commonMain/kotlin/at/mocode/shared/enums/Enums.kt +++ b/shared/src/commonMain/kotlin/at/mocode/shared/enums/Enums.kt @@ -18,7 +18,7 @@ enum class CupSerieTypE { CUP_SERIE } @Serializable enum class LizenzKategorieE { REITERLIZENZ, FAHRERLIZENZ, STARTKARTE } @Serializable -enum class LizenzTyp { REITER, FAHRER, VOLTIGIERER, WESTERN, WORKING_EQUITATION, POLO, STARTKARTE_ALLG, STARTKARTE_VOLTIGIEREN, STARTKARTE_WESTERN, STARTKARTE_ISLAND, STARTKARTE_FAHREN_JUGEND, STARTKARTE_HORSEBALL, STARTKARTE_POLO, PARAEQUESTRIAN, SONSTIGE } +enum class LizenzTypE { REITER, FAHRER, VOLTIGIERER, WESTERN, WORKING_EQUITATION, POLO, STARTKARTE_ALLG, STARTKARTE_VOLTIGIEREN, STARTKARTE_WESTERN, STARTKARTE_ISLAND, STARTKARTE_FAHREN_JUGEND, STARTKARTE_HORSEBALL, STARTKARTE_POLO, PARAEQUESTRIAN, SONSTIGE } @Serializable @@ -70,4 +70,4 @@ enum class RichterPositionE { C, E, H, M, B, VORSITZ, SEITENRICHTER, SONSTIGE } @Serializable enum class GeschlechtE { M, W, D, UNBEKANNT } @Serializable -enum class GeschlechtPferd { HENGST, STUTE, WALLACH, UNBEKANNT } +enum class GeschlechtPferdE { HENGST, STUTE, WALLACH, UNBEKANNT } diff --git a/shared/src/commonMain/kotlin/at/mocode/shared/model/Turnier.kt b/shared/src/commonMain/kotlin/at/mocode/shared/model/Turnier.kt index 6163d847..f4272ffd 100644 --- a/shared/src/commonMain/kotlin/at/mocode/shared/model/Turnier.kt +++ b/shared/src/commonMain/kotlin/at/mocode/shared/model/Turnier.kt @@ -1,7 +1,6 @@ package at.mocode.shared.model -import at.mocode.shared.model.Artikel -import at.mocode.shared.enums.NennungsArt +import at.mocode.shared.enums.NennungsArtE import at.mocode.shared.serializers.BigDecimalSerializer import at.mocode.shared.serializers.KotlinInstantSerializer import at.mocode.shared.serializers.KotlinLocalDateSerializer diff --git a/shared/src/commonMain/kotlin/at/mocode/shared/stammdaten/LizenzInfo.kt b/shared/src/commonMain/kotlin/at/mocode/shared/stammdaten/LizenzInfo.kt index 086f69cf..1e656929 100644 --- a/shared/src/commonMain/kotlin/at/mocode/shared/stammdaten/LizenzInfo.kt +++ b/shared/src/commonMain/kotlin/at/mocode/shared/stammdaten/LizenzInfo.kt @@ -1,6 +1,6 @@ package at.mocode.shared.stammdaten -import at.mocode.shared.enums.LizenzTyp +import at.mocode.shared.enums.LizenzTypE import at.mocode.shared.enums.SparteE import at.mocode.shared.serializers.KotlinLocalDateSerializer import kotlinx.datetime.LocalDate @@ -8,7 +8,7 @@ import kotlinx.serialization.Serializable @Serializable data class LizenzInfo( - val lizenzTyp: LizenzTyp, + val lizenzTyp: LizenzTypE, val stufe: String?, val sparteE: SparteE?, val gueltigBisJahr: Int?, // Jahr als Int diff --git a/shared/src/commonMain/kotlin/at/mocode/shared/stammdaten/Pferd.kt b/shared/src/commonMain/kotlin/at/mocode/shared/stammdaten/Pferd.kt index 9673e788..2d06c532 100644 --- a/shared/src/commonMain/kotlin/at/mocode/shared/stammdaten/Pferd.kt +++ b/shared/src/commonMain/kotlin/at/mocode/shared/stammdaten/Pferd.kt @@ -1,6 +1,6 @@ package at.mocode.shared.stammdaten -import at.mocode.shared.enums.GeschlechtPferd +import at.mocode.shared.enums.GeschlechtPferdE import at.mocode.shared.serializers.KotlinInstantSerializer import at.mocode.shared.serializers.UuidSerializer import com.benasher44.uuid.Uuid @@ -18,7 +18,7 @@ data class Pferd( var name: String, var lebensnummer: String?, var feiPassNr: String?, - var geschlecht: GeschlechtPferd?, + var geschlecht: GeschlechtPferdE?, var geburtsjahr: Int?, var rasse: String?, var farbe: String?,