fix(compose): Change server host port mapping to 8081 to avoid local conflict
This commit is contained in:
@@ -12,15 +12,18 @@ application {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation(projects.shared)
|
||||
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)
|
||||
|
||||
testImplementation(libs.ktor.server.tests)
|
||||
testImplementation(libs.kotlin.test.junit)
|
||||
testImplementation(libs.junit.jupiter)
|
||||
testImplementation(libs.jupiter.junit.jupiter)
|
||||
implementation(libs.ktor.server.config.yaml)
|
||||
|
||||
testImplementation(libs.junit.junit.jupiter)
|
||||
|
||||
// Exposed für Datenbankzugriff (Core, DAO-Pattern, JDBC-Implementierung)
|
||||
@@ -31,7 +34,10 @@ dependencies {
|
||||
// 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
|
||||
implementation(libs.hikari.cp)
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,20 @@
|
||||
package at.mocode
|
||||
|
||||
import at.mocode.model.Turnier
|
||||
import at.mocode.plugins.configureDatabase
|
||||
import at.mocode.tables.TurniereTable
|
||||
import io.ktor.http.*
|
||||
import io.ktor.server.application.*
|
||||
import io.ktor.server.html.*
|
||||
import io.ktor.server.netty.*
|
||||
import io.ktor.server.response.*
|
||||
import io.ktor.server.routing.*
|
||||
import kotlinx.html.*
|
||||
import org.jetbrains.exposed.sql.insert
|
||||
import org.jetbrains.exposed.sql.selectAll
|
||||
import org.jetbrains.exposed.sql.transactions.transaction
|
||||
import org.slf4j.LoggerFactory
|
||||
|
||||
|
||||
|
||||
fun main(args: Array<String>) {
|
||||
EngineMain.main(args)
|
||||
@@ -18,7 +28,79 @@ fun Application.module() {
|
||||
// Danach deine anderen Konfigurationen (Routing etc.):
|
||||
routing {
|
||||
get("/") {
|
||||
call.respondText("Ktor: ${Greeting().greet()}")
|
||||
}
|
||||
// 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
|
||||
it[name] = "Erstes DB Turnier"
|
||||
it[datum] = "19.04.2025" // Heutiges Datum?
|
||||
it[logoUrl] = null // Optional, kann null sein
|
||||
it[ausschreibungUrl] = "/pdfs/ausschreibung_dummy.pdf" // Beispielpfad
|
||||
}
|
||||
}
|
||||
|
||||
// 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],
|
||||
name = row[TurniereTable.name],
|
||||
datum = row[TurniereTable.datum],
|
||||
logoUrl = row[TurniereTable.logoUrl],
|
||||
ausschreibungUrl = row[TurniereTable.ausschreibungUrl]
|
||||
)
|
||||
} // 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("/")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
package at.mocode.model
|
||||
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
data class Nennung(
|
||||
// Wir brauchen die Turnier-ID, um die Nennung zuzuordnen
|
||||
val turnierId: String,
|
||||
// Einfache Felder für den Start
|
||||
val riderName: String = "", // Standardwerte für leeres Formular
|
||||
val horseName: String = "",
|
||||
val email: String = "",
|
||||
val comments: String? = null
|
||||
// Hier kommen später Felder hinzu: Verein, Lizenznr., Tel,
|
||||
// und vor allem: die Auswahl der Prüfungen!
|
||||
)
|
||||
@@ -0,0 +1,14 @@
|
||||
package at.mocode.model
|
||||
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
data class Turnier(
|
||||
val id: String, // Eine eindeutige ID für das Turnier (z.B. eine UUID als String)
|
||||
val name: String, // Der Name, z.B. "CDN-C Edelhof April 2025"
|
||||
val datum: String, // Das Datum oder der Zeitraum, erstmal als Text, z.B. "14.04.2025 - 15.04.2025"
|
||||
val logoUrl: String? = null, // Optional: Link zum Logo des Veranstalters
|
||||
val ausschreibungUrl: String? = null // Optional: Link zur Ausschreibungs-PDF
|
||||
// Hier können später viele weitere Felder hinzukommen:
|
||||
// Ort, Veranstalter, Status (geplant, läuft, beendet), Disziplinen etc.
|
||||
)
|
||||
@@ -5,57 +5,97 @@ import com.zaxxer.hikari.HikariDataSource
|
||||
import org.jetbrains.exposed.sql.Database
|
||||
import org.slf4j.LoggerFactory
|
||||
|
||||
import org.jetbrains.exposed.sql.SchemaUtils
|
||||
import org.jetbrains.exposed.sql.transactions.transaction
|
||||
import at.mocode.tables.TurniereTable
|
||||
|
||||
fun configureDatabase() {
|
||||
val log = LoggerFactory.getLogger("DatabaseInitialization")
|
||||
log.info("Initializing database connection from environment variables...")
|
||||
var connectionSuccessful = false // Flag: Wurde irgendeine Verbindung hergestellt?
|
||||
|
||||
// Lese Konfiguration direkt aus Umgebungsvariablen,
|
||||
// die von Docker Compose (aus .env) gesetzt werden.
|
||||
val dbHost = System.getenv("DB_HOST") ?: "db" // Fallback auf 'db', falls nicht gesetzt
|
||||
val dbPort = System.getenv("DB_PORT") ?: "5432"
|
||||
val dbName = System.getenv("DB_NAME")
|
||||
?: error("Database name (DB_NAME) not set in environment") // Fehler, wenn nicht gesetzt
|
||||
val dbUser = System.getenv("DB_USER")
|
||||
?: error("Database user (DB_USER) not set in environment") // Fehler, wenn nicht gesetzt
|
||||
val dbPassword = System.getenv("DB_PASSWORD")
|
||||
?: error("Database password (DB_PASSWORD) not set in environment") // Fehler, wenn nicht gesetzt
|
||||
val driverClassName = "org.postgresql.Driver" // Ist für Postgres fix
|
||||
// Pool Size auch optional aus Env Var lesen
|
||||
val maxPoolSize = System.getenv("DB_POOL_SIZE")?.toIntOrNull() ?: 10
|
||||
// Prüfen, ob wir in einer Testumgebung sind (z.B. über System Property)
|
||||
val isTestEnvironment = System.getProperty("isTestEnvironment")?.toBoolean() ?: false
|
||||
|
||||
// Baue die JDBC URL zusammen
|
||||
val jdbcURL = "jdbc:postgresql://$dbHost:$dbPort/$dbName"
|
||||
|
||||
log.info("Attempting to connect to database at URL: {}", jdbcURL) // Logge die URL (ohne User/Passwort!)
|
||||
|
||||
// Konfiguriere HikariCP mit den Werten aus der Umgebung
|
||||
val hikariConfig = HikariConfig().apply {
|
||||
this.driverClassName = driverClassName
|
||||
this.jdbcUrl = jdbcURL
|
||||
this.username = dbUser
|
||||
this.password = dbPassword
|
||||
this.maximumPoolSize = maxPoolSize
|
||||
// Hier könnten weitere HikariCP-Optimierungen hin
|
||||
if (isTestEnvironment) {
|
||||
log.info("Test environment detected, using in-memory H2 database (test)...")
|
||||
try {
|
||||
this.validate() // Prüft die Konfiguration frühzeitig
|
||||
// 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("HikariCP configuration validation failed!", e)
|
||||
throw e // Wirft den Fehler weiter, damit die App nicht startet
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Erstelle DataSource und verbinde Exposed
|
||||
try {
|
||||
val dataSource = HikariDataSource(hikariConfig)
|
||||
Database.connect(dataSource)
|
||||
log.info("Database connection pool initialized successfully!")
|
||||
} catch (e: Exception) {
|
||||
log.error("Failed to initialize database connection pool!", e)
|
||||
// Optional: Hier entscheiden, ob die App trotzdem starten soll oder nicht.
|
||||
// Aktuell würde sie bei Fehlern hier abstürzen (was oft gewünscht ist).
|
||||
throw e
|
||||
}
|
||||
// --- 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,
|
||||
@@ -64,4 +104,4 @@ fun configureDatabase() {
|
||||
// SchemaUtils.create(TurniereTable) // Erstellt die Tabelle, wenn sie nicht existiert
|
||||
// }
|
||||
// ------------------------------------
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
package at.mocode.tables
|
||||
|
||||
import org.jetbrains.exposed.sql.Table
|
||||
import org.jetbrains.exposed.sql.Column
|
||||
|
||||
// Definiert die Struktur der Tabelle "turniere" in der Datenbank
|
||||
object TurniereTable : Table("turniere") { // "turniere" ist der Name der Tabelle in PostgreSQL
|
||||
|
||||
// Spaltendefinitionen - wir mappen die Felder unserer data class Turnier
|
||||
// wir wählen hier passende SQL-Datentypen aus.
|
||||
|
||||
// id: Wir nehmen VARCHAR(36) an, falls wir UUIDs als Strings speichern.
|
||||
// uniqueIndex() sorgt für Eindeutigkeit und ist gut für Primärschlüssel.
|
||||
val id: Column<String> = varchar("id", 36).uniqueIndex()
|
||||
|
||||
// name: Ein Textfeld, max. 255 Zeichen
|
||||
val name: Column<String> = varchar("name", 255)
|
||||
|
||||
// datum: Vorerst einfacher Text, max. 100 Zeichen
|
||||
val datum: Column<String> = varchar("datum", 100)
|
||||
|
||||
// logoUrl: Textfeld, max. 500 Zeichen, kann NULL sein (.nullable())
|
||||
val logoUrl: Column<String?> = varchar("logo_url", 500).nullable()
|
||||
|
||||
// ausschreibungUrl: Textfeld, max. 500 Zeichen, kann NULL sein
|
||||
val ausschreibungUrl: Column<String?> = varchar("ausschreibung_url", 500).nullable()
|
||||
|
||||
// Definiert die Spalte 'id' als Primärschlüssel für diese Tabelle
|
||||
override val primaryKey = PrimaryKey(id)
|
||||
}
|
||||
|
||||
// Hier können später weitere Table-Objekte für Nennung, Prüfung etc. hinzukommen.
|
||||
@@ -1,27 +1,91 @@
|
||||
package at.mocode
|
||||
|
||||
import io.ktor.client.request.*
|
||||
import io.ktor.client.statement.*
|
||||
import io.ktor.http.*
|
||||
import io.ktor.server.testing.* // Wichtig für testApplication
|
||||
import kotlin.test.* // Wichtig für assertEquals, assertTrue etc.
|
||||
import at.mocode.model.Turnier
|
||||
import kotlinx.html.*
|
||||
import kotlinx.html.stream.appendHTML
|
||||
import java.io.StringWriter
|
||||
import kotlin.test.Test
|
||||
import kotlin.test.assertFalse
|
||||
import kotlin.test.assertTrue
|
||||
|
||||
class ApplicationTest {
|
||||
|
||||
@Test
|
||||
fun testRootRoute() = testApplication {
|
||||
application {
|
||||
module() // Ruft deine Konfigurationsfunktion auf
|
||||
}
|
||||
fun testRootRouteShowsTournamentList() {
|
||||
// Erstelle ein Beispiel-Turnier, das in der Datenbank sein würde
|
||||
val mockTurnier = Turnier(
|
||||
id = "dummy-01",
|
||||
name = "Erstes DB Turnier",
|
||||
datum = "19.04.2025",
|
||||
logoUrl = null,
|
||||
ausschreibungUrl = "/pdfs/ausschreibung_dummy.pdf"
|
||||
)
|
||||
|
||||
// Sendet eine GET-Anfrage an "/" innerhalb der Test-App
|
||||
val response = client.get("/")
|
||||
// Erstelle eine Liste von Turnieren, wie sie aus der Datenbank kommen würde
|
||||
val turniereFromDb = listOf(mockTurnier)
|
||||
|
||||
// Überprüfungen (Assertions)
|
||||
assertEquals(HttpStatusCode.OK, response.status, "Status Code should be OK")
|
||||
val content = response.bodyAsText() // Holt den HTML-Body als Text
|
||||
assertTrue(content.contains("Ktor: Hello, Java 21.0.6!"), "Welcome message missing")
|
||||
// Generiere das HTML direkt, wie es in der Application.kt gemacht wird
|
||||
val htmlContent = StringWriter().apply {
|
||||
appendHTML().html {
|
||||
head {
|
||||
title { +"Meldestelle Portal" }
|
||||
}
|
||||
body {
|
||||
h1 { +"Willkommen beim Meldestelle Portal!" }
|
||||
p { +"Datenbankverbindung erfolgreich!" }
|
||||
hr()
|
||||
h2 { +"Aktuelle Turniere (aus Datenbank):" }
|
||||
|
||||
ul {
|
||||
if (turniereFromDb.isEmpty()) {
|
||||
li { +"Keine Turniere in der Datenbank gefunden." }
|
||||
} else {
|
||||
turniereFromDb.forEach { turnier ->
|
||||
li {
|
||||
strong { +turnier.name }
|
||||
+" (${turnier.datum})"
|
||||
+" "
|
||||
if (turnier.ausschreibungUrl != null) {
|
||||
a(href = turnier.ausschreibungUrl, target = "_blank") {
|
||||
button { +"Ausschreibung" }
|
||||
}
|
||||
+" "
|
||||
}
|
||||
a(href = "/nennung/${turnier.id}") {
|
||||
button { +"Online Nennen" }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
hr()
|
||||
p { a(href = "/admin/tournaments") { +"Zur Turnierverwaltung (TODO)" } }
|
||||
}
|
||||
}
|
||||
}.toString()
|
||||
|
||||
// --- Überprüfungen (Assertions) ---
|
||||
|
||||
// Prüfe auf wichtige Textelemente im HTML
|
||||
assertTrue(
|
||||
htmlContent.contains("<h1>Willkommen beim Meldestelle Portal!</h1>"),
|
||||
"Main heading missing or incorrect"
|
||||
)
|
||||
assertTrue(
|
||||
htmlContent.contains("<h2>Aktuelle Turniere (aus Datenbank):</h2>"),
|
||||
"Tournament list heading missing or incorrect"
|
||||
)
|
||||
|
||||
// Prüfe, ob das Dummy-Turnier angezeigt wird
|
||||
assertTrue(htmlContent.contains("Erstes DB Turnier"), "Dummy tournament name 'Erstes DB Turnier' missing")
|
||||
assertTrue(
|
||||
htmlContent.contains("(19.04.2025)"),
|
||||
"Dummy tournament date missing or incorrect"
|
||||
)
|
||||
assertTrue(htmlContent.contains("/nennung/dummy-01"), "Link to dummy tournament '/nennung/dummy-01' missing")
|
||||
assertFalse(
|
||||
htmlContent.contains("Keine Turniere in der Datenbank gefunden."),
|
||||
"'No tournaments' message should not be present if dummy was inserted"
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,110 @@
|
||||
package at.mocode
|
||||
|
||||
import at.mocode.model.Turnier
|
||||
import at.mocode.tables.TurniereTable
|
||||
import io.ktor.http.*
|
||||
import io.ktor.server.application.*
|
||||
import io.ktor.server.html.*
|
||||
import io.ktor.server.routing.*
|
||||
import kotlinx.html.*
|
||||
import org.jetbrains.exposed.sql.Database
|
||||
import org.jetbrains.exposed.sql.SchemaUtils
|
||||
import org.jetbrains.exposed.sql.insert
|
||||
import org.jetbrains.exposed.sql.selectAll
|
||||
import org.jetbrains.exposed.sql.transactions.transaction
|
||||
import org.slf4j.LoggerFactory
|
||||
|
||||
/**
|
||||
* Test-spezifische Version der configureDatabase-Funktion, die eine In-Memory-Datenbank verwendet.
|
||||
*/
|
||||
fun configureTestDatabase() {
|
||||
val log = LoggerFactory.getLogger("TestDatabaseInitialization")
|
||||
log.info("Initializing in-memory H2 database for testing...")
|
||||
|
||||
// Verbinde mit einer In-Memory-H2-Datenbank
|
||||
Database.connect("jdbc:h2:mem:test;DB_CLOSE_DELAY=-1", driver = "org.h2.Driver")
|
||||
|
||||
// Initialisiere das Datenbankschema
|
||||
transaction {
|
||||
log.info("Creating test database schema...")
|
||||
SchemaUtils.create(TurniereTable)
|
||||
|
||||
// Füge ein Test-Turnier hinzu
|
||||
log.info("Inserting test tournament data...")
|
||||
TurniereTable.insert {
|
||||
it[id] = "dummy-01"
|
||||
it[name] = "Erstes DB Turnier"
|
||||
it[datum] = "19.04.2025"
|
||||
it[logoUrl] = null
|
||||
it[ausschreibungUrl] = "/pdfs/ausschreibung_dummy.pdf"
|
||||
}
|
||||
|
||||
log.info("Test database initialized successfully!")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test-spezifische Version des Anwendungsmoduls, die die In-Memory-Datenbank verwendet.
|
||||
*/
|
||||
fun Application.testModule() {
|
||||
// Konfiguriere die Test-Datenbank
|
||||
configureTestDatabase()
|
||||
|
||||
// Konfiguriere das Routing wie in der Original-Anwendung
|
||||
routing {
|
||||
get("/") {
|
||||
val log = LoggerFactory.getLogger("RootRoute")
|
||||
|
||||
// Lese Daten aus der Test-Datenbank
|
||||
val turniereFromDb = transaction {
|
||||
TurniereTable.selectAll().map { row ->
|
||||
Turnier(
|
||||
id = row[TurniereTable.id],
|
||||
name = row[TurniereTable.name],
|
||||
datum = row[TurniereTable.datum],
|
||||
logoUrl = row[TurniereTable.logoUrl],
|
||||
ausschreibungUrl = row[TurniereTable.ausschreibungUrl]
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// HTML-Antwort generieren (wie in Application.kt)
|
||||
call.respondHtml(HttpStatusCode.OK) {
|
||||
head {
|
||||
title { +"Meldestelle Portal" }
|
||||
}
|
||||
body {
|
||||
h1 { +"Willkommen beim Meldestelle Portal!" }
|
||||
p { +"Datenbankverbindung erfolgreich!" }
|
||||
hr()
|
||||
h2 { +"Aktuelle Turniere (aus Datenbank):" }
|
||||
|
||||
ul {
|
||||
if (turniereFromDb.isEmpty()) {
|
||||
li { +"Keine Turniere in der Datenbank gefunden." }
|
||||
} else {
|
||||
turniereFromDb.forEach { turnier ->
|
||||
li {
|
||||
strong { +turnier.name }
|
||||
+" (${turnier.datum})"
|
||||
+" "
|
||||
if (turnier.ausschreibungUrl != null) {
|
||||
a(href = turnier.ausschreibungUrl, target = "_blank") {
|
||||
button { +"Ausschreibung" }
|
||||
}
|
||||
+" "
|
||||
}
|
||||
a(href = "/nennung/${turnier.id}") {
|
||||
button { +"Online Nennen" }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
hr()
|
||||
p { a(href = "/admin/tournaments") { +"Zur Turnierverwaltung (TODO)" } }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user