chore: remove deprecated horses, clubs, officials, and persons services
- Deleted obsolete modules related to horses, clubs, officials, and persons services, including their configurations, build files, and database provisioning scripts. - Cleaned up associated references in the project structure (e.g., `settings.gradle.kts`). - Removed unused database tables and Spring beans related to these domains. Signed-off-by: Stefan Mogeritsch <stefan.mo.co@gmail.com>
This commit is contained in:
+1
-1
@@ -12,5 +12,5 @@ import org.springframework.context.annotation.EnableAspectJAutoProxy
|
||||
class PingServiceApplication
|
||||
|
||||
fun main(args: Array<String>) {
|
||||
runApplication<PingServiceApplication>(*args)
|
||||
runApplication<PingServiceApplication>(*args)
|
||||
}
|
||||
|
||||
+23
-23
@@ -19,35 +19,35 @@ import java.time.Instant
|
||||
@Profile("!test") // Nicht im Test-Profil laden, damit wir Mocks nutzen können
|
||||
@OptIn(ExperimentalUuidApi::class)
|
||||
class PingService(
|
||||
private val repository: PingRepository
|
||||
private val repository: PingRepository
|
||||
) : PingUseCase {
|
||||
|
||||
private val logger = LoggerFactory.getLogger(PingService::class.java)
|
||||
private val logger = LoggerFactory.getLogger(PingService::class.java)
|
||||
|
||||
@Transactional
|
||||
override fun executePing(message: String): Ping {
|
||||
logger.info("Executing ping with message: {}", message)
|
||||
@Transactional
|
||||
override fun executePing(message: String): Ping {
|
||||
logger.info("Executing ping with message: {}", message)
|
||||
|
||||
// Domain Logic: Erstelle neue Entity (generiert UUID v7 automatisch)
|
||||
val ping = Ping(message = message)
|
||||
// Domain Logic: Erstelle neue Entity (generiert UUID v7 automatisch)
|
||||
val ping = Ping(message = message)
|
||||
|
||||
// Persistence
|
||||
return repository.save(ping)
|
||||
}
|
||||
// Persistence
|
||||
return repository.save(ping)
|
||||
}
|
||||
|
||||
@Transactional(readOnly = true)
|
||||
override fun getPingHistory(): List<Ping> {
|
||||
return repository.findAll()
|
||||
}
|
||||
@Transactional(readOnly = true)
|
||||
override fun getPingHistory(): List<Ping> {
|
||||
return repository.findAll()
|
||||
}
|
||||
|
||||
@Transactional(readOnly = true)
|
||||
override fun getPing(id: Uuid): Ping? {
|
||||
return repository.findById(id)
|
||||
}
|
||||
@Transactional(readOnly = true)
|
||||
override fun getPing(id: Uuid): Ping? {
|
||||
return repository.findById(id)
|
||||
}
|
||||
|
||||
@Transactional(readOnly = true)
|
||||
override fun getPingsSince(timestamp: Long): List<Ping> {
|
||||
val instant = Instant.ofEpochMilli(timestamp)
|
||||
return repository.findByTimestampAfter(instant)
|
||||
}
|
||||
@Transactional(readOnly = true)
|
||||
override fun getPingsSince(timestamp: Long): List<Ping> {
|
||||
val instant = Instant.ofEpochMilli(timestamp)
|
||||
return repository.findByTimestampAfter(instant)
|
||||
}
|
||||
}
|
||||
|
||||
+4
-4
@@ -10,8 +10,8 @@ import kotlin.uuid.Uuid
|
||||
*/
|
||||
@OptIn(ExperimentalUuidApi::class)
|
||||
interface PingUseCase {
|
||||
fun executePing(message: String): Ping
|
||||
fun getPingHistory(): List<Ping>
|
||||
fun getPing(id: Uuid): Ping?
|
||||
fun getPingsSince(timestamp: Long): List<Ping>
|
||||
fun executePing(message: String): Ping
|
||||
fun getPingHistory(): List<Ping>
|
||||
fun getPing(id: Uuid): Ping?
|
||||
fun getPingsSince(timestamp: Long): List<Ping>
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ import java.time.Instant
|
||||
*/
|
||||
@OptIn(ExperimentalUuidApi::class)
|
||||
data class Ping(
|
||||
val id: Uuid = Uuid.generateV7(), // Kotlin 2.3.0 UUID v7
|
||||
val message: String,
|
||||
val timestamp: Instant = Instant.now()
|
||||
val id: Uuid = Uuid.generateV7(), // Kotlin 2.3.0 UUID v7
|
||||
val message: String,
|
||||
val timestamp: Instant = Instant.now()
|
||||
)
|
||||
|
||||
+4
-4
@@ -10,8 +10,8 @@ import java.time.Instant
|
||||
*/
|
||||
@OptIn(ExperimentalUuidApi::class)
|
||||
interface PingRepository {
|
||||
fun save(ping: Ping): Ping
|
||||
fun findAll(): List<Ping>
|
||||
fun findById(id: Uuid): Ping?
|
||||
fun findByTimestampAfter(timestamp: Instant): List<Ping>
|
||||
fun save(ping: Ping): Ping
|
||||
fun findAll(): List<Ping>
|
||||
fun findById(id: Uuid): Ping?
|
||||
fun findByTimestampAfter(timestamp: Instant): List<Ping>
|
||||
}
|
||||
|
||||
+4
-10
@@ -9,15 +9,9 @@ import java.util.UUID
|
||||
@Entity
|
||||
@Table(name = "ping")
|
||||
class PingJpaEntity(
|
||||
@Id
|
||||
val id: UUID,
|
||||
val message: String,
|
||||
val createdAt: Instant
|
||||
@Id
|
||||
var id: UUID,
|
||||
var message: String,
|
||||
var createdAt: Instant
|
||||
) {
|
||||
// The default constructor for JPA
|
||||
// Protected is fine for Hibernate, but the Kotlin compiler might complain about visibility.
|
||||
// We can make it private or internal if needed, but protected is standard.
|
||||
// To suppress the warning "effectively private", we can just leave it as is or make it public/internal.
|
||||
// Let's try making it internal to satisfy Kotlin while keeping it hidden from public API.
|
||||
internal constructor() : this(UUID.randomUUID(), "", Instant.now())
|
||||
}
|
||||
|
||||
+25
-25
@@ -14,36 +14,36 @@ import java.time.Instant
|
||||
// @Profile("!test") entfernt, damit Integrationstests den echten Adapter nutzen können.
|
||||
// In Unit-Tests wird er durch Mocks (@MockBean oder @TestConfiguration) ersetzt.
|
||||
class PingRepositoryAdapter(
|
||||
private val jpaRepository: SpringDataPingRepository
|
||||
private val jpaRepository: SpringDataPingRepository
|
||||
) : PingRepository {
|
||||
|
||||
override fun save(ping: Ping): Ping {
|
||||
val entity = ping.toEntity()
|
||||
val savedEntity = jpaRepository.save(entity)
|
||||
return savedEntity.toDomain()
|
||||
}
|
||||
override fun save(ping: Ping): Ping {
|
||||
val entity = ping.toEntity()
|
||||
val savedEntity = jpaRepository.save(entity)
|
||||
return savedEntity.toDomain()
|
||||
}
|
||||
|
||||
override fun findAll(): List<Ping> {
|
||||
return jpaRepository.findAll().map { it.toDomain() }
|
||||
}
|
||||
override fun findAll(): List<Ping> {
|
||||
return jpaRepository.findAll().map { it.toDomain() }
|
||||
}
|
||||
|
||||
override fun findById(id: Uuid): Ping? {
|
||||
return jpaRepository.findById(id.toJavaUuid()).map { it.toDomain() }.orElse(null)
|
||||
}
|
||||
override fun findById(id: Uuid): Ping? {
|
||||
return jpaRepository.findById(id.toJavaUuid()).map { it.toDomain() }.orElse(null)
|
||||
}
|
||||
|
||||
override fun findByTimestampAfter(timestamp: Instant): List<Ping> {
|
||||
return jpaRepository.findByCreatedAtAfter(timestamp).map { it.toDomain() }
|
||||
}
|
||||
override fun findByTimestampAfter(timestamp: Instant): List<Ping> {
|
||||
return jpaRepository.findByCreatedAtAfter(timestamp).map { it.toDomain() }
|
||||
}
|
||||
|
||||
private fun Ping.toEntity() = PingJpaEntity(
|
||||
id = this.id.toJavaUuid(),
|
||||
message = this.message,
|
||||
createdAt = this.timestamp
|
||||
)
|
||||
private fun Ping.toEntity() = PingJpaEntity(
|
||||
id = this.id.toJavaUuid(),
|
||||
message = this.message,
|
||||
createdAt = this.timestamp
|
||||
)
|
||||
|
||||
private fun PingJpaEntity.toDomain() = Ping(
|
||||
id = this.id.toKotlinUuid(),
|
||||
message = this.message,
|
||||
timestamp = this.createdAt
|
||||
)
|
||||
private fun PingJpaEntity.toDomain() = Ping(
|
||||
id = this.id.toKotlinUuid(),
|
||||
message = this.message,
|
||||
timestamp = this.createdAt
|
||||
)
|
||||
}
|
||||
|
||||
+1
-1
@@ -5,5 +5,5 @@ import java.util.UUID
|
||||
import java.time.Instant
|
||||
|
||||
interface SpringDataPingRepository : JpaRepository<PingJpaEntity, UUID> {
|
||||
fun findByCreatedAtAfter(createdAt: Instant): List<PingJpaEntity>
|
||||
fun findByCreatedAtAfter(createdAt: Instant): List<PingJpaEntity>
|
||||
}
|
||||
|
||||
+88
-88
@@ -21,102 +21,102 @@ import kotlin.uuid.ExperimentalUuidApi
|
||||
@RestController
|
||||
@OptIn(ExperimentalUuidApi::class)
|
||||
class PingController(
|
||||
private val pingUseCase: PingUseCase,
|
||||
private val properties: PingProperties
|
||||
private val pingUseCase: PingUseCase,
|
||||
private val properties: PingProperties
|
||||
) : PingApi {
|
||||
|
||||
private val logger = LoggerFactory.getLogger(PingController::class.java)
|
||||
private val formatter = DateTimeFormatter.ISO_OFFSET_DATE_TIME
|
||||
private val logger = LoggerFactory.getLogger(PingController::class.java)
|
||||
private val formatter = DateTimeFormatter.ISO_OFFSET_DATE_TIME
|
||||
|
||||
companion object {
|
||||
const val PING_CIRCUIT_BREAKER = "pingCircuitBreaker"
|
||||
companion object {
|
||||
const val PING_CIRCUIT_BREAKER = "pingCircuitBreaker"
|
||||
}
|
||||
|
||||
@GetMapping("/ping/simple")
|
||||
override suspend fun simplePing(): PingResponse {
|
||||
val domainPing = pingUseCase.executePing("Simple Ping")
|
||||
return createResponse(domainPing, "pong")
|
||||
}
|
||||
|
||||
@GetMapping("/ping/enhanced")
|
||||
@CircuitBreaker(name = PING_CIRCUIT_BREAKER, fallbackMethod = "fallbackPing")
|
||||
override suspend fun enhancedPing(
|
||||
@RequestParam(required = false, defaultValue = "false") simulate: Boolean
|
||||
): EnhancedPingResponse {
|
||||
val start = System.nanoTime()
|
||||
|
||||
if (simulate && Random.nextDouble() < 0.6) {
|
||||
throw RuntimeException("Simulated service failure")
|
||||
}
|
||||
|
||||
@GetMapping("/ping/simple")
|
||||
override suspend fun simplePing(): PingResponse {
|
||||
val domainPing = pingUseCase.executePing("Simple Ping")
|
||||
return createResponse(domainPing, "pong")
|
||||
}
|
||||
val domainPing = pingUseCase.executePing("Enhanced Ping")
|
||||
val elapsedMs = (System.nanoTime() - start) / 1_000_000
|
||||
|
||||
@GetMapping("/ping/enhanced")
|
||||
@CircuitBreaker(name = PING_CIRCUIT_BREAKER, fallbackMethod = "fallbackPing")
|
||||
override suspend fun enhancedPing(
|
||||
@RequestParam(required = false, defaultValue = "false") simulate: Boolean
|
||||
): EnhancedPingResponse {
|
||||
val start = System.nanoTime()
|
||||
|
||||
if (simulate && Random.nextDouble() < 0.6) {
|
||||
throw RuntimeException("Simulated service failure")
|
||||
}
|
||||
|
||||
val domainPing = pingUseCase.executePing("Enhanced Ping")
|
||||
val elapsedMs = (System.nanoTime() - start) / 1_000_000
|
||||
|
||||
return EnhancedPingResponse(
|
||||
status = "pong",
|
||||
timestamp = domainPing.timestamp.atOffset(ZoneOffset.UTC).format(formatter),
|
||||
service = properties.serviceName,
|
||||
circuitBreakerState = "CLOSED",
|
||||
responseTime = elapsedMs
|
||||
)
|
||||
}
|
||||
|
||||
// Neue Endpunkte
|
||||
|
||||
@GetMapping("/ping/public")
|
||||
override suspend fun publicPing(): PingResponse {
|
||||
val domainPing = pingUseCase.executePing("Public Ping")
|
||||
return createResponse(domainPing, "public-pong")
|
||||
}
|
||||
|
||||
@GetMapping("/ping/secure")
|
||||
@PreAuthorize("hasRole('MELD_USER') or hasRole('MELD_ADMIN')") // Beispiel-Rollen
|
||||
override suspend fun securePing(): PingResponse {
|
||||
val domainPing = pingUseCase.executePing("Secure Ping")
|
||||
return createResponse(domainPing, "secure-pong")
|
||||
}
|
||||
|
||||
@GetMapping("/ping/sync")
|
||||
override suspend fun syncPings(
|
||||
// Changed the parameter name to 'since' to match SyncManager convention
|
||||
@RequestParam(required = false, defaultValue = "0") since: Long
|
||||
): List<PingEvent> {
|
||||
return pingUseCase.getPingsSince(since).map {
|
||||
PingEvent(
|
||||
id = it.id.toString(),
|
||||
message = it.message,
|
||||
lastModified = it.timestamp.toEpochMilli()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// Helper
|
||||
private fun createResponse(domainPing: at.mocode.ping.domain.Ping, status: String) = PingResponse(
|
||||
status = status,
|
||||
timestamp = domainPing.timestamp.atOffset(ZoneOffset.UTC).format(formatter),
|
||||
service = properties.serviceName
|
||||
return EnhancedPingResponse(
|
||||
status = "pong",
|
||||
timestamp = domainPing.timestamp.atOffset(ZoneOffset.UTC).format(formatter),
|
||||
service = properties.serviceName,
|
||||
circuitBreakerState = "CLOSED",
|
||||
responseTime = elapsedMs
|
||||
)
|
||||
}
|
||||
|
||||
// Fallback
|
||||
@Suppress("unused", "UNUSED_PARAMETER")
|
||||
fun fallbackPing(simulate: Boolean, ex: Exception): EnhancedPingResponse {
|
||||
logger.warn("Circuit breaker fallback triggered: {}", ex.message)
|
||||
return EnhancedPingResponse(
|
||||
status = "fallback",
|
||||
timestamp = java.time.OffsetDateTime.now().format(formatter),
|
||||
service = properties.serviceNameFallback,
|
||||
circuitBreakerState = "OPEN",
|
||||
responseTime = 0
|
||||
)
|
||||
}
|
||||
// Neue Endpunkte
|
||||
|
||||
@GetMapping("/ping/health")
|
||||
override suspend fun healthCheck(): HealthResponse {
|
||||
return HealthResponse(
|
||||
status = "up",
|
||||
timestamp = java.time.OffsetDateTime.now().format(formatter),
|
||||
service = properties.serviceName,
|
||||
healthy = true
|
||||
)
|
||||
@GetMapping("/ping/public")
|
||||
override suspend fun publicPing(): PingResponse {
|
||||
val domainPing = pingUseCase.executePing("Public Ping")
|
||||
return createResponse(domainPing, "public-pong")
|
||||
}
|
||||
|
||||
@GetMapping("/ping/secure")
|
||||
@PreAuthorize("hasRole('MELD_USER') or hasRole('MELD_ADMIN')") // Beispiel-Rollen
|
||||
override suspend fun securePing(): PingResponse {
|
||||
val domainPing = pingUseCase.executePing("Secure Ping")
|
||||
return createResponse(domainPing, "secure-pong")
|
||||
}
|
||||
|
||||
@GetMapping("/ping/sync")
|
||||
override suspend fun syncPings(
|
||||
// Changed the parameter name to 'since' to match SyncManager convention
|
||||
@RequestParam(required = false, defaultValue = "0") since: Long
|
||||
): List<PingEvent> {
|
||||
return pingUseCase.getPingsSince(since).map {
|
||||
PingEvent(
|
||||
id = it.id.toString(),
|
||||
message = it.message,
|
||||
lastModified = it.timestamp.toEpochMilli()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// Helper
|
||||
private fun createResponse(domainPing: at.mocode.ping.domain.Ping, status: String) = PingResponse(
|
||||
status = status,
|
||||
timestamp = domainPing.timestamp.atOffset(ZoneOffset.UTC).format(formatter),
|
||||
service = properties.serviceName
|
||||
)
|
||||
|
||||
// Fallback
|
||||
@Suppress("unused", "UNUSED_PARAMETER")
|
||||
fun fallbackPing(simulate: Boolean, ex: Exception): EnhancedPingResponse {
|
||||
logger.warn("Circuit breaker fallback triggered: {}", ex.message)
|
||||
return EnhancedPingResponse(
|
||||
status = "fallback",
|
||||
timestamp = java.time.OffsetDateTime.now().format(formatter),
|
||||
service = properties.serviceNameFallback,
|
||||
circuitBreakerState = "OPEN",
|
||||
responseTime = 0
|
||||
)
|
||||
}
|
||||
|
||||
@GetMapping("/ping/health")
|
||||
override suspend fun healthCheck(): HealthResponse {
|
||||
return HealthResponse(
|
||||
status = "up",
|
||||
timestamp = java.time.OffsetDateTime.now().format(formatter),
|
||||
service = properties.serviceName,
|
||||
healthy = true
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,19 +1,19 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<configuration>
|
||||
<property name="LOG_PATTERN" value="%d{ISO8601} %-5level [%X{traceId:-}:%X{spanId:-}] %logger{36} - %msg%n"/>
|
||||
<property name="LOG_PATTERN" value="%d{ISO8601} %-5level [%X{traceId:-}:%X{spanId:-}] %logger{36} - %msg%n"/>
|
||||
|
||||
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
|
||||
<encoder>
|
||||
<pattern>${LOG_PATTERN}</pattern>
|
||||
</encoder>
|
||||
</appender>
|
||||
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
|
||||
<encoder>
|
||||
<pattern>${LOG_PATTERN}</pattern>
|
||||
</encoder>
|
||||
</appender>
|
||||
|
||||
<logger name="org.springframework" level="INFO"/>
|
||||
<logger name="org.springframework.web" level="INFO"/>
|
||||
<logger name="org.springframework.boot.actuate" level="INFO"/>
|
||||
<logger name="reactor.netty" level="WARN"/>
|
||||
<logger name="org.springframework" level="INFO"/>
|
||||
<logger name="org.springframework.web" level="INFO"/>
|
||||
<logger name="org.springframework.boot.actuate" level="INFO"/>
|
||||
<logger name="reactor.netty" level="WARN"/>
|
||||
|
||||
<root level="INFO">
|
||||
<appender-ref ref="CONSOLE"/>
|
||||
</root>
|
||||
<root level="INFO">
|
||||
<appender-ref ref="CONSOLE"/>
|
||||
</root>
|
||||
</configuration>
|
||||
|
||||
+94
-94
@@ -19,123 +19,123 @@ import kotlin.uuid.Uuid
|
||||
@OptIn(ExperimentalUuidApi::class)
|
||||
class PingServiceTest {
|
||||
|
||||
private val repository: PingRepository = mockk()
|
||||
private lateinit var service: PingService
|
||||
private val repository: PingRepository = mockk()
|
||||
private lateinit var service: PingService
|
||||
|
||||
@BeforeEach
|
||||
fun setUp() {
|
||||
service = PingService(repository)
|
||||
}
|
||||
@BeforeEach
|
||||
fun setUp() {
|
||||
service = PingService(repository)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `executePing should persist and return ping with given message`() {
|
||||
// Given
|
||||
every { repository.save(any()) } answers { firstArg() }
|
||||
@Test
|
||||
fun `executePing should persist and return ping with given message`() {
|
||||
// Given
|
||||
every { repository.save(any()) } answers { firstArg() }
|
||||
|
||||
// When
|
||||
val result = service.executePing("Hello")
|
||||
// When
|
||||
val result = service.executePing("Hello")
|
||||
|
||||
// Then
|
||||
assertThat(result.message).isEqualTo("Hello")
|
||||
verify { repository.save(any()) }
|
||||
}
|
||||
// Then
|
||||
assertThat(result.message).isEqualTo("Hello")
|
||||
verify { repository.save(any()) }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `executePing should generate a new UUID for each ping`() {
|
||||
// Given
|
||||
every { repository.save(any()) } answers { firstArg() }
|
||||
@Test
|
||||
fun `executePing should generate a new UUID for each ping`() {
|
||||
// Given
|
||||
every { repository.save(any()) } answers { firstArg() }
|
||||
|
||||
// When
|
||||
val result1 = service.executePing("Ping 1")
|
||||
val result2 = service.executePing("Ping 2")
|
||||
// When
|
||||
val result1 = service.executePing("Ping 1")
|
||||
val result2 = service.executePing("Ping 2")
|
||||
|
||||
// Then
|
||||
assertThat(result1.id).isNotEqualTo(result2.id)
|
||||
}
|
||||
// Then
|
||||
assertThat(result1.id).isNotEqualTo(result2.id)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `getPingHistory should delegate to repository and return all pings`() {
|
||||
// Given
|
||||
val pings = listOf(
|
||||
Ping(message = "Ping A"),
|
||||
Ping(message = "Ping B")
|
||||
)
|
||||
every { repository.findAll() } returns pings
|
||||
@Test
|
||||
fun `getPingHistory should delegate to repository and return all pings`() {
|
||||
// Given
|
||||
val pings = listOf(
|
||||
Ping(message = "Ping A"),
|
||||
Ping(message = "Ping B")
|
||||
)
|
||||
every { repository.findAll() } returns pings
|
||||
|
||||
// When
|
||||
val result = service.getPingHistory()
|
||||
// When
|
||||
val result = service.getPingHistory()
|
||||
|
||||
// Then
|
||||
assertThat(result).hasSize(2)
|
||||
assertThat(result.map { it.message }).containsExactly("Ping A", "Ping B")
|
||||
verify { repository.findAll() }
|
||||
}
|
||||
// Then
|
||||
assertThat(result).hasSize(2)
|
||||
assertThat(result.map { it.message }).containsExactly("Ping A", "Ping B")
|
||||
verify { repository.findAll() }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `getPingHistory should return empty list when no pings exist`() {
|
||||
// Given
|
||||
every { repository.findAll() } returns emptyList()
|
||||
@Test
|
||||
fun `getPingHistory should return empty list when no pings exist`() {
|
||||
// Given
|
||||
every { repository.findAll() } returns emptyList()
|
||||
|
||||
// When
|
||||
val result = service.getPingHistory()
|
||||
// When
|
||||
val result = service.getPingHistory()
|
||||
|
||||
// Then
|
||||
assertThat(result).isEmpty()
|
||||
}
|
||||
// Then
|
||||
assertThat(result).isEmpty()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `getPing should return ping by id`() {
|
||||
// Given
|
||||
val id = Uuid.generateV7()
|
||||
val ping = Ping(id = id, message = "Find me")
|
||||
every { repository.findById(id) } returns ping
|
||||
@Test
|
||||
fun `getPing should return ping by id`() {
|
||||
// Given
|
||||
val id = Uuid.generateV7()
|
||||
val ping = Ping(id = id, message = "Find me")
|
||||
every { repository.findById(id) } returns ping
|
||||
|
||||
// When
|
||||
val result = service.getPing(id)
|
||||
// When
|
||||
val result = service.getPing(id)
|
||||
|
||||
// Then
|
||||
assertThat(result).isEqualTo(ping)
|
||||
verify { repository.findById(id) }
|
||||
}
|
||||
// Then
|
||||
assertThat(result).isEqualTo(ping)
|
||||
verify { repository.findById(id) }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `getPing should return null when ping not found`() {
|
||||
// Given
|
||||
val id = Uuid.generateV7()
|
||||
every { repository.findById(id) } returns null
|
||||
@Test
|
||||
fun `getPing should return null when ping not found`() {
|
||||
// Given
|
||||
val id = Uuid.generateV7()
|
||||
every { repository.findById(id) } returns null
|
||||
|
||||
// When
|
||||
val result = service.getPing(id)
|
||||
// When
|
||||
val result = service.getPing(id)
|
||||
|
||||
// Then
|
||||
assertThat(result).isNull()
|
||||
}
|
||||
// Then
|
||||
assertThat(result).isNull()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `getPingsSince should return pings after given timestamp`() {
|
||||
// Given
|
||||
val timestamp = Instant.parse("2024-01-01T00:00:00Z").toEpochMilli()
|
||||
val ping = Ping(message = "Recent Ping", timestamp = Instant.parse("2024-06-01T00:00:00Z"))
|
||||
every { repository.findByTimestampAfter(any()) } returns listOf(ping)
|
||||
@Test
|
||||
fun `getPingsSince should return pings after given timestamp`() {
|
||||
// Given
|
||||
val timestamp = Instant.parse("2024-01-01T00:00:00Z").toEpochMilli()
|
||||
val ping = Ping(message = "Recent Ping", timestamp = Instant.parse("2024-06-01T00:00:00Z"))
|
||||
every { repository.findByTimestampAfter(any()) } returns listOf(ping)
|
||||
|
||||
// When
|
||||
val result = service.getPingsSince(timestamp)
|
||||
// When
|
||||
val result = service.getPingsSince(timestamp)
|
||||
|
||||
// Then
|
||||
assertThat(result).hasSize(1)
|
||||
assertThat(result[0].message).isEqualTo("Recent Ping")
|
||||
verify { repository.findByTimestampAfter(Instant.ofEpochMilli(timestamp)) }
|
||||
}
|
||||
// Then
|
||||
assertThat(result).hasSize(1)
|
||||
assertThat(result[0].message).isEqualTo("Recent Ping")
|
||||
verify { repository.findByTimestampAfter(Instant.ofEpochMilli(timestamp)) }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `getPingsSince should return empty list when no pings after timestamp`() {
|
||||
// Given
|
||||
every { repository.findByTimestampAfter(any()) } returns emptyList()
|
||||
@Test
|
||||
fun `getPingsSince should return empty list when no pings after timestamp`() {
|
||||
// Given
|
||||
every { repository.findByTimestampAfter(any()) } returns emptyList()
|
||||
|
||||
// When
|
||||
val result = service.getPingsSince(System.currentTimeMillis())
|
||||
// When
|
||||
val result = service.getPingsSince(System.currentTimeMillis())
|
||||
|
||||
// Then
|
||||
assertThat(result).isEmpty()
|
||||
}
|
||||
// Then
|
||||
assertThat(result).isEmpty()
|
||||
}
|
||||
}
|
||||
|
||||
+42
-42
@@ -35,54 +35,54 @@ import kotlin.uuid.toJavaUuid
|
||||
@OptIn(ExperimentalUuidApi::class)
|
||||
class PingRepositoryTest {
|
||||
|
||||
@Autowired
|
||||
private lateinit var repositoryAdapter: PingRepositoryAdapter
|
||||
@Autowired
|
||||
private lateinit var repositoryAdapter: PingRepositoryAdapter
|
||||
|
||||
// Wir nutzen das Repository direkt, um zu prüfen, ob JPA funktioniert
|
||||
@Autowired
|
||||
private lateinit var springDataRepository: SpringDataPingRepository
|
||||
// Wir nutzen das Repository direkt, um zu prüfen, ob JPA funktioniert
|
||||
@Autowired
|
||||
private lateinit var springDataRepository: SpringDataPingRepository
|
||||
|
||||
companion object {
|
||||
@Container
|
||||
val postgres = PostgreSQLContainer("postgres:16-alpine")
|
||||
.withDatabaseName("testdb")
|
||||
.withUsername("test")
|
||||
.withPassword("test")
|
||||
companion object {
|
||||
@Container
|
||||
val postgres = PostgreSQLContainer("postgres:16-alpine")
|
||||
.withDatabaseName("testdb")
|
||||
.withUsername("test")
|
||||
.withPassword("test")
|
||||
|
||||
@JvmStatic
|
||||
@DynamicPropertySource
|
||||
fun registerPgProperties(registry: DynamicPropertyRegistry) {
|
||||
registry.add("spring.datasource.url", postgres::getJdbcUrl)
|
||||
registry.add("spring.datasource.username", postgres::getUsername)
|
||||
registry.add("spring.datasource.password", postgres::getPassword)
|
||||
// Wichtig: Flyway muss laufen, um Tabellen zu erstellen
|
||||
registry.add("spring.flyway.enabled") { "true" }
|
||||
}
|
||||
@JvmStatic
|
||||
@DynamicPropertySource
|
||||
fun registerPgProperties(registry: DynamicPropertyRegistry) {
|
||||
registry.add("spring.datasource.url", postgres::getJdbcUrl)
|
||||
registry.add("spring.datasource.username", postgres::getUsername)
|
||||
registry.add("spring.datasource.password", postgres::getPassword)
|
||||
// Wichtig: Flyway muss laufen, um Tabellen zu erstellen
|
||||
registry.add("spring.flyway.enabled") { "true" }
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should save and load ping entity`() {
|
||||
// Given
|
||||
val pingId = Uuid.generateV7()
|
||||
val ping = Ping(
|
||||
id = pingId,
|
||||
message = "Integration Test Ping",
|
||||
timestamp = Instant.now()
|
||||
)
|
||||
@Test
|
||||
fun `should save and load ping entity`() {
|
||||
// Given
|
||||
val pingId = Uuid.generateV7()
|
||||
val ping = Ping(
|
||||
id = pingId,
|
||||
message = "Integration Test Ping",
|
||||
timestamp = Instant.now()
|
||||
)
|
||||
|
||||
// When
|
||||
repositoryAdapter.save(ping)
|
||||
// When
|
||||
repositoryAdapter.save(ping)
|
||||
|
||||
// Then (via Adapter)
|
||||
val loadedPing = repositoryAdapter.findById(pingId)
|
||||
assertThat(loadedPing).isNotNull
|
||||
assertThat(loadedPing?.message).isEqualTo("Integration Test Ping")
|
||||
assertThat(loadedPing?.id).isEqualTo(pingId)
|
||||
// Then (via Adapter)
|
||||
val loadedPing = repositoryAdapter.findById(pingId)
|
||||
assertThat(loadedPing).isNotNull
|
||||
assertThat(loadedPing?.message).isEqualTo("Integration Test Ping")
|
||||
assertThat(loadedPing?.id).isEqualTo(pingId)
|
||||
|
||||
// Then (Direct DB Check via Spring Data)
|
||||
// Fix: Use toJavaUuid() instead of UUID.fromString(pingId.toString())
|
||||
val entity = springDataRepository.findById(pingId.toJavaUuid())
|
||||
assertThat(entity).isPresent
|
||||
assertThat(entity.get().message).isEqualTo("Integration Test Ping")
|
||||
}
|
||||
// Then (Direct DB Check via Spring Data)
|
||||
// Fix: Use toJavaUuid() instead of UUID.fromString(pingId.toString())
|
||||
val entity = springDataRepository.findById(pingId.toJavaUuid())
|
||||
assertThat(entity).isPresent
|
||||
assertThat(entity.get().message).isEqualTo("Integration Test Ping")
|
||||
}
|
||||
}
|
||||
|
||||
+125
-125
@@ -37,8 +37,8 @@ import kotlin.uuid.ExperimentalUuidApi
|
||||
* Nutzt @WebMvcTest für einen isolierten MVC-Slice ohne echte Services oder DB.
|
||||
*/
|
||||
@WebMvcTest(
|
||||
controllers = [PingController::class],
|
||||
properties = ["spring.aop.proxy-target-class=true"]
|
||||
controllers = [PingController::class],
|
||||
properties = ["spring.aop.proxy-target-class=true"]
|
||||
)
|
||||
@ContextConfiguration(classes = [TestPingServiceApplication::class])
|
||||
@ActiveProfiles("test")
|
||||
@@ -47,153 +47,153 @@ import kotlin.uuid.ExperimentalUuidApi
|
||||
@OptIn(ExperimentalUuidApi::class)
|
||||
class PingControllerTest {
|
||||
|
||||
@Autowired
|
||||
private lateinit var mockMvc: MockMvc
|
||||
@Autowired
|
||||
private lateinit var mockMvc: MockMvc
|
||||
|
||||
@Autowired
|
||||
@Qualifier("pingUseCaseMock")
|
||||
private lateinit var pingUseCase: PingUseCase
|
||||
@Autowired
|
||||
@Qualifier("pingUseCaseMock")
|
||||
private lateinit var pingUseCase: PingUseCase
|
||||
|
||||
@Autowired
|
||||
private lateinit var properties: PingProperties
|
||||
@Autowired
|
||||
private lateinit var properties: PingProperties
|
||||
|
||||
@Autowired
|
||||
private lateinit var objectMapper: ObjectMapper
|
||||
@Autowired
|
||||
private lateinit var objectMapper: ObjectMapper
|
||||
|
||||
@TestConfiguration
|
||||
class PingControllerTestConfig {
|
||||
@Bean("pingUseCaseMock")
|
||||
@Primary
|
||||
fun pingUseCase(): PingUseCase = mockk(relaxed = true)
|
||||
@TestConfiguration
|
||||
class PingControllerTestConfig {
|
||||
@Bean("pingUseCaseMock")
|
||||
@Primary
|
||||
fun pingUseCase(): PingUseCase = mockk(relaxed = true)
|
||||
|
||||
@Bean
|
||||
@Primary
|
||||
fun pingRepositoryAdapter(): PingRepositoryAdapter = mockk(relaxed = true)
|
||||
@Bean
|
||||
@Primary
|
||||
fun pingRepositoryAdapter(): PingRepositoryAdapter = mockk(relaxed = true)
|
||||
|
||||
@Bean
|
||||
@Primary
|
||||
fun pingProperties(): PingProperties = mockk(relaxed = true)
|
||||
}
|
||||
@Bean
|
||||
@Primary
|
||||
fun pingProperties(): PingProperties = mockk(relaxed = true)
|
||||
}
|
||||
|
||||
@BeforeEach
|
||||
fun setUp() {
|
||||
clearMocks(pingUseCase, properties)
|
||||
every { properties.serviceName } returns "ping-service"
|
||||
every { properties.serviceNameFallback } returns "ping-service-fallback"
|
||||
}
|
||||
@BeforeEach
|
||||
fun setUp() {
|
||||
clearMocks(pingUseCase, properties)
|
||||
every { properties.serviceName } returns "ping-service"
|
||||
every { properties.serviceNameFallback } returns "ping-service-fallback"
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should return simple ping response`() {
|
||||
// Given
|
||||
every { pingUseCase.executePing(any()) } returns Ping(
|
||||
message = "Simple Ping",
|
||||
timestamp = Instant.parse("2023-10-01T10:00:00Z")
|
||||
)
|
||||
@Test
|
||||
fun `should return simple ping response`() {
|
||||
// Given
|
||||
every { pingUseCase.executePing(any()) } returns Ping(
|
||||
message = "Simple Ping",
|
||||
timestamp = Instant.parse("2023-10-01T10:00:00Z")
|
||||
)
|
||||
|
||||
// When
|
||||
val mvcResult: MvcResult = mockMvc.perform(get("/ping/simple"))
|
||||
.andExpect(request().asyncStarted())
|
||||
.andReturn()
|
||||
// When
|
||||
val mvcResult: MvcResult = mockMvc.perform(get("/ping/simple"))
|
||||
.andExpect(request().asyncStarted())
|
||||
.andReturn()
|
||||
|
||||
val result = mockMvc.perform(asyncDispatch(mvcResult))
|
||||
.andExpect(status().isOk)
|
||||
.andReturn()
|
||||
val result = mockMvc.perform(asyncDispatch(mvcResult))
|
||||
.andExpect(status().isOk)
|
||||
.andReturn()
|
||||
|
||||
// Then
|
||||
val json = objectMapper.readTree(result.response.contentAsString)
|
||||
assertThat(json["status"].asText()).isEqualTo("pong")
|
||||
assertThat(json["service"].asText()).isEqualTo(properties.serviceName)
|
||||
verify { pingUseCase.executePing("Simple Ping") }
|
||||
}
|
||||
// Then
|
||||
val json = objectMapper.readTree(result.response.contentAsString)
|
||||
assertThat(json["status"].asText()).isEqualTo("pong")
|
||||
assertThat(json["service"].asText()).isEqualTo(properties.serviceName)
|
||||
verify { pingUseCase.executePing("Simple Ping") }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should return enhanced ping response`() {
|
||||
// Given
|
||||
every { pingUseCase.executePing(any()) } returns Ping(
|
||||
message = "Enhanced Ping",
|
||||
timestamp = Instant.parse("2023-10-01T10:00:00Z")
|
||||
)
|
||||
@Test
|
||||
fun `should return enhanced ping response`() {
|
||||
// Given
|
||||
every { pingUseCase.executePing(any()) } returns Ping(
|
||||
message = "Enhanced Ping",
|
||||
timestamp = Instant.parse("2023-10-01T10:00:00Z")
|
||||
)
|
||||
|
||||
// When
|
||||
val mvcResult: MvcResult = mockMvc.perform(get("/ping/enhanced"))
|
||||
.andExpect(request().asyncStarted())
|
||||
.andReturn()
|
||||
// When
|
||||
val mvcResult: MvcResult = mockMvc.perform(get("/ping/enhanced"))
|
||||
.andExpect(request().asyncStarted())
|
||||
.andReturn()
|
||||
|
||||
val result = mockMvc.perform(asyncDispatch(mvcResult))
|
||||
.andExpect(status().isOk)
|
||||
.andReturn()
|
||||
val result = mockMvc.perform(asyncDispatch(mvcResult))
|
||||
.andExpect(status().isOk)
|
||||
.andReturn()
|
||||
|
||||
// Then
|
||||
val json = objectMapper.readTree(result.response.contentAsString)
|
||||
assertThat(json["status"].asText()).isEqualTo("pong")
|
||||
assertThat(json["service"].asText()).isEqualTo(properties.serviceName)
|
||||
verify { pingUseCase.executePing("Enhanced Ping") }
|
||||
}
|
||||
// Then
|
||||
val json = objectMapper.readTree(result.response.contentAsString)
|
||||
assertThat(json["status"].asText()).isEqualTo("pong")
|
||||
assertThat(json["service"].asText()).isEqualTo(properties.serviceName)
|
||||
verify { pingUseCase.executePing("Enhanced Ping") }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should return health check response with status up`() {
|
||||
// When
|
||||
val mvcResult: MvcResult = mockMvc.perform(get("/ping/health"))
|
||||
.andExpect(request().asyncStarted())
|
||||
.andReturn()
|
||||
@Test
|
||||
fun `should return health check response with status up`() {
|
||||
// When
|
||||
val mvcResult: MvcResult = mockMvc.perform(get("/ping/health"))
|
||||
.andExpect(request().asyncStarted())
|
||||
.andReturn()
|
||||
|
||||
val result = mockMvc.perform(asyncDispatch(mvcResult))
|
||||
.andExpect(status().isOk)
|
||||
.andReturn()
|
||||
val result = mockMvc.perform(asyncDispatch(mvcResult))
|
||||
.andExpect(status().isOk)
|
||||
.andReturn()
|
||||
|
||||
// Then
|
||||
val json = objectMapper.readTree(result.response.contentAsString)
|
||||
assertThat(json["status"].asText()).isEqualTo("up")
|
||||
assertThat(json["service"].asText()).isEqualTo(properties.serviceName)
|
||||
}
|
||||
// Then
|
||||
val json = objectMapper.readTree(result.response.contentAsString)
|
||||
assertThat(json["status"].asText()).isEqualTo("up")
|
||||
assertThat(json["service"].asText()).isEqualTo(properties.serviceName)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should return sync pings as list`() {
|
||||
// Given
|
||||
val timestamp = 1696154400000L
|
||||
every { pingUseCase.getPingsSince(timestamp) } returns listOf(
|
||||
Ping(
|
||||
message = "Sync Ping",
|
||||
timestamp = Instant.ofEpochMilli(timestamp + 1000)
|
||||
)
|
||||
)
|
||||
@Test
|
||||
fun `should return sync pings as list`() {
|
||||
// Given
|
||||
val timestamp = 1696154400000L
|
||||
every { pingUseCase.getPingsSince(timestamp) } returns listOf(
|
||||
Ping(
|
||||
message = "Sync Ping",
|
||||
timestamp = Instant.ofEpochMilli(timestamp + 1000)
|
||||
)
|
||||
)
|
||||
|
||||
// When
|
||||
val mvcResult: MvcResult = mockMvc.perform(get("/ping/sync").param("since", timestamp.toString()))
|
||||
.andExpect(request().asyncStarted())
|
||||
.andReturn()
|
||||
// When
|
||||
val mvcResult: MvcResult = mockMvc.perform(get("/ping/sync").param("since", timestamp.toString()))
|
||||
.andExpect(request().asyncStarted())
|
||||
.andReturn()
|
||||
|
||||
val result = mockMvc.perform(asyncDispatch(mvcResult))
|
||||
.andExpect(status().isOk)
|
||||
.andReturn()
|
||||
val result = mockMvc.perform(asyncDispatch(mvcResult))
|
||||
.andExpect(status().isOk)
|
||||
.andReturn()
|
||||
|
||||
// Then
|
||||
val json = objectMapper.readTree(result.response.contentAsString)
|
||||
assertThat(json.isArray).isTrue
|
||||
assertThat(json.size()).isEqualTo(1)
|
||||
assertThat(json[0]["message"].asText()).isEqualTo("Sync Ping")
|
||||
assertThat(json[0]["lastModified"].asLong()).isEqualTo(timestamp + 1000)
|
||||
verify { pingUseCase.getPingsSince(timestamp) }
|
||||
}
|
||||
// Then
|
||||
val json = objectMapper.readTree(result.response.contentAsString)
|
||||
assertThat(json.isArray).isTrue
|
||||
assertThat(json.size()).isEqualTo(1)
|
||||
assertThat(json[0]["message"].asText()).isEqualTo("Sync Ping")
|
||||
assertThat(json[0]["lastModified"].asLong()).isEqualTo(timestamp + 1000)
|
||||
verify { pingUseCase.getPingsSince(timestamp) }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should return empty list when no pings since timestamp`() {
|
||||
// Given
|
||||
val timestamp = System.currentTimeMillis()
|
||||
every { pingUseCase.getPingsSince(timestamp) } returns emptyList()
|
||||
@Test
|
||||
fun `should return empty list when no pings since timestamp`() {
|
||||
// Given
|
||||
val timestamp = System.currentTimeMillis()
|
||||
every { pingUseCase.getPingsSince(timestamp) } returns emptyList()
|
||||
|
||||
// When
|
||||
val mvcResult: MvcResult = mockMvc.perform(get("/ping/sync").param("since", timestamp.toString()))
|
||||
.andExpect(request().asyncStarted())
|
||||
.andReturn()
|
||||
// When
|
||||
val mvcResult: MvcResult = mockMvc.perform(get("/ping/sync").param("since", timestamp.toString()))
|
||||
.andExpect(request().asyncStarted())
|
||||
.andReturn()
|
||||
|
||||
val result = mockMvc.perform(asyncDispatch(mvcResult))
|
||||
.andExpect(status().isOk)
|
||||
.andReturn()
|
||||
val result = mockMvc.perform(asyncDispatch(mvcResult))
|
||||
.andExpect(status().isOk)
|
||||
.andReturn()
|
||||
|
||||
// Then
|
||||
val json = objectMapper.readTree(result.response.contentAsString)
|
||||
assertThat(json.isArray).isTrue
|
||||
assertThat(json.size()).isEqualTo(0)
|
||||
}
|
||||
// Then
|
||||
val json = objectMapper.readTree(result.response.contentAsString)
|
||||
assertThat(json.isArray).isTrue
|
||||
assertThat(json.size()).isEqualTo(0)
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -20,7 +20,7 @@ import org.springframework.context.annotation.Import
|
||||
*/
|
||||
@SpringBootApplication
|
||||
@ComponentScan(
|
||||
basePackages = ["at.mocode.infrastructure.security"]
|
||||
basePackages = ["at.mocode.infrastructure.security"]
|
||||
)
|
||||
@Import(PingController::class, PingProperties::class)
|
||||
@EnableAspectJAutoProxy(proxyTargetClass = true) // Erzwingt CGLIB Proxies für Controller
|
||||
|
||||
Reference in New Issue
Block a user