diff --git a/docs/README.md b/docs/README.md index e69de29..afe8809 100644 --- a/docs/README.md +++ b/docs/README.md @@ -0,0 +1,32 @@ +## domain +#### 로또 구입 + +- 입력 받은 로또 구입 금액을 통해 로또 발행한 수량을 구한다. +- 각 로또 번호를 오름차순으로 구한다. + +#### 로또 당첨 번호 + +- 입력 받은 당첨 번호와 보너스 번호를 저장한다. + +#### 당첨 통계 + +- 구매한 로또 번호와 당첨 번호를 비교하여 당첨 결과를 확인한다. +- 당첨 내역을 이용하여 총 수익률을 계산한다. + +## view +#### 입력 + +- 로또 구입 금액을 입력 받는다. +- 당첨 번호를 입력 받는다. +- 보너스 번호를 입력 받는다. + +#### 출력 + +- 발행한 로또 수량 및 번호(오름차순)를 출력한다. +- 당첨 내역을 출력한다. +- 수익률은 소수점 둘째 자리에서 반올림하여 출력한다. +- 예외 상황 시 에러 문구를 출력한다. + +## controller + +- domain과 view를 결정하여 전달 diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index a595206..2bbac7d 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -2,4 +2,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip zipStoreBase=GRADLE_USER_HOME -zipStorePath=wrapper/dists +zipStorePath=wrapper/dists \ No newline at end of file diff --git a/src/main/java/lotto/Application.java b/src/main/java/lotto/Application.java index d190922..a0862b0 100644 --- a/src/main/java/lotto/Application.java +++ b/src/main/java/lotto/Application.java @@ -1,7 +1,11 @@ package lotto; +import lotto.controller.LottoController; + public class Application { public static void main(String[] args) { // TODO: 프로그램 구현 + LottoController lottoController = new LottoController(); + lottoController.lottoStart(); } } diff --git a/src/main/java/lotto/controller/LottoController.java b/src/main/java/lotto/controller/LottoController.java new file mode 100644 index 0000000..c6e3ccf --- /dev/null +++ b/src/main/java/lotto/controller/LottoController.java @@ -0,0 +1,36 @@ +package lotto.controller; + +import lotto.domain.*; +import lotto.view.InputView; +import lotto.view.OutputView; + +import java.util.Map; + +public class LottoController { + public void lottoStart() { + try { + int purchaseAmount = InputView.getPurchaseAmount(); // 구매 로또 개수 + LottoGenerator lottoGenerator = new LottoGenerator(purchaseAmount); // 구매 개수만큼 로또 발행 + Lottos lottos = new Lottos(lottoGenerator.generateLottos()); // 발행한 로또들 저장 + printLottosInformation(lottoGenerator, lottos); // 구매 개수, 발행한 로또 번호 출력 + WinningLotto winningLotto = new WinningLotto(InputView.getLottoNumber(), InputView.getLottoBonusNumber()); //당첨 번호, 보너스 번호 저장 + Map winningDetails = WinningStatistics.getWinningDetails(lottos, winningLotto); //로또 당첨 통계 + printWinningInformation(winningDetails, purchaseAmount); //로또 당첨 내역, 수익률 출력 + + } catch (IllegalArgumentException e) { + System.out.println(e.getMessage()); + } + } + + private void printLottosInformation(LottoGenerator lottoGenerator, Lottos lottos) { + OutputView.printHowManyLottoUserPurchased(lottoGenerator.getLottoQuantity()); + OutputView.printLottos(lottos); + } + + private void printWinningInformation(Map winningDetails, int purchaseAmount) { + OutputView.printWinningStatistics(); + OutputView.printWinningDetails(winningDetails); + long winningAmount = WinningStatistics.getWinningAmount(winningDetails); + OutputView.printLottoYield(WinningStatistics.getLottoYield(winningAmount, purchaseAmount)); + } +} diff --git a/src/main/java/lotto/Lotto.java b/src/main/java/lotto/domain/Lotto.java similarity index 64% rename from src/main/java/lotto/Lotto.java rename to src/main/java/lotto/domain/Lotto.java index 519793d..9b59b97 100644 --- a/src/main/java/lotto/Lotto.java +++ b/src/main/java/lotto/domain/Lotto.java @@ -1,6 +1,8 @@ -package lotto; +package lotto.domain; +import java.util.HashSet; import java.util.List; +import java.util.Set; public class Lotto { private final List numbers; @@ -10,6 +12,7 @@ public Lotto(List numbers) { this.numbers = numbers; } + // 입력받은 로또 번호가 6개가 아니라면 예외 발생 private void validate(List numbers) { if (numbers.size() != 6) { throw new IllegalArgumentException(); @@ -17,4 +20,8 @@ private void validate(List numbers) { } // TODO: 추가 기능 구현 + + public List getNumbers() { + return numbers; + } } diff --git a/src/main/java/lotto/domain/LottoGenerator.java b/src/main/java/lotto/domain/LottoGenerator.java new file mode 100644 index 0000000..64d5112 --- /dev/null +++ b/src/main/java/lotto/domain/LottoGenerator.java @@ -0,0 +1,73 @@ +package lotto.domain; + + +import org.kokodak.Randoms; + +import java.util.ArrayList; +import java.util.Comparator; +import java.util.List; + +public class LottoGenerator { + + private static final int LOTTO_NUMBER_MIN = 1; + private static final int LOTTO_NUMBER_MAX = 45; + private static final int LOTTO_NUMBER_QUANTITY = 6; + + private List lottos = new ArrayList<>(); + private int lottoQuantity; + + + + //구매 가능한 로또 수량 + public LottoGenerator(int money) { + lottoQuantity = money / 1000; + } + + //돈을 0원 이하로 받거나 1000원 단위로 받지 않으면 예외 발생 + private void validateMoney(int money) { + if (isZeroOrNegativeNumber(money) || !isDividedByOneThousand(money)) { + throw new IllegalArgumentException("[ERROR] 구입 금액은 1,000원 단위로만 받을 수 있습니다."); + } + } + + //돈을 0원 이하로 받았는지 확인 + private boolean isZeroOrNegativeNumber(int money) { + if (money <= 0){ + return true; + } + return false; + } + + //돈을 1000원 단위로 받았는지 확인 + private boolean isDividedByOneThousand(int money) { + if (money % 1000 == 0) { + return true; + } + return false; + } + + //1 ~ 45 사이의 6개 숫자를 랜덤으로 뽑아서 로또 발행 + public Lotto generateLotto() { + List randomNumbers = new ArrayList<>( + Randoms.pickUniqueNumbersInRange(LOTTO_NUMBER_MIN, LOTTO_NUMBER_MAX, LOTTO_NUMBER_QUANTITY) + ); + randomNumbers.sort(Comparator.naturalOrder()); + return new Lotto(randomNumbers); + + } + + // + public List generateLottos() { + for (int i = 0; i < lottoQuantity; i++) { + Lotto lotto = generateLotto(); + lottos.add(lotto); + } + return lottos; + } + + // + public int getLottoQuantity() { + return lottoQuantity; + } + +} diff --git a/src/main/java/lotto/domain/Lottos.java b/src/main/java/lotto/domain/Lottos.java new file mode 100644 index 0000000..1c4204e --- /dev/null +++ b/src/main/java/lotto/domain/Lottos.java @@ -0,0 +1,15 @@ +package lotto.domain; + +import java.util.List; + +public class Lottos { + private List lottos; + + public Lottos(List lottos) { + this.lottos = lottos; + } + + public List getLottos() { + return lottos; + } +} diff --git a/src/main/java/lotto/domain/WinningLotto.java b/src/main/java/lotto/domain/WinningLotto.java new file mode 100644 index 0000000..8285e90 --- /dev/null +++ b/src/main/java/lotto/domain/WinningLotto.java @@ -0,0 +1,75 @@ +package lotto.domain; + +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +public class WinningLotto { + private static final String WINNING_NUMBERS_ARE_BETWEEN_ONE_AND_FORTY_FIVE = "[ERROR] 당첨 번호는 1부터 45 사이의 숫자여야 합니다."; + private static final String WINNING_NUMBERS_MUST_BE_SIX_DIFFERENT_NUMBERS = "[ERROR] 당첨 번호는 서로 다른 6개의 수여야 합니다."; + private static final String BONUS_NUMBER_IS_BETWEEN_ONE_AND_FORTY_FIVE = "[ERROR] 보너스 번호는 1부터 45 사이의 숫자여야 합니다."; + private static final String WINNING_NUMBERS_CONTAIN_BONUS_NUMBER = "[ERROR] 당첨 번호와 보너스 번호가 중복됩니다."; + private static final int LOTTO_NUMBER_LOWER_LIMIT = 1; + private static final int LOTTO_NUMBER_UPPER_LIMIT = 45; + private static final int LOTTO_NUMBERS_SIZE = 6; + + private final List winningNumbers; + private final int bonusNumber; + + public WinningLotto(List winningNumbers, int bonusNumber) { + validateWinningNumbers(winningNumbers); + validateBonusNumber(bonusNumber); + validateDuplicate(winningNumbers, bonusNumber); + this.winningNumbers = winningNumbers; + this.bonusNumber = bonusNumber; + } + + private void validateWinningNumbers(List winningNumbers) { + if (!isSixDifferentNumbers(winningNumbers)) { + throw new IllegalArgumentException(WINNING_NUMBERS_MUST_BE_SIX_DIFFERENT_NUMBERS); + } + if (!isBetweenOneAndFortyFive(winningNumbers)) { + throw new IllegalArgumentException(WINNING_NUMBERS_ARE_BETWEEN_ONE_AND_FORTY_FIVE); + } + } + + // 중복을 허용하지 않는 Set의 특성을 이용해서 + // 6개가 전부 다른 숫자인지 확인 + private boolean isSixDifferentNumbers(List winningNumbers) { + Set duplicateChecker = new HashSet<>(winningNumbers); + return duplicateChecker.size() == LOTTO_NUMBERS_SIZE; + } + + private boolean isBetweenOneAndFortyFive(List winningNumbers) { + for (int winningNumber : winningNumbers) { + if (winningNumber < LOTTO_NUMBER_LOWER_LIMIT || winningNumber > LOTTO_NUMBER_UPPER_LIMIT) { + return false; + } + } + return true; + } + + private void validateBonusNumber(int bonusNumber) { + if (!isBetweenOneAndFortyFive((bonusNumber))) { + throw new IllegalArgumentException(BONUS_NUMBER_IS_BETWEEN_ONE_AND_FORTY_FIVE); + } + } + + private boolean isBetweenOneAndFortyFive(int bonusNumber) { + return bonusNumber >= LOTTO_NUMBER_LOWER_LIMIT && bonusNumber <= LOTTO_NUMBER_UPPER_LIMIT; + } + + private void validateDuplicate(List winningNumbers, int bonusNumber) { + if (winningNumbers.contains(bonusNumber)) { + throw new IllegalArgumentException(WINNING_NUMBERS_CONTAIN_BONUS_NUMBER); + } + } + + public List getWinningNumbers() { + return winningNumbers; + } + + public int getBonusNumber() { + return bonusNumber; + } +} diff --git a/src/main/java/lotto/domain/WinningRank.java b/src/main/java/lotto/domain/WinningRank.java new file mode 100644 index 0000000..4aa3cdb --- /dev/null +++ b/src/main/java/lotto/domain/WinningRank.java @@ -0,0 +1,40 @@ +package lotto.domain; + + +import java.util.Arrays; + +public enum WinningRank { + + LAST_PLACE(0, false, 0), + FIFTH_PLACE(3, false, 5_000), + FOURTH_PLACE(4, false, 50_000), + THIRD_PLACE(5, false, 1_500_000), + SECOND_PLACE(5, true, 30_000_000), + FIRST_PLACE(6, false, 2_000_000_000); + + private final int matchingCount; + private final boolean containsBonusNumber; + private final int winningPrice; + + WinningRank(int matchingCount, boolean containsBonusNumber, int winningPrice) { + this.matchingCount = matchingCount; + this.containsBonusNumber = containsBonusNumber; + this.winningPrice = winningPrice; + } + + public static WinningRank findWinningRank(int matchingCount, boolean containsBonusNumber) { + return Arrays.stream(values()) + .filter(winningRank -> winningRank.matchingCount == matchingCount) + .filter(winningRank -> winningRank.containsBonusNumber == containsBonusNumber) + .findFirst() + .orElse(WinningRank.LAST_PLACE); + } + + public int getMatchingCount() { + return matchingCount; + } + + public int getWinningPrice() { + return winningPrice; + } +} diff --git a/src/main/java/lotto/domain/WinningStatistics.java b/src/main/java/lotto/domain/WinningStatistics.java new file mode 100644 index 0000000..480ec1a --- /dev/null +++ b/src/main/java/lotto/domain/WinningStatistics.java @@ -0,0 +1,58 @@ +package lotto.domain; + +import java.util.Arrays; +import java.util.EnumMap; +import java.util.List; +import java.util.Map; + +public class WinningStatistics { + + private static final int AT_LEAST_THIRD_PLACE = 5; + private static final int PERCENTAGE = 100; + private static final int INITIAL_VALUE = 0; + + public static Map getWinningDetails(Lottos lottos, WinningLotto winningLotto) { + Map winningDetails = generateWinningDetails(); + for (Lotto lotto : lottos.getLottos()) { + int matchingCount = compareNumbersWithWinningNumbers(lotto, winningLotto); + boolean containsBonusNumber = compareNumbersWithBonusNumber(lotto, winningLotto, matchingCount); + WinningRank winningRank = WinningRank.findWinningRank(matchingCount, containsBonusNumber); + winningDetails.replace(winningRank, winningDetails.get(winningRank) + 1); + } + return winningDetails; + } + + public static Map generateWinningDetails() { + Map winningDetails = new EnumMap<>(WinningRank.class); + Arrays.stream(WinningRank.values()).forEach(winningRank -> winningDetails.put(winningRank, INITIAL_VALUE)); + return winningDetails; + } + + private static int compareNumbersWithWinningNumbers(Lotto lotto, WinningLotto winningLotto) { + List numbers = lotto.getNumbers(); + List winningNumbers = winningLotto.getWinningNumbers(); + return (int) numbers.stream() + .filter(winningNumbers::contains) + .count(); + } + + private static boolean compareNumbersWithBonusNumber(Lotto lotto, WinningLotto winningLotto, int matchingCount) { + if (matchingCount != AT_LEAST_THIRD_PLACE) { + return false; + } + List numbers = lotto.getNumbers(); + int bonusNumber = winningLotto.getBonusNumber(); + return numbers.contains(bonusNumber); + } + + public static long getWinningAmount(Map winningDetails) { + return winningDetails.entrySet().stream() + .mapToLong(entry -> (long) entry.getKey().getWinningPrice() * entry.getValue()) + .sum(); + } + + public static double getLottoYield(long winningAmount, int money) { + double lottoYield = PERCENTAGE + (double) (winningAmount - money) / money * PERCENTAGE; + return Math.round(lottoYield * 10) / 10.0; + } +} diff --git a/src/main/java/lotto/view/InputView.java b/src/main/java/lotto/view/InputView.java new file mode 100644 index 0000000..d5e572d --- /dev/null +++ b/src/main/java/lotto/view/InputView.java @@ -0,0 +1,42 @@ +package lotto.view; + + +import org.kokodak.Console; + +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + +public class InputView { + + private static final String NOT_NUMBER = "[ERROR] 숫자가 아닌 값이 입력됐습니다."; + + public static int getPurchaseAmount() { + System.out.println("구입금액을 입력해주세요."); + try { + return Integer.parseInt(Console.readLine()); + } catch (NumberFormatException numberFormatException) { + throw new IllegalArgumentException(NOT_NUMBER); + } + } + + public static List getLottoNumber() { + System.out.println("당첨 번호를 입력해 주세요."); + try { + return Arrays.stream(Console.readLine().split(",")) + .map(Integer::parseInt) + .collect(Collectors.toList()); + } catch (NumberFormatException numberFormatException) { + throw new IllegalArgumentException(NOT_NUMBER); + } + } + + public static int getLottoBonusNumber() { + System.out.println("보너스 번호를 입력해 주세요."); + try { + return Integer.parseInt(Console.readLine()); + } catch (NumberFormatException numberFormatException) { + throw new IllegalArgumentException(NOT_NUMBER); + } + } +} diff --git a/src/main/java/lotto/view/OutputView.java b/src/main/java/lotto/view/OutputView.java new file mode 100644 index 0000000..7373383 --- /dev/null +++ b/src/main/java/lotto/view/OutputView.java @@ -0,0 +1,67 @@ +package lotto.view; + + +import java.text.DecimalFormat; +import java.util.Map; +import lotto.domain.Lottos; +import lotto.domain.WinningRank; + +public class OutputView { + + private static final String HOW_MANY_LOTTO_USER_PURCHASED_MESSAGE = "%d개를 구매했습니다.\n"; + private static final String WINNING_STATISTICS_MESSAGE = "당첨 통계\n---"; + private static final String WINNING_DETAILS_MESSAGE = "%d개 일치 (%s원) - %d개\n"; + private static final String WINNING_DETAILS_WITH_BONUS_MESSAGE = "%d개 일치, 보너스 볼 일치 (%s원) - %d개\n"; + private static final String LOTTO_YIELD_MESSAGE = "총 수익률은 %.1f%%입니다.\n"; + private static final String SEPARATOR_PATTERN = "###,###"; + + public static void printHowManyLottoUserPurchased(int lottoQuantity) { + System.out.printf(HOW_MANY_LOTTO_USER_PURCHASED_MESSAGE, lottoQuantity); + } + + public static void printLottos(Lottos lottos) { + lottos.getLottos().stream() + .forEach(lotto -> System.out.println(lotto.getNumbers().toString())); + } + + public static void printWinningStatistics() { + System.out.println(WINNING_STATISTICS_MESSAGE); + } + + public static void printWinningDetails(Map winningDetails) { + winningDetails.entrySet().stream() + .filter(entry -> entry.getKey() != WinningRank.LAST_PLACE) + .forEach(entry -> { + if (entry.getKey() == WinningRank.SECOND_PLACE) { + printWinningDetailsWithBonus(entry); + return; + } + printWinningDetailsWithoutBonus(entry); + }); + } + + private static void printWinningDetailsWithBonus(Map.Entry entry) { + System.out.printf(WINNING_DETAILS_WITH_BONUS_MESSAGE, + entry.getKey().getMatchingCount(), + getFormattingPrice(entry.getKey().getWinningPrice()), + entry.getValue()); + } + + private static void printWinningDetailsWithoutBonus(Map.Entry entry) { + System.out.printf(WINNING_DETAILS_MESSAGE, + entry.getKey().getMatchingCount(), + getFormattingPrice(entry.getKey().getWinningPrice()), + entry.getValue()); + } + + private static String getFormattingPrice(int winningPrice) { + DecimalFormat df = new DecimalFormat(SEPARATOR_PATTERN); + return df.format(winningPrice); + } + + public static void printLottoYield(double lottoYield) { + System.out.printf(LOTTO_YIELD_MESSAGE, lottoYield); + } + + +} diff --git a/src/test/java/lotto/domain/LottoGeneratorTest.java b/src/test/java/lotto/domain/LottoGeneratorTest.java new file mode 100644 index 0000000..1b85d3c --- /dev/null +++ b/src/test/java/lotto/domain/LottoGeneratorTest.java @@ -0,0 +1,53 @@ +package lotto.domain; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import java.util.List; +import java.util.Map; + +import static org.assertj.core.api.Assertions.assertThat; + +public class LottoGeneratorTest { + @Test + @DisplayName("각 등수마다 몇 개씩 당첨되었는지 계산 -> 1등 1개, 2등 2개, 5등 2개, 꽝 1개") + void getWinningDetailsTest() { + Lotto lotto1 = new Lotto(List.of(1, 2, 3, 4, 5, 6)); + Lotto lotto2 = new Lotto(List.of(40, 41, 42, 43, 44, 45)); + Lotto lotto3 = new Lotto(List.of(1, 2, 3, 43, 44, 45)); + Lotto lotto4 = new Lotto(List.of(40, 2, 3, 43, 44, 6)); + Lotto lotto5 = new Lotto(List.of(40, 2, 3, 4, 5, 6)); + Lotto lotto6 = new Lotto(List.of(40, 1, 2, 3, 4, 5)); + Lottos lottos = new Lottos(List.of(lotto1, lotto2, lotto3, lotto4, lotto5, lotto6)); + WinningLotto winningLotto = new WinningLotto(List.of(1, 2, 3, 4, 5, 6), 40); + Map winningDetails = WinningStatistics.getWinningDetails(lottos, winningLotto); + + assertThat(winningDetails.get(WinningRank.LAST_PLACE)).isEqualTo(1); + assertThat(winningDetails.get(WinningRank.FIFTH_PLACE)).isEqualTo(2); + assertThat(winningDetails.get(WinningRank.SECOND_PLACE)).isEqualTo(2); + assertThat(winningDetails.get(WinningRank.FIRST_PLACE)).isEqualTo(1); + } + + @Test + @DisplayName("당첨 내역을 이용해 당첨 금액을 구한다.") + void getWinningAmountTest() { + Map winningDetails = WinningStatistics.generateWinningDetails(); + winningDetails.replace(WinningRank.SECOND_PLACE, 2); + winningDetails.replace(WinningRank.FOURTH_PLACE, 5); + winningDetails.replace(WinningRank.FIFTH_PLACE, 13); + + long winningAmount = WinningStatistics.getWinningAmount(winningDetails); + assertThat(winningAmount).isEqualTo(60_315_000); + } + + @Test + @DisplayName("당첨 내역을 이용해 수익률을 구한다.") + void getLottoYieldTest() { + double lottoYield; + lottoYield = WinningStatistics.getLottoYield(130_290_000, 45000); + assertThat(lottoYield).isEqualTo(289533.3); + + lottoYield = WinningStatistics.getLottoYield(123300, 523000); + assertThat(lottoYield).isEqualTo(23.6); + } +} diff --git a/src/test/java/lotto/LottoTest.java b/src/test/java/lotto/domain/LottoTest.java similarity index 94% rename from src/test/java/lotto/LottoTest.java rename to src/test/java/lotto/domain/LottoTest.java index 14ed50f..cb0566c 100644 --- a/src/test/java/lotto/LottoTest.java +++ b/src/test/java/lotto/domain/LottoTest.java @@ -1,8 +1,10 @@ -package lotto; +package lotto.domain; import static org.assertj.core.api.Assertions.assertThatThrownBy; import java.util.List; + +import lotto.domain.Lotto; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; diff --git a/src/test/java/lotto/domain/WinningLottoTest.java b/src/test/java/lotto/domain/WinningLottoTest.java new file mode 100644 index 0000000..8dca164 --- /dev/null +++ b/src/test/java/lotto/domain/WinningLottoTest.java @@ -0,0 +1,99 @@ +package lotto.domain; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; + +import java.util.List; + +import static org.assertj.core.api.AssertionsForClassTypes.assertThatThrownBy; + +public class WinningLottoTest { + @Test + @DisplayName("당첨 번호가 6개가 아닐 경우 실패") + void winningNumbersSizeIsNotSixTest() { + List winningNumbers1 = List.of(1, 2, 3, 4, 5); + List winningNumbers2 = List.of(1, 2, 3); + List winningNumbers3 = List.of(1, 2, 3, 4, 5, 6, 7, 8, 9); + List winningNumbers4 = List.of(); + int bonusNumber = 45; + + assertThatThrownBy(() -> new WinningLotto(winningNumbers1, bonusNumber)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessageContaining("[ERROR]"); + assertThatThrownBy(() -> new WinningLotto(winningNumbers2, bonusNumber)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessageContaining("[ERROR]"); + assertThatThrownBy(() -> new WinningLotto(winningNumbers3, bonusNumber)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessageContaining("[ERROR]"); + assertThatThrownBy(() -> new WinningLotto(winningNumbers4, bonusNumber)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessageContaining("[ERROR]"); + } + + @Test + @DisplayName("당첨 번호에 중복되는 번호가 있을 경우 실패") + void winningNumbersContainDuplicateNumberTest() { + List winningNumbers1 = List.of(1, 2, 3, 3, 4, 5); + List winningNumbers2 = List.of(3, 3, 3, 3, 4, 5); + List winningNumbers3 = List.of(5, 1, 2, 3, 4, 5); + int bonusNumber = 45; + + assertThatThrownBy(() -> new WinningLotto(winningNumbers1, bonusNumber)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessageContaining("[ERROR]"); + assertThatThrownBy(() -> new WinningLotto(winningNumbers2, bonusNumber)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessageContaining("[ERROR]"); + assertThatThrownBy(() -> new WinningLotto(winningNumbers3, bonusNumber)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessageContaining("[ERROR]"); + } + + @Test + @DisplayName("당첨 번호에 1~45의 범위를 벗어나는 수가 들어올 경우 실패") + void winningNumberOutsideTheRangeOfOneToFortyFiveTest() { + List winningNumbers1 = List.of(-1, 2, 3, 4, 5, 7); + List winningNumbers2 = List.of(0, 2, 3, 4, 5, 7); + List winningNumbers3 = List.of(1, 2, 3, 4, 5, 46); + List winningNumbers4 = List.of(-1, 0, 3, 4, 5, 101); + int bonusNumber = 45; + + assertThatThrownBy(() -> new WinningLotto(winningNumbers1, bonusNumber)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessageContaining("[ERROR]"); + assertThatThrownBy(() -> new WinningLotto(winningNumbers2, bonusNumber)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessageContaining("[ERROR]"); + assertThatThrownBy(() -> new WinningLotto(winningNumbers3, bonusNumber)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessageContaining("[ERROR]"); + assertThatThrownBy(() -> new WinningLotto(winningNumbers4, bonusNumber)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessageContaining("[ERROR]"); + } + + @ParameterizedTest + @ValueSource(ints = {-4, 0, 46, 101, -45}) + @DisplayName("보너스 번호에 1~45의 범위를 벗어나는 수가 들어올 경우 실패") + void bonusNumberOutsideTheRangeOfOneToFortyFiveTest(int bonusNumber) { + List winningNumbers = List.of(1, 2, 3, 4, 5, 6); + + assertThatThrownBy(() -> new WinningLotto(winningNumbers, bonusNumber)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessageContaining("[ERROR]"); + } + + @Test + @DisplayName("당첨 번호와 보너스 번호가 중복될 경우 실패") + void duplicateWinningNumberAndBonusNumberTest() { + List winningNumbers = List.of(1, 2, 3, 4, 5, 45); + int bonusNumber = 3; + + assertThatThrownBy(() -> new WinningLotto(winningNumbers, bonusNumber)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessageContaining("[ERROR]"); + } +} diff --git a/src/test/java/lotto/domain/WinningRankTest.java b/src/test/java/lotto/domain/WinningRankTest.java new file mode 100644 index 0000000..28d67c4 --- /dev/null +++ b/src/test/java/lotto/domain/WinningRankTest.java @@ -0,0 +1,30 @@ +package lotto.domain; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; + +public class WinningRankTest { + @Test + @DisplayName("당첨 번호 일치 개수와 보너스 번호 일치 여부를 통해서 당첨 등수를 구한다.") + void findWinningRankTest() { + WinningRank winningRank1 = WinningRank.findWinningRank(0, false); + WinningRank winningRank2 = WinningRank.findWinningRank(1, false); + WinningRank winningRank3 = WinningRank.findWinningRank(2, false); + WinningRank winningRank4 = WinningRank.findWinningRank(3, false); + WinningRank winningRank5 = WinningRank.findWinningRank(4, false); + WinningRank winningRank6 = WinningRank.findWinningRank(5, false); + WinningRank winningRank7 = WinningRank.findWinningRank(5, true); + WinningRank winningRank8 = WinningRank.findWinningRank(6, false); + + assertThat(winningRank1).isEqualTo(WinningRank.LAST_PLACE); + assertThat(winningRank2).isEqualTo(WinningRank.LAST_PLACE); + assertThat(winningRank3).isEqualTo(WinningRank.LAST_PLACE); + assertThat(winningRank4).isEqualTo(WinningRank.FIFTH_PLACE); + assertThat(winningRank5).isEqualTo(WinningRank.FOURTH_PLACE); + assertThat(winningRank6).isEqualTo(WinningRank.THIRD_PLACE); + assertThat(winningRank7).isEqualTo(WinningRank.SECOND_PLACE); + assertThat(winningRank8).isEqualTo(WinningRank.FIRST_PLACE); + } +} diff --git a/src/test/java/lotto/domain/WinningStatisticsTest.java b/src/test/java/lotto/domain/WinningStatisticsTest.java new file mode 100644 index 0000000..a2aef5f --- /dev/null +++ b/src/test/java/lotto/domain/WinningStatisticsTest.java @@ -0,0 +1,53 @@ +package lotto.domain; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import java.util.List; +import java.util.Map; + +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; + +public class WinningStatisticsTest { + @Test + @DisplayName("각 등수마다 몇 개씩 당첨되었는지 계산 -> 1등 1개, 2등 2개, 5등 2개, 꽝 1개") + void getWinningDetailsTest() { + Lotto lotto1 = new Lotto(List.of(1, 2, 3, 4, 5, 6)); + Lotto lotto2 = new Lotto(List.of(40, 41, 42, 43, 44, 45)); + Lotto lotto3 = new Lotto(List.of(1, 2, 3, 43, 44, 45)); + Lotto lotto4 = new Lotto(List.of(40, 2, 3, 43, 44, 6)); + Lotto lotto5 = new Lotto(List.of(40, 2, 3, 4, 5, 6)); + Lotto lotto6 = new Lotto(List.of(40, 1, 2, 3, 4, 5)); + Lottos lottos = new Lottos(List.of(lotto1, lotto2, lotto3, lotto4, lotto5, lotto6)); + WinningLotto winningLotto = new WinningLotto(List.of(1, 2, 3, 4, 5, 6), 40); + Map winningDetails = WinningStatistics.getWinningDetails(lottos, winningLotto); + + assertThat(winningDetails.get(WinningRank.LAST_PLACE)).isEqualTo(1); + assertThat(winningDetails.get(WinningRank.FIFTH_PLACE)).isEqualTo(2); + assertThat(winningDetails.get(WinningRank.SECOND_PLACE)).isEqualTo(2); + assertThat(winningDetails.get(WinningRank.FIRST_PLACE)).isEqualTo(1); + } + + @Test + @DisplayName("당첨 내역을 이용해 당첨 금액을 구한다.") + void getWinningAmountTest() { + Map winningDetails = WinningStatistics.generateWinningDetails(); + winningDetails.replace(WinningRank.SECOND_PLACE, 2); + winningDetails.replace(WinningRank.FOURTH_PLACE, 5); + winningDetails.replace(WinningRank.FIFTH_PLACE, 13); + + long winningAmount = WinningStatistics.getWinningAmount(winningDetails); + assertThat(winningAmount).isEqualTo(60_315_000); + } + + @Test + @DisplayName("당첨 내역을 이용해 수익률을 구한다.") + void getLottoYieldTest() { + double lottoYield; + lottoYield = WinningStatistics.getLottoYield(130_290_000, 45000); + assertThat(lottoYield).isEqualTo(289533.3); + + lottoYield = WinningStatistics.getLottoYield(123300, 523000); + assertThat(lottoYield).isEqualTo(23.6); + } +}