Server/Server/src/test/java/uulm/teamname/marvelous/server/MarvelousServerApplicationT...

281 lines
10 KiB
Java

package uulm.teamname.marvelous.server;
import org.junit.jupiter.api.*;
import org.mockito.MockedStatic;
import org.mockito.Mockito;
import org.mockito.invocation.Invocation;
import org.tinylog.configuration.Configuration;
import uulm.teamname.marvelous.gamelibrary.json.JSON;
import uulm.teamname.marvelous.gamelibrary.messages.BasicMessage;
import uulm.teamname.marvelous.gamelibrary.messages.ParticipantType;
import uulm.teamname.marvelous.gamelibrary.messages.RoleEnum;
import uulm.teamname.marvelous.gamelibrary.messages.client.CharacterSelectionMessage;
import uulm.teamname.marvelous.gamelibrary.messages.client.HelloServerMessage;
import uulm.teamname.marvelous.gamelibrary.messages.client.PlayerReadyMessage;
import uulm.teamname.marvelous.gamelibrary.messages.client.ReconnectMessage;
import uulm.teamname.marvelous.gamelibrary.messages.server.*;
import uulm.teamname.marvelous.server.net.Client;
import uulm.teamname.marvelous.server.net.ServerSession;
import uulm.teamname.marvelous.server.net.SocketEvent;
import uulm.teamname.marvelous.server.net.SocketEventType;
import java.lang.reflect.Field;
import java.util.Optional;
import static org.mockito.Mockito.*;
@SuppressWarnings({"ResultOfMethodCallIgnored", "SameParameterValue", "unchecked"})
class MarvelousServerApplicationTest extends BaseGameLogicTest {
private static ServerSession session;
private static MockedStatic<ServerApplication> serverMock;
private static JSON json;
@BeforeAll
static void start() {
session = new ServerSession();
serverMock = Mockito.mockStatic(ServerApplication.class);
generate();
serverMock.when(ServerApplication::getSession).thenReturn(session);
serverMock.when(ServerApplication::getPartyConfig).thenReturn(partyConfig);
serverMock.when(ServerApplication::getScenarioConfig).thenReturn(scenarioConfig);
serverMock.when(ServerApplication::getCharacterConfig).thenReturn(characterConfig);
json = new JSON(characterConfig);
if(!Configuration.isFrozen()) {
Configuration.set("writer1", "console");
Configuration.set("writer1.level", "trace");
Configuration.set("writer1.format", "[{thread}] {level}: <TEST> {message}");
}
}
@Test
@Disabled
void main() {
session.run();
Client p1 = mock(Client.class);
Client p2 = mock(Client.class);
Client s1 = mock(Client.class);
Client s2 = mock(Client.class);
session.addEvent(new SocketEvent(SocketEventType.Connect, p1));
ensureHandshake(p1, "Player 1", "1234", false);
session.addEvent(new SocketEvent(SocketEventType.Connect, p2));
ensureHandshake(p2, "Player 2", "4321", false);
session.addEvent(new SocketEvent(SocketEventType.Connect, s1));
ensureHandshake(s1, "Spectator 1", "3333", false);
session.addEvent(new SocketEvent(SocketEventType.Connect, s2));
ensureHandshake(s2, "Spectator 2", "4444", false);
ensurePlayerReady(p1, true, RoleEnum.PLAYER);
ensurePlayerReady(p2, true, RoleEnum.PLAYER);
ensureSpectatorReady(s1, true, RoleEnum.SPECTATOR);
ensureCharacterSelection(p1, true);
ensureCharacterSelection(p2, false);
GameStructureMessage game = new GameStructureMessage();
game.playerOneName = "Player 1";
game.playerTwoName = "Player 2";
game.matchconfig = partyConfig;
game.scenarioconfig = scenarioConfig;
game.assignment = ParticipantType.PlayerOne;
ensureReceived(p1, game);
game.assignment = ParticipantType.PlayerTwo;
ensureReceived(p2, game);
game.assignment = ParticipantType.Spectator;
ensureReceived(s1, game);
EventMessage events = new EventMessage();
ensureReceived(p1, events);
ensureReceived(p2, events);
ensureReceived(s1, events);
clearInvocations(p1, p2, s1);
session.addEvent(new SocketEvent(SocketEventType.Disconnect, p1));
session.addEvent(new SocketEvent(SocketEventType.Connect, p1));
ensureHandshake(p1, "Player 1", "1234", true);
ReconnectMessage message = new ReconnectMessage();
message.reconnect = true;
sendMessage(p1, message);
ensureReceived(p1, new GeneralAssignmentMessage());
ensureReceived(p1, new GameStructureMessage());
System.out.println("Test Completed");
}
private void ensureHandshake(Client c, String name, String deviceID, boolean runningGame) {
HelloServerMessage message = new HelloServerMessage();
message.name = name;
message.deviceID = deviceID;
HelloClientMessage response = new HelloClientMessage();
response.runningGame = runningGame;
ensureResponse(c, message, response);
}
private void ensurePlayerReady(Client c, boolean startGame, RoleEnum role) {
PlayerReadyMessage message = new PlayerReadyMessage();
message.startGame = startGame;
message.role = role;
GameAssignmentMessage response = new GameAssignmentMessage();
//properties are left null because we can't test their content (they are random)
ensureResponse(c, message, response);
}
private void ensureSpectatorReady(Client c, boolean startGame, RoleEnum role) {
PlayerReadyMessage message = new PlayerReadyMessage();
message.startGame = startGame;
message.role = role;
GeneralAssignmentMessage response = new GeneralAssignmentMessage();
//properties are left null because we can't test their content (they are random)
ensureResponse(c, message, response);
}
private void ensureCharacterSelection(Client c, boolean confirmSelection) {
CharacterSelectionMessage message = new CharacterSelectionMessage();
message.characters = new Boolean[12];
for(int i = 0; i < 6; i++) {
message.characters[i] = true;
}
for(int i = 6; i < 12; i++) {
message.characters[i] = false;
}
if(confirmSelection) {
ConfirmSelectionMessage response = new ConfirmSelectionMessage();
response.selectionComplete = false;
ensureResponse(c, message, response);
} else {
sendMessage(c, message);
}
}
/** Sends a message from the socket. */
private void sendMessage(Client c, BasicMessage message) {
Optional<String> in = json.stringify(message);
if(in.isPresent()) {
session.addEvent(new SocketEvent(SocketEventType.Message, c, in.get()));
}else {
throw new IllegalArgumentException("[TEST] Message in test call could not be serialized!");
}
}
/** Ensures that the given socket received the given response in response to the given message. */
private void ensureResponse(Client c, BasicMessage message, BasicMessage response) {
Optional<String> in = json.stringify(message);
if(in.isPresent()) {
session.addEvent(new SocketEvent(SocketEventType.Message, c, in.get()));
verify(c.getSocket()).send((String)argThat(
(out)->verifyResponse((String)out, response)
));
clearInvocations(c.getSocket());
}else {
throw new IllegalArgumentException("[TEST] Message in test call could not be serialized!");
}
}
/** Ensures that the given socket received the given response. */
private <T extends BasicMessage> void ensureReceived(Client c, T response) {
boolean found = false;
for(Invocation i: mockingDetails(c.getSocket()).getInvocations()) {
Optional<BasicMessage> received = json.parse(i.getArgument(0, String.class));
if(received.isPresent()) {
BasicMessage parsed = received.get();
if(parsed.getClass() != response.getClass()) {
continue; //message is not of the right type
}
T message = (T)parsed;
for(Field field: response.getClass().getDeclaredFields()) {
try {
Object value = field.get(response);
Object actualValue;
try {
actualValue = field.get(message);
}catch(IllegalArgumentException ignored) {
continue;
}
if(value != null && !value.equals(actualValue)) {
throw new IllegalArgumentException("[TEST] Field " + field.getName() + " expected to be '" + value + "' but was '" + field.get(message) + "'");
}
}catch(IllegalAccessException ignored) { }
}
found = true;
break;
}else {
throw new IllegalArgumentException("[TEST] Response in test call could not be deserialized!");
}
}
if(!found) {
throw new IllegalArgumentException("[TEST] Expected response " + response + " was not received!");
}
}
/**
* Verifies a response message and requires all non-null fields of the expected message
* to equal the respective values in the actual received message.
*/
private <T extends BasicMessage> boolean verifyResponse(String actual, T expected) {
Optional<BasicMessage> in = json.parse(actual);
if(in.isPresent()) {
T message = (T)in.get();
for(Field field: expected.getClass().getDeclaredFields()) {
try {
Object value = field.get(expected);
Object actualValue;
try {
actualValue = field.get(message);
}catch(IllegalArgumentException e) {
throw new IllegalArgumentException("[TEST] Response message expected to be '" + expected.getClass().getName() + "' but was '" + message.getClass().getName() + "'");
}
if(value != null && !value.equals(actualValue)) {
throw new IllegalArgumentException("[TEST] Field " + field.getName() + " expected to be '" + value + "' but was '" + field.get(message) + "'");
}
}catch(IllegalAccessException ignore) { }
}
return true;
}else {
throw new IllegalArgumentException("[TEST] Response in test call could not be deserialized!");
}
}
@AfterAll
static void stop() {
session.stop();
serverMock.close();
}
}