test: fix GameStateRouteTest
This commit is contained in:
@@ -30,7 +30,10 @@ class ReactionListener(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
logger.error { "${this::class.simpleName} is already init for this bus" }
|
"${this::class.simpleName} is already init for this bus".let {
|
||||||
|
logger.error { it }
|
||||||
|
error(it)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ class BusInMemory<E>(
|
|||||||
|
|
||||||
override suspend fun publish(item: E) {
|
override suspend fun publish(item: E) {
|
||||||
withLoggingContext("busItem" to item.toString()) {
|
withLoggingContext("busItem" to item.toString()) {
|
||||||
logger.info { "Item sent to the bus: $item" }
|
logger.info { "Item sent to the bus" }
|
||||||
subscribers
|
subscribers
|
||||||
.forEach {
|
.forEach {
|
||||||
coroutineScope {
|
coroutineScope {
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import com.rabbitmq.client.Connection
|
|||||||
import com.rabbitmq.client.ConnectionFactory
|
import com.rabbitmq.client.ConnectionFactory
|
||||||
import com.rabbitmq.client.DefaultConsumer
|
import com.rabbitmq.client.DefaultConsumer
|
||||||
import com.rabbitmq.client.Envelope
|
import com.rabbitmq.client.Envelope
|
||||||
|
import io.github.oshai.kotlinlogging.KotlinLogging
|
||||||
import io.ktor.utils.io.core.toByteArray
|
import io.ktor.utils.io.core.toByteArray
|
||||||
import kotlinx.coroutines.runBlocking
|
import kotlinx.coroutines.runBlocking
|
||||||
|
|
||||||
@@ -15,6 +16,8 @@ class BusInRabbitMQ<E>(
|
|||||||
private val objectToString: (E) -> String,
|
private val objectToString: (E) -> String,
|
||||||
private val stringToObject: (String) -> E,
|
private val stringToObject: (String) -> E,
|
||||||
) : Bus<E> {
|
) : Bus<E> {
|
||||||
|
private val logger = KotlinLogging.logger { }
|
||||||
|
|
||||||
private val connection: Connection = connectionFactory.newConnection()
|
private val connection: Connection = connectionFactory.newConnection()
|
||||||
get() {
|
get() {
|
||||||
return if (field.isOpen) {
|
return if (field.isOpen) {
|
||||||
@@ -50,6 +53,7 @@ class BusInRabbitMQ<E>(
|
|||||||
objectToString(item).toByteArray(),
|
objectToString(item).toByteArray(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
logger.info { "Item sent to the bus" }
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun subscribe(block: suspend (E) -> Unit): Bus.Subscription {
|
override fun subscribe(block: suspend (E) -> Unit): Bus.Subscription {
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ interface ProjectionSnapshotRepository<E : Event<ID>, P : Projection<ID>, ID : A
|
|||||||
/**
|
/**
|
||||||
* Create a snapshot for the event
|
* Create a snapshot for the event
|
||||||
*/
|
*/
|
||||||
fun applyAndPutToCache(event: E): P
|
suspend fun applyAndPutToCache(event: E): P
|
||||||
|
|
||||||
fun count(aggregateId: ID): Int
|
fun count(aggregateId: ID): Int
|
||||||
|
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ class ProjectionSnapshotRepositoryInMemory<E : Event<ID>, P : Projection<ID>, ID
|
|||||||
* 5. save it
|
* 5. save it
|
||||||
* 6. remove old one
|
* 6. remove old one
|
||||||
*/
|
*/
|
||||||
override fun applyAndPutToCache(event: E): P =
|
override suspend fun applyAndPutToCache(event: E): P =
|
||||||
getUntil(event)
|
getUntil(event)
|
||||||
.also {
|
.also {
|
||||||
withLoggingContext("projection" to it.toString()) {
|
withLoggingContext("projection" to it.toString()) {
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ class ProjectionSnapshotRepositoryInRedis<E : Event<ID>, P : Projection<ID>, ID
|
|||||||
* 5. save it
|
* 5. save it
|
||||||
* 6. remove old one
|
* 6. remove old one
|
||||||
*/
|
*/
|
||||||
override fun applyAndPutToCache(event: E): P =
|
override suspend fun applyAndPutToCache(event: E): P =
|
||||||
getUntil(event)
|
getUntil(event)
|
||||||
.also {
|
.also {
|
||||||
withLoggingContext(mapOf("projection" to it.toString(), "event" to event.toString())) {
|
withLoggingContext(mapOf("projection" to it.toString(), "event" to event.toString())) {
|
||||||
@@ -131,17 +131,14 @@ class ProjectionSnapshotRepositoryInRedis<E : Event<ID>, P : Projection<ID>, ID
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun save(projection: P) {
|
private fun save(projection: P) {
|
||||||
repeat(5) {
|
|
||||||
val added = jedis.zadd(projection.redisKey, projection.lastEventVersion.toDouble(), projectionToJson(projection))
|
val added = jedis.zadd(projection.redisKey, projection.lastEventVersion.toDouble(), projectionToJson(projection))
|
||||||
if (added < 1) {
|
if (added < 1) {
|
||||||
logger.error { "Projection NOT saved" }
|
logger.error { "Projection NOT saved (already exists)" }
|
||||||
} else {
|
} else {
|
||||||
logger.info { "Projection saved" }
|
logger.info { "Projection saved" }
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
jedis.expire(projection.redisKey, snapshotCacheConfig.maxSnapshotCacheTtl.inWholeSeconds)
|
jedis.expire(projection.redisKey, snapshotCacheConfig.maxSnapshotCacheTtl.inWholeSeconds)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Apply events to the projection.
|
* Apply events to the projection.
|
||||||
@@ -195,7 +192,7 @@ class ProjectionSnapshotRepositoryInRedis<E : Event<ID>, P : Projection<ID>, ID
|
|||||||
it.last.toDouble(),
|
it.last.toDouble(),
|
||||||
).also { removedCount ->
|
).also { removedCount ->
|
||||||
if (removedCount > 0) {
|
if (removedCount > 0) {
|
||||||
logger.info {
|
logger.debug {
|
||||||
"$removedCount snapshot removed Modulo(${snapshotCacheConfig.modulo}) (${it.first} to ${it.last}) [lastVersion=$lastVersion]"
|
"$removedCount snapshot removed Modulo(${snapshotCacheConfig.modulo}) (${it.first} to ${it.last}) [lastVersion=$lastVersion]"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -48,7 +48,6 @@ fun testApplicationWithConfig(
|
|||||||
application {
|
application {
|
||||||
val koin = getKoin()
|
val koin = getKoin()
|
||||||
koin.cleanDataTest()
|
koin.cleanDataTest()
|
||||||
koin.configureGameListener()
|
|
||||||
configBuilder(koin)
|
configBuilder(koin)
|
||||||
}
|
}
|
||||||
block()
|
block()
|
||||||
|
|||||||
@@ -8,9 +8,7 @@ import eventDemo.business.event.event.NewPlayerEvent
|
|||||||
import eventDemo.business.event.event.PlayerReadyEvent
|
import eventDemo.business.event.event.PlayerReadyEvent
|
||||||
import eventDemo.business.event.projection.gameList.GameList
|
import eventDemo.business.event.projection.gameList.GameList
|
||||||
import eventDemo.testApplicationWithConfig
|
import eventDemo.testApplicationWithConfig
|
||||||
import io.kotest.assertions.nondeterministic.continually
|
|
||||||
import io.kotest.assertions.nondeterministic.eventually
|
import io.kotest.assertions.nondeterministic.eventually
|
||||||
import io.kotest.assertions.nondeterministic.eventuallyConfig
|
|
||||||
import io.kotest.core.spec.style.FunSpec
|
import io.kotest.core.spec.style.FunSpec
|
||||||
import io.kotest.matchers.collections.shouldContain
|
import io.kotest.matchers.collections.shouldContain
|
||||||
import io.kotest.matchers.collections.shouldHaveSize
|
import io.kotest.matchers.collections.shouldHaveSize
|
||||||
@@ -24,7 +22,6 @@ import io.ktor.http.HttpStatusCode
|
|||||||
import kotlinx.coroutines.runBlocking
|
import kotlinx.coroutines.runBlocking
|
||||||
import kotlin.test.assertEquals
|
import kotlin.test.assertEquals
|
||||||
import kotlin.test.assertTrue
|
import kotlin.test.assertTrue
|
||||||
import kotlin.time.Duration.Companion.milliseconds
|
|
||||||
import kotlin.time.Duration.Companion.seconds
|
import kotlin.time.Duration.Companion.seconds
|
||||||
|
|
||||||
class GameListRouteTest :
|
class GameListRouteTest :
|
||||||
@@ -60,13 +57,7 @@ class GameListRouteTest :
|
|||||||
},
|
},
|
||||||
) {
|
) {
|
||||||
// Wait until the projection is created
|
// Wait until the projection is created
|
||||||
eventually(
|
eventually(1.seconds) {
|
||||||
eventuallyConfig {
|
|
||||||
duration = 3.seconds
|
|
||||||
interval = 300.milliseconds
|
|
||||||
},
|
|
||||||
) {
|
|
||||||
continually(1.seconds) {
|
|
||||||
httpClient()
|
httpClient()
|
||||||
.get("/games") {
|
.get("/games") {
|
||||||
withAuth(player1)
|
withAuth(player1)
|
||||||
@@ -83,7 +74,6 @@ class GameListRouteTest :
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
test("/games return a game with status IS_STARTED") {
|
test("/games return a game with status IS_STARTED") {
|
||||||
val gameId = GameId()
|
val gameId = GameId()
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ import eventDemo.business.entity.GameId
|
|||||||
import eventDemo.business.entity.Player
|
import eventDemo.business.entity.Player
|
||||||
import eventDemo.business.event.GameEventHandler
|
import eventDemo.business.event.GameEventHandler
|
||||||
import eventDemo.business.event.event.CardIsPlayedEvent
|
import eventDemo.business.event.event.CardIsPlayedEvent
|
||||||
import eventDemo.business.event.event.GameStartedEvent
|
|
||||||
import eventDemo.business.event.event.NewPlayerEvent
|
import eventDemo.business.event.event.NewPlayerEvent
|
||||||
import eventDemo.business.event.event.PlayerReadyEvent
|
import eventDemo.business.event.event.PlayerReadyEvent
|
||||||
import eventDemo.business.event.event.disableShuffleDeck
|
import eventDemo.business.event.event.disableShuffleDeck
|
||||||
@@ -22,7 +21,6 @@ import io.ktor.client.request.get
|
|||||||
import io.ktor.client.statement.bodyAsText
|
import io.ktor.client.statement.bodyAsText
|
||||||
import io.ktor.http.ContentType
|
import io.ktor.http.ContentType
|
||||||
import io.ktor.http.HttpStatusCode
|
import io.ktor.http.HttpStatusCode
|
||||||
import kotlinx.coroutines.delay
|
|
||||||
import kotlinx.coroutines.runBlocking
|
import kotlinx.coroutines.runBlocking
|
||||||
import kotlin.test.assertEquals
|
import kotlin.test.assertEquals
|
||||||
import kotlin.test.assertIs
|
import kotlin.test.assertIs
|
||||||
@@ -66,23 +64,13 @@ class GameStateRouteTest :
|
|||||||
eventHandler.handle(gameId) { NewPlayerEvent(gameId, player2, it) }
|
eventHandler.handle(gameId) { NewPlayerEvent(gameId, player2, it) }
|
||||||
eventHandler.handle(gameId) { PlayerReadyEvent(gameId, player1, it) }
|
eventHandler.handle(gameId) { PlayerReadyEvent(gameId, player1, it) }
|
||||||
eventHandler.handle(gameId) { PlayerReadyEvent(gameId, player2, it) }
|
eventHandler.handle(gameId) { PlayerReadyEvent(gameId, player2, it) }
|
||||||
eventHandler.handle(gameId) {
|
lastPlayedCard = eventually { stateRepo.getLast(gameId).playableCards(player1).first() }
|
||||||
GameStartedEvent.new(
|
|
||||||
gameId,
|
|
||||||
setOf(player1, player2),
|
|
||||||
it,
|
|
||||||
shuffleIsDisabled = true,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
delay(100)
|
|
||||||
lastPlayedCard = stateRepo.getLast(gameId).playableCards(player1).first()
|
|
||||||
assertNotNull(lastPlayedCard)
|
assertNotNull(lastPlayedCard)
|
||||||
.let { assertIs<Card.NumericCard>(lastPlayedCard) }
|
.let { assertIs<Card.NumericCard>(lastPlayedCard) }
|
||||||
.let {
|
.let {
|
||||||
it.number shouldBeEqual 0
|
it.number shouldBeEqual 0
|
||||||
it.color shouldBeEqual Card.Color.Red
|
it.color shouldBeEqual Card.Color.Red
|
||||||
}
|
}
|
||||||
delay(100)
|
|
||||||
eventHandler.handle(gameId) {
|
eventHandler.handle(gameId) {
|
||||||
CardIsPlayedEvent(
|
CardIsPlayedEvent(
|
||||||
gameId,
|
gameId,
|
||||||
@@ -91,7 +79,6 @@ class GameStateRouteTest :
|
|||||||
it,
|
it,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
delay(100)
|
|
||||||
}
|
}
|
||||||
}) {
|
}) {
|
||||||
eventually(1.seconds) {
|
eventually(1.seconds) {
|
||||||
@@ -131,23 +118,13 @@ class GameStateRouteTest :
|
|||||||
eventHandler.handle(gameId) { NewPlayerEvent(gameId, player2, it) }
|
eventHandler.handle(gameId) { NewPlayerEvent(gameId, player2, it) }
|
||||||
eventHandler.handle(gameId) { PlayerReadyEvent(gameId, player1, it) }
|
eventHandler.handle(gameId) { PlayerReadyEvent(gameId, player1, it) }
|
||||||
eventHandler.handle(gameId) { PlayerReadyEvent(gameId, player2, it) }
|
eventHandler.handle(gameId) { PlayerReadyEvent(gameId, player2, it) }
|
||||||
eventHandler.handle(gameId) {
|
lastPlayedCard = eventually { stateRepo.getLast(gameId).playableCards(player1).first() }
|
||||||
GameStartedEvent.new(
|
|
||||||
gameId,
|
|
||||||
setOf(player1, player2),
|
|
||||||
it,
|
|
||||||
shuffleIsDisabled = true,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
delay(100)
|
|
||||||
lastPlayedCard = stateRepo.getLast(gameId).playableCards(player1).first()
|
|
||||||
assertNotNull(lastPlayedCard)
|
assertNotNull(lastPlayedCard)
|
||||||
.let { assertIs<Card.NumericCard>(lastPlayedCard) }
|
.let { assertIs<Card.NumericCard>(lastPlayedCard) }
|
||||||
.let {
|
.let {
|
||||||
it.number shouldBeEqual 0
|
it.number shouldBeEqual 0
|
||||||
it.color shouldBeEqual Card.Color.Red
|
it.color shouldBeEqual Card.Color.Red
|
||||||
}
|
}
|
||||||
delay(100)
|
|
||||||
eventHandler.handle(gameId) {
|
eventHandler.handle(gameId) {
|
||||||
CardIsPlayedEvent(
|
CardIsPlayedEvent(
|
||||||
gameId,
|
gameId,
|
||||||
@@ -156,7 +133,6 @@ class GameStateRouteTest :
|
|||||||
it,
|
it,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
delay(100)
|
|
||||||
}
|
}
|
||||||
}) {
|
}) {
|
||||||
eventually(1.seconds) {
|
eventually(1.seconds) {
|
||||||
|
|||||||
@@ -158,7 +158,7 @@ class ProjectionSnapshotRepositoryTest :
|
|||||||
val versionBuilder = VersionBuilderLocal()
|
val versionBuilder = VersionBuilderLocal()
|
||||||
val aggregateId = IdTest()
|
val aggregateId = IdTest()
|
||||||
|
|
||||||
fun buildEndSendEventX() {
|
suspend fun buildEndSendEventX() {
|
||||||
EventXTest(num = 1, version = versionBuilder.buildNextVersion(aggregateId), aggregateId = aggregateId)
|
EventXTest(num = 1, version = versionBuilder.buildNextVersion(aggregateId), aggregateId = aggregateId)
|
||||||
.also { eventStore.publish(it) }
|
.also { eventStore.publish(it) }
|
||||||
.also { repo.applyAndPutToCache(it) }
|
.also { repo.applyAndPutToCache(it) }
|
||||||
|
|||||||
Reference in New Issue
Block a user