diff --git a/fh.fd.ci.server/src/main/java/de/fd/fh/ServerApp.java b/fh.fd.ci.server/src/main/java/de/fd/fh/ServerApp.java index 809da76..ed1ff03 100644 --- a/fh.fd.ci.server/src/main/java/de/fd/fh/ServerApp.java +++ b/fh.fd.ci.server/src/main/java/de/fd/fh/ServerApp.java @@ -4,6 +4,9 @@ import de.fd.fh.server.access.AccessContextEventListener; import de.fd.fh.server.access.AccessRepository; import de.fd.fh.server.access.AccessService; import de.fd.fh.server.access.web.AccessController; +import de.fd.fh.server.game.GameRepository; +import de.fd.fh.server.game.GameService; +import de.fd.fh.server.game.web.GameController; import de.fd.fh.server.user.UserContextEventListener; import de.fd.fh.server.user.web.UserController; import de.fd.fh.server.user.UserRepository; @@ -21,9 +24,11 @@ public class ServerApp { private static AccessRepository accessRepository; private static UserRepository userRepository; + private static GameRepository gameRepository; private static AccessController accessController; private static UserController userController; + private static GameController gameController; private static final Set listeners = new HashSet<>(); @@ -38,6 +43,7 @@ public class ServerApp addListeners(new AccessService(accessRepository))); userController = new UserController((UserService) addListeners(new UserService(userRepository))); + gameController = new GameController(new GameService(gameRepository)); } before("/*", (req, res) -> accessController.before(req)); @@ -50,17 +56,23 @@ public class ServerApp get("/users", userController::getCompleteUser); get("/users/:user_id", userController::getUser); + post("/games", gameController::createGame); + get("/games/:game_id", gameController::findGameById); + get("/games/findByUser/:user_id", gameController::findRunningGamesOfUser); + get("/hello", (req, res) -> "Hello World"); } public static void initController( final AccessController accessController, - final UserController userController + final UserController userController, + final GameController gameController ) { ServerApp.userController = userController; ServerApp.accessController = accessController; + ServerApp.gameController = gameController; } private static Object addListeners(Observable service) @@ -80,5 +92,6 @@ public class ServerApp { accessRepository = new AccessRepository(); userRepository = new UserRepository(); + gameRepository = new GameRepository(); } } diff --git a/fh.fd.ci.server/src/main/java/de/fd/fh/server/game/Field.java b/fh.fd.ci.server/src/main/java/de/fd/fh/server/game/Field.java new file mode 100644 index 0000000..c1d8bbd --- /dev/null +++ b/fh.fd.ci.server/src/main/java/de/fd/fh/server/game/Field.java @@ -0,0 +1,25 @@ +package de.fd.fh.server.game; + +import dev.morphia.annotations.Embedded; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@NoArgsConstructor +@AllArgsConstructor +@Getter +public class Field +{ + private int x; + + private int y; + + private Color color; + @Embedded + private Figure figure; + + public enum Color + { + WHITE, BLACK + } +} diff --git a/fh.fd.ci.server/src/main/java/de/fd/fh/server/game/Figure.java b/fh.fd.ci.server/src/main/java/de/fd/fh/server/game/Figure.java new file mode 100644 index 0000000..a6cea0f --- /dev/null +++ b/fh.fd.ci.server/src/main/java/de/fd/fh/server/game/Figure.java @@ -0,0 +1,22 @@ +package de.fd.fh.server.game; + +import de.fd.fh.server.user.UserId; +import dev.morphia.annotations.Embedded; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Getter +@NoArgsConstructor +@AllArgsConstructor +public class Figure +{ + private Type type; + @Embedded + private UserId player; + + public enum Type + { + KING, QUEEN, BISHOP, KNIGHT, ROOK, PAWN + } +} diff --git a/fh.fd.ci.server/src/main/java/de/fd/fh/server/game/Game.java b/fh.fd.ci.server/src/main/java/de/fd/fh/server/game/Game.java new file mode 100644 index 0000000..e2f7ba3 --- /dev/null +++ b/fh.fd.ci.server/src/main/java/de/fd/fh/server/game/Game.java @@ -0,0 +1,29 @@ +package de.fd.fh.server.game; + +import de.fd.fh.server.user.UserId; +import dev.morphia.annotations.Embedded; +import dev.morphia.annotations.Id; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; + +import java.util.Set; + +@NoArgsConstructor +@AllArgsConstructor +@Getter +public class Game +{ + @Id + private GameId id; + @Embedded + private UserId whitePlayer; + @Embedded + private UserId blackPlayer; + @Embedded + private UserId currentPlayer; + @Embedded + private Set fields; + @Embedded + private UserId winner; +} diff --git a/fh.fd.ci.server/src/main/java/de/fd/fh/server/game/GameId.java b/fh.fd.ci.server/src/main/java/de/fd/fh/server/game/GameId.java new file mode 100644 index 0000000..568d74c --- /dev/null +++ b/fh.fd.ci.server/src/main/java/de/fd/fh/server/game/GameId.java @@ -0,0 +1,25 @@ +package de.fd.fh.server.game; + +import dev.morphia.annotations.Embedded; +import lombok.*; +import org.bson.types.ObjectId; + +@Getter +@Embedded +@NoArgsConstructor +@AllArgsConstructor(access = AccessLevel.PRIVATE) +@EqualsAndHashCode(of = {"identifier"}) +public class GameId +{ + private String identifier; + + public static GameId of(final String identifier) + { + return new GameId(identifier); + } + + public static GameId random() + { + return new GameId(new ObjectId().toHexString()); + } +} diff --git a/fh.fd.ci.server/src/main/java/de/fd/fh/server/game/GameRepository.java b/fh.fd.ci.server/src/main/java/de/fd/fh/server/game/GameRepository.java new file mode 100644 index 0000000..5fb3d05 --- /dev/null +++ b/fh.fd.ci.server/src/main/java/de/fd/fh/server/game/GameRepository.java @@ -0,0 +1,53 @@ +package de.fd.fh.server.game; + +import com.mongodb.MongoClient; +import de.fd.fh.server.user.UserId; +import dev.morphia.Datastore; +import dev.morphia.Key; +import dev.morphia.Morphia; +import dev.morphia.query.Query; + +import java.util.List; + +public class GameRepository +{ + private final Datastore datastore; + + public GameRepository() + { + System.out.println("GameRepo"); + final Morphia morphia = new Morphia(); + + morphia.mapPackage("de.fd.fh.server.game"); + + this.datastore = morphia.createDatastore(new MongoClient(), "smartwarfare"); + datastore.ensureIndexes(); + } + + GameRepository(final Datastore datastore) + { + this.datastore = datastore; + } + + public Key save(final Game access) + { + return datastore.save(access); + } + + Game findByGameId(final GameId gameId) + { + return datastore.createQuery(Game.class) + .field("id.identifier").equal(gameId.getIdentifier()).first(); + } + + List findRunningGamesOfPlayer(final UserId userId) + { + final Query query = datastore.createQuery(Game.class); + query.or( + query.criteria("whitePlayer.identifier").equal(userId.getIdentifier()), + query.criteria("blackPlayer.identifier").equal(userId.getIdentifier())); + query.and(query.criteria("winner").doesNotExist()); + + return query.find().toList(); + } +} diff --git a/fh.fd.ci.server/src/main/java/de/fd/fh/server/game/GameService.java b/fh.fd.ci.server/src/main/java/de/fd/fh/server/game/GameService.java new file mode 100644 index 0000000..e77fe2d --- /dev/null +++ b/fh.fd.ci.server/src/main/java/de/fd/fh/server/game/GameService.java @@ -0,0 +1,110 @@ +package de.fd.fh.server.game; + +import de.fd.fh.server.user.UserId; +import lombok.RequiredArgsConstructor; + +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +@RequiredArgsConstructor +public class GameService +{ + private final GameRepository repository; + + public GameId createGame(final Game game) + { + return (GameId) repository.save(game).getId(); + } + + public GameId createGame(final UserId blackUser, final UserId whiteUser) + { + final Set fields = initFields(blackUser, whiteUser); + + final Game game = new Game(null, whiteUser, blackUser, whiteUser, fields, null); + + return (GameId) repository.save(game).getId(); + } + + public Game findGameById(final GameId gameId) + { + return repository.findByGameId(gameId); + } + + public List findRunningGamesOfUser(final UserId userId) + { + return repository.findRunningGamesOfPlayer(userId); + } + + private Set initFields(UserId blackUser, UserId whiteUser) + { + final Set fields = new HashSet<>(); + for (int x = 0; x < 8; x++) + { + for (int y = 0; y < 8; y++) + { + fields.add(new Field(x, y, getColor(x, y) , getFigure(x, y, whiteUser, blackUser))); + } + } + return fields; + } + + private Field.Color getColor(final int x, final int y) + { + return (x + y) % 2 != 1 ? Field.Color.WHITE : Field.Color.BLACK; + } + + private Figure getFigure(final int x, final int y, final UserId whitePlayer, final UserId blackPlayer) + { + if (x == 0) + { + return getFigureOfGroundLevel(y, whitePlayer); + } + + if (x == 1) + { + return new Figure(Figure.Type.PAWN, whitePlayer); + } + + if (x == 7) + { + return getFigureOfGroundLevel(y, blackPlayer); + } + + if (x == 6) + { + return new Figure(Figure.Type.PAWN, blackPlayer); + } + + return null; + } + + private Figure getFigureOfGroundLevel(final int y, final UserId player) + { + if (y == 0 || y == 7) + { + return new Figure(Figure.Type.ROOK, player); + } + + if (y == 1 || y == 6) + { + return new Figure(Figure.Type.KNIGHT, player); + } + + if (y == 2 || y == 5) + { + return new Figure(Figure.Type.BISHOP, player); + } + + if (y == 3) + { + return new Figure(Figure.Type.QUEEN, player); + } + + if (y == 4) + { + return new Figure(Figure.Type.KING, player); + } + return null; + } +} diff --git a/fh.fd.ci.server/src/main/java/de/fd/fh/server/game/web/GameController.java b/fh.fd.ci.server/src/main/java/de/fd/fh/server/game/web/GameController.java new file mode 100644 index 0000000..f499067 --- /dev/null +++ b/fh.fd.ci.server/src/main/java/de/fd/fh/server/game/web/GameController.java @@ -0,0 +1,83 @@ +package de.fd.fh.server.game.web; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import de.fd.fh.server.game.Game; +import de.fd.fh.server.game.GameId; +import de.fd.fh.server.game.GameService; +import de.fd.fh.server.user.UserId; +import de.fd.fh.shared.network.messages.CreateGameRequest; +import lombok.RequiredArgsConstructor; +import spark.Request; +import spark.Response; + +import java.io.IOException; +import java.util.List; + +@RequiredArgsConstructor +public class GameController +{ + private final ObjectMapper objectMapper = new ObjectMapper(); + + private final GameService service; + + public Response createGame(final Request request, final Response response) throws IOException + { + final CreateGameRequest createRequest = objectMapper.readValue(request.body(), CreateGameRequest.class); + + final UserId whiteUser = UserId.of(createRequest.getWhiteUserId()); + final UserId blackUser = UserId.of(createRequest.getBlackUserId()); + + final GameId gameId = service.createGame(blackUser, whiteUser); + + if (gameId == null) + { + response.status(400); + } + else + { + response.body(objectMapper.writeValueAsString(gameId)); + response.status(201); + } + + return response; + } + + public Response findGameById(final Request request, final Response response) throws JsonProcessingException + { + final GameId gameId = GameId.of(request.params(":game_id")); + + final Game game = service.findGameById(gameId); + + if (game == null) + { + response.status(404); + } + else + { + response.body(objectMapper.writeValueAsString(game)); + response.status(200); + } + + return response; + } + + public Response findRunningGamesOfUser(final Request request, final Response response) throws JsonProcessingException + { + final UserId userId = UserId.of(request.params(":user_id")); + + final List games = service.findRunningGamesOfUser(userId); + + if (games == null) + { + response.status(404); + } + else + { + response.body(objectMapper.writeValueAsString(games)); + response.status(200); + } + + return response; + } +} diff --git a/fh.fd.ci.server/src/main/java/de/fd/fh/server/user/User.java b/fh.fd.ci.server/src/main/java/de/fd/fh/server/user/User.java index ef18257..e250496 100644 --- a/fh.fd.ci.server/src/main/java/de/fd/fh/server/user/User.java +++ b/fh.fd.ci.server/src/main/java/de/fd/fh/server/user/User.java @@ -1,11 +1,13 @@ package de.fd.fh.server.user; +import dev.morphia.annotations.Id; import lombok.*; @AllArgsConstructor @Getter public class User { + @Id private final UserId id; private String name; diff --git a/fh.fd.ci.server/src/main/java/de/fd/fh/server/user/UserId.java b/fh.fd.ci.server/src/main/java/de/fd/fh/server/user/UserId.java index e6599af..d978069 100644 --- a/fh.fd.ci.server/src/main/java/de/fd/fh/server/user/UserId.java +++ b/fh.fd.ci.server/src/main/java/de/fd/fh/server/user/UserId.java @@ -6,6 +6,7 @@ import org.bson.types.ObjectId; @Getter @Embedded +@NoArgsConstructor @AllArgsConstructor(access = AccessLevel.PRIVATE) @EqualsAndHashCode(of = {"identifier"}) public class UserId diff --git a/fh.fd.ci.server/src/test/java/de/fd/fh/ServerAppTest.java b/fh.fd.ci.server/src/test/java/de/fd/fh/ServerAppTest.java index c864f6b..0422115 100644 --- a/fh.fd.ci.server/src/test/java/de/fd/fh/ServerAppTest.java +++ b/fh.fd.ci.server/src/test/java/de/fd/fh/ServerAppTest.java @@ -3,6 +3,7 @@ package de.fd.fh; import com.fasterxml.jackson.core.JsonProcessingException; import de.fd.fh.server.ApiTestUtils; import de.fd.fh.server.access.web.AccessController; +import de.fd.fh.server.game.web.GameController; import de.fd.fh.server.user.web.UserController; import de.fd.fh.shared.Utils; import org.junit.jupiter.api.AfterAll; @@ -26,11 +27,12 @@ class ServerAppTest { private static UserController userController = mock(UserController.class); private static AccessController accessController = mock(AccessController.class); + private static GameController gameController = mock(GameController.class); @BeforeAll static void before() { - ServerApp.initController(accessController, userController); + ServerApp.initController(accessController, userController, gameController); ServerApp.main(null); awaitInitialization(); @@ -163,4 +165,52 @@ class ServerAppTest assertEquals(200, res.getStatus()); then(userController).should().getUser(any(), any()); } + + @Test + void testCreateGame() throws IOException + { + when(gameController.createGame(any(), any())).thenReturn(mock(Response.class)); + + final String url = "/games"; + + ApiTestUtils.TestResponse res = new ApiTestUtils() + .request("POST", url, null, null, String.class); + + assertNotNull(res); + assertEquals(200, res.getStatus()); + then(gameController).should().createGame(any(), any()); + then(gameController).shouldHaveNoMoreInteractions(); + } + + @Test + void testFindGameById() throws JsonProcessingException + { + when(gameController.findGameById(any(), any())).thenReturn(mock(Response.class)); + + final String url = "/games/12345"; + + ApiTestUtils.TestResponse res = new ApiTestUtils() + .request("GET", url, null, null, String.class); + + assertNotNull(res); + assertEquals(200, res.getStatus()); + then(gameController).should().findGameById(any(), any()); + then(gameController).shouldHaveNoMoreInteractions(); + } + + @Test + void testFindRunningGamesOfUser() throws JsonProcessingException + { + when(gameController.findRunningGamesOfUser(any(), any())).thenReturn(mock(Response.class)); + + final String url = "/games/findByUser/12345"; + + ApiTestUtils.TestResponse res = new ApiTestUtils() + .request("GET", url, null, null, String.class); + + assertNotNull(res); + assertEquals(200, res.getStatus()); + then(gameController).should().findRunningGamesOfUser(any(), any()); + then(gameController).shouldHaveNoMoreInteractions(); + } } \ No newline at end of file diff --git a/fh.fd.ci.server/src/test/java/de/fd/fh/server/game/FieldTest.java b/fh.fd.ci.server/src/test/java/de/fd/fh/server/game/FieldTest.java new file mode 100644 index 0000000..43fbea1 --- /dev/null +++ b/fh.fd.ci.server/src/test/java/de/fd/fh/server/game/FieldTest.java @@ -0,0 +1,20 @@ +package de.fd.fh.server.game; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +class FieldTest +{ + @Test + void fieldInitTest() + { + final Field field = new Field(1, 2, Field.Color.WHITE, new Figure()); + + assertEquals(1, field.getX()); + assertEquals(2, field.getY()); + assertEquals(Field.Color.WHITE, field.getColor()); + assertEquals("WHITE", field.getColor().name()); + assertNotNull(field.getFigure()); + } +} \ No newline at end of file diff --git a/fh.fd.ci.server/src/test/java/de/fd/fh/server/game/FigureTest.java b/fh.fd.ci.server/src/test/java/de/fd/fh/server/game/FigureTest.java new file mode 100644 index 0000000..74c6cfd --- /dev/null +++ b/fh.fd.ci.server/src/test/java/de/fd/fh/server/game/FigureTest.java @@ -0,0 +1,19 @@ +package de.fd.fh.server.game; + +import de.fd.fh.server.user.UserId; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +class FigureTest +{ + @Test + void figureInitTest() + { + final Figure figure = new Figure(Figure.Type.KING, UserId.of("12345")); + + assertEquals(UserId.of("12345"), figure.getPlayer()); + assertEquals(Figure.Type.KING, figure.getType()); + assertEquals("KING", figure.getType().name()); + } +} \ No newline at end of file diff --git a/fh.fd.ci.server/src/test/java/de/fd/fh/server/game/GameRepositoryTest.java b/fh.fd.ci.server/src/test/java/de/fd/fh/server/game/GameRepositoryTest.java new file mode 100644 index 0000000..11440bf --- /dev/null +++ b/fh.fd.ci.server/src/test/java/de/fd/fh/server/game/GameRepositoryTest.java @@ -0,0 +1,114 @@ +package de.fd.fh.server.game; + +import com.mongodb.MongoClient; +import com.mongodb.client.MongoDatabase; +import de.fd.fh.server.user.UserId; +import dev.morphia.Datastore; +import dev.morphia.Key; +import dev.morphia.Morphia; +import dev.morphia.mapping.MappedClass; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; + +class GameRepositoryTest +{ + public static final String DB_NAME = "db_for_morphia"; + + private static MongoClient mongo; + private static MongoDatabase db; + private static Datastore ds; + private static final Morphia morphia = new Morphia(); + + private static GameRepository repository; + +// @BeforeAll + public static void beforeClass() { + mongo = new MongoClient(); + db = mongo.getDatabase(DB_NAME); + ds = morphia.createDatastore(mongo, db.getName()); + repository = new GameRepository(ds); + } + +// @AfterAll + public static void afterClass() { + cleanup(); + mongo.close(); + } + + private static void cleanup() { + for (final MappedClass mc : morphia.getMapper().getMappedClasses()) { + db.getCollection(mc.getCollectionName()).drop(); + } + } + + +// @Test + void saveTest() + { + final Set fields = new HashSet<>(); + fields.add(new Field(0, 0, Field.Color.BLACK, new Figure(Figure.Type.BISHOP, UserId.of("12345")))); + fields.add(new Field(0, 2, Field.Color.WHITE, new Figure(Figure.Type.BISHOP, UserId.of("98765")))); + + final Game game = new Game(GameId.random(), UserId.of("12345"), UserId.of("98765"), UserId.of("12345"), fields, UserId.of("12345")); + + final Key key = repository.save(game); + + assertEquals(Game.class, key.getType()); + final Game result = ds.find(key.getType()).filter("id", key.getId()).first(); + + assertNotNull(result); + assertEquals("12345", result.getWhitePlayer().getIdentifier()); + assertEquals("12345", result.getCurrentPlayer().getIdentifier()); + assertEquals("98765", result.getBlackPlayer().getIdentifier()); + assertEquals("12345", result.getWinner().getIdentifier()); + assertNotNull(result.getId()); + assertEquals(2, result.getFields().size()); + } + +// @Test + void findByGameIdTest() + { + final Set fields = new HashSet<>(); + fields.add(new Field(0, 0, Field.Color.BLACK, new Figure(Figure.Type.BISHOP, UserId.of("12345")))); + fields.add(new Field(0, 2, Field.Color.WHITE, new Figure(Figure.Type.BISHOP, UserId.of("98765")))); + + final Game game = new Game(GameId.of("4242"), UserId.of("12345"), UserId.of("98765"), UserId.of("12345"), fields, UserId.of("12345")); + + ds.save(game); + + final Game result = repository.findByGameId(GameId.of("4242")); + + assertNotNull(result); + assertEquals("12345", result.getWhitePlayer().getIdentifier()); + assertEquals("12345", result.getCurrentPlayer().getIdentifier()); + assertEquals("98765", result.getBlackPlayer().getIdentifier()); + assertEquals("12345", result.getWinner().getIdentifier()); + assertNotNull(result.getId()); + assertEquals(2, result.getFields().size()); + } + +// @Test + void getRunningGamesOfPlayerTest() + { + final Set fields = new HashSet<>(); + fields.add(new Field(0, 0, Field.Color.BLACK, new Figure(Figure.Type.BISHOP, UserId.of("12345")))); + fields.add(new Field(0, 2, Field.Color.WHITE, new Figure(Figure.Type.BISHOP, UserId.of("98765")))); + + final Game game = new Game(GameId.of("4242"), UserId.of("12345"), UserId.of("98765"), UserId.of("12345"), fields, null); + + ds.save(game); + + final List result = repository.findRunningGamesOfPlayer(UserId.of("98765")); + + assertNotNull(result); + assertEquals(1, result.size()); + } +} \ No newline at end of file diff --git a/fh.fd.ci.server/src/test/java/de/fd/fh/server/game/GameServiceTest.java b/fh.fd.ci.server/src/test/java/de/fd/fh/server/game/GameServiceTest.java new file mode 100644 index 0000000..ec0d5dc --- /dev/null +++ b/fh.fd.ci.server/src/test/java/de/fd/fh/server/game/GameServiceTest.java @@ -0,0 +1,107 @@ +package de.fd.fh.server.game; + +import de.fd.fh.server.user.UserId; +import dev.morphia.Key; +import org.junit.jupiter.api.Test; +import org.mockito.ArgumentCaptor; + +import java.util.List; +import java.util.Set; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.BDDMockito.then; +import static org.mockito.Mockito.*; + +class GameServiceTest +{ + @Test + void testCreateGame() + { + final GameRepository repository = when(mock(GameRepository.class).save(any())) + .thenReturn(new Key<>(Game.class, "testCollection", GameId.of("98765"))) + .getMock(); + + final Game game = new Game(); + + final GameId result = new GameService(repository).createGame(game); + + assertNotNull(result); + assertEquals("98765", result.getIdentifier()); + } + + @Test + void given_twoUserIds_when_initGame_should_initGame() + { + final UserId blackPlayer = UserId.of("12345"); + final UserId whitePlayer = UserId.of("98765"); + + final GameRepository repository = when(mock(GameRepository.class).save(any())) + .thenReturn(new Key<>(Game.class, "testCollection", GameId.of("4242"))) + .getMock(); + + final ArgumentCaptor captor = ArgumentCaptor.forClass(Game.class); + + final GameId result = new GameService(repository).createGame(blackPlayer, whitePlayer); + + verify(repository).save(captor.capture()); + + assertEquals("4242", result.getIdentifier()); + + assertNotNull(captor.getValue()); + + final Game savedGame = captor.getValue(); + + assertEquals("12345", savedGame.getBlackPlayer().getIdentifier()); + assertEquals("98765", savedGame.getWhitePlayer().getIdentifier()); + assertEquals("98765", savedGame.getCurrentPlayer().getIdentifier()); + assertEquals(64, savedGame.getFields().size()); + + final Set fields = savedGame.getFields(); + + fields.forEach(field -> { + + final Field.Color expected = (field.getX() + field.getY()) % 2 != 1 ? Field.Color.WHITE : Field.Color.BLACK; + + if (field.getX() == 0 || field.getX() == 1 || field.getX() == 6 || field.getX() == 7) + { + assertNotNull(field.getFigure()); + } + else + { + assertNull(field.getFigure()); + } + + assertEquals(expected, field.getColor()); + }); + + then(repository).should().save(any(Game.class)); + then(repository).shouldHaveNoMoreInteractions(); + } + + @Test + void given_storedGame_when_findGameById_should_returnGame() + { + final GameRepository repository = when(mock(GameRepository.class).findByGameId(any(GameId.class))) + .thenReturn(new Game(null, null, null, null, null, null)) + .getMock(); + + final Game result = new GameService(repository).findGameById(GameId.of("12345")); + + assertNotNull(result); + } + + @Test + void given_storedGames_when_findGamesOfUser_should_returnSetOfGames() + { + final GameRepository repository = when(mock(GameRepository.class).findRunningGamesOfPlayer(any(UserId.class))) + .thenReturn(List.of( + new Game(GameId.random(), UserId.of("12345"), UserId.random(), null, null, null), + new Game(GameId.random(), UserId.random(), UserId.of("12345"), null, null, null))) + .getMock(); + + final List result = new GameService(repository).findRunningGamesOfUser(UserId.of("12345")); + + assertEquals(2, result.size()); + } +} \ No newline at end of file diff --git a/fh.fd.ci.server/src/test/java/de/fd/fh/server/game/web/GameControllerTest.java b/fh.fd.ci.server/src/test/java/de/fd/fh/server/game/web/GameControllerTest.java new file mode 100644 index 0000000..2b848d9 --- /dev/null +++ b/fh.fd.ci.server/src/test/java/de/fd/fh/server/game/web/GameControllerTest.java @@ -0,0 +1,146 @@ +package de.fd.fh.server.game.web; + +import com.fasterxml.jackson.core.JsonProcessingException; +import de.fd.fh.server.game.Game; +import de.fd.fh.server.game.GameId; +import de.fd.fh.server.game.GameService; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import spark.Request; +import spark.Response; + +import java.io.IOException; +import java.util.List; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +public class GameControllerTest +{ + @Mock + Request request; + @Mock + Response response; + @Mock + GameService service; + + @BeforeEach + void before() + { + MockitoAnnotations.openMocks(this); + } + + @Test + void testCreateGame() throws IOException + { + when(request.body()).thenReturn("{\"whiteUserId\":\"12345\",\"blackUserId\":\"98765\"}"); + when(service.createGame(any(), any())).thenReturn(GameId.of("56789")); + + final ArgumentCaptor bodyCaptor = ArgumentCaptor.forClass(String.class); + final ArgumentCaptor statusCaptor = ArgumentCaptor.forClass(Integer.class); + + new GameController(service).createGame(request, response); + + verify(response).body(bodyCaptor.capture()); + verify(response).status(statusCaptor.capture()); + + assertNotNull(bodyCaptor.getValue()); + assertTrue(bodyCaptor.getValue().contains("56789")); + + assertNotNull(statusCaptor.getValue()); + assertEquals(201, statusCaptor.getValue()); + } + + @Test + void testFailedCreateGame() throws IOException + { + when(request.body()).thenReturn("{\"whiteUserId\":\"12345\",\"blackUserId\":\"98765\"}"); + when(service.createGame(any(), any())).thenReturn(null); + + final ArgumentCaptor statusCaptor = ArgumentCaptor.forClass(Integer.class); + + new GameController(service).createGame(request, response); + + verify(response).status(statusCaptor.capture()); + + assertNotNull(statusCaptor.getValue()); + assertEquals(400, statusCaptor.getValue()); + } + + @Test + void testFindGameById() throws IOException + { + when(request.params(any())).thenReturn("12345"); + when(service.findGameById(any())).thenReturn(new Game()); + + final ArgumentCaptor bodyCaptor = ArgumentCaptor.forClass(String.class); + final ArgumentCaptor statusCaptor = ArgumentCaptor.forClass(Integer.class); + + new GameController(service).findGameById(request, response); + + verify(response).body(bodyCaptor.capture()); + verify(response).status(statusCaptor.capture()); + + assertNotNull(bodyCaptor.getValue()); + + assertNotNull(statusCaptor.getValue()); + assertEquals(200, statusCaptor.getValue()); + } + + @Test + void testFailedFindGameById() throws IOException + { + when(request.params(any())).thenReturn("12345"); + when(service.findGameById(any())).thenReturn(null); + + final ArgumentCaptor statusCaptor = ArgumentCaptor.forClass(Integer.class); + + new GameController(service).findGameById(request, response); + + verify(response).status(statusCaptor.capture()); + + assertNotNull(statusCaptor.getValue()); + assertEquals(404, statusCaptor.getValue()); + } + + @Test + void testFindRunningGamesOfUser() throws JsonProcessingException + { + when(request.params(any())).thenReturn("12345"); + when(service.findRunningGamesOfUser(any())).thenReturn(List.of(new Game(), new Game())); + + final ArgumentCaptor bodyCaptor = ArgumentCaptor.forClass(String.class); + final ArgumentCaptor statusCaptor = ArgumentCaptor.forClass(Integer.class); + + new GameController(service).findRunningGamesOfUser(request, response); + + verify(response).body(bodyCaptor.capture()); + verify(response).status(statusCaptor.capture()); + + assertNotNull(bodyCaptor.getValue()); + + assertNotNull(statusCaptor.getValue()); + assertEquals(200, statusCaptor.getValue()); + } + + @Test + void testFailedFindRunningGamesOfUser() throws JsonProcessingException + { + when(request.params(any())).thenReturn("12345"); + when(service.findRunningGamesOfUser(any())).thenReturn(null); + + final ArgumentCaptor statusCaptor = ArgumentCaptor.forClass(Integer.class); + + new GameController(service).findRunningGamesOfUser(request, response); + + verify(response).status(statusCaptor.capture()); + + assertNotNull(statusCaptor.getValue()); + assertEquals(404, statusCaptor.getValue()); + } +} diff --git a/fh.fd.ci.shared/src/main/java/de/fd/fh/shared/network/messages/CreateGameRequest.java b/fh.fd.ci.shared/src/main/java/de/fd/fh/shared/network/messages/CreateGameRequest.java new file mode 100644 index 0000000..b5f0e42 --- /dev/null +++ b/fh.fd.ci.shared/src/main/java/de/fd/fh/shared/network/messages/CreateGameRequest.java @@ -0,0 +1,14 @@ +package de.fd.fh.shared.network.messages; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Getter +@AllArgsConstructor +@NoArgsConstructor +public class CreateGameRequest +{ + private String whiteUserId; + private String blackUserId; +}