feat(Tracer Bullet)
This commit is contained in:
@@ -1,44 +1,70 @@
|
||||
// Dieses Modul stellt gemeinsame technische Hilfsfunktionen bereit,
|
||||
// wie z.B. Konfigurations-Management, Datenbank-Verbindungen und Service Discovery.
|
||||
plugins {
|
||||
alias(libs.plugins.kotlin.jvm)
|
||||
alias(libs.plugins.kotlin.multiplatform)
|
||||
}
|
||||
|
||||
kotlin {
|
||||
compilerOptions {
|
||||
freeCompilerArgs.add("-opt-in=kotlin.time.ExperimentalTime")
|
||||
// Target platforms
|
||||
jvm {
|
||||
compilerOptions {
|
||||
freeCompilerArgs.add("-opt-in=kotlin.time.ExperimentalTime")
|
||||
}
|
||||
}
|
||||
js(IR) {
|
||||
browser()
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
val commonMain by getting {
|
||||
dependencies {
|
||||
// Abhängigkeit zum core-domain-Modul, um dessen Typen zu verwenden
|
||||
api(projects.core.coreDomain)
|
||||
|
||||
// Asynchronität (available for all platforms) - explicit version to avoid BOM issues
|
||||
api("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.9.0")
|
||||
|
||||
// Utilities (multiplatform compatible)
|
||||
api(libs.bignum)
|
||||
}
|
||||
}
|
||||
|
||||
val jvmMain by getting {
|
||||
dependencies {
|
||||
// Abhängigkeit zum platform-Modul für zentrale Versionsverwaltung
|
||||
api(projects.platform.platformDependencies)
|
||||
|
||||
// Datenbank-Management (JVM-specific)
|
||||
// OPTIMIERUNG: Verwendung von Bundles für Exposed und Flyway
|
||||
api(libs.bundles.exposed)
|
||||
api(libs.bundles.flyway)
|
||||
api(libs.hikari.cp)
|
||||
|
||||
// Service Discovery (JVM-specific)
|
||||
// api(libs.consul.client) wird getauscht mir spring-cloud-starter-consul-discovery
|
||||
api(libs.spring.cloud.starter.consul.discovery)
|
||||
|
||||
// Logging (JVM-specific)
|
||||
api(libs.kotlin.logging.jvm)
|
||||
|
||||
// JVM-specific utilities
|
||||
implementation(libs.room.common.jvm) // Für BigDecimal Serialisierung
|
||||
}
|
||||
}
|
||||
|
||||
val commonTest by getting {
|
||||
dependencies {
|
||||
implementation(libs.kotlin.test)
|
||||
}
|
||||
}
|
||||
|
||||
val jvmTest by getting {
|
||||
dependencies {
|
||||
// Testing (JVM-specific)
|
||||
implementation(projects.platform.platformTesting)
|
||||
implementation(libs.bundles.testing.jvm)
|
||||
runtimeOnly(libs.postgresql.driver)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
// Abhängigkeit zum platform-Modul für zentrale Versionsverwaltung
|
||||
api(projects.platform.platformDependencies)
|
||||
// Abhängigkeit zum core-domain-Modul, um dessen Typen zu verwenden
|
||||
api(projects.core.coreDomain)
|
||||
|
||||
// Asynchronität
|
||||
api(libs.kotlinx.coroutines.core)
|
||||
|
||||
// Datenbank-Management
|
||||
// OPTIMIERUNG: Verwendung von Bundles für Exposed und Flyway
|
||||
api(libs.bundles.exposed)
|
||||
api(libs.bundles.flyway)
|
||||
api(libs.hikari.cp)
|
||||
|
||||
// Service Discovery
|
||||
// api(libs.consul.client) wird getauscht mir spring-cloud-starter-consul-discovery
|
||||
api(libs.spring.cloud.starter.consul.discovery)
|
||||
|
||||
// Logging
|
||||
api(libs.kotlin.logging.jvm)
|
||||
|
||||
// Utilities
|
||||
api(libs.bignum)
|
||||
implementation(libs.room.common.jvm) // Für BigDecimal Serialisierung
|
||||
|
||||
// Testing
|
||||
testImplementation(projects.platform.platformTesting)
|
||||
testImplementation(libs.bundles.testing.jvm)
|
||||
testImplementation(libs.kotlin.test)
|
||||
testRuntimeOnly(libs.postgresql.driver)
|
||||
}
|
||||
|
||||
@@ -15,43 +15,55 @@ data class AppConfig(
|
||||
val rateLimit: RateLimitConfig
|
||||
)
|
||||
|
||||
data class AppInfoConfig(val name: String, val version: String, val description: String)
|
||||
data class AppInfoConfig(
|
||||
val name: ApplicationName,
|
||||
val version: ApplicationVersion,
|
||||
val description: String
|
||||
)
|
||||
|
||||
data class ServerConfig(
|
||||
val port: Int,
|
||||
val host: String,
|
||||
val advertisedHost: String,
|
||||
val workers: Int,
|
||||
val port: Port,
|
||||
val host: Host,
|
||||
val advertisedHost: Host,
|
||||
val workers: WorkerCount,
|
||||
val cors: CorsConfig
|
||||
) {
|
||||
data class CorsConfig(val enabled: Boolean, val allowedOrigins: List<String>)
|
||||
}
|
||||
|
||||
data class DatabaseConfig(
|
||||
val host: String,
|
||||
val port: Int,
|
||||
val name: String,
|
||||
val jdbcUrl: String,
|
||||
val username: String,
|
||||
val password: String,
|
||||
val host: Host,
|
||||
val port: Port,
|
||||
val name: DatabaseName,
|
||||
val jdbcUrl: JdbcUrl,
|
||||
val username: DatabaseUsername,
|
||||
val password: DatabasePassword,
|
||||
val driverClassName: String,
|
||||
val maxPoolSize: Int,
|
||||
val minPoolSize: Int,
|
||||
val maxPoolSize: PoolSize,
|
||||
val minPoolSize: PoolSize,
|
||||
val autoMigrate: Boolean
|
||||
)
|
||||
|
||||
data class ServiceDiscoveryConfig(val enabled: Boolean, val consulHost: String, val consulPort: Int)
|
||||
data class ServiceDiscoveryConfig(
|
||||
val enabled: Boolean,
|
||||
val consulHost: Host,
|
||||
val consulPort: Port
|
||||
)
|
||||
|
||||
data class SecurityConfig(val jwt: JwtConfig, val apiKey: String?) {
|
||||
data class SecurityConfig(val jwt: JwtConfig, val apiKey: ApiKey?) {
|
||||
data class JwtConfig(
|
||||
val secret: String,
|
||||
val issuer: String,
|
||||
val audience: String,
|
||||
val realm: String,
|
||||
val secret: JwtSecret,
|
||||
val issuer: JwtIssuer,
|
||||
val audience: JwtAudience,
|
||||
val realm: JwtRealm,
|
||||
val expirationInMinutes: Long
|
||||
)
|
||||
}
|
||||
|
||||
data class LoggingConfig(val level: String, val logRequests: Boolean, val logResponses: Boolean)
|
||||
|
||||
data class RateLimitConfig(val enabled: Boolean, val globalLimit: Int, val globalPeriodMinutes: Int)
|
||||
data class RateLimitConfig(
|
||||
val enabled: Boolean,
|
||||
val globalLimit: RateLimit,
|
||||
val globalPeriodMinutes: PeriodMinutes
|
||||
)
|
||||
|
||||
@@ -53,8 +53,8 @@ class ConfigLoader(private val configPath: String = "config") {
|
||||
|
||||
// Die Konfigurations-Erstellungslogik ist hierher verschoben
|
||||
private fun createAppInfoConfig(props: Properties) = AppInfoConfig(
|
||||
name = props.getProperty("app.name", "Meldestelle"),
|
||||
version = props.getProperty("app.version", "1.0.0"),
|
||||
name = ApplicationName(props.getProperty("app.name", "Meldestelle")),
|
||||
version = ApplicationVersion(props.getProperty("app.version", "1.0.0")),
|
||||
description = props.getProperty("app.description", "Pferdesport Meldestelle System")
|
||||
)
|
||||
|
||||
@@ -65,10 +65,10 @@ class ConfigLoader(private val configPath: String = "config") {
|
||||
"127.0.0.1"
|
||||
}
|
||||
return ServerConfig(
|
||||
port = props.getIntProperty("server.port", "API_PORT", 8081),
|
||||
host = props.getStringProperty("server.host", "API_HOST", "0.0.0.0"),
|
||||
advertisedHost = props.getStringProperty("server.advertisedHost", "API_HOST_ADVERTISED", defaultHost),
|
||||
workers = props.getIntProperty("server.workers", "API_WORKERS", Runtime.getRuntime().availableProcessors()),
|
||||
port = Port(props.getIntProperty("server.port", "API_PORT", 8081)),
|
||||
host = Host(props.getStringProperty("server.host", "API_HOST", "0.0.0.0")),
|
||||
advertisedHost = Host(props.getStringProperty("server.advertisedHost", "API_HOST_ADVERTISED", defaultHost)),
|
||||
workers = WorkerCount(props.getIntProperty("server.workers", "API_WORKERS", Runtime.getRuntime().availableProcessors())),
|
||||
cors = ServerConfig.CorsConfig(
|
||||
enabled = props.getBooleanProperty("server.cors.enabled", "API_CORS_ENABLED", true),
|
||||
allowedOrigins = props.getProperty("server.cors.allowedOrigins")?.split(",")?.map { it.trim() }
|
||||
@@ -82,15 +82,15 @@ class ConfigLoader(private val configPath: String = "config") {
|
||||
val port = props.getIntProperty("database.port", "DB_PORT", 5432)
|
||||
val name = props.getStringProperty("database.name", "DB_NAME", "meldestelle_db")
|
||||
return DatabaseConfig(
|
||||
host = host,
|
||||
port = port,
|
||||
name = name,
|
||||
jdbcUrl = "jdbc:postgresql://$host:$port/$name",
|
||||
username = props.getStringProperty("database.username", "DB_USER", "meldestelle_user"),
|
||||
password = props.getStringProperty("database.password", "DB_PASSWORD", "secure_password_change_me"),
|
||||
host = Host(host),
|
||||
port = Port(port),
|
||||
name = DatabaseName(name),
|
||||
jdbcUrl = JdbcUrl("jdbc:postgresql://$host:$port/$name"),
|
||||
username = DatabaseUsername(props.getStringProperty("database.username", "DB_USER", "meldestelle_user")),
|
||||
password = DatabasePassword(props.getStringProperty("database.password", "DB_PASSWORD", "secure_password_change_me")),
|
||||
driverClassName = "org.postgresql.Driver",
|
||||
maxPoolSize = props.getIntProperty("database.maxPoolSize", "DB_MAX_POOL_SIZE", 10),
|
||||
minPoolSize = props.getIntProperty("database.minPoolSize", "DB_MIN_POOL_SIZE", 5),
|
||||
maxPoolSize = PoolSize(props.getIntProperty("database.maxPoolSize", "DB_MAX_POOL_SIZE", 10)),
|
||||
minPoolSize = PoolSize(props.getIntProperty("database.minPoolSize", "DB_MIN_POOL_SIZE", 5)),
|
||||
autoMigrate = props.getBooleanProperty("database.autoMigrate", "DB_AUTO_MIGRATE", true)
|
||||
)
|
||||
}
|
||||
@@ -99,27 +99,27 @@ class ConfigLoader(private val configPath: String = "config") {
|
||||
// analog zu den 'fromProperties' Methoden aus der alten AppConfig.
|
||||
private fun createServiceDiscoveryConfig(props: Properties) = ServiceDiscoveryConfig(
|
||||
enabled = props.getBooleanProperty("service-discovery.enabled", "CONSUL_ENABLED", true),
|
||||
consulHost = props.getStringProperty("service-discovery.consul.host", "CONSUL_HOST", "consul"),
|
||||
consulPort = props.getIntProperty("service-discovery.consul.port", "CONSUL_PORT", 8500)
|
||||
consulHost = Host(props.getStringProperty("service-discovery.consul.host", "CONSUL_HOST", "consul")),
|
||||
consulPort = Port(props.getIntProperty("service-discovery.consul.port", "CONSUL_PORT", 8500))
|
||||
)
|
||||
|
||||
private fun createSecurityConfig(props: Properties) = SecurityConfig(
|
||||
jwt = SecurityConfig.JwtConfig(
|
||||
secret = props.getStringProperty(
|
||||
secret = JwtSecret(props.getStringProperty(
|
||||
"security.jwt.secret",
|
||||
"JWT_SECRET",
|
||||
"default-secret-please-change-in-production"
|
||||
),
|
||||
issuer = props.getStringProperty("security.jwt.issuer", "JWT_ISSUER", "meldestelle-api"),
|
||||
audience = props.getStringProperty("security.jwt.audience", "JWT_AUDIENCE", "meldestelle-clients"),
|
||||
realm = props.getStringProperty("security.jwt.realm", "JWT_REALM", "meldestelle"),
|
||||
)),
|
||||
issuer = JwtIssuer(props.getStringProperty("security.jwt.issuer", "JWT_ISSUER", "meldestelle-api")),
|
||||
audience = JwtAudience(props.getStringProperty("security.jwt.audience", "JWT_AUDIENCE", "meldestelle-clients")),
|
||||
realm = JwtRealm(props.getStringProperty("security.jwt.realm", "JWT_REALM", "meldestelle")),
|
||||
expirationInMinutes = props.getLongProperty(
|
||||
"security.jwt.expirationInMinutes",
|
||||
"JWT_EXPIRATION_MINUTES",
|
||||
60 * 24
|
||||
)
|
||||
),
|
||||
apiKey = props.getStringProperty("security.apiKey", "API_KEY", "").ifEmpty { null }
|
||||
apiKey = props.getStringProperty("security.apiKey", "API_KEY", "").ifEmpty { null }?.let { ApiKey(it) }
|
||||
)
|
||||
|
||||
private fun createLoggingConfig(props: Properties, env: AppEnvironment) = LoggingConfig(
|
||||
@@ -130,7 +130,7 @@ class ConfigLoader(private val configPath: String = "config") {
|
||||
|
||||
private fun createRateLimitConfig(props: Properties) = RateLimitConfig(
|
||||
enabled = props.getBooleanProperty("ratelimit.enabled", "RATE_LIMIT_ENABLED", true),
|
||||
globalLimit = props.getIntProperty("ratelimit.global.limit", "RATE_LIMIT_GLOBAL_LIMIT", 100),
|
||||
globalPeriodMinutes = props.getIntProperty("ratelimit.global.periodMinutes", "RATE_LIMIT_GLOBAL_PERIOD", 1)
|
||||
globalLimit = RateLimit(props.getIntProperty("ratelimit.global.limit", "RATE_LIMIT_GLOBAL_LIMIT", 100)),
|
||||
globalPeriodMinutes = PeriodMinutes(props.getIntProperty("ratelimit.global.periodMinutes", "RATE_LIMIT_GLOBAL_PERIOD", 1))
|
||||
)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,260 @@
|
||||
package at.mocode.core.utils.config
|
||||
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
/**
|
||||
* Value classes for strongly typed configuration parameters.
|
||||
* These provide compile-time type safety for configuration values.
|
||||
*/
|
||||
|
||||
// === Network Configuration Value Classes ===
|
||||
|
||||
/**
|
||||
* A strongly typed wrapper for port numbers.
|
||||
*/
|
||||
@Serializable
|
||||
@JvmInline
|
||||
value class Port(val value: Int) {
|
||||
init {
|
||||
require(value in 1..65535) { "Port must be between 1 and 65535, got: $value" }
|
||||
}
|
||||
|
||||
override fun toString(): String = value.toString()
|
||||
}
|
||||
|
||||
/**
|
||||
* A strongly typed wrapper for host names or IP addresses.
|
||||
*/
|
||||
@Serializable
|
||||
@JvmInline
|
||||
value class Host(val value: String) {
|
||||
init {
|
||||
require(value.isNotBlank()) { "Host cannot be blank" }
|
||||
require(value.length <= 253) { "Host name cannot exceed 253 characters" }
|
||||
}
|
||||
|
||||
override fun toString(): String = value
|
||||
}
|
||||
|
||||
// === Database Configuration Value Classes ===
|
||||
|
||||
/**
|
||||
* A strongly typed wrapper for database names.
|
||||
*/
|
||||
@Serializable
|
||||
@JvmInline
|
||||
value class DatabaseName(val value: String) {
|
||||
init {
|
||||
require(value.isNotBlank()) { "Database name cannot be blank" }
|
||||
require(value.matches(Regex("^[a-zA-Z][a-zA-Z0-9_]*$"))) {
|
||||
"Database name must start with a letter and contain only alphanumeric characters and underscores"
|
||||
}
|
||||
}
|
||||
|
||||
override fun toString(): String = value
|
||||
}
|
||||
|
||||
/**
|
||||
* A strongly typed wrapper for database usernames.
|
||||
*/
|
||||
@Serializable
|
||||
@JvmInline
|
||||
value class DatabaseUsername(val value: String) {
|
||||
init {
|
||||
require(value.isNotBlank()) { "Database username cannot be blank" }
|
||||
}
|
||||
|
||||
override fun toString(): String = value
|
||||
}
|
||||
|
||||
/**
|
||||
* A strongly typed wrapper for database passwords.
|
||||
*/
|
||||
@Serializable
|
||||
@JvmInline
|
||||
value class DatabasePassword(val value: String) {
|
||||
init {
|
||||
require(value.isNotBlank()) { "Database password cannot be blank" }
|
||||
}
|
||||
|
||||
override fun toString(): String = "***" // Never expose the actual password
|
||||
|
||||
fun getValue(): String = value
|
||||
}
|
||||
|
||||
/**
|
||||
* A strongly typed wrapper for JDBC URLs.
|
||||
*/
|
||||
@Serializable
|
||||
@JvmInline
|
||||
value class JdbcUrl(val value: String) {
|
||||
init {
|
||||
require(value.isNotBlank()) { "JDBC URL cannot be blank" }
|
||||
require(value.startsWith("jdbc:")) { "JDBC URL must start with 'jdbc:'" }
|
||||
}
|
||||
|
||||
override fun toString(): String = value
|
||||
}
|
||||
|
||||
/**
|
||||
* A strongly typed wrapper for connection pool sizes.
|
||||
*/
|
||||
@Serializable
|
||||
@JvmInline
|
||||
value class PoolSize(val value: Int) {
|
||||
init {
|
||||
require(value > 0) { "Pool size must be positive" }
|
||||
require(value <= 1000) { "Pool size cannot exceed 1000" }
|
||||
}
|
||||
|
||||
override fun toString(): String = value.toString()
|
||||
}
|
||||
|
||||
// === Security Configuration Value Classes ===
|
||||
|
||||
/**
|
||||
* A strongly typed wrapper for API keys.
|
||||
*/
|
||||
@Serializable
|
||||
@JvmInline
|
||||
value class ApiKey(val value: String) {
|
||||
init {
|
||||
require(value.isNotBlank()) { "API key cannot be blank" }
|
||||
require(value.length >= 16) { "API key must be at least 16 characters long" }
|
||||
}
|
||||
|
||||
override fun toString(): String = "***" // Never expose the actual key
|
||||
|
||||
fun getValue(): String = value
|
||||
}
|
||||
|
||||
/**
|
||||
* A strongly typed wrapper for JWT secrets.
|
||||
*/
|
||||
@Serializable
|
||||
@JvmInline
|
||||
value class JwtSecret(val value: String) {
|
||||
init {
|
||||
require(value.isNotBlank()) { "JWT secret cannot be blank" }
|
||||
require(value.length >= 32) { "JWT secret must be at least 32 characters long" }
|
||||
}
|
||||
|
||||
override fun toString(): String = "***" // Never expose the actual secret
|
||||
|
||||
fun getValue(): String = value
|
||||
}
|
||||
|
||||
/**
|
||||
* A strongly typed wrapper for JWT issuer.
|
||||
*/
|
||||
@Serializable
|
||||
@JvmInline
|
||||
value class JwtIssuer(val value: String) {
|
||||
init {
|
||||
require(value.isNotBlank()) { "JWT issuer cannot be blank" }
|
||||
}
|
||||
|
||||
override fun toString(): String = value
|
||||
}
|
||||
|
||||
/**
|
||||
* A strongly typed wrapper for JWT audience.
|
||||
*/
|
||||
@Serializable
|
||||
@JvmInline
|
||||
value class JwtAudience(val value: String) {
|
||||
init {
|
||||
require(value.isNotBlank()) { "JWT audience cannot be blank" }
|
||||
}
|
||||
|
||||
override fun toString(): String = value
|
||||
}
|
||||
|
||||
/**
|
||||
* A strongly typed wrapper for JWT realm.
|
||||
*/
|
||||
@Serializable
|
||||
@JvmInline
|
||||
value class JwtRealm(val value: String) {
|
||||
init {
|
||||
require(value.isNotBlank()) { "JWT realm cannot be blank" }
|
||||
}
|
||||
|
||||
override fun toString(): String = value
|
||||
}
|
||||
|
||||
// === Application Configuration Value Classes ===
|
||||
|
||||
/**
|
||||
* A strongly typed wrapper for application names.
|
||||
*/
|
||||
@Serializable
|
||||
@JvmInline
|
||||
value class ApplicationName(val value: String) {
|
||||
init {
|
||||
require(value.isNotBlank()) { "Application name cannot be blank" }
|
||||
require(value.matches(Regex("^[A-Za-z][A-Za-z0-9-_]*$"))) {
|
||||
"Application name must start with a letter and contain only letters, numbers, hyphens, and underscores"
|
||||
}
|
||||
}
|
||||
|
||||
override fun toString(): String = value
|
||||
}
|
||||
|
||||
/**
|
||||
* A strongly typed wrapper for application versions.
|
||||
*/
|
||||
@Serializable
|
||||
@JvmInline
|
||||
value class ApplicationVersion(val value: String) {
|
||||
init {
|
||||
require(value.isNotBlank()) { "Application version cannot be blank" }
|
||||
require(value.matches(Regex("^\\d+\\.\\d+\\.\\d+(-[a-zA-Z0-9]+)?$"))) {
|
||||
"Application version must follow semantic versioning (e.g., 1.0.0 or 1.0.0-beta)"
|
||||
}
|
||||
}
|
||||
|
||||
override fun toString(): String = value
|
||||
}
|
||||
|
||||
/**
|
||||
* A strongly typed wrapper for worker thread counts.
|
||||
*/
|
||||
@Serializable
|
||||
@JvmInline
|
||||
value class WorkerCount(val value: Int) {
|
||||
init {
|
||||
require(value > 0) { "Worker count must be positive" }
|
||||
require(value <= Runtime.getRuntime().availableProcessors() * 4) {
|
||||
"Worker count should not exceed 4 times the available processors"
|
||||
}
|
||||
}
|
||||
|
||||
override fun toString(): String = value.toString()
|
||||
}
|
||||
|
||||
/**
|
||||
* A strongly typed wrapper for rate limits.
|
||||
*/
|
||||
@Serializable
|
||||
@JvmInline
|
||||
value class RateLimit(val value: Int) {
|
||||
init {
|
||||
require(value > 0) { "Rate limit must be positive" }
|
||||
}
|
||||
|
||||
override fun toString(): String = value.toString()
|
||||
}
|
||||
|
||||
/**
|
||||
* A strongly typed wrapper for time periods in minutes.
|
||||
*/
|
||||
@Serializable
|
||||
@JvmInline
|
||||
value class PeriodMinutes(val value: Int) {
|
||||
init {
|
||||
require(value > 0) { "Period must be positive" }
|
||||
}
|
||||
|
||||
override fun toString(): String = value.toString()
|
||||
}
|
||||
@@ -51,11 +51,11 @@ class DatabaseFactory(private val config: DatabaseConfig) {
|
||||
private fun createHikariConfig(): HikariConfig {
|
||||
return HikariConfig().apply {
|
||||
driverClassName = config.driverClassName
|
||||
jdbcUrl = config.jdbcUrl
|
||||
username = config.username
|
||||
password = config.password
|
||||
maximumPoolSize = config.maxPoolSize
|
||||
minimumIdle = config.minPoolSize
|
||||
jdbcUrl = config.jdbcUrl.value
|
||||
username = config.username.value
|
||||
password = config.password.getValue() // Use getValue() for password to access actual value
|
||||
maximumPoolSize = config.maxPoolSize.value
|
||||
minimumIdle = config.minPoolSize.value
|
||||
isAutoCommit = false
|
||||
transactionIsolation = "TRANSACTION_READ_COMMITTED"
|
||||
validationTimeout = 5000
|
||||
|
||||
@@ -31,8 +31,8 @@ class ConfigLoaderTest {
|
||||
val config = configLoader.load(AppEnvironment.DEVELOPMENT)
|
||||
|
||||
// Assert
|
||||
assertEquals("Meldestelle", config.appInfo.name)
|
||||
assertEquals(8081, config.server.port) // Standard-Port
|
||||
assertEquals("Meldestelle", config.appInfo.name.value)
|
||||
assertEquals(8081, config.server.port.value) // Standard-Port
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -53,8 +53,8 @@ class ConfigLoaderTest {
|
||||
val config = configLoader.load(AppEnvironment.DEVELOPMENT)
|
||||
|
||||
// Assert
|
||||
assertEquals("TestApp", config.appInfo.name)
|
||||
assertEquals(9999, config.server.port)
|
||||
assertEquals("TestApp", config.appInfo.name.value)
|
||||
assertEquals(9999, config.server.port.value)
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -83,8 +83,8 @@ class ConfigLoaderTest {
|
||||
|
||||
// Assert
|
||||
assertEquals(AppEnvironment.TEST, config.environment, "Environment should be TEST")
|
||||
assertEquals("TestEnvApp", config.appInfo.name, "app.name should be overridden")
|
||||
assertEquals(9000, config.server.port, "server.port should be overridden")
|
||||
assertEquals("base-db-host", config.database.host, "database.host should come from the base file")
|
||||
assertEquals("TestEnvApp", config.appInfo.name.value, "app.name should be overridden")
|
||||
assertEquals(9000, config.server.port.value, "server.port should be overridden")
|
||||
assertEquals("base-db-host", config.database.host.value, "database.host should come from the base file")
|
||||
}
|
||||
}
|
||||
|
||||
+10
-10
@@ -1,6 +1,6 @@
|
||||
package at.mocode.core.utils.database
|
||||
|
||||
import at.mocode.core.utils.config.DatabaseConfig
|
||||
import at.mocode.core.utils.config.*
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import org.jetbrains.exposed.sql.SchemaUtils
|
||||
import org.jetbrains.exposed.sql.Table
|
||||
@@ -23,7 +23,7 @@ class DatabaseFactoryTest {
|
||||
companion object {
|
||||
@Container
|
||||
val postgresContainer = PostgreSQLContainer<Nothing>("postgres:16-alpine").apply {
|
||||
withDatabaseName("test-db")
|
||||
withDatabaseName("testdb")
|
||||
withUsername("test-user")
|
||||
withPassword("test-password")
|
||||
}
|
||||
@@ -37,15 +37,15 @@ class DatabaseFactoryTest {
|
||||
fun setup() {
|
||||
// Erstelle eine DB-Konfiguration mit den dynamischen Daten des gestarteten Containers
|
||||
dbConfig = DatabaseConfig(
|
||||
host = postgresContainer.host,
|
||||
port = postgresContainer.firstMappedPort,
|
||||
name = postgresContainer.databaseName,
|
||||
jdbcUrl = postgresContainer.jdbcUrl,
|
||||
username = postgresContainer.username,
|
||||
password = postgresContainer.password,
|
||||
host = Host(postgresContainer.host),
|
||||
port = Port(postgresContainer.firstMappedPort),
|
||||
name = DatabaseName(postgresContainer.databaseName),
|
||||
jdbcUrl = JdbcUrl(postgresContainer.jdbcUrl),
|
||||
username = DatabaseUsername(postgresContainer.username),
|
||||
password = DatabasePassword(postgresContainer.password),
|
||||
driverClassName = "org.postgresql.Driver",
|
||||
maxPoolSize = 2,
|
||||
minPoolSize = 1,
|
||||
maxPoolSize = PoolSize(2),
|
||||
minPoolSize = PoolSize(1),
|
||||
autoMigrate = false // Wir steuern Migrationen im Test manuell
|
||||
)
|
||||
// Erstelle eine neue Factory-Instanz und verbinde sie mit der Test-DB
|
||||
|
||||
Reference in New Issue
Block a user