From 227e10fd6a57a4baa9f20208fe07887253c362f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=ED=95=9C=EC=9A=B0=EC=84=9D?= <106640954+drunkenhw@users.noreply.github.com> Date: Fri, 14 Apr 2023 12:28:49 +0900 Subject: [PATCH 01/31] =?UTF-8?q?[1=EB=8B=A8=EA=B3=84=20-=20=EC=9B=B9=20?= =?UTF-8?q?=EC=9E=90=EB=8F=99=EC=B0=A8=20=EA=B2=BD=EC=A3=BC]=20=EB=B0=95?= =?UTF-8?q?=EC=8A=A4=ED=84=B0(=ED=95=9C=EC=9A=B0=EC=84=9D)=20=EB=AF=B8?= =?UTF-8?q?=EC=85=98=20=EC=A0=9C=EC=B6=9C=ED=95=A9=EB=8B=88=EB=8B=A4.=20(#?= =?UTF-8?q?49)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: 자동차 경주 코드 가져오기 * feat: 웹 요청,응답 구현 * feat: service 분리 * feat: 데이터베이스 연결 * feat: table 추가 * feat: 데이터베이스 저장하는 기능 구현 * test: car dao 테스트 추가 * test: racing game dao 테스트 추가 * test: racing game service 테스트 추가 * test: transactional 어노테이션 추가 * test: service test 리팩터링 * test: controller test 추가 --- build.gradle | 17 +-- src/main/java/racingcar/NumberGenerator.java | 5 + .../java/racingcar/RandomNumberGenerator.java | 13 ++ .../controller/RacingGameController.java | 24 ++++ src/main/java/racingcar/dao/CarDao.java | 13 ++ src/main/java/racingcar/dao/CarJdbcDao.java | 39 ++++++ .../java/racingcar/dao/RacingGameDao.java | 6 + .../java/racingcar/dao/RacingGameJdbcDao.java | 31 +++++ src/main/java/racingcar/domain/Car.java | 50 ++++++++ src/main/java/racingcar/domain/Cars.java | 52 ++++++++ .../java/racingcar/domain/RacingGame.java | 42 +++++++ src/main/java/racingcar/dto/CarDto.java | 26 ++++ .../java/racingcar/dto/RacingGameRequest.java | 32 +++++ .../racingcar/dto/RacingGameResponse.java | 22 ++++ .../racingcar/service/RacingGameService.java | 56 +++++++++ src/main/java/racingcar/view/InputView.java | 35 ++++++ src/main/java/racingcar/view/OutputView.java | 26 ++++ src/main/resources/application.properties | 3 + src/main/resources/data.sql | 20 ++- .../racingcar/AlwaysMoveNumberGenerator.java | 17 +++ .../controller/RacingGameControllerTest.java | 48 +++++++ .../java/racingcar/dao/CarJdbcDaoTest.java | 49 ++++++++ .../racingcar/dao/RacingGameJdbcDaoTest.java | 31 +++++ src/test/java/racingcar/domain/CarTest.java | 51 ++++++++ src/test/java/racingcar/domain/CarsTest.java | 60 +++++++++ .../java/racingcar/domain/RacingGameTest.java | 117 ++++++++++++++++++ .../service/RacingGameServiceTest.java | 33 +++++ 27 files changed, 908 insertions(+), 10 deletions(-) create mode 100644 src/main/java/racingcar/NumberGenerator.java create mode 100644 src/main/java/racingcar/RandomNumberGenerator.java create mode 100644 src/main/java/racingcar/controller/RacingGameController.java create mode 100644 src/main/java/racingcar/dao/CarDao.java create mode 100644 src/main/java/racingcar/dao/CarJdbcDao.java create mode 100644 src/main/java/racingcar/dao/RacingGameDao.java create mode 100644 src/main/java/racingcar/dao/RacingGameJdbcDao.java create mode 100644 src/main/java/racingcar/domain/Car.java create mode 100644 src/main/java/racingcar/domain/Cars.java create mode 100644 src/main/java/racingcar/domain/RacingGame.java create mode 100644 src/main/java/racingcar/dto/CarDto.java create mode 100644 src/main/java/racingcar/dto/RacingGameRequest.java create mode 100644 src/main/java/racingcar/dto/RacingGameResponse.java create mode 100644 src/main/java/racingcar/service/RacingGameService.java create mode 100644 src/main/java/racingcar/view/InputView.java create mode 100644 src/main/java/racingcar/view/OutputView.java create mode 100644 src/test/java/racingcar/AlwaysMoveNumberGenerator.java create mode 100644 src/test/java/racingcar/controller/RacingGameControllerTest.java create mode 100644 src/test/java/racingcar/dao/CarJdbcDaoTest.java create mode 100644 src/test/java/racingcar/dao/RacingGameJdbcDaoTest.java create mode 100644 src/test/java/racingcar/domain/CarTest.java create mode 100644 src/test/java/racingcar/domain/CarsTest.java create mode 100644 src/test/java/racingcar/domain/RacingGameTest.java create mode 100644 src/test/java/racingcar/service/RacingGameServiceTest.java diff --git a/build.gradle b/build.gradle index b9473b3bd..e9f5a3972 100644 --- a/build.gradle +++ b/build.gradle @@ -1,20 +1,23 @@ plugins { - id 'java' - id 'org.springframework.boot' version '2.7.9' - id 'io.spring.dependency-management' version '1.0.15.RELEASE' + id 'java' + id 'org.springframework.boot' version '2.7.9' + id 'io.spring.dependency-management' version '1.0.15.RELEASE' } sourceCompatibility = '11' repositories { - mavenCentral() + mavenCentral() } dependencies { - implementation 'org.springframework.boot:spring-boot-starter-web' - testImplementation 'org.springframework.boot:spring-boot-starter-test' + implementation 'org.springframework.boot:spring-boot-starter-web' + implementation 'org.springframework.boot:spring-boot-starter-jdbc' + runtimeOnly 'com.h2database:h2' + testImplementation 'io.rest-assured:rest-assured:4.4.0' + testImplementation 'org.springframework.boot:spring-boot-starter-test' } tasks.named('test') { - useJUnitPlatform() + useJUnitPlatform() } diff --git a/src/main/java/racingcar/NumberGenerator.java b/src/main/java/racingcar/NumberGenerator.java new file mode 100644 index 000000000..3669cd4ad --- /dev/null +++ b/src/main/java/racingcar/NumberGenerator.java @@ -0,0 +1,5 @@ +package racingcar; + +public interface NumberGenerator { + int generate(); +} diff --git a/src/main/java/racingcar/RandomNumberGenerator.java b/src/main/java/racingcar/RandomNumberGenerator.java new file mode 100644 index 000000000..9650a606c --- /dev/null +++ b/src/main/java/racingcar/RandomNumberGenerator.java @@ -0,0 +1,13 @@ +package racingcar; + +import org.springframework.stereotype.Component; + +@Component +public class RandomNumberGenerator implements NumberGenerator { + public static final int BOUND = 10; + + @Override + public int generate() { + return (int) (Math.random() * BOUND); + } +} diff --git a/src/main/java/racingcar/controller/RacingGameController.java b/src/main/java/racingcar/controller/RacingGameController.java new file mode 100644 index 000000000..6a6255015 --- /dev/null +++ b/src/main/java/racingcar/controller/RacingGameController.java @@ -0,0 +1,24 @@ +package racingcar.controller; + +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RestController; +import racingcar.dto.RacingGameRequest; +import racingcar.dto.RacingGameResponse; +import racingcar.service.RacingGameService; + +@RestController +public class RacingGameController { + private final RacingGameService racingGameService; + + public RacingGameController(RacingGameService racingGameService) { + this.racingGameService = racingGameService; + } + + @PostMapping("/plays") + public ResponseEntity play(@RequestBody RacingGameRequest racingGameRequest) { + RacingGameResponse response = racingGameService.play(racingGameRequest); + return ResponseEntity.ok(response); + } +} diff --git a/src/main/java/racingcar/dao/CarDao.java b/src/main/java/racingcar/dao/CarDao.java new file mode 100644 index 000000000..8c5ec9fb1 --- /dev/null +++ b/src/main/java/racingcar/dao/CarDao.java @@ -0,0 +1,13 @@ +package racingcar.dao; + +import java.util.List; +import racingcar.dto.CarDto; + +public interface CarDao { + + void save(Long gameId, CarDto carDto); + + void saveAll(Long gameId, List racingCars); + + List findByGameId(Long gameId); +} diff --git a/src/main/java/racingcar/dao/CarJdbcDao.java b/src/main/java/racingcar/dao/CarJdbcDao.java new file mode 100644 index 000000000..73d0214c9 --- /dev/null +++ b/src/main/java/racingcar/dao/CarJdbcDao.java @@ -0,0 +1,39 @@ +package racingcar.dao; + +import java.util.List; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.stereotype.Repository; +import racingcar.dto.CarDto; + +@Repository +public class CarJdbcDao implements CarDao { + private final JdbcTemplate jdbcTemplate; + + public CarJdbcDao(JdbcTemplate jdbcTemplate) { + this.jdbcTemplate = jdbcTemplate; + } + + @Override + public void save(Long gameId, CarDto carDto) { + String sql = "INSERT INTO CAR (name, position, is_win, racing_game_id) VALUES (?, ?, ?, ?)"; + jdbcTemplate.update(sql, carDto.getName(), carDto.getPosition(), carDto.isWin(), gameId); + } + + @Override + public void saveAll(Long gameId, List racingCars) { + racingCars.forEach(carDto -> save(gameId, carDto)); + } + + @Override + public List findByGameId(Long gameId) { + String sql = "SELECT name, position, is_win FROM car WHERE racing_game_id = ?"; + return jdbcTemplate.query(sql, (resultSet, rowNum) -> { + CarDto carDto = new CarDto( + resultSet.getString("name"), + resultSet.getInt("position"), + resultSet.getBoolean("is_win") + ); + return carDto; + }, gameId); + } +} diff --git a/src/main/java/racingcar/dao/RacingGameDao.java b/src/main/java/racingcar/dao/RacingGameDao.java new file mode 100644 index 000000000..e78c1e514 --- /dev/null +++ b/src/main/java/racingcar/dao/RacingGameDao.java @@ -0,0 +1,6 @@ +package racingcar.dao; + +public interface RacingGameDao { + + Long save(int trialCount); +} diff --git a/src/main/java/racingcar/dao/RacingGameJdbcDao.java b/src/main/java/racingcar/dao/RacingGameJdbcDao.java new file mode 100644 index 000000000..3f6108406 --- /dev/null +++ b/src/main/java/racingcar/dao/RacingGameJdbcDao.java @@ -0,0 +1,31 @@ +package racingcar.dao; + +import java.sql.PreparedStatement; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.jdbc.support.GeneratedKeyHolder; +import org.springframework.jdbc.support.KeyHolder; +import org.springframework.stereotype.Repository; + +@Repository +public class RacingGameJdbcDao implements RacingGameDao { + + private final JdbcTemplate jdbcTemplate; + + public RacingGameJdbcDao(JdbcTemplate jdbcTemplate) { + this.jdbcTemplate = jdbcTemplate; + } + + @Override + public Long save(int trialCount) { + String sql = "INSERT into RACING_GAME (trial_count) values (?)"; + + KeyHolder keyHolder = new GeneratedKeyHolder(); + jdbcTemplate.update(con -> { + PreparedStatement preparedStatement = con.prepareStatement(sql, new String[]{"id"}); + preparedStatement.setInt(1, trialCount); + return preparedStatement; + }, keyHolder); + + return keyHolder.getKey().longValue(); + } +} diff --git a/src/main/java/racingcar/domain/Car.java b/src/main/java/racingcar/domain/Car.java new file mode 100644 index 000000000..d69129f4f --- /dev/null +++ b/src/main/java/racingcar/domain/Car.java @@ -0,0 +1,50 @@ +package racingcar.domain; + +public class Car { + + private static final int MOVE_CONDITION = 4; + public static final int MAX_NAME_LENGTH = 5; + + private final String name; + private int position = 0; + + public Car(String name) { + validateNameLength(name); + this.name = name; + } + + private void validateNameLength(String name) { + if (name.length() > MAX_NAME_LENGTH) { + throw new IllegalArgumentException("이름이 " + name.length() + "자 입니다. " + "이름은 5자 이하로 가능합니다."); + } + } + + public void move(int number) { + if (number >= MOVE_CONDITION) { + addPosition(); + } + } + + private void addPosition() { + position++; + } + + public boolean isDraw(Car other) { + return this.position == other.position; + } + + public Car isWin(Car other) { + if (this.position > other.position) { + return this; + } + return other; + } + + public int getPosition() { + return position; + } + + public String getName() { + return name; + } +} diff --git a/src/main/java/racingcar/domain/Cars.java b/src/main/java/racingcar/domain/Cars.java new file mode 100644 index 000000000..bd8972ce2 --- /dev/null +++ b/src/main/java/racingcar/domain/Cars.java @@ -0,0 +1,52 @@ +package racingcar.domain; + +import java.util.Collections; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; +import racingcar.NumberGenerator; + +public class Cars { + + private final List cars; + + public Cars(List cars) { + validateDuplicateName(cars); + this.cars = cars; + } + + private void validateDuplicateName(List cars) { + Set distinctNames = cars.stream() + .map(Car::getName) + .collect(Collectors.toSet()); + + if (distinctNames.size() != cars.size()) { + throw new IllegalArgumentException("중복된 이름은 사용할 수 없습니다"); + } + } + + public List findWinners() { + Car maxPositionCar = findMaxPositionCar(); + + return cars.stream() + .filter(car -> car.isDraw(maxPositionCar)) + .collect(Collectors.toList()); + } + + private Car findMaxPositionCar() { + return cars.stream() + .reduce(Car::isWin) + .orElseThrow(() -> new IllegalArgumentException("자동차 위치의 최대값을 구할 수 없습니다.")); + } + + public void moveAll(NumberGenerator numberGenerator) { + for (Car car : cars) { + int moveNumber = numberGenerator.generate(); + car.move(moveNumber); + } + } + + 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 new file mode 100644 index 000000000..15465f0b7 --- /dev/null +++ b/src/main/java/racingcar/domain/RacingGame.java @@ -0,0 +1,42 @@ +package racingcar.domain; + +import racingcar.NumberGenerator; +import racingcar.RandomNumberGenerator; + +public class RacingGame { + public static final int MAX_TRY_COUNT_BOUND = 100; + + private final NumberGenerator numberGenerator; + private final Cars cars; + private int tryCount; + + public RacingGame(NumberGenerator numberGenerator, int tryCount, Cars cars) { + validateTryCount(tryCount); + this.numberGenerator = numberGenerator; + this.cars = cars; + this.tryCount = tryCount; + } + + public RacingGame(int tryCount, Cars cars) { + this(new RandomNumberGenerator(), tryCount, cars); + } + + private void validateTryCount(int tryCount) { + if (tryCount > MAX_TRY_COUNT_BOUND) { + throw new IllegalArgumentException("시도 횟수는 100회 이하만 가능합니다 현재 : " + tryCount + "회"); + } + } + + public void playOneRound() { + cars.moveAll(numberGenerator); + tryCount--; + } + + public boolean isEnd() { + return tryCount == 0; + } + + public Cars getCars() { + return cars; + } +} diff --git a/src/main/java/racingcar/dto/CarDto.java b/src/main/java/racingcar/dto/CarDto.java new file mode 100644 index 000000000..4930eee7e --- /dev/null +++ b/src/main/java/racingcar/dto/CarDto.java @@ -0,0 +1,26 @@ +package racingcar.dto; + +public class CarDto { + + private final String name; + private final int position; + private final boolean isWin; + + public CarDto(String name, int position, boolean isWin) { + this.name = name; + this.position = position; + this.isWin = isWin; + } + + public String getName() { + return name; + } + + public int getPosition() { + return position; + } + + public boolean isWin() { + return isWin; + } +} diff --git a/src/main/java/racingcar/dto/RacingGameRequest.java b/src/main/java/racingcar/dto/RacingGameRequest.java new file mode 100644 index 000000000..3e6f4c050 --- /dev/null +++ b/src/main/java/racingcar/dto/RacingGameRequest.java @@ -0,0 +1,32 @@ +package racingcar.dto; + +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + +public class RacingGameRequest { + private String names; + private int count; + + public RacingGameRequest(String names, int count) { + this.names = names; + this.count = count; + } + + public RacingGameRequest() { + + } + + public String getNames() { + return names; + } + + public List getNamesList() { + return Arrays.stream(names.split(",")) + .collect(Collectors.toList()); + } + + public int getCount() { + return count; + } +} diff --git a/src/main/java/racingcar/dto/RacingGameResponse.java b/src/main/java/racingcar/dto/RacingGameResponse.java new file mode 100644 index 000000000..5e296b44d --- /dev/null +++ b/src/main/java/racingcar/dto/RacingGameResponse.java @@ -0,0 +1,22 @@ +package racingcar.dto; + +import java.util.List; + +public class RacingGameResponse { + + private final List winners; + private final List racingCars; + + public RacingGameResponse(List winners, List racingCars) { + this.winners = winners; + this.racingCars = racingCars; + } + + public List getWinners() { + return winners; + } + + public List getRacingCars() { + return racingCars; + } +} diff --git a/src/main/java/racingcar/service/RacingGameService.java b/src/main/java/racingcar/service/RacingGameService.java new file mode 100644 index 000000000..98e9ea0a8 --- /dev/null +++ b/src/main/java/racingcar/service/RacingGameService.java @@ -0,0 +1,56 @@ +package racingcar.service; + +import java.util.List; +import java.util.stream.Collectors; +import org.springframework.stereotype.Service; +import racingcar.NumberGenerator; +import racingcar.dao.CarDao; +import racingcar.dao.RacingGameDao; +import racingcar.domain.Car; +import racingcar.domain.Cars; +import racingcar.domain.RacingGame; +import racingcar.dto.CarDto; +import racingcar.dto.RacingGameRequest; +import racingcar.dto.RacingGameResponse; + +@Service +public class RacingGameService { + private final CarDao carDao; + private final RacingGameDao racingGameDao; + private final NumberGenerator numberGenerator; + + public RacingGameService(CarDao carDao, RacingGameDao racingGameDao, NumberGenerator numberGenerator) { + this.carDao = carDao; + this.racingGameDao = racingGameDao; + this.numberGenerator = numberGenerator; + } + + public RacingGameResponse play(RacingGameRequest racingGameRequest) { + Cars cars = new Cars(racingGameRequest.getNamesList().stream() + .map(Car::new) + .collect(Collectors.toList())); + RacingGame game = new RacingGame(numberGenerator, racingGameRequest.getCount(), cars); + Long racingGameId = racingGameDao.save(racingGameRequest.getCount()); + while (!game.isEnd()) { + game.playOneRound(); + } + RacingGameResponse result = getResult(game); + carDao.saveAll(racingGameId, result.getRacingCars()); + return result; + } + + private RacingGameResponse getResult(RacingGame racingGame) { + Cars cars = racingGame.getCars(); + List winners = cars.findWinners(); + List racingCars = cars.getCars().stream() + .map(car -> new CarDto(car.getName(), car.getPosition(), winners.contains(car))) + .collect(Collectors.toList()); + return new RacingGameResponse(getWinnerNames(winners), racingCars); + } + + private List getWinnerNames(List winners) { + return winners.stream() + .map(Car::getName) + .collect(Collectors.toList()); + } +} diff --git a/src/main/java/racingcar/view/InputView.java b/src/main/java/racingcar/view/InputView.java new file mode 100644 index 000000000..a733d4d79 --- /dev/null +++ b/src/main/java/racingcar/view/InputView.java @@ -0,0 +1,35 @@ +package racingcar.view; + +import java.util.Arrays; +import java.util.List; +import java.util.Scanner; +import java.util.stream.Collectors; + +public class InputView { + public static final String INPUT_NAMES = "경주할 자동차 이름을 입력하세요(이름은 쉼표(,)를 기준으로 구분)."; + public static final String INPUT_TRY_COUNT = "시도할 회수는 몇회인가요?"; + public static final String SPLIT_DELIMITER = ","; + + private static final Scanner SCANNER = new Scanner(System.in); + + public static List inputNames() { + System.out.println(INPUT_NAMES); + String input = SCANNER.next(); + return Arrays.stream(input.split(SPLIT_DELIMITER)) + .collect(Collectors.toList()); + } + + public static int inputTryCount() { + System.out.println(INPUT_TRY_COUNT); + return inputNumber(); + } + + private static int inputNumber() { + try { + String input = SCANNER.next(); + return Integer.parseInt(input); + } catch (NumberFormatException e) { + throw new IllegalArgumentException("숫자만 입력 가능합니다."); + } + } +} diff --git a/src/main/java/racingcar/view/OutputView.java b/src/main/java/racingcar/view/OutputView.java new file mode 100644 index 000000000..cd50f90a2 --- /dev/null +++ b/src/main/java/racingcar/view/OutputView.java @@ -0,0 +1,26 @@ +package racingcar.view; + +import java.util.List; +import racingcar.domain.Car; +import racingcar.domain.Cars; + +public class OutputView { + public static final String GAME_RESULT_FORMAT = "%s : %s\n"; + public static final String WINNER_FORMAT = "%s가 최종 우승했습니다.\n"; + private static final String POSITION_VIEW = "-"; + public static final String WINNER_DELIMITER = ", "; + + public static void printRacing(Cars cars) { + for (Car car : cars.getCars()) { + int position = car.getPosition(); + String positionView = POSITION_VIEW.repeat(position); + System.out.printf(GAME_RESULT_FORMAT, car.getName(), positionView); + } + System.out.println(); + } + + public static void printWinners(List names) { + String winners = String.join(WINNER_DELIMITER, names); + System.out.printf(WINNER_FORMAT, winners); + } +} diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index e69de29bb..119af0059 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -0,0 +1,3 @@ +spring.h2.console.enabled=true +spring.datasource.url=jdbc:h2:mem:testdb;MODE=MySQL +spring.datasource.driver-class-name=org.h2.Driver diff --git a/src/main/resources/data.sql b/src/main/resources/data.sql index 56f06adfd..c2e1039a0 100644 --- a/src/main/resources/data.sql +++ b/src/main/resources/data.sql @@ -1,7 +1,21 @@ --- TODO: 기능 구현에 필요한 내용을 추가하거나 수정하세요. -CREATE TABLE PLAY_RESULT ( +DROP TABLE IF EXISTS car; +DROP TABLE IF EXISTS racing_game; + +CREATE TABLE RACING_GAME +( id INT NOT NULL AUTO_INCREMENT, - winners VARCHAR(50) NOT NULL, + trial_count INT NOT NULL, created_at DATETIME NOT NULL default current_timestamp, PRIMARY KEY (id) ); + +CREATE TABLE CAR +( + id INT NOT NULL AUTO_INCREMENT, + name VARCHAR(50) NOT NULL, + position INT NOT NULL, + is_win SMALLINT NOT NULL , + racing_game_id INT NOT NULL, + PRIMARY KEY (id), + FOREIGN KEY (racing_game_id) REFERENCES RACING_GAME (id) +) diff --git a/src/test/java/racingcar/AlwaysMoveNumberGenerator.java b/src/test/java/racingcar/AlwaysMoveNumberGenerator.java new file mode 100644 index 000000000..06b89ad98 --- /dev/null +++ b/src/test/java/racingcar/AlwaysMoveNumberGenerator.java @@ -0,0 +1,17 @@ +package racingcar; + +import javax.annotation.Priority; +import org.springframework.boot.test.context.TestComponent; + +@TestComponent +@Priority(1) +public class AlwaysMoveNumberGenerator implements NumberGenerator { + + public static final int MOVE_NUMBER = 4; + + @Override + public int generate() { + return MOVE_NUMBER; + } + +} diff --git a/src/test/java/racingcar/controller/RacingGameControllerTest.java b/src/test/java/racingcar/controller/RacingGameControllerTest.java new file mode 100644 index 000000000..32b32aa9f --- /dev/null +++ b/src/test/java/racingcar/controller/RacingGameControllerTest.java @@ -0,0 +1,48 @@ +package racingcar.controller; + +import static org.hamcrest.Matchers.is; + +import io.restassured.RestAssured; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; +import org.springframework.boot.test.web.server.LocalServerPort; +import org.springframework.context.annotation.Import; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import racingcar.AlwaysMoveNumberGenerator; +import racingcar.dto.RacingGameRequest; +import racingcar.dto.RacingGameResponse; +import racingcar.service.RacingGameService; + +@Import(value = AlwaysMoveNumberGenerator.class) +@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT) +class RacingGameControllerTest { + + @Autowired + RacingGameService racingGameService; + + @LocalServerPort + int port; + + @BeforeEach + void setUp() { + RestAssured.port = port; + } + + @Test + void playGame() { + RacingGameRequest request = new RacingGameRequest("브리,토미,브라운", 10); + RacingGameResponse response = racingGameService.play(request); + RestAssured.given().log().all() + .contentType(MediaType.APPLICATION_JSON_VALUE) + .body(request) + .when().post("/plays") + .then().log().all() + .statusCode(HttpStatus.OK.value()) + .body("winners", is(response.getWinners())); + } + +} diff --git a/src/test/java/racingcar/dao/CarJdbcDaoTest.java b/src/test/java/racingcar/dao/CarJdbcDaoTest.java new file mode 100644 index 000000000..79a52eb79 --- /dev/null +++ b/src/test/java/racingcar/dao/CarJdbcDaoTest.java @@ -0,0 +1,49 @@ +package racingcar.dao; + +import java.util.List; +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.transaction.annotation.Transactional; +import racingcar.dto.CarDto; + +@SpringBootTest +@Transactional +class CarJdbcDaoTest { + + @Autowired + private CarDao carDao; + + @Autowired + private JdbcTemplate jdbcTemplate; + + private Long gameId; + + @BeforeEach + void setUp() { + RacingGameDao racingGameDao = new RacingGameJdbcDao(jdbcTemplate); + gameId = racingGameDao.save(10); + + CarDto carDto1 = new CarDto("boxster", 10, true); + CarDto carDto2 = new CarDto("encho", 7, false); + + jdbcTemplate.update("INSERT INTO CAR (name, position, is_win, racing_game_id) VALUES (?, ?, ?, ?)", + carDto1.getName(), carDto1.getPosition(), carDto1.isWin(), gameId); + jdbcTemplate.update("INSERT INTO CAR (name, position, is_win, racing_game_id) VALUES (?, ?, ?, ?)", + carDto2.getName(), carDto2.getPosition(), carDto2.isWin(), gameId); + + } + + @Test + void insert() { + CarDto carDto = new CarDto("car", 9, false); + carDao.save(gameId, carDto); + + List cars = carDao.findByGameId(gameId); + + Assertions.assertThat(cars).hasSize(3); + } +} diff --git a/src/test/java/racingcar/dao/RacingGameJdbcDaoTest.java b/src/test/java/racingcar/dao/RacingGameJdbcDaoTest.java new file mode 100644 index 000000000..451dc13fc --- /dev/null +++ b/src/test/java/racingcar/dao/RacingGameJdbcDaoTest.java @@ -0,0 +1,31 @@ +package racingcar.dao; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.transaction.annotation.Transactional; + +@SpringBootTest +@Transactional +class RacingGameJdbcDaoTest { + + @Autowired + private RacingGameDao racingGameDao; + + @Autowired + private JdbcTemplate jdbcTemplate; + + @Test + void insert() { + int trialCount = 5; + Long gameId = racingGameDao.save(trialCount); + + String sql = "SELECT trial_count FROM racing_game WHERE id = " + gameId; + Integer savedTrialCount = jdbcTemplate.queryForObject(sql, Integer.class); + + assertThat(trialCount).isEqualTo(savedTrialCount); + } +} diff --git a/src/test/java/racingcar/domain/CarTest.java b/src/test/java/racingcar/domain/CarTest.java new file mode 100644 index 000000000..16a662c94 --- /dev/null +++ b/src/test/java/racingcar/domain/CarTest.java @@ -0,0 +1,51 @@ +package racingcar.domain; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +class CarTest { + + @Test + @DisplayName("이름이 5글자 초과면 예외") + void nameLengthEx() { + assertThatThrownBy(() -> new Car("loooooongName")) + .isInstanceOf(IllegalArgumentException.class); + } + + @Test + @DisplayName("4 이상의 숫자가 들어오면 position은 1 증가한다.") + void moveTest() { + Car car = new Car("test"); + + car.move(4); + + int position = car.getPosition(); + assertThat(position).isEqualTo(1); + } + + @Test + @DisplayName("3 이하의 숫자가 들어오면 position은 증가하지않는다.") + void notMoveTest() { + Car boxster = new Car("boxer"); + Car anotherBoxster = boxster; + boxster.move(4); + + assertThat(anotherBoxster.getPosition()).isEqualTo(1); + } + + @Test + @DisplayName("position이 앞에 있는 차를 반환한다") + void winTest() { + Car boxster = new Car("박스터"); + Car hyundai = new Car("현대"); + + boxster.move(4); + + Car win = boxster.isWin(hyundai); + + assertThat(win).isEqualTo(boxster); + } +} diff --git a/src/test/java/racingcar/domain/CarsTest.java b/src/test/java/racingcar/domain/CarsTest.java new file mode 100644 index 000000000..9d5d5591b --- /dev/null +++ b/src/test/java/racingcar/domain/CarsTest.java @@ -0,0 +1,60 @@ +package racingcar.domain; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import java.util.List; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +class CarsTest { + private final Car boxster = new Car("박스터"); + private final Car sonata = new Car("소나타"); + private final Car benz = new Car("벤츠"); + + private final List dummy = List.of( + boxster, + sonata, + benz + ); + + + @Test + @DisplayName("Position이 가장 큰 Car를 여러개 반환한다.") + void getFirstCarsTest() { + Cars cars = new Cars(dummy); + + int moveNumber = 4; + boxster.move(moveNumber); + sonata.move(moveNumber); + + List firstCars = cars.findWinners(); + + assertThat(firstCars).containsOnly(boxster, sonata); + } + + @Test + @DisplayName("Position이 가장 큰 Car 하나를 반환한다.") + void getFirstPositionTest() { + Cars cars = new Cars(dummy); + + int moveNumber = 4; + boxster.move(moveNumber); + + List firstCars = cars.findWinners(); + + assertThat(firstCars).containsOnly(boxster); + } + + @Test + @DisplayName("중복된 이름이면 예외가 발생한다") + void duplicateNameEx() { + List duplicateNameCars = List.of(new Car("박스터"), new Car("박스터")); + + assertThatThrownBy(()-> new Cars(duplicateNameCars)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("중복된 이름은 사용할 수 없습니다"); + + } + +} diff --git a/src/test/java/racingcar/domain/RacingGameTest.java b/src/test/java/racingcar/domain/RacingGameTest.java new file mode 100644 index 000000000..9a5402c2e --- /dev/null +++ b/src/test/java/racingcar/domain/RacingGameTest.java @@ -0,0 +1,117 @@ +package racingcar.domain; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.junit.jupiter.api.Assertions.assertAll; + +import java.util.List; +import java.util.stream.Collectors; +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 racingcar.NumberGenerator; + +class RacingGameTest { + private final AlwaysMoveNumberGenerator alwaysMoveNumberGenerator = new AlwaysMoveNumberGenerator(); + private final NeverMoveNumberGenerator neverMoveNumberGenerator = new NeverMoveNumberGenerator(); + + private final Car boxster = new Car("박스터"); + private final Car sonata = new Car("소나타"); + private final Car benz = new Car("벤츠"); + + private final Cars dummy = new Cars(List.of( + boxster, + sonata, + benz + )); + + @Test + @DisplayName("레이싱 게임을 한 라운드 진행할 때 4이상의 숫자가 주어지면 우승자의 위치가 1이다.") + void moveTest() { + RacingGame game = new RacingGame(alwaysMoveNumberGenerator, 3, dummy); + game.playOneRound(); + + List carList = dummy.getCars(); + List names = carsToNames(carList); + List positions = carsToPositions(carList); + + assertAll(() -> { + assertThat(names).contains("박스터", "소나타", "벤츠"); + assertThat(positions).containsOnly(1); + }); + } + + @Test + @DisplayName("레이싱 게임을 한 라운드 진행할 때 3이하의 숫자가 주어지면 우승자의 위치가 0이다.") + void notMoveTest() { + RacingGame game = new RacingGame(neverMoveNumberGenerator, 3, dummy); + game.playOneRound(); + + List carList = dummy.getCars(); + List names = carsToNames(carList); + List positions = carsToPositions(carList); + + assertAll(() -> { + assertThat(names).contains("박스터", "소나타", "벤츠"); + assertThat(positions).containsOnly(0); + }); + } + + @DisplayName("isEnd 메소드는 게임 종료 여부를 반환한다") + @ParameterizedTest(name = "시도 횟수가 {0}일 때 {1}번 시도하면 {2}") + @CsvSource(value = {"3:1:false", "3:2:false", "3:3:true"}, delimiter = ':') + void isEndTest(int count, int tryCount, boolean result) { + RacingGame game = new RacingGame(alwaysMoveNumberGenerator, count, dummy); + + for (int i = 0; i < tryCount; i++) { + game.playOneRound(); + } + + assertThat(game.isEnd()).isEqualTo(result); + } + + @Test + @DisplayName("시도 횟수가 100회 초과하면 예외가 발생한다") + void tryCountEx() { + int tryCount = 101; + + assertThatThrownBy(() -> new RacingGame(tryCount, dummy)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("시도 횟수는 100회 이하만 가능합니다 현재 : " + tryCount + "회"); + } + + static class AlwaysMoveNumberGenerator implements NumberGenerator { + + public static final int MOVE_NUMBER = 4; + + @Override + public int generate() { + return MOVE_NUMBER; + } + + } + + static class NeverMoveNumberGenerator implements NumberGenerator { + + public static final int NOT_MOVE_NUMBER = 3; + + @Override + public int generate() { + return NOT_MOVE_NUMBER; + } + + } + + private List carsToNames(List carList) { + return carList.stream() + .map(Car::getName) + .collect(Collectors.toList()); + } + + private List carsToPositions(List carList) { + return carList.stream() + .map(Car::getPosition) + .collect(Collectors.toList()); + } +} diff --git a/src/test/java/racingcar/service/RacingGameServiceTest.java b/src/test/java/racingcar/service/RacingGameServiceTest.java new file mode 100644 index 000000000..0afd17d85 --- /dev/null +++ b/src/test/java/racingcar/service/RacingGameServiceTest.java @@ -0,0 +1,33 @@ +package racingcar.service; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.util.List; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.context.annotation.Import; +import org.springframework.transaction.annotation.Transactional; +import racingcar.AlwaysMoveNumberGenerator; +import racingcar.dto.RacingGameRequest; +import racingcar.dto.RacingGameResponse; + +@Import(value = AlwaysMoveNumberGenerator.class) +@Transactional +@SpringBootTest +class RacingGameServiceTest { + + @Autowired + RacingGameService racingGameService; + + @Test + void playTest() { + RacingGameRequest racingGameRequest = new RacingGameRequest("박스터,엔초", 10); + List winners = List.of("박스터", "엔초"); + + RacingGameResponse play = racingGameService.play(racingGameRequest); + + assertThat(play.getWinners()).isEqualTo(winners); + assertThat(play.getRacingCars()).hasSize(2); + } +} From 479dd33600892cd02c79d36732de5065616808a9 Mon Sep 17 00:00:00 2001 From: drunkenhw Date: Fri, 14 Apr 2023 16:03:04 +0900 Subject: [PATCH 02/31] =?UTF-8?q?refactor:=20domain=EC=97=90=EC=84=9C=20sp?= =?UTF-8?q?ring=20=EC=9D=98=EC=A1=B4=EC=84=B1=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/racingcar/domain/Cars.java | 1 - .../{ => domain}/NumberGenerator.java | 2 +- .../java/racingcar/domain/RacingGame.java | 3 --- .../{ => domain}/RandomNumberGenerator.java | 5 +---- .../racingcar/service/RacingGameService.java | 7 ++---- .../racingcar/AlwaysMoveNumberGenerator.java | 17 -------------- .../controller/RacingGameControllerTest.java | 22 +++++-------------- .../java/racingcar/domain/RacingGameTest.java | 1 - .../service/RacingGameServiceTest.java | 14 +++++------- 9 files changed, 14 insertions(+), 58 deletions(-) rename src/main/java/racingcar/{ => domain}/NumberGenerator.java (69%) rename src/main/java/racingcar/{ => domain}/RandomNumberGenerator.java (71%) delete mode 100644 src/test/java/racingcar/AlwaysMoveNumberGenerator.java diff --git a/src/main/java/racingcar/domain/Cars.java b/src/main/java/racingcar/domain/Cars.java index bd8972ce2..f90900e53 100644 --- a/src/main/java/racingcar/domain/Cars.java +++ b/src/main/java/racingcar/domain/Cars.java @@ -4,7 +4,6 @@ import java.util.List; import java.util.Set; import java.util.stream.Collectors; -import racingcar.NumberGenerator; public class Cars { diff --git a/src/main/java/racingcar/NumberGenerator.java b/src/main/java/racingcar/domain/NumberGenerator.java similarity index 69% rename from src/main/java/racingcar/NumberGenerator.java rename to src/main/java/racingcar/domain/NumberGenerator.java index 3669cd4ad..7a09dcd10 100644 --- a/src/main/java/racingcar/NumberGenerator.java +++ b/src/main/java/racingcar/domain/NumberGenerator.java @@ -1,4 +1,4 @@ -package racingcar; +package racingcar.domain; public interface NumberGenerator { int generate(); diff --git a/src/main/java/racingcar/domain/RacingGame.java b/src/main/java/racingcar/domain/RacingGame.java index 15465f0b7..0cc997cf0 100644 --- a/src/main/java/racingcar/domain/RacingGame.java +++ b/src/main/java/racingcar/domain/RacingGame.java @@ -1,8 +1,5 @@ package racingcar.domain; -import racingcar.NumberGenerator; -import racingcar.RandomNumberGenerator; - public class RacingGame { public static final int MAX_TRY_COUNT_BOUND = 100; diff --git a/src/main/java/racingcar/RandomNumberGenerator.java b/src/main/java/racingcar/domain/RandomNumberGenerator.java similarity index 71% rename from src/main/java/racingcar/RandomNumberGenerator.java rename to src/main/java/racingcar/domain/RandomNumberGenerator.java index 9650a606c..570fa2a66 100644 --- a/src/main/java/racingcar/RandomNumberGenerator.java +++ b/src/main/java/racingcar/domain/RandomNumberGenerator.java @@ -1,8 +1,5 @@ -package racingcar; +package racingcar.domain; -import org.springframework.stereotype.Component; - -@Component public class RandomNumberGenerator implements NumberGenerator { public static final int BOUND = 10; diff --git a/src/main/java/racingcar/service/RacingGameService.java b/src/main/java/racingcar/service/RacingGameService.java index 98e9ea0a8..75828303a 100644 --- a/src/main/java/racingcar/service/RacingGameService.java +++ b/src/main/java/racingcar/service/RacingGameService.java @@ -3,7 +3,6 @@ import java.util.List; import java.util.stream.Collectors; import org.springframework.stereotype.Service; -import racingcar.NumberGenerator; import racingcar.dao.CarDao; import racingcar.dao.RacingGameDao; import racingcar.domain.Car; @@ -17,19 +16,17 @@ public class RacingGameService { private final CarDao carDao; private final RacingGameDao racingGameDao; - private final NumberGenerator numberGenerator; - public RacingGameService(CarDao carDao, RacingGameDao racingGameDao, NumberGenerator numberGenerator) { + public RacingGameService(CarDao carDao, RacingGameDao racingGameDao) { this.carDao = carDao; this.racingGameDao = racingGameDao; - this.numberGenerator = numberGenerator; } public RacingGameResponse play(RacingGameRequest racingGameRequest) { Cars cars = new Cars(racingGameRequest.getNamesList().stream() .map(Car::new) .collect(Collectors.toList())); - RacingGame game = new RacingGame(numberGenerator, racingGameRequest.getCount(), cars); + RacingGame game = new RacingGame(racingGameRequest.getCount(), cars); Long racingGameId = racingGameDao.save(racingGameRequest.getCount()); while (!game.isEnd()) { game.playOneRound(); diff --git a/src/test/java/racingcar/AlwaysMoveNumberGenerator.java b/src/test/java/racingcar/AlwaysMoveNumberGenerator.java deleted file mode 100644 index 06b89ad98..000000000 --- a/src/test/java/racingcar/AlwaysMoveNumberGenerator.java +++ /dev/null @@ -1,17 +0,0 @@ -package racingcar; - -import javax.annotation.Priority; -import org.springframework.boot.test.context.TestComponent; - -@TestComponent -@Priority(1) -public class AlwaysMoveNumberGenerator implements NumberGenerator { - - public static final int MOVE_NUMBER = 4; - - @Override - public int generate() { - return MOVE_NUMBER; - } - -} diff --git a/src/test/java/racingcar/controller/RacingGameControllerTest.java b/src/test/java/racingcar/controller/RacingGameControllerTest.java index 32b32aa9f..a004ca472 100644 --- a/src/test/java/racingcar/controller/RacingGameControllerTest.java +++ b/src/test/java/racingcar/controller/RacingGameControllerTest.java @@ -1,48 +1,36 @@ package racingcar.controller; -import static org.hamcrest.Matchers.is; - import io.restassured.RestAssured; +import org.hamcrest.Matchers; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; import org.springframework.boot.test.web.server.LocalServerPort; -import org.springframework.context.annotation.Import; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; -import racingcar.AlwaysMoveNumberGenerator; import racingcar.dto.RacingGameRequest; -import racingcar.dto.RacingGameResponse; -import racingcar.service.RacingGameService; -@Import(value = AlwaysMoveNumberGenerator.class) @SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT) class RacingGameControllerTest { - @Autowired - RacingGameService racingGameService; - - @LocalServerPort - int port; - @BeforeEach - void setUp() { + void setUp(@LocalServerPort int port) { RestAssured.port = port; } @Test void playGame() { RacingGameRequest request = new RacingGameRequest("브리,토미,브라운", 10); - RacingGameResponse response = racingGameService.play(request); + RestAssured.given().log().all() .contentType(MediaType.APPLICATION_JSON_VALUE) .body(request) .when().post("/plays") .then().log().all() .statusCode(HttpStatus.OK.value()) - .body("winners", is(response.getWinners())); + .body("winners", Matchers.notNullValue()) + .body("racingCars", Matchers.hasSize(3)); } } diff --git a/src/test/java/racingcar/domain/RacingGameTest.java b/src/test/java/racingcar/domain/RacingGameTest.java index 9a5402c2e..203ebec62 100644 --- a/src/test/java/racingcar/domain/RacingGameTest.java +++ b/src/test/java/racingcar/domain/RacingGameTest.java @@ -10,7 +10,6 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.CsvSource; -import racingcar.NumberGenerator; class RacingGameTest { private final AlwaysMoveNumberGenerator alwaysMoveNumberGenerator = new AlwaysMoveNumberGenerator(); diff --git a/src/test/java/racingcar/service/RacingGameServiceTest.java b/src/test/java/racingcar/service/RacingGameServiceTest.java index 0afd17d85..0cc27fa86 100644 --- a/src/test/java/racingcar/service/RacingGameServiceTest.java +++ b/src/test/java/racingcar/service/RacingGameServiceTest.java @@ -1,19 +1,14 @@ package racingcar.service; import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertAll; -import java.util.List; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.context.annotation.Import; -import org.springframework.transaction.annotation.Transactional; -import racingcar.AlwaysMoveNumberGenerator; import racingcar.dto.RacingGameRequest; import racingcar.dto.RacingGameResponse; -@Import(value = AlwaysMoveNumberGenerator.class) -@Transactional @SpringBootTest class RacingGameServiceTest { @@ -23,11 +18,12 @@ class RacingGameServiceTest { @Test void playTest() { RacingGameRequest racingGameRequest = new RacingGameRequest("박스터,엔초", 10); - List winners = List.of("박스터", "엔초"); RacingGameResponse play = racingGameService.play(racingGameRequest); - assertThat(play.getWinners()).isEqualTo(winners); - assertThat(play.getRacingCars()).hasSize(2); + assertAll( + () -> assertThat(play.getWinners()).isNotEmpty(), + () -> assertThat(play.getRacingCars()).hasSize(2) + ); } } From fa3f0818479aee4e40195150155f30065592f6ff Mon Sep 17 00:00:00 2001 From: drunkenhw Date: Fri, 14 Apr 2023 21:15:03 +0900 Subject: [PATCH 03/31] =?UTF-8?q?refactor:=20save=20all=20=EB=A9=94?= =?UTF-8?q?=EC=84=9C=EB=93=9C=20batch=20update=EB=A1=9C=20=EB=B3=80?= =?UTF-8?q?=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 | 4 -- src/main/java/racingcar/dao/CarJdbcDao.java | 37 +++++++++---------- .../java/racingcar/dao/CarJdbcDaoTest.java | 24 ++++++------ .../racingcar/dao/RacingGameJdbcDaoTest.java | 14 ++++--- 4 files changed, 37 insertions(+), 42 deletions(-) diff --git a/src/main/java/racingcar/dao/CarDao.java b/src/main/java/racingcar/dao/CarDao.java index 8c5ec9fb1..02285f7e2 100644 --- a/src/main/java/racingcar/dao/CarDao.java +++ b/src/main/java/racingcar/dao/CarDao.java @@ -5,9 +5,5 @@ public interface CarDao { - void save(Long gameId, CarDto carDto); - void saveAll(Long gameId, List racingCars); - - List findByGameId(Long gameId); } diff --git a/src/main/java/racingcar/dao/CarJdbcDao.java b/src/main/java/racingcar/dao/CarJdbcDao.java index 73d0214c9..5a1b29736 100644 --- a/src/main/java/racingcar/dao/CarJdbcDao.java +++ b/src/main/java/racingcar/dao/CarJdbcDao.java @@ -1,6 +1,9 @@ package racingcar.dao; +import java.sql.PreparedStatement; +import java.sql.SQLException; import java.util.List; +import org.springframework.jdbc.core.BatchPreparedStatementSetter; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.stereotype.Repository; import racingcar.dto.CarDto; @@ -13,27 +16,23 @@ public CarJdbcDao(JdbcTemplate jdbcTemplate) { this.jdbcTemplate = jdbcTemplate; } - @Override - public void save(Long gameId, CarDto carDto) { - String sql = "INSERT INTO CAR (name, position, is_win, racing_game_id) VALUES (?, ?, ?, ?)"; - jdbcTemplate.update(sql, carDto.getName(), carDto.getPosition(), carDto.isWin(), gameId); - } - @Override public void saveAll(Long gameId, List racingCars) { - racingCars.forEach(carDto -> save(gameId, carDto)); - } + String sql = "INSERT INTO CAR (name, position, is_win, racing_game_id) VALUES (?, ?, ?, ?)"; + jdbcTemplate.batchUpdate(sql, new BatchPreparedStatementSetter() { + @Override + public void setValues(PreparedStatement ps, int i) throws SQLException { + CarDto carDto = racingCars.get(i); + ps.setString(1, carDto.getName()); + ps.setInt(2, carDto.getPosition()); + ps.setBoolean(3, carDto.isWin()); + ps.setLong(4, gameId); + } - @Override - public List findByGameId(Long gameId) { - String sql = "SELECT name, position, is_win FROM car WHERE racing_game_id = ?"; - return jdbcTemplate.query(sql, (resultSet, rowNum) -> { - CarDto carDto = new CarDto( - resultSet.getString("name"), - resultSet.getInt("position"), - resultSet.getBoolean("is_win") - ); - return carDto; - }, gameId); + @Override + public int getBatchSize() { + return racingCars.size(); + } + }); } } diff --git a/src/test/java/racingcar/dao/CarJdbcDaoTest.java b/src/test/java/racingcar/dao/CarJdbcDaoTest.java index 79a52eb79..49216c2b0 100644 --- a/src/test/java/racingcar/dao/CarJdbcDaoTest.java +++ b/src/test/java/racingcar/dao/CarJdbcDaoTest.java @@ -1,29 +1,26 @@ package racingcar.dao; +import static org.assertj.core.api.Assertions.assertThat; + import java.util.List; -import org.assertj.core.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.autoconfigure.jdbc.JdbcTest; import org.springframework.jdbc.core.JdbcTemplate; -import org.springframework.transaction.annotation.Transactional; import racingcar.dto.CarDto; -@SpringBootTest -@Transactional +@JdbcTest class CarJdbcDaoTest { - @Autowired - private CarDao carDao; - @Autowired private JdbcTemplate jdbcTemplate; - + private CarDao carDao; private Long gameId; @BeforeEach void setUp() { + carDao = new CarJdbcDao(jdbcTemplate); RacingGameDao racingGameDao = new RacingGameJdbcDao(jdbcTemplate); gameId = racingGameDao.save(10); @@ -39,11 +36,12 @@ void setUp() { @Test void insert() { - CarDto carDto = new CarDto("car", 9, false); - carDao.save(gameId, carDto); + List carDtos = List.of(new CarDto("gumak", 9, false), new CarDto("benz", 10, true)); + + carDao.saveAll(gameId, carDtos); - List cars = carDao.findByGameId(gameId); + Integer count = jdbcTemplate.queryForObject("SELECT count(*) FROM CAR", Integer.class); - Assertions.assertThat(cars).hasSize(3); + assertThat(count).isEqualTo(4); } } diff --git a/src/test/java/racingcar/dao/RacingGameJdbcDaoTest.java b/src/test/java/racingcar/dao/RacingGameJdbcDaoTest.java index 451dc13fc..0794eabcd 100644 --- a/src/test/java/racingcar/dao/RacingGameJdbcDaoTest.java +++ b/src/test/java/racingcar/dao/RacingGameJdbcDaoTest.java @@ -2,21 +2,23 @@ import static org.assertj.core.api.Assertions.assertThat; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.autoconfigure.jdbc.JdbcTest; import org.springframework.jdbc.core.JdbcTemplate; -import org.springframework.transaction.annotation.Transactional; -@SpringBootTest -@Transactional +@JdbcTest class RacingGameJdbcDaoTest { @Autowired + private JdbcTemplate jdbcTemplate; private RacingGameDao racingGameDao; - @Autowired - private JdbcTemplate jdbcTemplate; + @BeforeEach + void setUp() { + racingGameDao = new RacingGameJdbcDao(jdbcTemplate); + } @Test void insert() { From 11d31ed3de641e06846b6a6f8ad9078b3f279c93 Mon Sep 17 00:00:00 2001 From: drunkenhw Date: Fri, 14 Apr 2023 21:16:07 +0900 Subject: [PATCH 04/31] =?UTF-8?q?refactor:=20=EB=B6=88=ED=95=84=EC=9A=94?= =?UTF-8?q?=ED=95=9C=20=EC=96=B4=EB=85=B8=ED=85=8C=EC=9D=B4=EC=85=98=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/dao/CarJdbcDao.java | 5 +++-- src/main/java/racingcar/dao/RacingGameJdbcDao.java | 4 ++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/main/java/racingcar/dao/CarJdbcDao.java b/src/main/java/racingcar/dao/CarJdbcDao.java index 5a1b29736..cf40e93b4 100644 --- a/src/main/java/racingcar/dao/CarJdbcDao.java +++ b/src/main/java/racingcar/dao/CarJdbcDao.java @@ -5,11 +5,12 @@ import java.util.List; import org.springframework.jdbc.core.BatchPreparedStatementSetter; import org.springframework.jdbc.core.JdbcTemplate; -import org.springframework.stereotype.Repository; +import org.springframework.stereotype.Component; import racingcar.dto.CarDto; -@Repository +@Component public class CarJdbcDao implements CarDao { + private final JdbcTemplate jdbcTemplate; public CarJdbcDao(JdbcTemplate jdbcTemplate) { diff --git a/src/main/java/racingcar/dao/RacingGameJdbcDao.java b/src/main/java/racingcar/dao/RacingGameJdbcDao.java index 3f6108406..486782084 100644 --- a/src/main/java/racingcar/dao/RacingGameJdbcDao.java +++ b/src/main/java/racingcar/dao/RacingGameJdbcDao.java @@ -4,9 +4,9 @@ import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.support.GeneratedKeyHolder; import org.springframework.jdbc.support.KeyHolder; -import org.springframework.stereotype.Repository; +import org.springframework.stereotype.Component; -@Repository +@Component public class RacingGameJdbcDao implements RacingGameDao { private final JdbcTemplate jdbcTemplate; From 314f00d8bc4524cdde72ce6f9ae80b46a77b8b29 Mon Sep 17 00:00:00 2001 From: drunkenhw Date: Fri, 14 Apr 2023 21:19:59 +0900 Subject: [PATCH 05/31] =?UTF-8?q?test:=20controller=20=EB=8B=A8=EC=9C=84?= =?UTF-8?q?=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/RacingGameControllerTest.java | 68 ++++++++++++------- .../racingcar/integration/GamePlayTest.java | 38 +++++++++++ 2 files changed, 81 insertions(+), 25 deletions(-) create mode 100644 src/test/java/racingcar/integration/GamePlayTest.java diff --git a/src/test/java/racingcar/controller/RacingGameControllerTest.java b/src/test/java/racingcar/controller/RacingGameControllerTest.java index a004ca472..17dc8ef39 100644 --- a/src/test/java/racingcar/controller/RacingGameControllerTest.java +++ b/src/test/java/racingcar/controller/RacingGameControllerTest.java @@ -1,36 +1,54 @@ package racingcar.controller; -import io.restassured.RestAssured; -import org.hamcrest.Matchers; -import org.junit.jupiter.api.BeforeEach; +import static org.hamcrest.Matchers.hasSize; +import static org.hamcrest.Matchers.is; +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; + +import com.fasterxml.jackson.databind.ObjectMapper; +import java.util.List; import org.junit.jupiter.api.Test; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; -import org.springframework.boot.test.web.server.LocalServerPort; -import org.springframework.http.HttpStatus; +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.http.MediaType; +import org.springframework.test.web.servlet.MockMvc; +import racingcar.dto.CarDto; import racingcar.dto.RacingGameRequest; +import racingcar.dto.RacingGameResponse; +import racingcar.service.RacingGameService; -@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT) -class RacingGameControllerTest { +@WebMvcTest(RacingGameController.class) +public class RacingGameControllerTest { - @BeforeEach - void setUp(@LocalServerPort int port) { - RestAssured.port = port; - } + @Autowired + private MockMvc mockMvc; + + @Autowired + private ObjectMapper objectMapper; + + @MockBean + private RacingGameService racingGameService; @Test - void playGame() { - RacingGameRequest request = new RacingGameRequest("브리,토미,브라운", 10); - - RestAssured.given().log().all() - .contentType(MediaType.APPLICATION_JSON_VALUE) - .body(request) - .when().post("/plays") - .then().log().all() - .statusCode(HttpStatus.OK.value()) - .body("winners", Matchers.notNullValue()) - .body("racingCars", Matchers.hasSize(3)); - } + public void plays() throws Exception { + RacingGameRequest request = new RacingGameRequest("현구막,박스터", 10); + RacingGameResponse expectedResponse = new RacingGameResponse( + List.of("현구막"), + List.of(new CarDto("현구막", 10, true), new CarDto("박스터", 7, false)) + ); + String requestString = objectMapper.writeValueAsString(request); + when(racingGameService.play(any())).thenReturn(expectedResponse); + + mockMvc.perform(post("/plays") + .contentType(MediaType.APPLICATION_JSON) + .content(requestString)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.winners", is(List.of("현구막")))) + .andExpect(jsonPath("$.racingCars", hasSize(2))); + } } diff --git a/src/test/java/racingcar/integration/GamePlayTest.java b/src/test/java/racingcar/integration/GamePlayTest.java new file mode 100644 index 000000000..20617675f --- /dev/null +++ b/src/test/java/racingcar/integration/GamePlayTest.java @@ -0,0 +1,38 @@ +package racingcar.integration; + +import static org.hamcrest.Matchers.hasSize; +import static org.hamcrest.Matchers.notNullValue; + +import io.restassured.RestAssured; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; +import org.springframework.boot.test.web.server.LocalServerPort; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import racingcar.dto.RacingGameRequest; + +@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT) +class GamePlayTest { + + @BeforeEach + void setUp(@LocalServerPort int port) { + RestAssured.port = port; + } + + @Test + void playGame() { + RacingGameRequest request = new RacingGameRequest("브리,토미,브라운", 10); + RestAssured.given().log().all() + .contentType(MediaType.APPLICATION_JSON_VALUE) + .body(request) + + .when().post("/plays") + + .then().log().all() + .statusCode(HttpStatus.OK.value()) + .body("winners", notNullValue()) + .body("racingCars", hasSize(3)); + } +} From 6a0e14246624b5a18c80f70359f396d522c32d84 Mon Sep 17 00:00:00 2001 From: drunkenhw Date: Fri, 14 Apr 2023 21:23:32 +0900 Subject: [PATCH 06/31] =?UTF-8?q?test:=20display=20name=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 --- .../java/racingcar/controller/RacingGameControllerTest.java | 2 ++ src/test/java/racingcar/dao/CarJdbcDaoTest.java | 2 ++ src/test/java/racingcar/dao/RacingGameJdbcDaoTest.java | 2 ++ src/test/java/racingcar/integration/GamePlayTest.java | 2 ++ src/test/java/racingcar/service/RacingGameServiceTest.java | 2 ++ 5 files changed, 10 insertions(+) diff --git a/src/test/java/racingcar/controller/RacingGameControllerTest.java b/src/test/java/racingcar/controller/RacingGameControllerTest.java index 17dc8ef39..d8547cce2 100644 --- a/src/test/java/racingcar/controller/RacingGameControllerTest.java +++ b/src/test/java/racingcar/controller/RacingGameControllerTest.java @@ -10,6 +10,7 @@ import com.fasterxml.jackson.databind.ObjectMapper; import java.util.List; +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; @@ -34,6 +35,7 @@ public class RacingGameControllerTest { private RacingGameService racingGameService; @Test + @DisplayName("자동차 경주를 실행하고 결과를 반환한다.") public void plays() throws Exception { RacingGameRequest request = new RacingGameRequest("현구막,박스터", 10); RacingGameResponse expectedResponse = new RacingGameResponse( diff --git a/src/test/java/racingcar/dao/CarJdbcDaoTest.java b/src/test/java/racingcar/dao/CarJdbcDaoTest.java index 49216c2b0..9e3de2d72 100644 --- a/src/test/java/racingcar/dao/CarJdbcDaoTest.java +++ b/src/test/java/racingcar/dao/CarJdbcDaoTest.java @@ -4,6 +4,7 @@ import java.util.List; 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; @@ -35,6 +36,7 @@ void setUp() { } @Test + @DisplayName("car를 여러 개 저장한다") void insert() { List carDtos = List.of(new CarDto("gumak", 9, false), new CarDto("benz", 10, true)); diff --git a/src/test/java/racingcar/dao/RacingGameJdbcDaoTest.java b/src/test/java/racingcar/dao/RacingGameJdbcDaoTest.java index 0794eabcd..e233cb813 100644 --- a/src/test/java/racingcar/dao/RacingGameJdbcDaoTest.java +++ b/src/test/java/racingcar/dao/RacingGameJdbcDaoTest.java @@ -3,6 +3,7 @@ import static org.assertj.core.api.Assertions.assertThat; 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; @@ -21,6 +22,7 @@ void setUp() { } @Test + @DisplayName("racing game을 저장한다") void insert() { int trialCount = 5; Long gameId = racingGameDao.save(trialCount); diff --git a/src/test/java/racingcar/integration/GamePlayTest.java b/src/test/java/racingcar/integration/GamePlayTest.java index 20617675f..854fbc308 100644 --- a/src/test/java/racingcar/integration/GamePlayTest.java +++ b/src/test/java/racingcar/integration/GamePlayTest.java @@ -5,6 +5,7 @@ import io.restassured.RestAssured; import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; @@ -22,6 +23,7 @@ void setUp(@LocalServerPort int port) { } @Test + @DisplayName("자동차 경주 게임을 진행한다.") void playGame() { RacingGameRequest request = new RacingGameRequest("브리,토미,브라운", 10); RestAssured.given().log().all() diff --git a/src/test/java/racingcar/service/RacingGameServiceTest.java b/src/test/java/racingcar/service/RacingGameServiceTest.java index 0cc27fa86..578a57ef2 100644 --- a/src/test/java/racingcar/service/RacingGameServiceTest.java +++ b/src/test/java/racingcar/service/RacingGameServiceTest.java @@ -3,6 +3,7 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertAll; +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; @@ -16,6 +17,7 @@ class RacingGameServiceTest { RacingGameService racingGameService; @Test + @DisplayName("이름과 실행 횟수를 받아 게임의 결과를 반환한다") void playTest() { RacingGameRequest racingGameRequest = new RacingGameRequest("박스터,엔초", 10); From 95f6855ff2e3a94f1e9edad5c535125d4221765f Mon Sep 17 00:00:00 2001 From: drunkenhw Date: Sat, 15 Apr 2023 13:10:15 +0900 Subject: [PATCH 07/31] =?UTF-8?q?refactor:=20db=EC=A0=91=EA=B7=BC=EC=9D=84?= =?UTF-8?q?=20=EC=9C=84=ED=95=9C=20entity=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/CarDao.java | 4 +- src/main/java/racingcar/dao/CarJdbcDao.java | 14 +++---- .../java/racingcar/dao/RacingGameDao.java | 4 +- .../java/racingcar/dao/RacingGameJdbcDao.java | 5 ++- .../java/racingcar/dao/entity/CarEntity.java | 32 +++++++++++++++ .../dao/entity/RacingGameEntity.java | 14 +++++++ src/main/java/racingcar/dto/CarDto.java | 6 --- .../racingcar/dto/RacingGameResponse.java | 19 +++++++++ .../racingcar/service/RacingGameService.java | 39 +++++++++---------- .../java/racingcar/dao/CarJdbcDaoTest.java | 18 +++++---- .../racingcar/dao/RacingGameJdbcDaoTest.java | 7 ++-- 11 files changed, 112 insertions(+), 50 deletions(-) create mode 100644 src/main/java/racingcar/dao/entity/CarEntity.java create mode 100644 src/main/java/racingcar/dao/entity/RacingGameEntity.java diff --git a/src/main/java/racingcar/dao/CarDao.java b/src/main/java/racingcar/dao/CarDao.java index 02285f7e2..b14007878 100644 --- a/src/main/java/racingcar/dao/CarDao.java +++ b/src/main/java/racingcar/dao/CarDao.java @@ -1,9 +1,9 @@ package racingcar.dao; import java.util.List; -import racingcar.dto.CarDto; +import racingcar.dao.entity.CarEntity; public interface CarDao { - void saveAll(Long gameId, List racingCars); + void saveAll(List racingCars); } diff --git a/src/main/java/racingcar/dao/CarJdbcDao.java b/src/main/java/racingcar/dao/CarJdbcDao.java index cf40e93b4..a3d2e3887 100644 --- a/src/main/java/racingcar/dao/CarJdbcDao.java +++ b/src/main/java/racingcar/dao/CarJdbcDao.java @@ -6,7 +6,7 @@ import org.springframework.jdbc.core.BatchPreparedStatementSetter; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.stereotype.Component; -import racingcar.dto.CarDto; +import racingcar.dao.entity.CarEntity; @Component public class CarJdbcDao implements CarDao { @@ -18,16 +18,16 @@ public CarJdbcDao(JdbcTemplate jdbcTemplate) { } @Override - public void saveAll(Long gameId, List racingCars) { + public void saveAll(List racingCars) { String sql = "INSERT INTO CAR (name, position, is_win, racing_game_id) VALUES (?, ?, ?, ?)"; jdbcTemplate.batchUpdate(sql, new BatchPreparedStatementSetter() { @Override public void setValues(PreparedStatement ps, int i) throws SQLException { - CarDto carDto = racingCars.get(i); - ps.setString(1, carDto.getName()); - ps.setInt(2, carDto.getPosition()); - ps.setBoolean(3, carDto.isWin()); - ps.setLong(4, gameId); + CarEntity carEntity = racingCars.get(i); + ps.setString(1, carEntity.getName()); + ps.setInt(2, carEntity.getPosition()); + ps.setBoolean(3, carEntity.isWin()); + ps.setLong(4, carEntity.getGameId()); } @Override diff --git a/src/main/java/racingcar/dao/RacingGameDao.java b/src/main/java/racingcar/dao/RacingGameDao.java index e78c1e514..0632e2773 100644 --- a/src/main/java/racingcar/dao/RacingGameDao.java +++ b/src/main/java/racingcar/dao/RacingGameDao.java @@ -1,6 +1,8 @@ package racingcar.dao; +import racingcar.dao.entity.RacingGameEntity; + public interface RacingGameDao { - Long save(int trialCount); + Long save(RacingGameEntity racingGameEntity); } diff --git a/src/main/java/racingcar/dao/RacingGameJdbcDao.java b/src/main/java/racingcar/dao/RacingGameJdbcDao.java index 486782084..f51b2e72b 100644 --- a/src/main/java/racingcar/dao/RacingGameJdbcDao.java +++ b/src/main/java/racingcar/dao/RacingGameJdbcDao.java @@ -5,6 +5,7 @@ import org.springframework.jdbc.support.GeneratedKeyHolder; import org.springframework.jdbc.support.KeyHolder; import org.springframework.stereotype.Component; +import racingcar.dao.entity.RacingGameEntity; @Component public class RacingGameJdbcDao implements RacingGameDao { @@ -16,13 +17,13 @@ public RacingGameJdbcDao(JdbcTemplate jdbcTemplate) { } @Override - public Long save(int trialCount) { + public Long save(RacingGameEntity racingGameEntity) { String sql = "INSERT into RACING_GAME (trial_count) values (?)"; KeyHolder keyHolder = new GeneratedKeyHolder(); jdbcTemplate.update(con -> { PreparedStatement preparedStatement = con.prepareStatement(sql, new String[]{"id"}); - preparedStatement.setInt(1, trialCount); + preparedStatement.setInt(1, racingGameEntity.getTrialCount()); return preparedStatement; }, keyHolder); diff --git a/src/main/java/racingcar/dao/entity/CarEntity.java b/src/main/java/racingcar/dao/entity/CarEntity.java new file mode 100644 index 000000000..e9d463c9e --- /dev/null +++ b/src/main/java/racingcar/dao/entity/CarEntity.java @@ -0,0 +1,32 @@ +package racingcar.dao.entity; + +public class CarEntity { + + private final String name; + private final int position; + private final boolean isWin; + private final Long gameId; + + public CarEntity(String name, int position, boolean isWin, Long gameId) { + this.name = name; + this.position = position; + this.isWin = isWin; + this.gameId = gameId; + } + + public String getName() { + return name; + } + + public int getPosition() { + return position; + } + + public boolean isWin() { + return isWin; + } + + public Long getGameId() { + return gameId; + } +} diff --git a/src/main/java/racingcar/dao/entity/RacingGameEntity.java b/src/main/java/racingcar/dao/entity/RacingGameEntity.java new file mode 100644 index 000000000..f7079b2e6 --- /dev/null +++ b/src/main/java/racingcar/dao/entity/RacingGameEntity.java @@ -0,0 +1,14 @@ +package racingcar.dao.entity; + +public class RacingGameEntity { + + private final int trialCount; + + public RacingGameEntity(int trialCount) { + this.trialCount = trialCount; + } + + public int getTrialCount() { + return trialCount; + } +} diff --git a/src/main/java/racingcar/dto/CarDto.java b/src/main/java/racingcar/dto/CarDto.java index 4930eee7e..45147c6b2 100644 --- a/src/main/java/racingcar/dto/CarDto.java +++ b/src/main/java/racingcar/dto/CarDto.java @@ -4,12 +4,10 @@ public class CarDto { private final String name; private final int position; - private final boolean isWin; public CarDto(String name, int position, boolean isWin) { this.name = name; this.position = position; - this.isWin = isWin; } public String getName() { @@ -19,8 +17,4 @@ public String getName() { public int getPosition() { return position; } - - public boolean isWin() { - return isWin; - } } diff --git a/src/main/java/racingcar/dto/RacingGameResponse.java b/src/main/java/racingcar/dto/RacingGameResponse.java index 5e296b44d..ca374c425 100644 --- a/src/main/java/racingcar/dto/RacingGameResponse.java +++ b/src/main/java/racingcar/dto/RacingGameResponse.java @@ -1,6 +1,10 @@ package racingcar.dto; import java.util.List; +import java.util.stream.Collectors; +import racingcar.domain.Car; +import racingcar.domain.Cars; +import racingcar.domain.RacingGame; public class RacingGameResponse { @@ -12,6 +16,21 @@ public RacingGameResponse(List winners, List racingCars) { this.racingCars = racingCars; } + public static RacingGameResponse of(RacingGame racingGame) { + Cars cars = racingGame.getCars(); + List winners = cars.findWinners(); + List racingCars = cars.getCars().stream() + .map(car -> new CarDto(car.getName(), car.getPosition(), winners.contains(car))) + .collect(Collectors.toList()); + return new RacingGameResponse(getWinnerNames(winners), racingCars); + } + + private static List getWinnerNames(List winners) { + return winners.stream() + .map(Car::getName) + .collect(Collectors.toList()); + } + public List getWinners() { return winners; } diff --git a/src/main/java/racingcar/service/RacingGameService.java b/src/main/java/racingcar/service/RacingGameService.java index 75828303a..c2aebf488 100644 --- a/src/main/java/racingcar/service/RacingGameService.java +++ b/src/main/java/racingcar/service/RacingGameService.java @@ -5,10 +5,11 @@ import org.springframework.stereotype.Service; import racingcar.dao.CarDao; import racingcar.dao.RacingGameDao; +import racingcar.dao.entity.CarEntity; +import racingcar.dao.entity.RacingGameEntity; import racingcar.domain.Car; import racingcar.domain.Cars; import racingcar.domain.RacingGame; -import racingcar.dto.CarDto; import racingcar.dto.RacingGameRequest; import racingcar.dto.RacingGameResponse; @@ -23,31 +24,27 @@ public RacingGameService(CarDao carDao, RacingGameDao racingGameDao) { } public RacingGameResponse play(RacingGameRequest racingGameRequest) { - Cars cars = new Cars(racingGameRequest.getNamesList().stream() - .map(Car::new) - .collect(Collectors.toList())); - RacingGame game = new RacingGame(racingGameRequest.getCount(), cars); - Long racingGameId = racingGameDao.save(racingGameRequest.getCount()); - while (!game.isEnd()) { - game.playOneRound(); + Cars cars = toCars(racingGameRequest); + RacingGame racingGame = new RacingGame(racingGameRequest.getCount(), cars); + while (!racingGame.isEnd()) { + racingGame.playOneRound(); } - RacingGameResponse result = getResult(game); - carDao.saveAll(racingGameId, result.getRacingCars()); - return result; + Long racingGameId = racingGameDao.save(new RacingGameEntity(racingGameRequest.getCount())); + carDao.saveAll(toCarEntities(racingGame, racingGameId)); + return RacingGameResponse.of(racingGame); } - private RacingGameResponse getResult(RacingGame racingGame) { - Cars cars = racingGame.getCars(); - List winners = cars.findWinners(); - List racingCars = cars.getCars().stream() - .map(car -> new CarDto(car.getName(), car.getPosition(), winners.contains(car))) - .collect(Collectors.toList()); - return new RacingGameResponse(getWinnerNames(winners), racingCars); + private Cars toCars(RacingGameRequest racingGameRequest) { + return new Cars(racingGameRequest.getNamesList().stream() + .map(Car::new) + .collect(Collectors.toList())); } - private List getWinnerNames(List winners) { - return winners.stream() - .map(Car::getName) + private List toCarEntities(RacingGame game, Long racingGameId) { + Cars cars = game.getCars(); + List winners = cars.findWinners(); + return cars.getCars().stream() + .map(car -> new CarEntity(car.getName(), car.getPosition(), winners.contains(car), racingGameId)) .collect(Collectors.toList()); } } diff --git a/src/test/java/racingcar/dao/CarJdbcDaoTest.java b/src/test/java/racingcar/dao/CarJdbcDaoTest.java index 9e3de2d72..257774e6f 100644 --- a/src/test/java/racingcar/dao/CarJdbcDaoTest.java +++ b/src/test/java/racingcar/dao/CarJdbcDaoTest.java @@ -9,7 +9,8 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.jdbc.JdbcTest; import org.springframework.jdbc.core.JdbcTemplate; -import racingcar.dto.CarDto; +import racingcar.dao.entity.CarEntity; +import racingcar.dao.entity.RacingGameEntity; @JdbcTest class CarJdbcDaoTest { @@ -23,24 +24,25 @@ class CarJdbcDaoTest { void setUp() { carDao = new CarJdbcDao(jdbcTemplate); RacingGameDao racingGameDao = new RacingGameJdbcDao(jdbcTemplate); - gameId = racingGameDao.save(10); + gameId = racingGameDao.save(new RacingGameEntity(10)); - CarDto carDto1 = new CarDto("boxster", 10, true); - CarDto carDto2 = new CarDto("encho", 7, false); + CarEntity boxster = new CarEntity("boxster", 10, true, gameId); + CarEntity encho = new CarEntity("encho", 7, false, gameId); jdbcTemplate.update("INSERT INTO CAR (name, position, is_win, racing_game_id) VALUES (?, ?, ?, ?)", - carDto1.getName(), carDto1.getPosition(), carDto1.isWin(), gameId); + boxster.getName(), boxster.getPosition(), boxster.isWin(), boxster.getGameId()); jdbcTemplate.update("INSERT INTO CAR (name, position, is_win, racing_game_id) VALUES (?, ?, ?, ?)", - carDto2.getName(), carDto2.getPosition(), carDto2.isWin(), gameId); + encho.getName(), encho.getPosition(), encho.isWin(), encho.getGameId()); } @Test @DisplayName("car를 여러 개 저장한다") void insert() { - List carDtos = List.of(new CarDto("gumak", 9, false), new CarDto("benz", 10, true)); + List carDtos = List.of(new CarEntity("gumak", 9, false, gameId), + new CarEntity("benz", 10, true, gameId)); - carDao.saveAll(gameId, carDtos); + carDao.saveAll(carDtos); Integer count = jdbcTemplate.queryForObject("SELECT count(*) FROM CAR", Integer.class); diff --git a/src/test/java/racingcar/dao/RacingGameJdbcDaoTest.java b/src/test/java/racingcar/dao/RacingGameJdbcDaoTest.java index e233cb813..5bb26cfdc 100644 --- a/src/test/java/racingcar/dao/RacingGameJdbcDaoTest.java +++ b/src/test/java/racingcar/dao/RacingGameJdbcDaoTest.java @@ -8,6 +8,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.entity.RacingGameEntity; @JdbcTest class RacingGameJdbcDaoTest { @@ -24,12 +25,12 @@ void setUp() { @Test @DisplayName("racing game을 저장한다") void insert() { - int trialCount = 5; - Long gameId = racingGameDao.save(trialCount); + RacingGameEntity racingGameEntity = new RacingGameEntity(5); + Long gameId = racingGameDao.save(racingGameEntity); String sql = "SELECT trial_count FROM racing_game WHERE id = " + gameId; Integer savedTrialCount = jdbcTemplate.queryForObject(sql, Integer.class); - assertThat(trialCount).isEqualTo(savedTrialCount); + assertThat(savedTrialCount).isEqualTo(5); } } From ca7c8d689031f2c4dbdb7f47ab1280861d3c5e3e Mon Sep 17 00:00:00 2001 From: drunkenhw Date: Sat, 15 Apr 2023 14:13:24 +0900 Subject: [PATCH 08/31] =?UTF-8?q?feat:=20=EA=B0=9D=EC=B2=B4=20=EA=B2=80?= =?UTF-8?q?=EC=A6=9D=20=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 --- src/main/java/racingcar/domain/Car.java | 17 ++++++++++++++--- src/main/java/racingcar/domain/Cars.java | 7 +++++++ src/test/java/racingcar/domain/CarTest.java | 11 +++++++++++ src/test/java/racingcar/domain/CarsTest.java | 11 ++++++++++- 4 files changed, 42 insertions(+), 4 deletions(-) diff --git a/src/main/java/racingcar/domain/Car.java b/src/main/java/racingcar/domain/Car.java index d69129f4f..bcbb56569 100644 --- a/src/main/java/racingcar/domain/Car.java +++ b/src/main/java/racingcar/domain/Car.java @@ -3,22 +3,33 @@ public class Car { private static final int MOVE_CONDITION = 4; - public static final int MAX_NAME_LENGTH = 5; + private static final int MAX_NAME_LENGTH = 5; private final String name; private int position = 0; public Car(String name) { - validateNameLength(name); + validateName(name); this.name = name; } - private void validateNameLength(String name) { + private void validateName(String name) { + validateEmpty(name); + validateLength(name); + } + + private void validateLength(String name) { if (name.length() > MAX_NAME_LENGTH) { throw new IllegalArgumentException("이름이 " + name.length() + "자 입니다. " + "이름은 5자 이하로 가능합니다."); } } + private void validateEmpty(String name) { + if (name == null || name.isBlank()) { + throw new IllegalArgumentException("이름은 공백일 수 없습니다."); + } + } + public void move(int number) { if (number >= MOVE_CONDITION) { addPosition(); diff --git a/src/main/java/racingcar/domain/Cars.java b/src/main/java/racingcar/domain/Cars.java index f90900e53..6ead9a1e1 100644 --- a/src/main/java/racingcar/domain/Cars.java +++ b/src/main/java/racingcar/domain/Cars.java @@ -10,10 +10,17 @@ public class Cars { private final List cars; public Cars(List cars) { + validateCarsSize(cars); validateDuplicateName(cars); this.cars = cars; } + private void validateCarsSize(List cars) { + if (cars.isEmpty()) { + throw new IllegalArgumentException("최소 하나의 자동차가 있어야 합니다."); + } + } + private void validateDuplicateName(List cars) { Set distinctNames = cars.stream() .map(Car::getName) diff --git a/src/test/java/racingcar/domain/CarTest.java b/src/test/java/racingcar/domain/CarTest.java index 16a662c94..617802784 100644 --- a/src/test/java/racingcar/domain/CarTest.java +++ b/src/test/java/racingcar/domain/CarTest.java @@ -5,6 +5,8 @@ import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.NullAndEmptySource; class CarTest { @@ -48,4 +50,13 @@ void winTest() { assertThat(win).isEqualTo(boxster); } + + @ParameterizedTest + @NullAndEmptySource + @DisplayName("이름이 공백이면 예외를 발생한다") + void nameIsEmptyEx(String name) { + assertThatThrownBy(() -> new Car(name)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("이름은 공백일 수 없습니다."); + } } diff --git a/src/test/java/racingcar/domain/CarsTest.java b/src/test/java/racingcar/domain/CarsTest.java index 9d5d5591b..ff27adbd4 100644 --- a/src/test/java/racingcar/domain/CarsTest.java +++ b/src/test/java/racingcar/domain/CarsTest.java @@ -51,10 +51,19 @@ void getFirstPositionTest() { void duplicateNameEx() { List duplicateNameCars = List.of(new Car("박스터"), new Car("박스터")); - assertThatThrownBy(()-> new Cars(duplicateNameCars)) + assertThatThrownBy(() -> new Cars(duplicateNameCars)) .isInstanceOf(IllegalArgumentException.class) .hasMessage("중복된 이름은 사용할 수 없습니다"); } + @Test + @DisplayName("car의 수가 1보다 작으면 예외를 발생한다") + void carSizeEx() { + List cars = List.of(); + + assertThatThrownBy(() -> new Cars(cars)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("최소 하나의 자동차가 있어야 합니다."); + } } From 06abb19c1836cb54fbbf5a823d6722858bd18f40 Mon Sep 17 00:00:00 2001 From: drunkenhw Date: Sat, 15 Apr 2023 19:41:10 +0900 Subject: [PATCH 09/31] =?UTF-8?q?refactor:=20racing=20game=20test=20?= =?UTF-8?q?=EA=B0=80=EB=8F=85=EC=84=B1=20=EC=A2=8B=EA=B2=8C=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/racingcar/domain/RacingGameTest.java | 74 +++++-------------- 1 file changed, 20 insertions(+), 54 deletions(-) diff --git a/src/test/java/racingcar/domain/RacingGameTest.java b/src/test/java/racingcar/domain/RacingGameTest.java index 203ebec62..2310655ec 100644 --- a/src/test/java/racingcar/domain/RacingGameTest.java +++ b/src/test/java/racingcar/domain/RacingGameTest.java @@ -5,71 +5,49 @@ import static org.junit.jupiter.api.Assertions.assertAll; import java.util.List; -import java.util.stream.Collectors; 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; class RacingGameTest { - private final AlwaysMoveNumberGenerator alwaysMoveNumberGenerator = new AlwaysMoveNumberGenerator(); - private final NeverMoveNumberGenerator neverMoveNumberGenerator = new NeverMoveNumberGenerator(); - - private final Car boxster = new Car("박스터"); - private final Car sonata = new Car("소나타"); - private final Car benz = new Car("벤츠"); private final Cars dummy = new Cars(List.of( - boxster, - sonata, - benz + new Car("박스터"), + new Car("소나타"), + new Car("벤츠") )); @Test - @DisplayName("레이싱 게임을 한 라운드 진행할 때 4이상의 숫자가 주어지면 우승자의 위치가 1이다.") + @DisplayName("레이싱 게임을 진행할 때 4이상의 숫자가 주어지면 우승자의 위치가 3이다.") void moveTest() { - RacingGame game = new RacingGame(alwaysMoveNumberGenerator, 3, dummy); - game.playOneRound(); + RacingGame game = new RacingGame(new AlwaysMoveNumberGenerator(), 3, dummy); - List carList = dummy.getCars(); - List names = carsToNames(carList); - List positions = carsToPositions(carList); + game.run(); + List carList = dummy.getCars(); assertAll(() -> { - assertThat(names).contains("박스터", "소나타", "벤츠"); - assertThat(positions).containsOnly(1); + assertThat(carList) + .extracting("name").contains("박스터", "소나타", "벤츠"); + assertThat(carList) + .extracting("position").containsOnly(3); }); } @Test - @DisplayName("레이싱 게임을 한 라운드 진행할 때 3이하의 숫자가 주어지면 우승자의 위치가 0이다.") + @DisplayName("레이싱 게임을 진행할 때 3이하의 숫자가 주어지면 우승자의 위치가 0이다.") void notMoveTest() { - RacingGame game = new RacingGame(neverMoveNumberGenerator, 3, dummy); - game.playOneRound(); + RacingGame game = new RacingGame(new NeverMoveNumberGenerator(), 3, dummy); - List carList = dummy.getCars(); - List names = carsToNames(carList); - List positions = carsToPositions(carList); + game.run(); + List carList = dummy.getCars(); assertAll(() -> { - assertThat(names).contains("박스터", "소나타", "벤츠"); - assertThat(positions).containsOnly(0); + assertThat(carList) + .extracting("name").contains("박스터", "소나타", "벤츠"); + assertThat(carList) + .extracting("position").containsOnly(0); }); } - @DisplayName("isEnd 메소드는 게임 종료 여부를 반환한다") - @ParameterizedTest(name = "시도 횟수가 {0}일 때 {1}번 시도하면 {2}") - @CsvSource(value = {"3:1:false", "3:2:false", "3:3:true"}, delimiter = ':') - void isEndTest(int count, int tryCount, boolean result) { - RacingGame game = new RacingGame(alwaysMoveNumberGenerator, count, dummy); - - for (int i = 0; i < tryCount; i++) { - game.playOneRound(); - } - - assertThat(game.isEnd()).isEqualTo(result); - } - @Test @DisplayName("시도 횟수가 100회 초과하면 예외가 발생한다") void tryCountEx() { @@ -77,7 +55,7 @@ void tryCountEx() { assertThatThrownBy(() -> new RacingGame(tryCount, dummy)) .isInstanceOf(IllegalArgumentException.class) - .hasMessage("시도 횟수는 100회 이하만 가능합니다 현재 : " + tryCount + "회"); + .hasMessage("시도 횟수는 100회 이하만 가능합니다 현재 : " + 101 + "회"); } static class AlwaysMoveNumberGenerator implements NumberGenerator { @@ -101,16 +79,4 @@ public int generate() { } } - - private List carsToNames(List carList) { - return carList.stream() - .map(Car::getName) - .collect(Collectors.toList()); - } - - private List carsToPositions(List carList) { - return carList.stream() - .map(Car::getPosition) - .collect(Collectors.toList()); - } } From ce71dda65a3f25753bf21fe568a08876295f2a93 Mon Sep 17 00:00:00 2001 From: drunkenhw Date: Sat, 15 Apr 2023 19:48:58 +0900 Subject: [PATCH 10/31] =?UTF-8?q?refactor:=20=EC=B6=9C=EB=A0=A5=20?= =?UTF-8?q?=ED=98=95=EC=8B=9D=EC=97=90=20=EB=A7=9E=EB=8F=84=EB=A1=9D=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 --- src/main/java/racingcar/dto/RacingGameRequest.java | 11 ++++++----- src/main/java/racingcar/dto/RacingGameResponse.java | 6 +++--- .../java/racingcar/service/RacingGameService.java | 4 +--- .../controller/RacingGameControllerTest.java | 2 +- 4 files changed, 11 insertions(+), 12 deletions(-) diff --git a/src/main/java/racingcar/dto/RacingGameRequest.java b/src/main/java/racingcar/dto/RacingGameRequest.java index 3e6f4c050..e523103e3 100644 --- a/src/main/java/racingcar/dto/RacingGameRequest.java +++ b/src/main/java/racingcar/dto/RacingGameRequest.java @@ -17,15 +17,16 @@ public RacingGameRequest() { } - public String getNames() { - return names; - } - - public List getNamesList() { + public List toNameList() { return Arrays.stream(names.split(",")) + .map(String::strip) .collect(Collectors.toList()); } + public String getNames() { + return names; + } + public int getCount() { return count; } diff --git a/src/main/java/racingcar/dto/RacingGameResponse.java b/src/main/java/racingcar/dto/RacingGameResponse.java index ca374c425..38e36d8cf 100644 --- a/src/main/java/racingcar/dto/RacingGameResponse.java +++ b/src/main/java/racingcar/dto/RacingGameResponse.java @@ -8,11 +8,11 @@ public class RacingGameResponse { - private final List winners; + private final String winners; private final List racingCars; public RacingGameResponse(List winners, List racingCars) { - this.winners = winners; + this.winners = String.join(",", winners); this.racingCars = racingCars; } @@ -31,7 +31,7 @@ private static List getWinnerNames(List winners) { .collect(Collectors.toList()); } - public List getWinners() { + public String getWinners() { return winners; } diff --git a/src/main/java/racingcar/service/RacingGameService.java b/src/main/java/racingcar/service/RacingGameService.java index c2aebf488..d95f44240 100644 --- a/src/main/java/racingcar/service/RacingGameService.java +++ b/src/main/java/racingcar/service/RacingGameService.java @@ -26,9 +26,7 @@ public RacingGameService(CarDao carDao, RacingGameDao racingGameDao) { public RacingGameResponse play(RacingGameRequest racingGameRequest) { Cars cars = toCars(racingGameRequest); RacingGame racingGame = new RacingGame(racingGameRequest.getCount(), cars); - while (!racingGame.isEnd()) { - racingGame.playOneRound(); - } + racingGame.run(); Long racingGameId = racingGameDao.save(new RacingGameEntity(racingGameRequest.getCount())); carDao.saveAll(toCarEntities(racingGame, racingGameId)); return RacingGameResponse.of(racingGame); diff --git a/src/test/java/racingcar/controller/RacingGameControllerTest.java b/src/test/java/racingcar/controller/RacingGameControllerTest.java index d8547cce2..d5c033e3c 100644 --- a/src/test/java/racingcar/controller/RacingGameControllerTest.java +++ b/src/test/java/racingcar/controller/RacingGameControllerTest.java @@ -50,7 +50,7 @@ public void plays() throws Exception { .content(requestString)) .andExpect(status().isOk()) - .andExpect(jsonPath("$.winners", is(List.of("현구막")))) + .andExpect(jsonPath("$.winners", is("현구막"))) .andExpect(jsonPath("$.racingCars", hasSize(2))); } } From 1e63c665eb899b856e651778d7bbdaac41b04f28 Mon Sep 17 00:00:00 2001 From: drunkenhw Date: Sat, 15 Apr 2023 19:49:13 +0900 Subject: [PATCH 11/31] =?UTF-8?q?refactor:=20racing=20game=20=EB=B0=A9?= =?UTF-8?q?=EC=8B=9D=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/domain/RacingGame.java | 12 +++++++++--- .../java/racingcar/service/RacingGameService.java | 2 +- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/src/main/java/racingcar/domain/RacingGame.java b/src/main/java/racingcar/domain/RacingGame.java index 0cc997cf0..5f9ef768c 100644 --- a/src/main/java/racingcar/domain/RacingGame.java +++ b/src/main/java/racingcar/domain/RacingGame.java @@ -24,13 +24,19 @@ private void validateTryCount(int tryCount) { } } - public void playOneRound() { + public void run() { + while (isNotEnd()) { + playOneRound(); + } + } + + private void playOneRound() { cars.moveAll(numberGenerator); tryCount--; } - public boolean isEnd() { - return tryCount == 0; + private boolean isNotEnd() { + return tryCount != 0; } public Cars getCars() { diff --git a/src/main/java/racingcar/service/RacingGameService.java b/src/main/java/racingcar/service/RacingGameService.java index d95f44240..ba7362d1c 100644 --- a/src/main/java/racingcar/service/RacingGameService.java +++ b/src/main/java/racingcar/service/RacingGameService.java @@ -33,7 +33,7 @@ public RacingGameResponse play(RacingGameRequest racingGameRequest) { } private Cars toCars(RacingGameRequest racingGameRequest) { - return new Cars(racingGameRequest.getNamesList().stream() + return new Cars(racingGameRequest.toNameList().stream() .map(Car::new) .collect(Collectors.toList())); } From ce96d764b94c751df24f55af820e1f306364f2dc Mon Sep 17 00:00:00 2001 From: drunkenhw Date: Sat, 15 Apr 2023 20:02:11 +0900 Subject: [PATCH 12/31] =?UTF-8?q?refactor:=20=EC=8A=B9=EC=9E=90=EB=A5=BC?= =?UTF-8?q?=20=EA=B5=AC=ED=95=98=EB=8A=94=20=EB=A1=9C=EC=A7=81=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 --- src/main/java/racingcar/domain/Cars.java | 2 +- src/main/java/racingcar/domain/RacingGame.java | 11 +++++++++-- src/main/java/racingcar/dto/RacingGameResponse.java | 8 +++----- .../java/racingcar/service/RacingGameService.java | 5 ++--- 4 files changed, 15 insertions(+), 11 deletions(-) diff --git a/src/main/java/racingcar/domain/Cars.java b/src/main/java/racingcar/domain/Cars.java index 6ead9a1e1..c81430e75 100644 --- a/src/main/java/racingcar/domain/Cars.java +++ b/src/main/java/racingcar/domain/Cars.java @@ -36,7 +36,7 @@ public List findWinners() { return cars.stream() .filter(car -> car.isDraw(maxPositionCar)) - .collect(Collectors.toList()); + .collect(Collectors.toUnmodifiableList()); } private Car findMaxPositionCar() { diff --git a/src/main/java/racingcar/domain/RacingGame.java b/src/main/java/racingcar/domain/RacingGame.java index 5f9ef768c..9d8b1d040 100644 --- a/src/main/java/racingcar/domain/RacingGame.java +++ b/src/main/java/racingcar/domain/RacingGame.java @@ -1,5 +1,7 @@ package racingcar.domain; +import java.util.List; + public class RacingGame { public static final int MAX_TRY_COUNT_BOUND = 100; @@ -39,7 +41,12 @@ private boolean isNotEnd() { return tryCount != 0; } - public Cars getCars() { - return cars; + public List findWinners() { + return cars.findWinners(); + } + + public List getCars() { + return cars.getCars(); } + } diff --git a/src/main/java/racingcar/dto/RacingGameResponse.java b/src/main/java/racingcar/dto/RacingGameResponse.java index 38e36d8cf..7317a4c5a 100644 --- a/src/main/java/racingcar/dto/RacingGameResponse.java +++ b/src/main/java/racingcar/dto/RacingGameResponse.java @@ -3,7 +3,6 @@ import java.util.List; import java.util.stream.Collectors; import racingcar.domain.Car; -import racingcar.domain.Cars; import racingcar.domain.RacingGame; public class RacingGameResponse { @@ -17,12 +16,11 @@ public RacingGameResponse(List winners, List racingCars) { } public static RacingGameResponse of(RacingGame racingGame) { - Cars cars = racingGame.getCars(); - List winners = cars.findWinners(); - List racingCars = cars.getCars().stream() + List winners = racingGame.findWinners(); + List carDtos = racingGame.getCars().stream() .map(car -> new CarDto(car.getName(), car.getPosition(), winners.contains(car))) .collect(Collectors.toList()); - return new RacingGameResponse(getWinnerNames(winners), racingCars); + return new RacingGameResponse(getWinnerNames(winners), carDtos); } private static List getWinnerNames(List winners) { diff --git a/src/main/java/racingcar/service/RacingGameService.java b/src/main/java/racingcar/service/RacingGameService.java index ba7362d1c..49c068050 100644 --- a/src/main/java/racingcar/service/RacingGameService.java +++ b/src/main/java/racingcar/service/RacingGameService.java @@ -39,9 +39,8 @@ private Cars toCars(RacingGameRequest racingGameRequest) { } private List toCarEntities(RacingGame game, Long racingGameId) { - Cars cars = game.getCars(); - List winners = cars.findWinners(); - return cars.getCars().stream() + List winners = game.findWinners(); + return game.getCars().stream() .map(car -> new CarEntity(car.getName(), car.getPosition(), winners.contains(car), racingGameId)) .collect(Collectors.toList()); } From 6ba353301aad659f801ad10cc49c09ee90dbda40 Mon Sep 17 00:00:00 2001 From: drunkenhw Date: Sun, 16 Apr 2023 16:46:47 +0900 Subject: [PATCH 13/31] =?UTF-8?q?feat:=20racing=20game=EC=9D=84=20?= =?UTF-8?q?=EC=A1=B0=ED=9A=8C=ED=95=98=EB=8A=94=20=EA=B8=B0=EB=8A=A5=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/dao/RacingGameDao.java | 3 +++ .../java/racingcar/dao/RacingGameJdbcDao.java | 16 ++++++++++++++- .../dao/entity/RacingGameEntity.java | 20 ++++++++++++++++++- src/main/resources/data.sql | 2 +- .../racingcar/dao/RacingGameJdbcDaoTest.java | 20 +++++++++++++++++++ 5 files changed, 58 insertions(+), 3 deletions(-) diff --git a/src/main/java/racingcar/dao/RacingGameDao.java b/src/main/java/racingcar/dao/RacingGameDao.java index 0632e2773..ae5a28dd5 100644 --- a/src/main/java/racingcar/dao/RacingGameDao.java +++ b/src/main/java/racingcar/dao/RacingGameDao.java @@ -1,8 +1,11 @@ package racingcar.dao; +import java.util.List; import racingcar.dao.entity.RacingGameEntity; public interface RacingGameDao { Long save(RacingGameEntity racingGameEntity); + + List findAllByCreatedTimeAsc(); } diff --git a/src/main/java/racingcar/dao/RacingGameJdbcDao.java b/src/main/java/racingcar/dao/RacingGameJdbcDao.java index f51b2e72b..ce7086721 100644 --- a/src/main/java/racingcar/dao/RacingGameJdbcDao.java +++ b/src/main/java/racingcar/dao/RacingGameJdbcDao.java @@ -1,6 +1,8 @@ package racingcar.dao; import java.sql.PreparedStatement; +import java.sql.Timestamp; +import java.util.List; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.support.GeneratedKeyHolder; import org.springframework.jdbc.support.KeyHolder; @@ -18,15 +20,27 @@ public RacingGameJdbcDao(JdbcTemplate jdbcTemplate) { @Override public Long save(RacingGameEntity racingGameEntity) { - String sql = "INSERT into RACING_GAME (trial_count) values (?)"; + String sql = "INSERT INTO racing_game (trial_count, created_at) VALUES (?,?)"; KeyHolder keyHolder = new GeneratedKeyHolder(); jdbcTemplate.update(con -> { PreparedStatement preparedStatement = con.prepareStatement(sql, new String[]{"id"}); preparedStatement.setInt(1, racingGameEntity.getTrialCount()); + preparedStatement.setTimestamp(2, Timestamp.valueOf(racingGameEntity.getCreatedTime())); return preparedStatement; }, keyHolder); return keyHolder.getKey().longValue(); } + + @Override + public List findAllByCreatedTimeAsc() { + return jdbcTemplate.query( + "SELECT * FROM racing_game ORDER BY created_at DESC ", + (resultSet, rowNum) -> new RacingGameEntity( + resultSet.getLong("id"), + resultSet.getInt("trial_count"), + resultSet.getTimestamp("created_at").toLocalDateTime() + )); + } } diff --git a/src/main/java/racingcar/dao/entity/RacingGameEntity.java b/src/main/java/racingcar/dao/entity/RacingGameEntity.java index f7079b2e6..880ce0bba 100644 --- a/src/main/java/racingcar/dao/entity/RacingGameEntity.java +++ b/src/main/java/racingcar/dao/entity/RacingGameEntity.java @@ -1,14 +1,32 @@ package racingcar.dao.entity; +import java.time.LocalDateTime; + public class RacingGameEntity { + private final Long id; private final int trialCount; + private final LocalDateTime createdTime; - public RacingGameEntity(int trialCount) { + public RacingGameEntity(Long id, int trialCount, LocalDateTime createdTime) { + this.id = id; this.trialCount = trialCount; + this.createdTime = createdTime; + } + + public RacingGameEntity(int trialCount) { + this(null, trialCount, LocalDateTime.now()); } public int getTrialCount() { return trialCount; } + + public Long getId() { + return id; + } + + public LocalDateTime getCreatedTime() { + return createdTime; + } } diff --git a/src/main/resources/data.sql b/src/main/resources/data.sql index c2e1039a0..dda2ac342 100644 --- a/src/main/resources/data.sql +++ b/src/main/resources/data.sql @@ -5,7 +5,7 @@ CREATE TABLE RACING_GAME ( id INT NOT NULL AUTO_INCREMENT, trial_count INT NOT NULL, - created_at DATETIME NOT NULL default current_timestamp, + created_at DATETIME NOT NULL, PRIMARY KEY (id) ); diff --git a/src/test/java/racingcar/dao/RacingGameJdbcDaoTest.java b/src/test/java/racingcar/dao/RacingGameJdbcDaoTest.java index 5bb26cfdc..1535f45d2 100644 --- a/src/test/java/racingcar/dao/RacingGameJdbcDaoTest.java +++ b/src/test/java/racingcar/dao/RacingGameJdbcDaoTest.java @@ -1,7 +1,10 @@ package racingcar.dao; import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertAll; +import java.time.LocalDateTime; +import java.util.List; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; @@ -20,6 +23,11 @@ class RacingGameJdbcDaoTest { @BeforeEach void setUp() { racingGameDao = new RacingGameJdbcDao(jdbcTemplate); + String sql = "INSERT into RACING_GAME (trial_count, created_at) values (?,?)"; + + jdbcTemplate.update(sql, 10, LocalDateTime.now()); + jdbcTemplate.update(sql, 9, LocalDateTime.now()); + jdbcTemplate.update(sql, 8, LocalDateTime.now()); } @Test @@ -33,4 +41,16 @@ void insert() { assertThat(savedTrialCount).isEqualTo(5); } + + @Test + @DisplayName("전체 게임들을 생성순으로 조회한다.") + void findAll() { + List racingGameEntities = racingGameDao.findAllByCreatedTimeAsc(); + + assertAll( + () -> assertThat(racingGameEntities).hasSize(3), + () -> assertThat(racingGameEntities) + .extracting("trialCount").containsExactly(8, 9, 10) + ); + } } From 39d4e46286754bf1294619580ce9c175480b5560 Mon Sep 17 00:00:00 2001 From: drunkenhw Date: Sun, 16 Apr 2023 17:43:49 +0900 Subject: [PATCH 14/31] =?UTF-8?q?feat:=20racing=20game=20id=EB=A1=9C=20car?= =?UTF-8?q?=EB=A5=BC=20=EC=A1=B0=ED=9A=8C=ED=95=98=EB=8A=94=20=EA=B8=B0?= =?UTF-8?q?=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 --- src/main/java/racingcar/dao/CarDao.java | 2 ++ src/main/java/racingcar/dao/CarJdbcDao.java | 11 +++++++++++ src/test/java/racingcar/dao/CarJdbcDaoTest.java | 8 ++++++++ 3 files changed, 21 insertions(+) diff --git a/src/main/java/racingcar/dao/CarDao.java b/src/main/java/racingcar/dao/CarDao.java index b14007878..26a3891f3 100644 --- a/src/main/java/racingcar/dao/CarDao.java +++ b/src/main/java/racingcar/dao/CarDao.java @@ -6,4 +6,6 @@ public interface CarDao { void saveAll(List racingCars); + + List findByRacingGameId(Long id); } diff --git a/src/main/java/racingcar/dao/CarJdbcDao.java b/src/main/java/racingcar/dao/CarJdbcDao.java index a3d2e3887..919e57cb7 100644 --- a/src/main/java/racingcar/dao/CarJdbcDao.java +++ b/src/main/java/racingcar/dao/CarJdbcDao.java @@ -36,4 +36,15 @@ public int getBatchSize() { } }); } + + @Override + public List findByRacingGameId(Long racingGameId) { + return jdbcTemplate.query("SELECT * FROM car WHERE racing_game_id = " + racingGameId, + (resultSet, rowNum) -> new CarEntity( + resultSet.getString("name"), + resultSet.getInt("position"), + resultSet.getBoolean("is_win"), + racingGameId + )); + } } diff --git a/src/test/java/racingcar/dao/CarJdbcDaoTest.java b/src/test/java/racingcar/dao/CarJdbcDaoTest.java index 257774e6f..4827ad574 100644 --- a/src/test/java/racingcar/dao/CarJdbcDaoTest.java +++ b/src/test/java/racingcar/dao/CarJdbcDaoTest.java @@ -48,4 +48,12 @@ void insert() { assertThat(count).isEqualTo(4); } + + @Test + @DisplayName("레이싱 게임 id로 car를 조회한다") + void findByGameId() { + List carEntities = carDao.findByRacingGameId(gameId); + + assertThat(carEntities).hasSize(2); + } } From 1b31c3c660afab0adbc5fce1db568a49ab030c5e Mon Sep 17 00:00:00 2001 From: drunkenhw Date: Mon, 17 Apr 2023 19:42:15 +0900 Subject: [PATCH 15/31] =?UTF-8?q?feat:=20=EC=A0=84=EC=B2=B4=20=EA=B2=B0?= =?UTF-8?q?=EA=B3=BC=EB=A5=BC=20=EC=A1=B0=ED=9A=8C=ED=95=98=EC=97=AC=20?= =?UTF-8?q?=EA=B2=B0=EA=B3=BC=EB=A5=BC=20=EB=B0=98=ED=99=98=ED=95=98?= =?UTF-8?q?=EB=8A=94=20=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 --- src/main/java/racingcar/dto/CarDto.java | 2 +- .../racingcar/dto/RacingGameResponse.java | 22 ++++++---- .../racingcar/service/RacingGameService.java | 24 +++++++++-- .../service/RacingGameServiceTest.java | 41 +++++++++++++++++-- 4 files changed, 73 insertions(+), 16 deletions(-) diff --git a/src/main/java/racingcar/dto/CarDto.java b/src/main/java/racingcar/dto/CarDto.java index 45147c6b2..e8871b1a6 100644 --- a/src/main/java/racingcar/dto/CarDto.java +++ b/src/main/java/racingcar/dto/CarDto.java @@ -5,7 +5,7 @@ public class CarDto { private final String name; private final int position; - public CarDto(String name, int position, boolean isWin) { + public CarDto(String name, int position) { this.name = name; this.position = position; } diff --git a/src/main/java/racingcar/dto/RacingGameResponse.java b/src/main/java/racingcar/dto/RacingGameResponse.java index 7317a4c5a..f03be7b1e 100644 --- a/src/main/java/racingcar/dto/RacingGameResponse.java +++ b/src/main/java/racingcar/dto/RacingGameResponse.java @@ -2,6 +2,7 @@ import java.util.List; import java.util.stream.Collectors; +import racingcar.dao.entity.CarEntity; import racingcar.domain.Car; import racingcar.domain.RacingGame; @@ -15,18 +16,25 @@ public RacingGameResponse(List winners, List racingCars) { this.racingCars = racingCars; } - public static RacingGameResponse of(RacingGame racingGame) { - List winners = racingGame.findWinners(); - List carDtos = racingGame.getCars().stream() - .map(car -> new CarDto(car.getName(), car.getPosition(), winners.contains(car))) + public static RacingGameResponse of(List carEntities) { + List winners = carEntities.stream() + .filter(CarEntity::isWin) + .map(CarEntity::getName) + .collect(Collectors.toList()); + List carDtos = carEntities.stream() + .map(carEntity -> new CarDto(carEntity.getName(), carEntity.getPosition())) .collect(Collectors.toList()); - return new RacingGameResponse(getWinnerNames(winners), carDtos); + return new RacingGameResponse(winners, carDtos); } - private static List getWinnerNames(List winners) { - return winners.stream() + public static RacingGameResponse of(RacingGame racingGame) { + List winners = racingGame.findWinners().stream() .map(Car::getName) .collect(Collectors.toList()); + List carDtos = racingGame.getCars().stream() + .map(car -> new CarDto(car.getName(), car.getPosition())) + .collect(Collectors.toList()); + return new RacingGameResponse(winners, carDtos); } public String getWinners() { diff --git a/src/main/java/racingcar/service/RacingGameService.java b/src/main/java/racingcar/service/RacingGameService.java index 49c068050..6db1b95f3 100644 --- a/src/main/java/racingcar/service/RacingGameService.java +++ b/src/main/java/racingcar/service/RacingGameService.java @@ -27,15 +27,21 @@ public RacingGameResponse play(RacingGameRequest racingGameRequest) { Cars cars = toCars(racingGameRequest); RacingGame racingGame = new RacingGame(racingGameRequest.getCount(), cars); racingGame.run(); - Long racingGameId = racingGameDao.save(new RacingGameEntity(racingGameRequest.getCount())); - carDao.saveAll(toCarEntities(racingGame, racingGameId)); + + save(racingGameRequest, racingGame); return RacingGameResponse.of(racingGame); } private Cars toCars(RacingGameRequest racingGameRequest) { - return new Cars(racingGameRequest.toNameList().stream() + List cars = racingGameRequest.toNameList().stream() .map(Car::new) - .collect(Collectors.toList())); + .collect(Collectors.toList()); + return new Cars(cars); + } + + private void save(RacingGameRequest racingGameRequest, RacingGame racingGame) { + Long racingGameId = racingGameDao.save(new RacingGameEntity(racingGameRequest.getCount())); + carDao.saveAll(toCarEntities(racingGame, racingGameId)); } private List toCarEntities(RacingGame game, Long racingGameId) { @@ -44,4 +50,14 @@ private List toCarEntities(RacingGame game, Long racingGameId) { .map(car -> new CarEntity(car.getName(), car.getPosition(), winners.contains(car), racingGameId)) .collect(Collectors.toList()); } + + public List findHistory() { + List racingGameEntities = racingGameDao.findAllByCreatedTimeAsc(); + + return racingGameEntities.stream() + .map(RacingGameEntity::getId) + .map(carDao::findByRacingGameId) + .map(RacingGameResponse::of) + .collect(Collectors.toList()); + } } diff --git a/src/test/java/racingcar/service/RacingGameServiceTest.java b/src/test/java/racingcar/service/RacingGameServiceTest.java index 578a57ef2..63075f9ac 100644 --- a/src/test/java/racingcar/service/RacingGameServiceTest.java +++ b/src/test/java/racingcar/service/RacingGameServiceTest.java @@ -2,19 +2,34 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertAll; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.BDDMockito.given; +import java.util.List; +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.context.SpringBootTest; +import org.mockito.Mockito; +import racingcar.dao.CarDao; +import racingcar.dao.RacingGameDao; +import racingcar.dao.entity.CarEntity; +import racingcar.dao.entity.RacingGameEntity; import racingcar.dto.RacingGameRequest; import racingcar.dto.RacingGameResponse; -@SpringBootTest + class RacingGameServiceTest { - @Autowired RacingGameService racingGameService; + RacingGameDao racingGameDao; + CarDao carDao; + + @BeforeEach + void setUp() { + carDao = Mockito.mock(CarDao.class); + racingGameDao = Mockito.mock(RacingGameDao.class); + racingGameService = new RacingGameService(carDao, racingGameDao); + } @Test @DisplayName("이름과 실행 횟수를 받아 게임의 결과를 반환한다") @@ -28,4 +43,22 @@ void playTest() { () -> assertThat(play.getRacingCars()).hasSize(2) ); } + + @Test + @DisplayName("전체 결과를 조회하여 결과를 반환한다") + void findHistory() { + given(racingGameDao.findAllByCreatedTimeAsc()) + .willReturn(List.of(new RacingGameEntity(10))); + given(carDao.findByRacingGameId(any())) + .willReturn(List.of(new CarEntity("박스터", 7, false, 1L), + new CarEntity("현구막", 10, true, 1L))); + + List history = racingGameService.findHistory(); + + assertAll( + () -> assertThat(history).hasSize(1), + () -> assertThat(history.get(0).getRacingCars()).hasSize(2), + () -> assertThat(history.get(0).getWinners()).isEqualTo("현구막") + ); + } } From c4a600c6dee162d95cb6759c3ebb370486a44ba4 Mon Sep 17 00:00:00 2001 From: drunkenhw Date: Mon, 17 Apr 2023 19:43:00 +0900 Subject: [PATCH 16/31] =?UTF-8?q?feat:=20api=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/RacingGameController.java | 8 ++++++ .../controller/RacingGameControllerTest.java | 24 ++++++++++++++++- .../racingcar/integration/GamePlayTest.java | 27 +++++++++++++++++++ 3 files changed, 58 insertions(+), 1 deletion(-) diff --git a/src/main/java/racingcar/controller/RacingGameController.java b/src/main/java/racingcar/controller/RacingGameController.java index 6a6255015..ea6cefb11 100644 --- a/src/main/java/racingcar/controller/RacingGameController.java +++ b/src/main/java/racingcar/controller/RacingGameController.java @@ -1,6 +1,8 @@ package racingcar.controller; +import java.util.List; import org.springframework.http.ResponseEntity; +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; @@ -21,4 +23,10 @@ public ResponseEntity play(@RequestBody RacingGameRequest ra RacingGameResponse response = racingGameService.play(racingGameRequest); return ResponseEntity.ok(response); } + + @GetMapping("/plays") + public ResponseEntity> playHistory() { + List history = racingGameService.findHistory(); + return ResponseEntity.ok(history); + } } diff --git a/src/test/java/racingcar/controller/RacingGameControllerTest.java b/src/test/java/racingcar/controller/RacingGameControllerTest.java index d5c033e3c..c5c7d3773 100644 --- a/src/test/java/racingcar/controller/RacingGameControllerTest.java +++ b/src/test/java/racingcar/controller/RacingGameControllerTest.java @@ -4,6 +4,7 @@ import static org.hamcrest.Matchers.is; import static org.mockito.ArgumentMatchers.any; 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.jsonPath; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; @@ -40,7 +41,7 @@ public void plays() throws Exception { RacingGameRequest request = new RacingGameRequest("현구막,박스터", 10); RacingGameResponse expectedResponse = new RacingGameResponse( List.of("현구막"), - List.of(new CarDto("현구막", 10, true), new CarDto("박스터", 7, false)) + List.of(new CarDto("현구막", 10), new CarDto("박스터", 7)) ); String requestString = objectMapper.writeValueAsString(request); when(racingGameService.play(any())).thenReturn(expectedResponse); @@ -53,4 +54,25 @@ public void plays() throws Exception { .andExpect(jsonPath("$.winners", is("현구막"))) .andExpect(jsonPath("$.racingCars", hasSize(2))); } + + @Test + @DisplayName("자동차 경주 경기 이력을 반환한다.") + public void findHistory() throws Exception { + RacingGameResponse firstGameResponse = new RacingGameResponse( + List.of("현구막"), + List.of(new CarDto("현구막", 10), new CarDto("박스터", 7)) + ); + RacingGameResponse secondGameResponse = new RacingGameResponse( + List.of("박스터"), + List.of(new CarDto("현구막", 6), new CarDto("박스터", 8)) + ); + List history = List.of(firstGameResponse, secondGameResponse); + + when(racingGameService.findHistory()).thenReturn(history); + + mockMvc.perform(get("/plays")) + + .andExpect(status().isOk()) + .andExpect(jsonPath("$.size()", is(2))); + } } diff --git a/src/test/java/racingcar/integration/GamePlayTest.java b/src/test/java/racingcar/integration/GamePlayTest.java index 854fbc308..3e95faae8 100644 --- a/src/test/java/racingcar/integration/GamePlayTest.java +++ b/src/test/java/racingcar/integration/GamePlayTest.java @@ -4,22 +4,37 @@ import static org.hamcrest.Matchers.notNullValue; import io.restassured.RestAssured; +import java.util.List; 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.context.SpringBootTest; import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; import org.springframework.boot.test.web.server.LocalServerPort; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; +import racingcar.dao.CarDao; +import racingcar.dao.RacingGameDao; +import racingcar.dao.entity.CarEntity; +import racingcar.dao.entity.RacingGameEntity; import racingcar.dto.RacingGameRequest; @SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT) class GamePlayTest { + @Autowired + CarDao carDao; + @Autowired + RacingGameDao racingGameDao; + @BeforeEach void setUp(@LocalServerPort int port) { RestAssured.port = port; + Long firstId = racingGameDao.save(new RacingGameEntity(10)); + carDao.saveAll(List.of(new CarEntity("박스터", 10, true, firstId), new CarEntity("현구막", 5, false, firstId))); + Long secondId = racingGameDao.save(new RacingGameEntity(5)); + carDao.saveAll(List.of(new CarEntity("벤츠", 3, false, secondId), new CarEntity("엔초", 4, true, secondId))); } @Test @@ -37,4 +52,16 @@ void playGame() { .body("winners", notNullValue()) .body("racingCars", hasSize(3)); } + + @Test + @DisplayName("자동차 경주 게임 이력을 조회한다") + void findHistory() { + RestAssured.given().log().all() + + .when().get("/plays") + + .then().log().all() + .statusCode(HttpStatus.OK.value()) + .body("$", hasSize(2)); + } } From 35f4996933add247094f6ced8cb8c80058877afb Mon Sep 17 00:00:00 2001 From: drunkenhw Date: Mon, 17 Apr 2023 22:29:19 +0900 Subject: [PATCH 17/31] =?UTF-8?q?feat:=20=EC=98=88=EC=99=B8=20=EC=B2=98?= =?UTF-8?q?=EB=A6=AC=20=EC=BB=A8=ED=8A=B8=EB=A1=A4=EB=9F=AC=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 --- .../controller/ControllerAdvice.java | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 src/main/java/racingcar/controller/ControllerAdvice.java diff --git a/src/main/java/racingcar/controller/ControllerAdvice.java b/src/main/java/racingcar/controller/ControllerAdvice.java new file mode 100644 index 000000000..dbd43efbb --- /dev/null +++ b/src/main/java/racingcar/controller/ControllerAdvice.java @@ -0,0 +1,19 @@ +package racingcar.controller; + +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.RestControllerAdvice; + +@RestControllerAdvice +public class ControllerAdvice { + + @ExceptionHandler + public ResponseEntity handleValidationExceptions(RuntimeException e) { + return ResponseEntity.internalServerError().body(e.getMessage()); + } + + @ExceptionHandler + public ResponseEntity handleValidationExceptions(IllegalArgumentException e) { + return ResponseEntity.badRequest().body(e.getMessage()); + } +} From 068f3951cd4c33fdbb30b0a737ff6cf73d73feb1 Mon Sep 17 00:00:00 2001 From: drunkenhw Date: Mon, 17 Apr 2023 22:30:11 +0900 Subject: [PATCH 18/31] =?UTF-8?q?feat:=20=EC=BD=98=EC=86=94=20=EA=B2=8C?= =?UTF-8?q?=EC=9E=84=20=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 --- .../RacingCarConsoleApplication.java | 61 +++++++++++++++++++ .../controller/RacingConsoleController.java | 27 ++++++++ src/main/java/racingcar/view/InputView.java | 15 ++--- src/main/java/racingcar/view/OutputView.java | 13 ++-- 4 files changed, 98 insertions(+), 18 deletions(-) create mode 100644 src/main/java/racingcar/RacingCarConsoleApplication.java create mode 100644 src/main/java/racingcar/controller/RacingConsoleController.java diff --git a/src/main/java/racingcar/RacingCarConsoleApplication.java b/src/main/java/racingcar/RacingCarConsoleApplication.java new file mode 100644 index 000000000..f0f90f9fd --- /dev/null +++ b/src/main/java/racingcar/RacingCarConsoleApplication.java @@ -0,0 +1,61 @@ +package racingcar; + +import java.util.Comparator; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.stream.Collectors; +import racingcar.controller.RacingConsoleController; +import racingcar.dao.CarDao; +import racingcar.dao.RacingGameDao; +import racingcar.dao.entity.CarEntity; +import racingcar.dao.entity.RacingGameEntity; +import racingcar.service.RacingGameService; + +public class RacingCarConsoleApplication { + public static void main(String[] args) { + RacingGameService racingGameService = new RacingGameService(new InnerCarDao(), new InnerRacingGameDao()); + RacingConsoleController racingConsoleController = new RacingConsoleController(racingGameService); + racingConsoleController.run(); + } + + static class InnerCarDao implements CarDao { + private final Map database = new HashMap<>(); + private Long key = 1L; + + @Override + public void saveAll(List racingCars) { + for (CarEntity racingCar : racingCars) { + database.put(key, racingCar); + key++; + } + } + + @Override + public List findByRacingGameId(Long id) { + return database.values().stream() + .filter(carEntity -> Objects.equals(carEntity.getGameId(), id)) + .collect(Collectors.toList()); + } + } + + static class InnerRacingGameDao implements RacingGameDao { + private final Map database = new HashMap<>(); + private Long id = 1L; + + @Override + public Long save(RacingGameEntity racingGameEntity) { + database.put(id, racingGameEntity); + id++; + return id - 1; + } + + @Override + public List findAllByCreatedTimeAsc() { + return database.values() + .stream().sorted(Comparator.comparing(RacingGameEntity::getCreatedTime)) + .collect(Collectors.toList()); + } + } +} diff --git a/src/main/java/racingcar/controller/RacingConsoleController.java b/src/main/java/racingcar/controller/RacingConsoleController.java new file mode 100644 index 000000000..5973fc21a --- /dev/null +++ b/src/main/java/racingcar/controller/RacingConsoleController.java @@ -0,0 +1,27 @@ +package racingcar.controller; + +import racingcar.dto.RacingGameRequest; +import racingcar.dto.RacingGameResponse; +import racingcar.service.RacingGameService; +import racingcar.view.InputView; +import racingcar.view.OutputView; + +public class RacingConsoleController { + + private final RacingGameService racingGameService; + + public RacingConsoleController(RacingGameService racingGameService) { + this.racingGameService = racingGameService; + } + + public void run() { + String names = InputView.inputNames(); + int count = InputView.inputTryCount(); + RacingGameRequest racingGameRequest = new RacingGameRequest(names, count); + + RacingGameResponse racingGameResponse = racingGameService.play(racingGameRequest); + + OutputView.printRacing(racingGameResponse.getRacingCars()); + OutputView.printWinners(racingGameResponse.getWinners()); + } +} diff --git a/src/main/java/racingcar/view/InputView.java b/src/main/java/racingcar/view/InputView.java index a733d4d79..4beed69b0 100644 --- a/src/main/java/racingcar/view/InputView.java +++ b/src/main/java/racingcar/view/InputView.java @@ -1,22 +1,15 @@ package racingcar.view; -import java.util.Arrays; -import java.util.List; import java.util.Scanner; -import java.util.stream.Collectors; public class InputView { - public static final String INPUT_NAMES = "경주할 자동차 이름을 입력하세요(이름은 쉼표(,)를 기준으로 구분)."; - public static final String INPUT_TRY_COUNT = "시도할 회수는 몇회인가요?"; - public static final String SPLIT_DELIMITER = ","; - + private static final String INPUT_NAMES = "경주할 자동차 이름을 입력하세요(이름은 쉼표(,)를 기준으로 구분)."; + private static final String INPUT_TRY_COUNT = "시도할 회수는 몇회인가요?"; private static final Scanner SCANNER = new Scanner(System.in); - public static List inputNames() { + public static String inputNames() { System.out.println(INPUT_NAMES); - String input = SCANNER.next(); - return Arrays.stream(input.split(SPLIT_DELIMITER)) - .collect(Collectors.toList()); + return SCANNER.next(); } public static int inputTryCount() { diff --git a/src/main/java/racingcar/view/OutputView.java b/src/main/java/racingcar/view/OutputView.java index cd50f90a2..e94af0f14 100644 --- a/src/main/java/racingcar/view/OutputView.java +++ b/src/main/java/racingcar/view/OutputView.java @@ -1,8 +1,7 @@ package racingcar.view; import java.util.List; -import racingcar.domain.Car; -import racingcar.domain.Cars; +import racingcar.dto.CarDto; public class OutputView { public static final String GAME_RESULT_FORMAT = "%s : %s\n"; @@ -10,16 +9,16 @@ public class OutputView { private static final String POSITION_VIEW = "-"; public static final String WINNER_DELIMITER = ", "; - public static void printRacing(Cars cars) { - for (Car car : cars.getCars()) { - int position = car.getPosition(); + public static void printRacing(List carDtos) { + for (CarDto carDto : carDtos) { + int position = carDto.getPosition(); String positionView = POSITION_VIEW.repeat(position); - System.out.printf(GAME_RESULT_FORMAT, car.getName(), positionView); + System.out.printf(GAME_RESULT_FORMAT, carDto.getName(), positionView); } System.out.println(); } - public static void printWinners(List names) { + public static void printWinners(String names) { String winners = String.join(WINNER_DELIMITER, names); System.out.printf(WINNER_FORMAT, winners); } From 578e6757ef0f65f7b699f9296bedee90718bdd89 Mon Sep 17 00:00:00 2001 From: drunkenhw Date: Fri, 21 Apr 2023 15:28:22 +0900 Subject: [PATCH 19/31] =?UTF-8?q?feat:=20In=20memory=20Dao=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 --- .../RacingCarConsoleApplication.java | 53 ++----------------- .../java/racingcar/dao/CarInMemoryDao.java | 28 ++++++++++ .../racingcar/dao/RacingGameInMemoryDao.java | 27 ++++++++++ 3 files changed, 58 insertions(+), 50 deletions(-) create mode 100644 src/main/java/racingcar/dao/CarInMemoryDao.java create mode 100644 src/main/java/racingcar/dao/RacingGameInMemoryDao.java diff --git a/src/main/java/racingcar/RacingCarConsoleApplication.java b/src/main/java/racingcar/RacingCarConsoleApplication.java index f0f90f9fd..0d48d84c7 100644 --- a/src/main/java/racingcar/RacingCarConsoleApplication.java +++ b/src/main/java/racingcar/RacingCarConsoleApplication.java @@ -1,61 +1,14 @@ package racingcar; -import java.util.Comparator; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.stream.Collectors; import racingcar.controller.RacingConsoleController; -import racingcar.dao.CarDao; -import racingcar.dao.RacingGameDao; -import racingcar.dao.entity.CarEntity; -import racingcar.dao.entity.RacingGameEntity; +import racingcar.dao.CarInMemoryDao; +import racingcar.dao.RacingGameInMemoryDao; import racingcar.service.RacingGameService; public class RacingCarConsoleApplication { public static void main(String[] args) { - RacingGameService racingGameService = new RacingGameService(new InnerCarDao(), new InnerRacingGameDao()); + RacingGameService racingGameService = new RacingGameService(new CarInMemoryDao(), new RacingGameInMemoryDao()); RacingConsoleController racingConsoleController = new RacingConsoleController(racingGameService); racingConsoleController.run(); } - - static class InnerCarDao implements CarDao { - private final Map database = new HashMap<>(); - private Long key = 1L; - - @Override - public void saveAll(List racingCars) { - for (CarEntity racingCar : racingCars) { - database.put(key, racingCar); - key++; - } - } - - @Override - public List findByRacingGameId(Long id) { - return database.values().stream() - .filter(carEntity -> Objects.equals(carEntity.getGameId(), id)) - .collect(Collectors.toList()); - } - } - - static class InnerRacingGameDao implements RacingGameDao { - private final Map database = new HashMap<>(); - private Long id = 1L; - - @Override - public Long save(RacingGameEntity racingGameEntity) { - database.put(id, racingGameEntity); - id++; - return id - 1; - } - - @Override - public List findAllByCreatedTimeAsc() { - return database.values() - .stream().sorted(Comparator.comparing(RacingGameEntity::getCreatedTime)) - .collect(Collectors.toList()); - } - } } diff --git a/src/main/java/racingcar/dao/CarInMemoryDao.java b/src/main/java/racingcar/dao/CarInMemoryDao.java new file mode 100644 index 000000000..3eee6e79a --- /dev/null +++ b/src/main/java/racingcar/dao/CarInMemoryDao.java @@ -0,0 +1,28 @@ +package racingcar.dao; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.stream.Collectors; +import racingcar.dao.entity.CarEntity; + +public class CarInMemoryDao implements CarDao { + private final Map database = new HashMap<>(); + private Long id = 1L; + + @Override + public void saveAll(List racingCars) { + for (CarEntity racingCar : racingCars) { + database.put(id, racingCar); + id++; + } + } + + @Override + public List findByRacingGameId(Long id) { + return database.values().stream() + .filter(carEntity -> Objects.equals(carEntity.getRacingGameId(), id)) + .collect(Collectors.toList()); + } +} diff --git a/src/main/java/racingcar/dao/RacingGameInMemoryDao.java b/src/main/java/racingcar/dao/RacingGameInMemoryDao.java new file mode 100644 index 000000000..363798085 --- /dev/null +++ b/src/main/java/racingcar/dao/RacingGameInMemoryDao.java @@ -0,0 +1,27 @@ +package racingcar.dao; + +import java.util.Comparator; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; +import racingcar.dao.entity.RacingGameEntity; + +public class RacingGameInMemoryDao implements RacingGameDao { + private final Map database = new HashMap<>(); + private Long id = 1L; + + @Override + public Long save(RacingGameEntity racingGameEntity) { + database.put(id, racingGameEntity); + id++; + return id - 1; + } + + @Override + public List findAllByCreatedTimeAsc() { + return database.values() + .stream().sorted(Comparator.comparing(RacingGameEntity::getCreatedTime)) + .collect(Collectors.toList()); + } +} From 06b060800cafb0de01b74336a483b72c2fd51798 Mon Sep 17 00:00:00 2001 From: drunkenhw Date: Fri, 21 Apr 2023 15:35:57 +0900 Subject: [PATCH 20/31] =?UTF-8?q?refactor:=20simpleJdbcInsert=EB=A5=BC=20?= =?UTF-8?q?=EC=82=AC=EC=9A=A9=ED=95=98=EB=8F=84=EB=A1=9D=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/racingcar/dao/CarJdbcDao.java | 31 +++++++------------ .../racingcar/dao/RacingGameInMemoryDao.java | 2 +- .../java/racingcar/dao/RacingGameJdbcDao.java | 24 ++++++-------- .../java/racingcar/dao/entity/CarEntity.java | 16 +++++----- .../dao/entity/RacingGameEntity.java | 8 ++--- .../racingcar/dto/RacingGameResponse.java | 2 +- src/main/resources/data.sql | 16 +++++----- .../java/racingcar/dao/CarJdbcDaoTest.java | 8 ++--- 8 files changed, 46 insertions(+), 61 deletions(-) diff --git a/src/main/java/racingcar/dao/CarJdbcDao.java b/src/main/java/racingcar/dao/CarJdbcDao.java index 919e57cb7..7f6e5ff26 100644 --- a/src/main/java/racingcar/dao/CarJdbcDao.java +++ b/src/main/java/racingcar/dao/CarJdbcDao.java @@ -1,40 +1,31 @@ package racingcar.dao; -import java.sql.PreparedStatement; -import java.sql.SQLException; import java.util.List; -import org.springframework.jdbc.core.BatchPreparedStatementSetter; import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.jdbc.core.namedparam.BeanPropertySqlParameterSource; +import org.springframework.jdbc.core.simple.SimpleJdbcInsert; import org.springframework.stereotype.Component; import racingcar.dao.entity.CarEntity; @Component public class CarJdbcDao implements CarDao { + private final SimpleJdbcInsert simpleJdbcInsert; private final JdbcTemplate jdbcTemplate; public CarJdbcDao(JdbcTemplate jdbcTemplate) { + this.simpleJdbcInsert = new SimpleJdbcInsert(jdbcTemplate) + .withTableName("car") + .usingGeneratedKeyColumns("id"); this.jdbcTemplate = jdbcTemplate; } @Override public void saveAll(List racingCars) { - String sql = "INSERT INTO CAR (name, position, is_win, racing_game_id) VALUES (?, ?, ?, ?)"; - jdbcTemplate.batchUpdate(sql, new BatchPreparedStatementSetter() { - @Override - public void setValues(PreparedStatement ps, int i) throws SQLException { - CarEntity carEntity = racingCars.get(i); - ps.setString(1, carEntity.getName()); - ps.setInt(2, carEntity.getPosition()); - ps.setBoolean(3, carEntity.isWin()); - ps.setLong(4, carEntity.getGameId()); - } - - @Override - public int getBatchSize() { - return racingCars.size(); - } - }); + BeanPropertySqlParameterSource[] parameterSources = racingCars.stream() + .map(BeanPropertySqlParameterSource::new) + .toArray(BeanPropertySqlParameterSource[]::new); + simpleJdbcInsert.executeBatch(parameterSources); } @Override @@ -43,7 +34,7 @@ public List findByRacingGameId(Long racingGameId) { (resultSet, rowNum) -> new CarEntity( resultSet.getString("name"), resultSet.getInt("position"), - resultSet.getBoolean("is_win"), + resultSet.getBoolean("winner"), racingGameId )); } diff --git a/src/main/java/racingcar/dao/RacingGameInMemoryDao.java b/src/main/java/racingcar/dao/RacingGameInMemoryDao.java index 363798085..510b2dbbf 100644 --- a/src/main/java/racingcar/dao/RacingGameInMemoryDao.java +++ b/src/main/java/racingcar/dao/RacingGameInMemoryDao.java @@ -21,7 +21,7 @@ public Long save(RacingGameEntity racingGameEntity) { @Override public List findAllByCreatedTimeAsc() { return database.values() - .stream().sorted(Comparator.comparing(RacingGameEntity::getCreatedTime)) + .stream().sorted(Comparator.comparing(RacingGameEntity::getCreatedAt)) .collect(Collectors.toList()); } } diff --git a/src/main/java/racingcar/dao/RacingGameJdbcDao.java b/src/main/java/racingcar/dao/RacingGameJdbcDao.java index ce7086721..ae8c49239 100644 --- a/src/main/java/racingcar/dao/RacingGameJdbcDao.java +++ b/src/main/java/racingcar/dao/RacingGameJdbcDao.java @@ -1,11 +1,10 @@ package racingcar.dao; -import java.sql.PreparedStatement; -import java.sql.Timestamp; import java.util.List; import org.springframework.jdbc.core.JdbcTemplate; -import org.springframework.jdbc.support.GeneratedKeyHolder; -import org.springframework.jdbc.support.KeyHolder; +import org.springframework.jdbc.core.namedparam.BeanPropertySqlParameterSource; +import org.springframework.jdbc.core.namedparam.SqlParameterSource; +import org.springframework.jdbc.core.simple.SimpleJdbcInsert; import org.springframework.stereotype.Component; import racingcar.dao.entity.RacingGameEntity; @@ -13,24 +12,19 @@ public class RacingGameJdbcDao implements RacingGameDao { private final JdbcTemplate jdbcTemplate; + private final SimpleJdbcInsert simpleJdbcInsert; public RacingGameJdbcDao(JdbcTemplate jdbcTemplate) { this.jdbcTemplate = jdbcTemplate; + this.simpleJdbcInsert = new SimpleJdbcInsert(jdbcTemplate) + .withTableName("racing_game") + .usingGeneratedKeyColumns("id"); } @Override public Long save(RacingGameEntity racingGameEntity) { - String sql = "INSERT INTO racing_game (trial_count, created_at) VALUES (?,?)"; - - KeyHolder keyHolder = new GeneratedKeyHolder(); - jdbcTemplate.update(con -> { - PreparedStatement preparedStatement = con.prepareStatement(sql, new String[]{"id"}); - preparedStatement.setInt(1, racingGameEntity.getTrialCount()); - preparedStatement.setTimestamp(2, Timestamp.valueOf(racingGameEntity.getCreatedTime())); - return preparedStatement; - }, keyHolder); - - return keyHolder.getKey().longValue(); + SqlParameterSource params = new BeanPropertySqlParameterSource(racingGameEntity); + return simpleJdbcInsert.executeAndReturnKey(params).longValue(); } @Override diff --git a/src/main/java/racingcar/dao/entity/CarEntity.java b/src/main/java/racingcar/dao/entity/CarEntity.java index e9d463c9e..ec6f05595 100644 --- a/src/main/java/racingcar/dao/entity/CarEntity.java +++ b/src/main/java/racingcar/dao/entity/CarEntity.java @@ -4,14 +4,14 @@ public class CarEntity { private final String name; private final int position; - private final boolean isWin; - private final Long gameId; + private final boolean winner; + private final Long racingGameId; public CarEntity(String name, int position, boolean isWin, Long gameId) { this.name = name; this.position = position; - this.isWin = isWin; - this.gameId = gameId; + this.winner = isWin; + this.racingGameId = gameId; } public String getName() { @@ -22,11 +22,11 @@ public int getPosition() { return position; } - public boolean isWin() { - return isWin; + public boolean isWinner() { + return winner; } - public Long getGameId() { - return gameId; + public Long getRacingGameId() { + return racingGameId; } } diff --git a/src/main/java/racingcar/dao/entity/RacingGameEntity.java b/src/main/java/racingcar/dao/entity/RacingGameEntity.java index 880ce0bba..d4a5ade66 100644 --- a/src/main/java/racingcar/dao/entity/RacingGameEntity.java +++ b/src/main/java/racingcar/dao/entity/RacingGameEntity.java @@ -6,12 +6,12 @@ public class RacingGameEntity { private final Long id; private final int trialCount; - private final LocalDateTime createdTime; + private final LocalDateTime createdAt; public RacingGameEntity(Long id, int trialCount, LocalDateTime createdTime) { this.id = id; this.trialCount = trialCount; - this.createdTime = createdTime; + this.createdAt = createdTime; } public RacingGameEntity(int trialCount) { @@ -26,7 +26,7 @@ public Long getId() { return id; } - public LocalDateTime getCreatedTime() { - return createdTime; + public LocalDateTime getCreatedAt() { + return createdAt; } } diff --git a/src/main/java/racingcar/dto/RacingGameResponse.java b/src/main/java/racingcar/dto/RacingGameResponse.java index f03be7b1e..7fbaac206 100644 --- a/src/main/java/racingcar/dto/RacingGameResponse.java +++ b/src/main/java/racingcar/dto/RacingGameResponse.java @@ -18,7 +18,7 @@ public RacingGameResponse(List winners, List racingCars) { public static RacingGameResponse of(List carEntities) { List winners = carEntities.stream() - .filter(CarEntity::isWin) + .filter(CarEntity::isWinner) .map(CarEntity::getName) .collect(Collectors.toList()); List carDtos = carEntities.stream() diff --git a/src/main/resources/data.sql b/src/main/resources/data.sql index dda2ac342..bcb1ea9e3 100644 --- a/src/main/resources/data.sql +++ b/src/main/resources/data.sql @@ -3,19 +3,19 @@ DROP TABLE IF EXISTS racing_game; CREATE TABLE RACING_GAME ( - id INT NOT NULL AUTO_INCREMENT, - trial_count INT NOT NULL, - created_at DATETIME NOT NULL, + id INT NOT NULL AUTO_INCREMENT, + trial_count INT NOT NULL, + created_at DATETIME NOT NULL, PRIMARY KEY (id) ); CREATE TABLE CAR ( - id INT NOT NULL AUTO_INCREMENT, - name VARCHAR(50) NOT NULL, - position INT NOT NULL, - is_win SMALLINT NOT NULL , - racing_game_id INT NOT NULL, + id INT NOT NULL AUTO_INCREMENT, + name VARCHAR(50) NOT NULL, + position INT NOT NULL, + winner BOOL NOT NULL, + racing_game_id INT NOT NULL, PRIMARY KEY (id), FOREIGN KEY (racing_game_id) REFERENCES RACING_GAME (id) ) diff --git a/src/test/java/racingcar/dao/CarJdbcDaoTest.java b/src/test/java/racingcar/dao/CarJdbcDaoTest.java index 4827ad574..ac579b881 100644 --- a/src/test/java/racingcar/dao/CarJdbcDaoTest.java +++ b/src/test/java/racingcar/dao/CarJdbcDaoTest.java @@ -29,10 +29,10 @@ void setUp() { CarEntity boxster = new CarEntity("boxster", 10, true, gameId); CarEntity encho = new CarEntity("encho", 7, false, gameId); - jdbcTemplate.update("INSERT INTO CAR (name, position, is_win, racing_game_id) VALUES (?, ?, ?, ?)", - boxster.getName(), boxster.getPosition(), boxster.isWin(), boxster.getGameId()); - jdbcTemplate.update("INSERT INTO CAR (name, position, is_win, racing_game_id) VALUES (?, ?, ?, ?)", - encho.getName(), encho.getPosition(), encho.isWin(), encho.getGameId()); + jdbcTemplate.update("INSERT INTO CAR (name, position, winner, racing_game_id) VALUES (?, ?, ?, ?)", + boxster.getName(), boxster.getPosition(), boxster.isWinner(), boxster.getRacingGameId()); + jdbcTemplate.update("INSERT INTO CAR (name, position, winner, racing_game_id) VALUES (?, ?, ?, ?)", + encho.getName(), encho.getPosition(), encho.isWinner(), encho.getRacingGameId()); } From e886c529ad6e25741a59e8e47c53fd07619aa4d9 Mon Sep 17 00:00:00 2001 From: drunkenhw Date: Fri, 21 Apr 2023 15:36:56 +0900 Subject: [PATCH 21/31] =?UTF-8?q?refactor:=20=EC=B5=9C=EC=83=81=EC=9C=84?= =?UTF-8?q?=20=EC=98=88=EC=99=B8=EB=A5=BC=20=EC=9E=A1=EB=8A=94=20=EB=A9=94?= =?UTF-8?q?=EC=84=9C=EB=93=9C=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/controller/ControllerAdvice.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/main/java/racingcar/controller/ControllerAdvice.java b/src/main/java/racingcar/controller/ControllerAdvice.java index dbd43efbb..e18334dcd 100644 --- a/src/main/java/racingcar/controller/ControllerAdvice.java +++ b/src/main/java/racingcar/controller/ControllerAdvice.java @@ -7,6 +7,11 @@ @RestControllerAdvice public class ControllerAdvice { + @ExceptionHandler + public ResponseEntity handleValidationExceptions(Exception e) { + return ResponseEntity.internalServerError().body(e.getMessage()); + } + @ExceptionHandler public ResponseEntity handleValidationExceptions(RuntimeException e) { return ResponseEntity.internalServerError().body(e.getMessage()); From a5de3c2cac361949400a50fab8ab8f4fdbf2446e Mon Sep 17 00:00:00 2001 From: drunkenhw Date: Sun, 23 Apr 2023 13:42:34 +0900 Subject: [PATCH 22/31] =?UTF-8?q?refactor:=20=ED=94=84=EB=A1=A0=ED=8A=B8?= =?UTF-8?q?=EC=97=90=EC=84=9C=20List=EB=A1=9C=20=EB=B0=98=ED=99=98?= =?UTF-8?q?=ED=95=98=EB=8F=84=EB=A1=9D=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/RacingConsoleController.java | 3 +- .../java/racingcar/dto/RacingGameRequest.java | 14 +- .../racingcar/dto/RacingGameResponse.java | 7 +- .../racingcar/service/RacingGameService.java | 2 +- src/main/java/racingcar/view/InputView.java | 8 +- src/main/java/racingcar/view/OutputView.java | 2 +- src/main/resources/static/index.html | 124 +++++++++--------- .../controller/RacingGameControllerTest.java | 4 +- .../racingcar/integration/GamePlayTest.java | 2 +- .../service/RacingGameServiceTest.java | 4 +- 10 files changed, 83 insertions(+), 87 deletions(-) diff --git a/src/main/java/racingcar/controller/RacingConsoleController.java b/src/main/java/racingcar/controller/RacingConsoleController.java index 5973fc21a..c6100af30 100644 --- a/src/main/java/racingcar/controller/RacingConsoleController.java +++ b/src/main/java/racingcar/controller/RacingConsoleController.java @@ -1,5 +1,6 @@ package racingcar.controller; +import java.util.List; import racingcar.dto.RacingGameRequest; import racingcar.dto.RacingGameResponse; import racingcar.service.RacingGameService; @@ -15,7 +16,7 @@ public RacingConsoleController(RacingGameService racingGameService) { } public void run() { - String names = InputView.inputNames(); + List names = InputView.inputNames(); int count = InputView.inputTryCount(); RacingGameRequest racingGameRequest = new RacingGameRequest(names, count); diff --git a/src/main/java/racingcar/dto/RacingGameRequest.java b/src/main/java/racingcar/dto/RacingGameRequest.java index e523103e3..929be9678 100644 --- a/src/main/java/racingcar/dto/RacingGameRequest.java +++ b/src/main/java/racingcar/dto/RacingGameRequest.java @@ -1,14 +1,12 @@ package racingcar.dto; -import java.util.Arrays; import java.util.List; -import java.util.stream.Collectors; public class RacingGameRequest { - private String names; + private List names; private int count; - public RacingGameRequest(String names, int count) { + public RacingGameRequest(List names, int count) { this.names = names; this.count = count; } @@ -17,13 +15,7 @@ public RacingGameRequest() { } - public List toNameList() { - return Arrays.stream(names.split(",")) - .map(String::strip) - .collect(Collectors.toList()); - } - - public String getNames() { + public List getNames() { return names; } diff --git a/src/main/java/racingcar/dto/RacingGameResponse.java b/src/main/java/racingcar/dto/RacingGameResponse.java index 7fbaac206..5bd68d4b9 100644 --- a/src/main/java/racingcar/dto/RacingGameResponse.java +++ b/src/main/java/racingcar/dto/RacingGameResponse.java @@ -7,12 +7,11 @@ import racingcar.domain.RacingGame; public class RacingGameResponse { - - private final String winners; + private final List winners; private final List racingCars; public RacingGameResponse(List winners, List racingCars) { - this.winners = String.join(",", winners); + this.winners = winners; this.racingCars = racingCars; } @@ -37,7 +36,7 @@ public static RacingGameResponse of(RacingGame racingGame) { return new RacingGameResponse(winners, carDtos); } - public String getWinners() { + public List getWinners() { return winners; } diff --git a/src/main/java/racingcar/service/RacingGameService.java b/src/main/java/racingcar/service/RacingGameService.java index 6db1b95f3..f686ad2e5 100644 --- a/src/main/java/racingcar/service/RacingGameService.java +++ b/src/main/java/racingcar/service/RacingGameService.java @@ -33,7 +33,7 @@ public RacingGameResponse play(RacingGameRequest racingGameRequest) { } private Cars toCars(RacingGameRequest racingGameRequest) { - List cars = racingGameRequest.toNameList().stream() + List cars = racingGameRequest.getNames().stream() .map(Car::new) .collect(Collectors.toList()); return new Cars(cars); diff --git a/src/main/java/racingcar/view/InputView.java b/src/main/java/racingcar/view/InputView.java index 4beed69b0..cbb58e524 100644 --- a/src/main/java/racingcar/view/InputView.java +++ b/src/main/java/racingcar/view/InputView.java @@ -1,15 +1,19 @@ package racingcar.view; +import java.util.Arrays; +import java.util.List; import java.util.Scanner; +import java.util.stream.Collectors; public class InputView { private static final String INPUT_NAMES = "경주할 자동차 이름을 입력하세요(이름은 쉼표(,)를 기준으로 구분)."; private static final String INPUT_TRY_COUNT = "시도할 회수는 몇회인가요?"; private static final Scanner SCANNER = new Scanner(System.in); - public static String inputNames() { + public static List inputNames() { System.out.println(INPUT_NAMES); - return SCANNER.next(); + return Arrays.stream(SCANNER.next().split(",")) + .collect(Collectors.toList()); } public static int inputTryCount() { diff --git a/src/main/java/racingcar/view/OutputView.java b/src/main/java/racingcar/view/OutputView.java index e94af0f14..8a8a7adc9 100644 --- a/src/main/java/racingcar/view/OutputView.java +++ b/src/main/java/racingcar/view/OutputView.java @@ -18,7 +18,7 @@ public static void printRacing(List carDtos) { System.out.println(); } - public static void printWinners(String names) { + public static void printWinners(List names) { String winners = String.join(WINNER_DELIMITER, names); System.out.printf(WINNER_FORMAT, winners); } diff --git a/src/main/resources/static/index.html b/src/main/resources/static/index.html index c731a65ff..9d4b01e30 100644 --- a/src/main/resources/static/index.html +++ b/src/main/resources/static/index.html @@ -1,83 +1,83 @@ - - 자동차 경주 게임 - + + 자동차 경주 게임 +
-

Racing Car

-
- - -
+

Racing Car

+
+ + +

자동차 경주 게임

-
- - +
+ + - - -
+ + +
- + -
- 우승자: -
- 결과: -
-
+
+ 우승자: +
+ 결과: +
+
diff --git a/src/test/java/racingcar/controller/RacingGameControllerTest.java b/src/test/java/racingcar/controller/RacingGameControllerTest.java index c5c7d3773..2c2a4996b 100644 --- a/src/test/java/racingcar/controller/RacingGameControllerTest.java +++ b/src/test/java/racingcar/controller/RacingGameControllerTest.java @@ -38,7 +38,7 @@ public class RacingGameControllerTest { @Test @DisplayName("자동차 경주를 실행하고 결과를 반환한다.") public void plays() throws Exception { - RacingGameRequest request = new RacingGameRequest("현구막,박스터", 10); + RacingGameRequest request = new RacingGameRequest(List.of("현구막", "박스터"), 10); RacingGameResponse expectedResponse = new RacingGameResponse( List.of("현구막"), List.of(new CarDto("현구막", 10), new CarDto("박스터", 7)) @@ -51,7 +51,7 @@ public void plays() throws Exception { .content(requestString)) .andExpect(status().isOk()) - .andExpect(jsonPath("$.winners", is("현구막"))) + .andExpect(jsonPath("$.winners[0]", is("현구막"))) .andExpect(jsonPath("$.racingCars", hasSize(2))); } diff --git a/src/test/java/racingcar/integration/GamePlayTest.java b/src/test/java/racingcar/integration/GamePlayTest.java index 3e95faae8..559d832ad 100644 --- a/src/test/java/racingcar/integration/GamePlayTest.java +++ b/src/test/java/racingcar/integration/GamePlayTest.java @@ -40,7 +40,7 @@ void setUp(@LocalServerPort int port) { @Test @DisplayName("자동차 경주 게임을 진행한다.") void playGame() { - RacingGameRequest request = new RacingGameRequest("브리,토미,브라운", 10); + RacingGameRequest request = new RacingGameRequest(List.of("브리", "토미", "브라운"), 10); RestAssured.given().log().all() .contentType(MediaType.APPLICATION_JSON_VALUE) .body(request) diff --git a/src/test/java/racingcar/service/RacingGameServiceTest.java b/src/test/java/racingcar/service/RacingGameServiceTest.java index 63075f9ac..8cfd23c0a 100644 --- a/src/test/java/racingcar/service/RacingGameServiceTest.java +++ b/src/test/java/racingcar/service/RacingGameServiceTest.java @@ -34,7 +34,7 @@ void setUp() { @Test @DisplayName("이름과 실행 횟수를 받아 게임의 결과를 반환한다") void playTest() { - RacingGameRequest racingGameRequest = new RacingGameRequest("박스터,엔초", 10); + RacingGameRequest racingGameRequest = new RacingGameRequest(List.of("박스터", "엔초"), 10); RacingGameResponse play = racingGameService.play(racingGameRequest); @@ -58,7 +58,7 @@ void findHistory() { assertAll( () -> assertThat(history).hasSize(1), () -> assertThat(history.get(0).getRacingCars()).hasSize(2), - () -> assertThat(history.get(0).getWinners()).isEqualTo("현구막") + () -> assertThat(history.get(0).getWinners()).containsExactly("현구막") ); } } From 04bb0d8a5f01c42851f254aae46bcb053c000ff5 Mon Sep 17 00:00:00 2001 From: drunkenhw Date: Sun, 23 Apr 2023 15:19:45 +0900 Subject: [PATCH 23/31] =?UTF-8?q?refactor:=20service=20=ED=81=B4=EB=9E=98?= =?UTF-8?q?=EC=8A=A4=20=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../RacingCarConsoleApplication.java | 5 +- .../controller/RacingConsoleController.java | 10 ++-- .../controller/RacingGameController.java | 15 +++--- ...Service.java => RacingGameAddService.java} | 14 +----- .../service/RacingGameFindService.java | 30 ++++++++++++ .../controller/RacingGameControllerTest.java | 10 ++-- .../service/RacingGameFindServiceTest.java | 48 +++++++++++++++++++ .../service/RacingGameServiceTest.java | 28 ++--------- 8 files changed, 107 insertions(+), 53 deletions(-) rename src/main/java/racingcar/service/{RacingGameService.java => RacingGameAddService.java} (78%) create mode 100644 src/main/java/racingcar/service/RacingGameFindService.java create mode 100644 src/test/java/racingcar/service/RacingGameFindServiceTest.java diff --git a/src/main/java/racingcar/RacingCarConsoleApplication.java b/src/main/java/racingcar/RacingCarConsoleApplication.java index 0d48d84c7..5e69a14f5 100644 --- a/src/main/java/racingcar/RacingCarConsoleApplication.java +++ b/src/main/java/racingcar/RacingCarConsoleApplication.java @@ -3,11 +3,12 @@ import racingcar.controller.RacingConsoleController; import racingcar.dao.CarInMemoryDao; import racingcar.dao.RacingGameInMemoryDao; -import racingcar.service.RacingGameService; +import racingcar.service.RacingGameAddService; public class RacingCarConsoleApplication { public static void main(String[] args) { - RacingGameService racingGameService = new RacingGameService(new CarInMemoryDao(), new RacingGameInMemoryDao()); + RacingGameAddService racingGameService = new RacingGameAddService(new CarInMemoryDao(), + new RacingGameInMemoryDao()); RacingConsoleController racingConsoleController = new RacingConsoleController(racingGameService); racingConsoleController.run(); } diff --git a/src/main/java/racingcar/controller/RacingConsoleController.java b/src/main/java/racingcar/controller/RacingConsoleController.java index c6100af30..68aac896c 100644 --- a/src/main/java/racingcar/controller/RacingConsoleController.java +++ b/src/main/java/racingcar/controller/RacingConsoleController.java @@ -3,16 +3,16 @@ import java.util.List; import racingcar.dto.RacingGameRequest; import racingcar.dto.RacingGameResponse; -import racingcar.service.RacingGameService; +import racingcar.service.RacingGameAddService; import racingcar.view.InputView; import racingcar.view.OutputView; public class RacingConsoleController { - private final RacingGameService racingGameService; + private final RacingGameAddService racingGameAddService; - public RacingConsoleController(RacingGameService racingGameService) { - this.racingGameService = racingGameService; + public RacingConsoleController(RacingGameAddService racingGameAddService) { + this.racingGameAddService = racingGameAddService; } public void run() { @@ -20,7 +20,7 @@ public void run() { int count = InputView.inputTryCount(); RacingGameRequest racingGameRequest = new RacingGameRequest(names, count); - RacingGameResponse racingGameResponse = racingGameService.play(racingGameRequest); + RacingGameResponse racingGameResponse = racingGameAddService.play(racingGameRequest); OutputView.printRacing(racingGameResponse.getRacingCars()); OutputView.printWinners(racingGameResponse.getWinners()); diff --git a/src/main/java/racingcar/controller/RacingGameController.java b/src/main/java/racingcar/controller/RacingGameController.java index ea6cefb11..65be551c7 100644 --- a/src/main/java/racingcar/controller/RacingGameController.java +++ b/src/main/java/racingcar/controller/RacingGameController.java @@ -8,25 +8,28 @@ import org.springframework.web.bind.annotation.RestController; import racingcar.dto.RacingGameRequest; import racingcar.dto.RacingGameResponse; -import racingcar.service.RacingGameService; +import racingcar.service.RacingGameAddService; +import racingcar.service.RacingGameFindService; @RestController public class RacingGameController { - private final RacingGameService racingGameService; + private final RacingGameAddService racingGameAddService; + private final RacingGameFindService racingGameFindService; - public RacingGameController(RacingGameService racingGameService) { - this.racingGameService = racingGameService; + public RacingGameController(RacingGameAddService racingGameService, RacingGameFindService racingGameFindService) { + this.racingGameAddService = racingGameService; + this.racingGameFindService = racingGameFindService; } @PostMapping("/plays") public ResponseEntity play(@RequestBody RacingGameRequest racingGameRequest) { - RacingGameResponse response = racingGameService.play(racingGameRequest); + RacingGameResponse response = racingGameAddService.play(racingGameRequest); return ResponseEntity.ok(response); } @GetMapping("/plays") public ResponseEntity> playHistory() { - List history = racingGameService.findHistory(); + List history = racingGameFindService.findHistory(); return ResponseEntity.ok(history); } } diff --git a/src/main/java/racingcar/service/RacingGameService.java b/src/main/java/racingcar/service/RacingGameAddService.java similarity index 78% rename from src/main/java/racingcar/service/RacingGameService.java rename to src/main/java/racingcar/service/RacingGameAddService.java index f686ad2e5..8d595aaf3 100644 --- a/src/main/java/racingcar/service/RacingGameService.java +++ b/src/main/java/racingcar/service/RacingGameAddService.java @@ -14,11 +14,11 @@ import racingcar.dto.RacingGameResponse; @Service -public class RacingGameService { +public class RacingGameAddService { private final CarDao carDao; private final RacingGameDao racingGameDao; - public RacingGameService(CarDao carDao, RacingGameDao racingGameDao) { + public RacingGameAddService(CarDao carDao, RacingGameDao racingGameDao) { this.carDao = carDao; this.racingGameDao = racingGameDao; } @@ -50,14 +50,4 @@ private List toCarEntities(RacingGame game, Long racingGameId) { .map(car -> new CarEntity(car.getName(), car.getPosition(), winners.contains(car), racingGameId)) .collect(Collectors.toList()); } - - public List findHistory() { - List racingGameEntities = racingGameDao.findAllByCreatedTimeAsc(); - - return racingGameEntities.stream() - .map(RacingGameEntity::getId) - .map(carDao::findByRacingGameId) - .map(RacingGameResponse::of) - .collect(Collectors.toList()); - } } diff --git a/src/main/java/racingcar/service/RacingGameFindService.java b/src/main/java/racingcar/service/RacingGameFindService.java new file mode 100644 index 000000000..aeb9f86e0 --- /dev/null +++ b/src/main/java/racingcar/service/RacingGameFindService.java @@ -0,0 +1,30 @@ +package racingcar.service; + +import java.util.List; +import java.util.stream.Collectors; +import org.springframework.stereotype.Service; +import racingcar.dao.CarDao; +import racingcar.dao.RacingGameDao; +import racingcar.dao.entity.RacingGameEntity; +import racingcar.dto.RacingGameResponse; + +@Service +public class RacingGameFindService { + private final CarDao carDao; + private final RacingGameDao racingGameDao; + + public RacingGameFindService(CarDao carDao, RacingGameDao racingGameDao) { + this.carDao = carDao; + this.racingGameDao = racingGameDao; + } + + public List findHistory() { + List racingGameEntities = racingGameDao.findAllByCreatedTimeAsc(); + + return racingGameEntities.stream() + .map(RacingGameEntity::getId) + .map(carDao::findByRacingGameId) + .map(RacingGameResponse::of) + .collect(Collectors.toList()); + } +} diff --git a/src/test/java/racingcar/controller/RacingGameControllerTest.java b/src/test/java/racingcar/controller/RacingGameControllerTest.java index 2c2a4996b..57e75c3f8 100644 --- a/src/test/java/racingcar/controller/RacingGameControllerTest.java +++ b/src/test/java/racingcar/controller/RacingGameControllerTest.java @@ -21,7 +21,8 @@ import racingcar.dto.CarDto; import racingcar.dto.RacingGameRequest; import racingcar.dto.RacingGameResponse; -import racingcar.service.RacingGameService; +import racingcar.service.RacingGameAddService; +import racingcar.service.RacingGameFindService; @WebMvcTest(RacingGameController.class) public class RacingGameControllerTest { @@ -33,7 +34,10 @@ public class RacingGameControllerTest { private ObjectMapper objectMapper; @MockBean - private RacingGameService racingGameService; + private RacingGameAddService racingGameService; + + @MockBean + private RacingGameFindService racingGameFindService; @Test @DisplayName("자동차 경주를 실행하고 결과를 반환한다.") @@ -68,7 +72,7 @@ public void findHistory() throws Exception { ); List history = List.of(firstGameResponse, secondGameResponse); - when(racingGameService.findHistory()).thenReturn(history); + when(racingGameFindService.findHistory()).thenReturn(history); mockMvc.perform(get("/plays")) diff --git a/src/test/java/racingcar/service/RacingGameFindServiceTest.java b/src/test/java/racingcar/service/RacingGameFindServiceTest.java new file mode 100644 index 000000000..fa2b54408 --- /dev/null +++ b/src/test/java/racingcar/service/RacingGameFindServiceTest.java @@ -0,0 +1,48 @@ +package racingcar.service; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertAll; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.BDDMockito.given; + +import java.util.List; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; +import racingcar.dao.CarDao; +import racingcar.dao.RacingGameDao; +import racingcar.dao.entity.CarEntity; +import racingcar.dao.entity.RacingGameEntity; +import racingcar.dto.RacingGameResponse; + +public class RacingGameFindServiceTest { + RacingGameFindService racingGameFindService; + RacingGameDao racingGameDao; + CarDao carDao; + + @BeforeEach + void setUp() { + carDao = Mockito.mock(CarDao.class); + racingGameDao = Mockito.mock(RacingGameDao.class); + racingGameFindService = new RacingGameFindService(carDao, racingGameDao); + } + + @Test + @DisplayName("전체 결과를 조회하여 결과를 반환한다") + void findHistory() { + given(racingGameDao.findAllByCreatedTimeAsc()) + .willReturn(List.of(new RacingGameEntity(10))); + given(carDao.findByRacingGameId(any())) + .willReturn(List.of(new CarEntity("박스터", 7, false, 1L), + new CarEntity("현구막", 10, true, 1L))); + + List history = racingGameFindService.findHistory(); + + assertAll( + () -> assertThat(history).hasSize(1), + () -> assertThat(history.get(0).getRacingCars()).hasSize(2), + () -> assertThat(history.get(0).getWinners()).containsExactly("현구막") + ); + } +} diff --git a/src/test/java/racingcar/service/RacingGameServiceTest.java b/src/test/java/racingcar/service/RacingGameServiceTest.java index 8cfd23c0a..bbe43d9ab 100644 --- a/src/test/java/racingcar/service/RacingGameServiceTest.java +++ b/src/test/java/racingcar/service/RacingGameServiceTest.java @@ -2,8 +2,6 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertAll; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.BDDMockito.given; import java.util.List; import org.junit.jupiter.api.BeforeEach; @@ -12,15 +10,13 @@ import org.mockito.Mockito; import racingcar.dao.CarDao; import racingcar.dao.RacingGameDao; -import racingcar.dao.entity.CarEntity; -import racingcar.dao.entity.RacingGameEntity; import racingcar.dto.RacingGameRequest; import racingcar.dto.RacingGameResponse; class RacingGameServiceTest { - RacingGameService racingGameService; + RacingGameAddService racingGameAddService; RacingGameDao racingGameDao; CarDao carDao; @@ -28,7 +24,7 @@ class RacingGameServiceTest { void setUp() { carDao = Mockito.mock(CarDao.class); racingGameDao = Mockito.mock(RacingGameDao.class); - racingGameService = new RacingGameService(carDao, racingGameDao); + racingGameAddService = new RacingGameAddService(carDao, racingGameDao); } @Test @@ -36,29 +32,11 @@ void setUp() { void playTest() { RacingGameRequest racingGameRequest = new RacingGameRequest(List.of("박스터", "엔초"), 10); - RacingGameResponse play = racingGameService.play(racingGameRequest); + RacingGameResponse play = racingGameAddService.play(racingGameRequest); assertAll( () -> assertThat(play.getWinners()).isNotEmpty(), () -> assertThat(play.getRacingCars()).hasSize(2) ); } - - @Test - @DisplayName("전체 결과를 조회하여 결과를 반환한다") - void findHistory() { - given(racingGameDao.findAllByCreatedTimeAsc()) - .willReturn(List.of(new RacingGameEntity(10))); - given(carDao.findByRacingGameId(any())) - .willReturn(List.of(new CarEntity("박스터", 7, false, 1L), - new CarEntity("현구막", 10, true, 1L))); - - List history = racingGameService.findHistory(); - - assertAll( - () -> assertThat(history).hasSize(1), - () -> assertThat(history.get(0).getRacingCars()).hasSize(2), - () -> assertThat(history.get(0).getWinners()).containsExactly("현구막") - ); - } } From 7fada27f95e213cbdb086e7dde6e3acc42fa3b91 Mon Sep 17 00:00:00 2001 From: drunkenhw Date: Sun, 23 Apr 2023 15:52:03 +0900 Subject: [PATCH 24/31] =?UTF-8?q?refactor:=20rest=20api=20=EC=9D=91?= =?UTF-8?q?=EB=8B=B5=20=ED=98=95=EC=8B=9D=EC=9C=BC=EB=A1=9C=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/RacingGameController.java | 3 ++- .../java/racingcar/dto/RacingGameResponse.java | 16 ++++++++++++---- .../racingcar/service/RacingGameAddService.java | 11 +++-------- .../controller/RacingGameControllerTest.java | 8 ++++---- .../java/racingcar/integration/GamePlayTest.java | 2 +- 5 files changed, 22 insertions(+), 18 deletions(-) diff --git a/src/main/java/racingcar/controller/RacingGameController.java b/src/main/java/racingcar/controller/RacingGameController.java index 65be551c7..0bf0ccdae 100644 --- a/src/main/java/racingcar/controller/RacingGameController.java +++ b/src/main/java/racingcar/controller/RacingGameController.java @@ -1,5 +1,6 @@ package racingcar.controller; +import java.net.URI; import java.util.List; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.GetMapping; @@ -24,7 +25,7 @@ public RacingGameController(RacingGameAddService racingGameService, RacingGameFi @PostMapping("/plays") public ResponseEntity play(@RequestBody RacingGameRequest racingGameRequest) { RacingGameResponse response = racingGameAddService.play(racingGameRequest); - return ResponseEntity.ok(response); + return ResponseEntity.created(URI.create("/plays/" + response.getGameId())).body(response); } @GetMapping("/plays") diff --git a/src/main/java/racingcar/dto/RacingGameResponse.java b/src/main/java/racingcar/dto/RacingGameResponse.java index 5bd68d4b9..3f11af019 100644 --- a/src/main/java/racingcar/dto/RacingGameResponse.java +++ b/src/main/java/racingcar/dto/RacingGameResponse.java @@ -7,10 +7,12 @@ import racingcar.domain.RacingGame; public class RacingGameResponse { + private final Long gameId; private final List winners; private final List racingCars; - public RacingGameResponse(List winners, List racingCars) { + public RacingGameResponse(Long gameId, List winners, List racingCars) { + this.gameId = gameId; this.winners = winners; this.racingCars = racingCars; } @@ -20,20 +22,22 @@ public static RacingGameResponse of(List carEntities) { .filter(CarEntity::isWinner) .map(CarEntity::getName) .collect(Collectors.toList()); + List carDtos = carEntities.stream() .map(carEntity -> new CarDto(carEntity.getName(), carEntity.getPosition())) .collect(Collectors.toList()); - return new RacingGameResponse(winners, carDtos); + return new RacingGameResponse(null, winners, carDtos); } - public static RacingGameResponse of(RacingGame racingGame) { + public static RacingGameResponse of(RacingGame racingGame, Long racingGameId) { List winners = racingGame.findWinners().stream() .map(Car::getName) .collect(Collectors.toList()); + List carDtos = racingGame.getCars().stream() .map(car -> new CarDto(car.getName(), car.getPosition())) .collect(Collectors.toList()); - return new RacingGameResponse(winners, carDtos); + return new RacingGameResponse(racingGameId, winners, carDtos); } public List getWinners() { @@ -43,4 +47,8 @@ public List getWinners() { public List getRacingCars() { return racingCars; } + + public Long getGameId() { + return gameId; + } } diff --git a/src/main/java/racingcar/service/RacingGameAddService.java b/src/main/java/racingcar/service/RacingGameAddService.java index 8d595aaf3..1e85b4506 100644 --- a/src/main/java/racingcar/service/RacingGameAddService.java +++ b/src/main/java/racingcar/service/RacingGameAddService.java @@ -6,7 +6,6 @@ import racingcar.dao.CarDao; import racingcar.dao.RacingGameDao; import racingcar.dao.entity.CarEntity; -import racingcar.dao.entity.RacingGameEntity; import racingcar.domain.Car; import racingcar.domain.Cars; import racingcar.domain.RacingGame; @@ -28,8 +27,9 @@ public RacingGameResponse play(RacingGameRequest racingGameRequest) { RacingGame racingGame = new RacingGame(racingGameRequest.getCount(), cars); racingGame.run(); - save(racingGameRequest, racingGame); - return RacingGameResponse.of(racingGame); + Long racingGameId = racingGameDao.save(new racingcar.dao.entity.RacingGameEntity(racingGameRequest.getCount())); + carDao.saveAll(toCarEntities(racingGame, racingGameId)); + return RacingGameResponse.of(racingGame, racingGameId); } private Cars toCars(RacingGameRequest racingGameRequest) { @@ -39,11 +39,6 @@ private Cars toCars(RacingGameRequest racingGameRequest) { return new Cars(cars); } - private void save(RacingGameRequest racingGameRequest, RacingGame racingGame) { - Long racingGameId = racingGameDao.save(new RacingGameEntity(racingGameRequest.getCount())); - carDao.saveAll(toCarEntities(racingGame, racingGameId)); - } - private List toCarEntities(RacingGame game, Long racingGameId) { List winners = game.findWinners(); return game.getCars().stream() diff --git a/src/test/java/racingcar/controller/RacingGameControllerTest.java b/src/test/java/racingcar/controller/RacingGameControllerTest.java index 57e75c3f8..d08d4c315 100644 --- a/src/test/java/racingcar/controller/RacingGameControllerTest.java +++ b/src/test/java/racingcar/controller/RacingGameControllerTest.java @@ -44,7 +44,7 @@ public class RacingGameControllerTest { public void plays() throws Exception { RacingGameRequest request = new RacingGameRequest(List.of("현구막", "박스터"), 10); RacingGameResponse expectedResponse = new RacingGameResponse( - List.of("현구막"), + 1L, List.of("현구막"), List.of(new CarDto("현구막", 10), new CarDto("박스터", 7)) ); String requestString = objectMapper.writeValueAsString(request); @@ -54,7 +54,7 @@ public void plays() throws Exception { .contentType(MediaType.APPLICATION_JSON) .content(requestString)) - .andExpect(status().isOk()) + .andExpect(status().isCreated()) .andExpect(jsonPath("$.winners[0]", is("현구막"))) .andExpect(jsonPath("$.racingCars", hasSize(2))); } @@ -63,11 +63,11 @@ public void plays() throws Exception { @DisplayName("자동차 경주 경기 이력을 반환한다.") public void findHistory() throws Exception { RacingGameResponse firstGameResponse = new RacingGameResponse( - List.of("현구막"), + 1L, List.of("현구막"), List.of(new CarDto("현구막", 10), new CarDto("박스터", 7)) ); RacingGameResponse secondGameResponse = new RacingGameResponse( - List.of("박스터"), + 2L, List.of("박스터"), List.of(new CarDto("현구막", 6), new CarDto("박스터", 8)) ); List history = List.of(firstGameResponse, secondGameResponse); diff --git a/src/test/java/racingcar/integration/GamePlayTest.java b/src/test/java/racingcar/integration/GamePlayTest.java index 559d832ad..807439276 100644 --- a/src/test/java/racingcar/integration/GamePlayTest.java +++ b/src/test/java/racingcar/integration/GamePlayTest.java @@ -48,7 +48,7 @@ void playGame() { .when().post("/plays") .then().log().all() - .statusCode(HttpStatus.OK.value()) + .statusCode(HttpStatus.CREATED.value()) .body("winners", notNullValue()) .body("racingCars", hasSize(3)); } From 6fda34366eebaafacc703cdb7a9019a6f58fec7a Mon Sep 17 00:00:00 2001 From: drunkenhw Date: Sun, 23 Apr 2023 16:06:58 +0900 Subject: [PATCH 25/31] =?UTF-8?q?feat:=20valid=EB=A5=BC=20=ED=86=B5?= =?UTF-8?q?=ED=95=9C=20=EA=B2=80=EC=A6=9D=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 1 + src/main/java/racingcar/controller/ControllerAdvice.java | 6 ++++++ .../java/racingcar/controller/RacingGameController.java | 3 ++- src/main/java/racingcar/domain/Cars.java | 4 ++-- src/main/java/racingcar/dto/RacingGameRequest.java | 5 +++++ src/test/java/racingcar/domain/CarsTest.java | 6 +++--- 6 files changed, 19 insertions(+), 6 deletions(-) diff --git a/build.gradle b/build.gradle index e9f5a3972..6cc2e9356 100644 --- a/build.gradle +++ b/build.gradle @@ -16,6 +16,7 @@ dependencies { runtimeOnly 'com.h2database:h2' testImplementation 'io.rest-assured:rest-assured:4.4.0' testImplementation 'org.springframework.boot:spring-boot-starter-test' + implementation 'org.springframework.boot:spring-boot-starter-validation' } tasks.named('test') { diff --git a/src/main/java/racingcar/controller/ControllerAdvice.java b/src/main/java/racingcar/controller/ControllerAdvice.java index e18334dcd..459960e6a 100644 --- a/src/main/java/racingcar/controller/ControllerAdvice.java +++ b/src/main/java/racingcar/controller/ControllerAdvice.java @@ -1,6 +1,7 @@ package racingcar.controller; import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.MethodArgumentNotValidException; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.RestControllerAdvice; @@ -21,4 +22,9 @@ public ResponseEntity handleValidationExceptions(RuntimeException e) { public ResponseEntity handleValidationExceptions(IllegalArgumentException e) { return ResponseEntity.badRequest().body(e.getMessage()); } + + @ExceptionHandler + public ResponseEntity processValidationError(MethodArgumentNotValidException e) { + return ResponseEntity.badRequest().body(e.getBindingResult().getFieldError().getDefaultMessage()); + } } diff --git a/src/main/java/racingcar/controller/RacingGameController.java b/src/main/java/racingcar/controller/RacingGameController.java index 0bf0ccdae..6987b7626 100644 --- a/src/main/java/racingcar/controller/RacingGameController.java +++ b/src/main/java/racingcar/controller/RacingGameController.java @@ -2,6 +2,7 @@ import java.net.URI; import java.util.List; +import javax.validation.Valid; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; @@ -23,7 +24,7 @@ public RacingGameController(RacingGameAddService racingGameService, RacingGameFi } @PostMapping("/plays") - public ResponseEntity play(@RequestBody RacingGameRequest racingGameRequest) { + public ResponseEntity play(@RequestBody @Valid RacingGameRequest racingGameRequest) { RacingGameResponse response = racingGameAddService.play(racingGameRequest); return ResponseEntity.created(URI.create("/plays/" + response.getGameId())).body(response); } diff --git a/src/main/java/racingcar/domain/Cars.java b/src/main/java/racingcar/domain/Cars.java index c81430e75..4b10e0e19 100644 --- a/src/main/java/racingcar/domain/Cars.java +++ b/src/main/java/racingcar/domain/Cars.java @@ -16,8 +16,8 @@ public Cars(List cars) { } private void validateCarsSize(List cars) { - if (cars.isEmpty()) { - throw new IllegalArgumentException("최소 하나의 자동차가 있어야 합니다."); + if (cars.size() < 2) { + throw new IllegalArgumentException("최소 2대의 자동차가 있어야 합니다."); } } diff --git a/src/main/java/racingcar/dto/RacingGameRequest.java b/src/main/java/racingcar/dto/RacingGameRequest.java index 929be9678..00c5e18ba 100644 --- a/src/main/java/racingcar/dto/RacingGameRequest.java +++ b/src/main/java/racingcar/dto/RacingGameRequest.java @@ -1,9 +1,14 @@ package racingcar.dto; import java.util.List; +import javax.validation.constraints.Min; +import javax.validation.constraints.Size; public class RacingGameRequest { + + @Size(min = 2, message = "참가자는 최소 2명이여야 합니다") private List names; + @Min(value = 1, message = "최소 1보다 커야됩니다") private int count; public RacingGameRequest(List names, int count) { diff --git a/src/test/java/racingcar/domain/CarsTest.java b/src/test/java/racingcar/domain/CarsTest.java index ff27adbd4..5277fafa3 100644 --- a/src/test/java/racingcar/domain/CarsTest.java +++ b/src/test/java/racingcar/domain/CarsTest.java @@ -58,12 +58,12 @@ void duplicateNameEx() { } @Test - @DisplayName("car의 수가 1보다 작으면 예외를 발생한다") + @DisplayName("car의 수가 2보다 작으면 예외를 발생한다") void carSizeEx() { - List cars = List.of(); + List cars = List.of(new Car("박스터")); assertThatThrownBy(() -> new Cars(cars)) .isInstanceOf(IllegalArgumentException.class) - .hasMessage("최소 하나의 자동차가 있어야 합니다."); + .hasMessage("최소 2대의 자동차가 있어야 합니다."); } } From f7d899e785a45fbc7b96456dbd7e1db715c7f959 Mon Sep 17 00:00:00 2001 From: drunkenhw Date: Sun, 23 Apr 2023 16:22:45 +0900 Subject: [PATCH 26/31] =?UTF-8?q?refactor:=20=EA=B2=B0=EA=B3=BC=EB=A5=BC?= =?UTF-8?q?=20=EC=A1=B0=ED=9A=8C=ED=95=98=EB=8A=94=20=EC=84=9C=EB=B9=84?= =?UTF-8?q?=EC=8A=A4=EC=97=90=20transactional=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/service/RacingGameFindService.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/java/racingcar/service/RacingGameFindService.java b/src/main/java/racingcar/service/RacingGameFindService.java index aeb9f86e0..3a5ba586c 100644 --- a/src/main/java/racingcar/service/RacingGameFindService.java +++ b/src/main/java/racingcar/service/RacingGameFindService.java @@ -3,12 +3,14 @@ import java.util.List; import java.util.stream.Collectors; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; import racingcar.dao.CarDao; import racingcar.dao.RacingGameDao; import racingcar.dao.entity.RacingGameEntity; import racingcar.dto.RacingGameResponse; @Service +@Transactional(readOnly = true) public class RacingGameFindService { private final CarDao carDao; private final RacingGameDao racingGameDao; From 3b99dd9e5f0044672852023ca7deedc3e6989842 Mon Sep 17 00:00:00 2001 From: drunkenhw Date: Sun, 23 Apr 2023 16:25:38 +0900 Subject: [PATCH 27/31] =?UTF-8?q?refactor:=20=EC=98=88=EC=99=B8=20?= =?UTF-8?q?=EC=B2=98=EB=A6=AC=20=EB=AC=B8=EA=B5=AC=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/racingcar/controller/ControllerAdvice.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/racingcar/controller/ControllerAdvice.java b/src/main/java/racingcar/controller/ControllerAdvice.java index 459960e6a..ca5b8bf7a 100644 --- a/src/main/java/racingcar/controller/ControllerAdvice.java +++ b/src/main/java/racingcar/controller/ControllerAdvice.java @@ -10,12 +10,12 @@ public class ControllerAdvice { @ExceptionHandler public ResponseEntity handleValidationExceptions(Exception e) { - return ResponseEntity.internalServerError().body(e.getMessage()); + return ResponseEntity.internalServerError().body("예상치 못한 오류가 발생했습니다"); } @ExceptionHandler public ResponseEntity handleValidationExceptions(RuntimeException e) { - return ResponseEntity.internalServerError().body(e.getMessage()); + return ResponseEntity.internalServerError().body("예상치 못한 오류가 발생했습니다"); } @ExceptionHandler @@ -24,7 +24,7 @@ public ResponseEntity handleValidationExceptions(IllegalArgumentExceptio } @ExceptionHandler - public ResponseEntity processValidationError(MethodArgumentNotValidException e) { + public ResponseEntity handleValidationExceptions(MethodArgumentNotValidException e) { return ResponseEntity.badRequest().body(e.getBindingResult().getFieldError().getDefaultMessage()); } } From 9cf0152b4c3384e90fbb1bb96c81cc1e4c1433bc Mon Sep 17 00:00:00 2001 From: drunkenhw Date: Sun, 23 Apr 2023 19:50:29 +0900 Subject: [PATCH 28/31] =?UTF-8?q?refactor:=20trasactional=20=EC=96=B4?= =?UTF-8?q?=EB=85=B8=ED=85=8C=EC=9D=B4=EC=85=98=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/service/RacingGameAddService.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/java/racingcar/service/RacingGameAddService.java b/src/main/java/racingcar/service/RacingGameAddService.java index 1e85b4506..5180765a2 100644 --- a/src/main/java/racingcar/service/RacingGameAddService.java +++ b/src/main/java/racingcar/service/RacingGameAddService.java @@ -3,6 +3,7 @@ import java.util.List; import java.util.stream.Collectors; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; import racingcar.dao.CarDao; import racingcar.dao.RacingGameDao; import racingcar.dao.entity.CarEntity; @@ -13,6 +14,7 @@ import racingcar.dto.RacingGameResponse; @Service +@Transactional public class RacingGameAddService { private final CarDao carDao; private final RacingGameDao racingGameDao; From dcf1b6dc432b09532b6734eb09df15af63651cf8 Mon Sep 17 00:00:00 2001 From: drunkenhw Date: Sun, 23 Apr 2023 22:41:03 +0900 Subject: [PATCH 29/31] =?UTF-8?q?refactor:=20racing=20game=EC=9D=84=20?= =?UTF-8?q?=EC=88=98=ED=96=89=ED=95=98=EB=8A=94=20=ED=81=B4=EB=9E=98?= =?UTF-8?q?=EC=8A=A4=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../RacingCarConsoleApplication.java | 13 ++++----- ....java => RacingGameConsoleController.java} | 14 ++++------ ...ller.java => RacingGameWebController.java} | 18 ++++++------ .../java/racingcar/dao/CarInMemoryDao.java | 28 ------------------- .../racingcar/dao/RacingGameInMemoryDao.java | 27 ------------------ .../java/racingcar/domain/RacingGame.java | 9 +++++- .../racingcar/dto/RacingGameResponse.java | 11 ++++++++ .../service/RacingGameAddService.java | 18 ++---------- .../racingcar/service/RacingGameManager.java | 19 +++++++++++++ .../service/RacingGamePlayConsoleService.java | 19 +++++++++++++ .../service/RacingGamePlayService.java | 9 ++++++ .../service/RacingGamePlayWebService.java | 24 ++++++++++++++++ .../controller/RacingGameControllerTest.java | 8 +++--- ...est.java => RacingGameAddServiceTest.java} | 8 +++--- 14 files changed, 122 insertions(+), 103 deletions(-) rename src/main/java/racingcar/controller/{RacingConsoleController.java => RacingGameConsoleController.java} (50%) rename src/main/java/racingcar/controller/{RacingGameController.java => RacingGameWebController.java} (61%) delete mode 100644 src/main/java/racingcar/dao/CarInMemoryDao.java delete mode 100644 src/main/java/racingcar/dao/RacingGameInMemoryDao.java create mode 100644 src/main/java/racingcar/service/RacingGameManager.java create mode 100644 src/main/java/racingcar/service/RacingGamePlayConsoleService.java create mode 100644 src/main/java/racingcar/service/RacingGamePlayService.java create mode 100644 src/main/java/racingcar/service/RacingGamePlayWebService.java rename src/test/java/racingcar/service/{RacingGameServiceTest.java => RacingGameAddServiceTest.java} (80%) diff --git a/src/main/java/racingcar/RacingCarConsoleApplication.java b/src/main/java/racingcar/RacingCarConsoleApplication.java index 5e69a14f5..35878d251 100644 --- a/src/main/java/racingcar/RacingCarConsoleApplication.java +++ b/src/main/java/racingcar/RacingCarConsoleApplication.java @@ -1,15 +1,14 @@ package racingcar; -import racingcar.controller.RacingConsoleController; -import racingcar.dao.CarInMemoryDao; -import racingcar.dao.RacingGameInMemoryDao; -import racingcar.service.RacingGameAddService; +import racingcar.controller.RacingGameConsoleController; +import racingcar.service.RacingGameManager; +import racingcar.service.RacingGamePlayConsoleService; +import racingcar.service.RacingGamePlayService; public class RacingCarConsoleApplication { public static void main(String[] args) { - RacingGameAddService racingGameService = new RacingGameAddService(new CarInMemoryDao(), - new RacingGameInMemoryDao()); - RacingConsoleController racingConsoleController = new RacingConsoleController(racingGameService); + RacingGamePlayService racingPlayService = new RacingGamePlayConsoleService(new RacingGameManager()); + RacingGameConsoleController racingConsoleController = new RacingGameConsoleController(racingPlayService); racingConsoleController.run(); } } diff --git a/src/main/java/racingcar/controller/RacingConsoleController.java b/src/main/java/racingcar/controller/RacingGameConsoleController.java similarity index 50% rename from src/main/java/racingcar/controller/RacingConsoleController.java rename to src/main/java/racingcar/controller/RacingGameConsoleController.java index 68aac896c..28b62a7f3 100644 --- a/src/main/java/racingcar/controller/RacingConsoleController.java +++ b/src/main/java/racingcar/controller/RacingGameConsoleController.java @@ -3,24 +3,22 @@ import java.util.List; import racingcar.dto.RacingGameRequest; import racingcar.dto.RacingGameResponse; -import racingcar.service.RacingGameAddService; +import racingcar.service.RacingGamePlayService; import racingcar.view.InputView; import racingcar.view.OutputView; -public class RacingConsoleController { +public class RacingGameConsoleController { + private final RacingGamePlayService racingPlayService; - private final RacingGameAddService racingGameAddService; - - public RacingConsoleController(RacingGameAddService racingGameAddService) { - this.racingGameAddService = racingGameAddService; + public RacingGameConsoleController(RacingGamePlayService racingPlayService) { + this.racingPlayService = racingPlayService; } public void run() { List names = InputView.inputNames(); int count = InputView.inputTryCount(); - RacingGameRequest racingGameRequest = new RacingGameRequest(names, count); - RacingGameResponse racingGameResponse = racingGameAddService.play(racingGameRequest); + RacingGameResponse racingGameResponse = racingPlayService.play(new RacingGameRequest(names, count)); OutputView.printRacing(racingGameResponse.getRacingCars()); OutputView.printWinners(racingGameResponse.getWinners()); diff --git a/src/main/java/racingcar/controller/RacingGameController.java b/src/main/java/racingcar/controller/RacingGameWebController.java similarity index 61% rename from src/main/java/racingcar/controller/RacingGameController.java rename to src/main/java/racingcar/controller/RacingGameWebController.java index 6987b7626..9b6e51178 100644 --- a/src/main/java/racingcar/controller/RacingGameController.java +++ b/src/main/java/racingcar/controller/RacingGameWebController.java @@ -2,7 +2,6 @@ import java.net.URI; import java.util.List; -import javax.validation.Valid; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; @@ -10,23 +9,24 @@ import org.springframework.web.bind.annotation.RestController; import racingcar.dto.RacingGameRequest; import racingcar.dto.RacingGameResponse; -import racingcar.service.RacingGameAddService; import racingcar.service.RacingGameFindService; +import racingcar.service.RacingGamePlayService; @RestController -public class RacingGameController { - private final RacingGameAddService racingGameAddService; +public class RacingGameWebController { + private final RacingGamePlayService racingPlayService; private final RacingGameFindService racingGameFindService; - public RacingGameController(RacingGameAddService racingGameService, RacingGameFindService racingGameFindService) { - this.racingGameAddService = racingGameService; + public RacingGameWebController(RacingGamePlayService racingPlayService, + RacingGameFindService racingGameFindService) { + this.racingPlayService = racingPlayService; this.racingGameFindService = racingGameFindService; } @PostMapping("/plays") - public ResponseEntity play(@RequestBody @Valid RacingGameRequest racingGameRequest) { - RacingGameResponse response = racingGameAddService.play(racingGameRequest); - return ResponseEntity.created(URI.create("/plays/" + response.getGameId())).body(response); + public ResponseEntity play(@RequestBody RacingGameRequest racingGameRequest) { + RacingGameResponse racingGameResponse = racingPlayService.play(racingGameRequest); + return ResponseEntity.created(URI.create("/plays/" + racingGameResponse.getGameId())).body(racingGameResponse); } @GetMapping("/plays") diff --git a/src/main/java/racingcar/dao/CarInMemoryDao.java b/src/main/java/racingcar/dao/CarInMemoryDao.java deleted file mode 100644 index 3eee6e79a..000000000 --- a/src/main/java/racingcar/dao/CarInMemoryDao.java +++ /dev/null @@ -1,28 +0,0 @@ -package racingcar.dao; - -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.stream.Collectors; -import racingcar.dao.entity.CarEntity; - -public class CarInMemoryDao implements CarDao { - private final Map database = new HashMap<>(); - private Long id = 1L; - - @Override - public void saveAll(List racingCars) { - for (CarEntity racingCar : racingCars) { - database.put(id, racingCar); - id++; - } - } - - @Override - public List findByRacingGameId(Long id) { - return database.values().stream() - .filter(carEntity -> Objects.equals(carEntity.getRacingGameId(), id)) - .collect(Collectors.toList()); - } -} diff --git a/src/main/java/racingcar/dao/RacingGameInMemoryDao.java b/src/main/java/racingcar/dao/RacingGameInMemoryDao.java deleted file mode 100644 index 510b2dbbf..000000000 --- a/src/main/java/racingcar/dao/RacingGameInMemoryDao.java +++ /dev/null @@ -1,27 +0,0 @@ -package racingcar.dao; - -import java.util.Comparator; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.stream.Collectors; -import racingcar.dao.entity.RacingGameEntity; - -public class RacingGameInMemoryDao implements RacingGameDao { - private final Map database = new HashMap<>(); - private Long id = 1L; - - @Override - public Long save(RacingGameEntity racingGameEntity) { - database.put(id, racingGameEntity); - id++; - return id - 1; - } - - @Override - public List findAllByCreatedTimeAsc() { - return database.values() - .stream().sorted(Comparator.comparing(RacingGameEntity::getCreatedAt)) - .collect(Collectors.toList()); - } -} diff --git a/src/main/java/racingcar/domain/RacingGame.java b/src/main/java/racingcar/domain/RacingGame.java index 9d8b1d040..6f0f4e264 100644 --- a/src/main/java/racingcar/domain/RacingGame.java +++ b/src/main/java/racingcar/domain/RacingGame.java @@ -1,6 +1,7 @@ package racingcar.domain; import java.util.List; +import java.util.stream.Collectors; public class RacingGame { public static final int MAX_TRY_COUNT_BOUND = 100; @@ -20,6 +21,13 @@ public RacingGame(int tryCount, Cars cars) { this(new RandomNumberGenerator(), tryCount, cars); } + public static RacingGame of(List names, int tryCount) { + List cars = names.stream() + .map(Car::new) + .collect(Collectors.toList()); + return new RacingGame(tryCount, new Cars(cars)); + } + private void validateTryCount(int tryCount) { if (tryCount > MAX_TRY_COUNT_BOUND) { throw new IllegalArgumentException("시도 횟수는 100회 이하만 가능합니다 현재 : " + tryCount + "회"); @@ -48,5 +56,4 @@ public List findWinners() { public List getCars() { return cars.getCars(); } - } diff --git a/src/main/java/racingcar/dto/RacingGameResponse.java b/src/main/java/racingcar/dto/RacingGameResponse.java index 3f11af019..e649cf50e 100644 --- a/src/main/java/racingcar/dto/RacingGameResponse.java +++ b/src/main/java/racingcar/dto/RacingGameResponse.java @@ -40,6 +40,17 @@ public static RacingGameResponse of(RacingGame racingGame, Long racingGameId) { return new RacingGameResponse(racingGameId, winners, carDtos); } + public static RacingGameResponse from(RacingGame racingGame) { + List winners = racingGame.findWinners().stream() + .map(Car::getName) + .collect(Collectors.toList()); + + List carDtos = racingGame.getCars().stream() + .map(car -> new CarDto(car.getName(), car.getPosition())) + .collect(Collectors.toList()); + return new RacingGameResponse(null, winners, carDtos); + } + public List getWinners() { return winners; } diff --git a/src/main/java/racingcar/service/RacingGameAddService.java b/src/main/java/racingcar/service/RacingGameAddService.java index 5180765a2..ae904c49c 100644 --- a/src/main/java/racingcar/service/RacingGameAddService.java +++ b/src/main/java/racingcar/service/RacingGameAddService.java @@ -7,10 +7,9 @@ import racingcar.dao.CarDao; import racingcar.dao.RacingGameDao; import racingcar.dao.entity.CarEntity; +import racingcar.dao.entity.RacingGameEntity; import racingcar.domain.Car; -import racingcar.domain.Cars; import racingcar.domain.RacingGame; -import racingcar.dto.RacingGameRequest; import racingcar.dto.RacingGameResponse; @Service @@ -24,23 +23,12 @@ public RacingGameAddService(CarDao carDao, RacingGameDao racingGameDao) { this.racingGameDao = racingGameDao; } - public RacingGameResponse play(RacingGameRequest racingGameRequest) { - Cars cars = toCars(racingGameRequest); - RacingGame racingGame = new RacingGame(racingGameRequest.getCount(), cars); - racingGame.run(); - - Long racingGameId = racingGameDao.save(new racingcar.dao.entity.RacingGameEntity(racingGameRequest.getCount())); + public RacingGameResponse addGame(RacingGame racingGame, int tryCount) { + Long racingGameId = racingGameDao.save(new RacingGameEntity(tryCount)); carDao.saveAll(toCarEntities(racingGame, racingGameId)); return RacingGameResponse.of(racingGame, racingGameId); } - private Cars toCars(RacingGameRequest racingGameRequest) { - List cars = racingGameRequest.getNames().stream() - .map(Car::new) - .collect(Collectors.toList()); - return new Cars(cars); - } - private List toCarEntities(RacingGame game, Long racingGameId) { List winners = game.findWinners(); return game.getCars().stream() diff --git a/src/main/java/racingcar/service/RacingGameManager.java b/src/main/java/racingcar/service/RacingGameManager.java new file mode 100644 index 000000000..11aa41628 --- /dev/null +++ b/src/main/java/racingcar/service/RacingGameManager.java @@ -0,0 +1,19 @@ +package racingcar.service; + +import java.util.List; +import org.springframework.stereotype.Service; +import racingcar.domain.RacingGame; + +@Service +public class RacingGameManager { + + public RacingGame play(List names, int tryCount) { + RacingGame racingGame = createGame(names, tryCount); + racingGame.run(); + return racingGame; + } + + private RacingGame createGame(List names, int tryCount) { + return RacingGame.of(names, tryCount); + } +} diff --git a/src/main/java/racingcar/service/RacingGamePlayConsoleService.java b/src/main/java/racingcar/service/RacingGamePlayConsoleService.java new file mode 100644 index 000000000..f25a6687b --- /dev/null +++ b/src/main/java/racingcar/service/RacingGamePlayConsoleService.java @@ -0,0 +1,19 @@ +package racingcar.service; + +import racingcar.domain.RacingGame; +import racingcar.dto.RacingGameRequest; +import racingcar.dto.RacingGameResponse; + +public class RacingGamePlayConsoleService implements RacingGamePlayService { + private final RacingGameManager racingGameManager; + + public RacingGamePlayConsoleService(RacingGameManager racingGameManager) { + this.racingGameManager = racingGameManager; + } + + @Override + public RacingGameResponse play(RacingGameRequest racingGameRequest) { + RacingGame racingGame = racingGameManager.play(racingGameRequest.getNames(), racingGameRequest.getCount()); + return RacingGameResponse.from(racingGame); + } +} diff --git a/src/main/java/racingcar/service/RacingGamePlayService.java b/src/main/java/racingcar/service/RacingGamePlayService.java new file mode 100644 index 000000000..324f31e25 --- /dev/null +++ b/src/main/java/racingcar/service/RacingGamePlayService.java @@ -0,0 +1,9 @@ +package racingcar.service; + +import racingcar.dto.RacingGameRequest; +import racingcar.dto.RacingGameResponse; + +public interface RacingGamePlayService { + + RacingGameResponse play(RacingGameRequest racingGameRequest); +} diff --git a/src/main/java/racingcar/service/RacingGamePlayWebService.java b/src/main/java/racingcar/service/RacingGamePlayWebService.java new file mode 100644 index 000000000..c48f926f5 --- /dev/null +++ b/src/main/java/racingcar/service/RacingGamePlayWebService.java @@ -0,0 +1,24 @@ +package racingcar.service; + +import org.springframework.stereotype.Service; +import racingcar.domain.RacingGame; +import racingcar.dto.RacingGameRequest; +import racingcar.dto.RacingGameResponse; + +@Service +public class RacingGamePlayWebService implements RacingGamePlayService { + private final RacingGameAddService racingGameAddService; + private final RacingGameManager racingGameManager; + + public RacingGamePlayWebService(RacingGameAddService racingGameAddService, RacingGameManager racingGameManager) { + this.racingGameAddService = racingGameAddService; + this.racingGameManager = racingGameManager; + } + + @Override + public RacingGameResponse play(RacingGameRequest racingGameRequest) { + RacingGame racingGame = racingGameManager.play(racingGameRequest.getNames(), racingGameRequest.getCount()); + racingGame.run(); + return racingGameAddService.addGame(racingGame, racingGameRequest.getCount()); + } +} diff --git a/src/test/java/racingcar/controller/RacingGameControllerTest.java b/src/test/java/racingcar/controller/RacingGameControllerTest.java index d08d4c315..c16bae430 100644 --- a/src/test/java/racingcar/controller/RacingGameControllerTest.java +++ b/src/test/java/racingcar/controller/RacingGameControllerTest.java @@ -21,10 +21,10 @@ import racingcar.dto.CarDto; import racingcar.dto.RacingGameRequest; import racingcar.dto.RacingGameResponse; -import racingcar.service.RacingGameAddService; import racingcar.service.RacingGameFindService; +import racingcar.service.RacingGamePlayService; -@WebMvcTest(RacingGameController.class) +@WebMvcTest(RacingGameWebController.class) public class RacingGameControllerTest { @Autowired @@ -34,7 +34,7 @@ public class RacingGameControllerTest { private ObjectMapper objectMapper; @MockBean - private RacingGameAddService racingGameService; + private RacingGamePlayService racingPlayService; @MockBean private RacingGameFindService racingGameFindService; @@ -48,7 +48,7 @@ public void plays() throws Exception { List.of(new CarDto("현구막", 10), new CarDto("박스터", 7)) ); String requestString = objectMapper.writeValueAsString(request); - when(racingGameService.play(any())).thenReturn(expectedResponse); + when(racingPlayService.play(any())).thenReturn(expectedResponse); mockMvc.perform(post("/plays") .contentType(MediaType.APPLICATION_JSON) diff --git a/src/test/java/racingcar/service/RacingGameServiceTest.java b/src/test/java/racingcar/service/RacingGameAddServiceTest.java similarity index 80% rename from src/test/java/racingcar/service/RacingGameServiceTest.java rename to src/test/java/racingcar/service/RacingGameAddServiceTest.java index bbe43d9ab..975a86dc4 100644 --- a/src/test/java/racingcar/service/RacingGameServiceTest.java +++ b/src/test/java/racingcar/service/RacingGameAddServiceTest.java @@ -10,11 +10,11 @@ import org.mockito.Mockito; import racingcar.dao.CarDao; import racingcar.dao.RacingGameDao; -import racingcar.dto.RacingGameRequest; +import racingcar.domain.RacingGame; import racingcar.dto.RacingGameResponse; -class RacingGameServiceTest { +class RacingGameAddServiceTest { RacingGameAddService racingGameAddService; RacingGameDao racingGameDao; @@ -30,9 +30,9 @@ void setUp() { @Test @DisplayName("이름과 실행 횟수를 받아 게임의 결과를 반환한다") void playTest() { - RacingGameRequest racingGameRequest = new RacingGameRequest(List.of("박스터", "엔초"), 10); + RacingGame racingGame = RacingGame.of(List.of("박스터", "엔초"), 10); - RacingGameResponse play = racingGameAddService.play(racingGameRequest); + RacingGameResponse play = racingGameAddService.addGame(racingGame, 10); assertAll( () -> assertThat(play.getWinners()).isNotEmpty(), From ea55c02ae842f2919617ef4727f9cdec7163fed8 Mon Sep 17 00:00:00 2001 From: drunkenhw Date: Wed, 26 Apr 2023 14:00:49 +0900 Subject: [PATCH 30/31] =?UTF-8?q?refactor:=20racing=20game=20console=20?= =?UTF-8?q?=EC=84=9C=EB=B9=84=EC=8A=A4=20=ED=81=B4=EB=9E=98=EC=8A=A4=20?= =?UTF-8?q?=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../RacingCarConsoleApplication.java | 6 +---- .../RacingGameConsoleController.java | 21 ++++++++-------- .../racingcar/service/RacingGameManager.java | 19 --------------- .../service/RacingGamePlayConsoleService.java | 19 --------------- .../service/RacingGamePlayService.java | 23 ++++++++++++++++-- .../service/RacingGamePlayWebService.java | 24 ------------------- src/main/java/racingcar/view/OutputView.java | 17 +++++++------ 7 files changed, 43 insertions(+), 86 deletions(-) delete mode 100644 src/main/java/racingcar/service/RacingGameManager.java delete mode 100644 src/main/java/racingcar/service/RacingGamePlayConsoleService.java delete mode 100644 src/main/java/racingcar/service/RacingGamePlayWebService.java diff --git a/src/main/java/racingcar/RacingCarConsoleApplication.java b/src/main/java/racingcar/RacingCarConsoleApplication.java index 35878d251..d9b7c36c6 100644 --- a/src/main/java/racingcar/RacingCarConsoleApplication.java +++ b/src/main/java/racingcar/RacingCarConsoleApplication.java @@ -1,14 +1,10 @@ package racingcar; import racingcar.controller.RacingGameConsoleController; -import racingcar.service.RacingGameManager; -import racingcar.service.RacingGamePlayConsoleService; -import racingcar.service.RacingGamePlayService; public class RacingCarConsoleApplication { public static void main(String[] args) { - RacingGamePlayService racingPlayService = new RacingGamePlayConsoleService(new RacingGameManager()); - RacingGameConsoleController racingConsoleController = new RacingGameConsoleController(racingPlayService); + RacingGameConsoleController racingConsoleController = new RacingGameConsoleController(); racingConsoleController.run(); } } diff --git a/src/main/java/racingcar/controller/RacingGameConsoleController.java b/src/main/java/racingcar/controller/RacingGameConsoleController.java index 28b62a7f3..acd211875 100644 --- a/src/main/java/racingcar/controller/RacingGameConsoleController.java +++ b/src/main/java/racingcar/controller/RacingGameConsoleController.java @@ -1,26 +1,27 @@ package racingcar.controller; import java.util.List; -import racingcar.dto.RacingGameRequest; -import racingcar.dto.RacingGameResponse; -import racingcar.service.RacingGamePlayService; +import java.util.stream.Collectors; +import racingcar.domain.Car; +import racingcar.domain.Cars; +import racingcar.domain.RacingGame; import racingcar.view.InputView; import racingcar.view.OutputView; public class RacingGameConsoleController { - private final RacingGamePlayService racingPlayService; - public RacingGameConsoleController(RacingGamePlayService racingPlayService) { - this.racingPlayService = racingPlayService; - } public void run() { List names = InputView.inputNames(); int count = InputView.inputTryCount(); - RacingGameResponse racingGameResponse = racingPlayService.play(new RacingGameRequest(names, count)); + List cars = names.stream() + .map(Car::new) + .collect(Collectors.toList()); + RacingGame racingGame = new RacingGame(count, new Cars(cars)); + racingGame.run(); - OutputView.printRacing(racingGameResponse.getRacingCars()); - OutputView.printWinners(racingGameResponse.getWinners()); + OutputView.printRacing(racingGame.getCars()); + OutputView.printWinners(racingGame.findWinners()); } } diff --git a/src/main/java/racingcar/service/RacingGameManager.java b/src/main/java/racingcar/service/RacingGameManager.java deleted file mode 100644 index 11aa41628..000000000 --- a/src/main/java/racingcar/service/RacingGameManager.java +++ /dev/null @@ -1,19 +0,0 @@ -package racingcar.service; - -import java.util.List; -import org.springframework.stereotype.Service; -import racingcar.domain.RacingGame; - -@Service -public class RacingGameManager { - - public RacingGame play(List names, int tryCount) { - RacingGame racingGame = createGame(names, tryCount); - racingGame.run(); - return racingGame; - } - - private RacingGame createGame(List names, int tryCount) { - return RacingGame.of(names, tryCount); - } -} diff --git a/src/main/java/racingcar/service/RacingGamePlayConsoleService.java b/src/main/java/racingcar/service/RacingGamePlayConsoleService.java deleted file mode 100644 index f25a6687b..000000000 --- a/src/main/java/racingcar/service/RacingGamePlayConsoleService.java +++ /dev/null @@ -1,19 +0,0 @@ -package racingcar.service; - -import racingcar.domain.RacingGame; -import racingcar.dto.RacingGameRequest; -import racingcar.dto.RacingGameResponse; - -public class RacingGamePlayConsoleService implements RacingGamePlayService { - private final RacingGameManager racingGameManager; - - public RacingGamePlayConsoleService(RacingGameManager racingGameManager) { - this.racingGameManager = racingGameManager; - } - - @Override - public RacingGameResponse play(RacingGameRequest racingGameRequest) { - RacingGame racingGame = racingGameManager.play(racingGameRequest.getNames(), racingGameRequest.getCount()); - return RacingGameResponse.from(racingGame); - } -} diff --git a/src/main/java/racingcar/service/RacingGamePlayService.java b/src/main/java/racingcar/service/RacingGamePlayService.java index 324f31e25..1385cd350 100644 --- a/src/main/java/racingcar/service/RacingGamePlayService.java +++ b/src/main/java/racingcar/service/RacingGamePlayService.java @@ -1,9 +1,28 @@ package racingcar.service; +import java.util.List; +import java.util.stream.Collectors; +import org.springframework.stereotype.Service; +import racingcar.domain.Car; +import racingcar.domain.Cars; +import racingcar.domain.RacingGame; import racingcar.dto.RacingGameRequest; import racingcar.dto.RacingGameResponse; -public interface RacingGamePlayService { +@Service +public class RacingGamePlayService { + private final RacingGameAddService racingGameAddService; - RacingGameResponse play(RacingGameRequest racingGameRequest); + public RacingGamePlayService(RacingGameAddService racingGameAddService) { + this.racingGameAddService = racingGameAddService; + } + + public RacingGameResponse play(RacingGameRequest racingGameRequest) { + List cars = racingGameRequest.getNames().stream() + .map(Car::new) + .collect(Collectors.toList()); + RacingGame racingGame = new RacingGame(racingGameRequest.getCount(), new Cars(cars)); + racingGame.run(); + return racingGameAddService.addGame(racingGame, racingGameRequest.getCount()); + } } diff --git a/src/main/java/racingcar/service/RacingGamePlayWebService.java b/src/main/java/racingcar/service/RacingGamePlayWebService.java deleted file mode 100644 index c48f926f5..000000000 --- a/src/main/java/racingcar/service/RacingGamePlayWebService.java +++ /dev/null @@ -1,24 +0,0 @@ -package racingcar.service; - -import org.springframework.stereotype.Service; -import racingcar.domain.RacingGame; -import racingcar.dto.RacingGameRequest; -import racingcar.dto.RacingGameResponse; - -@Service -public class RacingGamePlayWebService implements RacingGamePlayService { - private final RacingGameAddService racingGameAddService; - private final RacingGameManager racingGameManager; - - public RacingGamePlayWebService(RacingGameAddService racingGameAddService, RacingGameManager racingGameManager) { - this.racingGameAddService = racingGameAddService; - this.racingGameManager = racingGameManager; - } - - @Override - public RacingGameResponse play(RacingGameRequest racingGameRequest) { - RacingGame racingGame = racingGameManager.play(racingGameRequest.getNames(), racingGameRequest.getCount()); - racingGame.run(); - return racingGameAddService.addGame(racingGame, racingGameRequest.getCount()); - } -} diff --git a/src/main/java/racingcar/view/OutputView.java b/src/main/java/racingcar/view/OutputView.java index 8a8a7adc9..61af1b417 100644 --- a/src/main/java/racingcar/view/OutputView.java +++ b/src/main/java/racingcar/view/OutputView.java @@ -1,7 +1,8 @@ package racingcar.view; import java.util.List; -import racingcar.dto.CarDto; +import java.util.stream.Collectors; +import racingcar.domain.Car; public class OutputView { public static final String GAME_RESULT_FORMAT = "%s : %s\n"; @@ -9,17 +10,19 @@ public class OutputView { private static final String POSITION_VIEW = "-"; public static final String WINNER_DELIMITER = ", "; - public static void printRacing(List carDtos) { - for (CarDto carDto : carDtos) { - int position = carDto.getPosition(); + public static void printRacing(List cars) { + for (Car car : cars) { + int position = car.getPosition(); String positionView = POSITION_VIEW.repeat(position); - System.out.printf(GAME_RESULT_FORMAT, carDto.getName(), positionView); + System.out.printf(GAME_RESULT_FORMAT, car.getName(), positionView); } System.out.println(); } - public static void printWinners(List names) { - String winners = String.join(WINNER_DELIMITER, names); + public static void printWinners(List cars) { + String winners = cars.stream() + .map(Car::getName) + .collect(Collectors.joining(WINNER_DELIMITER)); System.out.printf(WINNER_FORMAT, winners); } } From d46221cf38cb07c6757b71d017717d635030a01f Mon Sep 17 00:00:00 2001 From: drunkenhw Date: Wed, 26 Apr 2023 14:31:51 +0900 Subject: [PATCH 31/31] =?UTF-8?q?refactor:=20racing=20game=20play=20?= =?UTF-8?q?=EC=84=9C=EB=B9=84=EC=8A=A4=20=ED=81=B4=EB=9E=98=EC=8A=A4=20?= =?UTF-8?q?=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/RacingGameWebController.java | 10 +++---- .../service/RacingGameAddService.java | 14 ++++++---- .../service/RacingGamePlayService.java | 28 ------------------- .../controller/RacingGameControllerTest.java | 6 ++-- .../service/RacingGameAddServiceTest.java | 6 ++-- 5 files changed, 20 insertions(+), 44 deletions(-) delete mode 100644 src/main/java/racingcar/service/RacingGamePlayService.java diff --git a/src/main/java/racingcar/controller/RacingGameWebController.java b/src/main/java/racingcar/controller/RacingGameWebController.java index 9b6e51178..2df22ab6f 100644 --- a/src/main/java/racingcar/controller/RacingGameWebController.java +++ b/src/main/java/racingcar/controller/RacingGameWebController.java @@ -9,23 +9,23 @@ import org.springframework.web.bind.annotation.RestController; import racingcar.dto.RacingGameRequest; import racingcar.dto.RacingGameResponse; +import racingcar.service.RacingGameAddService; import racingcar.service.RacingGameFindService; -import racingcar.service.RacingGamePlayService; @RestController public class RacingGameWebController { - private final RacingGamePlayService racingPlayService; + private final RacingGameAddService racingGameAddService; private final RacingGameFindService racingGameFindService; - public RacingGameWebController(RacingGamePlayService racingPlayService, + public RacingGameWebController(RacingGameAddService racingGameAddService, RacingGameFindService racingGameFindService) { - this.racingPlayService = racingPlayService; + this.racingGameAddService = racingGameAddService; this.racingGameFindService = racingGameFindService; } @PostMapping("/plays") public ResponseEntity play(@RequestBody RacingGameRequest racingGameRequest) { - RacingGameResponse racingGameResponse = racingPlayService.play(racingGameRequest); + RacingGameResponse racingGameResponse = racingGameAddService.addGame(racingGameRequest); return ResponseEntity.created(URI.create("/plays/" + racingGameResponse.getGameId())).body(racingGameResponse); } diff --git a/src/main/java/racingcar/service/RacingGameAddService.java b/src/main/java/racingcar/service/RacingGameAddService.java index ae904c49c..74adba8be 100644 --- a/src/main/java/racingcar/service/RacingGameAddService.java +++ b/src/main/java/racingcar/service/RacingGameAddService.java @@ -7,9 +7,10 @@ import racingcar.dao.CarDao; import racingcar.dao.RacingGameDao; import racingcar.dao.entity.CarEntity; -import racingcar.dao.entity.RacingGameEntity; import racingcar.domain.Car; +import racingcar.domain.Cars; import racingcar.domain.RacingGame; +import racingcar.dto.RacingGameRequest; import racingcar.dto.RacingGameResponse; @Service @@ -23,10 +24,13 @@ public RacingGameAddService(CarDao carDao, RacingGameDao racingGameDao) { this.racingGameDao = racingGameDao; } - public RacingGameResponse addGame(RacingGame racingGame, int tryCount) { - Long racingGameId = racingGameDao.save(new RacingGameEntity(tryCount)); - carDao.saveAll(toCarEntities(racingGame, racingGameId)); - return RacingGameResponse.of(racingGame, racingGameId); + public RacingGameResponse addGame(RacingGameRequest racingGameRequest) { + List cars = racingGameRequest.getNames().stream() + .map(Car::new) + .collect(Collectors.toList()); + RacingGame racingGame = new RacingGame(racingGameRequest.getCount(), new Cars(cars)); + racingGame.run(); + return RacingGameResponse.from(racingGame); } private List toCarEntities(RacingGame game, Long racingGameId) { diff --git a/src/main/java/racingcar/service/RacingGamePlayService.java b/src/main/java/racingcar/service/RacingGamePlayService.java deleted file mode 100644 index 1385cd350..000000000 --- a/src/main/java/racingcar/service/RacingGamePlayService.java +++ /dev/null @@ -1,28 +0,0 @@ -package racingcar.service; - -import java.util.List; -import java.util.stream.Collectors; -import org.springframework.stereotype.Service; -import racingcar.domain.Car; -import racingcar.domain.Cars; -import racingcar.domain.RacingGame; -import racingcar.dto.RacingGameRequest; -import racingcar.dto.RacingGameResponse; - -@Service -public class RacingGamePlayService { - private final RacingGameAddService racingGameAddService; - - public RacingGamePlayService(RacingGameAddService racingGameAddService) { - this.racingGameAddService = racingGameAddService; - } - - public RacingGameResponse play(RacingGameRequest racingGameRequest) { - List cars = racingGameRequest.getNames().stream() - .map(Car::new) - .collect(Collectors.toList()); - RacingGame racingGame = new RacingGame(racingGameRequest.getCount(), new Cars(cars)); - racingGame.run(); - return racingGameAddService.addGame(racingGame, racingGameRequest.getCount()); - } -} diff --git a/src/test/java/racingcar/controller/RacingGameControllerTest.java b/src/test/java/racingcar/controller/RacingGameControllerTest.java index c16bae430..2183e617d 100644 --- a/src/test/java/racingcar/controller/RacingGameControllerTest.java +++ b/src/test/java/racingcar/controller/RacingGameControllerTest.java @@ -21,8 +21,8 @@ import racingcar.dto.CarDto; import racingcar.dto.RacingGameRequest; import racingcar.dto.RacingGameResponse; +import racingcar.service.RacingGameAddService; import racingcar.service.RacingGameFindService; -import racingcar.service.RacingGamePlayService; @WebMvcTest(RacingGameWebController.class) public class RacingGameControllerTest { @@ -34,7 +34,7 @@ public class RacingGameControllerTest { private ObjectMapper objectMapper; @MockBean - private RacingGamePlayService racingPlayService; + private RacingGameAddService racingGameAddService; @MockBean private RacingGameFindService racingGameFindService; @@ -48,7 +48,7 @@ public void plays() throws Exception { List.of(new CarDto("현구막", 10), new CarDto("박스터", 7)) ); String requestString = objectMapper.writeValueAsString(request); - when(racingPlayService.play(any())).thenReturn(expectedResponse); + when(racingGameAddService.addGame(any())).thenReturn(expectedResponse); mockMvc.perform(post("/plays") .contentType(MediaType.APPLICATION_JSON) diff --git a/src/test/java/racingcar/service/RacingGameAddServiceTest.java b/src/test/java/racingcar/service/RacingGameAddServiceTest.java index 975a86dc4..213611115 100644 --- a/src/test/java/racingcar/service/RacingGameAddServiceTest.java +++ b/src/test/java/racingcar/service/RacingGameAddServiceTest.java @@ -10,7 +10,7 @@ import org.mockito.Mockito; import racingcar.dao.CarDao; import racingcar.dao.RacingGameDao; -import racingcar.domain.RacingGame; +import racingcar.dto.RacingGameRequest; import racingcar.dto.RacingGameResponse; @@ -30,9 +30,9 @@ void setUp() { @Test @DisplayName("이름과 실행 횟수를 받아 게임의 결과를 반환한다") void playTest() { - RacingGame racingGame = RacingGame.of(List.of("박스터", "엔초"), 10); + RacingGameRequest request = new RacingGameRequest(List.of("박스터", "엔초"), 10); - RacingGameResponse play = racingGameAddService.addGame(racingGame, 10); + RacingGameResponse play = racingGameAddService.addGame(request); assertAll( () -> assertThat(play.getWinners()).isNotEmpty(),