package com.cardshifter.server.main; import java.io.File; import java.io.IOException; import java.net.UnknownHostException; import java.nio.file.Paths; import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.function.Predicate; import com.cardshifter.api.outgoing.*; import com.fasterxml.jackson.core.JsonGenerationException; import org.apache.log4j.PropertyConfigurator; import org.junit.After; import org.junit.Before; import org.junit.Test; import com.cardshifter.ai.AIs; import com.cardshifter.ai.ScoringAI; import com.cardshifter.api.ClientIO; import com.cardshifter.api.both.ChatMessage; import com.cardshifter.api.both.PlayerConfigMessage; import com.cardshifter.api.incoming.LoginMessage; import com.cardshifter.api.incoming.ServerQueryMessage; import com.cardshifter.api.incoming.ServerQueryMessage.Request; import com.cardshifter.api.incoming.StartGameRequest; import com.cardshifter.api.incoming.UseAbilityMessage; import com.cardshifter.api.outgoing.UserStatusMessage.Status; import com.cardshifter.core.game.ServerGame; import com.cardshifter.core.game.TCGGame; import com.cardshifter.modapi.actions.ECSAction; import com.cardshifter.modapi.ai.AIComponent; import com.cardshifter.modapi.ai.CardshifterAI; import com.cardshifter.modapi.base.ECSGameState; import com.cardshifter.modapi.base.Entity; import com.cardshifter.modapi.base.PlayerComponent; import com.cardshifter.server.model.MainServer; import com.cardshifter.server.model.Server; import static org.junit.Assert.*; public class ServerConnectionTest { private String getTestMod() { return mods.getMods()[0]; } private TestClient createTestClient() throws IOException { return new TestClient(socketPort); } private MainServer main; private Server server; private int socketPort; private TestClient client1; private int userId; private AvailableModsMessage mods; @Before public void setup() throws IOException, InterruptedException { PropertyConfigurator.configure(getClass().getResourceAsStream("log4j.properties")); ServerConfiguration config = ServerConfiguration.defaults(); // Use any available port config.setPortSocket(0); config.setPortWebsocket(0); main = new MainServer(config); main.getMods().loadExternal(Paths.get("../extra-resources/groovy")); server = main.start(); assertTrue("Server should start correctly.", server.getClients().size() > 0); socketPort = config.getPortSocket(); client1 = createTestClient(); client1.send(new LoginMessage("Tester1")); WelcomeMessage welcome = client1.await(WelcomeMessage.class); assertEquals(200, welcome.getStatus()); System.out.println(server.getClients()); assertEquals(server.getClients().size() + 1, welcome.getUserId()); userId = welcome.getUserId(); client1.await(ChatMessage.class); mods = client1.await(AvailableModsMessage.class); assertNotEquals("No mods found in " + new File("").getAbsolutePath(), 0, mods.getMods().length); } @After public void shutdown() throws InterruptedException { try { client1.disconnect(); } catch (IOException e) { } server.stop(); } @Test(timeout = 20000) public void testUserOnlineOffline() throws InterruptedException, UnknownHostException, IOException { TestClient client2 = createTestClient(); client2.send(new LoginMessage("Test2")); client2.await(WelcomeMessage.class); client2.await(ChatMessage.class); UserStatusMessage statusMessage = client1.await(UserStatusMessage.class); ChatMessage chat = client1.await(ChatMessage.class); String message = chat.getMessage(); assertTrue("Unexpected message: " + message, message.contains(client2.getName()) && message.contains("joined")); int client2id = statusMessage.getUserId(); assertEquals(Status.ONLINE, statusMessage.getStatus()); assertEquals(server.getClients().size() + 1, client2id); assertEquals(client2.getName(), statusMessage.getName()); client2.send(new ServerQueryMessage(Request.USERS)); client2.await(AvailableModsMessage.class); List<UserStatusMessage> users = client2.awaitMany(6, UserStatusMessage.class); System.out.println("Online users: " + users); // There is no determined order in which the UserStatusMessages are received, so it is harder to make any assertions. assertUserFound(users, client1.getName()); assertUserFound(users, client2.getName()); assertUserFound(users, "AI Fighter"); assertUserFound(users, "AI Loser"); assertUserFound(users, "AI Medium"); assertUserFound(users, "AI Idiot"); client2.disconnect(); System.out.println(chat); statusMessage = client1.await(UserStatusMessage.class); assertEquals(Status.OFFLINE, statusMessage.getStatus()); assertEquals(client2id, statusMessage.getUserId()); assertEquals(client2.getName(), statusMessage.getName()); } @Test(timeout = 5000) public void testSameUserName() throws IOException, InterruptedException { TestClient client2 = createTestClient(); client2.send(new LoginMessage(client1.getName())); WelcomeMessage welcomeMessage = client2.await(WelcomeMessage.class); assertFalse(welcomeMessage.isOK()); } private static void assertUserFound(Collection<UserStatusMessage> users, String name) { assertTrue("User '" + name + "' not found", users.stream().filter(mess -> mess.getName().equals(name)).findAny().isPresent()); } @Test(timeout = 50000) public void testStartGame() throws InterruptedException, IOException { client1.send(new StartGameRequest(2, getTestMod())); NewGameMessage gameMessage = client1.await(NewGameMessage.class); assertEquals(1, gameMessage.getGameId()); client1.awaitUntil(PlayerConfigMessage.class); TCGGame game = (TCGGame) server.getGames().get(1); assertEquals(2, game.getGameModel().getEntitiesWithComponent(PlayerComponent.class).size()); assertTrue(game.hasPlayer(server.getClients().get(userId))); assertTrue(game.hasPlayer(server.getClients().get(2))); game.incomingPlayerConfig(new PlayerConfigMessage(game.getId(), getTestMod(), new HashMap<>()), server.getClients().get(2)); game.incomingPlayerConfig(new PlayerConfigMessage(game.getId(), getTestMod(), new HashMap<>()), server.getClients().get(userId)); client1.awaitUntil(ResetAvailableActionsMessage.class); assertEquals(ECSGameState.RUNNING, game.getState()); } @Test(timeout = 100000) public void testPlayGame() throws InterruptedException, IOException { testPlayAny(); client1.awaitUntil(PlayerConfigMessage.class); TCGGame game = (TCGGame) server.getGames().get(1); ClientIO io = server.getClients().get(userId); assertEquals(2, game.getGameModel().getEntitiesWithComponent(PlayerComponent.class).size()); game.incomingPlayerConfig(new PlayerConfigMessage(game.getId(), getTestMod(), new HashMap<>()), io); assertEquals(ECSGameState.RUNNING, game.getGameModel().getGameState()); Entity human = game.playerFor(io); Entity ai = game.getGameModel().getEntitiesWithComponent(AIComponent.class).stream().findFirst().get(); ai.getComponent(AIComponent.class).setDelay(0); CardshifterAI humanActions = new ScoringAI(AIs.medium()); client1.awaitUntil(ResetAvailableActionsMessage.class); client1.awaitUntil(ResetAvailableActionsMessage.class); client1.awaitUntil(UsableActionMessage.class); while (!game.isGameOver()) { ECSAction action = humanActions.getAction(human); if (action != null) { System.out.println("Perform " + action); int[] targets = new int[]{ }; if (!action.getTargetSets().isEmpty()) { targets = action.getTargetSets().get(0).getChosenTargets().stream().mapToInt(e -> e.getId()).toArray(); } UseAbilityMessage message = new UseAbilityMessage(game.getId(), action.getOwner().getId(), action.getName(), targets); System.out.println("Sending message: " + message); client1.send(message); client1.awaitUntil(ResetAvailableActionsMessage.class); } else { System.out.println("Nothing to perform, busy-loop"); } } } @Test(timeout = 10000) public void testPlayAny() throws InterruptedException, IOException { Predicate<ClientIO> opponentFilter = client -> client.getName().equals("AI Loser"); server.getIncomingHandler().perform(new StartGameRequest(-1, getTestMod()), server.getClients().values().stream().filter(opponentFilter).findAny().get()); client1.send(new StartGameRequest(-1, getTestMod())); NewGameMessage gameMessage = client1.await(NewGameMessage.class); assertEquals(1, gameMessage.getGameId()); ServerGame game = server.getGames().get(1); assertTrue(game.hasPlayer(server.getClients().get(userId))); } @Test(timeout = 10000) public void testOnlyOneInvite() throws IOException, InterruptedException { TestClient client2 = createTestClient(); client2.send(new LoginMessage("client2")); WelcomeMessage welcomeMessage = client2.await(WelcomeMessage.class); assertTrue(welcomeMessage.isOK()); int client2id = welcomeMessage.getUserId(); client1.await(UserStatusMessage.class); client1.await(ChatMessage.class); client1.send(new StartGameRequest(client2id, getTestMod())); client1.send(new StartGameRequest(client2id, getTestMod())); client1.await(ServerErrorMessage.class); } /** * Assert that no message is in the queue or being sent from the server. * DOES NOT WORK IF THE MESSAGE IN THE QUEUE IS ServerStatusMessage! */ private static void assertNoMessage(TestClient client) throws IOException, InterruptedException { client.send(new ServerQueryMessage(Request.STATUS, "")); client.await(ServerStatusMessage.class); } @Test(timeout = 5000) public void testUsersQueryNotLoggedIn() throws IOException, InterruptedException { TestClient client2 = createTestClient(); client1.send(new ServerQueryMessage(Request.USERS, "")); List<UserStatusMessage> users = client1.awaitMany(5, UserStatusMessage.class); assertUserFound(users, client1.getName()); assertUserFound(users, "AI Fighter"); assertUserFound(users, "AI Loser"); assertUserFound(users, "AI Medium"); assertUserFound(users, "AI Idiot"); // There shouldn't be a UserStatusMessage for client2 assertNoMessage(client1); } }