From f85fbde89d3938ed949b06e2b8df4177aa039bd5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Brage=20Skj=C3=B8nborg?= Date: Fri, 2 Jan 2026 01:48:57 +0100 Subject: [PATCH] Wip 3 - Added one missing TaskStore + Testing Expiry --- .../MediaCreateMetadataSearchTaskListener.kt | 5 +- .../no/iktdev/mediaprocessing/MockData.kt | 6 +- ...ediaCreateCoverDownloadTaskListenerTest.kt | 35 +++- ...diaCreateMetadataSearchTaskListenerTest.kt | 198 +++++++++++++++++- 4 files changed, 238 insertions(+), 6 deletions(-) diff --git a/apps/coordinator/src/main/kotlin/no/iktdev/mediaprocessing/coordinator/listeners/events/MediaCreateMetadataSearchTaskListener.kt b/apps/coordinator/src/main/kotlin/no/iktdev/mediaprocessing/coordinator/listeners/events/MediaCreateMetadataSearchTaskListener.kt index c7d7861d..cf63a4d2 100644 --- a/apps/coordinator/src/main/kotlin/no/iktdev/mediaprocessing/coordinator/listeners/events/MediaCreateMetadataSearchTaskListener.kt +++ b/apps/coordinator/src/main/kotlin/no/iktdev/mediaprocessing/coordinator/listeners/events/MediaCreateMetadataSearchTaskListener.kt @@ -9,6 +9,7 @@ import no.iktdev.mediaprocessing.shared.common.event_task_contract.events.Metada import no.iktdev.mediaprocessing.shared.common.event_task_contract.events.MetadataSearchTaskCreatedEvent import no.iktdev.mediaprocessing.shared.common.event_task_contract.tasks.MetadataSearchTask import no.iktdev.mediaprocessing.shared.common.stores.TaskStore +import org.jetbrains.annotations.VisibleForTesting import org.springframework.stereotype.Component import java.util.* import java.util.concurrent.ConcurrentHashMap @@ -20,7 +21,8 @@ import java.util.concurrent.TimeUnit @ListenerOrder(5) class MediaCreateMetadataSearchTaskListener: EventListener() { - private val scheduledExpiries = ConcurrentHashMap>() + @VisibleForTesting + internal val scheduledExpiries = ConcurrentHashMap>() private val scheduler = Executors.newScheduledThreadPool(1) override fun onEvent( @@ -50,6 +52,7 @@ class MediaCreateMetadataSearchTaskListener: EventListener() { collection = useEvent.data.parsedCollection ) ).derivedOf(useEvent) + TaskStore.persist(task) val finalResult = MetadataSearchTaskCreatedEvent(task.taskId).derivedOf(useEvent) scheduleTaskExpiry(task.taskId, finalResult.eventId, task.referenceId) return finalResult diff --git a/apps/coordinator/src/test/kotlin/no/iktdev/mediaprocessing/MockData.kt b/apps/coordinator/src/test/kotlin/no/iktdev/mediaprocessing/MockData.kt index 79534cdc..5b025250 100644 --- a/apps/coordinator/src/test/kotlin/no/iktdev/mediaprocessing/MockData.kt +++ b/apps/coordinator/src/test/kotlin/no/iktdev/mediaprocessing/MockData.kt @@ -22,7 +22,7 @@ object MockData { ) ) - fun metadataEvent(derivedFrom: Event): List { + fun metadataEvent(derivedFrom: Event, source: String = "potetland", coverUrl: String = "cover.jpg"): List { val dummyTask = DummyTask().derivedOf(derivedFrom) val create = MetadataSearchTaskCreatedEvent(dummyTask.taskId).derivedOf(derivedFrom) @@ -34,9 +34,9 @@ object MockData { advancedScore = 10, sourceWeight = 1f, data = MetadataSearchResultEvent.SearchResult.MetadataResult( - source = "test", + source = source, title = "MyCollection", - cover = "cover.jpg", + cover = coverUrl, type = MediaType.Movie, summary = listOf( MetadataSearchResultEvent.SearchResult.MetadataResult.Summary( diff --git a/apps/coordinator/src/test/kotlin/no/iktdev/mediaprocessing/coordinator/listeners/events/MediaCreateCoverDownloadTaskListenerTest.kt b/apps/coordinator/src/test/kotlin/no/iktdev/mediaprocessing/coordinator/listeners/events/MediaCreateCoverDownloadTaskListenerTest.kt index 7518f488..cded3f54 100644 --- a/apps/coordinator/src/test/kotlin/no/iktdev/mediaprocessing/coordinator/listeners/events/MediaCreateCoverDownloadTaskListenerTest.kt +++ b/apps/coordinator/src/test/kotlin/no/iktdev/mediaprocessing/coordinator/listeners/events/MediaCreateCoverDownloadTaskListenerTest.kt @@ -1,5 +1,38 @@ package no.iktdev.mediaprocessing.coordinator.listeners.events -class MediaCreateCoverDownloadTaskListenerTest { +import io.mockk.slot +import io.mockk.verify +import no.iktdev.mediaprocessing.MockData.metadataEvent +import no.iktdev.mediaprocessing.TestBase +import no.iktdev.mediaprocessing.shared.common.event_task_contract.events.CoverDownloadTaskCreatedEvent +import no.iktdev.mediaprocessing.shared.common.event_task_contract.tasks.CoverDownloadTask +import no.iktdev.mediaprocessing.shared.common.stores.TaskStore +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + + +class MediaCreateCoverDownloadTaskListenerTest: TestBase() { + + val listener = MediaCreateCoverDownloadTaskListener() + + @Test + fun success1() { + val started = defaultStartEvent() + val metadata = metadataEvent(started, coverUrl = "http://example.com/fancy.jpg") + + val history = listOf(started, *metadata.toTypedArray()) + + val result = listener.onEvent(metadata.last(), history) + assertThat(result is CoverDownloadTaskCreatedEvent) + val slot = slot() + + verify(exactly = 1) { + TaskStore.persist(capture(slot)) + } + + val storeTask = slot.captured + assertThat(storeTask.data.url).isEqualTo("http://example.com/fancy.jpg") + assertThat(storeTask.data.outputFileName).isEqualTo("MyCollection-potetland") + } } \ No newline at end of file diff --git a/apps/coordinator/src/test/kotlin/no/iktdev/mediaprocessing/coordinator/listeners/events/MediaCreateMetadataSearchTaskListenerTest.kt b/apps/coordinator/src/test/kotlin/no/iktdev/mediaprocessing/coordinator/listeners/events/MediaCreateMetadataSearchTaskListenerTest.kt index 64aaec06..df440fae 100644 --- a/apps/coordinator/src/test/kotlin/no/iktdev/mediaprocessing/coordinator/listeners/events/MediaCreateMetadataSearchTaskListenerTest.kt +++ b/apps/coordinator/src/test/kotlin/no/iktdev/mediaprocessing/coordinator/listeners/events/MediaCreateMetadataSearchTaskListenerTest.kt @@ -1,5 +1,201 @@ package no.iktdev.mediaprocessing.coordinator.listeners.events -class MediaCreateMetadataSearchTaskListenerTest { +import io.mockk.slot +import io.mockk.verify +import no.iktdev.eventi.models.Event +import no.iktdev.eventi.models.store.TaskStatus +import no.iktdev.mediaprocessing.MockData.mediaParsedEvent +import no.iktdev.mediaprocessing.TestBase +import no.iktdev.mediaprocessing.shared.common.event_task_contract.events.MetadataSearchResultEvent +import no.iktdev.mediaprocessing.shared.common.event_task_contract.events.MetadataSearchTaskCreatedEvent +import no.iktdev.mediaprocessing.shared.common.event_task_contract.tasks.MetadataSearchTask +import no.iktdev.mediaprocessing.shared.common.model.MediaType +import no.iktdev.mediaprocessing.shared.common.stores.TaskStore +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.DisplayName +import org.junit.jupiter.api.Test + +class MediaCreateMetadataSearchTaskListenerTest : TestBase() { + + val listener = MediaCreateMetadataSearchTaskListener() + + @Test + fun success1() { + val started = defaultStartEvent() + val parsedInfo = mediaParsedEvent("Baking Bread", "Baking Bread - S01E01 - Flour", MediaType.Serie) + .derivedOf(started) + + val history = listOf(started, parsedInfo) + val result = listener.onEvent(parsedInfo, history) + assertThat(result is MetadataSearchTaskCreatedEvent) + val slot = slot() + + verify(exactly = 1) { + TaskStore.persist(capture(slot)) + } + + val storeTask = slot.captured + assertThat(storeTask.data.collection).isEqualTo("Baking Bread") + assertThat(storeTask.data.searchTitles).hasSize(2) + assertThat(storeTask.data.searchTitles).isEqualTo( + listOf( + "Baking Bread", + "Baking Bread - S01E01 - Flour" + ) + ) + } + + @DisplayName( + """ + Hvis event ikke er MediaParsedInfoEvent + Når onEvent kalles + Så: + Returneres null + """ + ) + @Test + fun ignored1() { + // Hvis + val listener = MediaCreateMetadataSearchTaskListener() + val event = MetadataSearchResultEvent( + status = TaskStatus.Completed + ) + val history = emptyList() + + // Når + val result = listener.onEvent(event, history) + + // Så + assertThat(result).isNull() + verify(exactly = 0) { TaskStore.persist(any()) } + + } + + @DisplayName( + """ + Hvis event ikke er MediaParsedInfoEvent + Når onEvent kalles + Så: + Returneres null + """ + ) + @Test + fun `hvis event ikke er MediaParsedInfoEvent saa returneres null`() { + // Hvis + val listener = MediaCreateMetadataSearchTaskListener() + val event = MetadataSearchResultEvent(status = TaskStatus.Completed) + val history = emptyList() + + // Når + val result = listener.onEvent(event, history) + + // Så + verify(exactly = 0) { TaskStore.persist(any()) } + assertThat(result).isNull() + } + + @DisplayName( + """ + Hvis MediaParsedInfoEvent mottas + Når onEvent kalles + Så: + Opprettes MetadataSearchTask + Og timeout planlegges + """ + ) + @Test + fun `hvis parsed event saa opprettes task og timeout planlegges`() { + // Hvis + val listener = MediaCreateMetadataSearchTaskListener() + val started = defaultStartEvent() + val parsed = mediaParsedEvent( + "Baking Bread", + "Baking Bread - S01E01 - Flour", + MediaType.Serie + ).derivedOf(started) + + val history = listOf(started, parsed) + + // Når + val result = listener.onEvent(parsed, history) + + // Så + assertThat(result).isInstanceOf(MetadataSearchTaskCreatedEvent::class.java) + + val slot = slot() + verify(exactly = 1) { TaskStore.persist(capture(slot)) } + + val taskId = slot.captured.taskId + assertThat(listener.scheduledExpiries).containsKey(taskId) + } + + @DisplayName(""" + Hvis MetadataSearchTaskCreatedEvent re-spilles + Og historikken inneholder MetadataSearchResultEvent for samme task + Når onEvent kalles + Så: + Skal timeout ikke planlegges + """) + @Test + fun `hvis replay og result finnes saa planlegges ikke timeout`() { + // Hvis + val listener = MediaCreateMetadataSearchTaskListener() + + val started = defaultStartEvent() + val parsed = mediaParsedEvent( + "Baking Bread", + "Baking Bread - S01E01 - Flour", + MediaType.Serie + ).apply { derivedOf(started) } + + val task = MetadataSearchTask( + MetadataSearchTask.SearchData( + searchTitles = parsed.data.parsedSearchTitles, + collection = parsed.data.parsedCollection + ) + ).derivedOf(parsed) + + val created = MetadataSearchTaskCreatedEvent(task.taskId).derivedOf(parsed) + + val resultEvent = MetadataSearchResultEvent( + status = TaskStatus.Completed, + results = emptyList() + ).producedFrom(task) + + val history = listOf(started, parsed, created, resultEvent) + + // Når + listener.onEvent(created, history) + + // Så + assertThat(listener.scheduledExpiries).doesNotContainKey(task.taskId) + } + + @DisplayName(""" + Hvis flere MediaParsedInfoEvent mottas + Når onEvent kalles for hver + Så: + Opprettes én MetadataSearchTask per event +""") + @Test + fun `hvis flere parsed events saa opprettes en task per event`() { + // Hvis + val listener = MediaCreateMetadataSearchTaskListener() + + val started = defaultStartEvent() + + val parsed1 = mediaParsedEvent("A", "A - E01", MediaType.Serie).derivedOf(started) + val parsed2 = mediaParsedEvent("B", "B - E01", MediaType.Serie).derivedOf(started) + + // Når + listener.onEvent(parsed1, listOf(started, parsed1)) + listener.onEvent(parsed2, listOf(started, parsed2)) + + // Så + verify(exactly = 2) { TaskStore.persist(any()) } + } + + + } \ No newline at end of file