Added signal event

This commit is contained in:
Brage Skjønborg 2026-01-31 18:16:23 +01:00
parent f5cc32487f
commit a9779d2371
3 changed files with 96 additions and 23 deletions

View File

@ -2,6 +2,7 @@ package no.iktdev.eventi.events
import no.iktdev.eventi.models.DeleteEvent
import no.iktdev.eventi.models.Event
import no.iktdev.eventi.models.SignalEvent
import no.iktdev.eventi.stores.EventStore
import java.util.UUID
@ -11,6 +12,7 @@ open class EventDispatcher(val eventStore: EventStore) {
val derivedFromIds = events.mapNotNull { it.metadata.derivedFromId }.flatten().toSet()
val deletedEventIds = events.filterIsInstance<DeleteEvent>().map { it.deletedEventId }
val candidates = events
.filterNot { it is SignalEvent }
.filter { it.eventId !in derivedFromIds }
.filter { it.eventId !in deletedEventIds }

View File

@ -43,6 +43,6 @@ abstract class DeleteEvent(
open val deletedEventId: UUID
) : Event()
abstract class SignalEvent(): Event()

View File

@ -8,6 +8,7 @@ import no.iktdev.eventi.events.EventListenerRegistry
import no.iktdev.eventi.events.EventTypeRegistry
import no.iktdev.eventi.models.DeleteEvent
import no.iktdev.eventi.models.Event
import no.iktdev.eventi.models.SignalEvent
import no.iktdev.eventi.testUtil.wipe
import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.Assertions.assertEquals
@ -19,12 +20,14 @@ import org.junit.jupiter.api.DisplayName
import org.junit.jupiter.api.Test
import java.util.UUID
@DisplayName("""
@DisplayName(
"""
EventDispatcher
Når hendelser dispatches til lyttere
Hvis hendelsene inneholder avledede, slettede eller nye events
skal dispatcheren håndtere filtrering, replays og historikk korrekt
""")
"""
)
class EventDispatcherTest : TestBase() {
val dispatcher = EventDispatcher(eventStore)
@ -51,11 +54,13 @@ class EventDispatcherTest : TestBase() {
}
@Test
@DisplayName("""
@DisplayName(
"""
Når en TriggerEvent dispatches
Hvis en lytter produserer én DerivedEvent
skal kun én ny event produseres og prosessen stoppe
""")
"""
)
fun shouldProduceOneEventAndStop() {
ProducingListener()
@ -72,11 +77,13 @@ class EventDispatcherTest : TestBase() {
}
@Test
@DisplayName("""
@DisplayName(
"""
Når en event allerede har avledet en DerivedEvent
Hvis dispatcheren replays historikken
skal ikke DerivedEvent produseres nytt
""")
"""
)
fun shouldSkipAlreadyDerivedEvents() {
ProducingListener()
@ -91,11 +98,13 @@ class EventDispatcherTest : TestBase() {
}
@Test
@DisplayName("""
@DisplayName(
"""
Når flere events dispatches
Hvis en lytter mottar en event
skal hele historikken leveres i context
""")
"""
)
fun shouldPassFullContextToListener() {
val listener = ContextCapturingListener()
@ -107,11 +116,13 @@ class EventDispatcherTest : TestBase() {
}
@Test
@DisplayName("""
@DisplayName(
"""
Når en replay skjer
Hvis en event allerede har produsert en DerivedEvent
skal ikke DerivedEvent produseres nytt
""")
"""
)
fun shouldBehaveDeterministicallyAcrossReplays() {
ProducingListener()
@ -125,11 +136,13 @@ class EventDispatcherTest : TestBase() {
}
@Test
@DisplayName("""
@DisplayName(
"""
Når en DeleteEvent peker en tidligere event
Hvis dispatcheren filtrerer kandidater
skal slettede events ikke leveres som kandidater
""")
"""
)
fun shouldNotDeliverDeletedEventsAsCandidates() {
val dispatcher = EventDispatcher(eventStore)
val received = mutableListOf<Event>()
@ -162,11 +175,13 @@ class EventDispatcherTest : TestBase() {
}
@Test
@DisplayName("""
@DisplayName(
"""
Når en DeleteEvent dispatches alene
Hvis en lytter reagerer DeleteEvent
skal DeleteEvent leveres som kandidat
""")
"""
)
fun shouldDeliverDeleteEventToListenersThatReactToIt() {
val received = mutableListOf<Event>()
object : EventListener() {
@ -183,11 +198,13 @@ class EventDispatcherTest : TestBase() {
}
@Test
@DisplayName("""
@DisplayName(
"""
Når en event har avledet en ny event
Hvis dispatcheren replays historikken
skal ikke original-eventen leveres som kandidat igjen
""")
"""
)
fun shouldNotRedeliverEventsThatHaveProducedDerivedEvents() {
ProducingListener()
@ -211,11 +228,13 @@ class EventDispatcherTest : TestBase() {
}
@Test
@DisplayName("""
@DisplayName(
"""
Når en DeleteEvent slettet en tidligere event
Hvis dispatcheren bygger historikk
skal slettede events ikke være med i history
""")
"""
)
fun historyShouldExcludeDeletedEvents() {
val dispatcher = EventDispatcher(eventStore)
@ -238,11 +257,13 @@ class EventDispatcherTest : TestBase() {
}
@Test
@DisplayName("""
@DisplayName(
"""
Når en DeleteEvent slettet en event
Hvis andre events fortsatt er gyldige
skal history kun inneholde de ikke-slettede events
""")
"""
)
fun historyShouldKeepNonDeletedEvents() {
val dispatcher = EventDispatcher(eventStore)
@ -267,11 +288,13 @@ class EventDispatcherTest : TestBase() {
}
@Test
@DisplayName("""
@DisplayName(
"""
Når en DeleteEvent er kandidat
Hvis historikken kun inneholder slettede events
skal history være tom
""")
"""
)
fun deleteEventShouldBeDeliveredButHistoryEmpty() {
val dispatcher = EventDispatcher(eventStore)
@ -295,6 +318,54 @@ class EventDispatcherTest : TestBase() {
assertTrue(receivedHistory.isEmpty())
}
@Test
@DisplayName(
"""
Når en SignalEvent dispatches
Hvis SignalEvent ikke skal være kandidat
skal den ikke leveres til lyttere, men fortsatt være i historikken
"""
)
fun shouldNotDeliverSignalEventAsCandidate() {
// Arrange
class TestSignalEvent : SignalEvent()
EventTypeRegistry.register(listOf(TestSignalEvent::class.java,))
val received = mutableListOf<Event>()
var finalHistory: List<Event>? = null
object : EventListener() {
override fun onEvent(event: Event, history: List<Event>): Event? {
received += event
finalHistory = history
return null
}
}
val refId = UUID.randomUUID()
val trigger = TriggerEvent().usingReferenceId(refId)
val signal = TestSignalEvent().usingReferenceId(refId)
// Act
dispatcher.dispatch(trigger.referenceId, listOf(trigger, signal))
// Assert
// 1) TriggerEvent skal leveres
assertTrue(received.any { it is TriggerEvent }) {
"TriggerEvent skal leveres som kandidat"
}
// 2) SignalEvent skal IKKE leveres
assertFalse(received.any { it is TestSignalEvent }) {
"SignalEvent skal ikke leveres som kandidat"
}
assertNotNull(finalHistory)
assertTrue(finalHistory!!.any { it is TestSignalEvent }) {
"SignalEvent skal være i historikken selv om den ikke er kandidat"
}
}
// --- Test helpers ---
class ProducingListener : EventListener() {