summaryrefslogtreecommitdiff
path: root/backend/src/test/kotlin
diff options
context:
space:
mode:
authorJoffrey Bion <joffrey.bion@amadeus.com>2018-07-05 10:38:14 +0200
committerJoffrey BION <joffrey.bion@gmail.com>2018-07-14 03:06:23 +0200
commit2c28e6e21137ec6bb1ae6ca34860ad920c289426 (patch)
tree67ba2f866e2b9d5188b26b0d3f9924b110587671 /backend/src/test/kotlin
parentRework visibilities (diff)
downloadseven-wonders-2c28e6e21137ec6bb1ae6ca34860ad920c289426.tar.gz
seven-wonders-2c28e6e21137ec6bb1ae6ca34860ad920c289426.tar.bz2
seven-wonders-2c28e6e21137ec6bb1ae6ca34860ad920c289426.zip
Kotlin migration: Spring server
Diffstat (limited to 'backend/src/test/kotlin')
-rw-r--r--backend/src/test/kotlin/org/luxons/sevenwonders/SevenWondersTest.kt151
-rw-r--r--backend/src/test/kotlin/org/luxons/sevenwonders/controllers/GameBrowserControllerTest.kt114
-rw-r--r--backend/src/test/kotlin/org/luxons/sevenwonders/controllers/HomeControllerTest.kt26
-rw-r--r--backend/src/test/kotlin/org/luxons/sevenwonders/controllers/LobbyControllerTest.kt199
-rw-r--r--backend/src/test/kotlin/org/luxons/sevenwonders/controllers/TestPrincipal.kt10
-rw-r--r--backend/src/test/kotlin/org/luxons/sevenwonders/lobby/LobbyTest.kt229
-rw-r--r--backend/src/test/kotlin/org/luxons/sevenwonders/repositories/LobbyRepositoryTest.kt70
-rw-r--r--backend/src/test/kotlin/org/luxons/sevenwonders/repositories/PlayerRepositoryTest.kt67
-rw-r--r--backend/src/test/kotlin/org/luxons/sevenwonders/test/TestUtils.kt21
-rw-r--r--backend/src/test/kotlin/org/luxons/sevenwonders/test/api/ApiBoard.kt64
-rw-r--r--backend/src/test/kotlin/org/luxons/sevenwonders/test/api/ApiCard.kt38
-rw-r--r--backend/src/test/kotlin/org/luxons/sevenwonders/test/api/ApiCardBack.kt6
-rw-r--r--backend/src/test/kotlin/org/luxons/sevenwonders/test/api/ApiEffect.kt3
-rw-r--r--backend/src/test/kotlin/org/luxons/sevenwonders/test/api/ApiHandCard.kt16
-rw-r--r--backend/src/test/kotlin/org/luxons/sevenwonders/test/api/ApiLobby.kt36
-rw-r--r--backend/src/test/kotlin/org/luxons/sevenwonders/test/api/ApiMilitary.kt10
-rw-r--r--backend/src/test/kotlin/org/luxons/sevenwonders/test/api/ApiPlayer.kt27
-rw-r--r--backend/src/test/kotlin/org/luxons/sevenwonders/test/api/ApiPlayerTurnInfo.kt37
-rw-r--r--backend/src/test/kotlin/org/luxons/sevenwonders/test/api/ApiProduction.kt11
-rw-r--r--backend/src/test/kotlin/org/luxons/sevenwonders/test/api/ApiScience.kt10
-rw-r--r--backend/src/test/kotlin/org/luxons/sevenwonders/test/api/ApiTable.kt23
-rw-r--r--backend/src/test/kotlin/org/luxons/sevenwonders/test/api/ApiTradingRules.kt9
-rw-r--r--backend/src/test/kotlin/org/luxons/sevenwonders/test/api/ApiWonder.kt16
-rw-r--r--backend/src/test/kotlin/org/luxons/sevenwonders/test/api/ApiWonderStage.kt13
-rw-r--r--backend/src/test/kotlin/org/luxons/sevenwonders/test/api/SevenWondersClient.kt25
-rw-r--r--backend/src/test/kotlin/org/luxons/sevenwonders/test/api/SevenWondersSession.kt70
-rw-r--r--backend/src/test/kotlin/org/luxons/sevenwonders/validation/DestinationAccessValidatorTest.kt138
27 files changed, 1439 insertions, 0 deletions
diff --git a/backend/src/test/kotlin/org/luxons/sevenwonders/SevenWondersTest.kt b/backend/src/test/kotlin/org/luxons/sevenwonders/SevenWondersTest.kt
new file mode 100644
index 00000000..cf6c7ce8
--- /dev/null
+++ b/backend/src/test/kotlin/org/luxons/sevenwonders/SevenWondersTest.kt
@@ -0,0 +1,151 @@
+package org.luxons.sevenwonders
+
+import org.junit.After
+import org.junit.Assert.*
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.luxons.sevenwonders.test.api.SevenWondersClient
+import org.luxons.sevenwonders.test.api.SevenWondersSession
+import org.springframework.boot.test.context.SpringBootTest
+import org.springframework.boot.test.context.SpringBootTest.WebEnvironment
+import org.springframework.boot.web.server.LocalServerPort
+import org.springframework.test.context.junit4.SpringRunner
+import java.util.concurrent.ExecutionException
+import java.util.concurrent.TimeUnit
+import java.util.concurrent.TimeoutException
+
+@RunWith(SpringRunner::class)
+@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
+class SevenWondersTest {
+
+ @LocalServerPort
+ private val randomServerPort: Int = 0
+
+ private lateinit var client: SevenWondersClient
+
+ private lateinit var serverUrl: String
+
+ @Before
+ fun setUpClientAndUrl() {
+ client = SevenWondersClient()
+ serverUrl = "ws://localhost:$randomServerPort"
+ }
+
+ private fun disconnect(vararg sessions: SevenWondersSession) {
+ for (session in sessions) {
+ session.disconnect()
+ }
+ }
+
+ @Test
+ @Throws(InterruptedException::class, ExecutionException::class, TimeoutException::class)
+ fun chooseName() {
+ val session = client.connect(serverUrl)
+ val playerName = "Test User"
+ val player = session.chooseName(playerName)
+ assertNotNull(player)
+ assertEquals(playerName, player.displayName)
+ session.disconnect()
+ }
+
+ @Throws(InterruptedException::class, TimeoutException::class, ExecutionException::class)
+ private fun newPlayer(name: String): SevenWondersSession {
+ val otherSession = client.connect(serverUrl)
+ otherSession.chooseName(name)
+ return otherSession
+ }
+
+ @Test
+ @Throws(InterruptedException::class, ExecutionException::class, TimeoutException::class)
+ fun lobbySubscription_ignoredForOutsiders() {
+ val ownerSession = newPlayer("GameOwner")
+ val session1 = newPlayer("Player1")
+ val session2 = newPlayer("Player2")
+ val gameName = "Test Game"
+ val lobby = ownerSession.createGame(gameName)
+ session1.joinGame(lobby.id)
+ session2.joinGame(lobby.id)
+
+ val outsiderSession = newPlayer("Outsider")
+ val session = outsiderSession.jackstompSession
+ val started = session.subscribeEmptyMsgs("/topic/lobby/" + lobby.id + "/started")
+
+ ownerSession.startGame(lobby.id)
+ val nothing = started.next(1, TimeUnit.SECONDS)
+ assertNull(nothing)
+ disconnect(ownerSession, session1, session2, outsiderSession)
+ }
+
+ @Test
+ @Throws(InterruptedException::class, ExecutionException::class, TimeoutException::class)
+ fun createGame_success() {
+ val ownerSession = newPlayer("GameOwner")
+
+ val gameName = "Test Game"
+ val lobby = ownerSession.createGame(gameName)
+ assertNotNull(lobby)
+ assertEquals(gameName, lobby.name)
+
+ disconnect(ownerSession)
+ }
+
+ @Test
+ @Throws(InterruptedException::class, ExecutionException::class, TimeoutException::class)
+ fun createGame_seenByConnectedPlayers() {
+ val otherSession = newPlayer("OtherPlayer")
+ val games = otherSession.watchGames()
+
+ var receivedLobbies = games.next()
+ assertNotNull(receivedLobbies)
+ assertEquals(0, receivedLobbies.size.toLong())
+
+ val ownerSession = newPlayer("GameOwner")
+ val gameName = "Test Game"
+ val createdLobby = ownerSession.createGame(gameName)
+
+ receivedLobbies = games.next()
+ assertNotNull(receivedLobbies)
+ assertEquals(1, receivedLobbies.size.toLong())
+ val receivedLobby = receivedLobbies[0]
+ assertEquals(createdLobby.id, receivedLobby.id)
+ assertEquals(createdLobby.name, receivedLobby.name)
+
+ disconnect(ownerSession, otherSession)
+ }
+
+ @Test
+ @Throws(Exception::class)
+ fun startGame_3players() {
+ val session1 = newPlayer("Player1")
+ val session2 = newPlayer("Player2")
+
+ val lobby = session1.createGame("Test Game")
+ session2.joinGame(lobby.id)
+
+ val session3 = newPlayer("Player3")
+ session3.joinGame(lobby.id)
+
+ session1.startGame(lobby.id)
+
+ val turns1 = session1.watchTurns()
+ val turns2 = session2.watchTurns()
+ val turns3 = session3.watchTurns()
+ session1.sayReady()
+ session2.sayReady()
+ session3.sayReady()
+ val turn1 = turns1.next()
+ val turn2 = turns2.next()
+ val turn3 = turns3.next()
+ assertNotNull(turn1)
+ assertNotNull(turn2)
+ assertNotNull(turn3)
+
+ disconnect(session1, session2, session3)
+ }
+
+ @After
+ fun tearDown() {
+ client.stop()
+ }
+}
diff --git a/backend/src/test/kotlin/org/luxons/sevenwonders/controllers/GameBrowserControllerTest.kt b/backend/src/test/kotlin/org/luxons/sevenwonders/controllers/GameBrowserControllerTest.kt
new file mode 100644
index 00000000..96d4bc85
--- /dev/null
+++ b/backend/src/test/kotlin/org/luxons/sevenwonders/controllers/GameBrowserControllerTest.kt
@@ -0,0 +1,114 @@
+package org.luxons.sevenwonders.controllers
+
+import org.junit.Assert.*
+import org.junit.Before
+import org.junit.Test
+import org.luxons.sevenwonders.actions.CreateGameAction
+import org.luxons.sevenwonders.actions.JoinGameAction
+import org.luxons.sevenwonders.controllers.GameBrowserController.UserAlreadyInGameException
+import org.luxons.sevenwonders.repositories.LobbyRepository
+import org.luxons.sevenwonders.repositories.PlayerNotFoundException
+import org.luxons.sevenwonders.repositories.PlayerRepository
+import org.luxons.sevenwonders.test.TestUtils
+
+class GameBrowserControllerTest {
+
+ private lateinit var playerRepository: PlayerRepository
+
+ private lateinit var gameBrowserController: GameBrowserController
+
+ @Before
+ fun setUp() {
+ playerRepository = PlayerRepository()
+ val lobbyRepository = LobbyRepository()
+ val template = TestUtils.createSimpMessagingTemplate()
+ val lobbyController = LobbyController(lobbyRepository, playerRepository, template)
+ gameBrowserController = GameBrowserController(lobbyController, lobbyRepository, playerRepository, template)
+ }
+
+ @Test
+ fun listGames_initiallyEmpty() {
+ val principal = TestPrincipal("testuser")
+ val games = gameBrowserController.listGames(principal)
+ assertTrue(games.isEmpty())
+ }
+
+ @Test
+ fun createGame_success() {
+ val player = playerRepository.createOrUpdate("testuser", "Test User")
+ val principal = TestPrincipal("testuser")
+
+ val action = CreateGameAction("Test Game")
+
+ val createdLobby = gameBrowserController.createGame(action, principal)
+
+ assertEquals("Test Game", createdLobby.name)
+
+ val games = gameBrowserController.listGames(principal)
+ assertFalse(games.isEmpty())
+ val lobby = games.iterator().next()
+ assertSame(lobby, createdLobby)
+ assertSame(player, lobby.getPlayers()[0])
+ }
+
+ @Test(expected = PlayerNotFoundException::class)
+ fun createGame_failsForUnknownPlayer() {
+ val principal = TestPrincipal("unknown")
+
+ val action = CreateGameAction("Test Game")
+ gameBrowserController.createGame(action, principal)
+ }
+
+ @Test(expected = UserAlreadyInGameException::class)
+ fun createGame_failsWhenAlreadyInGame() {
+ playerRepository.createOrUpdate("testuser", "Test User")
+ val principal = TestPrincipal("testuser")
+
+ val createGameAction1 = CreateGameAction("Test Game 1")
+
+ // auto-enters the game
+ gameBrowserController.createGame(createGameAction1, principal)
+
+ val createGameAction2 = CreateGameAction("Test Game 2")
+
+ // already in a game
+ gameBrowserController.createGame(createGameAction2, principal)
+ }
+
+ @Test
+ fun joinGame_success() {
+ val owner = playerRepository.createOrUpdate("testowner", "Test User Owner")
+ val ownerPrincipal = TestPrincipal("testowner")
+ val createGameAction = CreateGameAction("Test Game")
+
+ val createdLobby = gameBrowserController.createGame(createGameAction, ownerPrincipal)
+
+ val joiner = playerRepository.createOrUpdate("testjoiner", "Test User Joiner")
+ val joinerPrincipal = TestPrincipal("testjoiner")
+ val joinGameAction = JoinGameAction(createdLobby.id)
+
+ val joinedLobby = gameBrowserController.joinGame(joinGameAction, joinerPrincipal)
+
+ assertSame(createdLobby, joinedLobby)
+ assertSame(owner, joinedLobby.getPlayers()[0])
+ assertSame(joiner, joinedLobby.getPlayers()[1])
+ }
+
+ @Test(expected = UserAlreadyInGameException::class)
+ fun joinGame_failsWhenAlreadyInGame() {
+ playerRepository.createOrUpdate("testowner", "Test User Owner")
+ val ownerPrincipal = TestPrincipal("testowner")
+ val createGameAction = CreateGameAction("Test Game")
+
+ val createdLobby = gameBrowserController.createGame(createGameAction, ownerPrincipal)
+
+ playerRepository.createOrUpdate("testjoiner", "Test User Joiner")
+ val joinerPrincipal = TestPrincipal("testjoiner")
+ val joinGameAction = JoinGameAction(createdLobby.id)
+
+ // joins the game
+ gameBrowserController.joinGame(joinGameAction, joinerPrincipal)
+ // should fail because already in a game
+ gameBrowserController.joinGame(joinGameAction, joinerPrincipal)
+ }
+}
diff --git a/backend/src/test/kotlin/org/luxons/sevenwonders/controllers/HomeControllerTest.kt b/backend/src/test/kotlin/org/luxons/sevenwonders/controllers/HomeControllerTest.kt
new file mode 100644
index 00000000..b11ef878
--- /dev/null
+++ b/backend/src/test/kotlin/org/luxons/sevenwonders/controllers/HomeControllerTest.kt
@@ -0,0 +1,26 @@
+package org.luxons.sevenwonders.controllers
+
+import org.junit.Assert.*
+import org.junit.Test
+import org.luxons.sevenwonders.actions.ChooseNameAction
+import org.luxons.sevenwonders.repositories.PlayerRepository
+
+class HomeControllerTest {
+
+ @Test
+ fun chooseName() {
+ val playerRepository = PlayerRepository()
+ val homeController = HomeController(playerRepository)
+
+ val action = ChooseNameAction("Test User")
+ val principal = TestPrincipal("testuser")
+
+ val player = homeController.chooseName(action, principal)
+
+ assertSame(player, playerRepository.find("testuser"))
+ assertEquals("testuser", player.username)
+ assertEquals("Test User", player.displayName)
+ assertFalse(player.isInLobby)
+ assertFalse(player.isInGame)
+ }
+}
diff --git a/backend/src/test/kotlin/org/luxons/sevenwonders/controllers/LobbyControllerTest.kt b/backend/src/test/kotlin/org/luxons/sevenwonders/controllers/LobbyControllerTest.kt
new file mode 100644
index 00000000..6269ae40
--- /dev/null
+++ b/backend/src/test/kotlin/org/luxons/sevenwonders/controllers/LobbyControllerTest.kt
@@ -0,0 +1,199 @@
+package org.luxons.sevenwonders.controllers
+
+import org.junit.Assert.*
+import org.junit.Before
+import org.junit.Test
+import org.luxons.sevenwonders.actions.ReorderPlayersAction
+import org.luxons.sevenwonders.actions.UpdateSettingsAction
+import org.luxons.sevenwonders.game.api.CustomizableSettings
+import org.luxons.sevenwonders.game.data.WonderSidePickMethod.ALL_A
+import org.luxons.sevenwonders.lobby.Lobby
+import org.luxons.sevenwonders.lobby.Player
+import org.luxons.sevenwonders.lobby.PlayerIsNotOwnerException
+import org.luxons.sevenwonders.lobby.PlayerNotInLobbyException
+import org.luxons.sevenwonders.lobby.State
+import org.luxons.sevenwonders.repositories.LobbyRepository
+import org.luxons.sevenwonders.repositories.PlayerNotFoundException
+import org.luxons.sevenwonders.repositories.PlayerRepository
+import org.luxons.sevenwonders.test.TestUtils
+import java.util.Arrays
+import java.util.HashMap
+
+class LobbyControllerTest {
+
+ private lateinit var playerRepository: PlayerRepository
+
+ private lateinit var lobbyRepository: LobbyRepository
+
+ private lateinit var lobbyController: LobbyController
+
+ @Before
+ fun setUp() {
+ val template = TestUtils.createSimpMessagingTemplate()
+ playerRepository = PlayerRepository()
+ lobbyRepository = LobbyRepository()
+ lobbyController = LobbyController(lobbyRepository, playerRepository, template)
+ }
+
+ @Test
+ fun init_succeeds() {
+ val owner = playerRepository.createOrUpdate("testuser", "Test User")
+ val lobby = lobbyRepository.create("Test Game", owner)
+
+ assertTrue(lobby.getPlayers().contains(owner))
+ assertSame(lobby, owner.lobby)
+ assertEquals(owner.username, lobby.owner)
+ assertTrue(owner.isInLobby)
+ assertFalse(owner.isInGame)
+ }
+
+ @Test(expected = PlayerNotFoundException::class)
+ fun leave_failsWhenPlayerDoesNotExist() {
+ val principal = TestPrincipal("I don't exist")
+ lobbyController.leave(principal)
+ }
+
+ @Test(expected = PlayerNotInLobbyException::class)
+ fun leave_failsWhenNotInLobby() {
+ playerRepository.createOrUpdate("testuser", "Test User")
+ val principal = TestPrincipal("testuser")
+ lobbyController.leave(principal)
+ }
+
+ @Test
+ fun leave_succeedsWhenInALobby_asOwner() {
+ val player = playerRepository.createOrUpdate("testuser", "Test User")
+ val lobby = lobbyRepository.create("Test Game", player)
+
+ val principal = TestPrincipal("testuser")
+ lobbyController.leave(principal)
+
+ assertFalse(lobbyRepository.list().contains(lobby))
+ assertFalse(player.isInLobby)
+ assertFalse(player.isInGame)
+ }
+
+ @Test
+ fun leave_succeedsWhenInALobby_asPeasant() {
+ val player = playerRepository.createOrUpdate("testuser", "Test User")
+ val lobby = lobbyRepository.create("Test Game", player)
+ val player2 = addPlayer(lobby, "testuser2")
+
+ val principal = TestPrincipal("testuser2")
+ lobbyController.leave(principal)
+
+ assertFalse(lobby.getPlayers().contains(player2))
+ assertFalse(player2.isInLobby)
+ assertFalse(player2.isInGame)
+ }
+
+ @Test
+ fun reorderPlayers_succeedsForOwner() {
+ val player = playerRepository.createOrUpdate("testuser", "Test User")
+ val lobby = lobbyRepository.create("Test Game", player)
+
+ val player2 = addPlayer(lobby, "testuser2")
+ val player3 = addPlayer(lobby, "testuser3")
+ val player4 = addPlayer(lobby, "testuser4")
+
+ val players = Arrays.asList(player, player2, player3, player4)
+ assertEquals(players, lobby.getPlayers())
+
+ val reorderedPlayers = Arrays.asList(player3, player, player2, player4)
+ val playerNames = reorderedPlayers.map { it.username }
+ val reorderPlayersAction = ReorderPlayersAction(playerNames)
+
+ val principal = TestPrincipal("testuser")
+ lobbyController.reorderPlayers(reorderPlayersAction, principal)
+
+ assertEquals(reorderedPlayers, lobby.getPlayers())
+ }
+
+ @Test(expected = PlayerIsNotOwnerException::class)
+ fun reorderPlayers_failsForPeasant() {
+ val player = playerRepository.createOrUpdate("testuser", "Test User")
+ val lobby = lobbyRepository.create("Test Game", player)
+
+ val player2 = addPlayer(lobby, "testuser2")
+ val player3 = addPlayer(lobby, "testuser3")
+
+ val reorderedPlayers = Arrays.asList(player3, player, player2)
+ val playerNames = reorderedPlayers.map { it.username }
+ val reorderPlayersAction = ReorderPlayersAction(playerNames)
+
+ val principal = TestPrincipal("testuser2")
+ lobbyController.reorderPlayers(reorderPlayersAction, principal)
+ }
+
+ @Test
+ fun updateSettings_succeedsForOwner() {
+ val player = playerRepository.createOrUpdate("testuser", "Test User")
+ val lobby = lobbyRepository.create("Test Game", player)
+
+ addPlayer(lobby, "testuser2")
+ addPlayer(lobby, "testuser3")
+ addPlayer(lobby, "testuser4")
+
+ assertEquals(CustomizableSettings(), lobby.settings)
+
+ val newSettings = CustomizableSettings(12L, 5, ALL_A, 5, 5, 4, 10, 2, HashMap())
+ val updateSettingsAction = UpdateSettingsAction(newSettings)
+
+ val principal = TestPrincipal("testuser")
+ lobbyController.updateSettings(updateSettingsAction, principal)
+
+ assertEquals(newSettings, lobby.settings)
+ }
+
+ @Test(expected = PlayerIsNotOwnerException::class)
+ fun updateSettings_failsForPeasant() {
+ val player = playerRepository.createOrUpdate("testuser", "Test User")
+ val lobby = lobbyRepository.create("Test Game", player)
+
+ addPlayer(lobby, "testuser2")
+ addPlayer(lobby, "testuser3")
+
+ val updateSettingsAction = UpdateSettingsAction(CustomizableSettings())
+
+ val principal = TestPrincipal("testuser2")
+ lobbyController.updateSettings(updateSettingsAction, principal)
+ }
+
+ @Test
+ fun startGame_succeedsForOwner() {
+ val player = playerRepository.createOrUpdate("testuser", "Test User")
+ val lobby = lobbyRepository.create("Test Game", player)
+
+ addPlayer(lobby, "testuser2")
+ addPlayer(lobby, "testuser3")
+ addPlayer(lobby, "testuser4")
+
+ val principal = TestPrincipal("testuser")
+ lobbyController.startGame(principal)
+
+ assertSame(State.PLAYING, lobby.state)
+ }
+
+ @Test(expected = PlayerIsNotOwnerException::class)
+ fun startGame_failsForPeasant() {
+ val player = playerRepository.createOrUpdate("testuser", "Test User")
+ val lobby = lobbyRepository.create("Test Game", player)
+
+ addPlayer(lobby, "testuser2")
+ addPlayer(lobby, "testuser3")
+
+ val principal = TestPrincipal("testuser2")
+ lobbyController.startGame(principal)
+ }
+
+ private fun addPlayer(lobby: Lobby, username: String): Player {
+ val player = playerRepository.createOrUpdate(username, username)
+ lobby.addPlayer(player)
+
+ assertTrue(lobby.getPlayers().contains(player))
+ assertSame(lobby, player.lobby)
+ assertTrue(player.isInLobby)
+ assertFalse(player.isInGame)
+ return player
+ }
+}
diff --git a/backend/src/test/kotlin/org/luxons/sevenwonders/controllers/TestPrincipal.kt b/backend/src/test/kotlin/org/luxons/sevenwonders/controllers/TestPrincipal.kt
new file mode 100644
index 00000000..ce3cf317
--- /dev/null
+++ b/backend/src/test/kotlin/org/luxons/sevenwonders/controllers/TestPrincipal.kt
@@ -0,0 +1,10 @@
+package org.luxons.sevenwonders.controllers
+
+import java.security.Principal
+
+internal class TestPrincipal(private val name: String) : Principal {
+
+ override fun getName(): String {
+ return name
+ }
+}
diff --git a/backend/src/test/kotlin/org/luxons/sevenwonders/lobby/LobbyTest.kt b/backend/src/test/kotlin/org/luxons/sevenwonders/lobby/LobbyTest.kt
new file mode 100644
index 00000000..3abdde3b
--- /dev/null
+++ b/backend/src/test/kotlin/org/luxons/sevenwonders/lobby/LobbyTest.kt
@@ -0,0 +1,229 @@
+package org.luxons.sevenwonders.lobby
+
+import org.junit.Assert.*
+import org.junit.Assume.assumeTrue
+import org.junit.Before
+import org.junit.BeforeClass
+import org.junit.Rule
+import org.junit.Test
+import org.junit.experimental.theories.DataPoints
+import org.junit.experimental.theories.Theories
+import org.junit.experimental.theories.Theory
+import org.junit.rules.ExpectedException
+import org.junit.runner.RunWith
+import org.luxons.sevenwonders.game.api.CustomizableSettings
+import org.luxons.sevenwonders.game.data.GameDefinition
+import org.luxons.sevenwonders.game.data.GameDefinitionLoader
+import org.luxons.sevenwonders.lobby.Lobby.*
+import java.util.Arrays
+
+@RunWith(Theories::class)
+class LobbyTest {
+
+ @JvmField
+ @Rule
+ var thrown = ExpectedException.none()
+
+ private lateinit var gameOwner: Player
+
+ private lateinit var lobby: Lobby
+
+ @Before
+ fun setUp() {
+ gameOwner = Player("gameowner", "Game owner")
+ lobby = Lobby(0, "Test Game", gameOwner, gameDefinition)
+ }
+
+ @Test
+ fun testId() {
+ val lobby = Lobby(5, "Test Game", gameOwner, gameDefinition)
+ assertEquals(5, lobby.id)
+ }
+
+ @Test
+ fun testName() {
+ val lobby = Lobby(5, "Test Game", gameOwner, gameDefinition)
+ assertEquals("Test Game", lobby.name)
+ }
+
+ @Test
+ fun testOwner() {
+ val lobby = Lobby(5, "Test Game", gameOwner, gameDefinition)
+ assertSame(gameOwner, lobby.getPlayers()[0])
+ assertSame(lobby, gameOwner.lobby)
+ }
+
+ @Test
+ fun isOwner_falseWhenNull() {
+ assertFalse(lobby.isOwner(null))
+ }
+
+ @Test
+ fun isOwner_falseWhenEmptyString() {
+ assertFalse(lobby.isOwner(""))
+ }
+
+ @Test
+ fun isOwner_falseWhenGarbageString() {
+ assertFalse(lobby.isOwner("this is garbage"))
+ }
+
+ @Test
+ fun isOwner_trueWhenOwnerUsername() {
+ assertTrue(lobby.isOwner(gameOwner.username))
+ }
+
+ @Test
+ fun isOwner_falseWhenOtherPlayerName() {
+ val player = Player("testuser", "Test User")
+ lobby.addPlayer(player)
+ assertFalse(lobby.isOwner(player.username))
+ }
+
+ @Test
+ fun addPlayer_success() {
+ val player = Player("testuser", "Test User")
+ lobby.addPlayer(player)
+ assertTrue(lobby.containsUser("testuser"))
+ assertSame(lobby, player.lobby)
+ }
+
+ @Test(expected = PlayerNameAlreadyUsedException::class)
+ fun addPlayer_failsOnSameName() {
+ val player = Player("testuser", "Test User")
+ val player2 = Player("testuser2", "Test User")
+ lobby.addPlayer(player)
+ lobby.addPlayer(player2)
+ }
+
+ @Test(expected = PlayerOverflowException::class)
+ fun addPlayer_playerOverflowWhenTooMany() {
+ // the owner + the max number gives an overflow
+ addPlayers(gameDefinition.maxPlayers)
+ }
+
+ @Test(expected = GameAlreadyStartedException::class)
+ fun addPlayer_failWhenGameStarted() {
+ // total with owner is the minimum
+ addPlayers(gameDefinition.minPlayers - 1)
+ lobby.startGame()
+ lobby.addPlayer(Player("soonerNextTime", "The Late Guy"))
+ }
+
+ private fun addPlayers(nbPlayers: Int) {
+ for (i in 0 until nbPlayers) {
+ val player = Player("testuser$i", "Test User $i")
+ lobby.addPlayer(player)
+ }
+ }
+
+ @Test(expected = UnknownPlayerException::class)
+ fun removePlayer_failsWhenNotPresent() {
+ lobby.removePlayer("anyname")
+ }
+
+ @Test
+ fun removePlayer_success() {
+ val player = Player("testuser", "Test User")
+ lobby.addPlayer(player)
+ assertTrue(player.isInLobby)
+ assertFalse(player.isInGame)
+
+ lobby.removePlayer("testuser")
+ assertFalse(lobby.containsUser("testuser"))
+ assertFalse(player.isInLobby)
+ assertFalse(player.isInGame)
+ }
+
+ @Test
+ fun reorderPlayers_success() {
+ val player1 = Player("testuser1", "Test User 1")
+ val player2 = Player("testuser2", "Test User 2")
+ val player3 = Player("testuser3", "Test User 3")
+ lobby.addPlayer(player1)
+ lobby.addPlayer(player2)
+ lobby.addPlayer(player3)
+ lobby.reorderPlayers(Arrays.asList("testuser3", "testuser1", "testuser2"))
+ assertEquals("testuser3", lobby.getPlayers()[0].username)
+ assertEquals("testuser1", lobby.getPlayers()[1].username)
+ assertEquals("testuser2", lobby.getPlayers()[2].username)
+ }
+
+ @Test(expected = UnknownPlayerException::class)
+ fun reorderPlayers_failsOnUnknownPlayer() {
+ val player1 = Player("testuser1", "Test User 1")
+ val player2 = Player("testuser2", "Test User 2")
+ val player3 = Player("testuser3", "Test User 3")
+ lobby.addPlayer(player1)
+ lobby.addPlayer(player2)
+ lobby.addPlayer(player3)
+ lobby.reorderPlayers(Arrays.asList("unknown", "testuser1", "testuser2"))
+ }
+
+ @Theory
+ fun startGame_failsBelowMinPlayers(nbPlayers: Int) {
+ assumeTrue(nbPlayers < gameDefinition.minPlayers)
+ thrown.expect(PlayerUnderflowException::class.java)
+ // there is already the owner
+ addPlayers(nbPlayers - 1)
+ lobby.startGame()
+ }
+
+ @Theory
+ fun startGame_succeedsAboveMinPlayers(nbPlayers: Int) {
+ assumeTrue(nbPlayers >= gameDefinition.minPlayers)
+ assumeTrue(nbPlayers <= gameDefinition.maxPlayers)
+ // there is already the owner
+ addPlayers(nbPlayers - 1)
+
+ assertEquals(nbPlayers, lobby.getPlayers().size)
+ lobby.getPlayers().forEach {
+ assertSame(lobby, it.lobby)
+ assertTrue(it.isInLobby)
+ assertFalse(it.isInGame)
+ }
+
+ val game = lobby.startGame()
+ assertNotNull(game)
+ lobby.getPlayers().forEachIndexed { index, it ->
+ assertSame(index, it.index)
+ assertSame(lobby, it.lobby)
+ assertSame(game, it.game)
+ assertTrue(it.isInLobby)
+ assertTrue(it.isInGame)
+ }
+ }
+
+ @Test
+ fun startGame_switchesState() {
+ assertTrue(lobby.state === State.LOBBY)
+ // there is already the owner
+ addPlayers(gameDefinition.minPlayers - 1)
+ lobby.startGame()
+ assertTrue(lobby.state === State.PLAYING)
+ }
+
+ @Test
+ fun setSettings() {
+ val settings = CustomizableSettings()
+ lobby.settings = settings
+ assertSame(settings, lobby.settings)
+ }
+
+ companion object {
+
+ private lateinit var gameDefinition: GameDefinition
+
+ @JvmStatic
+ @DataPoints
+ fun nbPlayers(): IntArray {
+ return intArrayOf(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
+ }
+
+ @JvmStatic
+ @BeforeClass
+ fun loadDefinition() {
+ gameDefinition = GameDefinitionLoader().gameDefinition
+ }
+ }
+}
diff --git a/backend/src/test/kotlin/org/luxons/sevenwonders/repositories/LobbyRepositoryTest.kt b/backend/src/test/kotlin/org/luxons/sevenwonders/repositories/LobbyRepositoryTest.kt
new file mode 100644
index 00000000..a5009d2a
--- /dev/null
+++ b/backend/src/test/kotlin/org/luxons/sevenwonders/repositories/LobbyRepositoryTest.kt
@@ -0,0 +1,70 @@
+package org.luxons.sevenwonders.repositories
+
+import org.junit.Assert.*
+import org.junit.Before
+import org.junit.Test
+import org.luxons.sevenwonders.lobby.Player
+
+class LobbyRepositoryTest {
+
+ private lateinit var repository: LobbyRepository
+
+ @Before
+ fun setUp() {
+ repository = LobbyRepository()
+ }
+
+ @Test
+ fun list_initiallyEmpty() {
+ assertTrue(repository.list().isEmpty())
+ }
+
+ @Test
+ fun list_returnsAllLobbies() {
+ val owner = Player("owner", "The Owner")
+ val lobby1 = repository.create("Test Name 1", owner)
+ val lobby2 = repository.create("Test Name 2", owner)
+ assertTrue(repository.list().contains(lobby1))
+ assertTrue(repository.list().contains(lobby2))
+ }
+
+ @Test
+ fun create_withCorrectOwner() {
+ val owner = Player("owner", "The Owner")
+ val lobby = repository.create("Test Name", owner)
+ assertTrue(lobby.isOwner(owner.username))
+ }
+
+ @Test(expected = LobbyNotFoundException::class)
+ fun find_failsOnUnknownId() {
+ repository.find(123)
+ }
+
+ @Test
+ fun find_returnsTheSameObject() {
+ val owner = Player("owner", "The Owner")
+ val lobby1 = repository.create("Test Name 1", owner)
+ val lobby2 = repository.create("Test Name 2", owner)
+ assertSame(lobby1, repository.find(lobby1.id))
+ assertSame(lobby2, repository.find(lobby2.id))
+ }
+
+ @Test(expected = LobbyNotFoundException::class)
+ fun remove_failsOnUnknownId() {
+ repository.remove(123)
+ }
+
+ @Test
+ fun remove_succeeds() {
+ val owner = Player("owner", "The Owner")
+ val lobby1 = repository.create("Test Name 1", owner)
+ assertNotNull(repository.find(lobby1.id))
+ repository.remove(lobby1.id)
+ try {
+ repository.find(lobby1.id)
+ fail() // the call to find() should have failed
+ } catch (e: LobbyNotFoundException) {
+ // the lobby has been properly removed
+ }
+ }
+}
diff --git a/backend/src/test/kotlin/org/luxons/sevenwonders/repositories/PlayerRepositoryTest.kt b/backend/src/test/kotlin/org/luxons/sevenwonders/repositories/PlayerRepositoryTest.kt
new file mode 100644
index 00000000..64e3d8fd
--- /dev/null
+++ b/backend/src/test/kotlin/org/luxons/sevenwonders/repositories/PlayerRepositoryTest.kt
@@ -0,0 +1,67 @@
+package org.luxons.sevenwonders.repositories
+
+import org.junit.Assert.*
+import org.junit.Before
+import org.junit.Test
+
+class PlayerRepositoryTest {
+
+ private lateinit var repository: PlayerRepository
+
+ @Before
+ fun setUp() {
+ repository = PlayerRepository()
+ }
+
+ @Test
+ fun contains_falseIfNoUserAdded() {
+ assertFalse(repository.contains("anyUsername"))
+ }
+
+ @Test
+ fun contains_trueForCreatedPlayer() {
+ repository.createOrUpdate("player1", "Player 1")
+ assertTrue(repository.contains("player1"))
+ }
+
+ @Test
+ fun createOrUpdate_createsProperly() {
+ val player1 = repository.createOrUpdate("player1", "Player 1")
+ assertEquals("player1", player1.username)
+ assertEquals("Player 1", player1.displayName)
+ }
+
+ @Test
+ fun createOrUpdate_updatesDisplayName() {
+ val player1 = repository.createOrUpdate("player1", "Player 1")
+ val player1Updated = repository.createOrUpdate("player1", "Much Better Name")
+ assertSame(player1, player1Updated)
+ assertEquals("Much Better Name", player1Updated.displayName)
+ }
+
+ @Test(expected = PlayerNotFoundException::class)
+ fun find_failsOnUnknownUsername() {
+ repository.find("anyUsername")
+ }
+
+ @Test
+ fun find_returnsTheSameObject() {
+ val player1 = repository.createOrUpdate("player1", "Player 1")
+ val player2 = repository.createOrUpdate("player2", "Player 2")
+ assertSame(player1, repository.find("player1"))
+ assertSame(player2, repository.find("player2"))
+ }
+
+ @Test(expected = PlayerNotFoundException::class)
+ fun remove_failsOnUnknownUsername() {
+ repository.remove("anyUsername")
+ }
+
+ @Test
+ fun remove_succeeds() {
+ repository.createOrUpdate("player1", "Player 1")
+ assertTrue(repository.contains("player1"))
+ repository.remove("player1")
+ assertFalse(repository.contains("player1"))
+ }
+}
diff --git a/backend/src/test/kotlin/org/luxons/sevenwonders/test/TestUtils.kt b/backend/src/test/kotlin/org/luxons/sevenwonders/test/TestUtils.kt
new file mode 100644
index 00000000..51db8d91
--- /dev/null
+++ b/backend/src/test/kotlin/org/luxons/sevenwonders/test/TestUtils.kt
@@ -0,0 +1,21 @@
+package org.luxons.sevenwonders.test
+
+import org.springframework.messaging.Message
+import org.springframework.messaging.MessageChannel
+import org.springframework.messaging.simp.SimpMessagingTemplate
+
+object TestUtils {
+
+ fun createSimpMessagingTemplate(): SimpMessagingTemplate {
+ val messageChannel = object : MessageChannel {
+ override fun send(message: Message<*>): Boolean {
+ return true
+ }
+
+ override fun send(message: Message<*>, timeout: Long): Boolean {
+ return true
+ }
+ }
+ return SimpMessagingTemplate(messageChannel)
+ }
+}
diff --git a/backend/src/test/kotlin/org/luxons/sevenwonders/test/api/ApiBoard.kt b/backend/src/test/kotlin/org/luxons/sevenwonders/test/api/ApiBoard.kt
new file mode 100644
index 00000000..a3d54773
--- /dev/null
+++ b/backend/src/test/kotlin/org/luxons/sevenwonders/test/api/ApiBoard.kt
@@ -0,0 +1,64 @@
+package org.luxons.sevenwonders.test.api
+
+import java.util.Objects
+
+import org.luxons.sevenwonders.game.cards.Card
+import org.luxons.sevenwonders.game.effects.SpecialAbility
+
+class ApiBoard {
+
+ var wonder: ApiWonder? = null
+
+ var playerIndex: Int = 0
+
+ var playedCards: List<ApiCard>? = null
+
+ var production: ApiProduction? = null
+
+ var publicProduction: ApiProduction? = null
+
+ var science: ApiScience? = null
+
+ var tradingRules: ApiTradingRules? = null
+
+ var military: ApiMilitary? = null
+
+ var specialAbilities: Set<SpecialAbility>? = null
+
+ var consumedFreeCards: Map<Int, Boolean>? = null
+
+ var copiedGuild: Card? = null
+
+ var gold: Int = 0
+
+ var pointsPer3Gold: Int = 0
+
+ override fun equals(o: Any?): Boolean {
+ if (this === o) {
+ return true
+ }
+ if (o == null || javaClass != o.javaClass) {
+ return false
+ }
+ val apiBoard = o as ApiBoard?
+ return (playerIndex == apiBoard!!.playerIndex && gold == apiBoard.gold && pointsPer3Gold == apiBoard.pointsPer3Gold && wonder == apiBoard.wonder && playedCards == apiBoard.playedCards && production == apiBoard.production && publicProduction == apiBoard.publicProduction && science == apiBoard.science && tradingRules == apiBoard.tradingRules && military == apiBoard.military && specialAbilities == apiBoard.specialAbilities && consumedFreeCards == apiBoard.consumedFreeCards && copiedGuild == apiBoard.copiedGuild)
+ }
+
+ override fun hashCode(): Int {
+ return Objects.hash(
+ wonder,
+ playerIndex,
+ playedCards,
+ production,
+ publicProduction,
+ science,
+ tradingRules,
+ military,
+ specialAbilities,
+ consumedFreeCards,
+ copiedGuild,
+ gold,
+ pointsPer3Gold
+ )
+ }
+}
diff --git a/backend/src/test/kotlin/org/luxons/sevenwonders/test/api/ApiCard.kt b/backend/src/test/kotlin/org/luxons/sevenwonders/test/api/ApiCard.kt
new file mode 100644
index 00000000..2f473367
--- /dev/null
+++ b/backend/src/test/kotlin/org/luxons/sevenwonders/test/api/ApiCard.kt
@@ -0,0 +1,38 @@
+package org.luxons.sevenwonders.test.api
+
+import java.util.Objects
+
+import org.luxons.sevenwonders.game.cards.Color
+import org.luxons.sevenwonders.game.cards.Requirements
+
+class ApiCard {
+
+ var name: String? = null
+
+ var color: Color? = null
+
+ var requirements: Requirements? = null
+
+ var chainParent: String? = null
+
+ var chainChildren: List<String>? = null
+
+ var image: String? = null
+
+ var back: ApiCardBack? = null
+
+ override fun equals(o: Any?): Boolean {
+ if (this === o) {
+ return true
+ }
+ if (o == null || javaClass != o.javaClass) {
+ return false
+ }
+ val apiCard = o as ApiCard?
+ return name == apiCard!!.name && color === apiCard.color && requirements == apiCard.requirements && chainParent == apiCard.chainParent && chainChildren == apiCard.chainChildren && image == apiCard.image && back == apiCard.back
+ }
+
+ override fun hashCode(): Int {
+ return Objects.hash(name, color, requirements, chainParent, chainChildren, image, back)
+ }
+}
diff --git a/backend/src/test/kotlin/org/luxons/sevenwonders/test/api/ApiCardBack.kt b/backend/src/test/kotlin/org/luxons/sevenwonders/test/api/ApiCardBack.kt
new file mode 100644
index 00000000..15bfd009
--- /dev/null
+++ b/backend/src/test/kotlin/org/luxons/sevenwonders/test/api/ApiCardBack.kt
@@ -0,0 +1,6 @@
+package org.luxons.sevenwonders.test.api
+
+class ApiCardBack {
+
+ var image: String? = null
+}
diff --git a/backend/src/test/kotlin/org/luxons/sevenwonders/test/api/ApiEffect.kt b/backend/src/test/kotlin/org/luxons/sevenwonders/test/api/ApiEffect.kt
new file mode 100644
index 00000000..afe8809f
--- /dev/null
+++ b/backend/src/test/kotlin/org/luxons/sevenwonders/test/api/ApiEffect.kt
@@ -0,0 +1,3 @@
+package org.luxons.sevenwonders.test.api
+
+class ApiEffect
diff --git a/backend/src/test/kotlin/org/luxons/sevenwonders/test/api/ApiHandCard.kt b/backend/src/test/kotlin/org/luxons/sevenwonders/test/api/ApiHandCard.kt
new file mode 100644
index 00000000..003c3783
--- /dev/null
+++ b/backend/src/test/kotlin/org/luxons/sevenwonders/test/api/ApiHandCard.kt
@@ -0,0 +1,16 @@
+package org.luxons.sevenwonders.test.api
+
+/**
+ * A card with contextual information relative to the hand it is sitting in. The extra information is especially
+ * useful because it frees the client from a painful business logic implementation.
+ */
+class ApiHandCard {
+
+ var card: ApiCard? = null
+
+ var isChainable: Boolean = false
+
+ var isFree: Boolean = false
+
+ var isPlayable: Boolean = false
+}
diff --git a/backend/src/test/kotlin/org/luxons/sevenwonders/test/api/ApiLobby.kt b/backend/src/test/kotlin/org/luxons/sevenwonders/test/api/ApiLobby.kt
new file mode 100644
index 00000000..5bc4dad6
--- /dev/null
+++ b/backend/src/test/kotlin/org/luxons/sevenwonders/test/api/ApiLobby.kt
@@ -0,0 +1,36 @@
+package org.luxons.sevenwonders.test.api
+
+import java.util.Objects
+
+import org.luxons.sevenwonders.game.api.CustomizableSettings
+import org.luxons.sevenwonders.lobby.State
+
+class ApiLobby {
+
+ var id: Long = 0
+
+ var name: String? = null
+
+ var owner: String? = null
+
+ var players: List<ApiPlayer>? = null
+
+ var settings: CustomizableSettings? = null
+
+ var state: State? = null
+
+ override fun equals(o: Any?): Boolean {
+ if (this === o) {
+ return true
+ }
+ if (o == null || javaClass != o.javaClass) {
+ return false
+ }
+ val apiLobby = o as ApiLobby?
+ return (id == apiLobby!!.id && name == apiLobby.name && owner == apiLobby.owner && players == apiLobby.players && settings == apiLobby.settings && state === apiLobby.state)
+ }
+
+ override fun hashCode(): Int {
+ return Objects.hash(id, name, owner, players, settings, state)
+ }
+}
diff --git a/backend/src/test/kotlin/org/luxons/sevenwonders/test/api/ApiMilitary.kt b/backend/src/test/kotlin/org/luxons/sevenwonders/test/api/ApiMilitary.kt
new file mode 100644
index 00000000..23e4f34c
--- /dev/null
+++ b/backend/src/test/kotlin/org/luxons/sevenwonders/test/api/ApiMilitary.kt
@@ -0,0 +1,10 @@
+package org.luxons.sevenwonders.test.api
+
+class ApiMilitary {
+
+ var nbShields = 0
+
+ var totalPoints = 0
+
+ var nbDefeatTokens = 0
+}
diff --git a/backend/src/test/kotlin/org/luxons/sevenwonders/test/api/ApiPlayer.kt b/backend/src/test/kotlin/org/luxons/sevenwonders/test/api/ApiPlayer.kt
new file mode 100644
index 00000000..fc4c01fb
--- /dev/null
+++ b/backend/src/test/kotlin/org/luxons/sevenwonders/test/api/ApiPlayer.kt
@@ -0,0 +1,27 @@
+package org.luxons.sevenwonders.test.api
+
+import java.util.Objects
+
+class ApiPlayer {
+
+ val username: String? = null
+
+ var displayName: String? = null
+
+ var index: Int = 0
+
+ override fun equals(o: Any?): Boolean {
+ if (this === o) {
+ return true
+ }
+ if (o == null || javaClass != o.javaClass) {
+ return false
+ }
+ val apiPlayer = o as ApiPlayer?
+ return index == apiPlayer!!.index && username == apiPlayer.username && displayName == apiPlayer.displayName
+ }
+
+ override fun hashCode(): Int {
+ return Objects.hash(username, displayName, index)
+ }
+}
diff --git a/backend/src/test/kotlin/org/luxons/sevenwonders/test/api/ApiPlayerTurnInfo.kt b/backend/src/test/kotlin/org/luxons/sevenwonders/test/api/ApiPlayerTurnInfo.kt
new file mode 100644
index 00000000..ae22cdb6
--- /dev/null
+++ b/backend/src/test/kotlin/org/luxons/sevenwonders/test/api/ApiPlayerTurnInfo.kt
@@ -0,0 +1,37 @@
+package org.luxons.sevenwonders.test.api
+
+import java.util.Objects
+
+import org.luxons.sevenwonders.game.api.Action
+
+class ApiPlayerTurnInfo {
+
+ var playerIndex: Int = 0
+
+ var table: ApiTable? = null
+
+ var currentAge: Int = 0
+
+ var action: Action? = null
+
+ var hand: List<ApiHandCard>? = null
+
+ var neighbourGuildCards: List<ApiCard>? = null
+
+ var message: String? = null
+
+ override fun equals(o: Any?): Boolean {
+ if (this === o) {
+ return true
+ }
+ if (o == null || javaClass != o.javaClass) {
+ return false
+ }
+ val that = o as ApiPlayerTurnInfo?
+ return (playerIndex == that!!.playerIndex && currentAge == that.currentAge && table == that.table && action === that.action && hand == that.hand && neighbourGuildCards == that.neighbourGuildCards && message == that.message)
+ }
+
+ override fun hashCode(): Int {
+ return Objects.hash(playerIndex, table, currentAge, action, hand, neighbourGuildCards, message)
+ }
+}
diff --git a/backend/src/test/kotlin/org/luxons/sevenwonders/test/api/ApiProduction.kt b/backend/src/test/kotlin/org/luxons/sevenwonders/test/api/ApiProduction.kt
new file mode 100644
index 00000000..2ab7c343
--- /dev/null
+++ b/backend/src/test/kotlin/org/luxons/sevenwonders/test/api/ApiProduction.kt
@@ -0,0 +1,11 @@
+package org.luxons.sevenwonders.test.api
+
+import org.luxons.sevenwonders.game.resources.ResourceType
+import org.luxons.sevenwonders.game.resources.Resources
+
+class ApiProduction {
+
+ var fixedResources: Resources? = null
+
+ var alternativeResources: Set<Set<ResourceType>>? = null
+}
diff --git a/backend/src/test/kotlin/org/luxons/sevenwonders/test/api/ApiScience.kt b/backend/src/test/kotlin/org/luxons/sevenwonders/test/api/ApiScience.kt
new file mode 100644
index 00000000..5e619904
--- /dev/null
+++ b/backend/src/test/kotlin/org/luxons/sevenwonders/test/api/ApiScience.kt
@@ -0,0 +1,10 @@
+package org.luxons.sevenwonders.test.api
+
+import org.luxons.sevenwonders.game.boards.ScienceType
+
+class ApiScience {
+
+ var quantities: Map<ScienceType, Int>? = null
+
+ var jokers: Int = 0
+}
diff --git a/backend/src/test/kotlin/org/luxons/sevenwonders/test/api/ApiTable.kt b/backend/src/test/kotlin/org/luxons/sevenwonders/test/api/ApiTable.kt
new file mode 100644
index 00000000..cffbdf2f
--- /dev/null
+++ b/backend/src/test/kotlin/org/luxons/sevenwonders/test/api/ApiTable.kt
@@ -0,0 +1,23 @@
+package org.luxons.sevenwonders.test.api
+
+import org.luxons.sevenwonders.game.cards.HandRotationDirection
+import org.luxons.sevenwonders.game.moves.Move
+
+class ApiTable {
+
+ var nbPlayers: Int = 0
+
+ var boards: List<ApiBoard>? = null
+
+ var currentAge = 0
+
+ var handRotationDirection: HandRotationDirection? = null
+
+ var lastPlayedMoves: List<Move>? = null
+
+ var neighbourGuildCards: List<ApiCard>? = null
+
+ fun increaseCurrentAge() {
+ this.currentAge++
+ }
+}
diff --git a/backend/src/test/kotlin/org/luxons/sevenwonders/test/api/ApiTradingRules.kt b/backend/src/test/kotlin/org/luxons/sevenwonders/test/api/ApiTradingRules.kt
new file mode 100644
index 00000000..fcebe59e
--- /dev/null
+++ b/backend/src/test/kotlin/org/luxons/sevenwonders/test/api/ApiTradingRules.kt
@@ -0,0 +1,9 @@
+package org.luxons.sevenwonders.test.api
+
+import org.luxons.sevenwonders.game.resources.Provider
+import org.luxons.sevenwonders.game.resources.ResourceType
+
+class ApiTradingRules {
+
+ var costs: Map<ResourceType, Map<Provider, Int>>? = null
+}
diff --git a/backend/src/test/kotlin/org/luxons/sevenwonders/test/api/ApiWonder.kt b/backend/src/test/kotlin/org/luxons/sevenwonders/test/api/ApiWonder.kt
new file mode 100644
index 00000000..1efe7917
--- /dev/null
+++ b/backend/src/test/kotlin/org/luxons/sevenwonders/test/api/ApiWonder.kt
@@ -0,0 +1,16 @@
+package org.luxons.sevenwonders.test.api
+
+import org.luxons.sevenwonders.game.resources.ResourceType
+
+class ApiWonder {
+
+ private val name: String? = null
+
+ private val initialResource: ResourceType? = null
+
+ private val stages: List<ApiWonderStage>? = null
+
+ private val image: String? = null
+
+ private val nbBbuiltStages: Int = 0
+}
diff --git a/backend/src/test/kotlin/org/luxons/sevenwonders/test/api/ApiWonderStage.kt b/backend/src/test/kotlin/org/luxons/sevenwonders/test/api/ApiWonderStage.kt
new file mode 100644
index 00000000..41925a55
--- /dev/null
+++ b/backend/src/test/kotlin/org/luxons/sevenwonders/test/api/ApiWonderStage.kt
@@ -0,0 +1,13 @@
+package org.luxons.sevenwonders.test.api
+
+import org.luxons.sevenwonders.game.cards.CardBack
+import org.luxons.sevenwonders.game.cards.Requirements
+
+class ApiWonderStage {
+
+ var requirements: Requirements? = null
+
+ var cardBack: CardBack? = null
+
+ var isBuilt: Boolean = false
+}
diff --git a/backend/src/test/kotlin/org/luxons/sevenwonders/test/api/SevenWondersClient.kt b/backend/src/test/kotlin/org/luxons/sevenwonders/test/api/SevenWondersClient.kt
new file mode 100644
index 00000000..d98e932e
--- /dev/null
+++ b/backend/src/test/kotlin/org/luxons/sevenwonders/test/api/SevenWondersClient.kt
@@ -0,0 +1,25 @@
+package org.luxons.sevenwonders.test.api
+
+import java.util.concurrent.ExecutionException
+import java.util.concurrent.TimeoutException
+
+import org.hildan.jackstomp.JackstompClient
+import org.hildan.jackstomp.JackstompSession
+
+class SevenWondersClient @JvmOverloads constructor(private val client: JackstompClient = JackstompClient()) {
+
+ @Throws(InterruptedException::class, ExecutionException::class, TimeoutException::class)
+ fun connect(serverUrl: String): SevenWondersSession {
+ val session = client.connect(serverUrl + WEBSOCKET_ENDPOINT)
+ return SevenWondersSession(session)
+ }
+
+ fun stop() {
+ client.stop()
+ }
+
+ companion object {
+
+ private val WEBSOCKET_ENDPOINT = "/seven-wonders-websocket"
+ }
+}
diff --git a/backend/src/test/kotlin/org/luxons/sevenwonders/test/api/SevenWondersSession.kt b/backend/src/test/kotlin/org/luxons/sevenwonders/test/api/SevenWondersSession.kt
new file mode 100644
index 00000000..90b40cb0
--- /dev/null
+++ b/backend/src/test/kotlin/org/luxons/sevenwonders/test/api/SevenWondersSession.kt
@@ -0,0 +1,70 @@
+package org.luxons.sevenwonders.test.api
+
+import org.hildan.jackstomp.Channel
+import org.hildan.jackstomp.JackstompSession
+import org.junit.Assert.*
+import org.luxons.sevenwonders.actions.ChooseNameAction
+import org.luxons.sevenwonders.actions.CreateGameAction
+import org.luxons.sevenwonders.actions.JoinGameAction
+import org.luxons.sevenwonders.errors.ErrorDTO
+
+class SevenWondersSession(val jackstompSession: JackstompSession) {
+
+ fun disconnect() {
+ jackstompSession.disconnect()
+ }
+
+ fun watchErrors(): Channel<ErrorDTO> {
+ return jackstompSession.subscribe("/user/queue/errors", ErrorDTO::class.java)
+ }
+
+ @Throws(InterruptedException::class)
+ fun chooseName(displayName: String): ApiPlayer {
+ val action = ChooseNameAction(displayName)
+ return jackstompSession.request(action, ApiPlayer::class.java, "/app/chooseName", "/user/queue/nameChoice")
+ }
+
+ fun watchGames(): Channel<Array<ApiLobby>> {
+ return jackstompSession.subscribe("/topic/games", Array<ApiLobby>::class.java)
+ }
+
+ @Throws(InterruptedException::class)
+ fun createGame(gameName: String): ApiLobby {
+ val action = CreateGameAction(gameName)
+ return jackstompSession.request(action, ApiLobby::class.java, "/app/lobby/create", "/user/queue/lobby/joined")
+ }
+
+ @Throws(InterruptedException::class)
+ fun joinGame(gameId: Long): ApiLobby {
+ val action = JoinGameAction(gameId)
+ val lobby =
+ jackstompSession.request(action, ApiLobby::class.java, "/app/lobby/join", "/user/queue/lobby/joined")
+ assertNotNull(lobby)
+ assertEquals(gameId, lobby.id)
+ return lobby
+ }
+
+ fun watchLobbyUpdates(gameId: Long): Channel<ApiLobby> {
+ return jackstompSession.subscribe("/topic/lobby/$gameId/updated", ApiLobby::class.java)
+ }
+
+ fun watchLobbyStart(gameId: Long): Channel<ApiLobby> {
+ return jackstompSession.subscribe("/topic/lobby/$gameId/started", ApiLobby::class.java)
+ }
+
+ @Throws(InterruptedException::class)
+ fun startGame(gameId: Long) {
+ val sendDestination = "/app/lobby/startGame"
+ val receiveDestination = "/topic/lobby/$gameId/started"
+ val received = jackstompSession.request(null, sendDestination, receiveDestination)
+ assertTrue(received)
+ }
+
+ fun sayReady() {
+ jackstompSession.send("/app/game/sayReady", "")
+ }
+
+ fun watchTurns(): Channel<ApiPlayerTurnInfo> {
+ return jackstompSession.subscribe("/user/queue/game/turn", ApiPlayerTurnInfo::class.java)
+ }
+}
diff --git a/backend/src/test/kotlin/org/luxons/sevenwonders/validation/DestinationAccessValidatorTest.kt b/backend/src/test/kotlin/org/luxons/sevenwonders/validation/DestinationAccessValidatorTest.kt
new file mode 100644
index 00000000..45e58b7f
--- /dev/null
+++ b/backend/src/test/kotlin/org/luxons/sevenwonders/validation/DestinationAccessValidatorTest.kt
@@ -0,0 +1,138 @@
+package org.luxons.sevenwonders.validation
+
+import org.junit.Assert.assertFalse
+import org.junit.Assert.assertTrue
+import org.junit.Before
+import org.junit.Test
+import org.luxons.sevenwonders.lobby.Lobby
+import org.luxons.sevenwonders.lobby.Player
+import org.luxons.sevenwonders.repositories.LobbyNotFoundException
+import org.luxons.sevenwonders.repositories.LobbyRepository
+
+class DestinationAccessValidatorTest {
+
+ private lateinit var lobbyRepository: LobbyRepository
+
+ private lateinit var destinationAccessValidator: DestinationAccessValidator
+
+ @Before
+ fun setup() {
+ lobbyRepository = LobbyRepository()
+ destinationAccessValidator = DestinationAccessValidator(lobbyRepository)
+ }
+
+ private fun createLobby(gameName: String, ownerUsername: String, vararg otherPlayers: String): Lobby {
+ val owner = Player(ownerUsername, ownerUsername)
+ val lobby = lobbyRepository.create(gameName, owner)
+ for (playerName in otherPlayers) {
+ val player = Player(playerName, playerName)
+ lobby.addPlayer(player)
+ }
+ return lobby
+ }
+
+ private fun createGame(gameName: String, ownerUsername: String, vararg otherPlayers: String) {
+ val lobby = createLobby(gameName, ownerUsername, *otherPlayers)
+ lobby.startGame()
+ }
+
+ @Test
+ fun validate_failsOnNullUser() {
+ assertFalse(destinationAccessValidator.hasAccess(null, "doesNotMatter"))
+ }
+
+ @Test
+ fun validate_successWhenNoReference() {
+ assertTrue(destinationAccessValidator.hasAccess("", ""))
+ assertTrue(destinationAccessValidator.hasAccess("", "test"))
+ assertTrue(destinationAccessValidator.hasAccess("testUser", "test"))
+ }
+
+ @Test
+ fun validate_successWhenNoRefFollows() {
+ assertTrue(destinationAccessValidator.hasAccess("testUser", "/game/"))
+ assertTrue(destinationAccessValidator.hasAccess("testUser", "/lobby/"))
+ assertTrue(destinationAccessValidator.hasAccess("testUser", "prefix/game/"))
+ assertTrue(destinationAccessValidator.hasAccess("testUser", "prefix/lobby/"))
+ assertTrue(destinationAccessValidator.hasAccess("testUser", "/game//suffix"))
+ assertTrue(destinationAccessValidator.hasAccess("testUser", "/lobby//suffix"))
+ }
+
+ @Test
+ fun validate_successWhenRefIsNotANumber() {
+ assertTrue(destinationAccessValidator.hasAccess("testUser", "/game/notANumber"))
+ assertTrue(destinationAccessValidator.hasAccess("testUser", "/lobby/notANumber"))
+ assertTrue(destinationAccessValidator.hasAccess("testUser", "prefix/game/notANumber"))
+ assertTrue(destinationAccessValidator.hasAccess("testUser", "prefix/lobby/notANumber"))
+ assertTrue(destinationAccessValidator.hasAccess("testUser", "/game/notANumber/suffix"))
+ assertTrue(destinationAccessValidator.hasAccess("testUser", "/lobby/notANumber/suffix"))
+ }
+
+ @Test(expected = LobbyNotFoundException::class)
+ fun validate_failWhenNoLobbyExist() {
+ destinationAccessValidator.hasAccess("", "/lobby/0")
+ }
+
+ @Test(expected = LobbyNotFoundException::class)
+ fun validate_failWhenNoGameExist() {
+ destinationAccessValidator.hasAccess("", "/game/0")
+ }
+
+ @Test(expected = LobbyNotFoundException::class)
+ fun validate_failWhenReferencedLobbyDoesNotExist() {
+ createLobby("Test Game", "ownerUser1")
+ createLobby("Test Game 2", "ownerUser2")
+ destinationAccessValidator.hasAccess("doesNotMatter", "/lobby/3")
+ }
+
+ @Test(expected = LobbyNotFoundException::class)
+ fun validate_failWhenReferencedGameDoesNotExist() {
+ createGame("Test Game 1", "user1", "user2", "user3")
+ createGame("Test Game 2", "user4", "user5", "user6")
+ destinationAccessValidator.hasAccess("doesNotMatter", "/game/3")
+ }
+
+ @Test
+ fun validate_failWhenUserIsNotPartOfReferencedLobby() {
+ createLobby("Test Game", "ownerUser")
+ destinationAccessValidator.hasAccess("userNotInLobby", "/lobby/0")
+ }
+
+ @Test
+ fun validate_failWhenUserIsNotPartOfReferencedGame() {
+ createGame("Test Game", "ownerUser", "otherUser1", "otherUser2")
+ destinationAccessValidator.hasAccess("userNotInGame", "/game/0")
+ }
+
+ @Test
+ fun validate_successWhenUserIsOwnerOfReferencedLobby() {
+ createLobby("Test Game 1", "user1")
+ assertTrue(destinationAccessValidator.hasAccess("user1", "/lobby/0"))
+ createLobby("Test Game 2", "user2")
+ assertTrue(destinationAccessValidator.hasAccess("user2", "/lobby/1"))
+ }
+
+ @Test
+ fun validate_successWhenUserIsMemberOfReferencedLobby() {
+ createLobby("Test Game 1", "user1", "user2")
+ assertTrue(destinationAccessValidator.hasAccess("user2", "/lobby/0"))
+ createLobby("Test Game 2", "user3", "user4")
+ assertTrue(destinationAccessValidator.hasAccess("user4", "/lobby/1"))
+ }
+
+ @Test
+ fun validate_successWhenUserIsOwnerOfReferencedGame() {
+ createGame("Test Game 1", "owner1", "user2", "user3")
+ assertTrue(destinationAccessValidator.hasAccess("owner1", "/game/0"))
+ createGame("Test Game 2", "owner4", "user5", "user6")
+ assertTrue(destinationAccessValidator.hasAccess("owner4", "/game/1"))
+ }
+
+ @Test
+ fun validate_successWhenUserIsMemberOfReferencedGame() {
+ createGame("Test Game 1", "owner1", "user2", "user3")
+ assertTrue(destinationAccessValidator.hasAccess("user2", "/game/0"))
+ createGame("Test Game 2", "owner4", "user5", "user6")
+ assertTrue(destinationAccessValidator.hasAccess("user6", "/game/1"))
+ }
+}
bgstack15