From cd77c2d020859d318efc47645cda78b8f3a4b4c8 Mon Sep 17 00:00:00 2001 From: java-saeng Date: Thu, 13 Apr 2023 11:54:11 +0900 Subject: [PATCH 01/36] =?UTF-8?q?refactor=20:=20schema=20=EC=8A=A4?= =?UTF-8?q?=ED=8E=99=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/racingcar/service/RaceResultService.java | 1 - src/main/resources/schema.sql | 6 +++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/main/java/racingcar/service/RaceResultService.java b/src/main/java/racingcar/service/RaceResultService.java index 7b79dc0f6..7814cdfb5 100644 --- a/src/main/java/racingcar/service/RaceResultService.java +++ b/src/main/java/racingcar/service/RaceResultService.java @@ -15,7 +15,6 @@ @Service public class RaceResultService { - private final RaceResultDao raceResultDao; private final CarService carService; private final NumberGenerator numberGenerator; diff --git a/src/main/resources/schema.sql b/src/main/resources/schema.sql index 0cdb1dad1..1220c2f88 100644 --- a/src/main/resources/schema.sql +++ b/src/main/resources/schema.sql @@ -1,6 +1,6 @@ CREATE TABLE RACE_RESULT ( - id INT NOT NULL AUTO_INCREMENT, + id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, trial_count INT NOT NULL, winners VARCHAR(255) NOT NULL, created_at DATETIME NOT NULL, @@ -9,9 +9,9 @@ CREATE TABLE RACE_RESULT CREATE TABLE CAR ( - id INT NOT NULL AUTO_INCREMENT, + id BIGINT NOT NULL AUTO_INCREMENT, name VARCHAR(50) NOT NULL, position INT NOT NULL, - play_result_id INT NOT NULL, + play_result_id BIGINT NOT NULL, PRIMARY KEY (id) ); From 72fab1501034352e0a7dc0b87ab73b70c354ffd6 Mon Sep 17 00:00:00 2001 From: java-saeng Date: Thu, 13 Apr 2023 13:04:26 +0900 Subject: [PATCH 02/36] =?UTF-8?q?feat=20:=20bean=20validation=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 2 ++ .../controller/RacingCarWebController.java | 11 ++------ .../controller/dto/GameInfoRequest.java | 6 ++++ .../validator/GameOptionValidator.java | 28 ------------------- 4 files changed, 11 insertions(+), 36 deletions(-) delete mode 100644 src/main/java/racingcar/controller/validator/GameOptionValidator.java diff --git a/build.gradle b/build.gradle index 721b6af13..dac60ff00 100644 --- a/build.gradle +++ b/build.gradle @@ -16,6 +16,8 @@ dependencies { implementation 'org.springframework.boot:spring-boot-starter-jdbc' runtimeOnly 'com.h2database:h2' + + implementation 'org.springframework.boot:spring-boot-starter-validation' } tasks.named('test') { diff --git a/src/main/java/racingcar/controller/RacingCarWebController.java b/src/main/java/racingcar/controller/RacingCarWebController.java index 80d30e20b..f7160a16b 100644 --- a/src/main/java/racingcar/controller/RacingCarWebController.java +++ b/src/main/java/racingcar/controller/RacingCarWebController.java @@ -1,5 +1,6 @@ package racingcar.controller; +import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RestController; @@ -11,20 +12,14 @@ @RestController public class RacingCarWebController { - private final GameOptionValidator gameOptionValidator; private final RaceResultService raceResultService; - public RacingCarWebController(final GameOptionValidator gameOptionValidator, - final RaceResultService raceResultService) { - this.gameOptionValidator = gameOptionValidator; + public RacingCarWebController(final RaceResultService raceResultService) { this.raceResultService = raceResultService; } @PostMapping("/plays") - public RaceResultResponse registerRaceResult(@RequestBody final GameInfoRequest gameInfoRequest) { - - gameOptionValidator.validateGameOption(gameInfoRequest); - + public RaceResultResponse registerRaceResult(@Validated @RequestBody final GameInfoRequest gameInfoRequest) { return raceResultService.createRaceResult(gameInfoRequest); } } diff --git a/src/main/java/racingcar/controller/dto/GameInfoRequest.java b/src/main/java/racingcar/controller/dto/GameInfoRequest.java index 3857c43c9..77ce449a7 100644 --- a/src/main/java/racingcar/controller/dto/GameInfoRequest.java +++ b/src/main/java/racingcar/controller/dto/GameInfoRequest.java @@ -1,8 +1,14 @@ package racingcar.controller.dto; +import javax.validation.constraints.Positive; +import javax.validation.constraints.Size; + public class GameInfoRequest { + @Size(min = 2) private String names; + + @Positive private int count; private GameInfoRequest() { diff --git a/src/main/java/racingcar/controller/validator/GameOptionValidator.java b/src/main/java/racingcar/controller/validator/GameOptionValidator.java deleted file mode 100644 index d33e04333..000000000 --- a/src/main/java/racingcar/controller/validator/GameOptionValidator.java +++ /dev/null @@ -1,28 +0,0 @@ -package racingcar.controller.validator; - -import org.springframework.stereotype.Component; -import racingcar.controller.dto.GameInfoRequest; - -@Component -public class GameOptionValidator { - - private static final String CAR_NAMES_BLANK_ERROR = "[ERROR] 경주할 자동차 이름이 입력되지 않았습니다."; - private static final String TRY_NUM_NOT_POSITIVE_ERROR = "[ERROR] 시도할 횟수는 1 이상이어야 합니다."; - - public void validateGameOption(final GameInfoRequest gameInfoRequest) { - validateNames(gameInfoRequest.getNames()); - validateTrialCount(gameInfoRequest.getCount()); - } - - private void validateNames(String names) { - if (names.length() < 1) { - throw new IllegalArgumentException(CAR_NAMES_BLANK_ERROR); - } - } - - private void validateTrialCount(int trialCount) { - if (trialCount <= 0) { - throw new IllegalArgumentException(TRY_NUM_NOT_POSITIVE_ERROR); - } - } -} From c40b2a1eb5e1962b217ed4c56a5afcd862530919 Mon Sep 17 00:00:00 2001 From: java-saeng Date: Thu, 13 Apr 2023 13:13:23 +0900 Subject: [PATCH 03/36] =?UTF-8?q?refactor=20:=20column=20=EB=AA=85=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/racingcar/controller/RacingCarWebController.java | 1 - src/main/java/racingcar/dao/car/CarDao.java | 2 +- src/main/resources/schema.sql | 2 +- 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/main/java/racingcar/controller/RacingCarWebController.java b/src/main/java/racingcar/controller/RacingCarWebController.java index f7160a16b..51352239f 100644 --- a/src/main/java/racingcar/controller/RacingCarWebController.java +++ b/src/main/java/racingcar/controller/RacingCarWebController.java @@ -6,7 +6,6 @@ import org.springframework.web.bind.annotation.RestController; import racingcar.controller.dto.GameInfoRequest; import racingcar.controller.dto.RaceResultResponse; -import racingcar.controller.validator.GameOptionValidator; import racingcar.service.RaceResultService; @RestController diff --git a/src/main/java/racingcar/dao/car/CarDao.java b/src/main/java/racingcar/dao/car/CarDao.java index 9b12990fe..7b002c01b 100644 --- a/src/main/java/racingcar/dao/car/CarDao.java +++ b/src/main/java/racingcar/dao/car/CarDao.java @@ -24,7 +24,7 @@ public void save(final CarRegisterRequest carRegisterRequest) { final SqlParameterSource params = new MapSqlParameterSource() .addValue("name", carRegisterRequest.getName()) .addValue("position", carRegisterRequest.getPosition()) - .addValue("play_result_id", carRegisterRequest.getPlayResultId()); + .addValue("race_result_id", carRegisterRequest.getPlayResultId()); jdbcInsert.execute(params); } diff --git a/src/main/resources/schema.sql b/src/main/resources/schema.sql index 1220c2f88..4d8ca10c1 100644 --- a/src/main/resources/schema.sql +++ b/src/main/resources/schema.sql @@ -12,6 +12,6 @@ CREATE TABLE CAR id BIGINT NOT NULL AUTO_INCREMENT, name VARCHAR(50) NOT NULL, position INT NOT NULL, - play_result_id BIGINT NOT NULL, + race_result_id BIGINT NOT NULL, PRIMARY KEY (id) ); From 585644b8e8196e9e5a73cedc9729f65bdece67cf Mon Sep 17 00:00:00 2001 From: java-saeng Date: Thu, 13 Apr 2023 16:49:04 +0900 Subject: [PATCH 04/36] =?UTF-8?q?feat=20:=20=EA=B2=8C=EC=9E=84=20=EC=9D=B4?= =?UTF-8?q?=EB=A0=A5=20=EC=A1=B0=ED=9A=8C=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/RacingCarWebController.java | 8 +++ .../dao/raceresult/RaceResultDao.java | 52 +++++++++++++++++++ .../racingcar/service/RaceResultService.java | 14 +++++ .../service/mapper/RaceResultMapper.java | 7 +++ 4 files changed, 81 insertions(+) diff --git a/src/main/java/racingcar/controller/RacingCarWebController.java b/src/main/java/racingcar/controller/RacingCarWebController.java index 51352239f..c1ec6980d 100644 --- a/src/main/java/racingcar/controller/RacingCarWebController.java +++ b/src/main/java/racingcar/controller/RacingCarWebController.java @@ -1,6 +1,7 @@ package racingcar.controller; import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RestController; @@ -8,6 +9,8 @@ import racingcar.controller.dto.RaceResultResponse; import racingcar.service.RaceResultService; +import java.util.List; + @RestController public class RacingCarWebController { @@ -21,4 +24,9 @@ public RacingCarWebController(final RaceResultService raceResultService) { public RaceResultResponse registerRaceResult(@Validated @RequestBody final GameInfoRequest gameInfoRequest) { return raceResultService.createRaceResult(gameInfoRequest); } + + @GetMapping("/plays") + public List showRaceResult() { + return raceResultService.searchRaceResult(); + } } diff --git a/src/main/java/racingcar/dao/raceresult/RaceResultDao.java b/src/main/java/racingcar/dao/raceresult/RaceResultDao.java index cd8256089..f8260a88b 100644 --- a/src/main/java/racingcar/dao/raceresult/RaceResultDao.java +++ b/src/main/java/racingcar/dao/raceresult/RaceResultDao.java @@ -1,13 +1,21 @@ package racingcar.dao.raceresult; import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.jdbc.core.RowMapper; import org.springframework.jdbc.core.namedparam.MapSqlParameterSource; import org.springframework.jdbc.core.namedparam.SqlParameterSource; import org.springframework.jdbc.core.simple.SimpleJdbcInsert; import org.springframework.stereotype.Repository; import racingcar.dao.raceresult.dto.RaceResultRegisterRequest; +import racingcar.domain.Car; +import java.sql.ResultSet; +import java.sql.SQLException; import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; @Repository public class RaceResultDao { @@ -31,4 +39,48 @@ public int save(final RaceResultRegisterRequest raceResultRegisterRequest) { return jdbcInsert.executeAndReturnKey(params).intValue(); } + + public Map> findAllRaceResult() { + + final String sql = + "select r.winners, c.name, c.position" + + " from CAR c" + + " inner join RACE_RESULT r" + + " on r.id = c.race_result_id;"; + + RowMapper>> raceResultRowMapper = (resultSet, rowNum) -> { + Map> raceResult = new HashMap<>(); + + while (hasNext(resultSet)) { + convertResultSetToRaceResult(resultSet, raceResult); + resultSet.next(); + } + + return raceResult; + }; + + return jdbcTemplate.queryForObject(sql, raceResultRowMapper); + } + + private boolean hasNext(final ResultSet resultSet) throws SQLException { + return !resultSet.isAfterLast(); + } + + private void convertResultSetToRaceResult(final ResultSet resultSet, final Map> raceResult) { + try { + final String winners = resultSet.getString("winners"); + final String name = resultSet.getString("name"); + final int position = resultSet.getInt("position"); + + if (!raceResult.containsKey(winners)) { + raceResult.put(winners, new ArrayList<>()); + } + + raceResult.get(winners) + .add(new Car(name, position)); + + } catch (SQLException exception) { + throw new IllegalStateException("DB 조회 오류"); + } + } } diff --git a/src/main/java/racingcar/service/RaceResultService.java b/src/main/java/racingcar/service/RaceResultService.java index 7814cdfb5..b6aaec65e 100644 --- a/src/main/java/racingcar/service/RaceResultService.java +++ b/src/main/java/racingcar/service/RaceResultService.java @@ -6,11 +6,14 @@ import racingcar.controller.dto.RaceResultResponse; import racingcar.dao.raceresult.RaceResultDao; import racingcar.dao.raceresult.dto.RaceResultRegisterRequest; +import racingcar.domain.Car; import racingcar.domain.RacingCars; import racingcar.service.mapper.RaceResultMapper; import racingcar.util.NumberGenerator; import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; @Service public class RaceResultService { @@ -48,4 +51,15 @@ public RaceResultResponse createRaceResult(final GameInfoRequest gameInfoRequest return new RaceResultResponse(raceResultRegisterRequest.getWinners(), carStatusResponses); } + + public List searchRaceResult() { + + final Map> allRaceResult = raceResultDao.findAllRaceResult(); + + return allRaceResult.entrySet() + .stream() + .map(it -> new RaceResultResponse(it.getKey(), + raceResultMapper.mapToCarStatus(it.getValue()))) + .collect(Collectors.toList()); + } } diff --git a/src/main/java/racingcar/service/mapper/RaceResultMapper.java b/src/main/java/racingcar/service/mapper/RaceResultMapper.java index 5147f6bbc..6de6e6340 100644 --- a/src/main/java/racingcar/service/mapper/RaceResultMapper.java +++ b/src/main/java/racingcar/service/mapper/RaceResultMapper.java @@ -33,4 +33,11 @@ public List mapToCarStatus(final RacingCars racingCars) { car.getPosition())) .collect(Collectors.toList()); } + + public List mapToCarStatus(final List cars) { + return cars.stream() + .map(car -> new CarStatusResponse(car.getName(), + car.getPosition())) + .collect(Collectors.toList()); + } } From 2f1278b89af9c38e5fe5acfe813e70d7a8d06665 Mon Sep 17 00:00:00 2001 From: java-saeng Date: Thu, 13 Apr 2023 16:49:24 +0900 Subject: [PATCH 05/36] =?UTF-8?q?feat=20:=20mock=20data=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80=20=EB=B0=8F=20data=20=EC=B4=88=EA=B8=B0=ED=99=94=20pa?= =?UTF-8?q?th=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/resources/application.yml | 4 ++++ src/main/resources/sql/data.sql | 17 +++++++++++++++++ src/main/resources/{ => sql}/schema.sql | 0 3 files changed, 21 insertions(+) create mode 100644 src/main/resources/sql/data.sql rename src/main/resources/{ => sql}/schema.sql (100%) diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 3fdeef6a3..4848364f3 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -5,3 +5,7 @@ spring: datasource: url: jdbc:h2:mem:testdb;MODE=MySQL driver-class-name: org.h2.Driver + sql: + init: + schema-locations: classpath:sql/schema.sql + data-locations: classpath:sql/data.sql diff --git a/src/main/resources/sql/data.sql b/src/main/resources/sql/data.sql new file mode 100644 index 000000000..9af9bca8e --- /dev/null +++ b/src/main/resources/sql/data.sql @@ -0,0 +1,17 @@ +insert into RACE_RESULT +values (1, 10, '빙봉', current_timestamp); +insert into CAR +values (1, '우르', 9, 1); +insert into CAR +values (2, '빙봉', 10, 1); + +insert into RACE_RESULT +values (2, 5, 'a,b,c,d', current_timestamp); +insert into CAR +values (3, 'a', 5, 2); +insert into CAR +values (4, 'b', 5, 2); +insert into CAR +values (5, 'c', 5, 2); +insert into CAR +values (6, 'd', 5, 2); diff --git a/src/main/resources/schema.sql b/src/main/resources/sql/schema.sql similarity index 100% rename from src/main/resources/schema.sql rename to src/main/resources/sql/schema.sql From 450e8d7c10812b3fbabe8ef075086196173d53b0 Mon Sep 17 00:00:00 2001 From: java-saeng Date: Fri, 14 Apr 2023 22:00:56 +0900 Subject: [PATCH 06/36] =?UTF-8?q?refactor=20:=20RacingCarController,=20Rac?= =?UTF-8?q?eResultService=20=EC=9E=90=EB=8F=99=EC=B0=A8=20=EC=9B=80?= =?UTF-8?q?=EC=A7=81=EC=9E=84=20method=20extract?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/RacingCarController.java | 70 +++---------------- .../racingcar/service/RaceResultService.java | 10 ++- src/main/java/racingcar/view/InputView.java | 13 +--- src/main/java/racingcar/view/OutputView.java | 41 ++++++----- 4 files changed, 42 insertions(+), 92 deletions(-) diff --git a/src/main/java/racingcar/controller/RacingCarController.java b/src/main/java/racingcar/controller/RacingCarController.java index ce2d05fdb..d69299074 100644 --- a/src/main/java/racingcar/controller/RacingCarController.java +++ b/src/main/java/racingcar/controller/RacingCarController.java @@ -1,21 +1,12 @@ package racingcar.controller; -import racingcar.domain.Car; import racingcar.domain.RacingCars; import racingcar.util.NumberGenerator; import racingcar.view.InputView; import racingcar.view.OutputView; -import java.util.Arrays; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.stream.Collectors; - public class RacingCarController { - private static final int START_POSITION = 0; - private final OutputView outputView; private final InputView inputView; private final NumberGenerator numberGenerator; @@ -27,63 +18,24 @@ public RacingCarController(final NumberGenerator numberGenerator) { } public void run() { - String[] carNames = readCarNamesStep(); - RacingCars racingCars = generateRacingCarsStep(carNames); - int tryNum = readTryNumStep(); - race(tryNum, racingCars); - showWinners(racingCars); - } + final String carNames = inputView.readCarNames(); + int trialCount = inputView.readTryNum(); - private String[] readCarNamesStep() { - outputView.printReadCarNamesMessage(); - String[] carNames = inputView.readCarNames(); - return carNames; + RacingCars racingCars = moveCars(carNames, trialCount); + showRaceResult(racingCars); } - private RacingCars generateRacingCarsStep(String[] carNames) { - List cars = generateCars(carNames); - return null; + private void showRaceResult(final RacingCars racingCars) { + showWinners(racingCars); } - private List generateCars(String[] carNames) { - return Arrays.stream(carNames) - .map(carName -> new Car(carName, START_POSITION)) - .collect(Collectors.toUnmodifiableList()); + private RacingCars moveCars(String carNames, int trialCount) { + RacingCars racingCars = RacingCars.makeCars(carNames); + racingCars.moveAllCars(trialCount, numberGenerator); + return racingCars; } private void showWinners(RacingCars racingCars) { - List winners = convertWinnersNameForPrint(racingCars.getWinners()); - outputView.printWinners(winners); - } - - private void race(int tryNum, RacingCars racingCars) { - outputView.printRacingResultMessage(); - for (int repeatIndex = 0; repeatIndex < tryNum; repeatIndex++) { - List currentCars = racingCars.getCars(); - for (Car currentCar : currentCars) { - int randomValue = numberGenerator.generate(); - currentCar.move(randomValue); - } - outputView.printCurrentRacingCarsPosition(convertRacingCarsResultForPrint(currentCars)); - } - } - - private int readTryNumStep() { - outputView.printReadTryNumMessage(); - return inputView.readTryNum(); - } - - private Map convertRacingCarsResultForPrint(List currentCars) { - Map racingCarsResult = new HashMap<>(); - for (Car currentCar : currentCars) { - racingCarsResult.put(currentCar.getName(), currentCar.getPosition()); - } - return racingCarsResult; - } - - private List convertWinnersNameForPrint(List winners) { - return winners.stream() - .map(Car::getName) - .collect(Collectors.toList()); + outputView.printWinners(racingCars); } } diff --git a/src/main/java/racingcar/service/RaceResultService.java b/src/main/java/racingcar/service/RaceResultService.java index b6aaec65e..5fc5a9955 100644 --- a/src/main/java/racingcar/service/RaceResultService.java +++ b/src/main/java/racingcar/service/RaceResultService.java @@ -34,11 +34,9 @@ public RaceResultService(final RaceResultDao raceResultDao, final CarService car public RaceResultResponse createRaceResult(final GameInfoRequest gameInfoRequest) { final String names = gameInfoRequest.getNames(); - final RacingCars racingCars = RacingCars.makeCars(names); - final int tryCount = gameInfoRequest.getCount(); - racingCars.moveAllCars(tryCount, numberGenerator); + final RacingCars racingCars = moveCars(names, tryCount); final RaceResultRegisterRequest raceResultRegisterRequest = raceResultMapper.mapToRaceResult(tryCount, racingCars); @@ -52,6 +50,12 @@ public RaceResultResponse createRaceResult(final GameInfoRequest gameInfoRequest return new RaceResultResponse(raceResultRegisterRequest.getWinners(), carStatusResponses); } + private RacingCars moveCars(final String names, final int tryCount) { + final RacingCars racingCars = RacingCars.makeCars(names); + racingCars.moveAllCars(tryCount, numberGenerator); + return racingCars; + } + public List searchRaceResult() { final Map> allRaceResult = raceResultDao.findAllRaceResult(); diff --git a/src/main/java/racingcar/view/InputView.java b/src/main/java/racingcar/view/InputView.java index 8f649316a..41fc02357 100644 --- a/src/main/java/racingcar/view/InputView.java +++ b/src/main/java/racingcar/view/InputView.java @@ -8,18 +8,9 @@ public class InputView { private final InputViewValidator inputViewValidator = new InputViewValidator(); - public String[] readCarNames() { + public String readCarNames() { Scanner scanner = new Scanner(System.in); - String carNames = scanner.nextLine(); - inputViewValidator.validateCarNames(carNames); - String[] splitCarNames = getSplitCarNames(carNames); - inputViewValidator.validateSplitCarNames(splitCarNames); - - return splitCarNames; - } - - private String[] getSplitCarNames(String carNames) { - return carNames.split(CAR_NAMES_DELIMITER, -1); + return scanner.nextLine(); } public int readTryNum() { diff --git a/src/main/java/racingcar/view/OutputView.java b/src/main/java/racingcar/view/OutputView.java index 75e52524c..c63737c1c 100644 --- a/src/main/java/racingcar/view/OutputView.java +++ b/src/main/java/racingcar/view/OutputView.java @@ -1,31 +1,21 @@ package racingcar.view; -import java.util.List; +import racingcar.domain.Car; +import racingcar.domain.RacingCars; + import java.util.Map; +import java.util.stream.Collectors; public class OutputView { - private static final String PRINT_READ_CAR_NAMES_MESSAGE = "경주할 자동차 이름을 입력하세요(이름은 쉼표(,)를 기준으로 구분)."; - private static final String PRINT_READ_TRY_NUM_MESSAGE = "시도할 회수는 몇회인가요?"; - private static final String PRINT_RACING_RESULT_MESSAGE = "\n실행 결과"; - private static final String PRINT_WINNERS_MESSAGE = "가 최종 우승했습니다."; + private static final String PRINT_CAR_NAME = "자동차 이름 : "; + private static final String PRINT_POSITION_SIZE = "이동한 거리 : "; + private static final String PRINT_WINNER = "우승자 : "; private void printMessage(String message) { System.out.println(message); } - public void printReadCarNamesMessage() { - printMessage(PRINT_READ_CAR_NAMES_MESSAGE); - } - - public void printReadTryNumMessage() { - printMessage(PRINT_READ_TRY_NUM_MESSAGE); - } - - public void printRacingResultMessage() { - printMessage(PRINT_RACING_RESULT_MESSAGE); - } - public void printCurrentRacingCarsPosition(Map carPositonMap) { for (String carName : carPositonMap.keySet()) { Integer position = carPositonMap.get(carName); @@ -35,7 +25,20 @@ public void printCurrentRacingCarsPosition(Map carPositonMap) { printMessage(""); } - public void printWinners(List winners) { - printMessage(String.join(", ", winners) + PRINT_WINNERS_MESSAGE); + public void printWinners(RacingCars racingCars) { + final String winners = racingCars.getWinners() + .stream() + .map(Car::getName) + .collect(Collectors.joining(",")); + + System.out.println(PRINT_WINNER + winners); + + racingCars.getCars() + .forEach(car -> System.out.println( + PRINT_CAR_NAME + + car.getName() + + PRINT_POSITION_SIZE + + car.getPosition()) + ); } } From a7577cd715bd4e2b4cbd6ad557149f03e3dc6e13 Mon Sep 17 00:00:00 2001 From: java-saeng Date: Tue, 18 Apr 2023 14:25:34 +0900 Subject: [PATCH 07/36] =?UTF-8?q?feat=20:=20RaceResultService=20Unit=20Tes?= =?UTF-8?q?t=20=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/dto/GameInfoRequest.java | 5 + .../service/RaceResultServiceTest.java | 132 ++++++++++++++++++ 2 files changed, 137 insertions(+) create mode 100644 src/test/java/racingcar/service/RaceResultServiceTest.java diff --git a/src/main/java/racingcar/controller/dto/GameInfoRequest.java b/src/main/java/racingcar/controller/dto/GameInfoRequest.java index 77ce449a7..dd8a30cbb 100644 --- a/src/main/java/racingcar/controller/dto/GameInfoRequest.java +++ b/src/main/java/racingcar/controller/dto/GameInfoRequest.java @@ -14,6 +14,11 @@ public class GameInfoRequest { private GameInfoRequest() { } + public GameInfoRequest(final String names, final int count) { + this.names = names; + this.count = count; + } + public String getNames() { return names; } diff --git a/src/test/java/racingcar/service/RaceResultServiceTest.java b/src/test/java/racingcar/service/RaceResultServiceTest.java new file mode 100644 index 000000000..46a5d7f8b --- /dev/null +++ b/src/test/java/racingcar/service/RaceResultServiceTest.java @@ -0,0 +1,132 @@ +package racingcar.service; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import racingcar.controller.dto.CarStatusResponse; +import racingcar.controller.dto.GameInfoRequest; +import racingcar.controller.dto.RaceResultResponse; +import racingcar.dao.raceresult.RaceResultDao; +import racingcar.dao.raceresult.dto.RaceResultRegisterRequest; +import racingcar.domain.Car; +import racingcar.domain.RacingCars; +import racingcar.service.mapper.RaceResultMapper; +import racingcar.util.NumberGenerator; +import racingcar.util.RandomNumberGenerator; + +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertAll; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +@DisplayName("RaceResultService Unit Test") +class RaceResultServiceTest { + + private RaceResultMapper raceResultMapper; + private CarService carService; + private RaceResultDao raceResultDao; + private NumberGenerator numberGenerator; + + private RaceResultService raceResultService; + + @BeforeEach + void dataInit() { + carService = mock(CarService.class); + raceResultMapper = mock(RaceResultMapper.class); + raceResultDao = mock(RaceResultDao.class); + numberGenerator = new RandomNumberGenerator(); + + raceResultService = new RaceResultService(raceResultDao, carService, + numberGenerator, raceResultMapper); + } + + @Test + @DisplayName("createRaceResult() : 게임 정보를 통해 새로운 게임을 만들 수 있다.") + void test_createRaceResult() throws Exception { + //given + final GameInfoRequest gameInfoRequest = new GameInfoRequest("a,b,c,d", 4); + final int trialCount = gameInfoRequest.getCount(); + + final RaceResultRegisterRequest raceResultRegisterRequest = + new RaceResultRegisterRequest(trialCount, "a,b,c"); + + final List carStatusResponses = List.of(new CarStatusResponse("a", 1), + new CarStatusResponse("b", 1), + new CarStatusResponse("c", 1), + new CarStatusResponse("d", 0)); + + //when + when(raceResultMapper.mapToRaceResult(anyInt(), any())) + .thenReturn(raceResultRegisterRequest); + + when(raceResultDao.save(any())) + .thenReturn(1); + + doNothing().when(carService) + .registerCars(any(), anyInt()); + + when(raceResultMapper.mapToCarStatus((RacingCars) any())) + .thenReturn(carStatusResponses); + + final RaceResultResponse raceResult = raceResultService.createRaceResult(gameInfoRequest); + + //then + final List carStatusResult = raceResult.getRacingCars(); + + assertAll( + () -> assertEquals(raceResult.getWinners(), "a,b,c"), + () -> assertThat(carStatusResult).hasSize(4), + () -> assertThat(carStatusResult).extracting("name") + .containsExactly("a", "b", "c", "d"), + () -> assertThat(carStatusResult).extracting("position") + .containsExactly(1, 1, 1, 0) + ); + } + + @Test + @DisplayName("searchRaceResult() : 모든 경기 결과를 조회할 수 있다.") + void test_searchRaceResult() throws Exception { + //given + final List cars = List.of(new Car("a", 2), new Car("b", 3), + new Car("c", 1), new Car("d", 4)); + + final Map> allRaceResult = Map.of("d", cars); + + final List carStatusResponses = List.of(new CarStatusResponse("a", 2), + new CarStatusResponse("b", 3), + new CarStatusResponse("c", 1), + new CarStatusResponse("d", 4)); + + //when + when(raceResultDao.findAllRaceResult()) + .thenReturn(allRaceResult); + + when(raceResultMapper.mapToCarStatus((List) any())) + .thenReturn(carStatusResponses); + + final List raceResultResponses = raceResultService.searchRaceResult(); + + //then + final RaceResultResponse response = raceResultResponses.get(0); + + final Map responseMap = response.getRacingCars() + .stream() + .collect(Collectors.toMap(CarStatusResponse::getName, + CarStatusResponse::getPosition)); + + assertAll( + () -> assertEquals(response.getWinners(), "d"), + () -> assertThat(response.getRacingCars()).hasSize(4), + () -> assertThat(responseMap).containsKeys("a", "b", "c", "d") + .containsValues(2, 3, 1, 4) + ); + } +} From add4aad4730c5544662ea00068d5471600f02ea2 Mon Sep 17 00:00:00 2001 From: java-saeng Date: Tue, 18 Apr 2023 14:29:52 +0900 Subject: [PATCH 08/36] =?UTF-8?q?feat=20:=20RacingCarWebController=20Unit?= =?UTF-8?q?=20Test=20=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../RacingCarWebControllerTest.java | 107 ++++++++++++++++++ 1 file changed, 107 insertions(+) create mode 100644 src/test/java/racingcar/controller/RacingCarWebControllerTest.java diff --git a/src/test/java/racingcar/controller/RacingCarWebControllerTest.java b/src/test/java/racingcar/controller/RacingCarWebControllerTest.java new file mode 100644 index 000000000..9d0742c30 --- /dev/null +++ b/src/test/java/racingcar/controller/RacingCarWebControllerTest.java @@ -0,0 +1,107 @@ +package racingcar.controller; + +import com.fasterxml.jackson.databind.ObjectMapper; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.http.MediaType; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.setup.MockMvcBuilders; +import racingcar.controller.dto.CarStatusResponse; +import racingcar.controller.dto.GameInfoRequest; +import racingcar.controller.dto.RaceResultResponse; +import racingcar.service.RaceResultService; + +import java.util.List; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +@DisplayName("RacingCarWebController Unit Test") +class RacingCarWebControllerTest { + + private MockMvc mockMvc; + + private RacingCarWebController racingCarWebController; + + private RaceResultService raceResultService; + + private ObjectMapper objectMapper; + + @BeforeEach + void init() { + objectMapper = new ObjectMapper(); + raceResultService = mock(RaceResultService.class); + racingCarWebController = new RacingCarWebController(raceResultService); + + mockMvc = MockMvcBuilders.standaloneSetup(racingCarWebController) + .build(); + } + + @Test + @DisplayName("registerRaceResult() : 게임 정보를 통해 새로운 게임을 생성할 수 있다.") + void test_registerRaceResult() throws Exception { + //given + final GameInfoRequest gameInfoRequest = new GameInfoRequest("a,b,c,d", 4); + final List carStatusResponses = List.of(new CarStatusResponse("a", 4), + new CarStatusResponse("b", 2), + new CarStatusResponse("c", 4), + new CarStatusResponse("d", 3)); + + final String bodyData = objectMapper.writeValueAsString(gameInfoRequest); + + //when + final RaceResultResponse raceResultResponse = new RaceResultResponse("a,c", carStatusResponses); + + when(raceResultService.createRaceResult(any())) + .thenReturn(raceResultResponse); + + //then + + final String resultData = objectMapper.writeValueAsString(raceResultResponse); + + mockMvc.perform(post("/plays") + .contentType(MediaType.APPLICATION_JSON) + .content(bodyData)) + .andExpect(status().isOk()) + .andExpect(content().json(resultData)); + } + + @Test + @DisplayName("showRaceResult() : 모든 게임 정보를 불러올 수 있다.") + void test_showRaceResult() throws Exception { + //given + final List carStatusResponses1 = List.of(new CarStatusResponse("a", 4), + new CarStatusResponse("b", 2), + new CarStatusResponse("c", 4), + new CarStatusResponse("d", 3)); + + final List carStatusResponses2 = List.of(new CarStatusResponse("e", 4), + new CarStatusResponse("f", 2), + new CarStatusResponse("g", 4), + new CarStatusResponse("h", 3)); + + final List raceResultResponses = + List.of(new RaceResultResponse("a,c", carStatusResponses1), + new RaceResultResponse("e,g", carStatusResponses2)); + + //when + when(raceResultService.searchRaceResult()) + .thenReturn(raceResultResponses); + + //then + + final String resultData = objectMapper.writeValueAsString(raceResultResponses); + + mockMvc.perform(get("/plays") + .contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()) + .andExpect(content().json(resultData)); + } +} From c7fd7e9bae6c4eacf02804f9452922f7b9b6641d Mon Sep 17 00:00:00 2001 From: java-saeng Date: Tue, 18 Apr 2023 14:34:36 +0900 Subject: [PATCH 09/36] feat : CarService Unit Test --- .../racingcar/service/CarServiceTest.java | 42 +++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 src/test/java/racingcar/service/CarServiceTest.java diff --git a/src/test/java/racingcar/service/CarServiceTest.java b/src/test/java/racingcar/service/CarServiceTest.java new file mode 100644 index 000000000..b25fd3fb1 --- /dev/null +++ b/src/test/java/racingcar/service/CarServiceTest.java @@ -0,0 +1,42 @@ +package racingcar.service; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import racingcar.dao.car.CarDao; +import racingcar.domain.RacingCars; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; + +@DisplayName("CarService Unit Test") +class CarServiceTest { + + private CarService carService; + + private CarDao carDao; + + @BeforeEach + void init() { + carDao = mock(CarDao.class); + + carService = new CarService(carDao); + } + + @Test + @DisplayName("registerCars() : 자동차들을 저장합니다.") + void test_registerCars() throws Exception { + //given + final RacingCars racingCars = RacingCars.makeCars("a,b,c"); + final int savedId = 1; + + //when + carService.registerCars(racingCars, savedId); + + //then + verify(carDao, times(3)).save(any()); + } +} From b59f8909de87d300bc966a0b395eb03ac7025e9f Mon Sep 17 00:00:00 2001 From: java-saeng Date: Tue, 18 Apr 2023 14:39:12 +0900 Subject: [PATCH 10/36] =?UTF-8?q?style=20:=20code=20=EC=A0=9C=EC=B6=9C=20?= =?UTF-8?q?=EC=A0=84=20reformatting?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/racingcar/view/InputView.java | 2 -- src/main/resources/sql/schema.sql | 4 ++-- .../RacingCarWebControllerTest.java | 1 - src/test/java/racingcar/domain/CarTest.java | 19 ++++++++------- .../java/racingcar/domain/RacingCarsTest.java | 24 +++++++++---------- .../racingcar/service/CarServiceTest.java | 1 - .../view/InputViewValidatorTest.java | 13 +++++----- 7 files changed, 30 insertions(+), 34 deletions(-) diff --git a/src/main/java/racingcar/view/InputView.java b/src/main/java/racingcar/view/InputView.java index 41fc02357..23c7a043e 100644 --- a/src/main/java/racingcar/view/InputView.java +++ b/src/main/java/racingcar/view/InputView.java @@ -4,8 +4,6 @@ public class InputView { - private static final String CAR_NAMES_DELIMITER = ","; - private final InputViewValidator inputViewValidator = new InputViewValidator(); public String readCarNames() { diff --git a/src/main/resources/sql/schema.sql b/src/main/resources/sql/schema.sql index 4d8ca10c1..74b27fb05 100644 --- a/src/main/resources/sql/schema.sql +++ b/src/main/resources/sql/schema.sql @@ -9,9 +9,9 @@ CREATE TABLE RACE_RESULT CREATE TABLE CAR ( - id BIGINT NOT NULL AUTO_INCREMENT, + id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, name VARCHAR(50) NOT NULL, position INT NOT NULL, - race_result_id BIGINT NOT NULL, + race_result_id BIGINT NOT NULL, PRIMARY KEY (id) ); diff --git a/src/test/java/racingcar/controller/RacingCarWebControllerTest.java b/src/test/java/racingcar/controller/RacingCarWebControllerTest.java index 9d0742c30..6c1e8a718 100644 --- a/src/test/java/racingcar/controller/RacingCarWebControllerTest.java +++ b/src/test/java/racingcar/controller/RacingCarWebControllerTest.java @@ -20,7 +20,6 @@ import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; @DisplayName("RacingCarWebController Unit Test") diff --git a/src/test/java/racingcar/domain/CarTest.java b/src/test/java/racingcar/domain/CarTest.java index 2def91d9a..4dcf92fa1 100644 --- a/src/test/java/racingcar/domain/CarTest.java +++ b/src/test/java/racingcar/domain/CarTest.java @@ -1,13 +1,14 @@ package racingcar.domain; -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.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.CsvSource; import org.junit.jupiter.params.provider.ValueSource; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + class CarTest { Car car; @@ -23,7 +24,7 @@ void generateCar() { @DisplayName("자동차 이름이 5글자 초과인 경우") void wrongCarNameLengthTest(String carName) { - Assertions.assertThatThrownBy(() -> new Car(carName, 0)) + assertThatThrownBy(() -> new Car(carName, 0)) .isInstanceOf(IllegalArgumentException.class); } @@ -32,7 +33,7 @@ void wrongCarNameLengthTest(String carName) { void carNameBlankTest() { String carName = ""; - Assertions.assertThatThrownBy(() -> new Car(carName, 0)) + assertThatThrownBy(() -> new Car(carName, 0)) .isInstanceOf(IllegalArgumentException.class); } @@ -42,7 +43,7 @@ void carNotMoveTest() { int beforePosition = car.getPosition(); car.move(3); - Assertions.assertThat(car.getPosition()).isEqualTo(beforePosition); + assertThat(car.getPosition()).isEqualTo(beforePosition); } @Test @@ -51,7 +52,7 @@ void carMoveTest() { int beforePosition = car.getPosition(); car.move(4); - Assertions.assertThat(car.getPosition()).isEqualTo(beforePosition + 1); + assertThat(car.getPosition()).isEqualTo(beforePosition + 1); } @Test @@ -59,7 +60,7 @@ void carMoveTest() { void getLargerCarTest() { compareCar = new Car("car", 5); - Assertions.assertThat(car.compareTo(compareCar)).isLessThan(0); + assertThat(car.compareTo(compareCar)).isLessThan(0); } @Test @@ -67,7 +68,7 @@ void getLargerCarTest() { void getSamePositionCarExistTest() { compareCar = new Car("car", 0); - Assertions.assertThat(car.isSamePositionCar(compareCar)).isTrue(); + assertThat(car.isSamePositionCar(compareCar)).isTrue(); } @Test @@ -75,6 +76,6 @@ void getSamePositionCarExistTest() { void getSamePositionCarNotExistTest() { compareCar = new Car("car", 5); - Assertions.assertThat(car.isSamePositionCar(compareCar)).isFalse(); + assertThat(car.isSamePositionCar(compareCar)).isFalse(); } } diff --git a/src/test/java/racingcar/domain/RacingCarsTest.java b/src/test/java/racingcar/domain/RacingCarsTest.java index 5cc27734f..3a426e705 100644 --- a/src/test/java/racingcar/domain/RacingCarsTest.java +++ b/src/test/java/racingcar/domain/RacingCarsTest.java @@ -1,13 +1,11 @@ package racingcar.domain; -import java.util.ArrayList; -import java.util.List; -import java.util.stream.Collectors; - -import org.assertj.core.api.Assertions; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + class RacingCarsTest { @@ -19,8 +17,8 @@ void getOneWinnersTest() { final Car test1 = racingCars.getCars().get(0); test1.move(4); - Assertions.assertThat(racingCars.getWinners().size()).isEqualTo(1); - Assertions.assertThat(racingCars.getWinners()).containsExactly(test1); + assertThat(racingCars.getWinners().size()).isEqualTo(1); + assertThat(racingCars.getWinners()).containsExactly(test1); } @Test @@ -36,8 +34,8 @@ void getMultipleWinnersTest() { test2.move(4); test3.move(4); - Assertions.assertThat(racingCars.getWinners().size()).isEqualTo(3); - Assertions.assertThat(racingCars.getWinners()).containsOnly(test1, test2, test3); + assertThat(racingCars.getWinners().size()).isEqualTo(3); + assertThat(racingCars.getWinners()).containsOnly(test1, test2, test3); } @@ -58,8 +56,8 @@ void getAllWinnersTest() { test4.move(4); test5.move(4); - Assertions.assertThat(racingCars.getWinners().size()).isEqualTo(5); - Assertions.assertThat(racingCars.getWinners()).containsOnly(test1, test2, test3, test4, test5); + assertThat(racingCars.getWinners().size()).isEqualTo(5); + assertThat(racingCars.getWinners()).containsOnly(test1, test2, test3, test4, test5); } @Test @@ -67,7 +65,7 @@ void getAllWinnersTest() { void splitCarNamesDuplicateTest() { String carNames = "성하,성하,이오"; - Assertions.assertThatThrownBy(() -> RacingCars.makeCars(carNames)) - .isInstanceOf(IllegalArgumentException.class); + assertThatThrownBy(() -> RacingCars.makeCars(carNames)) + .isInstanceOf(IllegalArgumentException.class); } } diff --git a/src/test/java/racingcar/service/CarServiceTest.java b/src/test/java/racingcar/service/CarServiceTest.java index b25fd3fb1..3233e01f5 100644 --- a/src/test/java/racingcar/service/CarServiceTest.java +++ b/src/test/java/racingcar/service/CarServiceTest.java @@ -6,7 +6,6 @@ import racingcar.dao.car.CarDao; import racingcar.domain.RacingCars; -import static org.junit.jupiter.api.Assertions.*; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.times; diff --git a/src/test/java/racingcar/view/InputViewValidatorTest.java b/src/test/java/racingcar/view/InputViewValidatorTest.java index df4209881..7d03188f4 100644 --- a/src/test/java/racingcar/view/InputViewValidatorTest.java +++ b/src/test/java/racingcar/view/InputViewValidatorTest.java @@ -1,12 +1,13 @@ package racingcar.view; -import org.assertj.core.api.Assertions; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.ValueSource; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + class InputViewValidatorTest { private final InputViewValidator inputViewValidator = new InputViewValidator(); @@ -20,7 +21,7 @@ class carNamesTest { void carNamesBlankTest() { String carNames = ""; - Assertions.assertThatThrownBy(() -> inputViewValidator.validateCarNames(carNames)) + assertThatThrownBy(() -> inputViewValidator.validateCarNames(carNames)) .isInstanceOf(IllegalArgumentException.class); } } @@ -34,7 +35,7 @@ class splitCarNameTest { void splitCarNamesDuplicateTest() { String[] carNames = new String[]{"성하", "성하", "이오"}; - Assertions.assertThatThrownBy(() -> inputViewValidator.validateSplitCarNames(carNames)) + assertThatThrownBy(() -> inputViewValidator.validateSplitCarNames(carNames)) .isInstanceOf(IllegalArgumentException.class); } } @@ -48,7 +49,7 @@ class tryNumTest { void tryNumBlankTest() { String tryNum = ""; - Assertions.assertThatThrownBy(() -> inputViewValidator.validateTryNum(tryNum)) + assertThatThrownBy(() -> inputViewValidator.validateTryNum(tryNum)) .isInstanceOf(IllegalArgumentException.class); } @@ -57,7 +58,7 @@ void tryNumBlankTest() { @DisplayName("시도할 횟수가 정수가 아닌 경우 예외 처리") void tryNumIntegerTest(String tryNum) { - Assertions.assertThatThrownBy(() -> inputViewValidator.validateTryNum(tryNum)) + assertThatThrownBy(() -> inputViewValidator.validateTryNum(tryNum)) .isInstanceOf(IllegalArgumentException.class); } @@ -66,7 +67,7 @@ void tryNumIntegerTest(String tryNum) { @DisplayName("시도할 횟수가 0 이하인 경우 예외 테스트") void readTryNumNotPositiveTest(String tryNum) { - Assertions.assertThatThrownBy(() -> inputViewValidator.validateTryNum(tryNum)) + assertThatThrownBy(() -> inputViewValidator.validateTryNum(tryNum)) .isInstanceOf(IllegalArgumentException.class); } } From b4c6fc691aa866f2a61fa640dcfaa6b8529b95e2 Mon Sep 17 00:00:00 2001 From: java-saeng Date: Tue, 18 Apr 2023 14:55:13 +0900 Subject: [PATCH 11/36] =?UTF-8?q?refactor=20:=20dto=20=EB=A5=BC=20Controll?= =?UTF-8?q?er=20->=20Service=20=ED=8C=A8=ED=82=A4=EC=A7=80=EB=A1=9C=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/racingcar/controller/RacingCarWebController.java | 4 ++-- src/main/java/racingcar/service/RaceResultService.java | 6 +++--- .../{controller => service}/dto/CarStatusResponse.java | 2 +- .../{controller => service}/dto/GameInfoRequest.java | 2 +- .../{controller => service}/dto/RaceResultResponse.java | 2 +- .../java/racingcar/service/mapper/RaceResultMapper.java | 2 +- .../racingcar/controller/RacingCarWebControllerTest.java | 6 +++--- src/test/java/racingcar/service/RaceResultServiceTest.java | 6 +++--- 8 files changed, 15 insertions(+), 15 deletions(-) rename src/main/java/racingcar/{controller => service}/dto/CarStatusResponse.java (91%) rename src/main/java/racingcar/{controller => service}/dto/GameInfoRequest.java (93%) rename src/main/java/racingcar/{controller => service}/dto/RaceResultResponse.java (93%) diff --git a/src/main/java/racingcar/controller/RacingCarWebController.java b/src/main/java/racingcar/controller/RacingCarWebController.java index c1ec6980d..992f2980d 100644 --- a/src/main/java/racingcar/controller/RacingCarWebController.java +++ b/src/main/java/racingcar/controller/RacingCarWebController.java @@ -5,8 +5,8 @@ import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RestController; -import racingcar.controller.dto.GameInfoRequest; -import racingcar.controller.dto.RaceResultResponse; +import racingcar.service.dto.GameInfoRequest; +import racingcar.service.dto.RaceResultResponse; import racingcar.service.RaceResultService; import java.util.List; diff --git a/src/main/java/racingcar/service/RaceResultService.java b/src/main/java/racingcar/service/RaceResultService.java index 5fc5a9955..71928c4b3 100644 --- a/src/main/java/racingcar/service/RaceResultService.java +++ b/src/main/java/racingcar/service/RaceResultService.java @@ -1,9 +1,9 @@ package racingcar.service; import org.springframework.stereotype.Service; -import racingcar.controller.dto.CarStatusResponse; -import racingcar.controller.dto.GameInfoRequest; -import racingcar.controller.dto.RaceResultResponse; +import racingcar.service.dto.CarStatusResponse; +import racingcar.service.dto.GameInfoRequest; +import racingcar.service.dto.RaceResultResponse; import racingcar.dao.raceresult.RaceResultDao; import racingcar.dao.raceresult.dto.RaceResultRegisterRequest; import racingcar.domain.Car; diff --git a/src/main/java/racingcar/controller/dto/CarStatusResponse.java b/src/main/java/racingcar/service/dto/CarStatusResponse.java similarity index 91% rename from src/main/java/racingcar/controller/dto/CarStatusResponse.java rename to src/main/java/racingcar/service/dto/CarStatusResponse.java index 845e627b7..8385a9cf7 100644 --- a/src/main/java/racingcar/controller/dto/CarStatusResponse.java +++ b/src/main/java/racingcar/service/dto/CarStatusResponse.java @@ -1,4 +1,4 @@ -package racingcar.controller.dto; +package racingcar.service.dto; public class CarStatusResponse { diff --git a/src/main/java/racingcar/controller/dto/GameInfoRequest.java b/src/main/java/racingcar/service/dto/GameInfoRequest.java similarity index 93% rename from src/main/java/racingcar/controller/dto/GameInfoRequest.java rename to src/main/java/racingcar/service/dto/GameInfoRequest.java index dd8a30cbb..8a7b88dca 100644 --- a/src/main/java/racingcar/controller/dto/GameInfoRequest.java +++ b/src/main/java/racingcar/service/dto/GameInfoRequest.java @@ -1,4 +1,4 @@ -package racingcar.controller.dto; +package racingcar.service.dto; import javax.validation.constraints.Positive; import javax.validation.constraints.Size; diff --git a/src/main/java/racingcar/controller/dto/RaceResultResponse.java b/src/main/java/racingcar/service/dto/RaceResultResponse.java similarity index 93% rename from src/main/java/racingcar/controller/dto/RaceResultResponse.java rename to src/main/java/racingcar/service/dto/RaceResultResponse.java index b29eb24c3..60a4a10b9 100644 --- a/src/main/java/racingcar/controller/dto/RaceResultResponse.java +++ b/src/main/java/racingcar/service/dto/RaceResultResponse.java @@ -1,4 +1,4 @@ -package racingcar.controller.dto; +package racingcar.service.dto; import java.util.List; diff --git a/src/main/java/racingcar/service/mapper/RaceResultMapper.java b/src/main/java/racingcar/service/mapper/RaceResultMapper.java index 6de6e6340..ce4980108 100644 --- a/src/main/java/racingcar/service/mapper/RaceResultMapper.java +++ b/src/main/java/racingcar/service/mapper/RaceResultMapper.java @@ -1,7 +1,7 @@ package racingcar.service.mapper; import org.springframework.stereotype.Component; -import racingcar.controller.dto.CarStatusResponse; +import racingcar.service.dto.CarStatusResponse; import racingcar.dao.raceresult.dto.RaceResultRegisterRequest; import racingcar.domain.Car; import racingcar.domain.RacingCars; diff --git a/src/test/java/racingcar/controller/RacingCarWebControllerTest.java b/src/test/java/racingcar/controller/RacingCarWebControllerTest.java index 6c1e8a718..4e2d7a843 100644 --- a/src/test/java/racingcar/controller/RacingCarWebControllerTest.java +++ b/src/test/java/racingcar/controller/RacingCarWebControllerTest.java @@ -7,9 +7,9 @@ import org.springframework.http.MediaType; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.setup.MockMvcBuilders; -import racingcar.controller.dto.CarStatusResponse; -import racingcar.controller.dto.GameInfoRequest; -import racingcar.controller.dto.RaceResultResponse; +import racingcar.service.dto.CarStatusResponse; +import racingcar.service.dto.GameInfoRequest; +import racingcar.service.dto.RaceResultResponse; import racingcar.service.RaceResultService; import java.util.List; diff --git a/src/test/java/racingcar/service/RaceResultServiceTest.java b/src/test/java/racingcar/service/RaceResultServiceTest.java index 46a5d7f8b..917c0c64b 100644 --- a/src/test/java/racingcar/service/RaceResultServiceTest.java +++ b/src/test/java/racingcar/service/RaceResultServiceTest.java @@ -3,9 +3,9 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; -import racingcar.controller.dto.CarStatusResponse; -import racingcar.controller.dto.GameInfoRequest; -import racingcar.controller.dto.RaceResultResponse; +import racingcar.service.dto.CarStatusResponse; +import racingcar.service.dto.GameInfoRequest; +import racingcar.service.dto.RaceResultResponse; import racingcar.dao.raceresult.RaceResultDao; import racingcar.dao.raceresult.dto.RaceResultRegisterRequest; import racingcar.domain.Car; From af5f873faab5945cee4e7fea82b41cdda6b2cede Mon Sep 17 00:00:00 2001 From: java-saeng Date: Tue, 18 Apr 2023 15:10:38 +0900 Subject: [PATCH 12/36] =?UTF-8?q?feat=20:=20Car=20=EC=97=90=20created=5Fat?= =?UTF-8?q?=20column=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/resources/sql/data.sql | 12 ++++++------ src/main/resources/sql/schema.sql | 1 + 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/main/resources/sql/data.sql b/src/main/resources/sql/data.sql index 9af9bca8e..9f9919b73 100644 --- a/src/main/resources/sql/data.sql +++ b/src/main/resources/sql/data.sql @@ -1,17 +1,17 @@ insert into RACE_RESULT values (1, 10, '빙봉', current_timestamp); insert into CAR -values (1, '우르', 9, 1); +values (1, '우르', 9, 1, current_timestamp); insert into CAR -values (2, '빙봉', 10, 1); +values (2, '빙봉', 10, 1, current_timestamp); insert into RACE_RESULT values (2, 5, 'a,b,c,d', current_timestamp); insert into CAR -values (3, 'a', 5, 2); +values (3, 'a', 5, 2, current_timestamp); insert into CAR -values (4, 'b', 5, 2); +values (4, 'b', 5, 2, current_timestamp); insert into CAR -values (5, 'c', 5, 2); +values (5, 'c', 5, 2, current_timestamp); insert into CAR -values (6, 'd', 5, 2); +values (6, 'd', 5, 2, current_timestamp); diff --git a/src/main/resources/sql/schema.sql b/src/main/resources/sql/schema.sql index 74b27fb05..713f00103 100644 --- a/src/main/resources/sql/schema.sql +++ b/src/main/resources/sql/schema.sql @@ -13,5 +13,6 @@ CREATE TABLE CAR name VARCHAR(50) NOT NULL, position INT NOT NULL, race_result_id BIGINT NOT NULL, + created_at DATETIME NOT NULL, PRIMARY KEY (id) ); From 141f18b3f5dc4b19aad5fe83aef63f1362cc4b90 Mon Sep 17 00:00:00 2001 From: java-saeng Date: Tue, 18 Apr 2023 15:30:10 +0900 Subject: [PATCH 13/36] =?UTF-8?q?feat=20:=20Service=20Integration=20Test?= =?UTF-8?q?=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/racingcar/dao/car/CarDao.java | 5 +- .../service/CarServiceIntegrationTest.java | 32 ++++++++++++ .../RaceResultServiceIntegrationTest.java | 50 +++++++++++++++++++ 3 files changed, 86 insertions(+), 1 deletion(-) create mode 100644 src/test/java/racingcar/service/CarServiceIntegrationTest.java create mode 100644 src/test/java/racingcar/service/RaceResultServiceIntegrationTest.java diff --git a/src/main/java/racingcar/dao/car/CarDao.java b/src/main/java/racingcar/dao/car/CarDao.java index 7b002c01b..c28c60fc0 100644 --- a/src/main/java/racingcar/dao/car/CarDao.java +++ b/src/main/java/racingcar/dao/car/CarDao.java @@ -7,6 +7,8 @@ import org.springframework.stereotype.Repository; import racingcar.dao.car.dto.CarRegisterRequest; +import java.time.LocalDateTime; + @Repository public class CarDao { @@ -24,7 +26,8 @@ public void save(final CarRegisterRequest carRegisterRequest) { final SqlParameterSource params = new MapSqlParameterSource() .addValue("name", carRegisterRequest.getName()) .addValue("position", carRegisterRequest.getPosition()) - .addValue("race_result_id", carRegisterRequest.getPlayResultId()); + .addValue("race_result_id", carRegisterRequest.getPlayResultId()) + .addValue("created_at", LocalDateTime.now()); jdbcInsert.execute(params); } diff --git a/src/test/java/racingcar/service/CarServiceIntegrationTest.java b/src/test/java/racingcar/service/CarServiceIntegrationTest.java new file mode 100644 index 000000000..5027b83c6 --- /dev/null +++ b/src/test/java/racingcar/service/CarServiceIntegrationTest.java @@ -0,0 +1,32 @@ +package racingcar.service; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import racingcar.dao.car.CarDao; +import racingcar.domain.RacingCars; + +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; + +@DisplayName("CarService Integration Test") +@SpringBootTest +class CarServiceIntegrationTest { + + @Autowired + private CarDao carDao; + + @Autowired + private CarService carService; + + @Test + @DisplayName("registerCars() : 자동차들을 저장합니다.") + void test_registerCars() throws Exception { + //given + final RacingCars racingCars = RacingCars.makeCars("a,b,c"); + final int savedId = 1; + + //when & then + assertDoesNotThrow(() -> carService.registerCars(racingCars, 3)); + } +} diff --git a/src/test/java/racingcar/service/RaceResultServiceIntegrationTest.java b/src/test/java/racingcar/service/RaceResultServiceIntegrationTest.java new file mode 100644 index 000000000..015360be7 --- /dev/null +++ b/src/test/java/racingcar/service/RaceResultServiceIntegrationTest.java @@ -0,0 +1,50 @@ +package racingcar.service; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import racingcar.service.dto.CarStatusResponse; +import racingcar.service.dto.GameInfoRequest; +import racingcar.service.dto.RaceResultResponse; + +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; + +@SpringBootTest +@DisplayName("RaceResultService Integration Test") +class RaceResultServiceIntegrationTest { + + @Autowired + private RaceResultService raceResultService; + + @Test + @DisplayName("createRaceResult() : 게임 정보를 통해 새로운 게임을 만들 수 있다.") + void test_createRaceResult() throws Exception { + //given + final GameInfoRequest gameInfoRequest = new GameInfoRequest("a,b,c,d", 4); + + //when + final RaceResultResponse raceResult = raceResultService.createRaceResult(gameInfoRequest); + + //then + final List racingCars = raceResult.getRacingCars(); + + assertThat(racingCars).hasSize(4) + .extracting("name") + .containsExactly("a", "b", "c", "d"); + } + + @Test + @DisplayName("searchRaceResult() : 모든 경기 결과를 조회할 수 있다.") + void test_searchRaceResult() throws Exception { + //when + final List raceResultResponses = raceResultService.searchRaceResult(); + + //then + assertThat(raceResultResponses).extracting("winners") + .hasSize(2) + .containsExactly("빙봉", "a,b,c,d"); + } +} From 2c9f6e27252e768f53caabffea8a1b8849146385 Mon Sep 17 00:00:00 2001 From: java-saeng Date: Thu, 20 Apr 2023 13:05:28 +0900 Subject: [PATCH 14/36] =?UTF-8?q?refactor=20:=20=EA=B2=BD=EA=B8=B0=20?= =?UTF-8?q?=EA=B2=B0=EA=B3=BC=20=EC=A0=80=EC=9E=A5=20=EC=8B=9C=20=EC=9E=90?= =?UTF-8?q?=EB=8F=99=EC=B0=A8=20batch=20insert?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/racingcar/dao/car/CarDao.java | 37 +++++++++++++++---- .../java/racingcar/service/CarService.java | 16 ++++++-- .../racingcar/service/CarServiceTest.java | 4 +- 3 files changed, 43 insertions(+), 14 deletions(-) diff --git a/src/main/java/racingcar/dao/car/CarDao.java b/src/main/java/racingcar/dao/car/CarDao.java index c28c60fc0..f6d0e53d7 100644 --- a/src/main/java/racingcar/dao/car/CarDao.java +++ b/src/main/java/racingcar/dao/car/CarDao.java @@ -3,11 +3,14 @@ import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.core.namedparam.MapSqlParameterSource; import org.springframework.jdbc.core.namedparam.SqlParameterSource; +import org.springframework.jdbc.core.namedparam.SqlParameterSourceUtils; import org.springframework.jdbc.core.simple.SimpleJdbcInsert; import org.springframework.stereotype.Repository; import racingcar.dao.car.dto.CarRegisterRequest; import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.List; @Repository public class CarDao { @@ -18,17 +21,35 @@ public CarDao(final JdbcTemplate jdbcTemplate) { this.jdbcTemplate = jdbcTemplate; } - public void save(final CarRegisterRequest carRegisterRequest) { + public void save(final List carRegisterRequests) { final SimpleJdbcInsert jdbcInsert = new SimpleJdbcInsert(jdbcTemplate); - jdbcInsert.withTableName("CAR").usingGeneratedKeyColumns("id"); + jdbcInsert.withTableName("CAR") + .usingGeneratedKeyColumns("id"); - final SqlParameterSource params = new MapSqlParameterSource() - .addValue("name", carRegisterRequest.getName()) - .addValue("position", carRegisterRequest.getPosition()) - .addValue("race_result_id", carRegisterRequest.getPlayResultId()) - .addValue("created_at", LocalDateTime.now()); + List batchInsertData = makeBatchInsertDataFrom(carRegisterRequests); - jdbcInsert.execute(params); + jdbcInsert.executeBatch(SqlParameterSourceUtils.createBatch(batchInsertData)); + } + + private static List makeBatchInsertDataFrom( + final List carRegisterRequests + ) { + + List mapSqlParameterSources = new ArrayList<>(); + + for (final CarRegisterRequest carRegisterRequest : carRegisterRequests) { + + final MapSqlParameterSource mapSqlParameterSource = new MapSqlParameterSource(); + + mapSqlParameterSource.addValue("name", carRegisterRequest.getName()) + .addValue("position", carRegisterRequest.getPosition()) + .addValue("race_result_id", carRegisterRequest.getPlayResultId()) + .addValue("created_at", LocalDateTime.now()); + + mapSqlParameterSources.add(mapSqlParameterSource); + } + + return mapSqlParameterSources; } } diff --git a/src/main/java/racingcar/service/CarService.java b/src/main/java/racingcar/service/CarService.java index b717ec640..16cd019b4 100644 --- a/src/main/java/racingcar/service/CarService.java +++ b/src/main/java/racingcar/service/CarService.java @@ -3,9 +3,11 @@ import org.springframework.stereotype.Service; import racingcar.dao.car.CarDao; import racingcar.dao.car.dto.CarRegisterRequest; -import racingcar.domain.Car; import racingcar.domain.RacingCars; +import java.util.List; +import java.util.stream.Collectors; + @Service public class CarService { @@ -16,8 +18,14 @@ public CarService(final CarDao carDao) { } public void registerCars(final RacingCars racingCars, final int savedId) { - for (final Car car : racingCars.getCars()) { - carDao.save(new CarRegisterRequest(car.getName(), car.getPosition(), savedId)); - } + final List requests = + racingCars.getCars() + .stream() + .map(it -> new CarRegisterRequest(it.getName(), + it.getPosition(), + savedId)) + .collect(Collectors.toList()); + + carDao.save(requests); } } diff --git a/src/test/java/racingcar/service/CarServiceTest.java b/src/test/java/racingcar/service/CarServiceTest.java index 3233e01f5..bd54afb8e 100644 --- a/src/test/java/racingcar/service/CarServiceTest.java +++ b/src/test/java/racingcar/service/CarServiceTest.java @@ -27,7 +27,7 @@ void init() { @Test @DisplayName("registerCars() : 자동차들을 저장합니다.") - void test_registerCars() throws Exception { + void test_registerCars2() throws Exception { //given final RacingCars racingCars = RacingCars.makeCars("a,b,c"); final int savedId = 1; @@ -36,6 +36,6 @@ void test_registerCars() throws Exception { carService.registerCars(racingCars, savedId); //then - verify(carDao, times(3)).save(any()); + verify(carDao, times(1)).save(any()); } } From 781f2c15d5a0e0f039a2cda5469ded6a88809365 Mon Sep 17 00:00:00 2001 From: java-saeng Date: Thu, 20 Apr 2023 13:09:51 +0900 Subject: [PATCH 15/36] =?UTF-8?q?refactor=20:=20=EA=B2=BD=EA=B8=B0=20?= =?UTF-8?q?=EA=B2=B0=EA=B3=BC=20=EC=83=9D=EC=84=B1=EC=9D=BC=20=EA=B2=BD?= =?UTF-8?q?=EC=9A=B0=20Http=20=EC=83=81=ED=83=9C=EC=BD=94=EB=93=9C=20Creat?= =?UTF-8?q?ed=EB=A1=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/racingcar/controller/RacingCarWebController.java | 3 +++ .../java/racingcar/controller/RacingCarWebControllerTest.java | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/main/java/racingcar/controller/RacingCarWebController.java b/src/main/java/racingcar/controller/RacingCarWebController.java index 992f2980d..e303a132e 100644 --- a/src/main/java/racingcar/controller/RacingCarWebController.java +++ b/src/main/java/racingcar/controller/RacingCarWebController.java @@ -1,9 +1,11 @@ package racingcar.controller; +import org.springframework.http.HttpStatus; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.ResponseStatus; import org.springframework.web.bind.annotation.RestController; import racingcar.service.dto.GameInfoRequest; import racingcar.service.dto.RaceResultResponse; @@ -21,6 +23,7 @@ public RacingCarWebController(final RaceResultService raceResultService) { } @PostMapping("/plays") + @ResponseStatus(code = HttpStatus.CREATED) public RaceResultResponse registerRaceResult(@Validated @RequestBody final GameInfoRequest gameInfoRequest) { return raceResultService.createRaceResult(gameInfoRequest); } diff --git a/src/test/java/racingcar/controller/RacingCarWebControllerTest.java b/src/test/java/racingcar/controller/RacingCarWebControllerTest.java index 4e2d7a843..04b0ae102 100644 --- a/src/test/java/racingcar/controller/RacingCarWebControllerTest.java +++ b/src/test/java/racingcar/controller/RacingCarWebControllerTest.java @@ -68,7 +68,7 @@ void test_registerRaceResult() throws Exception { mockMvc.perform(post("/plays") .contentType(MediaType.APPLICATION_JSON) .content(bodyData)) - .andExpect(status().isOk()) + .andExpect(status().isCreated()) .andExpect(content().json(resultData)); } From f501bcdf4b5bd9fa7f9463eb3ae9697782580acd Mon Sep 17 00:00:00 2001 From: java-saeng Date: Thu, 20 Apr 2023 13:24:38 +0900 Subject: [PATCH 16/36] =?UTF-8?q?feat=20:=20=EA=B2=BD=EA=B8=B0=20=EA=B2=B0?= =?UTF-8?q?=EA=B3=BC=20=EC=83=9D=EC=84=B1=20=EC=8B=9C=20=EC=9E=90=EB=8F=99?= =?UTF-8?q?=EC=B0=A8=20=EC=A0=80=EC=9E=A5=EA=B9=8C=EC=A7=80=EC=9D=98=20?= =?UTF-8?q?=EB=A1=9C=EC=A7=81=EC=9D=84=20=EB=85=BC=EB=A6=AC=EC=A0=81?= =?UTF-8?q?=EC=9C=BC=EB=A1=9C=20=EB=AC=B6=EA=B8=B0=20=EC=9C=84=ED=95=B4=20?= =?UTF-8?q?=ED=8A=B8=EB=9E=9C=EC=9E=AD=EC=85=98=20=EC=82=AC=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/racingcar/service/RaceResultService.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/java/racingcar/service/RaceResultService.java b/src/main/java/racingcar/service/RaceResultService.java index 71928c4b3..f794f134e 100644 --- a/src/main/java/racingcar/service/RaceResultService.java +++ b/src/main/java/racingcar/service/RaceResultService.java @@ -1,6 +1,7 @@ package racingcar.service; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; import racingcar.service.dto.CarStatusResponse; import racingcar.service.dto.GameInfoRequest; import racingcar.service.dto.RaceResultResponse; @@ -31,6 +32,7 @@ public RaceResultService(final RaceResultDao raceResultDao, final CarService car this.raceResultMapper = raceResultMapper; } + @Transactional public RaceResultResponse createRaceResult(final GameInfoRequest gameInfoRequest) { final String names = gameInfoRequest.getNames(); From 4d50307a00b86c137ec11a51ace61e32beb9fd1e Mon Sep 17 00:00:00 2001 From: java-saeng Date: Thu, 20 Apr 2023 16:02:39 +0900 Subject: [PATCH 17/36] =?UTF-8?q?fix=20:=20batch=20insert=20=EC=A0=9C?= =?UTF-8?q?=EB=8C=80=EB=A1=9C=20=EC=88=98=ED=96=89=EC=95=88=EB=90=98?= =?UTF-8?q?=EC=96=B4=20=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - SqlParameterSourceUtils 를 사용하면 배열을 한번 더 감싸기 때문에 executeBatch 에서 value 를 찾을 수 없었음 --- src/main/java/racingcar/dao/car/CarDao.java | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/main/java/racingcar/dao/car/CarDao.java b/src/main/java/racingcar/dao/car/CarDao.java index f6d0e53d7..87ace76f3 100644 --- a/src/main/java/racingcar/dao/car/CarDao.java +++ b/src/main/java/racingcar/dao/car/CarDao.java @@ -2,8 +2,6 @@ import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.core.namedparam.MapSqlParameterSource; -import org.springframework.jdbc.core.namedparam.SqlParameterSource; -import org.springframework.jdbc.core.namedparam.SqlParameterSourceUtils; import org.springframework.jdbc.core.simple.SimpleJdbcInsert; import org.springframework.stereotype.Repository; import racingcar.dao.car.dto.CarRegisterRequest; @@ -29,7 +27,11 @@ public void save(final List carRegisterRequests) { List batchInsertData = makeBatchInsertDataFrom(carRegisterRequests); - jdbcInsert.executeBatch(SqlParameterSourceUtils.createBatch(batchInsertData)); + jdbcInsert.executeBatch(mapToArrayFrom(batchInsertData)); + } + + private static MapSqlParameterSource[] mapToArrayFrom(final List batchInsertData) { + return batchInsertData.toArray(new MapSqlParameterSource[0]); } private static List makeBatchInsertDataFrom( From ca6898f5264e242a3c69e4ca5183118cf4af138c Mon Sep 17 00:00:00 2001 From: java-saeng Date: Thu, 20 Apr 2023 16:05:56 +0900 Subject: [PATCH 18/36] =?UTF-8?q?feat=20:=20exception=20handler=20?= =?UTF-8?q?=EA=B8=B0=EB=8A=A5=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - exception message custom --- .../controller/RacingCarWebController.java | 2 +- .../exception/ExceptionHandlerController.java | 20 ++++++++ .../global/exception/ExceptionStatus.java | 32 +++++++++++++ .../exception/response/ExceptionResponse.java | 45 +++++++++++++++++ .../response/ExceptionTargetInfoResponse.java | 48 +++++++++++++++++++ 5 files changed, 146 insertions(+), 1 deletion(-) create mode 100644 src/main/java/racingcar/global/exception/ExceptionHandlerController.java create mode 100644 src/main/java/racingcar/global/exception/ExceptionStatus.java create mode 100644 src/main/java/racingcar/global/exception/response/ExceptionResponse.java create mode 100644 src/main/java/racingcar/global/exception/response/ExceptionTargetInfoResponse.java diff --git a/src/main/java/racingcar/controller/RacingCarWebController.java b/src/main/java/racingcar/controller/RacingCarWebController.java index e303a132e..019c86ebe 100644 --- a/src/main/java/racingcar/controller/RacingCarWebController.java +++ b/src/main/java/racingcar/controller/RacingCarWebController.java @@ -7,9 +7,9 @@ import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.ResponseStatus; import org.springframework.web.bind.annotation.RestController; +import racingcar.service.RaceResultService; import racingcar.service.dto.GameInfoRequest; import racingcar.service.dto.RaceResultResponse; -import racingcar.service.RaceResultService; import java.util.List; diff --git a/src/main/java/racingcar/global/exception/ExceptionHandlerController.java b/src/main/java/racingcar/global/exception/ExceptionHandlerController.java new file mode 100644 index 000000000..7cc0f8fe9 --- /dev/null +++ b/src/main/java/racingcar/global/exception/ExceptionHandlerController.java @@ -0,0 +1,20 @@ +package racingcar.global.exception; + +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.MethodArgumentNotValidException; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.ResponseStatus; +import org.springframework.web.bind.annotation.RestControllerAdvice; +import racingcar.global.exception.response.ExceptionResponse; + +@RestControllerAdvice +public class ExceptionHandlerController { + + @ExceptionHandler(MethodArgumentNotValidException.class) + @ResponseStatus(HttpStatus.BAD_REQUEST) + public ExceptionResponse handleMethodArgumentNotValidException( + MethodArgumentNotValidException exception + ) { + return ExceptionResponse.of(ExceptionStatus.INVALID_INPUT_VALUE_EXCEPTION, exception); + } +} diff --git a/src/main/java/racingcar/global/exception/ExceptionStatus.java b/src/main/java/racingcar/global/exception/ExceptionStatus.java new file mode 100644 index 000000000..eafa462f1 --- /dev/null +++ b/src/main/java/racingcar/global/exception/ExceptionStatus.java @@ -0,0 +1,32 @@ +package racingcar.global.exception; + +import org.springframework.http.HttpStatus; + +import static org.springframework.http.HttpStatus.BAD_REQUEST; + +public enum ExceptionStatus { + + INVALID_INPUT_VALUE_EXCEPTION(400, "유효하지 않는 입력 값입니다.", BAD_REQUEST); + + private final int status; + private final String message; + private final HttpStatus httpStatus; + + ExceptionStatus(final int status, final String message, final HttpStatus httpStatus) { + this.status = status; + this.message = message; + this.httpStatus = httpStatus; + } + + public int getStatus() { + return status; + } + + public String getMessage() { + return message; + } + + public HttpStatus getHttpStatus() { + return httpStatus; + } +} diff --git a/src/main/java/racingcar/global/exception/response/ExceptionResponse.java b/src/main/java/racingcar/global/exception/response/ExceptionResponse.java new file mode 100644 index 000000000..e09a48295 --- /dev/null +++ b/src/main/java/racingcar/global/exception/response/ExceptionResponse.java @@ -0,0 +1,45 @@ +package racingcar.global.exception.response; + +import org.springframework.validation.BindingResult; +import racingcar.global.exception.ExceptionStatus; + +import java.util.List; + +public class ExceptionResponse { + + private final int statusCode; + private final String message; + private final String status; + private final List errors; + + private ExceptionResponse(final int statusCode, final String message, + final String httpStatus, final List errors) { + this.statusCode = statusCode; + this.message = message; + this.status = httpStatus; + this.errors = errors; + } + + public static ExceptionResponse of(final ExceptionStatus exceptionStatus, BindingResult bindingResult) { + return new ExceptionResponse(exceptionStatus.getStatus(), + exceptionStatus.getMessage(), + exceptionStatus.getHttpStatus().name(), + ExceptionTargetInfoResponse.from(bindingResult)); + } + + public String getMessage() { + return message; + } + + public String getStatus() { + return status; + } + + public int getStatusCode() { + return statusCode; + } + + public List getErrors() { + return errors; + } +} diff --git a/src/main/java/racingcar/global/exception/response/ExceptionTargetInfoResponse.java b/src/main/java/racingcar/global/exception/response/ExceptionTargetInfoResponse.java new file mode 100644 index 000000000..08258db3f --- /dev/null +++ b/src/main/java/racingcar/global/exception/response/ExceptionTargetInfoResponse.java @@ -0,0 +1,48 @@ +package racingcar.global.exception.response; + +import org.springframework.validation.BindingResult; +import org.springframework.validation.FieldError; + +import java.util.List; +import java.util.Objects; +import java.util.stream.Collectors; + +public class ExceptionTargetInfoResponse { + + private static final String DEFAULT_ERROR_MESSAGE = ""; + + private final String field; + private final String value; + private final String reason; + + private ExceptionTargetInfoResponse(final String field, final String value, final String reason) { + this.field = field; + this.value = value; + this.reason = reason; + } + + public static List from(BindingResult bindingResult) { + + final List fieldErrors = bindingResult.getFieldErrors(); + + return fieldErrors.stream() + .map(error -> new ExceptionTargetInfoResponse( + error.getField(), + Objects.requireNonNullElse(error.getRejectedValue(), DEFAULT_ERROR_MESSAGE).toString(), + Objects.requireNonNullElse(error.getDefaultMessage(), DEFAULT_ERROR_MESSAGE)) + ) + .collect(Collectors.toList()); + } + + public String getField() { + return field; + } + + public String getValue() { + return value; + } + + public String getReason() { + return reason; + } +} From 1f39b721b071830899eef80fc38ff976ef4f6969 Mon Sep 17 00:00:00 2001 From: java-saeng Date: Thu, 20 Apr 2023 16:25:43 +0900 Subject: [PATCH 19/36] =?UTF-8?q?test=20:=20input,=20output=20=EC=97=90=20?= =?UTF-8?q?=EC=9D=98=EC=A1=B4=EB=90=98=EC=A7=80=20=EC=95=8A=EA=B2=8C=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../service/CarServiceIntegrationTest.java | 4 ++-- .../java/racingcar/service/CarServiceTest.java | 6 +++--- .../RaceResultServiceIntegrationTest.java | 16 +++++++++++----- 3 files changed, 16 insertions(+), 10 deletions(-) diff --git a/src/test/java/racingcar/service/CarServiceIntegrationTest.java b/src/test/java/racingcar/service/CarServiceIntegrationTest.java index 5027b83c6..218588e60 100644 --- a/src/test/java/racingcar/service/CarServiceIntegrationTest.java +++ b/src/test/java/racingcar/service/CarServiceIntegrationTest.java @@ -24,9 +24,9 @@ class CarServiceIntegrationTest { void test_registerCars() throws Exception { //given final RacingCars racingCars = RacingCars.makeCars("a,b,c"); - final int savedId = 1; + final int savedRaceResultId = 3; //when & then - assertDoesNotThrow(() -> carService.registerCars(racingCars, 3)); + assertDoesNotThrow(() -> carService.registerCars(racingCars, savedRaceResultId)); } } diff --git a/src/test/java/racingcar/service/CarServiceTest.java b/src/test/java/racingcar/service/CarServiceTest.java index bd54afb8e..c0031f6b6 100644 --- a/src/test/java/racingcar/service/CarServiceTest.java +++ b/src/test/java/racingcar/service/CarServiceTest.java @@ -27,13 +27,13 @@ void init() { @Test @DisplayName("registerCars() : 자동차들을 저장합니다.") - void test_registerCars2() throws Exception { + void test_registerCars() throws Exception { //given final RacingCars racingCars = RacingCars.makeCars("a,b,c"); - final int savedId = 1; + final int savedRaceResultId = 1; //when - carService.registerCars(racingCars, savedId); + carService.registerCars(racingCars, savedRaceResultId); //then verify(carDao, times(1)).save(any()); diff --git a/src/test/java/racingcar/service/RaceResultServiceIntegrationTest.java b/src/test/java/racingcar/service/RaceResultServiceIntegrationTest.java index 015360be7..d04242e42 100644 --- a/src/test/java/racingcar/service/RaceResultServiceIntegrationTest.java +++ b/src/test/java/racingcar/service/RaceResultServiceIntegrationTest.java @@ -23,7 +23,10 @@ class RaceResultServiceIntegrationTest { @DisplayName("createRaceResult() : 게임 정보를 통해 새로운 게임을 만들 수 있다.") void test_createRaceResult() throws Exception { //given - final GameInfoRequest gameInfoRequest = new GameInfoRequest("a,b,c,d", 4); + final String input = "a,b,c,d"; + final List splitInput = List.of(input.split(",")); + + final GameInfoRequest gameInfoRequest = new GameInfoRequest(input, splitInput.size()); //when final RaceResultResponse raceResult = raceResultService.createRaceResult(gameInfoRequest); @@ -31,20 +34,23 @@ void test_createRaceResult() throws Exception { //then final List racingCars = raceResult.getRacingCars(); - assertThat(racingCars).hasSize(4) + assertThat(racingCars).hasSize(splitInput.size()) .extracting("name") - .containsExactly("a", "b", "c", "d"); + .containsExactlyElementsOf(splitInput); } @Test @DisplayName("searchRaceResult() : 모든 경기 결과를 조회할 수 있다.") void test_searchRaceResult() throws Exception { + //given + final List winnerResult = List.of("빙봉", "a,b,c,d"); + //when final List raceResultResponses = raceResultService.searchRaceResult(); //then assertThat(raceResultResponses).extracting("winners") - .hasSize(2) - .containsExactly("빙봉", "a,b,c,d"); + .hasSize(winnerResult.size()) + .containsExactlyElementsOf(winnerResult); } } From a84ae9130990c31ac1902c1481533100faaa8aa0 Mon Sep 17 00:00:00 2001 From: java-saeng Date: Fri, 21 Apr 2023 14:18:23 +0900 Subject: [PATCH 20/36] =?UTF-8?q?feat=20:=20CarDao=20=EC=97=90=EC=84=9C=20?= =?UTF-8?q?Entity=20=EC=82=AC=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/racingcar/dao/car/CarDao.java | 38 +++------------ src/main/java/racingcar/entity/CarEntity.java | 47 +++++++++++++++++++ .../java/racingcar/service/CarService.java | 14 +++--- .../java/racingcar/dao/car/CarDaoTest.java | 40 ++++++++++++++++ src/test/resources/application.yml | 11 +++++ src/test/resources/sql/data.sql | 17 +++++++ src/test/resources/sql/schema.sql | 18 +++++++ 7 files changed, 147 insertions(+), 38 deletions(-) create mode 100644 src/main/java/racingcar/entity/CarEntity.java create mode 100644 src/test/java/racingcar/dao/car/CarDaoTest.java create mode 100644 src/test/resources/application.yml create mode 100644 src/test/resources/sql/data.sql create mode 100644 src/test/resources/sql/schema.sql diff --git a/src/main/java/racingcar/dao/car/CarDao.java b/src/main/java/racingcar/dao/car/CarDao.java index 87ace76f3..82785e730 100644 --- a/src/main/java/racingcar/dao/car/CarDao.java +++ b/src/main/java/racingcar/dao/car/CarDao.java @@ -1,13 +1,12 @@ package racingcar.dao.car; import org.springframework.jdbc.core.JdbcTemplate; -import org.springframework.jdbc.core.namedparam.MapSqlParameterSource; +import org.springframework.jdbc.core.namedparam.SqlParameterSource; +import org.springframework.jdbc.core.namedparam.SqlParameterSourceUtils; import org.springframework.jdbc.core.simple.SimpleJdbcInsert; import org.springframework.stereotype.Repository; -import racingcar.dao.car.dto.CarRegisterRequest; +import racingcar.entity.CarEntity; -import java.time.LocalDateTime; -import java.util.ArrayList; import java.util.List; @Repository @@ -19,39 +18,14 @@ public CarDao(final JdbcTemplate jdbcTemplate) { this.jdbcTemplate = jdbcTemplate; } - public void save(final List carRegisterRequests) { + public void save(final List carEntities) { final SimpleJdbcInsert jdbcInsert = new SimpleJdbcInsert(jdbcTemplate); jdbcInsert.withTableName("CAR") .usingGeneratedKeyColumns("id"); - List batchInsertData = makeBatchInsertDataFrom(carRegisterRequests); + final SqlParameterSource[] batch = SqlParameterSourceUtils.createBatch(carEntities); - jdbcInsert.executeBatch(mapToArrayFrom(batchInsertData)); - } - - private static MapSqlParameterSource[] mapToArrayFrom(final List batchInsertData) { - return batchInsertData.toArray(new MapSqlParameterSource[0]); - } - - private static List makeBatchInsertDataFrom( - final List carRegisterRequests - ) { - - List mapSqlParameterSources = new ArrayList<>(); - - for (final CarRegisterRequest carRegisterRequest : carRegisterRequests) { - - final MapSqlParameterSource mapSqlParameterSource = new MapSqlParameterSource(); - - mapSqlParameterSource.addValue("name", carRegisterRequest.getName()) - .addValue("position", carRegisterRequest.getPosition()) - .addValue("race_result_id", carRegisterRequest.getPlayResultId()) - .addValue("created_at", LocalDateTime.now()); - - mapSqlParameterSources.add(mapSqlParameterSource); - } - - return mapSqlParameterSources; + jdbcInsert.executeBatch(batch); } } diff --git a/src/main/java/racingcar/entity/CarEntity.java b/src/main/java/racingcar/entity/CarEntity.java new file mode 100644 index 000000000..da3aeb21e --- /dev/null +++ b/src/main/java/racingcar/entity/CarEntity.java @@ -0,0 +1,47 @@ +package racingcar.entity; + +import java.time.LocalDateTime; + +public class CarEntity { + + private final Integer id; + private final String name; + private final int position; + private final Integer raceResultId; + private final LocalDateTime createdAt; + + public CarEntity(final Integer id, final String name, + final int position, final Integer raceResultId, + final LocalDateTime createdAt) { + this.id = id; + this.name = name; + this.position = position; + this.raceResultId = raceResultId; + this.createdAt = createdAt; + } + + public CarEntity(final String name, final int position, + final Integer raceResultId, final LocalDateTime createdAt) { + this(null, name, position, raceResultId, createdAt); + } + + public Integer getId() { + return id; + } + + public String getName() { + return name; + } + + public int getPosition() { + return position; + } + + public Integer getRaceResultId() { + return raceResultId; + } + + public LocalDateTime getCreatedAt() { + return createdAt; + } +} diff --git a/src/main/java/racingcar/service/CarService.java b/src/main/java/racingcar/service/CarService.java index 16cd019b4..8c0481e47 100644 --- a/src/main/java/racingcar/service/CarService.java +++ b/src/main/java/racingcar/service/CarService.java @@ -2,9 +2,10 @@ import org.springframework.stereotype.Service; import racingcar.dao.car.CarDao; -import racingcar.dao.car.dto.CarRegisterRequest; +import racingcar.entity.CarEntity; import racingcar.domain.RacingCars; +import java.time.LocalDateTime; import java.util.List; import java.util.stream.Collectors; @@ -17,13 +18,14 @@ public CarService(final CarDao carDao) { this.carDao = carDao; } - public void registerCars(final RacingCars racingCars, final int savedId) { - final List requests = + public void registerCars(final RacingCars racingCars, final Integer savedId) { + final List requests = racingCars.getCars() .stream() - .map(it -> new CarRegisterRequest(it.getName(), - it.getPosition(), - savedId)) + .map(it -> new CarEntity(it.getName(), + it.getPosition(), + savedId, + LocalDateTime.now())) .collect(Collectors.toList()); carDao.save(requests); diff --git a/src/test/java/racingcar/dao/car/CarDaoTest.java b/src/test/java/racingcar/dao/car/CarDaoTest.java new file mode 100644 index 000000000..2f263c258 --- /dev/null +++ b/src/test/java/racingcar/dao/car/CarDaoTest.java @@ -0,0 +1,40 @@ +package racingcar.dao.car; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.jdbc.JdbcTest; +import org.springframework.jdbc.core.JdbcTemplate; +import racingcar.entity.CarEntity; + +import java.time.LocalDateTime; +import java.util.List; + +@JdbcTest +class CarDaoTest { + + CarDao carDao; + + @Autowired + JdbcTemplate jdbcTemplate; + + @BeforeEach + void init() { + carDao = new CarDao(jdbcTemplate); + } + + @Test + @DisplayName("CarEntity를 통해서 Car 를 저장할 수 있다.") + void test_save() throws Exception { + //given + final List carEntities = + List.of(new CarEntity("a", 1, 3, LocalDateTime.now()), + new CarEntity("b", 2, 3, LocalDateTime.now()) + ); + + //when + Assertions.assertDoesNotThrow(() -> carDao.save(carEntities)); + } +} diff --git a/src/test/resources/application.yml b/src/test/resources/application.yml new file mode 100644 index 000000000..3f9756363 --- /dev/null +++ b/src/test/resources/application.yml @@ -0,0 +1,11 @@ +spring: + h2: + console: + enabled: true + datasource: + url: jdbc:h2:mem:test;MODE=MySQL + driver-class-name: org.h2.Driver + sql: + init: + schema-locations: classpath:sql/schema.sql + data-locations: classpath:sql/data.sql diff --git a/src/test/resources/sql/data.sql b/src/test/resources/sql/data.sql new file mode 100644 index 000000000..f9b569224 --- /dev/null +++ b/src/test/resources/sql/data.sql @@ -0,0 +1,17 @@ +insert into RACE_RESULT +values (3, 10, '빙봉', current_timestamp); +insert into CAR +values (3, '우르', 9, 3, current_timestamp); +insert into CAR +values (4, '빙봉', 10, 3, current_timestamp); + +insert into RACE_RESULT +values (4, 5, 'a,b,c,d', current_timestamp); +insert into CAR +values (5, 'a', 5, 4, current_timestamp); +insert into CAR +values (6, 'b', 5, 4, current_timestamp); +insert into CAR +values (7, 'c', 5, 4, current_timestamp); +insert into CAR +values (8, 'd', 5, 4, current_timestamp); diff --git a/src/test/resources/sql/schema.sql b/src/test/resources/sql/schema.sql new file mode 100644 index 000000000..1338f1555 --- /dev/null +++ b/src/test/resources/sql/schema.sql @@ -0,0 +1,18 @@ +CREATE TABLE RACE_RESULT +( + id BIGINT NOT NULL AUTO_INCREMENT, + trial_count INT NOT NULL, + winners VARCHAR(255) NOT NULL, + created_at DATETIME NOT NULL, + PRIMARY KEY (id) +); + +CREATE TABLE CAR +( + id BIGINT NOT NULL AUTO_INCREMENT, + name VARCHAR(50) NOT NULL, + position INT NOT NULL, + race_result_id BIGINT NOT NULL, + created_at DATETIME NOT NULL, + PRIMARY KEY (id) +); From 74d46a99999b8b225d65c095bf53ca5fdefb1281 Mon Sep 17 00:00:00 2001 From: java-saeng Date: Fri, 21 Apr 2023 14:54:25 +0900 Subject: [PATCH 21/36] =?UTF-8?q?feat=20:=20RaceResultDao=20=EC=97=90?= =?UTF-8?q?=EC=84=9C=20Entity=20=EC=82=AC=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dao/raceresult/RaceResultDao.java | 8 ++-- .../dto/RaceResultRegisterRequest.java | 20 ---------- .../racingcar/entity/RaceResultEntity.java | 32 +++++++++++++++ .../racingcar/service/RaceResultService.java | 10 ++--- .../service/mapper/RaceResultMapper.java | 12 +++--- .../dao/raceresult/RaceResultDaoTest.java | 40 +++++++++++++++++++ .../service/RaceResultServiceTest.java | 15 +++---- 7 files changed, 96 insertions(+), 41 deletions(-) delete mode 100644 src/main/java/racingcar/dao/raceresult/dto/RaceResultRegisterRequest.java create mode 100644 src/main/java/racingcar/entity/RaceResultEntity.java create mode 100644 src/test/java/racingcar/dao/raceresult/RaceResultDaoTest.java diff --git a/src/main/java/racingcar/dao/raceresult/RaceResultDao.java b/src/main/java/racingcar/dao/raceresult/RaceResultDao.java index f8260a88b..daae84ff6 100644 --- a/src/main/java/racingcar/dao/raceresult/RaceResultDao.java +++ b/src/main/java/racingcar/dao/raceresult/RaceResultDao.java @@ -6,8 +6,8 @@ import org.springframework.jdbc.core.namedparam.SqlParameterSource; import org.springframework.jdbc.core.simple.SimpleJdbcInsert; import org.springframework.stereotype.Repository; -import racingcar.dao.raceresult.dto.RaceResultRegisterRequest; import racingcar.domain.Car; +import racingcar.entity.RaceResultEntity; import java.sql.ResultSet; import java.sql.SQLException; @@ -26,15 +26,15 @@ public RaceResultDao(final JdbcTemplate jdbcTemplate) { this.jdbcTemplate = jdbcTemplate; } - public int save(final RaceResultRegisterRequest raceResultRegisterRequest) { + public int save(final RaceResultEntity raceResultEntity) { final SimpleJdbcInsert jdbcInsert = new SimpleJdbcInsert(jdbcTemplate); jdbcInsert.withTableName("RACE_RESULT").usingGeneratedKeyColumns("id"); final SqlParameterSource params = new MapSqlParameterSource() - .addValue("trial_count", raceResultRegisterRequest.getTrialCount()) - .addValue("winners", raceResultRegisterRequest.getWinners()) + .addValue("trial_count", raceResultEntity.getTrialCount()) + .addValue("winners", raceResultEntity.getWinners()) .addValue("created_at", LocalDateTime.now()); return jdbcInsert.executeAndReturnKey(params).intValue(); diff --git a/src/main/java/racingcar/dao/raceresult/dto/RaceResultRegisterRequest.java b/src/main/java/racingcar/dao/raceresult/dto/RaceResultRegisterRequest.java deleted file mode 100644 index 11be9d433..000000000 --- a/src/main/java/racingcar/dao/raceresult/dto/RaceResultRegisterRequest.java +++ /dev/null @@ -1,20 +0,0 @@ -package racingcar.dao.raceresult.dto; - -public class RaceResultRegisterRequest { - - private final int trialCount; - private final String winners; - - public RaceResultRegisterRequest(final int trialCount, final String winners) { - this.trialCount = trialCount; - this.winners = winners; - } - - public int getTrialCount() { - return trialCount; - } - - public String getWinners() { - return winners; - } -} diff --git a/src/main/java/racingcar/entity/RaceResultEntity.java b/src/main/java/racingcar/entity/RaceResultEntity.java new file mode 100644 index 000000000..e7e7143a6 --- /dev/null +++ b/src/main/java/racingcar/entity/RaceResultEntity.java @@ -0,0 +1,32 @@ +package racingcar.entity; + +import java.time.LocalDateTime; + +public class RaceResultEntity { + + private final Long id; + private final int trialCount; + private final String winners; + private final LocalDateTime createdAt; + + public RaceResultEntity(final Long id, final int trialCount, + final String winners, final LocalDateTime createdAt) { + this.id = id; + this.trialCount = trialCount; + this.winners = winners; + this.createdAt = createdAt; + } + + public RaceResultEntity(final int trialCount, final String winners, + final LocalDateTime createdAt) { + this(null, trialCount, winners, createdAt); + } + + public int getTrialCount() { + return trialCount; + } + + public String getWinners() { + return winners; + } +} diff --git a/src/main/java/racingcar/service/RaceResultService.java b/src/main/java/racingcar/service/RaceResultService.java index f794f134e..3e5a28f92 100644 --- a/src/main/java/racingcar/service/RaceResultService.java +++ b/src/main/java/racingcar/service/RaceResultService.java @@ -2,16 +2,17 @@ import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import racingcar.entity.RaceResultEntity; import racingcar.service.dto.CarStatusResponse; import racingcar.service.dto.GameInfoRequest; import racingcar.service.dto.RaceResultResponse; import racingcar.dao.raceresult.RaceResultDao; -import racingcar.dao.raceresult.dto.RaceResultRegisterRequest; import racingcar.domain.Car; import racingcar.domain.RacingCars; import racingcar.service.mapper.RaceResultMapper; import racingcar.util.NumberGenerator; +import java.time.LocalDateTime; import java.util.List; import java.util.Map; import java.util.stream.Collectors; @@ -40,16 +41,15 @@ public RaceResultResponse createRaceResult(final GameInfoRequest gameInfoRequest final RacingCars racingCars = moveCars(names, tryCount); - final RaceResultRegisterRequest raceResultRegisterRequest = - raceResultMapper.mapToRaceResult(tryCount, racingCars); + final RaceResultEntity raceResultEntity = raceResultMapper.mapToRaceResult(tryCount, racingCars); - final int savedId = raceResultDao.save(raceResultRegisterRequest); + final int savedId = raceResultDao.save(raceResultEntity); carService.registerCars(racingCars, savedId); final List carStatusResponses = raceResultMapper.mapToCarStatus(racingCars); - return new RaceResultResponse(raceResultRegisterRequest.getWinners(), carStatusResponses); + return new RaceResultResponse(raceResultEntity.getWinners(), carStatusResponses); } private RacingCars moveCars(final String names, final int tryCount) { diff --git a/src/main/java/racingcar/service/mapper/RaceResultMapper.java b/src/main/java/racingcar/service/mapper/RaceResultMapper.java index ce4980108..8f489ef51 100644 --- a/src/main/java/racingcar/service/mapper/RaceResultMapper.java +++ b/src/main/java/racingcar/service/mapper/RaceResultMapper.java @@ -1,11 +1,12 @@ package racingcar.service.mapper; import org.springframework.stereotype.Component; -import racingcar.service.dto.CarStatusResponse; -import racingcar.dao.raceresult.dto.RaceResultRegisterRequest; import racingcar.domain.Car; import racingcar.domain.RacingCars; +import racingcar.entity.RaceResultEntity; +import racingcar.service.dto.CarStatusResponse; +import java.time.LocalDateTime; import java.util.List; import java.util.stream.Collectors; @@ -14,9 +15,10 @@ public class RaceResultMapper { private static final String CAR_NAMES_DELIMITER = ","; - public RaceResultRegisterRequest mapToRaceResult(final int tryCount, final RacingCars racingCars) { - return new RaceResultRegisterRequest(tryCount, - String.join(CAR_NAMES_DELIMITER, mapToNameFrom(racingCars))); + public RaceResultEntity mapToRaceResult(final int tryCount, final RacingCars racingCars) { + return new RaceResultEntity(tryCount, + String.join(CAR_NAMES_DELIMITER, mapToNameFrom(racingCars)), + LocalDateTime.now()); } private List mapToNameFrom(RacingCars racingCars) { diff --git a/src/test/java/racingcar/dao/raceresult/RaceResultDaoTest.java b/src/test/java/racingcar/dao/raceresult/RaceResultDaoTest.java new file mode 100644 index 000000000..b2b4c1a08 --- /dev/null +++ b/src/test/java/racingcar/dao/raceresult/RaceResultDaoTest.java @@ -0,0 +1,40 @@ +package racingcar.dao.raceresult; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.jdbc.JdbcTest; +import org.springframework.jdbc.core.JdbcTemplate; +import racingcar.entity.RaceResultEntity; + +import java.time.LocalDateTime; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +@JdbcTest +class RaceResultDaoTest { + + RaceResultDao raceResultDao; + + @Autowired + JdbcTemplate jdbcTemplate; + + @BeforeEach + void setUp() { + raceResultDao = new RaceResultDao(jdbcTemplate); + } + + @Test + @DisplayName("save() : 경기 결과를 저장할 수 있다.") + void test_save() throws Exception { + //given + final RaceResultEntity raceResultEntity = new RaceResultEntity(4, "a,b,c", LocalDateTime.now()); + + //when + final int savedId = raceResultDao.save(raceResultEntity); + + //then + assertEquals(savedId, 1); + } +} diff --git a/src/test/java/racingcar/service/RaceResultServiceTest.java b/src/test/java/racingcar/service/RaceResultServiceTest.java index 917c0c64b..fb94e7994 100644 --- a/src/test/java/racingcar/service/RaceResultServiceTest.java +++ b/src/test/java/racingcar/service/RaceResultServiceTest.java @@ -3,17 +3,18 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; -import racingcar.service.dto.CarStatusResponse; -import racingcar.service.dto.GameInfoRequest; -import racingcar.service.dto.RaceResultResponse; import racingcar.dao.raceresult.RaceResultDao; -import racingcar.dao.raceresult.dto.RaceResultRegisterRequest; import racingcar.domain.Car; import racingcar.domain.RacingCars; +import racingcar.entity.RaceResultEntity; +import racingcar.service.dto.CarStatusResponse; +import racingcar.service.dto.GameInfoRequest; +import racingcar.service.dto.RaceResultResponse; import racingcar.service.mapper.RaceResultMapper; import racingcar.util.NumberGenerator; import racingcar.util.RandomNumberGenerator; +import java.time.LocalDateTime; import java.util.List; import java.util.Map; import java.util.stream.Collectors; @@ -55,8 +56,8 @@ void test_createRaceResult() throws Exception { final GameInfoRequest gameInfoRequest = new GameInfoRequest("a,b,c,d", 4); final int trialCount = gameInfoRequest.getCount(); - final RaceResultRegisterRequest raceResultRegisterRequest = - new RaceResultRegisterRequest(trialCount, "a,b,c"); + final RaceResultEntity raceResultEntity = + new RaceResultEntity(trialCount, "a,b,c", LocalDateTime.now()); final List carStatusResponses = List.of(new CarStatusResponse("a", 1), new CarStatusResponse("b", 1), @@ -65,7 +66,7 @@ void test_createRaceResult() throws Exception { //when when(raceResultMapper.mapToRaceResult(anyInt(), any())) - .thenReturn(raceResultRegisterRequest); + .thenReturn(raceResultEntity); when(raceResultDao.save(any())) .thenReturn(1); From 69faec4be67cee89b8bd101b815a55c00de3f30a Mon Sep 17 00:00:00 2001 From: java-saeng Date: Fri, 21 Apr 2023 15:27:55 +0900 Subject: [PATCH 22/36] =?UTF-8?q?feat=20:=20RaceResultDao=20=EC=A1=B0?= =?UTF-8?q?=ED=9A=8C=EC=97=90=EC=84=9C=20=EB=8F=84=EB=A9=94=EC=9D=B8?= =?UTF-8?q?=EC=9D=B4=20=EC=95=84=EB=8B=8C=20Entity=20=EC=82=AC=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dao/car/dto/CarRegisterRequest.java | 26 --------- .../dao/raceresult/RaceResultDao.java | 56 ++++++++----------- .../racingcar/service/RaceResultService.java | 23 ++++---- .../service/mapper/RaceResultMapper.java | 15 ++--- .../dao/raceresult/RaceResultDaoTest.java | 22 ++++++++ .../service/RaceResultServiceTest.java | 18 +++--- 6 files changed, 75 insertions(+), 85 deletions(-) delete mode 100644 src/main/java/racingcar/dao/car/dto/CarRegisterRequest.java diff --git a/src/main/java/racingcar/dao/car/dto/CarRegisterRequest.java b/src/main/java/racingcar/dao/car/dto/CarRegisterRequest.java deleted file mode 100644 index ffa9a0db9..000000000 --- a/src/main/java/racingcar/dao/car/dto/CarRegisterRequest.java +++ /dev/null @@ -1,26 +0,0 @@ -package racingcar.dao.car.dto; - -public class CarRegisterRequest { - - private final String name; - private final int position; - private final int playResultId; - - public CarRegisterRequest(final String name, final int position, final int playResultId) { - this.name = name; - this.position = position; - this.playResultId = playResultId; - } - - public String getName() { - return name; - } - - public int getPosition() { - return position; - } - - public int getPlayResultId() { - return playResultId; - } -} diff --git a/src/main/java/racingcar/dao/raceresult/RaceResultDao.java b/src/main/java/racingcar/dao/raceresult/RaceResultDao.java index daae84ff6..c764247d1 100644 --- a/src/main/java/racingcar/dao/raceresult/RaceResultDao.java +++ b/src/main/java/racingcar/dao/raceresult/RaceResultDao.java @@ -6,7 +6,7 @@ import org.springframework.jdbc.core.namedparam.SqlParameterSource; import org.springframework.jdbc.core.simple.SimpleJdbcInsert; import org.springframework.stereotype.Repository; -import racingcar.domain.Car; +import racingcar.entity.CarEntity; import racingcar.entity.RaceResultEntity; import java.sql.ResultSet; @@ -40,47 +40,37 @@ public int save(final RaceResultEntity raceResultEntity) { return jdbcInsert.executeAndReturnKey(params).intValue(); } - public Map> findAllRaceResult() { + public Map> findAllRaceResult() { final String sql = - "select r.winners, c.name, c.position" - + " from CAR c" - + " inner join RACE_RESULT r" + "select r.winners, c.name, c.position, c.created_at, c.race_result_id" + + " from RACE_RESULT r" + + " inner join Car c" + " on r.id = c.race_result_id;"; - RowMapper>> raceResultRowMapper = (resultSet, rowNum) -> { - Map> raceResult = new HashMap<>(); + return jdbcTemplate.query(sql, (ResultSet rs) -> { + Map> resultMap = new HashMap<>(); - while (hasNext(resultSet)) { - convertResultSetToRaceResult(resultSet, raceResult); - resultSet.next(); - } - - return raceResult; - }; - - return jdbcTemplate.queryForObject(sql, raceResultRowMapper); - } + while (rs.next()) { + final String winners = rs.getString("winners"); - private boolean hasNext(final ResultSet resultSet) throws SQLException { - return !resultSet.isAfterLast(); - } + final CarEntity carEntity = new CarEntity( + rs.getString("name"), + rs.getInt("position"), + rs.getInt("race_result_id"), + rs.getTimestamp("created_at") + .toLocalDateTime() + ); - private void convertResultSetToRaceResult(final ResultSet resultSet, final Map> raceResult) { - try { - final String winners = resultSet.getString("winners"); - final String name = resultSet.getString("name"); - final int position = resultSet.getInt("position"); + if (!resultMap.containsKey(winners)) { + resultMap.put(winners, new ArrayList<>()); + } - if (!raceResult.containsKey(winners)) { - raceResult.put(winners, new ArrayList<>()); + resultMap.get(winners) + .add(carEntity); } - raceResult.get(winners) - .add(new Car(name, position)); - - } catch (SQLException exception) { - throw new IllegalStateException("DB 조회 오류"); - } + return resultMap; + }); } } diff --git a/src/main/java/racingcar/service/RaceResultService.java b/src/main/java/racingcar/service/RaceResultService.java index 3e5a28f92..a54c55633 100644 --- a/src/main/java/racingcar/service/RaceResultService.java +++ b/src/main/java/racingcar/service/RaceResultService.java @@ -2,17 +2,16 @@ import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import racingcar.dao.raceresult.RaceResultDao; +import racingcar.domain.RacingCars; +import racingcar.entity.CarEntity; import racingcar.entity.RaceResultEntity; import racingcar.service.dto.CarStatusResponse; import racingcar.service.dto.GameInfoRequest; import racingcar.service.dto.RaceResultResponse; -import racingcar.dao.raceresult.RaceResultDao; -import racingcar.domain.Car; -import racingcar.domain.RacingCars; import racingcar.service.mapper.RaceResultMapper; import racingcar.util.NumberGenerator; -import java.time.LocalDateTime; import java.util.List; import java.util.Map; import java.util.stream.Collectors; @@ -40,14 +39,12 @@ public RaceResultResponse createRaceResult(final GameInfoRequest gameInfoRequest final int tryCount = gameInfoRequest.getCount(); final RacingCars racingCars = moveCars(names, tryCount); - - final RaceResultEntity raceResultEntity = raceResultMapper.mapToRaceResult(tryCount, racingCars); - + final RaceResultEntity raceResultEntity = raceResultMapper.mapToRaceResultEntity(tryCount, racingCars); final int savedId = raceResultDao.save(raceResultEntity); carService.registerCars(racingCars, savedId); - final List carStatusResponses = raceResultMapper.mapToCarStatus(racingCars); + final List carStatusResponses = raceResultMapper.mapToCarStatusResponseFrom(racingCars); return new RaceResultResponse(raceResultEntity.getWinners(), carStatusResponses); } @@ -58,14 +55,18 @@ private RacingCars moveCars(final String names, final int tryCount) { return racingCars; } + @Transactional(readOnly = true) public List searchRaceResult() { - final Map> allRaceResult = raceResultDao.findAllRaceResult(); + final Map> allRaceResult = raceResultDao.findAllRaceResult(); return allRaceResult.entrySet() .stream() - .map(it -> new RaceResultResponse(it.getKey(), - raceResultMapper.mapToCarStatus(it.getValue()))) + .map(it -> new RaceResultResponse( + it.getKey(), + raceResultMapper.mapToCarStatusResponseFrom(it.getValue())) + ) .collect(Collectors.toList()); } + } diff --git a/src/main/java/racingcar/service/mapper/RaceResultMapper.java b/src/main/java/racingcar/service/mapper/RaceResultMapper.java index 8f489ef51..fbc82c9b7 100644 --- a/src/main/java/racingcar/service/mapper/RaceResultMapper.java +++ b/src/main/java/racingcar/service/mapper/RaceResultMapper.java @@ -3,6 +3,7 @@ import org.springframework.stereotype.Component; import racingcar.domain.Car; import racingcar.domain.RacingCars; +import racingcar.entity.CarEntity; import racingcar.entity.RaceResultEntity; import racingcar.service.dto.CarStatusResponse; @@ -15,7 +16,7 @@ public class RaceResultMapper { private static final String CAR_NAMES_DELIMITER = ","; - public RaceResultEntity mapToRaceResult(final int tryCount, final RacingCars racingCars) { + public RaceResultEntity mapToRaceResultEntity(final int tryCount, final RacingCars racingCars) { return new RaceResultEntity(tryCount, String.join(CAR_NAMES_DELIMITER, mapToNameFrom(racingCars)), LocalDateTime.now()); @@ -28,7 +29,7 @@ private List mapToNameFrom(RacingCars racingCars) { .collect(Collectors.toList()); } - public List mapToCarStatus(final RacingCars racingCars) { + public List mapToCarStatusResponseFrom(final RacingCars racingCars) { return racingCars.getCars() .stream() .map(car -> new CarStatusResponse(car.getName(), @@ -36,10 +37,10 @@ public List mapToCarStatus(final RacingCars racingCars) { .collect(Collectors.toList()); } - public List mapToCarStatus(final List cars) { - return cars.stream() - .map(car -> new CarStatusResponse(car.getName(), - car.getPosition())) - .collect(Collectors.toList()); + public List mapToCarStatusResponseFrom(final List carEntities) { + return carEntities.stream() + .map(entity -> new CarStatusResponse(entity.getName(), + entity.getPosition())) + .collect(Collectors.toList()); } } diff --git a/src/test/java/racingcar/dao/raceresult/RaceResultDaoTest.java b/src/test/java/racingcar/dao/raceresult/RaceResultDaoTest.java index b2b4c1a08..335157d6e 100644 --- a/src/test/java/racingcar/dao/raceresult/RaceResultDaoTest.java +++ b/src/test/java/racingcar/dao/raceresult/RaceResultDaoTest.java @@ -1,15 +1,20 @@ package racingcar.dao.raceresult; +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.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.jdbc.JdbcTest; import org.springframework.jdbc.core.JdbcTemplate; +import racingcar.entity.CarEntity; import racingcar.entity.RaceResultEntity; import java.time.LocalDateTime; +import java.util.List; +import java.util.Map; +import static org.junit.jupiter.api.Assertions.assertAll; import static org.junit.jupiter.api.Assertions.assertEquals; @JdbcTest @@ -37,4 +42,21 @@ void test_save() throws Exception { //then assertEquals(savedId, 1); } + + @Test + @DisplayName("findAllRaceResult() : 전체 경기 결과를 조회할 수 있다.") + void test_findAllRaceResult() throws Exception { + //given + final String winner1 = "빙봉"; + final String winner2 = "a,b,c,d"; + + //when + final Map> allRaceResult = raceResultDao.findAllRaceResult(); + + //then + assertAll( + () -> Assertions.assertThat(allRaceResult).hasSize(2), + () -> Assertions.assertThat(allRaceResult).containsOnlyKeys(winner1, winner2) + ); + } } diff --git a/src/test/java/racingcar/service/RaceResultServiceTest.java b/src/test/java/racingcar/service/RaceResultServiceTest.java index fb94e7994..10cd92f90 100644 --- a/src/test/java/racingcar/service/RaceResultServiceTest.java +++ b/src/test/java/racingcar/service/RaceResultServiceTest.java @@ -4,8 +4,8 @@ import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import racingcar.dao.raceresult.RaceResultDao; -import racingcar.domain.Car; import racingcar.domain.RacingCars; +import racingcar.entity.CarEntity; import racingcar.entity.RaceResultEntity; import racingcar.service.dto.CarStatusResponse; import racingcar.service.dto.GameInfoRequest; @@ -65,7 +65,7 @@ void test_createRaceResult() throws Exception { new CarStatusResponse("d", 0)); //when - when(raceResultMapper.mapToRaceResult(anyInt(), any())) + when(raceResultMapper.mapToRaceResultEntity(anyInt(), any())) .thenReturn(raceResultEntity); when(raceResultDao.save(any())) @@ -74,7 +74,7 @@ void test_createRaceResult() throws Exception { doNothing().when(carService) .registerCars(any(), anyInt()); - when(raceResultMapper.mapToCarStatus((RacingCars) any())) + when(raceResultMapper.mapToCarStatusResponseFrom((RacingCars) any())) .thenReturn(carStatusResponses); final RaceResultResponse raceResult = raceResultService.createRaceResult(gameInfoRequest); @@ -96,21 +96,23 @@ void test_createRaceResult() throws Exception { @DisplayName("searchRaceResult() : 모든 경기 결과를 조회할 수 있다.") void test_searchRaceResult() throws Exception { //given - final List cars = List.of(new Car("a", 2), new Car("b", 3), - new Car("c", 1), new Car("d", 4)); + final List cars = + List.of(new CarEntity("a", 2, 1, LocalDateTime.now()), + new CarEntity("b", 3, 1, LocalDateTime.now()), + new CarEntity("c", 1, 1, LocalDateTime.now()), + new CarEntity("d", 4, 1, LocalDateTime.now())); - final Map> allRaceResult = Map.of("d", cars); + final Map> allRaceResult = Map.of("d", cars); final List carStatusResponses = List.of(new CarStatusResponse("a", 2), new CarStatusResponse("b", 3), new CarStatusResponse("c", 1), new CarStatusResponse("d", 4)); - //when when(raceResultDao.findAllRaceResult()) .thenReturn(allRaceResult); - when(raceResultMapper.mapToCarStatus((List) any())) + when(raceResultMapper.mapToCarStatusResponseFrom(cars)) .thenReturn(carStatusResponses); final List raceResultResponses = raceResultService.searchRaceResult(); From f8ae60597145db4794da49367f86a77b708997ef Mon Sep 17 00:00:00 2001 From: java-saeng Date: Fri, 21 Apr 2023 15:49:40 +0900 Subject: [PATCH 23/36] =?UTF-8?q?refactor=20:=20=ED=8C=A8=ED=82=A4?= =?UTF-8?q?=EC=A7=80=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/racingcar/dao/{car => }/CarDao.java | 2 +- .../java/racingcar/dao/{raceresult => }/RaceResultDao.java | 2 +- src/main/java/racingcar/service/CarService.java | 2 +- src/main/java/racingcar/service/RaceResultService.java | 2 +- src/test/java/racingcar/dao/{car => }/CarDaoTest.java | 3 ++- .../java/racingcar/dao/{raceresult => }/RaceResultDaoTest.java | 3 ++- src/test/java/racingcar/service/CarServiceIntegrationTest.java | 2 +- src/test/java/racingcar/service/CarServiceTest.java | 2 +- src/test/java/racingcar/service/RaceResultServiceTest.java | 2 +- 9 files changed, 11 insertions(+), 9 deletions(-) rename src/main/java/racingcar/dao/{car => }/CarDao.java (97%) rename src/main/java/racingcar/dao/{raceresult => }/RaceResultDao.java (98%) rename src/test/java/racingcar/dao/{car => }/CarDaoTest.java (95%) rename src/test/java/racingcar/dao/{raceresult => }/RaceResultDaoTest.java (96%) diff --git a/src/main/java/racingcar/dao/car/CarDao.java b/src/main/java/racingcar/dao/CarDao.java similarity index 97% rename from src/main/java/racingcar/dao/car/CarDao.java rename to src/main/java/racingcar/dao/CarDao.java index 82785e730..497c556ce 100644 --- a/src/main/java/racingcar/dao/car/CarDao.java +++ b/src/main/java/racingcar/dao/CarDao.java @@ -1,4 +1,4 @@ -package racingcar.dao.car; +package racingcar.dao; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.core.namedparam.SqlParameterSource; diff --git a/src/main/java/racingcar/dao/raceresult/RaceResultDao.java b/src/main/java/racingcar/dao/RaceResultDao.java similarity index 98% rename from src/main/java/racingcar/dao/raceresult/RaceResultDao.java rename to src/main/java/racingcar/dao/RaceResultDao.java index c764247d1..6fbf8362d 100644 --- a/src/main/java/racingcar/dao/raceresult/RaceResultDao.java +++ b/src/main/java/racingcar/dao/RaceResultDao.java @@ -1,4 +1,4 @@ -package racingcar.dao.raceresult; +package racingcar.dao; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.core.RowMapper; diff --git a/src/main/java/racingcar/service/CarService.java b/src/main/java/racingcar/service/CarService.java index 8c0481e47..c0f80d90d 100644 --- a/src/main/java/racingcar/service/CarService.java +++ b/src/main/java/racingcar/service/CarService.java @@ -1,7 +1,7 @@ package racingcar.service; import org.springframework.stereotype.Service; -import racingcar.dao.car.CarDao; +import racingcar.dao.CarDao; import racingcar.entity.CarEntity; import racingcar.domain.RacingCars; diff --git a/src/main/java/racingcar/service/RaceResultService.java b/src/main/java/racingcar/service/RaceResultService.java index a54c55633..b6e73dc3e 100644 --- a/src/main/java/racingcar/service/RaceResultService.java +++ b/src/main/java/racingcar/service/RaceResultService.java @@ -2,7 +2,7 @@ import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import racingcar.dao.raceresult.RaceResultDao; +import racingcar.dao.RaceResultDao; import racingcar.domain.RacingCars; import racingcar.entity.CarEntity; import racingcar.entity.RaceResultEntity; diff --git a/src/test/java/racingcar/dao/car/CarDaoTest.java b/src/test/java/racingcar/dao/CarDaoTest.java similarity index 95% rename from src/test/java/racingcar/dao/car/CarDaoTest.java rename to src/test/java/racingcar/dao/CarDaoTest.java index 2f263c258..8b78c457f 100644 --- a/src/test/java/racingcar/dao/car/CarDaoTest.java +++ b/src/test/java/racingcar/dao/CarDaoTest.java @@ -1,4 +1,4 @@ -package racingcar.dao.car; +package racingcar.dao; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; @@ -7,6 +7,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.jdbc.JdbcTest; import org.springframework.jdbc.core.JdbcTemplate; +import racingcar.dao.CarDao; import racingcar.entity.CarEntity; import java.time.LocalDateTime; diff --git a/src/test/java/racingcar/dao/raceresult/RaceResultDaoTest.java b/src/test/java/racingcar/dao/RaceResultDaoTest.java similarity index 96% rename from src/test/java/racingcar/dao/raceresult/RaceResultDaoTest.java rename to src/test/java/racingcar/dao/RaceResultDaoTest.java index 335157d6e..fc5f66673 100644 --- a/src/test/java/racingcar/dao/raceresult/RaceResultDaoTest.java +++ b/src/test/java/racingcar/dao/RaceResultDaoTest.java @@ -1,4 +1,4 @@ -package racingcar.dao.raceresult; +package racingcar.dao; import org.assertj.core.api.Assertions; import org.junit.jupiter.api.BeforeEach; @@ -7,6 +7,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.jdbc.JdbcTest; import org.springframework.jdbc.core.JdbcTemplate; +import racingcar.dao.RaceResultDao; import racingcar.entity.CarEntity; import racingcar.entity.RaceResultEntity; diff --git a/src/test/java/racingcar/service/CarServiceIntegrationTest.java b/src/test/java/racingcar/service/CarServiceIntegrationTest.java index 218588e60..57388d196 100644 --- a/src/test/java/racingcar/service/CarServiceIntegrationTest.java +++ b/src/test/java/racingcar/service/CarServiceIntegrationTest.java @@ -4,7 +4,7 @@ import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; -import racingcar.dao.car.CarDao; +import racingcar.dao.CarDao; import racingcar.domain.RacingCars; import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; diff --git a/src/test/java/racingcar/service/CarServiceTest.java b/src/test/java/racingcar/service/CarServiceTest.java index c0031f6b6..caa9080d3 100644 --- a/src/test/java/racingcar/service/CarServiceTest.java +++ b/src/test/java/racingcar/service/CarServiceTest.java @@ -3,7 +3,7 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; -import racingcar.dao.car.CarDao; +import racingcar.dao.CarDao; import racingcar.domain.RacingCars; import static org.mockito.ArgumentMatchers.any; diff --git a/src/test/java/racingcar/service/RaceResultServiceTest.java b/src/test/java/racingcar/service/RaceResultServiceTest.java index 10cd92f90..1add34195 100644 --- a/src/test/java/racingcar/service/RaceResultServiceTest.java +++ b/src/test/java/racingcar/service/RaceResultServiceTest.java @@ -3,7 +3,7 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; -import racingcar.dao.raceresult.RaceResultDao; +import racingcar.dao.RaceResultDao; import racingcar.domain.RacingCars; import racingcar.entity.CarEntity; import racingcar.entity.RaceResultEntity; From b1ab8d01a2f99c9a39b532d4336bf8bde9500441 Mon Sep 17 00:00:00 2001 From: java-saeng Date: Fri, 21 Apr 2023 19:17:28 +0900 Subject: [PATCH 24/36] =?UTF-8?q?refactor=20:=20CarDao=20SimpleJdbcInsert?= =?UTF-8?q?=20=EB=A5=BC=20=EC=9D=B8=EC=8A=A4=ED=84=B4=EC=8A=A4=20=ED=95=84?= =?UTF-8?q?=EB=93=9C=EB=A1=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/racingcar/dao/CarDao.java | 17 +++++-------- .../java/racingcar/dao/RaceResultDao.java | 24 +++++++------------ .../racingcar/entity/RaceResultEntity.java | 4 ++++ 3 files changed, 18 insertions(+), 27 deletions(-) diff --git a/src/main/java/racingcar/dao/CarDao.java b/src/main/java/racingcar/dao/CarDao.java index 497c556ce..e5b787115 100644 --- a/src/main/java/racingcar/dao/CarDao.java +++ b/src/main/java/racingcar/dao/CarDao.java @@ -1,7 +1,6 @@ package racingcar.dao; import org.springframework.jdbc.core.JdbcTemplate; -import org.springframework.jdbc.core.namedparam.SqlParameterSource; import org.springframework.jdbc.core.namedparam.SqlParameterSourceUtils; import org.springframework.jdbc.core.simple.SimpleJdbcInsert; import org.springframework.stereotype.Repository; @@ -12,20 +11,16 @@ @Repository public class CarDao { - private final JdbcTemplate jdbcTemplate; + private final SimpleJdbcInsert simpleJdbcInsert; public CarDao(final JdbcTemplate jdbcTemplate) { - this.jdbcTemplate = jdbcTemplate; + this.simpleJdbcInsert = + new SimpleJdbcInsert(jdbcTemplate) + .withTableName("CAR") + .usingGeneratedKeyColumns("id"); } public void save(final List carEntities) { - final SimpleJdbcInsert jdbcInsert = new SimpleJdbcInsert(jdbcTemplate); - - jdbcInsert.withTableName("CAR") - .usingGeneratedKeyColumns("id"); - - final SqlParameterSource[] batch = SqlParameterSourceUtils.createBatch(carEntities); - - jdbcInsert.executeBatch(batch); + simpleJdbcInsert.executeBatch(SqlParameterSourceUtils.createBatch(carEntities)); } } diff --git a/src/main/java/racingcar/dao/RaceResultDao.java b/src/main/java/racingcar/dao/RaceResultDao.java index 6fbf8362d..badbb66fc 100644 --- a/src/main/java/racingcar/dao/RaceResultDao.java +++ b/src/main/java/racingcar/dao/RaceResultDao.java @@ -1,17 +1,13 @@ package racingcar.dao; import org.springframework.jdbc.core.JdbcTemplate; -import org.springframework.jdbc.core.RowMapper; -import org.springframework.jdbc.core.namedparam.MapSqlParameterSource; -import org.springframework.jdbc.core.namedparam.SqlParameterSource; +import org.springframework.jdbc.core.namedparam.BeanPropertySqlParameterSource; import org.springframework.jdbc.core.simple.SimpleJdbcInsert; import org.springframework.stereotype.Repository; import racingcar.entity.CarEntity; import racingcar.entity.RaceResultEntity; import java.sql.ResultSet; -import java.sql.SQLException; -import java.time.LocalDateTime; import java.util.ArrayList; import java.util.HashMap; import java.util.List; @@ -21,23 +17,19 @@ public class RaceResultDao { private final JdbcTemplate jdbcTemplate; + private final SimpleJdbcInsert simpleJdbcInsert; public RaceResultDao(final JdbcTemplate jdbcTemplate) { this.jdbcTemplate = jdbcTemplate; + this.simpleJdbcInsert = + new SimpleJdbcInsert(jdbcTemplate) + .withTableName("RACE_RESULT") + .usingGeneratedKeyColumns("id"); } public int save(final RaceResultEntity raceResultEntity) { - - final SimpleJdbcInsert jdbcInsert = new SimpleJdbcInsert(jdbcTemplate); - - jdbcInsert.withTableName("RACE_RESULT").usingGeneratedKeyColumns("id"); - - final SqlParameterSource params = new MapSqlParameterSource() - .addValue("trial_count", raceResultEntity.getTrialCount()) - .addValue("winners", raceResultEntity.getWinners()) - .addValue("created_at", LocalDateTime.now()); - - return jdbcInsert.executeAndReturnKey(params).intValue(); + return simpleJdbcInsert.executeAndReturnKey(new BeanPropertySqlParameterSource(raceResultEntity)) + .intValue(); } public Map> findAllRaceResult() { diff --git a/src/main/java/racingcar/entity/RaceResultEntity.java b/src/main/java/racingcar/entity/RaceResultEntity.java index e7e7143a6..d7bc39766 100644 --- a/src/main/java/racingcar/entity/RaceResultEntity.java +++ b/src/main/java/racingcar/entity/RaceResultEntity.java @@ -29,4 +29,8 @@ public int getTrialCount() { public String getWinners() { return winners; } + + public LocalDateTime getCreatedAt() { + return createdAt; + } } From e310a1f1f655d6fe3196aaf8a3247e9efb3a6d9f Mon Sep 17 00:00:00 2001 From: java-saeng Date: Fri, 21 Apr 2023 19:26:56 +0900 Subject: [PATCH 25/36] =?UTF-8?q?feat=20:=20CarEntity=EB=A1=9C=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD=EC=8B=9C=EC=BC=9C=EC=A3=BC=EB=8A=94=20CarMapper=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/racingcar/service/CarService.java | 18 ++++------- .../racingcar/service/mapper/CarMapper.java | 30 +++++++++++++++++++ .../racingcar/service/CarServiceTest.java | 19 +++++++++++- 3 files changed, 54 insertions(+), 13 deletions(-) create mode 100644 src/main/java/racingcar/service/mapper/CarMapper.java diff --git a/src/main/java/racingcar/service/CarService.java b/src/main/java/racingcar/service/CarService.java index c0f80d90d..24cdc4e1f 100644 --- a/src/main/java/racingcar/service/CarService.java +++ b/src/main/java/racingcar/service/CarService.java @@ -2,31 +2,25 @@ import org.springframework.stereotype.Service; import racingcar.dao.CarDao; -import racingcar.entity.CarEntity; import racingcar.domain.RacingCars; +import racingcar.entity.CarEntity; +import racingcar.service.mapper.CarMapper; -import java.time.LocalDateTime; import java.util.List; -import java.util.stream.Collectors; @Service public class CarService { private final CarDao carDao; + private final CarMapper carMapper; - public CarService(final CarDao carDao) { + public CarService(final CarDao carDao, final CarMapper carMapper) { this.carDao = carDao; + this.carMapper = carMapper; } public void registerCars(final RacingCars racingCars, final Integer savedId) { - final List requests = - racingCars.getCars() - .stream() - .map(it -> new CarEntity(it.getName(), - it.getPosition(), - savedId, - LocalDateTime.now())) - .collect(Collectors.toList()); + final List requests = carMapper.mapToCarEntitiesFrom(racingCars, savedId); carDao.save(requests); } diff --git a/src/main/java/racingcar/service/mapper/CarMapper.java b/src/main/java/racingcar/service/mapper/CarMapper.java new file mode 100644 index 000000000..fbc72a720 --- /dev/null +++ b/src/main/java/racingcar/service/mapper/CarMapper.java @@ -0,0 +1,30 @@ +package racingcar.service.mapper; + +import org.springframework.stereotype.Component; +import racingcar.domain.Car; +import racingcar.domain.RacingCars; +import racingcar.entity.CarEntity; + +import java.time.LocalDateTime; +import java.util.List; +import java.util.stream.Collectors; + +@Component +public class CarMapper { + + public List mapToCarEntitiesFrom(final RacingCars racingCars, + final Integer savedId) { + + return racingCars.getCars() + .stream() + .map(it -> mapToCarEntity(savedId, it)) + .collect(Collectors.toList()); + } + + private CarEntity mapToCarEntity(final Integer savedId, final Car it) { + return new CarEntity(it.getName(), + it.getPosition(), + savedId, + LocalDateTime.now()); + } +} diff --git a/src/test/java/racingcar/service/CarServiceTest.java b/src/test/java/racingcar/service/CarServiceTest.java index caa9080d3..832133258 100644 --- a/src/test/java/racingcar/service/CarServiceTest.java +++ b/src/test/java/racingcar/service/CarServiceTest.java @@ -5,11 +5,17 @@ import org.junit.jupiter.api.Test; import racingcar.dao.CarDao; import racingcar.domain.RacingCars; +import racingcar.entity.CarEntity; +import racingcar.service.mapper.CarMapper; + +import java.time.LocalDateTime; +import java.util.List; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; @DisplayName("CarService Unit Test") class CarServiceTest { @@ -17,12 +23,14 @@ class CarServiceTest { private CarService carService; private CarDao carDao; + private CarMapper carMapper; @BeforeEach void init() { carDao = mock(CarDao.class); + carMapper = mock(CarMapper.class); - carService = new CarService(carDao); + carService = new CarService(carDao, carMapper); } @Test @@ -32,7 +40,16 @@ void test_registerCars() throws Exception { final RacingCars racingCars = RacingCars.makeCars("a,b,c"); final int savedRaceResultId = 1; + final List carEntities = List.of( + new CarEntity("a", 3, 1, LocalDateTime.now()), + new CarEntity("b", 3, 1, LocalDateTime.now()), + new CarEntity("c", 3, 1, LocalDateTime.now()) + ); + //when + when(carMapper.mapToCarEntitiesFrom(racingCars, savedRaceResultId)) + .thenReturn(carEntities); + carService.registerCars(racingCars, savedRaceResultId); //then From 994ac586a1e2c3b5099cb50d0cfb0139ac0df39b Mon Sep 17 00:00:00 2001 From: java-saeng Date: Fri, 21 Apr 2023 19:45:27 +0900 Subject: [PATCH 26/36] =?UTF-8?q?refactor=20:=20=EB=AA=A8=EB=93=A0=20?= =?UTF-8?q?=EC=9D=B8=EC=8A=A4=ED=84=B4=EC=8A=A4=20=ED=95=84=EB=93=9C?= =?UTF-8?q?=EB=A5=BC=20=EB=8B=B4=EA=B3=A0=20=EC=9E=88=EB=8A=94=20=EC=83=9D?= =?UTF-8?q?=EC=84=B1=EC=9E=90=20=EC=A0=91=EA=B7=BC=20=EC=A0=9C=EC=96=B4?= =?UTF-8?q?=EC=9E=90=EB=A5=BC=20public=20->=20private=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/racingcar/entity/CarEntity.java | 2 +- src/main/java/racingcar/entity/RaceResultEntity.java | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/racingcar/entity/CarEntity.java b/src/main/java/racingcar/entity/CarEntity.java index da3aeb21e..e14413509 100644 --- a/src/main/java/racingcar/entity/CarEntity.java +++ b/src/main/java/racingcar/entity/CarEntity.java @@ -10,7 +10,7 @@ public class CarEntity { private final Integer raceResultId; private final LocalDateTime createdAt; - public CarEntity(final Integer id, final String name, + private CarEntity(final Integer id, final String name, final int position, final Integer raceResultId, final LocalDateTime createdAt) { this.id = id; diff --git a/src/main/java/racingcar/entity/RaceResultEntity.java b/src/main/java/racingcar/entity/RaceResultEntity.java index d7bc39766..3e4064368 100644 --- a/src/main/java/racingcar/entity/RaceResultEntity.java +++ b/src/main/java/racingcar/entity/RaceResultEntity.java @@ -9,8 +9,8 @@ public class RaceResultEntity { private final String winners; private final LocalDateTime createdAt; - public RaceResultEntity(final Long id, final int trialCount, - final String winners, final LocalDateTime createdAt) { + private RaceResultEntity(final Long id, final int trialCount, + final String winners, final LocalDateTime createdAt) { this.id = id; this.trialCount = trialCount; this.winners = winners; From 12317c49cfb28c058a8d6a3730f047a8a51f9c8c Mon Sep 17 00:00:00 2001 From: java-saeng Date: Fri, 21 Apr 2023 20:33:22 +0900 Subject: [PATCH 27/36] =?UTF-8?q?feat=20:=20domain=20=EB=93=A4=EC=9D=98=20?= =?UTF-8?q?=EC=9B=80=EC=A7=81=EC=9E=84=EA=B3=BC=20=EA=B2=B0=EA=B3=BC?= =?UTF-8?q?=EB=A5=BC=20=EB=AA=85=EB=A0=B9=ED=95=98=EB=8A=94=20RacingGame?= =?UTF-8?q?=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/racingcar/domain/RacingGame.java | 42 +++++++++++++++++++ .../racingcar/service/RaceResultService.java | 25 +++++------ .../service/mapper/RaceResultMapper.java | 16 +++---- .../service/RaceResultServiceTest.java | 23 ++++++---- 4 files changed, 75 insertions(+), 31 deletions(-) create mode 100644 src/main/java/racingcar/domain/RacingGame.java diff --git a/src/main/java/racingcar/domain/RacingGame.java b/src/main/java/racingcar/domain/RacingGame.java new file mode 100644 index 000000000..60bd9303a --- /dev/null +++ b/src/main/java/racingcar/domain/RacingGame.java @@ -0,0 +1,42 @@ +package racingcar.domain; + +import racingcar.util.NumberGenerator; + +import java.util.List; + +public class RacingGame { + + private final RacingCars racingCars; + private final NumberGenerator numberGenerator; + private final Integer trialCount; + + public RacingGame(final RacingCars racingCars, final NumberGenerator numberGenerator, final Integer trialCount) { + this.racingCars = racingCars; + this.numberGenerator = numberGenerator; + this.trialCount = trialCount; + } + + public static RacingGame readyToRacingGame( + final String names, + final NumberGenerator numberGenerator, + final Integer trialCount + ) { + return new RacingGame(RacingCars.makeCars(names), numberGenerator, trialCount); + } + + public void play() { + racingCars.moveAllCars(trialCount, numberGenerator); + } + + public List determineWinners() { + return racingCars.getWinners(); + } + + public List getParticipantAllCar() { + return racingCars.getCars(); + } + + public Integer getTrialCount() { + return trialCount; + } +} diff --git a/src/main/java/racingcar/service/RaceResultService.java b/src/main/java/racingcar/service/RaceResultService.java index b6e73dc3e..74e970faa 100644 --- a/src/main/java/racingcar/service/RaceResultService.java +++ b/src/main/java/racingcar/service/RaceResultService.java @@ -3,7 +3,7 @@ import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import racingcar.dao.RaceResultDao; -import racingcar.domain.RacingCars; +import racingcar.domain.RacingGame; import racingcar.entity.CarEntity; import racingcar.entity.RaceResultEntity; import racingcar.service.dto.CarStatusResponse; @@ -35,26 +35,24 @@ public RaceResultService(final RaceResultDao raceResultDao, final CarService car @Transactional public RaceResultResponse createRaceResult(final GameInfoRequest gameInfoRequest) { - final String names = gameInfoRequest.getNames(); - final int tryCount = gameInfoRequest.getCount(); + final RacingGame racingGame = RacingGame.readyToRacingGame( + gameInfoRequest.getNames(), + numberGenerator, + gameInfoRequest.getCount() + ); + racingGame.play(); - final RacingCars racingCars = moveCars(names, tryCount); - final RaceResultEntity raceResultEntity = raceResultMapper.mapToRaceResultEntity(tryCount, racingCars); + final RaceResultEntity raceResultEntity = raceResultMapper.mapToRaceResultEntity(racingGame); final int savedId = raceResultDao.save(raceResultEntity); - carService.registerCars(racingCars, savedId); + carService.registerCars(racingGame, savedId); - final List carStatusResponses = raceResultMapper.mapToCarStatusResponseFrom(racingCars); + final List carStatusResponses = + raceResultMapper.mapToCarStatusResponseFrom(racingGame); return new RaceResultResponse(raceResultEntity.getWinners(), carStatusResponses); } - private RacingCars moveCars(final String names, final int tryCount) { - final RacingCars racingCars = RacingCars.makeCars(names); - racingCars.moveAllCars(tryCount, numberGenerator); - return racingCars; - } - @Transactional(readOnly = true) public List searchRaceResult() { @@ -68,5 +66,4 @@ public List searchRaceResult() { ) .collect(Collectors.toList()); } - } diff --git a/src/main/java/racingcar/service/mapper/RaceResultMapper.java b/src/main/java/racingcar/service/mapper/RaceResultMapper.java index fbc82c9b7..7802e4afb 100644 --- a/src/main/java/racingcar/service/mapper/RaceResultMapper.java +++ b/src/main/java/racingcar/service/mapper/RaceResultMapper.java @@ -2,7 +2,7 @@ import org.springframework.stereotype.Component; import racingcar.domain.Car; -import racingcar.domain.RacingCars; +import racingcar.domain.RacingGame; import racingcar.entity.CarEntity; import racingcar.entity.RaceResultEntity; import racingcar.service.dto.CarStatusResponse; @@ -16,21 +16,21 @@ public class RaceResultMapper { private static final String CAR_NAMES_DELIMITER = ","; - public RaceResultEntity mapToRaceResultEntity(final int tryCount, final RacingCars racingCars) { - return new RaceResultEntity(tryCount, - String.join(CAR_NAMES_DELIMITER, mapToNameFrom(racingCars)), + public RaceResultEntity mapToRaceResultEntity(final RacingGame racingGame) { + return new RaceResultEntity(racingGame.getTrialCount(), + String.join(CAR_NAMES_DELIMITER, mapToWinnersNameFrom(racingGame)), LocalDateTime.now()); } - private List mapToNameFrom(RacingCars racingCars) { - return racingCars.getWinners() + private List mapToWinnersNameFrom(final RacingGame racingGame) { + return racingGame.determineWinners() .stream() .map(Car::getName) .collect(Collectors.toList()); } - public List mapToCarStatusResponseFrom(final RacingCars racingCars) { - return racingCars.getCars() + public List mapToCarStatusResponseFrom(final RacingGame racingGame) { + return racingGame.getParticipantAllCar() .stream() .map(car -> new CarStatusResponse(car.getName(), car.getPosition())) diff --git a/src/test/java/racingcar/service/RaceResultServiceTest.java b/src/test/java/racingcar/service/RaceResultServiceTest.java index 1add34195..b0f7d25bb 100644 --- a/src/test/java/racingcar/service/RaceResultServiceTest.java +++ b/src/test/java/racingcar/service/RaceResultServiceTest.java @@ -5,6 +5,7 @@ import org.junit.jupiter.api.Test; import racingcar.dao.RaceResultDao; import racingcar.domain.RacingCars; +import racingcar.domain.RacingGame; import racingcar.entity.CarEntity; import racingcar.entity.RaceResultEntity; import racingcar.service.dto.CarStatusResponse; @@ -45,8 +46,11 @@ void dataInit() { raceResultDao = mock(RaceResultDao.class); numberGenerator = new RandomNumberGenerator(); - raceResultService = new RaceResultService(raceResultDao, carService, - numberGenerator, raceResultMapper); + raceResultService = + new RaceResultService( + raceResultDao, carService, + numberGenerator, raceResultMapper + ); } @Test @@ -59,22 +63,23 @@ void test_createRaceResult() throws Exception { final RaceResultEntity raceResultEntity = new RaceResultEntity(trialCount, "a,b,c", LocalDateTime.now()); - final List carStatusResponses = List.of(new CarStatusResponse("a", 1), - new CarStatusResponse("b", 1), - new CarStatusResponse("c", 1), - new CarStatusResponse("d", 0)); + final List carStatusResponses = + List.of(new CarStatusResponse("a", 1), + new CarStatusResponse("b", 1), + new CarStatusResponse("c", 1), + new CarStatusResponse("d", 0)); //when - when(raceResultMapper.mapToRaceResultEntity(anyInt(), any())) + when(raceResultMapper.mapToRaceResultEntity(any())) .thenReturn(raceResultEntity); when(raceResultDao.save(any())) .thenReturn(1); doNothing().when(carService) - .registerCars(any(), anyInt()); + .registerCars((RacingCars) any(), anyInt()); - when(raceResultMapper.mapToCarStatusResponseFrom((RacingCars) any())) + when(raceResultMapper.mapToCarStatusResponseFrom((RacingGame) any())) .thenReturn(carStatusResponses); final RaceResultResponse raceResult = raceResultService.createRaceResult(gameInfoRequest); From 000d97b06b362863012fe48a924f96a79addde28 Mon Sep 17 00:00:00 2001 From: java-saeng Date: Fri, 21 Apr 2023 20:43:05 +0900 Subject: [PATCH 28/36] =?UTF-8?q?feat=20:=20RacingGame=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80=EC=97=90=20=EB=94=B0=EB=A5=B8=20CarService=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/racingcar/service/CarService.java | 9 +++++---- .../java/racingcar/service/mapper/CarMapper.java | 12 ++++++------ .../service/CarServiceIntegrationTest.java | 15 ++++++++------- .../java/racingcar/service/CarServiceTest.java | 13 +++++++++---- .../racingcar/service/RaceResultServiceTest.java | 2 +- 5 files changed, 29 insertions(+), 22 deletions(-) diff --git a/src/main/java/racingcar/service/CarService.java b/src/main/java/racingcar/service/CarService.java index 24cdc4e1f..944076742 100644 --- a/src/main/java/racingcar/service/CarService.java +++ b/src/main/java/racingcar/service/CarService.java @@ -2,7 +2,7 @@ import org.springframework.stereotype.Service; import racingcar.dao.CarDao; -import racingcar.domain.RacingCars; +import racingcar.domain.RacingGame; import racingcar.entity.CarEntity; import racingcar.service.mapper.CarMapper; @@ -19,9 +19,10 @@ public CarService(final CarDao carDao, final CarMapper carMapper) { this.carMapper = carMapper; } - public void registerCars(final RacingCars racingCars, final Integer savedId) { - final List requests = carMapper.mapToCarEntitiesFrom(racingCars, savedId); + public void registerCars(final RacingGame racingGame, final Integer savedId) { + final List carEntities = + carMapper.mapToCarEntitiesFrom(racingGame, savedId); - carDao.save(requests); + carDao.save(carEntities); } } diff --git a/src/main/java/racingcar/service/mapper/CarMapper.java b/src/main/java/racingcar/service/mapper/CarMapper.java index fbc72a720..000cd7641 100644 --- a/src/main/java/racingcar/service/mapper/CarMapper.java +++ b/src/main/java/racingcar/service/mapper/CarMapper.java @@ -2,7 +2,7 @@ import org.springframework.stereotype.Component; import racingcar.domain.Car; -import racingcar.domain.RacingCars; +import racingcar.domain.RacingGame; import racingcar.entity.CarEntity; import java.time.LocalDateTime; @@ -12,18 +12,18 @@ @Component public class CarMapper { - public List mapToCarEntitiesFrom(final RacingCars racingCars, + public List mapToCarEntitiesFrom(final RacingGame racingGame, final Integer savedId) { - return racingCars.getCars() + return racingGame.getParticipantAllCar() .stream() .map(it -> mapToCarEntity(savedId, it)) .collect(Collectors.toList()); } - private CarEntity mapToCarEntity(final Integer savedId, final Car it) { - return new CarEntity(it.getName(), - it.getPosition(), + private CarEntity mapToCarEntity(final Integer savedId, final Car car) { + return new CarEntity(car.getName(), + car.getPosition(), savedId, LocalDateTime.now()); } diff --git a/src/test/java/racingcar/service/CarServiceIntegrationTest.java b/src/test/java/racingcar/service/CarServiceIntegrationTest.java index 57388d196..52f2a7a63 100644 --- a/src/test/java/racingcar/service/CarServiceIntegrationTest.java +++ b/src/test/java/racingcar/service/CarServiceIntegrationTest.java @@ -4,8 +4,8 @@ import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; -import racingcar.dao.CarDao; -import racingcar.domain.RacingCars; +import racingcar.domain.RacingGame; +import racingcar.util.RandomNumberGenerator; import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; @@ -13,9 +13,6 @@ @SpringBootTest class CarServiceIntegrationTest { - @Autowired - private CarDao carDao; - @Autowired private CarService carService; @@ -23,10 +20,14 @@ class CarServiceIntegrationTest { @DisplayName("registerCars() : 자동차들을 저장합니다.") void test_registerCars() throws Exception { //given - final RacingCars racingCars = RacingCars.makeCars("a,b,c"); + final int trialCount = 2; + final RacingGame racingGame = + RacingGame.readyToRacingGame("a,b,c", + new RandomNumberGenerator(), + trialCount); final int savedRaceResultId = 3; //when & then - assertDoesNotThrow(() -> carService.registerCars(racingCars, savedRaceResultId)); + assertDoesNotThrow(() -> carService.registerCars(racingGame, savedRaceResultId)); } } diff --git a/src/test/java/racingcar/service/CarServiceTest.java b/src/test/java/racingcar/service/CarServiceTest.java index 832133258..ace3f8bd4 100644 --- a/src/test/java/racingcar/service/CarServiceTest.java +++ b/src/test/java/racingcar/service/CarServiceTest.java @@ -4,14 +4,16 @@ import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import racingcar.dao.CarDao; -import racingcar.domain.RacingCars; +import racingcar.domain.RacingGame; import racingcar.entity.CarEntity; import racingcar.service.mapper.CarMapper; +import racingcar.util.RandomNumberGenerator; import java.time.LocalDateTime; import java.util.List; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; @@ -37,7 +39,10 @@ void init() { @DisplayName("registerCars() : 자동차들을 저장합니다.") void test_registerCars() throws Exception { //given - final RacingCars racingCars = RacingCars.makeCars("a,b,c"); + final RacingGame racingGame = + RacingGame.readyToRacingGame("a,b,c", + new RandomNumberGenerator(), + 2); final int savedRaceResultId = 1; final List carEntities = List.of( @@ -47,10 +52,10 @@ void test_registerCars() throws Exception { ); //when - when(carMapper.mapToCarEntitiesFrom(racingCars, savedRaceResultId)) + when(carMapper.mapToCarEntitiesFrom(any(), anyInt())) .thenReturn(carEntities); - carService.registerCars(racingCars, savedRaceResultId); + carService.registerCars(racingGame, savedRaceResultId); //then verify(carDao, times(1)).save(any()); diff --git a/src/test/java/racingcar/service/RaceResultServiceTest.java b/src/test/java/racingcar/service/RaceResultServiceTest.java index b0f7d25bb..aaf5ede85 100644 --- a/src/test/java/racingcar/service/RaceResultServiceTest.java +++ b/src/test/java/racingcar/service/RaceResultServiceTest.java @@ -77,7 +77,7 @@ void test_createRaceResult() throws Exception { .thenReturn(1); doNothing().when(carService) - .registerCars((RacingCars) any(), anyInt()); + .registerCars(any(), anyInt()); when(raceResultMapper.mapToCarStatusResponseFrom((RacingGame) any())) .thenReturn(carStatusResponses); From 87f96b818a5a34f54c5ac6f4762c1904a98b8190 Mon Sep 17 00:00:00 2001 From: java-saeng Date: Fri, 21 Apr 2023 21:35:08 +0900 Subject: [PATCH 29/36] =?UTF-8?q?feat=20:=20java=20=EC=97=90=EC=84=9C=20?= =?UTF-8?q?=EC=A0=9C=EA=B3=B5=ED=95=98=EB=8A=94=20=EA=B8=B0=EB=B3=B8=20exc?= =?UTF-8?q?eption=20=EC=97=90=20=EB=8C=80=ED=95=9C=20handling=20method=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../exception/ExceptionHandlerController.java | 10 ++++++++++ .../response/DefaultExceptionResponse.java | 18 ++++++++++++++++++ .../racingcar/service/RaceResultService.java | 1 - 3 files changed, 28 insertions(+), 1 deletion(-) create mode 100644 src/main/java/racingcar/global/exception/response/DefaultExceptionResponse.java diff --git a/src/main/java/racingcar/global/exception/ExceptionHandlerController.java b/src/main/java/racingcar/global/exception/ExceptionHandlerController.java index 7cc0f8fe9..059419fba 100644 --- a/src/main/java/racingcar/global/exception/ExceptionHandlerController.java +++ b/src/main/java/racingcar/global/exception/ExceptionHandlerController.java @@ -1,10 +1,12 @@ package racingcar.global.exception; import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; import org.springframework.web.bind.MethodArgumentNotValidException; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.ResponseStatus; import org.springframework.web.bind.annotation.RestControllerAdvice; +import racingcar.global.exception.response.DefaultExceptionResponse; import racingcar.global.exception.response.ExceptionResponse; @RestControllerAdvice @@ -17,4 +19,12 @@ public ExceptionResponse handleMethodArgumentNotValidException( ) { return ExceptionResponse.of(ExceptionStatus.INVALID_INPUT_VALUE_EXCEPTION, exception); } + + @ExceptionHandler({IllegalArgumentException.class, IllegalStateException.class}) + public ResponseEntity handleDefaultException( + Exception exception + ) { + return ResponseEntity.badRequest() + .body(DefaultExceptionResponse.from(exception)); + } } diff --git a/src/main/java/racingcar/global/exception/response/DefaultExceptionResponse.java b/src/main/java/racingcar/global/exception/response/DefaultExceptionResponse.java new file mode 100644 index 000000000..ac45b9031 --- /dev/null +++ b/src/main/java/racingcar/global/exception/response/DefaultExceptionResponse.java @@ -0,0 +1,18 @@ +package racingcar.global.exception.response; + +public class DefaultExceptionResponse { + + private final String message; + + private DefaultExceptionResponse(final String message) { + this.message = message; + } + + public static DefaultExceptionResponse from(Exception exception) { + return new DefaultExceptionResponse(exception.getMessage()); + } + + public String getMessage() { + return message; + } +} diff --git a/src/main/java/racingcar/service/RaceResultService.java b/src/main/java/racingcar/service/RaceResultService.java index 74e970faa..a3a283a25 100644 --- a/src/main/java/racingcar/service/RaceResultService.java +++ b/src/main/java/racingcar/service/RaceResultService.java @@ -44,7 +44,6 @@ public RaceResultResponse createRaceResult(final GameInfoRequest gameInfoRequest final RaceResultEntity raceResultEntity = raceResultMapper.mapToRaceResultEntity(racingGame); final int savedId = raceResultDao.save(raceResultEntity); - carService.registerCars(racingGame, savedId); final List carStatusResponses = From 4f7b056010e160432cd17b45ae955599cdb6e2b9 Mon Sep 17 00:00:00 2001 From: java-saeng Date: Fri, 21 Apr 2023 21:58:40 +0900 Subject: [PATCH 30/36] =?UTF-8?q?refactor=20:=20Random=20=EB=8C=80?= =?UTF-8?q?=EC=8B=A0=20ThreadLocalRandom=20=EC=82=AC=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/racingcar/util/RandomNumberGenerator.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/main/java/racingcar/util/RandomNumberGenerator.java b/src/main/java/racingcar/util/RandomNumberGenerator.java index a6fbe9be0..f24cb036d 100644 --- a/src/main/java/racingcar/util/RandomNumberGenerator.java +++ b/src/main/java/racingcar/util/RandomNumberGenerator.java @@ -2,16 +2,17 @@ import org.springframework.stereotype.Component; -import java.util.Random; +import java.util.concurrent.ThreadLocalRandom; @Component public class RandomNumberGenerator implements NumberGenerator { private static final int MAX_RANDOM_INT = 10; + private static final ThreadLocalRandom random = ThreadLocalRandom.current(); + @Override public int generate() { - Random random = new Random(); return random.nextInt(MAX_RANDOM_INT); } } From 95aebb395c0a7cf02408906d4968c608d34b7d30 Mon Sep 17 00:00:00 2001 From: java-saeng Date: Sat, 22 Apr 2023 13:52:22 +0900 Subject: [PATCH 31/36] =?UTF-8?q?feat=20:=20Car=EA=B0=80=20=EC=9A=B0?= =?UTF-8?q?=EC=8A=B9=20=EC=97=AC=EB=B6=80=EB=A5=BC=20=EA=B0=80=EC=A7=80?= =?UTF-8?q?=EA=B3=A0=20=EC=9E=88=EA=B2=8C=20=ED=85=8C=EC=9D=B4=EB=B8=94=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/resources/sql/data.sql | 16 ++++++++-------- src/main/resources/sql/schema.sql | 6 +++--- src/test/resources/sql/data.sql | 16 ++++++++-------- src/test/resources/sql/schema.sql | 10 +++++----- 4 files changed, 24 insertions(+), 24 deletions(-) diff --git a/src/main/resources/sql/data.sql b/src/main/resources/sql/data.sql index 9f9919b73..c153995b6 100644 --- a/src/main/resources/sql/data.sql +++ b/src/main/resources/sql/data.sql @@ -1,17 +1,17 @@ insert into RACE_RESULT -values (1, 10, '빙봉', current_timestamp); +values (1, 10, current_timestamp); insert into CAR -values (1, '우르', 9, 1, current_timestamp); +values (1, '우르', 9, 1, 1, current_timestamp); insert into CAR -values (2, '빙봉', 10, 1, current_timestamp); +values (2, '빙봉', 10, 1, 0, current_timestamp); insert into RACE_RESULT -values (2, 5, 'a,b,c,d', current_timestamp); +values (2, 5, current_timestamp); insert into CAR -values (3, 'a', 5, 2, current_timestamp); +values (3, 'a', 5, 2, 1, current_timestamp); insert into CAR -values (4, 'b', 5, 2, current_timestamp); +values (4, 'b', 5, 2, 1, current_timestamp); insert into CAR -values (5, 'c', 5, 2, current_timestamp); +values (5, 'c', 5, 2, 1, current_timestamp); insert into CAR -values (6, 'd', 5, 2, current_timestamp); +values (6, 'd', 5, 2, 1, current_timestamp); diff --git a/src/main/resources/sql/schema.sql b/src/main/resources/sql/schema.sql index 713f00103..d397cdf6b 100644 --- a/src/main/resources/sql/schema.sql +++ b/src/main/resources/sql/schema.sql @@ -1,9 +1,8 @@ CREATE TABLE RACE_RESULT ( id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, - trial_count INT NOT NULL, - winners VARCHAR(255) NOT NULL, - created_at DATETIME NOT NULL, + trial_count INT NOT NULL, + created_at DATETIME NOT NULL, PRIMARY KEY (id) ); @@ -13,6 +12,7 @@ CREATE TABLE CAR name VARCHAR(50) NOT NULL, position INT NOT NULL, race_result_id BIGINT NOT NULL, + winner BOOL NOT NULL, created_at DATETIME NOT NULL, PRIMARY KEY (id) ); diff --git a/src/test/resources/sql/data.sql b/src/test/resources/sql/data.sql index f9b569224..e5698261e 100644 --- a/src/test/resources/sql/data.sql +++ b/src/test/resources/sql/data.sql @@ -1,17 +1,17 @@ insert into RACE_RESULT -values (3, 10, '빙봉', current_timestamp); +values (3, 10, current_timestamp); insert into CAR -values (3, '우르', 9, 3, current_timestamp); +values (4, '우르', 9, 1, 0, current_timestamp); insert into CAR -values (4, '빙봉', 10, 3, current_timestamp); +values (5, '빙봉', 10, 1, 1, current_timestamp); insert into RACE_RESULT -values (4, 5, 'a,b,c,d', current_timestamp); +values (4, 5, current_timestamp); insert into CAR -values (5, 'a', 5, 4, current_timestamp); +values (6, 'a', 5, 2, 1, current_timestamp); insert into CAR -values (6, 'b', 5, 4, current_timestamp); +values (7, 'b', 5, 2, 1, current_timestamp); insert into CAR -values (7, 'c', 5, 4, current_timestamp); +values (8, 'c', 5, 2, 1, current_timestamp); insert into CAR -values (8, 'd', 5, 4, current_timestamp); +values (9, 'd', 5, 2, 1, current_timestamp); diff --git a/src/test/resources/sql/schema.sql b/src/test/resources/sql/schema.sql index 1338f1555..61215b7ae 100644 --- a/src/test/resources/sql/schema.sql +++ b/src/test/resources/sql/schema.sql @@ -1,18 +1,18 @@ CREATE TABLE RACE_RESULT ( - id BIGINT NOT NULL AUTO_INCREMENT, - trial_count INT NOT NULL, - winners VARCHAR(255) NOT NULL, - created_at DATETIME NOT NULL, + id BIGINT NOT NULL AUTO_INCREMENT, + trial_count INT NOT NULL, + created_at DATETIME NOT NULL, PRIMARY KEY (id) ); CREATE TABLE CAR ( - id BIGINT NOT NULL AUTO_INCREMENT, + id BIGINT NOT NULL AUTO_INCREMENT, name VARCHAR(50) NOT NULL, position INT NOT NULL, race_result_id BIGINT NOT NULL, + winner BOOL NOT NULL, created_at DATETIME NOT NULL, PRIMARY KEY (id) ); From 0f99a89e26390dfe97d8db529bd144cfd4cee969 Mon Sep 17 00:00:00 2001 From: java-saeng Date: Sat, 22 Apr 2023 15:56:49 +0900 Subject: [PATCH 32/36] =?UTF-8?q?feat=20:=20Car=20=EC=97=90=20winner=20col?= =?UTF-8?q?umn=20=EC=B6=94=EA=B0=80=20=ED=9B=84=20=EC=BD=94=EB=93=9C=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - table 스펙 변경에 따라 Dao 수정 - Dao 수정에 따른 Serivce & test 수정 --- src/main/java/racingcar/dao/CarDao.java | 17 ++++++ .../java/racingcar/dao/RaceResultDao.java | 51 ++++------------- src/main/java/racingcar/domain/Car.java | 4 ++ .../java/racingcar/domain/RacingCars.java | 5 ++ .../java/racingcar/domain/RacingGame.java | 4 ++ src/main/java/racingcar/entity/CarEntity.java | 25 ++++++--- .../racingcar/entity/RaceResultEntity.java | 25 +++++---- .../java/racingcar/service/CarService.java | 6 +- .../racingcar/service/RaceResultService.java | 23 ++------ .../racingcar/service/mapper/CarMapper.java | 20 ++++--- .../service/mapper/RaceResultMapper.java | 56 +++++++++++++++---- src/test/java/racingcar/dao/CarDaoTest.java | 18 +++++- .../java/racingcar/dao/RaceResultDaoTest.java | 16 +++--- .../service/CarServiceIntegrationTest.java | 2 +- .../racingcar/service/CarServiceTest.java | 12 ++-- .../service/RaceResultServiceTest.java | 45 +++++++-------- 16 files changed, 191 insertions(+), 138 deletions(-) diff --git a/src/main/java/racingcar/dao/CarDao.java b/src/main/java/racingcar/dao/CarDao.java index e5b787115..2ed0fe33d 100644 --- a/src/main/java/racingcar/dao/CarDao.java +++ b/src/main/java/racingcar/dao/CarDao.java @@ -1,6 +1,7 @@ package racingcar.dao; import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.jdbc.core.RowMapper; import org.springframework.jdbc.core.namedparam.SqlParameterSourceUtils; import org.springframework.jdbc.core.simple.SimpleJdbcInsert; import org.springframework.stereotype.Repository; @@ -11,9 +12,20 @@ @Repository public class CarDao { + private final JdbcTemplate jdbcTemplate; private final SimpleJdbcInsert simpleJdbcInsert; + private final RowMapper rowMapper = (rs, rowNum) -> + new CarEntity( + rs.getLong("id"), + rs.getString("name"), + rs.getInt("position"), + rs.getLong("race_result_id"), + rs.getBoolean("winner"), + rs.getTimestamp("created_at").toLocalDateTime() + ); public CarDao(final JdbcTemplate jdbcTemplate) { + this.jdbcTemplate = jdbcTemplate; this.simpleJdbcInsert = new SimpleJdbcInsert(jdbcTemplate) .withTableName("CAR") @@ -23,4 +35,9 @@ public CarDao(final JdbcTemplate jdbcTemplate) { public void save(final List carEntities) { simpleJdbcInsert.executeBatch(SqlParameterSourceUtils.createBatch(carEntities)); } + + public List findAll() { + final String sql = "select * from CAR"; + return jdbcTemplate.query(sql, rowMapper); + } } diff --git a/src/main/java/racingcar/dao/RaceResultDao.java b/src/main/java/racingcar/dao/RaceResultDao.java index badbb66fc..4225c193e 100644 --- a/src/main/java/racingcar/dao/RaceResultDao.java +++ b/src/main/java/racingcar/dao/RaceResultDao.java @@ -1,23 +1,25 @@ package racingcar.dao; import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.jdbc.core.RowMapper; import org.springframework.jdbc.core.namedparam.BeanPropertySqlParameterSource; import org.springframework.jdbc.core.simple.SimpleJdbcInsert; import org.springframework.stereotype.Repository; -import racingcar.entity.CarEntity; import racingcar.entity.RaceResultEntity; -import java.sql.ResultSet; -import java.util.ArrayList; -import java.util.HashMap; import java.util.List; -import java.util.Map; @Repository public class RaceResultDao { private final JdbcTemplate jdbcTemplate; private final SimpleJdbcInsert simpleJdbcInsert; + private final RowMapper rowMapper = (rs, rowNum) -> + new RaceResultEntity( + rs.getLong("id"), + rs.getInt("trial_count"), + rs.getTimestamp("created_at").toLocalDateTime() + ); public RaceResultDao(final JdbcTemplate jdbcTemplate) { this.jdbcTemplate = jdbcTemplate; @@ -27,42 +29,13 @@ public RaceResultDao(final JdbcTemplate jdbcTemplate) { .usingGeneratedKeyColumns("id"); } - public int save(final RaceResultEntity raceResultEntity) { + public Long save(final RaceResultEntity raceResultEntity) { return simpleJdbcInsert.executeAndReturnKey(new BeanPropertySqlParameterSource(raceResultEntity)) - .intValue(); + .longValue(); } - public Map> findAllRaceResult() { - - final String sql = - "select r.winners, c.name, c.position, c.created_at, c.race_result_id" - + " from RACE_RESULT r" - + " inner join Car c" - + " on r.id = c.race_result_id;"; - - return jdbcTemplate.query(sql, (ResultSet rs) -> { - Map> resultMap = new HashMap<>(); - - while (rs.next()) { - final String winners = rs.getString("winners"); - - final CarEntity carEntity = new CarEntity( - rs.getString("name"), - rs.getInt("position"), - rs.getInt("race_result_id"), - rs.getTimestamp("created_at") - .toLocalDateTime() - ); - - if (!resultMap.containsKey(winners)) { - resultMap.put(winners, new ArrayList<>()); - } - - resultMap.get(winners) - .add(carEntity); - } - - return resultMap; - }); + public List findAllRaceResult() { + final String sql = "select * from RACE_RESULT"; + return jdbcTemplate.query(sql, rowMapper); } } diff --git a/src/main/java/racingcar/domain/Car.java b/src/main/java/racingcar/domain/Car.java index 719e5abd2..4496c4779 100644 --- a/src/main/java/racingcar/domain/Car.java +++ b/src/main/java/racingcar/domain/Car.java @@ -47,6 +47,10 @@ public boolean isSamePositionCar(Car maxPositionCar) { return this.position == maxPositionCar.position; } + public boolean isSameCar(Car car) { + return this.name.equals(car.name); + } + public String getName() { return name; } diff --git a/src/main/java/racingcar/domain/RacingCars.java b/src/main/java/racingcar/domain/RacingCars.java index 94a1e2313..6f0706313 100644 --- a/src/main/java/racingcar/domain/RacingCars.java +++ b/src/main/java/racingcar/domain/RacingCars.java @@ -76,6 +76,11 @@ private List getMaxPositionCars(Car maxPositionCar) { .collect(Collectors.toUnmodifiableList()); } + public boolean isWinner(Car car) { + return getWinners().stream() + .anyMatch(it -> it.isSameCar(car)); + } + public List getCars() { return Collections.unmodifiableList(cars); } diff --git a/src/main/java/racingcar/domain/RacingGame.java b/src/main/java/racingcar/domain/RacingGame.java index 60bd9303a..ae728156b 100644 --- a/src/main/java/racingcar/domain/RacingGame.java +++ b/src/main/java/racingcar/domain/RacingGame.java @@ -36,6 +36,10 @@ public List getParticipantAllCar() { return racingCars.getCars(); } + public boolean isWinner(Car car) { + return racingCars.isWinner(car); + } + public Integer getTrialCount() { return trialCount; } diff --git a/src/main/java/racingcar/entity/CarEntity.java b/src/main/java/racingcar/entity/CarEntity.java index e14413509..7d5e2747f 100644 --- a/src/main/java/racingcar/entity/CarEntity.java +++ b/src/main/java/racingcar/entity/CarEntity.java @@ -4,28 +4,31 @@ public class CarEntity { - private final Integer id; + private final Long id; private final String name; private final int position; - private final Integer raceResultId; + private final Long raceResultId; + private final boolean winner; private final LocalDateTime createdAt; - private CarEntity(final Integer id, final String name, - final int position, final Integer raceResultId, - final LocalDateTime createdAt) { + public CarEntity(final Long id, final String name, + final int position, final Long raceResultId, + final boolean winner, final LocalDateTime createdAt) { this.id = id; this.name = name; this.position = position; this.raceResultId = raceResultId; + this.winner = winner; this.createdAt = createdAt; } public CarEntity(final String name, final int position, - final Integer raceResultId, final LocalDateTime createdAt) { - this(null, name, position, raceResultId, createdAt); + final Long raceResultId, final boolean winner, + final LocalDateTime createdAt) { + this(null, name, position, raceResultId, winner, createdAt); } - public Integer getId() { + public Long getId() { return id; } @@ -37,10 +40,14 @@ public int getPosition() { return position; } - public Integer getRaceResultId() { + public Long getRaceResultId() { return raceResultId; } + public boolean isWinner() { + return winner; + } + public LocalDateTime getCreatedAt() { return createdAt; } diff --git a/src/main/java/racingcar/entity/RaceResultEntity.java b/src/main/java/racingcar/entity/RaceResultEntity.java index 3e4064368..98998e006 100644 --- a/src/main/java/racingcar/entity/RaceResultEntity.java +++ b/src/main/java/racingcar/entity/RaceResultEntity.java @@ -6,28 +6,31 @@ public class RaceResultEntity { private final Long id; private final int trialCount; - private final String winners; private final LocalDateTime createdAt; - private RaceResultEntity(final Long id, final int trialCount, - final String winners, final LocalDateTime createdAt) { + public RaceResultEntity( + final Long id, + final int trialCount, + final LocalDateTime createdAt + ) { this.id = id; this.trialCount = trialCount; - this.winners = winners; this.createdAt = createdAt; } - public RaceResultEntity(final int trialCount, final String winners, - final LocalDateTime createdAt) { - this(null, trialCount, winners, createdAt); + public RaceResultEntity( + final int trialCount, + final LocalDateTime createdAt + ) { + this(null, trialCount, createdAt); } - public int getTrialCount() { - return trialCount; + public Long getId() { + return id; } - public String getWinners() { - return winners; + public int getTrialCount() { + return trialCount; } public LocalDateTime getCreatedAt() { diff --git a/src/main/java/racingcar/service/CarService.java b/src/main/java/racingcar/service/CarService.java index 944076742..b5e0178fa 100644 --- a/src/main/java/racingcar/service/CarService.java +++ b/src/main/java/racingcar/service/CarService.java @@ -19,10 +19,14 @@ public CarService(final CarDao carDao, final CarMapper carMapper) { this.carMapper = carMapper; } - public void registerCars(final RacingGame racingGame, final Integer savedId) { + public void registerCars(final RacingGame racingGame, final Long savedId) { final List carEntities = carMapper.mapToCarEntitiesFrom(racingGame, savedId); carDao.save(carEntities); } + + public List searchAllCars() { + return carDao.findAll(); + } } diff --git a/src/main/java/racingcar/service/RaceResultService.java b/src/main/java/racingcar/service/RaceResultService.java index a3a283a25..dcdb8dce3 100644 --- a/src/main/java/racingcar/service/RaceResultService.java +++ b/src/main/java/racingcar/service/RaceResultService.java @@ -6,15 +6,12 @@ import racingcar.domain.RacingGame; import racingcar.entity.CarEntity; import racingcar.entity.RaceResultEntity; -import racingcar.service.dto.CarStatusResponse; import racingcar.service.dto.GameInfoRequest; import racingcar.service.dto.RaceResultResponse; import racingcar.service.mapper.RaceResultMapper; import racingcar.util.NumberGenerator; import java.util.List; -import java.util.Map; -import java.util.stream.Collectors; @Service public class RaceResultService { @@ -43,26 +40,16 @@ public RaceResultResponse createRaceResult(final GameInfoRequest gameInfoRequest racingGame.play(); final RaceResultEntity raceResultEntity = raceResultMapper.mapToRaceResultEntity(racingGame); - final int savedId = raceResultDao.save(raceResultEntity); - carService.registerCars(racingGame, savedId); + final Long savedId = raceResultDao.save(raceResultEntity); - final List carStatusResponses = - raceResultMapper.mapToCarStatusResponseFrom(racingGame); + carService.registerCars(racingGame, savedId); - return new RaceResultResponse(raceResultEntity.getWinners(), carStatusResponses); + return raceResultMapper.mapToRaceResultResponse(racingGame); } @Transactional(readOnly = true) public List searchRaceResult() { - - final Map> allRaceResult = raceResultDao.findAllRaceResult(); - - return allRaceResult.entrySet() - .stream() - .map(it -> new RaceResultResponse( - it.getKey(), - raceResultMapper.mapToCarStatusResponseFrom(it.getValue())) - ) - .collect(Collectors.toList()); + final List carEntities = carService.searchAllCars(); + return raceResultMapper.mapToRaceResultResponses(carEntities); } } diff --git a/src/main/java/racingcar/service/mapper/CarMapper.java b/src/main/java/racingcar/service/mapper/CarMapper.java index 000cd7641..51c86eaf3 100644 --- a/src/main/java/racingcar/service/mapper/CarMapper.java +++ b/src/main/java/racingcar/service/mapper/CarMapper.java @@ -13,18 +13,22 @@ public class CarMapper { public List mapToCarEntitiesFrom(final RacingGame racingGame, - final Integer savedId) { - + final Long savedId) { return racingGame.getParticipantAllCar() .stream() - .map(it -> mapToCarEntity(savedId, it)) + .map(it -> mapToCarEntityFrom(racingGame, savedId, it)) .collect(Collectors.toList()); } - private CarEntity mapToCarEntity(final Integer savedId, final Car car) { - return new CarEntity(car.getName(), - car.getPosition(), - savedId, - LocalDateTime.now()); + private static CarEntity mapToCarEntityFrom(final RacingGame racingGame, + final Long savedId, + final Car car) { + return new CarEntity( + car.getName(), + car.getPosition(), + savedId, + racingGame.isWinner(car), + LocalDateTime.now() + ); } } diff --git a/src/main/java/racingcar/service/mapper/RaceResultMapper.java b/src/main/java/racingcar/service/mapper/RaceResultMapper.java index 7802e4afb..283a7fa58 100644 --- a/src/main/java/racingcar/service/mapper/RaceResultMapper.java +++ b/src/main/java/racingcar/service/mapper/RaceResultMapper.java @@ -6,11 +6,15 @@ import racingcar.entity.CarEntity; import racingcar.entity.RaceResultEntity; import racingcar.service.dto.CarStatusResponse; +import racingcar.service.dto.RaceResultResponse; import java.time.LocalDateTime; import java.util.List; +import java.util.Map; import java.util.stream.Collectors; +import static java.util.stream.Collectors.toList; + @Component public class RaceResultMapper { @@ -18,29 +22,57 @@ public class RaceResultMapper { public RaceResultEntity mapToRaceResultEntity(final RacingGame racingGame) { return new RaceResultEntity(racingGame.getTrialCount(), - String.join(CAR_NAMES_DELIMITER, mapToWinnersNameFrom(racingGame)), LocalDateTime.now()); } - private List mapToWinnersNameFrom(final RacingGame racingGame) { - return racingGame.determineWinners() - .stream() - .map(Car::getName) - .collect(Collectors.toList()); + public RaceResultResponse mapToRaceResultResponse(final RacingGame racingGame) { + final List carStatusResponses = mapToCarStatusResponseFrom(racingGame); + final String winners = mapToWinnersNameFrom(racingGame); + + return new RaceResultResponse(winners, carStatusResponses); } - public List mapToCarStatusResponseFrom(final RacingGame racingGame) { + private List mapToCarStatusResponseFrom(final RacingGame racingGame) { return racingGame.getParticipantAllCar() .stream() .map(car -> new CarStatusResponse(car.getName(), car.getPosition())) - .collect(Collectors.toList()); + .collect(toList()); + } + + private String mapToWinnersNameFrom(final RacingGame racingGame) { + return racingGame.determineWinners() + .stream() + .map(Car::getName) + .collect(Collectors.joining(",")); + } + + public List mapToRaceResultResponses(final List carEntities) { + final Map> groupingByRaceResultId = + carEntities.stream() + .collect(Collectors.groupingBy( + CarEntity::getRaceResultId) + ); + + return groupingByRaceResultId.values() + .stream() + .map(it -> new RaceResultResponse( + mapToWinnersNameFrom(it), + mapToCarStatusResponses(it)) + ) + .collect(toList()); + } + + private String mapToWinnersNameFrom(final List carEntities) { + return carEntities.stream() + .filter(CarEntity::isWinner) + .map(CarEntity::getName) + .collect(Collectors.joining(CAR_NAMES_DELIMITER)); } - public List mapToCarStatusResponseFrom(final List carEntities) { + private List mapToCarStatusResponses(final List carEntities) { return carEntities.stream() - .map(entity -> new CarStatusResponse(entity.getName(), - entity.getPosition())) - .collect(Collectors.toList()); + .map(it -> new CarStatusResponse(it.getName(), it.getPosition())) + .collect(toList()); } } diff --git a/src/test/java/racingcar/dao/CarDaoTest.java b/src/test/java/racingcar/dao/CarDaoTest.java index 8b78c457f..2a89cab04 100644 --- a/src/test/java/racingcar/dao/CarDaoTest.java +++ b/src/test/java/racingcar/dao/CarDaoTest.java @@ -7,12 +7,13 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.jdbc.JdbcTest; import org.springframework.jdbc.core.JdbcTemplate; -import racingcar.dao.CarDao; import racingcar.entity.CarEntity; import java.time.LocalDateTime; import java.util.List; +import static org.junit.jupiter.api.Assertions.assertEquals; + @JdbcTest class CarDaoTest { @@ -31,11 +32,22 @@ void init() { void test_save() throws Exception { //given final List carEntities = - List.of(new CarEntity("a", 1, 3, LocalDateTime.now()), - new CarEntity("b", 2, 3, LocalDateTime.now()) + List.of( + new CarEntity("a", 1, 3L, true, LocalDateTime.now()), + new CarEntity("b", 2, 3L, true, LocalDateTime.now()) ); //when Assertions.assertDoesNotThrow(() -> carDao.save(carEntities)); } + + @Test + @DisplayName("자동차들을 전부 조회할 수 있다.") + void test_findCarsBy() throws Exception { + //when + final List carEntities = carDao.findAll(); + + //then + assertEquals(6, carEntities.size()); + } } diff --git a/src/test/java/racingcar/dao/RaceResultDaoTest.java b/src/test/java/racingcar/dao/RaceResultDaoTest.java index fc5f66673..dc8a35a17 100644 --- a/src/test/java/racingcar/dao/RaceResultDaoTest.java +++ b/src/test/java/racingcar/dao/RaceResultDaoTest.java @@ -35,29 +35,29 @@ void setUp() { @DisplayName("save() : 경기 결과를 저장할 수 있다.") void test_save() throws Exception { //given - final RaceResultEntity raceResultEntity = new RaceResultEntity(4, "a,b,c", LocalDateTime.now()); + final RaceResultEntity raceResultEntity = new RaceResultEntity(4, LocalDateTime.now()); //when - final int savedId = raceResultDao.save(raceResultEntity); + final Long savedId = raceResultDao.save(raceResultEntity); //then - assertEquals(savedId, 1); + assertEquals(1L, savedId); } @Test @DisplayName("findAllRaceResult() : 전체 경기 결과를 조회할 수 있다.") void test_findAllRaceResult() throws Exception { //given - final String winner1 = "빙봉"; - final String winner2 = "a,b,c,d"; + final List ids = List.of(3L, 4L); //when - final Map> allRaceResult = raceResultDao.findAllRaceResult(); + final List raceResultEntities = raceResultDao.findAllRaceResult(); //then assertAll( - () -> Assertions.assertThat(allRaceResult).hasSize(2), - () -> Assertions.assertThat(allRaceResult).containsOnlyKeys(winner1, winner2) + () -> assertEquals(2, raceResultEntities.size()), + () -> Assertions.assertThat(raceResultEntities).extracting("id") + .containsExactlyElementsOf(ids) ); } } diff --git a/src/test/java/racingcar/service/CarServiceIntegrationTest.java b/src/test/java/racingcar/service/CarServiceIntegrationTest.java index 52f2a7a63..92a88ab32 100644 --- a/src/test/java/racingcar/service/CarServiceIntegrationTest.java +++ b/src/test/java/racingcar/service/CarServiceIntegrationTest.java @@ -25,7 +25,7 @@ void test_registerCars() throws Exception { RacingGame.readyToRacingGame("a,b,c", new RandomNumberGenerator(), trialCount); - final int savedRaceResultId = 3; + final Long savedRaceResultId = 3L; //when & then assertDoesNotThrow(() -> carService.registerCars(racingGame, savedRaceResultId)); diff --git a/src/test/java/racingcar/service/CarServiceTest.java b/src/test/java/racingcar/service/CarServiceTest.java index ace3f8bd4..d2fdc7f77 100644 --- a/src/test/java/racingcar/service/CarServiceTest.java +++ b/src/test/java/racingcar/service/CarServiceTest.java @@ -13,7 +13,7 @@ import java.util.List; import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; @@ -43,16 +43,16 @@ void test_registerCars() throws Exception { RacingGame.readyToRacingGame("a,b,c", new RandomNumberGenerator(), 2); - final int savedRaceResultId = 1; + final Long savedRaceResultId = 1L; final List carEntities = List.of( - new CarEntity("a", 3, 1, LocalDateTime.now()), - new CarEntity("b", 3, 1, LocalDateTime.now()), - new CarEntity("c", 3, 1, LocalDateTime.now()) + new CarEntity("a", 3, 1L, true, LocalDateTime.now()), + new CarEntity("b", 3, 1L, true, LocalDateTime.now()), + new CarEntity("c", 3, 1L, true, LocalDateTime.now()) ); //when - when(carMapper.mapToCarEntitiesFrom(any(), anyInt())) + when(carMapper.mapToCarEntitiesFrom(any(), anyLong())) .thenReturn(carEntities); carService.registerCars(racingGame, savedRaceResultId); diff --git a/src/test/java/racingcar/service/RaceResultServiceTest.java b/src/test/java/racingcar/service/RaceResultServiceTest.java index aaf5ede85..7e3460fa8 100644 --- a/src/test/java/racingcar/service/RaceResultServiceTest.java +++ b/src/test/java/racingcar/service/RaceResultServiceTest.java @@ -4,8 +4,6 @@ import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import racingcar.dao.RaceResultDao; -import racingcar.domain.RacingCars; -import racingcar.domain.RacingGame; import racingcar.entity.CarEntity; import racingcar.entity.RaceResultEntity; import racingcar.service.dto.CarStatusResponse; @@ -24,7 +22,7 @@ import static org.junit.jupiter.api.Assertions.assertAll; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -61,7 +59,7 @@ void test_createRaceResult() throws Exception { final int trialCount = gameInfoRequest.getCount(); final RaceResultEntity raceResultEntity = - new RaceResultEntity(trialCount, "a,b,c", LocalDateTime.now()); + new RaceResultEntity(trialCount, LocalDateTime.now()); final List carStatusResponses = List.of(new CarStatusResponse("a", 1), @@ -69,18 +67,20 @@ void test_createRaceResult() throws Exception { new CarStatusResponse("c", 1), new CarStatusResponse("d", 0)); + final RaceResultResponse raceResultResponse = new RaceResultResponse("a,b,c", carStatusResponses); + //when when(raceResultMapper.mapToRaceResultEntity(any())) .thenReturn(raceResultEntity); when(raceResultDao.save(any())) - .thenReturn(1); + .thenReturn(1L); doNothing().when(carService) - .registerCars(any(), anyInt()); + .registerCars(any(), anyLong()); - when(raceResultMapper.mapToCarStatusResponseFrom((RacingGame) any())) - .thenReturn(carStatusResponses); + when(raceResultMapper.mapToRaceResultResponse(any())) + .thenReturn(raceResultResponse); final RaceResultResponse raceResult = raceResultService.createRaceResult(gameInfoRequest); @@ -88,7 +88,7 @@ void test_createRaceResult() throws Exception { final List carStatusResult = raceResult.getRacingCars(); assertAll( - () -> assertEquals(raceResult.getWinners(), "a,b,c"), + () -> assertEquals("a,b,c", raceResult.getWinners()), () -> assertThat(carStatusResult).hasSize(4), () -> assertThat(carStatusResult).extracting("name") .containsExactly("a", "b", "c", "d"), @@ -102,28 +102,29 @@ void test_createRaceResult() throws Exception { void test_searchRaceResult() throws Exception { //given final List cars = - List.of(new CarEntity("a", 2, 1, LocalDateTime.now()), - new CarEntity("b", 3, 1, LocalDateTime.now()), - new CarEntity("c", 1, 1, LocalDateTime.now()), - new CarEntity("d", 4, 1, LocalDateTime.now())); - - final Map> allRaceResult = Map.of("d", cars); + List.of(new CarEntity("a", 2, 1L, true, LocalDateTime.now()), + new CarEntity("b", 3, 1L, true, LocalDateTime.now()), + new CarEntity("c", 1, 1L, true, LocalDateTime.now()), + new CarEntity("d", 4, 1L, true, LocalDateTime.now())); final List carStatusResponses = List.of(new CarStatusResponse("a", 2), new CarStatusResponse("b", 3), new CarStatusResponse("c", 1), new CarStatusResponse("d", 4)); - when(raceResultDao.findAllRaceResult()) - .thenReturn(allRaceResult); + final List raceResultResponses = List.of( + new RaceResultResponse("d", carStatusResponses)); + + when(carService.searchAllCars()) + .thenReturn(cars); - when(raceResultMapper.mapToCarStatusResponseFrom(cars)) - .thenReturn(carStatusResponses); + when(raceResultMapper.mapToRaceResultResponses(cars)) + .thenReturn(raceResultResponses); - final List raceResultResponses = raceResultService.searchRaceResult(); + final List result = raceResultService.searchRaceResult(); //then - final RaceResultResponse response = raceResultResponses.get(0); + final RaceResultResponse response = result.get(0); final Map responseMap = response.getRacingCars() .stream() @@ -131,7 +132,7 @@ void test_searchRaceResult() throws Exception { CarStatusResponse::getPosition)); assertAll( - () -> assertEquals(response.getWinners(), "d"), + () -> assertEquals("d", response.getWinners()), () -> assertThat(response.getRacingCars()).hasSize(4), () -> assertThat(responseMap).containsKeys("a", "b", "c", "d") .containsValues(2, 3, 1, 4) From 78dd8091afe8b50c1c288ba5f4fc2b2cf87854b5 Mon Sep 17 00:00:00 2001 From: java-saeng Date: Sat, 22 Apr 2023 16:14:11 +0900 Subject: [PATCH 33/36] =?UTF-8?q?feat=20:=20DB=20=EA=B4=80=EB=A0=A8=20exce?= =?UTF-8?q?ption=20handler=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../global/exception/ExceptionHandlerController.java | 7 +++++++ .../java/racingcar/global/exception/ExceptionStatus.java | 4 +++- .../global/exception/response/ExceptionResponse.java | 7 +++++++ 3 files changed, 17 insertions(+), 1 deletion(-) diff --git a/src/main/java/racingcar/global/exception/ExceptionHandlerController.java b/src/main/java/racingcar/global/exception/ExceptionHandlerController.java index 059419fba..cc23e8472 100644 --- a/src/main/java/racingcar/global/exception/ExceptionHandlerController.java +++ b/src/main/java/racingcar/global/exception/ExceptionHandlerController.java @@ -1,5 +1,6 @@ package racingcar.global.exception; +import org.springframework.dao.TransientDataAccessException; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.MethodArgumentNotValidException; @@ -27,4 +28,10 @@ public ResponseEntity handleDefaultException( return ResponseEntity.badRequest() .body(DefaultExceptionResponse.from(exception)); } + + @ExceptionHandler(TransientDataAccessException.class) + @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR) + public ExceptionResponse handleTransientDataAccessException() { + return ExceptionResponse.of(ExceptionStatus.TEMPORARY_DATABASE_CONNECTION_EXCEPTION); + } } diff --git a/src/main/java/racingcar/global/exception/ExceptionStatus.java b/src/main/java/racingcar/global/exception/ExceptionStatus.java index eafa462f1..c65e92571 100644 --- a/src/main/java/racingcar/global/exception/ExceptionStatus.java +++ b/src/main/java/racingcar/global/exception/ExceptionStatus.java @@ -6,7 +6,9 @@ public enum ExceptionStatus { - INVALID_INPUT_VALUE_EXCEPTION(400, "유효하지 않는 입력 값입니다.", BAD_REQUEST); + INVALID_INPUT_VALUE_EXCEPTION(400, "유효하지 않는 입력 값입니다.", BAD_REQUEST), + TEMPORARY_DATABASE_CONNECTION_EXCEPTION(512, "일시적인 데이터베이스 오류입니다. 재시도 해주세요.", HttpStatus.INTERNAL_SERVER_ERROR); + private final int status; private final String message; diff --git a/src/main/java/racingcar/global/exception/response/ExceptionResponse.java b/src/main/java/racingcar/global/exception/response/ExceptionResponse.java index e09a48295..3e053f286 100644 --- a/src/main/java/racingcar/global/exception/response/ExceptionResponse.java +++ b/src/main/java/racingcar/global/exception/response/ExceptionResponse.java @@ -27,6 +27,13 @@ public static ExceptionResponse of(final ExceptionStatus exceptionStatus, Bindin ExceptionTargetInfoResponse.from(bindingResult)); } + public static ExceptionResponse of(final ExceptionStatus exceptionStatus) { + return new ExceptionResponse(exceptionStatus.getStatus(), + exceptionStatus.getMessage(), + exceptionStatus.getHttpStatus().name(), + null); + } + public String getMessage() { return message; } From 775e9a1d92fc1a44abda77d50fcce5d5d19d7255 Mon Sep 17 00:00:00 2001 From: java-saeng Date: Sat, 22 Apr 2023 16:19:18 +0900 Subject: [PATCH 34/36] =?UTF-8?q?style=20:=20=EC=BD=94=EB=93=9C=20?= =?UTF-8?q?=EC=A0=9C=EC=B6=9C=20=EC=A0=84=20reformatting?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../exception/response/ExceptionTargetInfoResponse.java | 3 ++- .../racingcar/controller/RacingCarWebControllerTest.java | 2 +- src/test/java/racingcar/dao/RaceResultDaoTest.java | 5 +---- 3 files changed, 4 insertions(+), 6 deletions(-) diff --git a/src/main/java/racingcar/global/exception/response/ExceptionTargetInfoResponse.java b/src/main/java/racingcar/global/exception/response/ExceptionTargetInfoResponse.java index 08258db3f..4ec285b04 100644 --- a/src/main/java/racingcar/global/exception/response/ExceptionTargetInfoResponse.java +++ b/src/main/java/racingcar/global/exception/response/ExceptionTargetInfoResponse.java @@ -28,7 +28,8 @@ public static List from(BindingResult bindingResult return fieldErrors.stream() .map(error -> new ExceptionTargetInfoResponse( error.getField(), - Objects.requireNonNullElse(error.getRejectedValue(), DEFAULT_ERROR_MESSAGE).toString(), + Objects.requireNonNullElse(error.getRejectedValue(), DEFAULT_ERROR_MESSAGE) + .toString(), Objects.requireNonNullElse(error.getDefaultMessage(), DEFAULT_ERROR_MESSAGE)) ) .collect(Collectors.toList()); diff --git a/src/test/java/racingcar/controller/RacingCarWebControllerTest.java b/src/test/java/racingcar/controller/RacingCarWebControllerTest.java index 04b0ae102..40f86b599 100644 --- a/src/test/java/racingcar/controller/RacingCarWebControllerTest.java +++ b/src/test/java/racingcar/controller/RacingCarWebControllerTest.java @@ -7,10 +7,10 @@ import org.springframework.http.MediaType; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.setup.MockMvcBuilders; +import racingcar.service.RaceResultService; import racingcar.service.dto.CarStatusResponse; import racingcar.service.dto.GameInfoRequest; import racingcar.service.dto.RaceResultResponse; -import racingcar.service.RaceResultService; import java.util.List; diff --git a/src/test/java/racingcar/dao/RaceResultDaoTest.java b/src/test/java/racingcar/dao/RaceResultDaoTest.java index dc8a35a17..8f8e82a02 100644 --- a/src/test/java/racingcar/dao/RaceResultDaoTest.java +++ b/src/test/java/racingcar/dao/RaceResultDaoTest.java @@ -7,13 +7,10 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.jdbc.JdbcTest; import org.springframework.jdbc.core.JdbcTemplate; -import racingcar.dao.RaceResultDao; -import racingcar.entity.CarEntity; import racingcar.entity.RaceResultEntity; import java.time.LocalDateTime; import java.util.List; -import java.util.Map; import static org.junit.jupiter.api.Assertions.assertAll; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -51,7 +48,7 @@ void test_findAllRaceResult() throws Exception { final List ids = List.of(3L, 4L); //when - final List raceResultEntities = raceResultDao.findAllRaceResult(); + final List raceResultEntities = raceResultDao.findAllRaceResult(); //then assertAll( From f4f90600838407aac0f0dccf92e2b2b0ff482421 Mon Sep 17 00:00:00 2001 From: java-saeng Date: Sat, 22 Apr 2023 16:32:43 +0900 Subject: [PATCH 35/36] =?UTF-8?q?refactor=20:=20=EC=82=AC=EC=9A=A9?= =?UTF-8?q?=ED=95=98=EC=A7=80=20=EC=95=8A=EB=8A=94=20=EB=A9=94=EC=84=9C?= =?UTF-8?q?=EB=93=9C=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/racingcar/dao/RaceResultDao.java | 16 --------------- .../java/racingcar/dao/RaceResultDaoTest.java | 20 ------------------- 2 files changed, 36 deletions(-) diff --git a/src/main/java/racingcar/dao/RaceResultDao.java b/src/main/java/racingcar/dao/RaceResultDao.java index 4225c193e..014ec1dff 100644 --- a/src/main/java/racingcar/dao/RaceResultDao.java +++ b/src/main/java/racingcar/dao/RaceResultDao.java @@ -1,28 +1,17 @@ package racingcar.dao; import org.springframework.jdbc.core.JdbcTemplate; -import org.springframework.jdbc.core.RowMapper; import org.springframework.jdbc.core.namedparam.BeanPropertySqlParameterSource; import org.springframework.jdbc.core.simple.SimpleJdbcInsert; import org.springframework.stereotype.Repository; import racingcar.entity.RaceResultEntity; -import java.util.List; - @Repository public class RaceResultDao { - private final JdbcTemplate jdbcTemplate; private final SimpleJdbcInsert simpleJdbcInsert; - private final RowMapper rowMapper = (rs, rowNum) -> - new RaceResultEntity( - rs.getLong("id"), - rs.getInt("trial_count"), - rs.getTimestamp("created_at").toLocalDateTime() - ); public RaceResultDao(final JdbcTemplate jdbcTemplate) { - this.jdbcTemplate = jdbcTemplate; this.simpleJdbcInsert = new SimpleJdbcInsert(jdbcTemplate) .withTableName("RACE_RESULT") @@ -33,9 +22,4 @@ public Long save(final RaceResultEntity raceResultEntity) { return simpleJdbcInsert.executeAndReturnKey(new BeanPropertySqlParameterSource(raceResultEntity)) .longValue(); } - - public List findAllRaceResult() { - final String sql = "select * from RACE_RESULT"; - return jdbcTemplate.query(sql, rowMapper); - } } diff --git a/src/test/java/racingcar/dao/RaceResultDaoTest.java b/src/test/java/racingcar/dao/RaceResultDaoTest.java index 8f8e82a02..bb7548ec1 100644 --- a/src/test/java/racingcar/dao/RaceResultDaoTest.java +++ b/src/test/java/racingcar/dao/RaceResultDaoTest.java @@ -1,6 +1,5 @@ package racingcar.dao; -import org.assertj.core.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; @@ -10,9 +9,7 @@ import racingcar.entity.RaceResultEntity; import java.time.LocalDateTime; -import java.util.List; -import static org.junit.jupiter.api.Assertions.assertAll; import static org.junit.jupiter.api.Assertions.assertEquals; @JdbcTest @@ -40,21 +37,4 @@ void test_save() throws Exception { //then assertEquals(1L, savedId); } - - @Test - @DisplayName("findAllRaceResult() : 전체 경기 결과를 조회할 수 있다.") - void test_findAllRaceResult() throws Exception { - //given - final List ids = List.of(3L, 4L); - - //when - final List raceResultEntities = raceResultDao.findAllRaceResult(); - - //then - assertAll( - () -> assertEquals(2, raceResultEntities.size()), - () -> Assertions.assertThat(raceResultEntities).extracting("id") - .containsExactlyElementsOf(ids) - ); - } } From d4c33f8ba923c26305f90d4859905b7f63a22066 Mon Sep 17 00:00:00 2001 From: java-saeng Date: Sat, 22 Apr 2023 17:56:13 +0900 Subject: [PATCH 36/36] =?UTF-8?q?feat=20:=20=EB=8D=B0=EC=9D=B4=ED=84=B0?= =?UTF-8?q?=EB=B2=A0=EC=9D=B4=EC=8A=A4=20=EB=AC=B8=EC=A0=9C=20=EC=83=9D?= =?UTF-8?q?=EA=B8=B8=20=EC=8B=9C=20exception=20test?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../exception/ExceptionHandlerController.java | 6 +- .../global/exception/ExceptionStatus.java | 2 +- ...RacingCarWebControllerIntegrationTest.java | 58 +++++++++++++++++++ 3 files changed, 62 insertions(+), 4 deletions(-) create mode 100644 src/test/java/racingcar/controller/RacingCarWebControllerIntegrationTest.java diff --git a/src/main/java/racingcar/global/exception/ExceptionHandlerController.java b/src/main/java/racingcar/global/exception/ExceptionHandlerController.java index cc23e8472..470ac2953 100644 --- a/src/main/java/racingcar/global/exception/ExceptionHandlerController.java +++ b/src/main/java/racingcar/global/exception/ExceptionHandlerController.java @@ -1,6 +1,6 @@ package racingcar.global.exception; -import org.springframework.dao.TransientDataAccessException; +import org.springframework.dao.DataAccessResourceFailureException; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.MethodArgumentNotValidException; @@ -29,9 +29,9 @@ public ResponseEntity handleDefaultException( .body(DefaultExceptionResponse.from(exception)); } - @ExceptionHandler(TransientDataAccessException.class) + @ExceptionHandler(DataAccessResourceFailureException.class) @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR) - public ExceptionResponse handleTransientDataAccessException() { + public ExceptionResponse handleDataAccessException() { return ExceptionResponse.of(ExceptionStatus.TEMPORARY_DATABASE_CONNECTION_EXCEPTION); } } diff --git a/src/main/java/racingcar/global/exception/ExceptionStatus.java b/src/main/java/racingcar/global/exception/ExceptionStatus.java index c65e92571..0a95669ca 100644 --- a/src/main/java/racingcar/global/exception/ExceptionStatus.java +++ b/src/main/java/racingcar/global/exception/ExceptionStatus.java @@ -7,7 +7,7 @@ public enum ExceptionStatus { INVALID_INPUT_VALUE_EXCEPTION(400, "유효하지 않는 입력 값입니다.", BAD_REQUEST), - TEMPORARY_DATABASE_CONNECTION_EXCEPTION(512, "일시적인 데이터베이스 오류입니다. 재시도 해주세요.", HttpStatus.INTERNAL_SERVER_ERROR); + TEMPORARY_DATABASE_CONNECTION_EXCEPTION(512, "데이터베이스에 문제가 생겼습니다.", HttpStatus.INTERNAL_SERVER_ERROR); private final int status; diff --git a/src/test/java/racingcar/controller/RacingCarWebControllerIntegrationTest.java b/src/test/java/racingcar/controller/RacingCarWebControllerIntegrationTest.java new file mode 100644 index 000000000..a521c8cfe --- /dev/null +++ b/src/test/java/racingcar/controller/RacingCarWebControllerIntegrationTest.java @@ -0,0 +1,58 @@ +package racingcar.controller; + +import com.fasterxml.jackson.databind.ObjectMapper; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.dao.DataAccessResourceFailureException; +import org.springframework.http.MediaType; +import org.springframework.test.web.servlet.MockMvc; +import racingcar.global.exception.ExceptionStatus; +import racingcar.service.RaceResultService; +import racingcar.service.dto.GameInfoRequest; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.when; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +@WebMvcTest(RacingCarWebController.class) +class RacingCarWebControllerIntegrationTest { + + @MockBean + private RaceResultService raceResultService; + + @Autowired + private MockMvc mockMvc; + + @Autowired + private ObjectMapper objectMapper; + + @Test + @DisplayName("데이터베이스 연결 실패 테스트") + void test_registerRaceResult_connectionFailure() throws Exception { + //given + final GameInfoRequest gameInfoRequest = new GameInfoRequest("a,b,c,d", 4); + final String requestData = objectMapper.writeValueAsString(gameInfoRequest); + + //when + when(raceResultService.createRaceResult(any())) + .thenThrow(DataAccessResourceFailureException.class); + + //then + mockMvc.perform(post("/plays") + .contentType(MediaType.APPLICATION_JSON) + .content(requestData)) + .andExpect(status().isInternalServerError()) + .andExpect(jsonPath("$.statusCode").value( + ExceptionStatus.TEMPORARY_DATABASE_CONNECTION_EXCEPTION.getStatus() + ) + ) + .andExpect(jsonPath("$.message").value( + ExceptionStatus.TEMPORARY_DATABASE_CONNECTION_EXCEPTION.getMessage()) + ); + } +}