Merge branch 'main' into feat/game-ui-optimation
This commit is contained in:
@@ -63,6 +63,7 @@ import ch.unibas.dmi.dbis.cs108.casono.server.app.commands.ping.PingRequest;
|
||||
import ch.unibas.dmi.dbis.cs108.casono.server.app.commands.send_message.SendMessageHandler;
|
||||
import ch.unibas.dmi.dbis.cs108.casono.server.app.commands.send_message.SendMessageParser;
|
||||
import ch.unibas.dmi.dbis.cs108.casono.server.app.commands.send_message.SendMessageRequest;
|
||||
import ch.unibas.dmi.dbis.cs108.casono.server.domain.lobby.LobbyCleanupJob;
|
||||
import ch.unibas.dmi.dbis.cs108.casono.server.domain.lobby.LobbyManager;
|
||||
import ch.unibas.dmi.dbis.cs108.casono.server.domain.user.UserCleanupJob;
|
||||
import ch.unibas.dmi.dbis.cs108.casono.server.domain.user.UserRegistry;
|
||||
@@ -73,11 +74,7 @@ import ch.unibas.dmi.dbis.cs108.casono.server.network.command.execution.CommandR
|
||||
import ch.unibas.dmi.dbis.cs108.casono.server.network.command.parsing.CommandParserDispatcher;
|
||||
import ch.unibas.dmi.dbis.cs108.casono.server.network.events.DisconnectEvent;
|
||||
import ch.unibas.dmi.dbis.cs108.casono.server.network.events.EventBus;
|
||||
import ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.request.RequestContext;
|
||||
import ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.response.SuccessResponse;
|
||||
import ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.response.builder.ResponseBody;
|
||||
import ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.response.dispatcher.ResponseDispatcher;
|
||||
import ch.unibas.dmi.dbis.cs108.casono.server.network.sessions.Session;
|
||||
import ch.unibas.dmi.dbis.cs108.casono.server.network.sessions.SessionDisconnectJob;
|
||||
import ch.unibas.dmi.dbis.cs108.casono.server.network.sessions.SessionManager;
|
||||
import java.time.Duration;
|
||||
@@ -141,37 +138,12 @@ public class ServerApp {
|
||||
userRegistry,
|
||||
new CommandContext(lobbyManager, sessionManager));
|
||||
|
||||
// Periodic cleanup: remove empty lobbies older than 30s and notify affected
|
||||
// users
|
||||
scheduler.scheduleAtFixedRate(
|
||||
() -> {
|
||||
try {
|
||||
var expired =
|
||||
lobbyManager.findEmptyLobbiesOlderThan(
|
||||
Duration.ofSeconds(LOBBY_EXPIRY_SECONDS));
|
||||
for (var lid : expired) {
|
||||
// remove lobby from manager first
|
||||
lobbyManager.removeLobby(lid);
|
||||
|
||||
// broadcast LOBBY_CLOSED event to all connected sessions
|
||||
// (requestId=0)
|
||||
for (Session s : sessionManager.getAllSessions()) {
|
||||
RequestContext ctx = new RequestContext(s.getId(), 0);
|
||||
SuccessResponse ev =
|
||||
new SuccessResponse(
|
||||
ctx,
|
||||
ResponseBody.builder()
|
||||
.param("EVENT", "LOBBY_CLOSED")
|
||||
.param("LOBBY_ID", lid.value())
|
||||
.build()) {};
|
||||
|
||||
responseDispatcher.dispatch(ev);
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.warn("Lobby expiry job failed", e);
|
||||
}
|
||||
},
|
||||
new LobbyCleanupJob(
|
||||
lobbyManager,
|
||||
sessionManager,
|
||||
responseDispatcher,
|
||||
Duration.ofSeconds(LOBBY_EXPIRY_SECONDS)),
|
||||
LOBBY_CLEANUP_INITIAL_DELAY_SECONDS,
|
||||
LOBBY_CLEANUP_PERIOD_SECONDS,
|
||||
TimeUnit.SECONDS);
|
||||
|
||||
+100
@@ -0,0 +1,100 @@
|
||||
package ch.unibas.dmi.dbis.cs108.casono.server.domain.lobby;
|
||||
|
||||
import ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.request.RequestContext;
|
||||
import ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.response.SuccessResponse;
|
||||
import ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.response.builder.ResponseBody;
|
||||
import ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.response.dispatcher.ResponseDispatcher;
|
||||
import ch.unibas.dmi.dbis.cs108.casono.server.network.sessions.Session;
|
||||
import ch.unibas.dmi.dbis.cs108.casono.server.network.sessions.SessionManager;
|
||||
import java.time.Duration;
|
||||
import java.util.List;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
/** Periodically removes expired empty lobbies and notifies connected sessions. */
|
||||
public class LobbyCleanupJob implements Runnable {
|
||||
|
||||
private static final Logger LOGGER = LogManager.getLogger(LobbyCleanupJob.class);
|
||||
|
||||
private final LobbyManager lobbyManager;
|
||||
private final SessionManager sessionManager;
|
||||
private final ResponseDispatcher responseDispatcher;
|
||||
private final Duration expiryThreshold;
|
||||
|
||||
/**
|
||||
* Creates a new cleanup job for expired empty lobbies.
|
||||
*
|
||||
* @param lobbyManager manager used to find and remove expired lobbies
|
||||
* @param sessionManager manager used to resolve connected sessions
|
||||
* @param responseDispatcher dispatcher used to notify clients about closed lobbies
|
||||
* @param expiryThreshold age threshold that marks an empty lobby as expired
|
||||
*/
|
||||
public LobbyCleanupJob(
|
||||
LobbyManager lobbyManager,
|
||||
SessionManager sessionManager,
|
||||
ResponseDispatcher responseDispatcher,
|
||||
Duration expiryThreshold) {
|
||||
this.lobbyManager = lobbyManager;
|
||||
this.sessionManager = sessionManager;
|
||||
this.responseDispatcher = responseDispatcher;
|
||||
this.expiryThreshold = expiryThreshold;
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs one cleanup cycle.
|
||||
*
|
||||
* <p>The method first fetches all expired empty lobbies. Each lobby is then processed
|
||||
* independently so that a failure for one lobby does not stop the remaining cleanups.
|
||||
*/
|
||||
@Override
|
||||
public void run() {
|
||||
LOGGER.debug("Job started.");
|
||||
try {
|
||||
List<LobbyId> expired;
|
||||
try {
|
||||
expired = lobbyManager.findEmptyLobbiesOlderThan(expiryThreshold);
|
||||
} catch (Exception e) {
|
||||
LOGGER.error("Lobby expiry job failed: could not fetch expired lobbies", e);
|
||||
return;
|
||||
}
|
||||
|
||||
for (LobbyId lobbyId : expired) {
|
||||
try {
|
||||
lobbyManager.removeLobby(lobbyId);
|
||||
broadcastLobbyClosed(lobbyId);
|
||||
} catch (RuntimeException e) {
|
||||
LOGGER.warn("Failed to process expired lobby {}", lobbyId.value(), e);
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
LOGGER.debug("Job finished.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Broadcasts a lobby-closed event to all currently connected sessions.
|
||||
*
|
||||
* @param lobbyId id of the lobby that was closed
|
||||
*/
|
||||
private void broadcastLobbyClosed(LobbyId lobbyId) {
|
||||
for (Session session : sessionManager.getAllSessions()) {
|
||||
try {
|
||||
RequestContext ctx = new RequestContext(session.getId(), 0);
|
||||
SuccessResponse event =
|
||||
new SuccessResponse(
|
||||
ctx,
|
||||
ResponseBody.builder()
|
||||
.param("EVENT", "LOBBY_CLOSED")
|
||||
.param("LOBBY_ID", lobbyId.value())
|
||||
.build()) {};
|
||||
responseDispatcher.dispatch(event);
|
||||
} catch (RuntimeException e) {
|
||||
LOGGER.warn(
|
||||
"Failed to notify session {} about closed lobby {}",
|
||||
session.getId().value(),
|
||||
lobbyId.value(),
|
||||
e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -48,7 +48,7 @@ public class TokenClassifier {
|
||||
}
|
||||
|
||||
private static void validateSeparator(List<RawToken> rawTokens, int index) {
|
||||
boolean missingKey = index == 0 || rawTokens.get(index - 1).type() != RawTokenType.WORD;
|
||||
boolean missingKey = index <= 1 || rawTokens.get(index - 1).type() != RawTokenType.WORD;
|
||||
|
||||
boolean missingValue =
|
||||
index + 1 >= rawTokens.size()
|
||||
|
||||
+94
@@ -0,0 +1,94 @@
|
||||
package ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.parser.tokenizer;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
public class TokenClassifierTest {
|
||||
@Test
|
||||
void testClassifySimpleCommand() {
|
||||
List<RawToken> raw = Tokenizer.tokenize("PING");
|
||||
List<Token> tokens = TokenClassifier.classify(raw);
|
||||
|
||||
assertEquals(2, tokens.size());
|
||||
assertEquals(TokenType.COMMAND, tokens.get(0).type());
|
||||
assertEquals("PING", tokens.get(0).value());
|
||||
assertEquals(TokenType.EOF, tokens.get(1).type());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testCommandWithParameter() {
|
||||
List<RawToken> raw = Tokenizer.tokenize("ANSWER VALUE=42");
|
||||
List<Token> tokens = TokenClassifier.classify(raw);
|
||||
|
||||
assertEquals(5, tokens.size());
|
||||
assertEquals(TokenType.COMMAND, tokens.get(0).type());
|
||||
assertEquals(TokenType.KEY, tokens.get(1).type());
|
||||
assertEquals(TokenType.SEPARATOR, tokens.get(2).type());
|
||||
assertEquals(TokenType.VALUE, tokens.get(3).type());
|
||||
assertEquals("42", tokens.get(3).value());
|
||||
assertEquals(TokenType.EOF, tokens.get(4).type());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testStringValue() {
|
||||
List<RawToken> raw = Tokenizer.tokenize("GREET MSG='Hello World'");
|
||||
List<Token> tokens = TokenClassifier.classify(raw);
|
||||
|
||||
assertEquals(5, tokens.size());
|
||||
assertEquals(TokenType.COMMAND, tokens.get(0).type());
|
||||
assertEquals(TokenType.KEY, tokens.get(1).type());
|
||||
assertEquals(TokenType.SEPARATOR, tokens.get(2).type());
|
||||
assertEquals(TokenType.VALUE, tokens.get(3).type());
|
||||
assertEquals("Hello World", tokens.get(3).value());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testUnexpectedStringLiteralThrows() {
|
||||
List<RawToken> raw = Tokenizer.tokenize("CMD 'oops'");
|
||||
|
||||
TokenizerException ex =
|
||||
assertThrows(TokenizerException.class, () -> TokenClassifier.classify(raw));
|
||||
assertTrue(ex.getMessage().contains("Unexpected string literal"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testMissingValueAfterSeparatorThrows() {
|
||||
List<RawToken> raw = Tokenizer.tokenize("CMD KEY=");
|
||||
|
||||
TokenizerException ex =
|
||||
assertThrows(TokenizerException.class, () -> TokenClassifier.classify(raw));
|
||||
assertEquals("Expected VALUE after '='", ex.getMessage());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testMissingKeyBeforeSeparatorThrows() {
|
||||
List<RawToken> raw = Tokenizer.tokenize("CMD =VALUE");
|
||||
|
||||
TokenizerException ex =
|
||||
assertThrows(TokenizerException.class, () -> TokenClassifier.classify(raw));
|
||||
assertEquals("Expected KEY before '='", ex.getMessage());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testNextWordIsKeyThrows() {
|
||||
List<RawToken> raw = Tokenizer.tokenize("CMD KEY1=KEY2=42");
|
||||
|
||||
TokenizerException ex =
|
||||
assertThrows(TokenizerException.class, () -> TokenClassifier.classify(raw));
|
||||
assertEquals("Expected VALUE after '='", ex.getMessage());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testEmptyRawTokensThrows() {
|
||||
List<RawToken> raw = new ArrayList<>();
|
||||
|
||||
TokenizerException ex =
|
||||
assertThrows(TokenizerException.class, () -> TokenClassifier.classify(raw));
|
||||
assertEquals("Expected COMMAND as first token", ex.getMessage());
|
||||
assertEquals(1, ex.getLine());
|
||||
assertEquals(1, ex.getColumn());
|
||||
}
|
||||
}
|
||||
+211
@@ -0,0 +1,211 @@
|
||||
package ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.parser.tokenizer;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
import java.util.List;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
class TokenizerTest {
|
||||
@Test
|
||||
void testEofAlwaysPresent() {
|
||||
List<RawToken> tokens = Tokenizer.tokenize("");
|
||||
|
||||
assertEquals(1, tokens.size());
|
||||
assertEquals(RawTokenType.EOF, tokens.get(0).type());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSimpleCommand() {
|
||||
List<RawToken> tokens = Tokenizer.tokenize("PING");
|
||||
assertEquals(2, tokens.size());
|
||||
assertEquals(RawTokenType.WORD, tokens.get(0).type());
|
||||
assertEquals("PING", tokens.get(0).value());
|
||||
assertEquals(RawTokenType.EOF, tokens.get(1).type());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSimpleCommandWithUnderscore() {
|
||||
List<RawToken> tokens = Tokenizer.tokenize("JOIN_GAME");
|
||||
assertEquals(2, tokens.size());
|
||||
assertEquals(RawTokenType.WORD, tokens.get(0).type());
|
||||
assertEquals("JOIN_GAME", tokens.get(0).value());
|
||||
assertEquals(RawTokenType.EOF, tokens.get(1).type());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testCommandWithOneParameter() {
|
||||
List<RawToken> tokens = Tokenizer.tokenize("GET VALUE=42");
|
||||
assertEquals(5, tokens.size());
|
||||
|
||||
assertEquals(RawTokenType.WORD, tokens.get(0).type());
|
||||
assertEquals("GET", tokens.get(0).value());
|
||||
|
||||
// Parameter
|
||||
assertEquals(RawTokenType.WORD, tokens.get(1).type());
|
||||
assertEquals("VALUE", tokens.get(1).value());
|
||||
|
||||
assertEquals(RawTokenType.SEPARATOR, tokens.get(2).type());
|
||||
assertEquals("=", tokens.get(2).value());
|
||||
|
||||
assertEquals(RawTokenType.WORD, tokens.get(3).type());
|
||||
assertEquals("42", tokens.get(3).value());
|
||||
|
||||
assertEquals(RawTokenType.EOF, tokens.get(4).type());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testCommandWithOneParameterWhitespacesBetweenSeperator() {
|
||||
List<RawToken> tokens = Tokenizer.tokenize("GET VALUE = 42");
|
||||
assertEquals(5, tokens.size());
|
||||
|
||||
assertEquals(RawTokenType.WORD, tokens.get(0).type());
|
||||
assertEquals("GET", tokens.get(0).value());
|
||||
|
||||
// Parameter
|
||||
assertEquals(RawTokenType.WORD, tokens.get(1).type());
|
||||
assertEquals("VALUE", tokens.get(1).value());
|
||||
|
||||
assertEquals(RawTokenType.SEPARATOR, tokens.get(2).type());
|
||||
assertEquals("=", tokens.get(2).value());
|
||||
|
||||
assertEquals(RawTokenType.WORD, tokens.get(3).type());
|
||||
assertEquals("42", tokens.get(3).value());
|
||||
|
||||
assertEquals(RawTokenType.EOF, tokens.get(4).type());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testCommandWithOneParameterAndNewline() {
|
||||
List<RawToken> tokens = Tokenizer.tokenize("GET\nVALUE=42");
|
||||
assertEquals(6, tokens.size());
|
||||
|
||||
assertEquals(RawTokenType.WORD, tokens.get(0).type());
|
||||
assertEquals("GET", tokens.get(0).value());
|
||||
|
||||
// Parameter
|
||||
assertEquals(RawTokenType.WORD, tokens.get(2).type());
|
||||
assertEquals("VALUE", tokens.get(2).value());
|
||||
|
||||
assertEquals(RawTokenType.SEPARATOR, tokens.get(3).type());
|
||||
assertEquals("=", tokens.get(3).value());
|
||||
|
||||
assertEquals(RawTokenType.WORD, tokens.get(4).type());
|
||||
assertEquals("42", tokens.get(4).value());
|
||||
|
||||
assertEquals(RawTokenType.EOF, tokens.get(5).type());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testCommandWithStringParameter() {
|
||||
List<RawToken> tokens = Tokenizer.tokenize("GREET MSG='Hello World'");
|
||||
assertEquals(5, tokens.size());
|
||||
|
||||
assertEquals(RawTokenType.WORD, tokens.get(0).type());
|
||||
assertEquals("GREET", tokens.get(0).value());
|
||||
|
||||
// First parameter
|
||||
assertEquals(RawTokenType.WORD, tokens.get(1).type());
|
||||
assertEquals("MSG", tokens.get(1).value());
|
||||
|
||||
assertEquals(RawTokenType.SEPARATOR, tokens.get(2).type());
|
||||
assertEquals("=", tokens.get(2).value());
|
||||
|
||||
assertEquals(RawTokenType.STRING, tokens.get(3).type());
|
||||
assertEquals("Hello World", tokens.get(3).value());
|
||||
|
||||
assertEquals(RawTokenType.EOF, tokens.get(4).type());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testCommandWithMultilineStringParameter() {
|
||||
List<RawToken> tokens = Tokenizer.tokenize("GREET MSG='Hello\nWorld'");
|
||||
assertEquals(5, tokens.size());
|
||||
|
||||
assertEquals(RawTokenType.WORD, tokens.get(0).type());
|
||||
assertEquals("GREET", tokens.get(0).value());
|
||||
|
||||
// First parameter
|
||||
assertEquals(RawTokenType.WORD, tokens.get(1).type());
|
||||
assertEquals("MSG", tokens.get(1).value());
|
||||
|
||||
assertEquals(RawTokenType.SEPARATOR, tokens.get(2).type());
|
||||
assertEquals("=", tokens.get(2).value());
|
||||
|
||||
assertEquals(RawTokenType.STRING, tokens.get(3).type());
|
||||
assertEquals("Hello\nWorld", tokens.get(3).value());
|
||||
|
||||
assertEquals(RawTokenType.EOF, tokens.get(4).type());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testWhitespace() {
|
||||
List<RawToken> tokens = Tokenizer.tokenize(" \t PING");
|
||||
assertEquals(2, tokens.size());
|
||||
assertEquals(RawTokenType.WORD, tokens.get(0).type());
|
||||
assertEquals("PING", tokens.get(0).value());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testStringWithEscapedQuote() {
|
||||
List<RawToken> tokens =
|
||||
Tokenizer.tokenize("WONDERFUL_GREETING MSG='it\\'s a wonderful day'");
|
||||
assertEquals(5, tokens.size());
|
||||
assertEquals(RawTokenType.STRING, tokens.get(3).type());
|
||||
assertEquals("it's a wonderful day", tokens.get(3).value());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testStringWithWronglyEscapedQuote() {
|
||||
TokenizerException ex =
|
||||
assertThrows(
|
||||
TokenizerException.class,
|
||||
() -> Tokenizer.tokenize("WONDERFUL_GREETING MSG='it\'s a wonderful day'"));
|
||||
assertEquals("Unterminated string literal", ex.getMessage());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testStringWithEscapedBackslash() {
|
||||
List<RawToken> tokens = Tokenizer.tokenize("EXECUTE_COMMAND CMD='\\whoami'");
|
||||
assertEquals(5, tokens.size());
|
||||
assertEquals(RawTokenType.STRING, tokens.get(3).type());
|
||||
assertEquals("\\whoami", tokens.get(3).value());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testColumnTracking() {
|
||||
List<RawToken> tokens = Tokenizer.tokenize("GET VAL=1");
|
||||
assertEquals(5, tokens.size());
|
||||
assertEquals(1, tokens.get(0).column());
|
||||
assertEquals(5, tokens.get(1).column());
|
||||
assertEquals(8, tokens.get(2).column());
|
||||
assertEquals(9, tokens.get(3).column());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testUnterminatedStringThrows() {
|
||||
TokenizerException ex =
|
||||
assertThrows(
|
||||
TokenizerException.class, () -> Tokenizer.tokenize("GREETING = 'unclosed"));
|
||||
assertEquals(1, ex.getLine());
|
||||
assertEquals(12, ex.getColumn());
|
||||
assertTrue(ex.getMessage().contains("Unterminated string literal"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testUnexpectedCharacterThrows() {
|
||||
TokenizerException ex =
|
||||
assertThrows(TokenizerException.class, () -> Tokenizer.tokenize("CMD @ KEY=VALUE"));
|
||||
assertTrue(ex.getMessage().contains("Unexpected character '@'"));
|
||||
assertEquals(1, ex.getLine());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testEmptyStringValue() {
|
||||
List<RawToken> tokens = Tokenizer.tokenize("cmd = ''");
|
||||
assertEquals(4, tokens.size());
|
||||
assertEquals(RawTokenType.STRING, tokens.get(2).type());
|
||||
assertEquals("", tokens.get(2).value());
|
||||
}
|
||||
}
|
||||
+115
@@ -0,0 +1,115 @@
|
||||
package ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.request.accessor;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
import ch.unibas.dmi.dbis.cs108.casono.server.network.command.parsing.RequestParameter;
|
||||
import java.util.List;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
public class RequestParameterAccessorTest {
|
||||
@Test
|
||||
void testRequiredParameterPresent() {
|
||||
List<RequestParameter> parameters = List.of(new RequestParameter("ARG1", "VAL1"));
|
||||
RequestParameterAccessor accessor = new RequestParameterAccessor(parameters);
|
||||
|
||||
assertEquals("VAL1", accessor.require("ARG1"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testMissingRequiredParameterThrows() {
|
||||
List<RequestParameter> parameters = List.of();
|
||||
RequestParameterAccessor accessor = new RequestParameterAccessor(parameters);
|
||||
|
||||
MissingParameterException ex =
|
||||
assertThrows(MissingParameterException.class, () -> accessor.require("ARG1"));
|
||||
assertEquals("Required parameter with key 'ARG1' is missing.", ex.getMessage());
|
||||
assertEquals("ARG1", ex.getParameterKey());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testRequiredParameterSuccessfulParsing() {
|
||||
List<RequestParameter> parameters = List.of(new RequestParameter("ARG1", "42"));
|
||||
RequestParameterAccessor accessor = new RequestParameterAccessor(parameters);
|
||||
|
||||
assertEquals(42, (int) accessor.require("ARG1", Integer::valueOf));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testRequiredParameterInvalidParsingThrows() {
|
||||
List<RequestParameter> parameters =
|
||||
List.of(new RequestParameter("ARG1", "The answer is: 42"));
|
||||
RequestParameterAccessor accessor = new RequestParameterAccessor(parameters);
|
||||
|
||||
ParameterParseException ex =
|
||||
assertThrows(
|
||||
ParameterParseException.class,
|
||||
() -> accessor.require("ARG1", Integer::valueOf));
|
||||
assertEquals("Error while parsing 'ARG1' with specified parser", ex.getMessage());
|
||||
assertEquals("ARG1", ex.getParameterKey());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testOptionalParameterPresent() {
|
||||
List<RequestParameter> parameters = List.of(new RequestParameter("ARG1", "VAL1"));
|
||||
RequestParameterAccessor accessor = new RequestParameterAccessor(parameters);
|
||||
|
||||
assertEquals("VAL1", accessor.optional("ARG1", "DEFAULT"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testOptionalParameterMissing() {
|
||||
List<RequestParameter> parameters = List.of();
|
||||
RequestParameterAccessor accessor = new RequestParameterAccessor(parameters);
|
||||
|
||||
assertEquals("DEFAULT", accessor.optional("ARG1", "DEFAULT"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testOptionalParameterSuccessfulParsing() {
|
||||
List<RequestParameter> parameters = List.of(new RequestParameter("ARG1", "42"));
|
||||
RequestParameterAccessor accessor = new RequestParameterAccessor(parameters);
|
||||
|
||||
assertEquals(42, (int) accessor.optional("ARG1", 7411, Integer::valueOf));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testMissingOptionalParameterSuccessfulParsing() {
|
||||
List<RequestParameter> parameters = List.of();
|
||||
RequestParameterAccessor accessor = new RequestParameterAccessor(parameters);
|
||||
|
||||
assertEquals(7411, (int) accessor.optional("ARG1", 7411, Integer::valueOf));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testOptionalParameterInvalidParsingThrows() {
|
||||
List<RequestParameter> parameters =
|
||||
List.of(new RequestParameter("ARG1", "The answer is: 42"));
|
||||
RequestParameterAccessor accessor = new RequestParameterAccessor(parameters);
|
||||
|
||||
ParameterParseException ex =
|
||||
assertThrows(
|
||||
ParameterParseException.class,
|
||||
() -> accessor.optional("ARG1", 7411, Integer::valueOf));
|
||||
assertEquals("Error while parsing 'ARG1' with specified parser", ex.getMessage());
|
||||
assertEquals("ARG1", ex.getParameterKey());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testContainsExistingKey() {
|
||||
List<RequestParameter> parameters = List.of(new RequestParameter("ARG1", "VAL1"));
|
||||
RequestParameterAccessor accessor = new RequestParameterAccessor(parameters);
|
||||
|
||||
assertTrue(accessor.containsKey("ARG1"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testContainsMissingKey() {
|
||||
List<RequestParameter> parameters = List.of();
|
||||
RequestParameterAccessor accessor = new RequestParameterAccessor(parameters);
|
||||
|
||||
assertFalse(accessor.containsKey("ARG1"));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user