Fix 2
This commit is contained in:
parent
767372be54
commit
4e7fd5a9f7
@ -61,6 +61,9 @@ dependencies {
|
|||||||
testImplementation("io.github.classgraph:classgraph:4.8.184")
|
testImplementation("io.github.classgraph:classgraph:4.8.184")
|
||||||
testImplementation("org.jetbrains.kotlinx:kotlinx-coroutines-test:1.10.2")
|
testImplementation("org.jetbrains.kotlinx:kotlinx-coroutines-test:1.10.2")
|
||||||
testImplementation("io.mockk:mockk:1.13.9")
|
testImplementation("io.mockk:mockk:1.13.9")
|
||||||
|
testImplementation("com.fasterxml.jackson.module:jackson-module-kotlin:2.17.0")
|
||||||
|
testImplementation("org.reflections:reflections:0.10.2")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -6,7 +6,7 @@ import no.iktdev.eventi.models.store.TaskStatus
|
|||||||
/**
|
/**
|
||||||
* Base class, should not be serialized into
|
* Base class, should not be serialized into
|
||||||
*/
|
*/
|
||||||
abstract class TaskResultEvent(
|
open class TaskResultEvent(
|
||||||
open val status: TaskStatus,
|
val status: TaskStatus,
|
||||||
open val error: String? = null
|
val error: String? = null
|
||||||
) : Event()
|
) : Event()
|
||||||
|
|||||||
@ -3,10 +3,10 @@ package no.iktdev.mediaprocessing.shared.common.event_task_contract.events
|
|||||||
import no.iktdev.eventi.models.store.TaskStatus
|
import no.iktdev.eventi.models.store.TaskStatus
|
||||||
import no.iktdev.mediaprocessing.shared.common.event_task_contract.TaskResultEvent
|
import no.iktdev.mediaprocessing.shared.common.event_task_contract.TaskResultEvent
|
||||||
|
|
||||||
data class ConvertTaskResultEvent(
|
class ConvertTaskResultEvent(
|
||||||
val data: ConvertedData?,
|
val data: ConvertedData?,
|
||||||
override val status: TaskStatus,
|
status: TaskStatus,
|
||||||
override val error: String? = null,
|
error: String? = null,
|
||||||
): TaskResultEvent(status, error) {
|
): TaskResultEvent(status, error) {
|
||||||
data class ConvertedData(
|
data class ConvertedData(
|
||||||
val language: String,
|
val language: String,
|
||||||
|
|||||||
@ -4,8 +4,8 @@ import com.google.gson.JsonObject
|
|||||||
import no.iktdev.eventi.models.store.TaskStatus
|
import no.iktdev.eventi.models.store.TaskStatus
|
||||||
import no.iktdev.mediaprocessing.shared.common.event_task_contract.TaskResultEvent
|
import no.iktdev.mediaprocessing.shared.common.event_task_contract.TaskResultEvent
|
||||||
|
|
||||||
data class CoordinatorReadStreamsResultEvent(
|
class CoordinatorReadStreamsResultEvent(
|
||||||
val data: JsonObject? = null,
|
val data: JsonObject? = null,
|
||||||
override val status: TaskStatus,
|
status: TaskStatus,
|
||||||
override val error: String? = null
|
error: String? = null
|
||||||
) : TaskResultEvent(status, error)
|
) : TaskResultEvent(status, error)
|
||||||
|
|||||||
@ -3,10 +3,10 @@ package no.iktdev.mediaprocessing.shared.common.event_task_contract.events
|
|||||||
import no.iktdev.eventi.models.store.TaskStatus
|
import no.iktdev.eventi.models.store.TaskStatus
|
||||||
import no.iktdev.mediaprocessing.shared.common.event_task_contract.TaskResultEvent
|
import no.iktdev.mediaprocessing.shared.common.event_task_contract.TaskResultEvent
|
||||||
|
|
||||||
data class CoverDownloadResultEvent(
|
class CoverDownloadResultEvent(
|
||||||
val data: CoverDownloadedData? = null,
|
val data: CoverDownloadedData? = null,
|
||||||
override val status: TaskStatus,
|
status: TaskStatus,
|
||||||
override val error: String? = null
|
error: String? = null
|
||||||
) : TaskResultEvent(status, error){
|
) : TaskResultEvent(status, error){
|
||||||
data class CoverDownloadedData(
|
data class CoverDownloadedData(
|
||||||
val source: String,
|
val source: String,
|
||||||
|
|||||||
@ -6,11 +6,11 @@ import no.iktdev.mediaprocessing.shared.common.event_task_contract.TaskResultEve
|
|||||||
import no.iktdev.mediaprocessing.shared.common.model.MediaType
|
import no.iktdev.mediaprocessing.shared.common.model.MediaType
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
data class MetadataSearchResultEvent(
|
class MetadataSearchResultEvent(
|
||||||
val results: List<SearchResult> = emptyList(),
|
val results: List<SearchResult> = emptyList(),
|
||||||
val recommended: SearchResult? = null,
|
val recommended: SearchResult? = null,
|
||||||
override val status: TaskStatus,
|
status: TaskStatus,
|
||||||
override val error: String? = null
|
error: String? = null
|
||||||
) : TaskResultEvent(status, error) {
|
) : TaskResultEvent(status, error) {
|
||||||
data class SearchResult(
|
data class SearchResult(
|
||||||
val simpleScore: Int,
|
val simpleScore: Int,
|
||||||
|
|||||||
@ -4,13 +4,13 @@ import no.iktdev.eventi.models.store.TaskStatus
|
|||||||
import no.iktdev.mediaprocessing.shared.common.event_task_contract.TaskResultEvent
|
import no.iktdev.mediaprocessing.shared.common.event_task_contract.TaskResultEvent
|
||||||
import no.iktdev.mediaprocessing.shared.common.model.MigrateStatus
|
import no.iktdev.mediaprocessing.shared.common.model.MigrateStatus
|
||||||
|
|
||||||
data class MigrateContentToStoreTaskResultEvent(
|
class MigrateContentToStoreTaskResultEvent(
|
||||||
val collection: String,
|
val collection: String,
|
||||||
val videoMigrate: FileMigration,
|
val videoMigrate: FileMigration,
|
||||||
val subtitleMigrate: List<SubtitleMigration>,
|
val subtitleMigrate: List<SubtitleMigration>,
|
||||||
val coverMigrate: List<FileMigration>,
|
val coverMigrate: List<FileMigration>,
|
||||||
override val status: TaskStatus,
|
status: TaskStatus,
|
||||||
override val error: String? = null
|
error: String? = null
|
||||||
) : TaskResultEvent(status, error) {
|
) : TaskResultEvent(status, error) {
|
||||||
data class FileMigration(
|
data class FileMigration(
|
||||||
val storedUri: String?,
|
val storedUri: String?,
|
||||||
|
|||||||
@ -3,10 +3,10 @@ package no.iktdev.mediaprocessing.shared.common.event_task_contract.events
|
|||||||
import no.iktdev.eventi.models.store.TaskStatus
|
import no.iktdev.eventi.models.store.TaskStatus
|
||||||
import no.iktdev.mediaprocessing.shared.common.event_task_contract.TaskResultEvent
|
import no.iktdev.mediaprocessing.shared.common.event_task_contract.TaskResultEvent
|
||||||
|
|
||||||
data class ProcesserEncodeResultEvent(
|
class ProcesserEncodeResultEvent(
|
||||||
val data: EncodeResult? = null,
|
val data: EncodeResult? = null,
|
||||||
override val status: TaskStatus,
|
status: TaskStatus,
|
||||||
override val error: String? = null
|
error: String? = null
|
||||||
) : TaskResultEvent(status, error) {
|
) : TaskResultEvent(status, error) {
|
||||||
data class EncodeResult(
|
data class EncodeResult(
|
||||||
val cachedOutputFile: String? = null
|
val cachedOutputFile: String? = null
|
||||||
|
|||||||
@ -3,10 +3,10 @@ package no.iktdev.mediaprocessing.shared.common.event_task_contract.events
|
|||||||
import no.iktdev.eventi.models.store.TaskStatus
|
import no.iktdev.eventi.models.store.TaskStatus
|
||||||
import no.iktdev.mediaprocessing.shared.common.event_task_contract.TaskResultEvent
|
import no.iktdev.mediaprocessing.shared.common.event_task_contract.TaskResultEvent
|
||||||
|
|
||||||
data class ProcesserExtractResultEvent(
|
class ProcesserExtractResultEvent(
|
||||||
val data: ExtractResult? = null,
|
val data: ExtractResult? = null,
|
||||||
override val status: TaskStatus,
|
status: TaskStatus,
|
||||||
override val error: String? = null
|
error: String? = null
|
||||||
) : TaskResultEvent(status, error) {
|
) : TaskResultEvent(status, error) {
|
||||||
data class ExtractResult(
|
data class ExtractResult(
|
||||||
val language: String,
|
val language: String,
|
||||||
|
|||||||
@ -3,8 +3,8 @@ package no.iktdev.mediaprocessing.shared.common.event_task_contract.events
|
|||||||
import no.iktdev.eventi.models.store.TaskStatus
|
import no.iktdev.eventi.models.store.TaskStatus
|
||||||
import no.iktdev.mediaprocessing.shared.common.event_task_contract.TaskResultEvent
|
import no.iktdev.mediaprocessing.shared.common.event_task_contract.TaskResultEvent
|
||||||
|
|
||||||
data class StoreContentAndMetadataTaskResultEvent(
|
class StoreContentAndMetadataTaskResultEvent(
|
||||||
override val status: TaskStatus,
|
status: TaskStatus,
|
||||||
override val error: String? = null
|
error: String? = null
|
||||||
) : TaskResultEvent(status, error){
|
) : TaskResultEvent(status, error){
|
||||||
}
|
}
|
||||||
@ -0,0 +1,116 @@
|
|||||||
|
package no.iktdev.mediaprocessing.shared.common.event_task_contract
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper
|
||||||
|
import com.fasterxml.jackson.databind.SerializationFeature
|
||||||
|
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule
|
||||||
|
import com.fasterxml.jackson.module.kotlin.KotlinFeature
|
||||||
|
import com.fasterxml.jackson.module.kotlin.KotlinModule
|
||||||
|
import com.google.gson.GsonBuilder
|
||||||
|
import com.google.gson.JsonDeserializer
|
||||||
|
import com.google.gson.JsonPrimitive
|
||||||
|
import com.google.gson.JsonSerializer
|
||||||
|
import no.iktdev.eventi.models.store.TaskStatus
|
||||||
|
import org.junit.jupiter.api.Test
|
||||||
|
import org.junit.jupiter.api.assertDoesNotThrow
|
||||||
|
import org.reflections.Reflections
|
||||||
|
import java.time.Instant
|
||||||
|
import java.util.*
|
||||||
|
import kotlin.reflect.KClass
|
||||||
|
import kotlin.reflect.full.primaryConstructor
|
||||||
|
|
||||||
|
class TaskResultEventSerializationTest {
|
||||||
|
|
||||||
|
val gson = GsonBuilder()
|
||||||
|
.registerTypeAdapter(Instant::class.java, JsonSerializer<Instant> { src, _, _ ->
|
||||||
|
JsonPrimitive(src.toString())
|
||||||
|
})
|
||||||
|
.registerTypeAdapter(Instant::class.java, JsonDeserializer { json, _, _ ->
|
||||||
|
Instant.parse(json.asString)
|
||||||
|
})
|
||||||
|
.create()
|
||||||
|
|
||||||
|
val jackson = ObjectMapper()
|
||||||
|
.registerModule(JavaTimeModule())
|
||||||
|
.registerModule(
|
||||||
|
KotlinModule.Builder()
|
||||||
|
.configure(KotlinFeature.NullToEmptyCollection, false)
|
||||||
|
.configure(KotlinFeature.NullToEmptyMap, false)
|
||||||
|
.configure(KotlinFeature.NullIsSameAsDefault, false)
|
||||||
|
.configure(KotlinFeature.StrictNullChecks, true)
|
||||||
|
.configure(KotlinFeature.UseJavaDurationConversion, true)
|
||||||
|
.build()
|
||||||
|
)
|
||||||
|
.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS)
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `all TaskResultEvent subclasses must serialize with Gson and Jackson`() {
|
||||||
|
val reflections = Reflections("no.iktdev.mediaprocessing") // rotpakken din
|
||||||
|
val subclasses = reflections.getSubTypesOf(TaskResultEvent::class.java)
|
||||||
|
|
||||||
|
require(subclasses.isNotEmpty()) {
|
||||||
|
"Fant ingen subklasser av TaskResultEvent — er pakken riktig?"
|
||||||
|
}
|
||||||
|
|
||||||
|
subclasses.forEach { clazz ->
|
||||||
|
assertDoesNotThrow("Serialization failed for ${clazz.simpleName}") {
|
||||||
|
val instance = createDummyInstance(clazz)
|
||||||
|
val jsonGson = gson.toJson(instance)
|
||||||
|
gson.fromJson(jsonGson, clazz)
|
||||||
|
|
||||||
|
val jsonJackson = jackson.writeValueAsString(instance)
|
||||||
|
jackson.readValue(jsonJackson, clazz)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Lager en dummy-instans av en TaskResultEvent-subklasse.
|
||||||
|
* Forutsetter at alle event-klasser har en primary constructor.
|
||||||
|
*/
|
||||||
|
private fun createDummyInstance(clazz: Class<*>): Any {
|
||||||
|
val kClass = clazz.kotlin
|
||||||
|
val ctor = kClass.primaryConstructor
|
||||||
|
?: error("Klassen ${clazz.simpleName} mangler primary constructor")
|
||||||
|
|
||||||
|
val args = ctor.parameters.associateWith { param ->
|
||||||
|
val kType = param.type
|
||||||
|
val classifier = kType.classifier
|
||||||
|
|
||||||
|
// --- 1. Handle concrete known types first ---
|
||||||
|
if (classifier == String::class) return@associateWith "dummy"
|
||||||
|
if (classifier == Int::class) return@associateWith 1
|
||||||
|
if (classifier == Long::class) return@associateWith 1L
|
||||||
|
if (classifier == Boolean::class) return@associateWith false
|
||||||
|
if (classifier == Double::class) return@associateWith 1.0
|
||||||
|
if (classifier == UUID::class) return@associateWith UUID.randomUUID()
|
||||||
|
if (classifier == Instant::class) return@associateWith Instant.now()
|
||||||
|
if (classifier == TaskStatus::class) return@associateWith TaskStatus.Completed
|
||||||
|
if (classifier == Float::class) return@associateWith 1.0f
|
||||||
|
|
||||||
|
|
||||||
|
// --- 2. Generic enum support ---
|
||||||
|
if (classifier is KClass<*> && classifier.java.isEnum) {
|
||||||
|
return@associateWith classifier.java.enumConstants.first()
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- 3. Lists and maps ---
|
||||||
|
if (classifier == List::class) return@associateWith emptyList<Any>()
|
||||||
|
if (classifier == Map::class) return@associateWith emptyMap<String, Any>()
|
||||||
|
|
||||||
|
// --- 4. Nested data classes ---
|
||||||
|
if (classifier is KClass<*> && classifier.isData) {
|
||||||
|
return@associateWith createDummyInstance(classifier.java)
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- 5. Fallback ---
|
||||||
|
null
|
||||||
|
}
|
||||||
|
|
||||||
|
return ctor.callBy(args)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user