update ktlint rules

This commit is contained in:
2025-03-14 03:23:16 +01:00
parent 492981bed0
commit b4234a9b37
97 changed files with 2392 additions and 2359 deletions

View File

@@ -3,8 +3,10 @@ package eventDemo
import eventDemo.app.entity.Card
import eventDemo.app.entity.Deck
fun Deck.allCardCount(): Int = stack.size + discard.size + playersHands.values.flatten().size
fun Deck.allCardCount(): Int =
stack.size + discard.size + playersHands.values.flatten().size
fun Deck.allCards(): Set<Card> = stack + discard + playersHands.values.flatten()
fun Deck.allCards(): Set<Card> =
stack + discard + playersHands.values.flatten()
// suspend fun SendChannel<Frame>.send(command: GameCommand) = send(Frame.Text(Json.encodeToString(command)))

View File

@@ -20,26 +20,26 @@ import kotlin.test.assertIs
@OptIn(DelicateCoroutinesApi::class)
class GameCommandHandlerTest :
FunSpec({
test("handle a command should execute the command") {
koinApplication { modules(appKoinModule) }.koin.apply {
val commandHandler by inject<GameCommandHandler>()
val notificationListener by inject<PlayerNotificationEventListener>()
val gameId = GameId()
val player = Player("Tesla")
val channelCommand = Channel<GameCommand>(Channel.BUFFERED)
val channelNotification = Channel<Notification>(Channel.BUFFERED)
ReactionEventListener(get(), get(), get()).init()
notificationListener.startListening(channelNotification, player)
FunSpec({
test("handle a command should execute the command") {
koinApplication { modules(appKoinModule) }.koin.apply {
val commandHandler by inject<GameCommandHandler>()
val notificationListener by inject<PlayerNotificationEventListener>()
val gameId = GameId()
val player = Player("Tesla")
val channelCommand = Channel<GameCommand>(Channel.BUFFERED)
val channelNotification = Channel<Notification>(Channel.BUFFERED)
ReactionEventListener(get(), get(), get()).init()
notificationListener.startListening(channelNotification, player)
GlobalScope.launch {
commandHandler.handle(player, channelCommand, channelNotification)
}
channelCommand.send(IWantToJoinTheGameCommand(IWantToJoinTheGameCommand.Payload(gameId, player)))
assertIs<WelcomeToTheGameNotification>(channelNotification.receive()).let {
it.players shouldContain player
}
}
GlobalScope.launch {
commandHandler.handle(player, channelCommand, channelNotification)
}
})
channelCommand.send(IWantToJoinTheGameCommand(IWantToJoinTheGameCommand.Payload(gameId, player)))
assertIs<WelcomeToTheGameNotification>(channelNotification.receive()).let {
it.players shouldContain player
}
}
}
})

View File

@@ -3,6 +3,6 @@ package eventDemo.app.command
import io.kotest.core.spec.style.FunSpec
class GameCommandRunnerTest :
FunSpec({
test("run should run the correct command") { }
})
FunSpec({
test("run should run the correct command") { }
})

View File

@@ -3,7 +3,7 @@ package eventDemo.app.command.command
import io.kotest.core.spec.style.FunSpec
class ICantPlayCommandTest :
FunSpec({
FunSpec({
xtest("run should publish the event") { }
})
xtest("run should publish the event") { }
})

View File

@@ -3,7 +3,7 @@ package eventDemo.app.command.command
import io.kotest.core.spec.style.FunSpec
class IWantToJoinTheGameCommandTest :
FunSpec({
FunSpec({
xtest("run should publish the event") { }
})
xtest("run should publish the event") { }
})

View File

@@ -3,7 +3,7 @@ package eventDemo.app.command.command
import io.kotest.core.spec.style.FunSpec
class IWantToPlayCardCommandTest :
FunSpec({
FunSpec({
xtest("run should publish the event") { }
})
xtest("run should publish the event") { }
})

View File

@@ -3,7 +3,7 @@ package eventDemo.app.command.command
import io.kotest.core.spec.style.FunSpec
class IamReadyToPlayCommandTest :
FunSpec({
FunSpec({
xtest("run should publish the event") { }
})
xtest("run should publish the event") { }
})

View File

@@ -8,97 +8,97 @@ import io.kotest.matchers.ints.shouldBeExactly
import kotlin.test.assertNotNull
class DeckTest :
FunSpec({
val totalCardsNumber = 104
test("newWithoutPlayers") {
// When
val deck = Deck.newWithoutPlayers()
FunSpec({
val totalCardsNumber = 104
test("newWithoutPlayers") {
// When
val deck = Deck.newWithoutPlayers()
// Then
deck.stack.size shouldBeExactly totalCardsNumber
deck.discard.size shouldBeExactly 0
deck.playersHands.size shouldBeExactly 0
// Then
deck.stack.size shouldBeExactly totalCardsNumber
deck.discard.size shouldBeExactly 0
deck.playersHands.size shouldBeExactly 0
deck.allCardCount() shouldBeExactly totalCardsNumber
deck.allCards().shouldBeUnique()
deck.allCards().map { it.id }.shouldBeUnique()
}
deck.allCardCount() shouldBeExactly totalCardsNumber
deck.allCards().shouldBeUnique()
deck.allCards().map { it.id }.shouldBeUnique()
}
test("initHands should be generate the hands of all players from the stack") {
// Given
val playerNumbers = 4
val players = (1..playerNumbers).map { Player(name = "name $it") }.toSet()
val deck = Deck.newWithoutPlayers()
test("initHands should be generate the hands of all players from the stack") {
// Given
val playerNumbers = 4
val players = (1..playerNumbers).map { Player(name = "name $it") }.toSet()
val deck = Deck.newWithoutPlayers()
// When
val initDeck = deck.initHands(players)
// When
val initDeck = deck.initHands(players)
// Then
initDeck.stack.size shouldBeExactly totalCardsNumber - (playerNumbers * 7)
initDeck.discard.size shouldBeExactly 0
initDeck.playersHands.size shouldBeExactly playerNumbers
initDeck.playersHands.forEach { (_, cards) -> cards.size shouldBeExactly 7 }
initDeck.allCardCount() shouldBeExactly totalCardsNumber
}
// Then
initDeck.stack.size shouldBeExactly totalCardsNumber - (playerNumbers * 7)
initDeck.discard.size shouldBeExactly 0
initDeck.playersHands.size shouldBeExactly playerNumbers
initDeck.playersHands.forEach { (_, cards) -> cards.size shouldBeExactly 7 }
initDeck.allCardCount() shouldBeExactly totalCardsNumber
}
test("takeOneCardFromStackTo player") {
// Given
val playerNumbers = 4
val players = (1..playerNumbers).map { Player(name = "name $it") }.toSet()
val deck = Deck.newWithoutPlayers().initHands(players)
val firstPlayer = players.first()
test("takeOneCardFromStackTo player") {
// Given
val playerNumbers = 4
val players = (1..playerNumbers).map { Player(name = "name $it") }.toSet()
val deck = Deck.newWithoutPlayers().initHands(players)
val firstPlayer = players.first()
// When
val modifiedDeck = deck.takeOneCardFromStackTo(firstPlayer)
// When
val modifiedDeck = deck.takeOneCardFromStackTo(firstPlayer)
// Then
modifiedDeck.discard.size shouldBeExactly 0
modifiedDeck.stack.size shouldBeExactly totalCardsNumber - (playerNumbers * 7) - 1
modifiedDeck.playersHands.size shouldBeExactly playerNumbers
assertNotNull(modifiedDeck.playersHands.getHand(firstPlayer)).size shouldBeExactly 7 + 1
modifiedDeck.playersHands
.filterKeys { it != firstPlayer.id }
.forEach { (_, cards) -> cards.size shouldBeExactly 7 }
modifiedDeck.allCardCount() shouldBeExactly totalCardsNumber
}
// Then
modifiedDeck.discard.size shouldBeExactly 0
modifiedDeck.stack.size shouldBeExactly totalCardsNumber - (playerNumbers * 7) - 1
modifiedDeck.playersHands.size shouldBeExactly playerNumbers
assertNotNull(modifiedDeck.playersHands.getHand(firstPlayer)).size shouldBeExactly 7 + 1
modifiedDeck.playersHands
.filterKeys { it != firstPlayer.id }
.forEach { (_, cards) -> cards.size shouldBeExactly 7 }
modifiedDeck.allCardCount() shouldBeExactly totalCardsNumber
}
test("putOneCardFromHand") {
// Given
val playerNumbers = 4
val players = (1..playerNumbers).map { Player(name = "name $it") }.toSet()
val deck = Deck.newWithoutPlayers().initHands(players)
val firstPlayer = players.first()
test("putOneCardFromHand") {
// Given
val playerNumbers = 4
val players = (1..playerNumbers).map { Player(name = "name $it") }.toSet()
val deck = Deck.newWithoutPlayers().initHands(players)
val firstPlayer = players.first()
// When
val card = deck.playersHands.getHand(firstPlayer)!!.first()
val modifiedDeck = deck.putOneCardFromHand(firstPlayer, card)
// When
val card = deck.playersHands.getHand(firstPlayer)!!.first()
val modifiedDeck = deck.putOneCardFromHand(firstPlayer, card)
// Then
modifiedDeck.discard.size shouldBeExactly 1
modifiedDeck.stack.size shouldBeExactly totalCardsNumber - (playerNumbers * 7)
modifiedDeck.playersHands.size shouldBeExactly playerNumbers
assertNotNull(modifiedDeck.playersHands.getHand(firstPlayer)).size shouldBeExactly 6
modifiedDeck.playersHands
.filterKeys { it != firstPlayer.id }
.forEach { (_, cards) -> cards.size shouldBeExactly 7 }
modifiedDeck.allCardCount() shouldBeExactly totalCardsNumber
}
// Then
modifiedDeck.discard.size shouldBeExactly 1
modifiedDeck.stack.size shouldBeExactly totalCardsNumber - (playerNumbers * 7)
modifiedDeck.playersHands.size shouldBeExactly playerNumbers
assertNotNull(modifiedDeck.playersHands.getHand(firstPlayer)).size shouldBeExactly 6
modifiedDeck.playersHands
.filterKeys { it != firstPlayer.id }
.forEach { (_, cards) -> cards.size shouldBeExactly 7 }
modifiedDeck.allCardCount() shouldBeExactly totalCardsNumber
}
test("placeFirstCardOnDiscard") {
// Given
val playerNumbers = 4
val players = (1..playerNumbers).map { Player(name = "name $it") }.toSet()
val deck = Deck.newWithoutPlayers().initHands(players)
test("placeFirstCardOnDiscard") {
// Given
val playerNumbers = 4
val players = (1..playerNumbers).map { Player(name = "name $it") }.toSet()
val deck = Deck.newWithoutPlayers().initHands(players)
// When
val modifiedDeck = deck.placeFirstCardOnDiscard()
// When
val modifiedDeck = deck.placeFirstCardOnDiscard()
// Then
modifiedDeck.discard.size shouldBeExactly 1
modifiedDeck.stack.size shouldBeExactly totalCardsNumber - (playerNumbers * 7) - 1
modifiedDeck.playersHands.size shouldBeExactly playerNumbers
modifiedDeck.playersHands
.forEach { (_, cards) -> cards.size shouldBeExactly 7 }
modifiedDeck.allCardCount() shouldBeExactly totalCardsNumber
}
})
// Then
modifiedDeck.discard.size shouldBeExactly 1
modifiedDeck.stack.size shouldBeExactly totalCardsNumber - (playerNumbers * 7) - 1
modifiedDeck.playersHands.size shouldBeExactly playerNumbers
modifiedDeck.playersHands
.forEach { (_, cards) -> cards.size shouldBeExactly 7 }
modifiedDeck.allCardCount() shouldBeExactly totalCardsNumber
}
})

View File

@@ -5,37 +5,37 @@ import io.kotest.matchers.ints.shouldBeExactly
import kotlin.test.assertNotNull
class PlayerHandKtTest :
FunSpec({
test("addCards") {
// Given
val playerNumbers = 4
val players = (1..playerNumbers).map { Player(name = "name $it") }.toSet()
val firstPlayer = players.first()
val playersHands = PlayersHands(players)
val card = Card.NumericCard(0, Card.Color.Red)
FunSpec({
test("addCards") {
// Given
val playerNumbers = 4
val players = (1..playerNumbers).map { Player(name = "name $it") }.toSet()
val firstPlayer = players.first()
val playersHands = PlayersHands(players)
val card = Card.NumericCard(0, Card.Color.Red)
// When
val newHands: PlayersHands = playersHands.addCards(firstPlayer, listOf(card))
// When
val newHands: PlayersHands = playersHands.addCards(firstPlayer, listOf(card))
assertNotNull(newHands.getHand(firstPlayer)).size shouldBeExactly 1
assertNotNull(newHands.getHand(players.last())).size shouldBeExactly 0
}
assertNotNull(newHands.getHand(firstPlayer)).size shouldBeExactly 1
assertNotNull(newHands.getHand(players.last())).size shouldBeExactly 0
}
test("removeCard") {
// Given
val playerNumbers = 4
val players = (1..playerNumbers).map { Player(name = "name $it") }.toSet()
val firstPlayer = players.first()
val card1 = Card.NumericCard(1, Card.Color.Red)
val card2 = Card.NumericCard(2, Card.Color.Red)
val playersHands: PlayersHands =
PlayersHands(players)
.addCards(firstPlayer, listOf(card1, card2))
test("removeCard") {
// Given
val playerNumbers = 4
val players = (1..playerNumbers).map { Player(name = "name $it") }.toSet()
val firstPlayer = players.first()
val card1 = Card.NumericCard(1, Card.Color.Red)
val card2 = Card.NumericCard(2, Card.Color.Red)
val playersHands: PlayersHands =
PlayersHands(players)
.addCards(firstPlayer, listOf(card1, card2))
// When
val newHands: PlayersHands = playersHands.removeCard(firstPlayer, card1)
// When
val newHands: PlayersHands = playersHands.removeCard(firstPlayer, card1)
assertNotNull(newHands.getHand(firstPlayer)).size shouldBeExactly 1
assertNotNull(newHands.getHand(players.last())).size shouldBeExactly 0
}
})
assertNotNull(newHands.getHand(firstPlayer)).size shouldBeExactly 1
assertNotNull(newHands.getHand(players.last())).size shouldBeExactly 0
}
})

View File

@@ -3,13 +3,13 @@ package eventDemo.app.entity
import io.kotest.core.spec.style.FunSpec
class PlayersHandsTest :
FunSpec({
FunSpec({
xtest("getHand should return the hand of the player") { }
xtest("getHand should return the hand of the player") { }
xtest("removeCard should remove the card") { }
xtest("removeCard should remove the card") { }
xtest("addCard should add the card to the correct hand") { }
xtest("addCard should add the card to the correct hand") { }
xtest("toPlayersHands should build object from map") { }
})
xtest("toPlayersHands should build object from map") { }
})

View File

@@ -3,8 +3,8 @@ package eventDemo.app.event
import io.kotest.core.spec.style.FunSpec
class GameEventHandlerTest :
FunSpec({
xtest("handle event should publish the event to the stream") { }
xtest("handle event should build the registered projection") { }
xtest("handle event should publish the event to the bus") { }
})
FunSpec({
xtest("handle event should publish the event to the stream") { }
xtest("handle event should build the registered projection") { }
xtest("handle event should publish the event to the bus") { }
})

View File

@@ -15,112 +15,112 @@ import kotlin.test.assertIs
import kotlin.test.assertNotNull
class GameStateBuilderTest :
FunSpec({
test("apply") {
disableShuffleDeck()
val versionBuilder = VersionBuilderLocal()
val gameId = GameId()
val player1 = Player(name = "Nikola")
val player2 = Player(name = "Einstein")
FunSpec({
test("apply") {
disableShuffleDeck()
val versionBuilder = VersionBuilderLocal()
val gameId = GameId()
val player1 = Player(name = "Nikola")
val player2 = Player(name = "Einstein")
GameState(gameId)
.run {
val event =
NewPlayerEvent(
aggregateId = gameId,
player = player1,
version = versionBuilder.buildNextVersion(gameId),
)
apply(event).also { state ->
state.aggregateId shouldBeEqual gameId
state.isReady shouldBeEqual false
state.isStarted shouldBeEqual false
}
}.run {
val event =
NewPlayerEvent(
aggregateId = gameId,
player = player2,
version = versionBuilder.buildNextVersion(gameId),
)
apply(event).also { state ->
state.aggregateId shouldBeEqual gameId
state.players shouldBeEqual setOf(player1, player2)
}
}.run {
val event =
PlayerReadyEvent(
aggregateId = gameId,
player = player1,
version = versionBuilder.buildNextVersion(gameId),
)
apply(event).also { state ->
state.aggregateId shouldBeEqual gameId
state.readyPlayers shouldBeEqual setOf(player1)
}
}.run {
val event =
PlayerReadyEvent(
aggregateId = gameId,
player = player2,
version = versionBuilder.buildNextVersion(gameId),
)
apply(event).also { state ->
state.aggregateId shouldBeEqual gameId
state.readyPlayers shouldBeEqual setOf(player1, player2)
state.isReady shouldBeEqual true
state.isStarted shouldBeEqual false
}
}.run {
val event =
GameStartedEvent.new(
id = gameId,
players = setOf(player1, player2),
shuffleIsDisabled = true,
version = versionBuilder.buildNextVersion(gameId),
)
apply(event).also { state ->
state.aggregateId shouldBeEqual gameId
state.isStarted shouldBeEqual true
assertIs<Card.NumericCard>(state.deck.stack.first()).let {
it.number shouldBeEqual 6
it.color shouldBeEqual Card.Color.Red
}
}
}.run {
val playedCard = playableCards(player1)[0]
val event =
CardIsPlayedEvent(
aggregateId = gameId,
card = playedCard,
player = player1,
version = versionBuilder.buildNextVersion(gameId),
)
apply(event).also { state ->
state.aggregateId shouldBeEqual gameId
assertNotNull(state.cardOnCurrentStack).card shouldBeEqual playedCard
assertIs<Card.NumericCard>(playedCard).let {
it.number shouldBeEqual 0
it.color shouldBeEqual Card.Color.Red
}
}
}.run {
val playedCard = playableCards(player2)[0]
val event =
CardIsPlayedEvent(
aggregateId = gameId,
card = playedCard,
player = player2,
version = versionBuilder.buildNextVersion(gameId),
)
apply(event).also { state ->
state.aggregateId shouldBeEqual gameId
assertNotNull(state.cardOnCurrentStack).card shouldBeEqual playedCard
assertIs<Card.NumericCard>(playedCard).let {
it.number shouldBeEqual 7
it.color shouldBeEqual Card.Color.Red
}
}
}
GameState(gameId)
.run {
val event =
NewPlayerEvent(
aggregateId = gameId,
player = player1,
version = versionBuilder.buildNextVersion(gameId),
)
apply(event).also { state ->
state.aggregateId shouldBeEqual gameId
state.isReady shouldBeEqual false
state.isStarted shouldBeEqual false
}
}.run {
val event =
NewPlayerEvent(
aggregateId = gameId,
player = player2,
version = versionBuilder.buildNextVersion(gameId),
)
apply(event).also { state ->
state.aggregateId shouldBeEqual gameId
state.players shouldBeEqual setOf(player1, player2)
}
}.run {
val event =
PlayerReadyEvent(
aggregateId = gameId,
player = player1,
version = versionBuilder.buildNextVersion(gameId),
)
apply(event).also { state ->
state.aggregateId shouldBeEqual gameId
state.readyPlayers shouldBeEqual setOf(player1)
}
}.run {
val event =
PlayerReadyEvent(
aggregateId = gameId,
player = player2,
version = versionBuilder.buildNextVersion(gameId),
)
apply(event).also { state ->
state.aggregateId shouldBeEqual gameId
state.readyPlayers shouldBeEqual setOf(player1, player2)
state.isReady shouldBeEqual true
state.isStarted shouldBeEqual false
}
}.run {
val event =
GameStartedEvent.new(
id = gameId,
players = setOf(player1, player2),
shuffleIsDisabled = true,
version = versionBuilder.buildNextVersion(gameId),
)
apply(event).also { state ->
state.aggregateId shouldBeEqual gameId
state.isStarted shouldBeEqual true
assertIs<Card.NumericCard>(state.deck.stack.first()).let {
it.number shouldBeEqual 6
it.color shouldBeEqual Card.Color.Red
}
}
}.run {
val playedCard = playableCards(player1)[0]
val event =
CardIsPlayedEvent(
aggregateId = gameId,
card = playedCard,
player = player1,
version = versionBuilder.buildNextVersion(gameId),
)
apply(event).also { state ->
state.aggregateId shouldBeEqual gameId
assertNotNull(state.cardOnCurrentStack).card shouldBeEqual playedCard
assertIs<Card.NumericCard>(playedCard).let {
it.number shouldBeEqual 0
it.color shouldBeEqual Card.Color.Red
}
}
}.run {
val playedCard = playableCards(player2)[0]
val event =
CardIsPlayedEvent(
aggregateId = gameId,
card = playedCard,
player = player2,
version = versionBuilder.buildNextVersion(gameId),
)
apply(event).also { state ->
state.aggregateId shouldBeEqual gameId
assertNotNull(state.cardOnCurrentStack).card shouldBeEqual playedCard
assertIs<Card.NumericCard>(playedCard).let {
it.number shouldBeEqual 7
it.color shouldBeEqual Card.Color.Red
}
}
}
})
}
})

View File

@@ -18,113 +18,113 @@ import kotlin.test.assertNotNull
@OptIn(DelicateCoroutinesApi::class)
class GameStateRepositoryTest :
FunSpec({
val player1 = Player("Tesla")
val player2 = Player(name = "Einstein")
FunSpec({
val player1 = Player("Tesla")
val player2 = Player(name = "Einstein")
test("GameStateRepository should build the projection when a new event occurs") {
val aggregateId = GameId()
koinApplication { modules(appKoinModule) }.koin.apply {
val repo = get<GameStateRepository>()
val eventHandler = get<GameEventHandler>()
eventHandler
.handle(aggregateId) { NewPlayerEvent(aggregateId = aggregateId, player = player1, version = it) }
.also { event ->
assertNotNull(repo.getUntil(event)).also {
assertNotNull(it.players) shouldBeEqual setOf(player1)
}
assertNotNull(repo.getLast(aggregateId)).also {
assertNotNull(it.players) shouldBeEqual setOf(player1)
}
}
test("GameStateRepository should build the projection when a new event occurs") {
val aggregateId = GameId()
koinApplication { modules(appKoinModule) }.koin.apply {
val repo = get<GameStateRepository>()
val eventHandler = get<GameEventHandler>()
eventHandler
.handle(aggregateId) { NewPlayerEvent(aggregateId = aggregateId, player = player1, version = it) }
.also { event ->
assertNotNull(repo.getUntil(event)).also {
assertNotNull(it.players) shouldBeEqual setOf(player1)
}
stopKoin()
}
test("get should build the last version of the state") {
val aggregateId = GameId()
koinApplication { modules(appKoinModule) }.koin.apply {
val repo = get<GameStateRepository>()
val eventHandler = get<GameEventHandler>()
eventHandler
.handle(aggregateId) { NewPlayerEvent(aggregateId = aggregateId, player = player1, version = it) }
.also {
assertNotNull(repo.getLast(aggregateId)).also {
assertNotNull(it.players) shouldBeEqual setOf(player1)
}
}
eventHandler
.handle(aggregateId) { NewPlayerEvent(aggregateId = aggregateId, player = player2, version = it) }
.also {
assertNotNull(repo.getLast(aggregateId)).also {
assertNotNull(it.players) shouldBeEqual setOf(player1, player2)
}
}
assertNotNull(repo.getLast(aggregateId)).also {
assertNotNull(it.players) shouldBeEqual setOf(player1)
}
}
}
}
stopKoin()
}
test("getUntil should build the state until the event") {
repeat(10) {
val aggregateId = GameId()
koinApplication { modules(appKoinModule) }.koin.apply {
val repo = get<GameStateRepository>()
val eventHandler = get<GameEventHandler>()
test("get should build the last version of the state") {
val aggregateId = GameId()
koinApplication { modules(appKoinModule) }.koin.apply {
val repo = get<GameStateRepository>()
val eventHandler = get<GameEventHandler>()
val event1 =
eventHandler
.handle(aggregateId) { NewPlayerEvent(aggregateId = aggregateId, player = player1, version = it) }
.also { event1 ->
assertNotNull(repo.getUntil(event1)).also {
assertNotNull(it.players) shouldBeEqual setOf(player1)
}
}
eventHandler
.handle(aggregateId) { NewPlayerEvent(aggregateId = aggregateId, player = player1, version = it) }
.also {
assertNotNull(repo.getLast(aggregateId)).also {
assertNotNull(it.players) shouldBeEqual setOf(player1)
}
}
eventHandler
.handle(aggregateId) { NewPlayerEvent(aggregateId = aggregateId, player = player2, version = it) }
.also { event2 ->
assertNotNull(repo.getUntil(event2)).also {
assertNotNull(it.players) shouldBeEqual setOf(player1, player2)
}
assertNotNull(repo.getUntil(event1)).also {
assertNotNull(it.players) shouldBeEqual setOf(player1)
}
}
eventHandler
.handle(aggregateId) { NewPlayerEvent(aggregateId = aggregateId, player = player2, version = it) }
.also {
assertNotNull(repo.getLast(aggregateId)).also {
assertNotNull(it.players) shouldBeEqual setOf(player1, player2)
}
}
}
}
test("getUntil should build the state until the event") {
repeat(10) {
val aggregateId = GameId()
koinApplication { modules(appKoinModule) }.koin.apply {
val repo = get<GameStateRepository>()
val eventHandler = get<GameEventHandler>()
val event1 =
eventHandler
.handle(aggregateId) { NewPlayerEvent(aggregateId = aggregateId, player = player1, version = it) }
.also { event1 ->
assertNotNull(repo.getUntil(event1)).also {
assertNotNull(it.players) shouldBeEqual setOf(player1)
}
}
eventHandler
.handle(aggregateId) { NewPlayerEvent(aggregateId = aggregateId, player = player2, version = it) }
.also { event2 ->
assertNotNull(repo.getUntil(event2)).also {
assertNotNull(it.players) shouldBeEqual setOf(player1, player2)
}
assertNotNull(repo.getUntil(event1)).also {
assertNotNull(it.players) shouldBeEqual setOf(player1)
}
}
}
}
}
test("getUntil should be concurrently secure") {
val aggregateId = GameId()
koinApplication { modules(appKoinModule) }.koin.apply {
val repo = get<GameStateRepository>()
val eventHandler = get<GameEventHandler>()
test("getUntil should be concurrently secure") {
val aggregateId = GameId()
koinApplication { modules(appKoinModule) }.koin.apply {
val repo = get<GameStateRepository>()
val eventHandler = get<GameEventHandler>()
(1..10)
.map { r ->
GlobalScope
.launch {
repeat(100) { r2 ->
val playerX = Player("player$r$r2")
eventHandler
.handle(aggregateId) {
NewPlayerEvent(
aggregateId = aggregateId,
player = playerX,
version = it,
)
}
}
}
}.joinAll()
repo.getLast(aggregateId).run {
lastEventVersion shouldBeEqual 1000
players shouldHaveSize 1000
(1..10)
.map { r ->
GlobalScope
.launch {
repeat(100) { r2 ->
val playerX = Player("player$r$r2")
eventHandler
.handle(aggregateId) {
NewPlayerEvent(
aggregateId = aggregateId,
player = playerX,
version = it,
)
}
}
}
}
}
}.joinAll()
xtest("get should be concurrently secure") { }
})
repo.getLast(aggregateId).run {
lastEventVersion shouldBeEqual 1000
players shouldHaveSize 1000
}
}
}
xtest("get should be concurrently secure") { }
})

View File

@@ -3,13 +3,13 @@ package eventDemo.app.event.projection
import io.kotest.core.spec.style.FunSpec
class GameStateTest :
FunSpec({
xtest("isReady") { }
xtest("nextPlayer") { }
xtest("nextPlayerTurn") { }
xtest("playerDiffIndex") { }
xtest("cardOnBoardIsForYou") { }
xtest("playableCards") { }
xtest("playerHasNoCardLeft") { }
xtest("canBePlayThisCard") { }
})
FunSpec({
xtest("isReady") { }
xtest("nextPlayer") { }
xtest("nextPlayerTurn") { }
xtest("playerDiffIndex") { }
xtest("cardOnBoardIsForYou") { }
xtest("playableCards") { }
xtest("playerHasNoCardLeft") { }
xtest("canBePlayThisCard") { }
})

View File

@@ -20,151 +20,151 @@ import kotlin.test.assertNotNull
@OptIn(DelicateCoroutinesApi::class)
class ProjectionSnapshotRepositoryInMemoryTest :
FunSpec({
FunSpec({
test("when call applyAndPutToCache, the getUntil method must be use the built projection cache") {
val eventStore: EventStore<TestEvents, IdTest> = EventStoreInMemory()
val repo = getSnapshotRepoTest(eventStore)
val aggregateId = IdTest()
test("when call applyAndPutToCache, the getUntil method must be use the built projection cache") {
val eventStore: EventStore<TestEvents, IdTest> = EventStoreInMemory()
val repo = getSnapshotRepoTest(eventStore)
val aggregateId = IdTest()
val eventOther = Event2Test(value2 = "valOther", version = 1, aggregateId = IdTest())
eventStore.publish(eventOther)
repo.applyAndPutToCache(eventOther)
assertNotNull(repo.getUntil(eventOther)).also {
assertNotNull(it.value) shouldBeEqual "valOther"
}
val eventOther = Event2Test(value2 = "valOther", version = 1, aggregateId = IdTest())
eventStore.publish(eventOther)
repo.applyAndPutToCache(eventOther)
assertNotNull(repo.getUntil(eventOther)).also {
assertNotNull(it.value) shouldBeEqual "valOther"
}
val event1 = Event1Test(value1 = "val1", version = 1, aggregateId = aggregateId)
eventStore.publish(event1)
repo.applyAndPutToCache(event1)
assertNotNull(repo.getLast(event1.aggregateId)).also {
assertNotNull(it.value) shouldBeEqual "val1"
}
assertNotNull(repo.getUntil(event1)).also {
assertNotNull(it.value) shouldBeEqual "val1"
}
val event1 = Event1Test(value1 = "val1", version = 1, aggregateId = aggregateId)
eventStore.publish(event1)
repo.applyAndPutToCache(event1)
assertNotNull(repo.getLast(event1.aggregateId)).also {
assertNotNull(it.value) shouldBeEqual "val1"
}
assertNotNull(repo.getUntil(event1)).also {
assertNotNull(it.value) shouldBeEqual "val1"
}
val event2 = Event2Test(value2 = "val2", version = 2, aggregateId = aggregateId)
eventStore.publish(event2)
repo.applyAndPutToCache(event2)
assertNotNull(repo.getLast(event2.aggregateId)).also {
assertNotNull(it.value) shouldBeEqual "val1val2"
}
assertNotNull(repo.getUntil(event1)).also {
assertNotNull(it.value) shouldBeEqual "val1"
}
assertNotNull(repo.getUntil(event2)).also {
assertNotNull(it.value) shouldBeEqual "val1val2"
}
}
val event2 = Event2Test(value2 = "val2", version = 2, aggregateId = aggregateId)
eventStore.publish(event2)
repo.applyAndPutToCache(event2)
assertNotNull(repo.getLast(event2.aggregateId)).also {
assertNotNull(it.value) shouldBeEqual "val1val2"
}
assertNotNull(repo.getUntil(event1)).also {
assertNotNull(it.value) shouldBeEqual "val1"
}
assertNotNull(repo.getUntil(event2)).also {
assertNotNull(it.value) shouldBeEqual "val1val2"
}
}
test("ProjectionSnapshotRepositoryInMemory should be thread safe") {
val eventStore: EventStore<TestEvents, IdTest> = EventStoreInMemory()
val repo = getSnapshotRepoTest(eventStore)
val aggregateId = IdTest()
val versionBuilder = VersionBuilderLocal()
val lock = ReentrantLock()
(0..9)
.map {
GlobalScope.launch {
(1..10).map {
val eventX =
lock.withLock {
EventXTest(num = 1, version = versionBuilder.buildNextVersion(aggregateId), aggregateId = aggregateId)
.also { eventStore.publish(it) }
}
repo.applyAndPutToCache(eventX)
}
}
}.joinAll()
assertNotNull(repo.getLast(aggregateId)).num shouldBeEqual 100
}
test("removeOldSnapshot") {
val versionBuilder = VersionBuilderLocal()
val eventStore: EventStore<TestEvents, IdTest> = EventStoreInMemory()
val repo = getSnapshotRepoTest(eventStore, SnapshotConfig(2))
val aggregateId = IdTest()
fun buildEndSendEventX() {
EventXTest(num = 1, version = versionBuilder.buildNextVersion(aggregateId), aggregateId = aggregateId)
test("ProjectionSnapshotRepositoryInMemory should be thread safe") {
val eventStore: EventStore<TestEvents, IdTest> = EventStoreInMemory()
val repo = getSnapshotRepoTest(eventStore)
val aggregateId = IdTest()
val versionBuilder = VersionBuilderLocal()
val lock = ReentrantLock()
(0..9)
.map {
GlobalScope.launch {
(1..10).map {
val eventX =
lock.withLock {
EventXTest(num = 1, version = versionBuilder.buildNextVersion(aggregateId), aggregateId = aggregateId)
.also { eventStore.publish(it) }
.also { repo.applyAndPutToCache(it) }
}
repo.applyAndPutToCache(eventX)
}
}
}.joinAll()
assertNotNull(repo.getLast(aggregateId)).num shouldBeEqual 100
}
buildEndSendEventX()
repo.getLast(aggregateId).num shouldBeEqual 1
buildEndSendEventX()
repo.getLast(aggregateId).num shouldBeEqual 2
buildEndSendEventX()
repo.getLast(aggregateId).num shouldBeEqual 3
buildEndSendEventX()
repo.getLast(aggregateId).num shouldBeEqual 4
}
})
test("removeOldSnapshot") {
val versionBuilder = VersionBuilderLocal()
val eventStore: EventStore<TestEvents, IdTest> = EventStoreInMemory()
val repo = getSnapshotRepoTest(eventStore, SnapshotConfig(2))
val aggregateId = IdTest()
fun buildEndSendEventX() {
EventXTest(num = 1, version = versionBuilder.buildNextVersion(aggregateId), aggregateId = aggregateId)
.also { eventStore.publish(it) }
.also { repo.applyAndPutToCache(it) }
}
buildEndSendEventX()
repo.getLast(aggregateId).num shouldBeEqual 1
buildEndSendEventX()
repo.getLast(aggregateId).num shouldBeEqual 2
buildEndSendEventX()
repo.getLast(aggregateId).num shouldBeEqual 3
buildEndSendEventX()
repo.getLast(aggregateId).num shouldBeEqual 4
}
})
@JvmInline
private value class IdTest(
override val id: UUID = UUID.randomUUID(),
override val id: UUID = UUID.randomUUID(),
) : AggregateId
private data class ProjectionTest(
override val aggregateId: IdTest,
override val lastEventVersion: Int = 0,
var value: String? = null,
var num: Int = 0,
override val aggregateId: IdTest,
override val lastEventVersion: Int = 0,
var value: String? = null,
var num: Int = 0,
) : Projection<IdTest>
private sealed interface TestEvents : Event<IdTest>
private data class Event1Test(
override val eventId: UUID = UUID.randomUUID(),
override val aggregateId: IdTest,
override val createdAt: Instant = Clock.System.now(),
override val version: Int,
val value1: String,
override val eventId: UUID = UUID.randomUUID(),
override val aggregateId: IdTest,
override val createdAt: Instant = Clock.System.now(),
override val version: Int,
val value1: String,
) : TestEvents
private data class Event2Test(
override val eventId: UUID = UUID.randomUUID(),
override val aggregateId: IdTest,
override val createdAt: Instant = Clock.System.now(),
override val version: Int,
val value2: String,
override val eventId: UUID = UUID.randomUUID(),
override val aggregateId: IdTest,
override val createdAt: Instant = Clock.System.now(),
override val version: Int,
val value2: String,
) : TestEvents
private data class EventXTest(
override val eventId: UUID = UUID.randomUUID(),
override val aggregateId: IdTest,
override val createdAt: Instant = Clock.System.now(),
override val version: Int,
val num: Int,
override val eventId: UUID = UUID.randomUUID(),
override val aggregateId: IdTest,
override val createdAt: Instant = Clock.System.now(),
override val version: Int,
val num: Int,
) : TestEvents
private fun getSnapshotRepoTest(
eventStore: EventStore<TestEvents, IdTest>,
snapshotConfig: SnapshotConfig = SnapshotConfig(2000),
eventStore: EventStore<TestEvents, IdTest>,
snapshotConfig: SnapshotConfig = SnapshotConfig(2000),
): ProjectionSnapshotRepositoryInMemory<TestEvents, ProjectionTest, IdTest> =
ProjectionSnapshotRepositoryInMemory(
eventStore = eventStore,
initialStateBuilder = { aggregateId: IdTest -> ProjectionTest(aggregateId) },
snapshotCacheConfig = snapshotConfig,
) { event ->
this.let { projection ->
when (event) {
is Event1Test -> {
projection.copy(value = (projection.value ?: "") + event.value1)
}
is Event2Test -> {
projection.copy(value = (projection.value ?: "") + event.value2)
}
is EventXTest -> {
projection.copy(num = projection.num + event.num)
}
}.copy(
lastEventVersion = event.version,
)
ProjectionSnapshotRepositoryInMemory(
eventStore = eventStore,
initialStateBuilder = { aggregateId: IdTest -> ProjectionTest(aggregateId) },
snapshotCacheConfig = snapshotConfig,
) { event ->
this.let { projection ->
when (event) {
is Event1Test -> {
projection.copy(value = (projection.value ?: "") + event.value1)
}
is Event2Test -> {
projection.copy(value = (projection.value ?: "") + event.value2)
}
is EventXTest -> {
projection.copy(num = projection.num + event.num)
}
}.copy(
lastEventVersion = event.version,
)
}
}

View File

@@ -33,89 +33,89 @@ import kotlin.test.assertIs
import kotlin.test.assertNotNull
class GameStateRouteTest :
FunSpec({
test("/game/{id}/state on empty game") {
testApplication {
val id = GameId()
val player1 = Player(name = "Nikola")
application {
stopKoin()
configure()
}
httpClient()
.get("/game/$id/state") {
withAuth(player1)
accept(ContentType.Application.Json)
}.apply {
assertEquals(HttpStatusCode.OK, status, message = bodyAsText())
val state = call.body<GameState>()
id shouldBeEqual state.aggregateId
state.players shouldHaveSize 0
state.isStarted shouldBeEqual false
}
}
FunSpec({
test("/game/{id}/state on empty game") {
testApplication {
val id = GameId()
val player1 = Player(name = "Nikola")
application {
stopKoin()
configure()
}
test("/game/{id}/card/last") {
testApplication {
val gameId = GameId()
val player1 = Player(name = "Nikola")
val player2 = Player(name = "Einstein")
var lastPlayedCard: Card? = null
httpClient()
.get("/game/$id/state") {
withAuth(player1)
accept(ContentType.Application.Json)
}.apply {
assertEquals(HttpStatusCode.OK, status, message = bodyAsText())
val state = call.body<GameState>()
id shouldBeEqual state.aggregateId
state.players shouldHaveSize 0
state.isStarted shouldBeEqual false
}
}
}
application {
stopKoin()
configure()
test("/game/{id}/card/last") {
testApplication {
val gameId = GameId()
val player1 = Player(name = "Nikola")
val player2 = Player(name = "Einstein")
var lastPlayedCard: Card? = null
val eventHandler by inject<GameEventHandler>()
val stateRepo by inject<GameStateRepository>()
runBlocking {
eventHandler.handle(gameId) { NewPlayerEvent(gameId, player1, it) }
eventHandler.handle(gameId) { NewPlayerEvent(gameId, player2, it) }
eventHandler.handle(gameId) { PlayerReadyEvent(gameId, player1, it) }
eventHandler.handle(gameId) { PlayerReadyEvent(gameId, player2, it) }
eventHandler.handle(gameId) {
GameStartedEvent.new(
gameId,
setOf(player1, player2),
shuffleIsDisabled = true,
it,
)
}
delay(100)
lastPlayedCard = stateRepo.getLast(gameId).playableCards(player1).first()
assertNotNull(lastPlayedCard)
.let { assertIs<Card.NumericCard>(lastPlayedCard) }
.let {
it.number shouldBeEqual 0
it.color shouldBeEqual Card.Color.Red
}
delay(100)
eventHandler.handle(gameId) {
CardIsPlayedEvent(
gameId,
assertNotNull(lastPlayedCard),
player1,
it,
)
}
delay(100)
}
}
application {
stopKoin()
configure()
httpClient()
.get("/game/$gameId/card/last") {
withAuth(player1)
accept(ContentType.Application.Json)
}.apply {
assertEquals(HttpStatusCode.OK, status, message = bodyAsText())
assertEquals(assertNotNull(lastPlayedCard), call.body<Card>())
}
val eventHandler by inject<GameEventHandler>()
val stateRepo by inject<GameStateRepository>()
runBlocking {
eventHandler.handle(gameId) { NewPlayerEvent(gameId, player1, it) }
eventHandler.handle(gameId) { NewPlayerEvent(gameId, player2, it) }
eventHandler.handle(gameId) { PlayerReadyEvent(gameId, player1, it) }
eventHandler.handle(gameId) { PlayerReadyEvent(gameId, player2, it) }
eventHandler.handle(gameId) {
GameStartedEvent.new(
gameId,
setOf(player1, player2),
shuffleIsDisabled = true,
it,
)
}
delay(100)
lastPlayedCard = stateRepo.getLast(gameId).playableCards(player1).first()
assertNotNull(lastPlayedCard)
.let { assertIs<Card.NumericCard>(lastPlayedCard) }
.let {
it.number shouldBeEqual 0
it.color shouldBeEqual Card.Color.Red
}
delay(100)
eventHandler.handle(gameId) {
CardIsPlayedEvent(
gameId,
assertNotNull(lastPlayedCard),
player1,
it,
)
}
delay(100)
}
}
})
httpClient()
.get("/game/$gameId/card/last") {
withAuth(player1)
accept(ContentType.Application.Json)
}.apply {
assertEquals(HttpStatusCode.OK, status, message = bodyAsText())
assertEquals(assertNotNull(lastPlayedCard), call.body<Card>())
}
}
}
})
private fun HttpRequestBuilder.withAuth(player: Player) {
header("Authorization", "Bearer ${player.makeJwt()}")
header("Authorization", "Bearer ${player.makeJwt()}")
}

View File

@@ -42,127 +42,127 @@ import kotlin.time.Duration.Companion.seconds
@DelicateCoroutinesApi
class GameStateTest :
FunSpec({
test("Simulation of a game") {
withTimeout(2.seconds) {
disableShuffleDeck()
val id = GameId()
val player1 = Player(name = "Nikola")
val player2 = Player(name = "Einstein")
val channelCommand1 = Channel<GameCommand>(Channel.BUFFERED)
val channelCommand2 = Channel<GameCommand>(Channel.BUFFERED)
val channelNotification1 = Channel<Notification>(Channel.BUFFERED)
val channelNotification2 = Channel<Notification>(Channel.BUFFERED)
FunSpec({
test("Simulation of a game") {
withTimeout(2.seconds) {
disableShuffleDeck()
val id = GameId()
val player1 = Player(name = "Nikola")
val player2 = Player(name = "Einstein")
val channelCommand1 = Channel<GameCommand>(Channel.BUFFERED)
val channelCommand2 = Channel<GameCommand>(Channel.BUFFERED)
val channelNotification1 = Channel<Notification>(Channel.BUFFERED)
val channelNotification2 = Channel<Notification>(Channel.BUFFERED)
var playedCard1: Card? = null
var playedCard2: Card? = null
var playedCard1: Card? = null
var playedCard2: Card? = null
val player1Job =
launch {
channelCommand1.send(IWantToJoinTheGameCommand(IWantToJoinTheGameCommand.Payload(id, player1)))
channelNotification1.receive().let {
assertIs<WelcomeToTheGameNotification>(it).players shouldBeEqual setOf(player1)
}
channelNotification1.receive().let {
assertIs<PlayerAsJoinTheGameNotification>(it).player shouldBeEqual player2
}
channelCommand1.send(IamReadyToPlayCommand(IamReadyToPlayCommand.Payload(id, player1)))
channelNotification1.receive().let {
assertIs<PlayerWasReadyNotification>(it).player shouldBeEqual player2
}
val player1Hand =
channelNotification1.receive().let {
assertIs<TheGameWasStartedNotification>(it).hand shouldHaveSize 7
}
playedCard1 = player1Hand.first()
channelNotification1.receive().let {
assertIs<ItsTheTurnOfNotification>(it).apply {
player shouldBeEqual player1
}
}
channelCommand1.send(IWantToPlayCardCommand(IWantToPlayCardCommand.Payload(id, player1, player1Hand.first())))
channelNotification1.receive().let {
assertIs<ItsTheTurnOfNotification>(it).apply {
player shouldBeEqual player2
}
}
channelNotification1.receive().let {
assertIs<PlayerAsPlayACardNotification>(it).apply {
player shouldBeEqual player2
card shouldBeEqual assertNotNull(playedCard2)
}
}
}
val player2Job =
launch {
delay(100)
channelCommand2.send(IWantToJoinTheGameCommand(IWantToJoinTheGameCommand.Payload(id, player2)))
channelNotification2.receive().let {
assertIs<WelcomeToTheGameNotification>(it).players shouldBeEqual setOf(player1, player2)
}
channelNotification2.receive().let {
assertIs<PlayerWasReadyNotification>(it).player shouldBeEqual player1
}
channelCommand2.send(IamReadyToPlayCommand(IamReadyToPlayCommand.Payload(id, player2)))
val player2Hand =
channelNotification2.receive().let {
assertIs<TheGameWasStartedNotification>(it).hand shouldHaveSize 7
}
channelNotification2.receive().let {
assertIs<ItsTheTurnOfNotification>(it).apply {
player shouldBeEqual player1
}
}
channelNotification2.receive().let {
assertIs<PlayerAsPlayACardNotification>(it).apply {
player shouldBeEqual player1
card shouldBeEqual assertNotNull(playedCard1)
}
}
playedCard2 = player2Hand.first()
channelNotification2.receive().let {
assertIs<ItsTheTurnOfNotification>(it).apply {
player shouldBeEqual player2
}
}
channelCommand2.send(IWantToPlayCardCommand(IWantToPlayCardCommand.Payload(id, player2, player2Hand.first())))
}
koinApplication { modules(appKoinModule) }.koin.apply {
val commandHandler by inject<GameCommandHandler>()
val eventStore by inject<GameEventStore>()
val playerNotificationListener by inject<PlayerNotificationEventListener>()
ReactionEventListener(get(), get(), get()).init()
playerNotificationListener.startListening(channelNotification1, player1)
playerNotificationListener.startListening(channelNotification2, player2)
GlobalScope.launch(Dispatchers.IO) {
commandHandler.handle(player1, channelCommand1, channelNotification1)
}
GlobalScope.launch(Dispatchers.IO) {
commandHandler.handle(player2, channelCommand2, channelNotification2)
}
joinAll(player1Job, player2Job)
val state =
ProjectionSnapshotRepositoryInMemory(
eventStore = eventStore,
initialStateBuilder = { aggregateId: GameId -> GameState(aggregateId) },
applyToProjection = GameState::apply,
).getLast(id)
state.aggregateId shouldBeEqual id
assertTrue(state.isStarted)
state.players shouldBeEqual setOf(player1, player2)
state.readyPlayers shouldBeEqual setOf(player1, player2)
state.direction shouldBeEqual GameState.Direction.CLOCKWISE
assertNotNull(state.cardOnCurrentStack) shouldBeEqual GameState.LastCard(assertNotNull(playedCard2), player2)
}
val player1Job =
launch {
channelCommand1.send(IWantToJoinTheGameCommand(IWantToJoinTheGameCommand.Payload(id, player1)))
channelNotification1.receive().let {
assertIs<WelcomeToTheGameNotification>(it).players shouldBeEqual setOf(player1)
}
channelNotification1.receive().let {
assertIs<PlayerAsJoinTheGameNotification>(it).player shouldBeEqual player2
}
channelCommand1.send(IamReadyToPlayCommand(IamReadyToPlayCommand.Payload(id, player1)))
channelNotification1.receive().let {
assertIs<PlayerWasReadyNotification>(it).player shouldBeEqual player2
}
val player1Hand =
channelNotification1.receive().let {
assertIs<TheGameWasStartedNotification>(it).hand shouldHaveSize 7
}
playedCard1 = player1Hand.first()
channelNotification1.receive().let {
assertIs<ItsTheTurnOfNotification>(it).apply {
player shouldBeEqual player1
}
}
channelCommand1.send(IWantToPlayCardCommand(IWantToPlayCardCommand.Payload(id, player1, player1Hand.first())))
channelNotification1.receive().let {
assertIs<ItsTheTurnOfNotification>(it).apply {
player shouldBeEqual player2
}
}
channelNotification1.receive().let {
assertIs<PlayerAsPlayACardNotification>(it).apply {
player shouldBeEqual player2
card shouldBeEqual assertNotNull(playedCard2)
}
}
}
val player2Job =
launch {
delay(100)
channelCommand2.send(IWantToJoinTheGameCommand(IWantToJoinTheGameCommand.Payload(id, player2)))
channelNotification2.receive().let {
assertIs<WelcomeToTheGameNotification>(it).players shouldBeEqual setOf(player1, player2)
}
channelNotification2.receive().let {
assertIs<PlayerWasReadyNotification>(it).player shouldBeEqual player1
}
channelCommand2.send(IamReadyToPlayCommand(IamReadyToPlayCommand.Payload(id, player2)))
val player2Hand =
channelNotification2.receive().let {
assertIs<TheGameWasStartedNotification>(it).hand shouldHaveSize 7
}
channelNotification2.receive().let {
assertIs<ItsTheTurnOfNotification>(it).apply {
player shouldBeEqual player1
}
}
channelNotification2.receive().let {
assertIs<PlayerAsPlayACardNotification>(it).apply {
player shouldBeEqual player1
card shouldBeEqual assertNotNull(playedCard1)
}
}
playedCard2 = player2Hand.first()
channelNotification2.receive().let {
assertIs<ItsTheTurnOfNotification>(it).apply {
player shouldBeEqual player2
}
}
channelCommand2.send(IWantToPlayCardCommand(IWantToPlayCardCommand.Payload(id, player2, player2Hand.first())))
}
koinApplication { modules(appKoinModule) }.koin.apply {
val commandHandler by inject<GameCommandHandler>()
val eventStore by inject<GameEventStore>()
val playerNotificationListener by inject<PlayerNotificationEventListener>()
ReactionEventListener(get(), get(), get()).init()
playerNotificationListener.startListening(channelNotification1, player1)
playerNotificationListener.startListening(channelNotification2, player2)
GlobalScope.launch(Dispatchers.IO) {
commandHandler.handle(player1, channelCommand1, channelNotification1)
}
GlobalScope.launch(Dispatchers.IO) {
commandHandler.handle(player2, channelCommand2, channelNotification2)
}
joinAll(player1Job, player2Job)
val state =
ProjectionSnapshotRepositoryInMemory(
eventStore = eventStore,
initialStateBuilder = { aggregateId: GameId -> GameState(aggregateId) },
applyToProjection = GameState::apply,
).getLast(id)
state.aggregateId shouldBeEqual id
assertTrue(state.isStarted)
state.players shouldBeEqual setOf(player1, player2)
state.readyPlayers shouldBeEqual setOf(player1, player2)
state.direction shouldBeEqual GameState.Direction.CLOCKWISE
assertNotNull(state.cardOnCurrentStack) shouldBeEqual GameState.LastCard(assertNotNull(playedCard2), player2)
}
})
}
}
})

View File

@@ -6,10 +6,10 @@ import io.ktor.serialization.kotlinx.json.json
import io.ktor.server.testing.ApplicationTestBuilder
fun ApplicationTestBuilder.httpClient(): HttpClient =
createClient {
install(ContentNegotiation) {
json(
defaultJsonSerializer(),
)
}
createClient {
install(ContentNegotiation) {
json(
defaultJsonSerializer(),
)
}
}

View File

@@ -14,42 +14,42 @@ import kotlin.test.assertIs
@Serializable
data class CommandTest(
override val id: CommandId,
override val id: CommandId,
) : Command
class FrameChannelConverterTest :
FunSpec({
FunSpec({
test("toObjectChannel") {
val uuid = "d737c631-76af-406e-bc29-f3e5b97226a5"
val id = CommandId(UUID.fromString(uuid))
val jsonCommand = """{"id":"$uuid"}"""
test("toObjectChannel") {
val uuid = "d737c631-76af-406e-bc29-f3e5b97226a5"
val id = CommandId(UUID.fromString(uuid))
val jsonCommand = """{"id":"$uuid"}"""
val channel = Channel<Frame>()
val channel = Channel<Frame>()
launch {
val commandChannel = toObjectChannel<CommandTest>(channel)
commandChannel.receive().id shouldBeEqual id
channel.close()
}
launch {
val commandChannel = toObjectChannel<CommandTest>(channel)
commandChannel.receive().id shouldBeEqual id
channel.close()
}
channel.send(Frame.Text(jsonCommand))
}
channel.send(Frame.Text(jsonCommand))
}
test("fromFrameChannel") {
val uuid = "d737c631-76af-406e-bc29-f3e5b97226a5"
val id = CommandId(UUID.fromString(uuid))
val command = CommandTest(id)
val jsonCommand = """{"id":"$uuid"}"""
test("fromFrameChannel") {
val uuid = "d737c631-76af-406e-bc29-f3e5b97226a5"
val id = CommandId(UUID.fromString(uuid))
val command = CommandTest(id)
val jsonCommand = """{"id":"$uuid"}"""
val channel = Channel<Frame>()
val channel = Channel<Frame>()
launch {
val commandChannel = fromFrameChannel<CommandTest>(channel)
commandChannel.send(command)
commandChannel.close()
}
launch {
val commandChannel = fromFrameChannel<CommandTest>(channel)
commandChannel.send(command)
commandChannel.close()
}
assertIs<Frame.Text>(channel.receive()).readText() shouldBeEqual jsonCommand
}
})
assertIs<Frame.Text>(channel.receive()).readText() shouldBeEqual jsonCommand
}
})

View File

@@ -8,26 +8,26 @@ import kotlinx.serialization.Serializable
@Serializable
class CommandTest(
override val id: CommandId,
override val id: CommandId,
) : Command
class CommandStreamChannelTest :
FunSpec({
FunSpec({
test("send and receive") {
val command = CommandTest(CommandId())
test("send and receive") {
val command = CommandTest(CommandId())
val channel = Channel<CommandTest>()
val stream =
CommandStreamChannel(channel)
val channel = Channel<CommandTest>()
val stream =
CommandStreamChannel(channel)
val spyCall: () -> Unit = mockk(relaxed = true)
val spyCall: () -> Unit = mockk(relaxed = true)
stream.blockAndProcess {
println("In action ${it.id}")
spyCall()
}
channel.send(command)
verify(exactly = 1) { spyCall() }
}
})
stream.blockAndProcess {
println("In action ${it.id}")
spyCall()
}
channel.send(command)
verify(exactly = 1) { spyCall() }
}
})

View File

@@ -3,8 +3,8 @@ package eventDemo.libs.event
import io.kotest.core.spec.style.FunSpec
class EventBusInMemoryTest :
FunSpec({
FunSpec({
xtest("publish should call the subscribed functions") { }
xtest("publish should call the subscribed functions on the priority order") { }
})
xtest("publish should call the subscribed functions") { }
xtest("publish should call the subscribed functions on the priority order") { }
})

View File

@@ -3,14 +3,14 @@ package eventDemo.libs.event
import io.kotest.core.spec.style.FunSpec
class EventStreamInMemoryTest :
FunSpec({
FunSpec({
xtest("publish should be concurrently secure") { }
xtest("publish should be concurrently secure") { }
xtest("readLast should only return the event of aggregate") { }
xtest("readLast should return the last event of the aggregate") { }
xtest("readLast should only return the event of aggregate") { }
xtest("readLast should return the last event of the aggregate") { }
xtest("readLastOf should return the last event of the aggregate of the type") { }
xtest("readLastOf should return the last event of the aggregate of the type") { }
xtest("readAll should only return the event of aggregate") { }
})
xtest("readAll should only return the event of aggregate") { }
})

View File

@@ -10,43 +10,43 @@ import java.util.UUID
@JvmInline
private value class IdTest(
override val id: UUID = UUID.randomUUID(),
override val id: UUID = UUID.randomUUID(),
) : AggregateId
@OptIn(DelicateCoroutinesApi::class)
class VersionBuilderLocalTest :
FunSpec({
FunSpec({
test("buildNextVersion") {
VersionBuilderLocal().run {
val id = IdTest()
buildNextVersion(id) shouldBeEqual 1
buildNextVersion(id) shouldBeEqual 2
buildNextVersion(IdTest()) shouldBeEqual 1
buildNextVersion(id) shouldBeEqual 3
test("buildNextVersion") {
VersionBuilderLocal().run {
val id = IdTest()
buildNextVersion(id) shouldBeEqual 1
buildNextVersion(id) shouldBeEqual 2
buildNextVersion(IdTest()) shouldBeEqual 1
buildNextVersion(id) shouldBeEqual 3
}
}
test("buildNextVersion concurrently") {
val versionBuilder = VersionBuilderLocal()
val id = IdTest()
(1..20)
.map {
GlobalScope.launch {
(1..1000).map {
versionBuilder.buildNextVersion(id)
}
}
}
}.joinAll()
versionBuilder.getLastVersion(id) shouldBeEqual 20 * 1000
}
test("buildNextVersion concurrently") {
val versionBuilder = VersionBuilderLocal()
val id = IdTest()
(1..20)
.map {
GlobalScope.launch {
(1..1000).map {
versionBuilder.buildNextVersion(id)
}
}
}.joinAll()
versionBuilder.getLastVersion(id) shouldBeEqual 20 * 1000
}
test("getLastVersion") {
VersionBuilderLocal().run {
val id = IdTest()
getLastVersion(id) shouldBeEqual 0
getLastVersion(id) shouldBeEqual 0
getLastVersion(id) shouldBeEqual 0
}
}
})
test("getLastVersion") {
VersionBuilderLocal().run {
val id = IdTest()
getLastVersion(id) shouldBeEqual 0
getLastVersion(id) shouldBeEqual 0
getLastVersion(id) shouldBeEqual 0
}
}
})