diff --git a/.github/workflows/build-v5.yml b/.github/workflows/build-v5.yml index 0d1602ab..2be83922 100644 --- a/.github/workflows/build-v5.yml +++ b/.github/workflows/build-v5.yml @@ -23,7 +23,8 @@ jobs: steps: - uses: actions/checkout@v4 with: - fetch-depth: 0 + ref: ${{ github.sha }} + base: ${{ github.event.before }} - name: Detect changes id: filter diff --git a/apps/converter/build.gradle.kts b/apps/converter/build.gradle.kts index 93008c16..76071c00 100644 --- a/apps/converter/build.gradle.kts +++ b/apps/converter/build.gradle.kts @@ -57,6 +57,8 @@ dependencies { testImplementation(project(":shared:common", configuration = "testArtifacts")) testImplementation("org.jetbrains.kotlinx:kotlinx-coroutines-test:1.10.2") + val exposedVersion = "0.61.0" + testImplementation("org.jetbrains.exposed:exposed-core:${exposedVersion}") } tasks.test { diff --git a/apps/converter/src/test/kotlin/no/iktdev/mediaprocessing/converter/ConverterApplicationTest.kt b/apps/converter/src/test/kotlin/no/iktdev/mediaprocessing/converter/ConverterApplicationTest.kt index 5b3c1e54..477bed99 100644 --- a/apps/converter/src/test/kotlin/no/iktdev/mediaprocessing/converter/ConverterApplicationTest.kt +++ b/apps/converter/src/test/kotlin/no/iktdev/mediaprocessing/converter/ConverterApplicationTest.kt @@ -1,15 +1,22 @@ package no.iktdev.mediaprocessing.converter +import io.mockk.every +import io.mockk.mockk +import io.mockk.mockkObject +import io.mockk.verify import no.iktdev.eventi.models.Task import no.iktdev.mediaprocessing.shared.common.TestBase import no.iktdev.mediaprocessing.shared.common.config.DatasourceConfiguration import no.iktdev.mediaprocessing.shared.common.stores.TaskStore +import org.jetbrains.exposed.sql.Database import org.junit.jupiter.api.Assertions.assertNotNull import org.junit.jupiter.api.Test import org.junit.jupiter.api.extension.ExtendWith +import org.springframework.boot.builder.SpringApplicationBuilder import org.springframework.boot.test.context.SpringBootTest import org.springframework.test.context.TestPropertySource import org.springframework.test.context.junit.jupiter.SpringExtension +import javax.sql.DataSource @SpringBootTest( classes = [ConverterApplication::class, @@ -18,7 +25,7 @@ import org.springframework.test.context.junit.jupiter.SpringExtension ) @TestPropertySource(properties = ["spring.flyway.enabled=true"]) @ExtendWith(SpringExtension::class) -class ConverterApplicationTest: TestBase() { +class ConverterApplicationTest : TestBase() { data class TestTask( val success: Boolean @@ -47,4 +54,28 @@ class ConverterApplicationTest: TestBase() { assertNotNull(tasksAfter) assert(tasksAfter.isNotEmpty()) } + + @Test + fun `ExposedInitializer should connect to database`() { + mockkObject(Database) + + every { + Database.connect( + any(), + any(), + any(), + any(), + any() + ) + } returns mockk() + + + val context = SpringApplicationBuilder(ConverterApplication::class.java) + .properties("spring.main.web-application-type=none") + .run() + + verify(exactly = 1) { Database.connect(any(), any(), any(), any(), any()) } + } + + } diff --git a/apps/coordinator/build.gradle.kts b/apps/coordinator/build.gradle.kts index 8bc91a7a..18194d22 100644 --- a/apps/coordinator/build.gradle.kts +++ b/apps/coordinator/build.gradle.kts @@ -82,7 +82,8 @@ dependencies { testImplementation("org.mockito:mockito-junit-jupiter:5.11.0") testImplementation(project(":shared:common", configuration = "testArtifacts")) testImplementation("org.springframework.boot:spring-boot-starter-test") - + val exposedVersion = "0.61.0" + testImplementation("org.jetbrains.exposed:exposed-core:${exposedVersion}") } diff --git a/apps/coordinator/src/main/kotlin/no/iktdev/mediaprocessing/coordinator/EventPoller.kt b/apps/coordinator/src/main/kotlin/no/iktdev/mediaprocessing/coordinator/EventPoller.kt index c7d34049..1a8d113b 100644 --- a/apps/coordinator/src/main/kotlin/no/iktdev/mediaprocessing/coordinator/EventPoller.kt +++ b/apps/coordinator/src/main/kotlin/no/iktdev/mediaprocessing/coordinator/EventPoller.kt @@ -2,30 +2,44 @@ package no.iktdev.mediaprocessing.coordinator import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.Job import kotlinx.coroutines.launch import no.iktdev.eventi.events.EventDispatcher import no.iktdev.eventi.events.EventPollerImplementation import no.iktdev.eventi.events.SequenceDispatchQueue import no.iktdev.mediaprocessing.shared.common.stores.EventStore -import org.springframework.boot.ApplicationArguments -import org.springframework.boot.ApplicationRunner +import org.springframework.context.SmartLifecycle +import org.springframework.context.annotation.DependsOn import org.springframework.stereotype.Component @Component class PollerAdministrator( - private val eventPoller: EventPoller, -): ApplicationRunner { - override fun run(args: ApplicationArguments?) { - CoroutineScope(Dispatchers.Default).launch { + private val eventPoller: EventPoller +) : SmartLifecycle { + + private var running = false + + var job: Job? = null + override fun start() { + job = CoroutineScope(Dispatchers.Default).launch { eventPoller.start() } + running = true } + + override fun stop() { + job?.cancel() + } + + override fun isRunning() = running } + val sequenceDispatcher = SequenceDispatchQueue(8) val dispatcher = EventDispatcher(eventStore = EventStore) @Component +@DependsOn("ExposedInit") class EventPoller: EventPollerImplementation(eventStore = EventStore, dispatchQueue = sequenceDispatcher, dispatcher = dispatcher) { } diff --git a/apps/coordinator/src/test/kotlin/no/iktdev/mediaprocessing/CoordinatorApplicationTest.kt b/apps/coordinator/src/test/kotlin/no/iktdev/mediaprocessing/CoordinatorApplicationTest.kt new file mode 100644 index 00000000..6ffeeaa7 --- /dev/null +++ b/apps/coordinator/src/test/kotlin/no/iktdev/mediaprocessing/CoordinatorApplicationTest.kt @@ -0,0 +1,45 @@ +package no.iktdev.mediaprocessing + +import io.mockk.every +import io.mockk.mockk +import io.mockk.mockkObject +import io.mockk.verify +import no.iktdev.mediaprocessing.coordinator.CoordinatorApplication +import org.jetbrains.exposed.sql.Database +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.extension.ExtendWith +import org.springframework.boot.builder.SpringApplicationBuilder +import org.springframework.boot.test.context.SpringBootTest +import org.springframework.test.context.junit.jupiter.SpringExtension +import javax.sql.DataSource + +@ExtendWith(SpringExtension::class) +@SpringBootTest( + classes = [CoordinatorApplication::class], + properties = ["spring.flyway.enabled=true"] +) +class CoordinatorApplicationTest { + + @Test + fun `ExposedInitializer should connect to database`() { + mockkObject(Database) + + every { + Database.connect( + any(), + any(), + any(), + any(), + any() + ) + } returns mockk() + + + val context = SpringApplicationBuilder(CoordinatorApplication::class.java) + .properties("spring.main.web-application-type=none") + .run() + + verify(exactly = 1) { Database.connect(any(), any(), any(), any(), any()) } + } + +} \ No newline at end of file diff --git a/apps/processer/build.gradle.kts b/apps/processer/build.gradle.kts index 65a42222..87596127 100644 --- a/apps/processer/build.gradle.kts +++ b/apps/processer/build.gradle.kts @@ -49,20 +49,33 @@ dependencies { implementation(project(mapOf("path" to ":shared:common"))) - - testImplementation(platform("org.junit:junit-bom:5.9.1")) - testImplementation("org.junit.jupiter:junit-jupiter") - - testImplementation("io.mockk:mockk:1.12.0") - testImplementation("com.h2database:h2:1.4.200") - testImplementation("org.assertj:assertj-core:3.4.1") - - testImplementation("org.junit.jupiter:junit-jupiter-api:5.7.2") - testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.7.2") - testImplementation("io.kotlintest:kotlintest-assertions:3.3.2") - testImplementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.3.0") - testImplementation("org.jetbrains.kotlinx:kotlinx-coroutines-test:1.10.2") implementation(kotlin("stdlib-jdk8")) + + // --- Spring Boot test stack (inkluderer JUnit 5.10, Mockito, AssertJ, etc.) --- + testImplementation("org.springframework.boot:spring-boot-starter-test") { + exclude(group = "org.mockito") // valgfritt hvis du kun bruker MockK + } + + // --- MockK (Kotlin mocking) --- + testImplementation("io.mockk:mockk:1.13.8") + + // --- Coroutines test utilities --- + testImplementation("org.jetbrains.kotlinx:kotlinx-coroutines-test:1.7.3") + + // --- H2 for database testing --- + testImplementation("com.h2database:h2:2.2.224") + + // --- Optional: AssertJ (Spring Boot inkluderer AssertJ, men du kan eksplisitt legge til) --- + // testImplementation("org.assertj:assertj-core:3.24.2") + + // --- Optional: JUnit params (brukes ofte) --- + testImplementation("org.junit.jupiter:junit-jupiter-params") + + // --- Hvis du trenger test artifacts fra shared:common --- + testImplementation(project(":shared:common", configuration = "testArtifacts")) + + val exposedVersion = "0.61.0" + testImplementation("org.jetbrains.exposed:exposed-core:${exposedVersion}") } tasks.test { diff --git a/apps/processer/src/test/kotlin/no/iktdev/mediaprocessing/processer/ProcesserApplicationTest.kt b/apps/processer/src/test/kotlin/no/iktdev/mediaprocessing/processer/ProcesserApplicationTest.kt new file mode 100644 index 00000000..b4a80e2d --- /dev/null +++ b/apps/processer/src/test/kotlin/no/iktdev/mediaprocessing/processer/ProcesserApplicationTest.kt @@ -0,0 +1,44 @@ +package no.iktdev.mediaprocessing.processer + +import io.mockk.every +import io.mockk.mockk +import io.mockk.mockkObject +import io.mockk.verify +import org.jetbrains.exposed.sql.Database +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.extension.ExtendWith +import org.springframework.boot.builder.SpringApplicationBuilder +import org.springframework.boot.test.context.SpringBootTest +import org.springframework.test.context.junit.jupiter.SpringExtension +import javax.sql.DataSource + +@ExtendWith(SpringExtension::class) +@SpringBootTest( + classes = [ProcesserApplication::class], + properties = ["spring.flyway.enabled=true"] +) +class ProcesserApplicationTest { + + @Test + fun `ExposedInitializer should connect to database`() { + mockkObject(Database) + + every { + Database.connect( + any(), + any(), + any(), + any(), + any() + ) + } returns mockk() + + + val context = SpringApplicationBuilder(ProcesserApplication::class.java) + .properties("spring.main.web-application-type=none") + .run() + + verify(exactly = 1) { Database.connect(any(), any(), any(), any(), any()) } + } + +} diff --git a/shared/common/src/main/kotlin/no/iktdev/mediaprocessing/shared/common/DatabaseApplication.kt b/shared/common/src/main/kotlin/no/iktdev/mediaprocessing/shared/common/DatabaseApplication.kt index b6f9eef2..9ed6ade6 100644 --- a/shared/common/src/main/kotlin/no/iktdev/mediaprocessing/shared/common/DatabaseApplication.kt +++ b/shared/common/src/main/kotlin/no/iktdev/mediaprocessing/shared/common/DatabaseApplication.kt @@ -1,8 +1,8 @@ package no.iktdev.mediaprocessing.shared.common +import mu.KotlinLogging import org.jetbrains.exposed.sql.Database -import org.springframework.boot.ApplicationArguments -import org.springframework.boot.ApplicationRunner +import org.springframework.beans.factory.InitializingBean import org.springframework.boot.autoconfigure.SpringBootApplication import org.springframework.boot.runApplication import org.springframework.context.annotation.ComponentScan @@ -17,12 +17,14 @@ abstract class DatabaseApplication { } } -@Component +@Component("ExposedInit") class ExposedInitializer( private val dataSource: DataSource -) : ApplicationRunner { +) : InitializingBean { + private val log = KotlinLogging.logger {} - override fun run(args: ApplicationArguments?) { + override fun afterPropertiesSet() { + log.info { "Starting database connection" } Database.connect(dataSource) } }