diff --git a/README.md b/README.md index 95ec3e4fd..bce18962d 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,10 @@ # 웹 애플리케이션 서버 -## 진행 방법 -* 웹 애플리케이션 서버 요구사항을 파악한다. -* 요구사항에 대한 구현을 완료한 후 자신의 github 아이디에 해당하는 브랜치에 Pull Request(이하 PR)를 통해 코드 리뷰 요청을 한다. -* 코드 리뷰 피드백에 대한 개선 작업을 하고 다시 PUSH한다. -* 모든 피드백을 완료하면 다음 단계를 도전하고 앞의 과정을 반복한다. +## 요구사항 +- [x] http://localhost:8080/index.html 로 접속했을 때 webapp 디렉토리의 index.html 파일을 읽어 클라이언트에 응답한다. +- [x] “회원가입” 메뉴를 클릭하면 http://localhost:8080/user/form.html 으로 이동하면서 회원가입할 수 있다. +- [x] http://localhost:8080/user/form.html 파일의 form 태그 method를 get에서 post로 수정한 후 회원가입 기능이 정상적으로 동작하도록 구현한다. +- [x] “회원가입”을 완료하면 /index.html 페이지로 이동하고 싶다. 현재는 URL이 /user/create 로 유지되는 상태로 읽어서 전달할 파일이 없다. 따라서 redirect 방식처럼 회원가입을 완료한 후 “index.html”로 이동해야 한다. +- [x] 지금까지 구현한 소스 코드는 stylesheet 파일을 지원하지 못하고 있다. Stylesheet 파일을 지원하도록 구현하도록 한다. ## 우아한테크코스 코드리뷰 * [온라인 코드 리뷰 과정](https://github.com/woowacourse/woowacourse-docs/blob/master/maincourse/README.md) \ No newline at end of file diff --git a/src/main/java/exception/InvalidHttpRequestException.java b/src/main/java/exception/InvalidHttpRequestException.java new file mode 100644 index 000000000..c710c9c0d --- /dev/null +++ b/src/main/java/exception/InvalidHttpRequestException.java @@ -0,0 +1,13 @@ +package exception; + +public class InvalidHttpRequestException extends RuntimeException { + private static final String INVALID_FILE_PATH_MASSAGE = "Http Request의 값이 올바르지 않습니다"; + + public InvalidHttpRequestException() { + this(INVALID_FILE_PATH_MASSAGE); + } + + public InvalidHttpRequestException(String message) { + super(message); + } +} diff --git a/src/main/java/exception/InvalidRequestBodyException.java b/src/main/java/exception/InvalidRequestBodyException.java new file mode 100644 index 000000000..8c7f85f2c --- /dev/null +++ b/src/main/java/exception/InvalidRequestBodyException.java @@ -0,0 +1,13 @@ +package exception; + +public class InvalidRequestBodyException extends RuntimeException { + private static final String INVALID_PATH_MESSAGE = "Request Body의 값이 올바르지 않습니다"; + + public InvalidRequestBodyException() { + this(INVALID_PATH_MESSAGE); + } + + public InvalidRequestBodyException(String message) { + super(message); + } +} diff --git a/src/main/java/exception/InvalidRequestParamsException.java b/src/main/java/exception/InvalidRequestParamsException.java new file mode 100644 index 000000000..5b6aafdf5 --- /dev/null +++ b/src/main/java/exception/InvalidRequestParamsException.java @@ -0,0 +1,12 @@ +package exception; + +public class InvalidRequestParamsException extends RuntimeException { + private static final String INVALID_PARAMS_MESSAGE = "Request에 포함된 인자의 값이 올바르지 않습니다"; + public InvalidRequestParamsException() { + this(INVALID_PARAMS_MESSAGE); + } + + public InvalidRequestParamsException(String message) { + super(message); + } +} diff --git a/src/main/java/exception/InvalidRequestPathException.java b/src/main/java/exception/InvalidRequestPathException.java new file mode 100644 index 000000000..98547c6e4 --- /dev/null +++ b/src/main/java/exception/InvalidRequestPathException.java @@ -0,0 +1,13 @@ +package exception; + +public class InvalidRequestPathException extends RuntimeException { + private static final String INVALID_PATH_MESSAGE = "Request Path의 값이 올바르지 않습니다"; + + public InvalidRequestPathException() { + this(INVALID_PATH_MESSAGE); + } + + public InvalidRequestPathException(String message) { + super(message); + } +} diff --git a/src/main/java/exception/RequestParameterNotFoundException.java b/src/main/java/exception/RequestParameterNotFoundException.java new file mode 100644 index 000000000..d2b6532d1 --- /dev/null +++ b/src/main/java/exception/RequestParameterNotFoundException.java @@ -0,0 +1,13 @@ +package exception; + +public class RequestParameterNotFoundException extends RuntimeException { + private static final String NOT_FOUND_PARAMS_MESSAGE = "해당 값을 인자 목록에서 찾을 수 없습니다"; + + public RequestParameterNotFoundException() { + super(NOT_FOUND_PARAMS_MESSAGE); + } + + public RequestParameterNotFoundException(String key) { + super(key + " : " + NOT_FOUND_PARAMS_MESSAGE); + } +} diff --git a/src/main/java/model/User.java b/src/main/java/model/User.java index b7abb7304..6bd723906 100644 --- a/src/main/java/model/User.java +++ b/src/main/java/model/User.java @@ -1,5 +1,7 @@ package model; +import java.util.Map; + public class User { private String userId; private String password; @@ -13,6 +15,13 @@ public User(String userId, String password, String name, String email) { this.email = email; } + public User(Map parameters) { + this.userId = parameters.get("userId"); + this.password = parameters.get("password"); + this.name = parameters.get("name"); + this.email = parameters.get("email"); + } + public String getUserId() { return userId; } diff --git a/src/main/java/utils/URIUtils.java b/src/main/java/utils/URIUtils.java new file mode 100644 index 000000000..e4f88f093 --- /dev/null +++ b/src/main/java/utils/URIUtils.java @@ -0,0 +1,32 @@ +package utils; + +import exception.InvalidRequestPathException; +import web.FileType; + +import java.util.Objects; + +public class URIUtils { + + /** + * getFilePathInRequest는 전달받은 파일 경로가 유효한지 확인하고, 파일 경로를 생성하여 반환한다. + * + * @param path Request에 포함되어 전달되는 파일 경로 값으로, String 객체가 전달된다. + * @return 파일의 경로가 String 객체로 반환된다. + * @throws InvalidRequestPathException 유효하지 않은 경로가 전달될 시 발생한다. + */ + public static String getFilePath(String path) { + try { + Objects.requireNonNull(path); + String resourcePath = FileType.findFileType(path).getResourcePath(); + Objects.requireNonNull(resourcePath); + + if (path.isEmpty() || resourcePath.isEmpty()) { + throw new ArrayIndexOutOfBoundsException(); + } + + return resourcePath + path; + } catch (ArrayIndexOutOfBoundsException | NullPointerException e) { + throw new InvalidRequestPathException(); + } + } +} diff --git a/src/main/java/web/FileType.java b/src/main/java/web/FileType.java new file mode 100644 index 000000000..60638b7f1 --- /dev/null +++ b/src/main/java/web/FileType.java @@ -0,0 +1,45 @@ +package web; + +import exception.InvalidRequestPathException; + +import java.util.Arrays; + +public enum FileType { + HTML(".html", "./templates", "text/html"), + ICO(".ico", "./templates", "image/vnd.microsoft.icon"), + CSS(".css", "./static", "text/css"), + JS(".js", "./static", "text/javascript"), + WOFF(".woff", "./static", "text/woff"), + PNG(".png", "./static", "image/png"), + JPEG(".jpeg", "./static", "image/jpeg"), + SVG(".svg", "./static", "image/svg_xml"); + + private final String fileType; + private final String resourcePath; + private final String contentType; + + FileType(String fileType, String resourcePath, String contentType) { + this.fileType = fileType; + this.resourcePath = resourcePath; + this.contentType = contentType; + } + + public static FileType findFileType(String path) { + return Arrays.stream(FileType.values()) + .filter(fileType -> path.endsWith(fileType.getFileType())) + .findAny() + .orElseThrow(InvalidRequestPathException::new); + } + + public String getFileType() { + return fileType; + } + + public String getResourcePath() { + return resourcePath; + } + + public String getContentType() { + return contentType; + } +} diff --git a/src/main/java/web/request/HttpRequest.java b/src/main/java/web/request/HttpRequest.java new file mode 100644 index 000000000..61c5f4c8d --- /dev/null +++ b/src/main/java/web/request/HttpRequest.java @@ -0,0 +1,88 @@ +package web.request; + +import exception.InvalidHttpRequestException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import utils.IOUtils; + +import java.io.BufferedReader; +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; + +public class HttpRequest { + private static final Logger logger = LoggerFactory.getLogger(HttpRequest.class); + + private final String method; + private final RequestPath requestPath; + private final String version; + private final Map requestHeader; + private final RequestBody requestBody; + + public HttpRequest(BufferedReader request) { + try { + String requestHeaderFirstLine = request.readLine(); + logger.debug(requestHeaderFirstLine); + String[] tokens = requestHeaderFirstLine.split(" "); + method = tokens[0].trim(); + requestPath = new RequestPath(tokens[1].trim()); + version = tokens[2].trim(); + + requestHeader = mappingHeaders(request); + requestBody = mappingBodies(request); + } catch (IndexOutOfBoundsException | NullPointerException | IOException e) { + throw new InvalidHttpRequestException(); + } + } + + private Map mappingHeaders(BufferedReader request) throws IOException { + Map headers = new HashMap<>(); + + String line = request.readLine(); + while (!Objects.isNull(line) && !line.isEmpty()) { + logger.debug(line); + String[] splitLine = line.split(":"); + String key = splitLine[0].trim(); + String value = splitLine[1].trim(); + + headers.put(key, value); + line = request.readLine(); + } + return headers; + } + + private RequestBody mappingBodies(BufferedReader request) throws IOException { + if (!method.equals("POST")) { + return new RequestBody(); + } + int contentLength = Integer.parseInt(requestHeader.get("Content-Length")); + String requestBodyData = IOUtils.readData(request, contentLength); + if (Objects.isNull(requestBodyData) || requestBodyData.isEmpty()) { + return new RequestBody(); + } + return new RequestBody(requestBodyData); + } + + public String getMethod() { + return method; + } + + public RequestPath getRequestPath() { + return requestPath; + } + + public String getVersion() { + return version; + } + + public RequestBody getRequestBody() { + return requestBody; + } + + public String getContentType() { + String acceptInfo = requestHeader.get("Accept"); + + return acceptInfo.split(",")[0]; + } +} diff --git a/src/main/java/web/request/ParameterMapper.java b/src/main/java/web/request/ParameterMapper.java new file mode 100644 index 000000000..c462666bb --- /dev/null +++ b/src/main/java/web/request/ParameterMapper.java @@ -0,0 +1,65 @@ +package web.request; + +import exception.InvalidRequestParamsException; +import exception.RequestParameterNotFoundException; + +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; + +public abstract class ParameterMapper { + private final Map parameters; + + public ParameterMapper() { + this.parameters = new HashMap<>(); + } + + protected void mappingParameters(String params) { + validateParameters(params); + for (String parameter : params.split("&")) { + validateParameter(parameter); + String[] tokens = parameter.split("="); + String key = tokens[0]; + String value = tokens[1]; + + parameters.put(key, value); + } + } + + private void validateParameters(String params) { + if (Objects.isNull(params) || params.isEmpty() || !params.contains("=")) { + throw new InvalidRequestParamsException(); + } + } + + private void validateParameter(String parameter) { + if (parameter.isEmpty() || !parameter.contains("=") || parameter.equals("=")) { + throw new InvalidRequestParamsException(); + } + try { + String key = parameter.split("=")[0]; + String value = parameter.split("=")[1]; + Objects.requireNonNull(key); + Objects.requireNonNull(value); + if (key.isEmpty() || value.isEmpty()) { + throw new InvalidRequestParamsException(); + } + } catch (NullPointerException | ArrayIndexOutOfBoundsException e) { + throw new InvalidRequestParamsException(); + } + } + + public Map getParameters() { + return parameters; + } + + public String getParameterByKey(String key) { + if (Objects.isNull(key) || key.isEmpty()) { + throw new RequestParameterNotFoundException(); + } + if (!parameters.containsKey(key)) { + throw new RequestParameterNotFoundException(key); + } + return parameters.get(key); + } +} diff --git a/src/main/java/web/request/RequestBody.java b/src/main/java/web/request/RequestBody.java new file mode 100644 index 000000000..29d8ebce7 --- /dev/null +++ b/src/main/java/web/request/RequestBody.java @@ -0,0 +1,22 @@ +package web.request; + +import exception.InvalidRequestBodyException; + +import java.util.Objects; + +public class RequestBody extends ParameterMapper { + public RequestBody() { + super(); + } + + public RequestBody(String requestBody) { + validateBody(requestBody); + mappingParameters(requestBody); + } + + private void validateBody(String requestBody) { + if (Objects.isNull(requestBody) || requestBody.isEmpty()) { + throw new InvalidRequestBodyException(); + } + } +} diff --git a/src/main/java/web/request/RequestPath.java b/src/main/java/web/request/RequestPath.java new file mode 100644 index 000000000..644af4b73 --- /dev/null +++ b/src/main/java/web/request/RequestPath.java @@ -0,0 +1,27 @@ +package web.request; + +import exception.InvalidRequestPathException; + +import java.util.Objects; + +public class RequestPath extends ParameterMapper { + private final String target; + + public RequestPath(String path) { + validatePath(path); + target = path.split("\\?")[0]; + if (path.contains("?")) { + mappingParameters(path.split("\\?")[1]); + } + } + + private void validatePath(String path) { + if (Objects.isNull(path) || path.isEmpty()) { + throw new InvalidRequestPathException(); + } + } + + public String getTarget() { + return target; + } +} diff --git a/src/main/java/webserver/RequestHandler.java b/src/main/java/webserver/RequestHandler.java index c7c0134ae..1a6b1363d 100644 --- a/src/main/java/webserver/RequestHandler.java +++ b/src/main/java/webserver/RequestHandler.java @@ -1,13 +1,18 @@ package webserver; -import java.io.DataOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.net.Socket; - +import db.DataBase; +import model.User; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import utils.FileIoUtils; +import utils.URIUtils; +import web.request.HttpRequest; +import web.request.RequestBody; +import web.request.RequestPath; + +import java.io.*; +import java.net.Socket; +import java.net.URISyntaxException; public class RequestHandler implements Runnable { private static final Logger logger = LoggerFactory.getLogger(RequestHandler.class); @@ -21,22 +26,41 @@ public RequestHandler(Socket connectionSocket) { public void run() { logger.debug("New Client Connect! Connected IP : {}, Port : {}", connection.getInetAddress(), connection.getPort()); - try (InputStream in = connection.getInputStream(); OutputStream out = connection.getOutputStream()) { - // TODO 사용자 요청에 대한 처리는 이 곳에 구현하면 된다. + BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(in, "UTF-8")); + + HttpRequest httpRequest = new HttpRequest(bufferedReader); + + RequestPath requestPath = httpRequest.getRequestPath(); + String requestTarget = requestPath.getTarget(); DataOutputStream dos = new DataOutputStream(out); - byte[] body = "Hello World".getBytes(); - response200Header(dos, body.length); + + if (requestTarget.equals("/user/create")) { + RequestBody requestBody = httpRequest.getRequestBody(); + User user = new User(requestBody.getParameters()); + DataBase.addUser(user); + logger.debug("New User created! -> {}", user); + + response302Header(dos, "/index.html"); + + byte[] body = user.toString().getBytes(); + responseBody(dos, body); + + return; + } + String filePath = URIUtils.getFilePath(requestTarget); + byte[] body = FileIoUtils.loadFileFromClasspath(filePath); + response200Header(dos, httpRequest.getContentType(), body.length); responseBody(dos, body); - } catch (IOException e) { + } catch (IOException | URISyntaxException e) { logger.error(e.getMessage()); } } - private void response200Header(DataOutputStream dos, int lengthOfBodyContent) { + private void response200Header(DataOutputStream dos, String contentType, int lengthOfBodyContent) { try { dos.writeBytes("HTTP/1.1 200 OK \r\n"); - dos.writeBytes("Content-Type: text/html;charset=utf-8\r\n"); + dos.writeBytes("Content-Type: " + contentType + ";charset=utf-8\r\n"); dos.writeBytes("Content-Length: " + lengthOfBodyContent + "\r\n"); dos.writeBytes("\r\n"); } catch (IOException e) { @@ -44,6 +68,16 @@ private void response200Header(DataOutputStream dos, int lengthOfBodyContent) { } } + private void response302Header(DataOutputStream dos, String relocateURI) { + try { + dos.writeBytes("HTTP/1.1 302 FOUND \r\n"); + dos.writeBytes("Location: " + relocateURI + " \r\n"); + dos.writeBytes("\r\n"); + } catch (IOException e) { + logger.error(e.getMessage()); + } + } + private void responseBody(DataOutputStream dos, byte[] body) { try { dos.write(body, 0, body.length); diff --git a/src/main/java/webserver/WebServer.java b/src/main/java/webserver/WebServer.java index eba161289..88cce46b5 100644 --- a/src/main/java/webserver/WebServer.java +++ b/src/main/java/webserver/WebServer.java @@ -1,11 +1,11 @@ package webserver; -import java.net.ServerSocket; -import java.net.Socket; - import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.net.ServerSocket; +import java.net.Socket; + public class WebServer { private static final Logger logger = LoggerFactory.getLogger(WebServer.class); private static final int DEFAULT_PORT = 8080; diff --git a/src/main/resources/templates/user/form.html b/src/main/resources/templates/user/form.html index 96fe1bd3a..f7a3b5612 100644 --- a/src/main/resources/templates/user/form.html +++ b/src/main/resources/templates/user/form.html @@ -75,7 +75,7 @@
-
+
diff --git a/src/test/java/utils/URIUtilsTest.java b/src/test/java/utils/URIUtilsTest.java new file mode 100644 index 000000000..21fc421fd --- /dev/null +++ b/src/test/java/utils/URIUtilsTest.java @@ -0,0 +1,36 @@ +package utils; + +import exception.InvalidRequestPathException; +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; +import org.junit.jupiter.params.provider.NullAndEmptySource; +import org.junit.jupiter.params.provider.ValueSource; + +public class URIUtilsTest { + @ParameterizedTest + @CsvSource(value = {"/index.html,./templates/index.html", "/style.css,./static/style.css"}) + @DisplayName("유효한 파일 경로를 전달받을 시, 이를 가공하여 반환한다.") + void getFilePathTest(String path, String expected) { + Assertions.assertThat(URIUtils.getFilePath(path)).isEqualTo(expected); + } + + @ParameterizedTest + @NullAndEmptySource + @DisplayName("유효하지 않은, 비어있는 파일 경로를 전달받을 시, 예외를 발생시킨다.") + void getFilePathNullAndEmptyExceptionTest(String invalidPath) { + Assertions.assertThatThrownBy(() -> URIUtils.getFilePath(invalidPath)) + .isInstanceOf(InvalidRequestPathException.class) + .hasMessage("Request Path의 값이 올바르지 않습니다"); + } + + @ParameterizedTest + @ValueSource(strings = {"123", ".", "file.", ".twice"}) + @DisplayName("유효하지 않은경로를 전달받을 시, 예외를 발생시킨다.") + void getFilePathInvalidExceptionTest(String invalidPath) { + Assertions.assertThatThrownBy(() -> URIUtils.getFilePath(invalidPath)) + .isInstanceOf(InvalidRequestPathException.class) + .hasMessage("Request Path의 값이 올바르지 않습니다"); + } +} diff --git a/src/test/java/web/HttpRequestTest.java b/src/test/java/web/HttpRequestTest.java new file mode 100644 index 000000000..b72ef9cb7 --- /dev/null +++ b/src/test/java/web/HttpRequestTest.java @@ -0,0 +1,72 @@ +package web; + +import exception.InvalidHttpRequestException; +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import web.request.HttpRequest; + +import java.io.*; + +public class HttpRequestTest { + private static final Logger logger = LoggerFactory.getLogger(HttpRequestTest.class); + private InputStream inputStream; + + @BeforeEach + void setup() { + StringBuilder stringBuilder = new StringBuilder(); + stringBuilder.append("GET /index.html HTTP/1.1\n"); + stringBuilder.append("Host: localhost:8080\n"); + stringBuilder.append("Connection: keep-alive\n"); + stringBuilder.append("Accept: */*\n\n"); + inputStream = new ByteArrayInputStream(stringBuilder.toString().getBytes()); + } + + @Test + @DisplayName("HttpRequest 객체가 올바르게 생성된다") + void createHttpRequestTest() { + try { + BufferedReader request = new BufferedReader(new InputStreamReader(inputStream, "UTF-8")); + HttpRequest httpRequest = new HttpRequest(request); + Assertions.assertThat(httpRequest.getMethod()).isEqualTo("GET"); + Assertions.assertThat(httpRequest.getRequestPath().getTarget()).isEqualTo("/index.html"); + Assertions.assertThat(httpRequest.getVersion()).isEqualTo("HTTP/1.1"); + } catch (IOException e) { + logger.error(e.getMessage()); + throw new AssertionError(); + } + } + + @Test + @DisplayName("예외 테스트: HttpRequest 생성 중 잘못된 값이 전달되면, 예외가 발생한다") + void createHttpRequestExceptionTest() { + String invalidRequest = "Es-Muss-Sein!"; + inputStream = new ByteArrayInputStream(invalidRequest.getBytes()); + try { + BufferedReader request = new BufferedReader(new InputStreamReader(inputStream, "UTF-8")); + Assertions.assertThatThrownBy(() -> new HttpRequest(request)) + .isInstanceOf(InvalidHttpRequestException.class) + .hasMessage("Http Request의 값이 올바르지 않습니다"); + } catch (IOException e) { + logger.error(e.getMessage()); + throw new AssertionError(); + } + } + + @Test + @DisplayName("HttpRequest에 contentType 정보를 요청하면, 올바른 값을 반환한다") + void getContentTypeTest() { + try { + BufferedReader request = new BufferedReader(new InputStreamReader(inputStream, "UTF-8")); + HttpRequest httpRequest = new HttpRequest(request); + + Assertions.assertThat(httpRequest.getContentType()).isEqualTo("*/*"); + } catch (IOException e) { + logger.error(e.getMessage()); + throw new AssertionError(); + } + } +} diff --git a/src/test/java/web/ParameterMapperTest.java b/src/test/java/web/ParameterMapperTest.java new file mode 100644 index 000000000..1991c2bc8 --- /dev/null +++ b/src/test/java/web/ParameterMapperTest.java @@ -0,0 +1,110 @@ +package web; + +import exception.InvalidRequestParamsException; +import exception.RequestParameterNotFoundException; +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; +import org.junit.jupiter.params.provider.NullAndEmptySource; +import org.junit.jupiter.params.provider.ValueSource; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import web.request.ParameterMapper; + +public class ParameterMapperTest { + + private static final Logger logger = LoggerFactory.getLogger(ParameterMapperTest.class); + class ParamsMapper extends ParameterMapper { + public void setParameters(String value) { + mappingParameters(value); + } + } + + @Test + @DisplayName("ParameterMapper를 상속한 클래스에 생성 요청 시 올바르게 생성된다.") + void createParameterMapperTest() { + try { + ParamsMapper paramsMapper = new ParamsMapper(); + Assertions.assertThat(paramsMapper).isNotNull(); + } catch (Exception e) { + logger.error(e.getMessage()); + throw new AssertionError(); + } + } + + @ParameterizedTest + @CsvSource(value={"poo, bar", "test, success", "cookie, monster"}) + @DisplayName("ParameterMapper의 mappingParameters에 올바른 값을 넣으면 정상적으로 수행된다.") + void mappingParametersTest(String key, String value) { + ParamsMapper paramsMapper = new ParamsMapper(); + String params = key+"="+value; + + paramsMapper.setParameters(params); + + Assertions.assertThat(paramsMapper.getParameterByKey(key)).isEqualTo(value); + } + + @ParameterizedTest + @ValueSource(strings={"poobar","=poo","=", "hey="}) + @DisplayName("예외 테스트: ParameterMapper의 mappingParameters에 잘못된 값을 넣으면 예외가 발생한다.") + void mappingParametersInvalidExceptionTest(String value) { + ParamsMapper paramsMapper = new ParamsMapper(); + + Assertions.assertThatThrownBy(() -> paramsMapper.setParameters(value)) + .isInstanceOf(InvalidRequestParamsException.class) + .hasMessage("Request에 포함된 인자의 값이 올바르지 않습니다"); + } + + @ParameterizedTest + @NullAndEmptySource + @DisplayName("예외 테스트: ParameterMapper의 mappingParameters에 비어있는 값을 넣으면 예외가 발생한다.") + void mappingParametersNullAndEmptyExceptionTest(String value) { + ParamsMapper paramsMapper = new ParamsMapper(); + + Assertions.assertThatThrownBy(() -> paramsMapper.setParameters(value)) + .isInstanceOf(InvalidRequestParamsException.class) + .hasMessage("Request에 포함된 인자의 값이 올바르지 않습니다"); + } + + @Test + @DisplayName("getParameterByKey를 호출 시, 올바른 값을 반환한다.") + void getParametersByKeyTest() { + ParamsMapper paramsMapper = new ParamsMapper(); + String params = "poo=bar&key=value&TWICE=good"; + + paramsMapper.setParameters(params); + Assertions.assertThat(paramsMapper.getParameterByKey("poo")).isEqualTo("bar"); + Assertions.assertThat(paramsMapper.getParameterByKey("key")).isEqualTo("value"); + Assertions.assertThat(paramsMapper.getParameterByKey("TWICE")).isEqualTo("good"); + } + + @ParameterizedTest + @NullAndEmptySource + @DisplayName("예외 테스트 : getParameterByKey를 호출 시, 빈 값을 전달하면 예외가 발생한다.") + void getParametersByKeyNullAndEmptyExceptionTest(String value) { + ParamsMapper paramsMapper = new ParamsMapper(); + String params = "poo=bar&key=value&TWICE=good"; + + paramsMapper.setParameters(params); + + Assertions.assertThatThrownBy(() -> paramsMapper.getParameterByKey(value)) + .isInstanceOf(RequestParameterNotFoundException.class) + .hasMessage("해당 값을 인자 목록에서 찾을 수 없습니다"); + } + + @ParameterizedTest + @ValueSource(strings = {"bar", "123", "OHMYGIRL"}) + @DisplayName("예외 테스트 : getParameterByKey를 호출 시, 빈 값을 전달하면 예외가 발생한다.") + void getParametersByKeyInvalidExceptionTest(String value) { + ParamsMapper paramsMapper = new ParamsMapper(); + String params = "poo=bar&key=value&TWICE=good"; + + paramsMapper.setParameters(params); + + Assertions.assertThatThrownBy(() -> paramsMapper.getParameterByKey(value)) + .isInstanceOf(RequestParameterNotFoundException.class) + .hasMessage(value + " : 해당 값을 인자 목록에서 찾을 수 없습니다"); + } +} diff --git a/src/test/java/web/RequestBodyTest.java b/src/test/java/web/RequestBodyTest.java new file mode 100644 index 000000000..744e6850e --- /dev/null +++ b/src/test/java/web/RequestBodyTest.java @@ -0,0 +1,32 @@ +package web; + +import exception.InvalidRequestBodyException; +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.NullAndEmptySource; +import web.request.RequestBody; + +public class RequestBodyTest { + @Test + @DisplayName("RequestBody 객체가 올바르게 생성된다") + void createRequestBodyTest() { + String parameters = "poo=bar&test=success"; + + RequestBody requestBody = new RequestBody(parameters); + + Assertions.assertThat(requestBody.getParameterByKey("poo")).isEqualTo("bar"); + Assertions.assertThat(requestBody.getParameterByKey("test")).isEqualTo("success"); + + } + + @ParameterizedTest + @NullAndEmptySource + @DisplayName("예외 테스트: RequestBody 생성 중 잘못된 값이 전달되면, 예외를 발생시킨다.") + void createHttpRequestExceptionTest(String invalidValue) { + Assertions.assertThatThrownBy(() -> new RequestBody(invalidValue)) + .isInstanceOf(InvalidRequestBodyException.class) + .hasMessage("Request Body의 값이 올바르지 않습니다"); + } +} diff --git a/src/test/java/web/RequestPathTest.java b/src/test/java/web/RequestPathTest.java new file mode 100644 index 000000000..3e8a15686 --- /dev/null +++ b/src/test/java/web/RequestPathTest.java @@ -0,0 +1,55 @@ +package web; + +import exception.InvalidRequestParamsException; +import exception.InvalidRequestPathException; +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.NullAndEmptySource; +import web.request.RequestPath; + +public class RequestPathTest { + @Test + @DisplayName("RequestPath 객체가 올바르게 생성된다") + void createRequestPathTest() { + String path = "/index.html"; + + RequestPath requestPath = new RequestPath(path); + + Assertions.assertThat(requestPath.getTarget()).isEqualTo(path); + Assertions.assertThat(requestPath.getParameters()).isEmpty(); + } + + @ParameterizedTest + @NullAndEmptySource + @DisplayName("예외 테스트: RequestPath 생성 중 잘못된 경로가 전달되면, 예외를 발생시킨다.") + void createHttpRequestExceptionTest(String invalidRequest) { + Assertions.assertThatThrownBy(() -> new RequestPath(invalidRequest)) + .isInstanceOf(InvalidRequestPathException.class) + .hasMessage("Request Path의 값이 올바르지 않습니다"); + } + + @Test + @DisplayName("RequestPath 객체에 파라미터가 포함된 경로를 보내면, 올바르게 생성된다.") + void createRequestPathWithParamsTest() { + String path = "/index.html?poo=bar&test=success"; + + RequestPath requestPath = new RequestPath(path); + + Assertions.assertThat(requestPath.getTarget()).isEqualTo("/index.html"); + + Assertions.assertThat(requestPath.getParameterByKey("poo")).isEqualTo("bar"); + Assertions.assertThat(requestPath.getParameterByKey("test")).isEqualTo("success"); + } + + @Test + @DisplayName("예외 테스트: RequestPath 생성 중 잘못된 파라미터가 전달되면, 예외를 발생시킨다.") + void createHttpRequestWithParmasExceptionTest() { + String invalidRequest = "/index.html?=123"; + + Assertions.assertThatThrownBy(() -> new RequestPath(invalidRequest)) + .isInstanceOf(InvalidRequestParamsException.class) + .hasMessage("Request에 포함된 인자의 값이 올바르지 않습니다"); + } +}