diff --git a/docs/README.md b/docs/README.md index e69de29bb2d..769011539b2 100644 --- a/docs/README.md +++ b/docs/README.md @@ -0,0 +1,59 @@ +### 기능 요구사항 +- [X] 구입 금액에 맞는 로또를 생성한다. + - [X] 로또 한장의 가격은 1000원 + - [X] 금액이 1000원 단위가 아니면 `IllegalArgumentException` 예외를 던진다 + - [X] 로또 번호는 1-45 사이의 중복 되지 않은 6개의 숫자 + - [X] 세부 기능 + - [X] 1-45 사이 서로 다른 랜덤한 숫자들을 생성한다. (테스팅 어려움! / 상위계층에서 전략을 주입한다.) + - [X] 숫자들로 한 개의 로또를 생성한다. Input: 숫자 리스트 Output: 로또 객체 + - [X] 생성할 숫자만큼 로또를 생성한다. Input: 로또 생성 숫자, 숫자 생성 전략 Output: 로또 리스트 + - [X] 구입 금액이 1000원으로 나누어 떨어지면 로또 생성 숫자 아니면 `IllegalArgumentException` 예외를 던진다. Input: 금액 Output: 로또 생성 숫자 or `IllegalArgumentException` + - [X] 숫자들이 1-45 사이 숫자가 아니면 `IllegalArgumentException` 예외를 던진다 + - [X] 숫자들 중 중복된 숫자가 있으면 `IllegalArgumentException` 예외를 던진다 +- [X] 당첨 번호와 보너스 번호를 저장한다. + - [X] 1-45 사이의 중복 되지 않는 숫자 6개와 보너스 번호 1개 + - [X] 세부 기능 + - [X] 당첨 번호와 보너스 번호를 저장한다. Input: 당첨 숫자 리스트, 보너스 번호 Output: 우승 로또 객체 + - [X] 숫자들이 1-45 사이 숫자가 아니면 `IllegalArgumentException` 예외를 던진다 + - [X] 숫자들 중 중복된 숫자가 있으면 `IllegalArgumentException` 예외를 던진다 +- [X] 발행한 로또 번호와 우승 로또를 비교하여 당첨 내역을 계산한다. + - [X] 세부 기능 + - [X] 로또 하나를 우승 로또 숫자들과 비교해서 맞은 숫자 개수와 보너스 번호 맞춤 여부를 알려준다. Input: 우승 로또 숫자들 Output: 맞은 숫자 개수 + - [X] 로또 하나를 우승 로또의 보너스 번호와 비교해서 보너스 번호 맞춤 여부를 알려준다. Input: 보너스 번호 Output: 보너스 번호 맞춤 여부 + - [X] 맞은 숫자 개수와 보너스 번호 맞춤 여부를 통해 등수를 계산한다. Input: 맞은 숫자 개수, 보너스 번호 맞춤 여부 Output: 등수 + - [X] 개별 로또의 등수를 모아서 당첨 내역을 반환한다. Input: 로또들 Output: 당첨 내역 + - [X] 당첨 내역에 따라 총 당첨 금액을 계산한다. Input: 당첨 내역 Output: 당첨 금액 +- [X] 지불한 금액과 당첨 금액을 통해 수익률을 계산한다. + - [X] 세부 기능 + - [X] 당첨 금액을 지불 금액으로 나누어 수익률을 계산한다. Input: 당첨 금액, 지불 금액 Output: 수익률 + - [X] 수익률을 소수점 둘째자리로 반올림한다. (유틸리티 기능 사용) +- [ ] 사용자가 잘못된 입력을 할 시에 예외 처리한다. + - [ ] 잘못된 입력 발생시 `IllegalArgumentException` 를 발생시키고, 에러 문구를 출력 후 그 부분부터 입력을 다시 받는다. + - [ ] Exception이 아닌 `IllegalArgumentException` 등과 같은 명확한 유형을 처리 + - [ ] 빈칸 입력시 예외 처리 + - [ ] 숫자 아닌 입력 예외 처리 + - [ ] 로또 번호와 보너스 번호는 1-45 사이의 중복 되지 않은 6개의 숫자, 아닐시 예외 처리 + +### 입출력 요구사항 +- [X] 로또 금액을 입력 받는다. +- [X] 당첨 번호를 입력 받는다. +- [X] 보너스 번호를 입력 받는다. + - [X] 번호는 쉼표로 구분 +- [X] 발행한 로또 수량 및 번호를 출력한다. + - [X] 출력한 번호는 오름차순 +- [X] 당첨 내역을 출력한다. +- [ ] 수익률을 출력한다. +- [X] 예외 문구를 출력한다. + - [X] "[ERROR]"로 시작하는 에러 메시지 + +### 유틸리티 기능 +#### 문자열 관련 +- [X] 숫자 오름차순 정렬 +- [X] 소숫점 둘째 자리 수에서 반올림 +- [X] 로또 번호 출력 형식 생성 [ , , , , ] +- [X] 입력을 반점 기준으로 분리 +- [X] 금액을 원 단위 형식으로 변환 +#### 입력값 검증 관련 +- [X] 금액 입력값 검증 +- [X] 로또 입력값 검증 +- [X] 보너스 숫자 입력값 검증 \ No newline at end of file diff --git a/src/main/java/lotto/Application.java b/src/main/java/lotto/Application.java index d190922ba44..ef67c3b89af 100644 --- a/src/main/java/lotto/Application.java +++ b/src/main/java/lotto/Application.java @@ -1,7 +1,10 @@ package lotto; +import lotto.controller.LottoController; + public class Application { public static void main(String[] args) { - // TODO: 프로그램 구현 + LottoController lottoController = new LottoController(); + lottoController.run(); } } diff --git a/src/main/java/lotto/Lotto.java b/src/main/java/lotto/Lotto.java deleted file mode 100644 index 519793d1f73..00000000000 --- a/src/main/java/lotto/Lotto.java +++ /dev/null @@ -1,20 +0,0 @@ -package lotto; - -import java.util.List; - -public class Lotto { - private final List numbers; - - public Lotto(List numbers) { - validate(numbers); - this.numbers = numbers; - } - - private void validate(List numbers) { - if (numbers.size() != 6) { - throw new IllegalArgumentException(); - } - } - - // TODO: 추가 기능 구현 -} diff --git a/src/main/java/lotto/controller/ExceptionHandler.java b/src/main/java/lotto/controller/ExceptionHandler.java new file mode 100644 index 00000000000..7e7bbf7723a --- /dev/null +++ b/src/main/java/lotto/controller/ExceptionHandler.java @@ -0,0 +1,9 @@ +package lotto.controller; + +public class ExceptionHandler { + private static final String ERROR_MSG_HEADER = "[ERROR] "; + + public static void handleException(IllegalArgumentException e) { + System.out.println(ERROR_MSG_HEADER + e.toString()); + } +} diff --git a/src/main/java/lotto/controller/LottoController.java b/src/main/java/lotto/controller/LottoController.java new file mode 100644 index 00000000000..bd911adfffc --- /dev/null +++ b/src/main/java/lotto/controller/LottoController.java @@ -0,0 +1,61 @@ +package lotto.controller; + +import java.util.function.Supplier; +import lotto.domain.Ranks; +import lotto.domain.WinningLotto; +import lotto.dto.LottoInfos; +import lotto.service.LottoCreationService; +import lotto.service.WinningLottoRegisterService; +import lotto.view.CreatedLottosView; +import lotto.view.InputView; +import lotto.view.WinningStatusView; + +public class LottoController { + private LottoCreationService lottoCreationService; + private WinningLottoRegisterService winningLottoRegisterService; + + public LottoController() { + this.lottoCreationService = new LottoCreationService(new NumberPickingStrategyImpl()); + this.winningLottoRegisterService = new WinningLottoRegisterService(); + } + + public void run() { + CreatedLottosView.viewCreatedLottos(createLottos()); + + registerWinningLottoNumbers(); + registerBonusNumber(); + + WinningLotto winningLotto = winningLottoRegisterService.getWinningLotto(); + Ranks ranks = winningLotto.calcRanksOfGivenLottos(lottoCreationService.getLottos()); + WinningStatusView.viewWinningStatus( + ranks.getRankCountPairs(), + ranks.toTotalReward().calcProfitRate(lottoCreationService.getTotalMoney().getAmount())); + + } + + private WinningLotto registerBonusNumber() { + return (WinningLotto) repeatUntilNoInternalException( + () -> winningLottoRegisterService.registerBonusNumber(InputView.readBonusNumberInput())); + } + + private WinningLotto registerWinningLottoNumbers() { + return (WinningLotto) repeatUntilNoInternalException( + () -> winningLottoRegisterService.registerNumbers(InputView.readNumbersInput())); + } + + private LottoInfos createLottos() { + return (LottoInfos) repeatUntilNoInternalException( + () -> lottoCreationService.createLottos(InputView.readMoneyInput()) + ); + } + + private Object repeatUntilNoInternalException(Supplier supplier) { + while (true) { + try { + return supplier.get(); + } catch (IllegalArgumentException e) { + ExceptionHandler.handleException(e); + } + } + } +} diff --git a/src/main/java/lotto/controller/NumberPickingStrategy.java b/src/main/java/lotto/controller/NumberPickingStrategy.java new file mode 100644 index 00000000000..32358566c70 --- /dev/null +++ b/src/main/java/lotto/controller/NumberPickingStrategy.java @@ -0,0 +1,7 @@ +package lotto.controller; + +import java.util.List; + +public interface NumberPickingStrategy { + List pickLottoNumbers(); +} diff --git a/src/main/java/lotto/controller/NumberPickingStrategyImpl.java b/src/main/java/lotto/controller/NumberPickingStrategyImpl.java new file mode 100644 index 00000000000..dc086c5352e --- /dev/null +++ b/src/main/java/lotto/controller/NumberPickingStrategyImpl.java @@ -0,0 +1,12 @@ +package lotto.controller; + +import camp.nextstep.edu.missionutils.Randoms; +import java.util.List; +import lotto.controller.NumberPickingStrategy; + +public class NumberPickingStrategyImpl implements NumberPickingStrategy { + @Override + public List pickLottoNumbers() { + return Randoms.pickUniqueNumbersInRange(1, 45, 6); + } +} diff --git a/src/main/java/lotto/domain/Lotto.java b/src/main/java/lotto/domain/Lotto.java new file mode 100644 index 00000000000..3facd604445 --- /dev/null +++ b/src/main/java/lotto/domain/Lotto.java @@ -0,0 +1,51 @@ +package lotto.domain; + +import java.util.List; +import java.util.Objects; +import lotto.utils.ValidationUtil; + +public class Lotto { + private final List numbers; + + public Lotto(List numbers) { + validate(numbers); + this.numbers = numbers; + } + + public int calcCorrectNumbers(List winningNumbers) { + return (int) numbers.stream() + .filter(winningNumbers::contains) + .count(); + } + + private void validate(List numbers) { + ValidationUtil.validateHasSixNumbers(numbers); + ValidationUtil.validateNoDuplicatedNumbers(numbers); + } + + public List getNumbers() { + return numbers; + } + + + public boolean containsBonusNumber(int bonusNumber) { + return numbers.contains(bonusNumber); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + Lotto lotto = (Lotto) o; + return Objects.equals(numbers, lotto.numbers); + } + + @Override + public int hashCode() { + return Objects.hash(numbers); + } +} diff --git a/src/main/java/lotto/domain/Lottos.java b/src/main/java/lotto/domain/Lottos.java new file mode 100644 index 00000000000..38e0ef42b8a --- /dev/null +++ b/src/main/java/lotto/domain/Lottos.java @@ -0,0 +1,45 @@ +package lotto.domain; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import lotto.controller.NumberPickingStrategy; + +public class Lottos { + private List lottos; + + public Lottos() { + this.lottos = new ArrayList<>(); + } + + public Lottos(List lottos) { + this.lottos = lottos; + } + + public Lotto createLotto(NumberPickingStrategy numberPickingStrategy) { + Lotto lotto = new Lotto(makeLottoNumbers(numberPickingStrategy)); + lottos.add(lotto); + return lotto; + } + + private List makeLottoNumbers(NumberPickingStrategy numberPickingStrategy) { + List numbers = numberPickingStrategy.pickLottoNumbers(); + Collections.sort(numbers); + return numbers; + } + + public Ranks calRanksWithWinningNumbers(List numbers, int bonusNumber) { + List ranks = new ArrayList<>(); + for (Lotto lotto : lottos) { + ranks.add(Rank.calcRank( + lotto.calcCorrectNumbers(numbers), + lotto.containsBonusNumber(bonusNumber)) + ); + } + return new Ranks(ranks); + } + + public List getLottos() { + return lottos; + } +} diff --git a/src/main/java/lotto/domain/Rank.java b/src/main/java/lotto/domain/Rank.java new file mode 100644 index 00000000000..ab8b10f464d --- /dev/null +++ b/src/main/java/lotto/domain/Rank.java @@ -0,0 +1,57 @@ +package lotto.domain; + +import java.util.Arrays; +import java.util.List; +import lotto.utils.StringUtil; + +public enum Rank { + FIRST(6, 6, false, 2000000000L), + SECOND(5, 5, true, 30000000L), + THIRD(4, 5, false, 1500000L), + FOURTH(3, 4, false, 50000L), + FIFTH(2, 3, false, 5000L), + LAST(1, 2, false, 0L); + + private int correctCount; + private boolean needBonus; + private Long reward; + private int priority; + + Rank(int priority, int correctCount, boolean needBonus, Long reward) { + this.priority = priority; + this.correctCount = correctCount; + this.needBonus = needBonus; + this.reward = reward; + } + + public static Rank calcRank(int correctCount, boolean doesLottoContainBonus) { + return Arrays.stream(Rank.values()) + .filter(rank -> rank.isCorrectCountEqualTo(correctCount)) + .filter(rank -> rank.doesSatisfyBonusConditions(doesLottoContainBonus)) + .findAny() + .orElse(LAST); + } + + public static Long calcReward(List ranks) { + return ranks.stream() + .map(rank -> rank.reward) + .mapToLong(i -> i) + .sum(); + } + + public String toMessage() { + return String.format("%d개 일치 (%s원)", correctCount, StringUtil.toKoreanWon(reward)); + } + + private boolean doesSatisfyBonusConditions(boolean doesLottoContainBonus) { + return !needBonus || doesLottoContainBonus; + } + + private boolean isCorrectCountEqualTo(int correctCount) { + return this.correctCount == correctCount; + } + + public int getPriority() { + return priority; + } +} diff --git a/src/main/java/lotto/domain/RankCountPair.java b/src/main/java/lotto/domain/RankCountPair.java new file mode 100644 index 00000000000..8b16ea58343 --- /dev/null +++ b/src/main/java/lotto/domain/RankCountPair.java @@ -0,0 +1,38 @@ +package lotto.domain; + +import java.util.Objects; + +public class RankCountPair { + private final Rank RANK; + private final int COUNT; + + public RankCountPair(Rank RANK, int COUNT) { + this.RANK = RANK; + this.COUNT = COUNT; + } + + public Rank getRANK() { + return RANK; + } + + public int getCOUNT() { + return COUNT; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + RankCountPair that = (RankCountPair) o; + return COUNT == that.COUNT && RANK == that.RANK; + } + + @Override + public int hashCode() { + return Objects.hash(RANK, COUNT); + } +} diff --git a/src/main/java/lotto/domain/Ranks.java b/src/main/java/lotto/domain/Ranks.java new file mode 100644 index 00000000000..6c4b4300c8e --- /dev/null +++ b/src/main/java/lotto/domain/Ranks.java @@ -0,0 +1,51 @@ +package lotto.domain; + +import java.util.Arrays; +import java.util.Comparator; +import java.util.HashMap; +import java.util.List; +import java.util.Objects; +import java.util.stream.Stream; + +public class Ranks { + private final List ranks; + + public Ranks(List ranks) { + this.ranks = ranks.stream() + .sorted(Comparator.comparing(Rank::getPriority).reversed()) + .filter(rank -> !rank.equals(Rank.LAST)) + .toList(); + } + + public Reward toTotalReward() { + return new Reward(Rank.calcReward(ranks)); + } + + public List getRankCountPairs() { + return Stream.of(Rank.FIRST, Rank.SECOND, Rank.THIRD, Rank.FOURTH, Rank.FIFTH) + .map(rankToCompare -> new RankCountPair(rankToCompare, (int) ranks.stream().filter(rank -> rank == rankToCompare) + .count())) + .toList(); + } + + public List getRanks() { + return ranks; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + Ranks ranks1 = (Ranks) o; + return Objects.equals(ranks, ranks1.ranks); + } + + @Override + public int hashCode() { + return Objects.hash(ranks); + } +} diff --git a/src/main/java/lotto/domain/Reward.java b/src/main/java/lotto/domain/Reward.java new file mode 100644 index 00000000000..55189d5d232 --- /dev/null +++ b/src/main/java/lotto/domain/Reward.java @@ -0,0 +1,15 @@ +package lotto.domain; + +import lotto.utils.CalculationUtil; + +public class Reward { + private final Long reward; + + public Reward(Long reward) { + this.reward = reward; + } + + public double calcProfitRate(Long totalMoney) { + return CalculationUtil.toPercentage(totalMoney, reward); + } +} diff --git a/src/main/java/lotto/domain/WinningLotto.java b/src/main/java/lotto/domain/WinningLotto.java new file mode 100644 index 00000000000..4e8ef507ebb --- /dev/null +++ b/src/main/java/lotto/domain/WinningLotto.java @@ -0,0 +1,44 @@ +package lotto.domain; + +import java.util.List; +import lotto.utils.ValidationUtil; + +public class WinningLotto { + private List numbers; + private int bonusNumber; + + public Boolean saveNumbers(List numbers) { + validateNumbers(numbers); + this.numbers = numbers; + return true; + } + + public Boolean saveBonusNumber(int bonusNumber) { + validateBonusNumber(numbers, bonusNumber); + this.bonusNumber = bonusNumber; + return true; + } + + public Ranks calcRanksOfGivenLottos(Lottos lottos) { + return lottos.calRanksWithWinningNumbers(numbers, bonusNumber); + } + + public List getNumbers() { + return numbers; + } + + public int getBonusNumber() { + return bonusNumber; + } + + private void validateNumbers(List numbers) { + ValidationUtil.validateHasSixNumbers(numbers); + numbers.forEach(ValidationUtil::validateNumberInRange); + ValidationUtil.validateNoDuplicatedNumbers(numbers); + } + + private void validateBonusNumber(List numbers, int bonusNumber) { + ValidationUtil.validateNumberInRange(bonusNumber); + ValidationUtil.validateIsBonusNumberDuplicated(numbers, bonusNumber); + } +} diff --git a/src/main/java/lotto/dto/LottoInfo.java b/src/main/java/lotto/dto/LottoInfo.java new file mode 100644 index 00000000000..78afec4d60c --- /dev/null +++ b/src/main/java/lotto/dto/LottoInfo.java @@ -0,0 +1,15 @@ +package lotto.dto; + +import java.util.List; + +public class LottoInfo { + private final List numbers; + + public LottoInfo(List numbers) { + this.numbers = numbers; + } + + public List getNumbers() { + return numbers; + } +} diff --git a/src/main/java/lotto/dto/LottoInfos.java b/src/main/java/lotto/dto/LottoInfos.java new file mode 100644 index 00000000000..d3eaeef0b3e --- /dev/null +++ b/src/main/java/lotto/dto/LottoInfos.java @@ -0,0 +1,14 @@ +package lotto.dto; + +import java.util.List; + +public class LottoInfos { + private final List lottoInfos; + public LottoInfos(List lottoInfos) { + this.lottoInfos = lottoInfos; + } + + public List getLottoInfos() { + return lottoInfos; + } +} diff --git a/src/main/java/lotto/service/LottoCreationService.java b/src/main/java/lotto/service/LottoCreationService.java new file mode 100644 index 00000000000..6967901d26c --- /dev/null +++ b/src/main/java/lotto/service/LottoCreationService.java @@ -0,0 +1,60 @@ +package lotto.service; + +import java.util.ArrayList; +import java.util.List; +import lotto.controller.NumberPickingStrategy; +import lotto.domain.Lotto; +import lotto.domain.Lottos; +import lotto.dto.LottoInfo; +import lotto.dto.LottoInfos; +import lotto.utils.StringUtil; +import lotto.utils.ValidationUtil; + +public class LottoCreationService { + private Lottos lottos; + private Money totalMoney; + private final NumberPickingStrategy numberPickingStrategy; + + public LottoCreationService(NumberPickingStrategy numberPickingStrategy) { + this.numberPickingStrategy = numberPickingStrategy; + this.lottos = new Lottos(); + } + + public LottoInfos createLottos(String money) { + validate(money); + totalMoney = new Money(Integer.parseInt(money)); + return toLottoInfos(createLottoByCount(toLottoCount(money))); + } + + private LottoInfos toLottoInfos(List createdLottos) { + return new LottoInfos(createdLottos.stream() + .map(lotto -> new LottoInfo(lotto.getNumbers())) + .toList() + ); + } + private List createLottoByCount(int lottoCnt) { + List createdLottos = new ArrayList<>(); + for (int i = 0; i < lottoCnt; i++) { + Lotto createdLotto = lottos.createLotto(numberPickingStrategy); + createdLottos.add(createdLotto); + } + return createdLottos; + } + + private int toLottoCount(String money) { + return StringUtil.divideByOneThousand(money); + } + + private void validate(String money) { + ValidationUtil.validateIsMoneyDigit(money); + ValidationUtil.validateIsMoneyThousandUnit(money); + } + + public Lottos getLottos() { + return lottos; + } + + public Money getTotalMoney() { + return totalMoney; + } +} diff --git a/src/main/java/lotto/service/Money.java b/src/main/java/lotto/service/Money.java new file mode 100644 index 00000000000..002c0aa5f28 --- /dev/null +++ b/src/main/java/lotto/service/Money.java @@ -0,0 +1,13 @@ +package lotto.service; + +public class Money { + private final int amount; + + public Money(int amount) { + this.amount = amount; + } + + public long getAmount() { + return amount; + } +} diff --git a/src/main/java/lotto/service/WinningLottoRegisterService.java b/src/main/java/lotto/service/WinningLottoRegisterService.java new file mode 100644 index 00000000000..932aad13496 --- /dev/null +++ b/src/main/java/lotto/service/WinningLottoRegisterService.java @@ -0,0 +1,47 @@ +package lotto.service; + +import java.util.List; +import lotto.domain.WinningLotto; +import lotto.utils.ParseUtil; +import lotto.utils.StringUtil; +import lotto.utils.ValidationUtil; + +public class WinningLottoRegisterService { + private WinningLotto winningLotto; + + public WinningLottoRegisterService() { + this.winningLotto = new WinningLotto(); + } + + public WinningLotto registerNumbers(String numbers) { + validateNumbers(numbers); + winningLotto.saveNumbers(parseNumbers(numbers)); + return winningLotto; + } + + public WinningLotto registerBonusNumber(String bonusNumber) { + validateBonusNumber(bonusNumber); + winningLotto.saveBonusNumber(parseBonusNumber(bonusNumber)); + return winningLotto; + } + + private void validateBonusNumber(String bonusNumber) { + ValidationUtil.validateIsDigit(bonusNumber); + } + + private void validateNumbers(String numbers) { + ValidationUtil.validateIsAllDigit(StringUtil.splitByCommas(numbers)); + } + + private List parseNumbers(String numbers) { + return ParseUtil.parseNumbers(numbers); + } + + private int parseBonusNumber(String bonusNumber) { + return ParseUtil.parseNumber(bonusNumber); + } + + public WinningLotto getWinningLotto() { + return winningLotto; + } +} diff --git a/src/main/java/lotto/utils/CalculationUtil.java b/src/main/java/lotto/utils/CalculationUtil.java new file mode 100644 index 00000000000..581f3bfb828 --- /dev/null +++ b/src/main/java/lotto/utils/CalculationUtil.java @@ -0,0 +1,11 @@ +package lotto.utils; + +import java.math.BigDecimal; +import java.math.RoundingMode; + +public class CalculationUtil { + public static double toPercentage(Long boonMo, Long boonja) { + BigDecimal moneyTimesHundred = BigDecimal.valueOf(boonja).multiply(BigDecimal.valueOf(100)); + return moneyTimesHundred.divide(BigDecimal.valueOf(boonMo), 1, RoundingMode.HALF_UP).doubleValue(); + } +} diff --git a/src/main/java/lotto/utils/ParseUtil.java b/src/main/java/lotto/utils/ParseUtil.java new file mode 100644 index 00000000000..0dc73f96beb --- /dev/null +++ b/src/main/java/lotto/utils/ParseUtil.java @@ -0,0 +1,15 @@ +package lotto.utils; + +import java.util.List; + +public class ParseUtil { + public static List parseNumbers(String numbers) { + return StringUtil.splitByCommas(numbers).stream() + .map(Integer::parseInt) + .toList(); + } + + public static int parseNumber(String number) { + return Integer.parseInt(number); + } +} diff --git a/src/main/java/lotto/utils/StringUtil.java b/src/main/java/lotto/utils/StringUtil.java new file mode 100644 index 00000000000..c8f377ce104 --- /dev/null +++ b/src/main/java/lotto/utils/StringUtil.java @@ -0,0 +1,42 @@ +package lotto.utils; + +import java.text.NumberFormat; +import java.util.Arrays; +import java.util.List; +import java.util.Locale; + +public class StringUtil { + public static int divideByOneThousand(String number) { + return Integer.parseInt(number) / 1000; + } + + public static List splitByCommas(String string) { + return Arrays.stream(string.split(",")).toList(); + } + + public static String divideNumsByCommas(List numbers) { + StringBuilder stringBuilder = new StringBuilder(getFirstNum(numbers)); + + stringBuilder.append(getFirstNum(numbers)); + for (int i = 1; i < numbers.size(); i++) { + stringBuilder.append(", ").append(numbers.get(i)); + } + + return stringBuilder.toString(); + } + + private static Integer getFirstNum(List numbers) { + return numbers.get(0); + } + + public static String coverWithBrackets(String string) { + String HEADER = "["; + String FOOTER = "]"; + return HEADER + string + FOOTER; + } + + public static String toKoreanWon(Long value) { + NumberFormat koreanWonFormat = NumberFormat.getNumberInstance(Locale.KOREA); + return koreanWonFormat.format(value); + } +} diff --git a/src/main/java/lotto/utils/ValidationUtil.java b/src/main/java/lotto/utils/ValidationUtil.java new file mode 100644 index 00000000000..0316f6e98df --- /dev/null +++ b/src/main/java/lotto/utils/ValidationUtil.java @@ -0,0 +1,61 @@ +package lotto.utils; + +import java.util.List; + +public class ValidationUtil { + + public static final int MIN_POSSIBLE_LOTTO_NUMBER = 1; + public static final int MAX_POSSIBLE_LOTTO_NUMBER = 45; + + public static void validateIsMoneyThousandUnit(String number) { + if (Integer.parseInt(number) % 1000 != 0) { + throw new IllegalArgumentException("로또 구입 금액은 1000원 단위여야 합니다."); + } + } + + public static void validateIsMoneyDigit(String input) { + try { + Integer.parseInt(input); + } catch (NumberFormatException e) { + throw new IllegalArgumentException("로또 구입 금액은 숫자여야 합니다."); + } + } + + public static void validateNoDuplicatedNumbers(List numbers) { + if (numbers.size() != numbers.stream().distinct().count()) { + throw new IllegalArgumentException("로또 번호는 서로 중복되지 않은 숫자여야 합니다."); + } + } + + public static void validateIsBonusNumberDuplicated(List numbers, int bonusNumber) { + if (numbers.contains(bonusNumber)) { + throw new IllegalArgumentException("해당 번호는 보너스 번호로 사용할 수 없습니다. 이미 로또 번호 안에 존재합니다."); + } + } + + public static void validateHasSixNumbers(List numbers) { + if (numbers.size() != 6) { + throw new IllegalArgumentException("로또 번호의 개수는 6개여야 합니다."); + } + } + + public static void validateIsAllDigit(List inputs) { + for (String input : inputs) { + validateIsDigit(input); + } + } + + public static void validateIsDigit(String input) { + try { + Integer.parseInt(input); + } catch (NumberFormatException e) { + throw new IllegalArgumentException("로또 번호는 숫자여야 합니다."); + } + } + + public static void validateNumberInRange(int number) { + if (number < MIN_POSSIBLE_LOTTO_NUMBER || number > MAX_POSSIBLE_LOTTO_NUMBER) { + throw new IllegalArgumentException("로또 번호는 1과 45 사이여야 합니다."); + } + } +} diff --git a/src/main/java/lotto/view/CreatedLottosView.java b/src/main/java/lotto/view/CreatedLottosView.java new file mode 100644 index 00000000000..b416f814500 --- /dev/null +++ b/src/main/java/lotto/view/CreatedLottosView.java @@ -0,0 +1,34 @@ +package lotto.view; + +import java.util.List; +import lotto.dto.LottoInfo; +import lotto.dto.LottoInfos; +import lotto.utils.StringUtil; + +public class CreatedLottosView { + private static final String HEADER_FORM = "\n%d개를 구매했습니다.\n"; + + public static void viewCreatedLottos(LottoInfos lottoInfos) { + String header = buildHeader(lottoInfos); + String content = buildContent(lottoInfos); + System.out.println(header + content); + } + + private static String buildHeader(LottoInfos lottoInfos) { + return String.format(HEADER_FORM, lottoInfos.getLottoInfos().size()); + } + + private static String buildContent(LottoInfos lottoInfos) { + StringBuilder stringBuilder = new StringBuilder(); + lottoInfos.getLottoInfos().stream() + .map(LottoInfo::getNumbers) + .forEach((numbers) -> stringBuilder.append(toContent(numbers)).append("\n") + ); + return stringBuilder.toString(); + } + + private static String toContent(List numbers) { + String content = StringUtil.divideNumsByCommas(numbers); + return StringUtil.coverWithBrackets(content); + } +} diff --git a/src/main/java/lotto/view/InputView.java b/src/main/java/lotto/view/InputView.java new file mode 100644 index 00000000000..464093f1b34 --- /dev/null +++ b/src/main/java/lotto/view/InputView.java @@ -0,0 +1,24 @@ +package lotto.view; + +import camp.nextstep.edu.missionutils.Console; + +public class InputView { + private static final String MONEY_INPUT_MESSAGE = "구입금액을 입력해 주세요."; + private static final String NUMBERS_INPUT_MESSAGE = "당첨 번호를 입력해 주세요."; + private static final String BONUS_NUMBER_INPUT_MESSAGE = "보너스 번호를 입력해 주세요."; + + public static String readMoneyInput() { + System.out.println(MONEY_INPUT_MESSAGE); + return Console.readLine(); + } + + public static String readNumbersInput() { + System.out.println(NUMBERS_INPUT_MESSAGE); + return Console.readLine(); + } + + public static String readBonusNumberInput() { + System.out.println(BONUS_NUMBER_INPUT_MESSAGE); + return Console.readLine(); + } +} diff --git a/src/main/java/lotto/view/WinningStatusView.java b/src/main/java/lotto/view/WinningStatusView.java new file mode 100644 index 00000000000..2a075775945 --- /dev/null +++ b/src/main/java/lotto/view/WinningStatusView.java @@ -0,0 +1,27 @@ +package lotto.view; + +import java.util.List; +import lotto.domain.Rank; +import lotto.domain.RankCountPair; +import lotto.domain.Reward; + +public class WinningStatusView { + private static final String STATUS_HEADER = "당첨 통계"; + private static final String SEPERATION_LINE = "---"; + private static final String RANK_COUNT_SEPARATOR = " - "; + private static final String COUNT_UNIT = "개"; + private static final String PROFIT_RATE_HEADER = "총 수익률은 "; + private static final String PROFIT_RATE_FOOTER = "%입니다."; + + public static void viewWinningStatus(List rankCountPairs, double profitRate) { + System.out.println(STATUS_HEADER); + System.out.println(SEPERATION_LINE); + rankCountPairs.forEach((rankCountPair) -> + viewWinningStatusEachRank(rankCountPair.getRANK(), rankCountPair.getCOUNT())); + System.out.println(PROFIT_RATE_HEADER + profitRate + PROFIT_RATE_FOOTER); + } + + private static void viewWinningStatusEachRank(Rank rank, int count) { + System.out.println(rank.toMessage() + RANK_COUNT_SEPARATOR + count + COUNT_UNIT); + } +} diff --git a/src/test/java/lotto/LottoTest.java b/src/test/java/lotto/LottoTest.java deleted file mode 100644 index 9f5dfe7eb83..00000000000 --- a/src/test/java/lotto/LottoTest.java +++ /dev/null @@ -1,27 +0,0 @@ -package lotto; - -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; - -import java.util.List; - -import static org.assertj.core.api.Assertions.assertThatThrownBy; - -class LottoTest { - @DisplayName("로또 번호의 개수가 6개가 넘어가면 예외가 발생한다.") - @Test - void createLottoByOverSize() { - assertThatThrownBy(() -> new Lotto(List.of(1, 2, 3, 4, 5, 6, 7))) - .isInstanceOf(IllegalArgumentException.class); - } - - @DisplayName("로또 번호에 중복된 숫자가 있으면 예외가 발생한다.") - @Test - void createLottoByDuplicatedNumber() { - // TODO: 이 테스트가 통과할 수 있게 구현 코드 작성 - assertThatThrownBy(() -> new Lotto(List.of(1, 2, 3, 4, 5, 5))) - .isInstanceOf(IllegalArgumentException.class); - } - - // 아래에 추가 테스트 작성 가능 -} \ No newline at end of file diff --git a/src/test/java/lotto/domain/LottoCreationServiceTest.java b/src/test/java/lotto/domain/LottoCreationServiceTest.java new file mode 100644 index 00000000000..925e48682f5 --- /dev/null +++ b/src/test/java/lotto/domain/LottoCreationServiceTest.java @@ -0,0 +1,36 @@ +package lotto.domain; + +import static org.assertj.core.api.Assertions.*; + +import java.util.Arrays; +import java.util.List; +import lotto.dto.LottoInfos; +import lotto.service.LottoCreationService; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +public class LottoCreationServiceTest { + private LottoCreationService lottoCreationService; + @BeforeEach + void beforeEach() { + lottoCreationService = new LottoCreationService(() -> Arrays.asList(1, 2, 3, 4, 5, 6)); + } + + @DisplayName("지불금액을_1000원으로_나눈_수만큼_로또_생성") + @Test + void 지불금액을_1000원으로_나눈_수만큼_로또_생성() { + LottoInfos createdLottoInfos = lottoCreationService.createLottos("2000"); + + assertThat(createdLottoInfos.getLottoInfos().size()).isEqualTo(2); + } + + @Test + void 지불금액이_1000원_단위가_아니면_예외를_던진다() { + ; + + assertThatIllegalArgumentException() + .isThrownBy(() -> lottoCreationService.createLottos("2222")) + .withMessage("로또 구입 금액은 1000원 단위여야 합니다."); + } +} diff --git a/src/test/java/lotto/domain/LottoTest.java b/src/test/java/lotto/domain/LottoTest.java new file mode 100644 index 00000000000..0a5e7dbb0b0 --- /dev/null +++ b/src/test/java/lotto/domain/LottoTest.java @@ -0,0 +1,49 @@ +package lotto.domain; + +import lotto.domain.Lotto; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +class LottoTest { + @DisplayName("로또 번호의 개수가 6개가 넘어가면 예외가 발생한다.") + @Test + void createLottoByOverSize() { + assertThatThrownBy(() -> new Lotto(List.of(1, 2, 3, 4, 5, 6, 7))) + .isInstanceOf(IllegalArgumentException.class); + } + + @DisplayName("로또 번호에 중복된 숫자가 있으면 예외가 발생한다.") + @Test + void createLottoByDuplicatedNumber() { + // TODO: 이 테스트가 통과할 수 있게 구현 코드 작성 + assertThatThrownBy(() -> new Lotto(List.of(1, 2, 3, 4, 5, 5))) + .isInstanceOf(IllegalArgumentException.class); + } + + @DisplayName("우승 로또 숫자들과 비교해서 맞은 숫자 개수를 알려준다.") + @Test + void 우승_로또_숫자와_비교후_맞은_개수_반환() { + List winnigLottoNumbers = List.of(1, 2, 3, 4, 5, 6); + Lotto allCorrectLotto = new Lotto(List.of(1, 2, 3, 4, 5, 6)); + Lotto allWrongLotto = new Lotto(List.of(7, 8, 9, 10, 11, 12)); + + assertThat(allCorrectLotto.calcCorrectNumbers(winnigLottoNumbers)).isEqualTo(6); + assertThat(allWrongLotto.calcCorrectNumbers(winnigLottoNumbers)).isEqualTo(0); + } + + @DisplayName("보너스 번호 포함 유무를 알려준다.") + @Test + void 보너스_번호_포함_유무_반환() { + int BONUS_NUMBER = 7; + Lotto BonusLotto = new Lotto(List.of(1, 2, 3, 4, 5, 6)); + Lotto noBonusLotto = new Lotto(List.of(1, 2, 3, 4, 5, 7)); + + assertThat(BonusLotto.containsBonusNumber(BONUS_NUMBER)).isFalse(); + assertThat(noBonusLotto.containsBonusNumber(BONUS_NUMBER)).isTrue(); + } +} \ No newline at end of file diff --git a/src/test/java/lotto/domain/LottosTest.java b/src/test/java/lotto/domain/LottosTest.java new file mode 100644 index 00000000000..7a37c3ffb16 --- /dev/null +++ b/src/test/java/lotto/domain/LottosTest.java @@ -0,0 +1,47 @@ +package lotto.domain; + +import java.util.Arrays; +import java.util.List; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.*; + +public class LottosTest { + @DisplayName("로또 한개 생성 저장") + @Test + void 로또_한개_생성_저장() { + Lottos lottos = new Lottos(); + + Lotto lotto = lottos.createLotto(() -> Arrays.asList(1, 2, 3, 4, 5, 6)); + + assertThat(lotto).isEqualTo(new Lotto(List.of(1, 2, 3, 4, 5, 6))); + assertThat(lottos.getLottos().size()).isEqualTo(1); + } + @DisplayName("로또를 생성할 때 숫자들을 오름차순 정렬해서 저장") + @Test + void 로또숫자들을_오름차순_정렬후_저장() { + Lottos lottos = new Lottos(); + + Lotto lotto = lottos.createLotto(() -> Arrays.asList(6, 5, 4, 3, 2, 1)); + + assertThat(lotto).isEqualTo(new Lotto(List.of(1, 2, 3, 4, 5, 6))); + assertThat(lottos.getLottos().size()).isEqualTo(1); + } + + @DisplayName("개별 로또의 등수를 모아서 우선순위 정렬 후 반환") + @Test + void 각_로또의_등수를_모아_우선순위_정렬후_반환() { + List winnigNumbers = List.of(1, 2, 3, 4, 5, 6); + int bonusNumber = 7; + + Lottos lottos = new Lottos(List.of( + new Lotto(List.of(1, 2, 3, 4, 5, 7)), + new Lotto(List.of(1, 2, 3, 4, 5, 6)), + new Lotto(List.of(40, 41, 42, 43, 44, 45))) + ); + + assertThat(lottos.calRanksWithWinningNumbers(winnigNumbers, bonusNumber)) + .isEqualTo(new Ranks(List.of(Rank.FIRST, Rank.SECOND, Rank.LAST))); + } +} diff --git a/src/test/java/lotto/domain/RankTest.java b/src/test/java/lotto/domain/RankTest.java new file mode 100644 index 00000000000..5ee27399229 --- /dev/null +++ b/src/test/java/lotto/domain/RankTest.java @@ -0,0 +1,28 @@ +package lotto.domain; + +import static org.assertj.core.api.Assertions.*; + +import java.util.List; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +class RankTest { + @Test + void 맞은_숫자개수와_보너스번호_유무를_통해_등수를_계산한다() { + assertThat(Rank.calcRank(6, true)).isEqualTo(Rank.FIRST); + assertThat(Rank.calcRank(6, false)).isEqualTo(Rank.FIRST); + assertThat(Rank.calcRank(5, true)).isEqualTo(Rank.SECOND); + assertThat(Rank.calcRank(5, false)).isEqualTo(Rank.THIRD); + assertThat(Rank.calcRank(1, true)).isEqualTo(Rank.LAST); + } + @DisplayName("총 상금을 계산한다.") + @Test + void 총_상금_계산() { + assertThat(Rank.calcReward(List.of(Rank.FIRST, Rank.SECOND, Rank.LAST))).isEqualTo(2030000000L); + } + + @Test + void toMessage() { + assertThat(Rank.FIFTH.toMessage()).isEqualTo("3개 일치 (5,000원)"); + } +} diff --git a/src/test/java/lotto/domain/RanksTest.java b/src/test/java/lotto/domain/RanksTest.java new file mode 100644 index 00000000000..6dcf2ecc93a --- /dev/null +++ b/src/test/java/lotto/domain/RanksTest.java @@ -0,0 +1,40 @@ +package lotto.domain; + +import static org.assertj.core.api.Assertions.*; + +import java.util.List; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +class RanksTest { + + @DisplayName("등수를 우선순위 정렬해서 저장한다.") + @Test + void 우선순위_정렬후_저장() { + Ranks ranks = new Ranks(List.of(Rank.SECOND, Rank.FIRST)); + + assertThat(ranks.getRanks()).isEqualTo(List.of(Rank.FIRST, Rank.SECOND)); + } + + @DisplayName("마지막_등수는_제외하고_반환") + @Test + void 마지막_등수는_제외하고_반환() { + Ranks ranks = new Ranks(List.of(Rank.LAST, Rank.FIRST)); + + assertThat(ranks.getRanks()).isEqualTo(List.of(Rank.FIRST)); + } + + @DisplayName("당첨된 등수를 개수와 함께 반환") + @Test + void 당첨된_등수를_개수와_함께_반환() { + Ranks ranks = new Ranks(List.of(Rank.LAST, Rank.FIRST)); + + assertThat(ranks.getRankCountPairs()) + .isEqualTo(List.of(new RankCountPair(Rank.FIRST, 1), + new RankCountPair(Rank.SECOND, 0), + new RankCountPair(Rank.THIRD, 0), + new RankCountPair(Rank.FOURTH, 0), + new RankCountPair(Rank.FIFTH, 0)) + ); + } +} diff --git a/src/test/java/lotto/domain/RewardTest.java b/src/test/java/lotto/domain/RewardTest.java new file mode 100644 index 00000000000..500ae7b600f --- /dev/null +++ b/src/test/java/lotto/domain/RewardTest.java @@ -0,0 +1,15 @@ +package lotto.domain; + +import static org.assertj.core.api.Assertions.*; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +class RewardTest { + @DisplayName("수익률을 소수점 둘째자리수로 반올림하여 반환") + @Test + void calcProfitRate() { + Reward reward = new Reward(5000L); + assertThat(reward.calcProfitRate(7000L)).isEqualTo(71.4); + } +} \ No newline at end of file diff --git a/src/test/java/lotto/domain/WinningLottoRegisterServiceTest.java b/src/test/java/lotto/domain/WinningLottoRegisterServiceTest.java new file mode 100644 index 00000000000..2e4cfda3230 --- /dev/null +++ b/src/test/java/lotto/domain/WinningLottoRegisterServiceTest.java @@ -0,0 +1,42 @@ +package lotto.domain; + +import static org.assertj.core.api.Assertions.*; + +import java.util.List; +import lotto.service.WinningLottoRegisterService; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +class WinningLottoRegisterServiceTest { + private WinningLottoRegisterService service; + private WinningLotto winningLotto; + + @BeforeEach + void setUp() { + service = new WinningLottoRegisterService(); + } + + @Test + @DisplayName("당첨번호 등록") + void 당첨번호_등록() { + winningLotto = service.registerNumbers("1,2,3,4,5,6"); + assertThat(winningLotto.getNumbers()).isEqualTo(List.of(1, 2, 3, 4, 5, 6)); + } + + @Test + @DisplayName("보너스 번호 등록") + void 보너스_번호_등록() { + winningLotto = service.registerNumbers("1,2,3,4,5,6"); + winningLotto = service.registerBonusNumber("7"); + assertThat(winningLotto.getBonusNumber()).isEqualTo(7); + } + + @Test + @DisplayName("로또번호가 숫자가 아닌 경우 예외 처리") + void 로또번호가_숫자가_아닐시_예외처리() { + assertThatIllegalArgumentException() + .isThrownBy(() -> service.registerNumbers("a,b,c,d,e,f")) + .withMessage("로또 번호는 숫자여야 합니다."); + } +} diff --git a/src/test/java/lotto/domain/WinningLottoTest.java b/src/test/java/lotto/domain/WinningLottoTest.java new file mode 100644 index 00000000000..d7cfea683a0 --- /dev/null +++ b/src/test/java/lotto/domain/WinningLottoTest.java @@ -0,0 +1,71 @@ +package lotto.domain; + +import static org.assertj.core.api.Assertions.*; + +import java.util.List; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +class WinningLottoTest { + private WinningLotto winningLotto; + private final List ALL_CORRECT = List.of(1, 2, 3, 4, 5, 6); + private final List FIVE_CORRECT_ONE_BONUS = List.of(1, 2, 3, 4, 5, 7); + private final List ALL_WRONG = List.of(40, 41, 42, 43, 44, 45); + private final int BONUS_NUMBER = 7; + private final Lotto FIRST_PLACE = new Lotto(ALL_CORRECT); + private final Lotto SECOND_PLACE = new Lotto(FIVE_CORRECT_ONE_BONUS); + private final Lotto LAST_PLACE = new Lotto(ALL_WRONG); + @BeforeEach + void setUp() { + winningLotto = new WinningLotto(); + } + + @Test + @DisplayName("로또_등수를_모아서_반환") + void 로또_등수를_모아서_반환() { + winningLotto.saveNumbers(ALL_CORRECT); + winningLotto.saveBonusNumber(BONUS_NUMBER); + + Lottos lottos = new Lottos(List.of(FIRST_PLACE, SECOND_PLACE, LAST_PLACE)); + assertThat(winningLotto.calcRanksOfGivenLottos(lottos)) + .isEqualTo(new Ranks(List.of(Rank.FIRST, Rank.SECOND, Rank.LAST))); + } + + @Test + @DisplayName("로또번호가 1-45사이 숫자가 아니면 예외를 던진다.") + void 번호는_1과_45_사이() { + assertThatIllegalArgumentException() + .isThrownBy(() -> winningLotto.saveNumbers(List.of(100, 101, 102, 103, 104, 105))) + .withMessage("로또 번호는 1과 45 사이여야 합니다."); + + assertThatIllegalArgumentException() + .isThrownBy(() -> winningLotto.saveBonusNumber(0)) + .withMessage("로또 번호는 1과 45 사이여야 합니다."); + } + + @Test + @DisplayName("로또번호가 6개가 아니면 예외를 던진다.") + void 로또번호가_6개가_아니면_예외() { + assertThatIllegalArgumentException() + .isThrownBy(() -> winningLotto.saveNumbers(List.of(1, 2, 3, 4, 5, 6, 7))) + .withMessage("로또 번호의 개수는 6개여야 합니다."); + } + + @Test + @DisplayName("로또번호에 중복된 숫자가 있으면 예외를 던진다.") + void 중복된_숫자_존재시_예외() { + assertThatIllegalArgumentException() + .isThrownBy(() -> winningLotto.saveNumbers(List.of(1, 2, 3, 4, 5, 5))) + .withMessage("로또 번호는 서로 중복되지 않은 숫자여야 합니다."); + } + + @Test + @DisplayName("보너스 번호가 로또 번호와 중복되면 예외를 던진다.") + void 중복된_보너스번호일시_예외를() { + winningLotto.saveNumbers(List.of(1, 2, 3, 4, 5, 6)); + assertThatIllegalArgumentException() + .isThrownBy(() -> winningLotto.saveBonusNumber(1)) + .withMessage("해당 번호는 보너스 번호로 사용할 수 없습니다. 이미 로또 번호 안에 존재합니다."); + } +} diff --git a/src/test/java/lotto/utils/CalculationUtilTest.java b/src/test/java/lotto/utils/CalculationUtilTest.java new file mode 100644 index 00000000000..5efa14801ae --- /dev/null +++ b/src/test/java/lotto/utils/CalculationUtilTest.java @@ -0,0 +1,12 @@ +package lotto.utils; + +import static org.assertj.core.api.Assertions.*; + +import org.junit.jupiter.api.Test; + +class CalculationUtilTest { + @Test + void toPercentage() { + assertThat(CalculationUtil.toPercentage(7000L, 5000L)).isEqualTo(71.4); + } +} diff --git a/src/test/java/lotto/utils/ParseUtilTest.java b/src/test/java/lotto/utils/ParseUtilTest.java new file mode 100644 index 00000000000..0e59fa86f2d --- /dev/null +++ b/src/test/java/lotto/utils/ParseUtilTest.java @@ -0,0 +1,18 @@ +package lotto.utils; + +import static org.assertj.core.api.Assertions.*; + +import java.util.List; +import org.junit.jupiter.api.Test; + +class ParseUtilTest { + @Test + void parseNumbers() { + assertThat(ParseUtil.parseNumbers("1,2,3,4,5,6")).isEqualTo(List.of(1, 2, 3, 4, 5, 6)); + } + + @Test + void parseNumber() { + assertThat(ParseUtil.parseNumber("1")).isEqualTo(1); + } +} diff --git a/src/test/java/lotto/utils/StringUtilTest.java b/src/test/java/lotto/utils/StringUtilTest.java new file mode 100644 index 00000000000..51a7d110afb --- /dev/null +++ b/src/test/java/lotto/utils/StringUtilTest.java @@ -0,0 +1,33 @@ +package lotto.utils; + +import static org.assertj.core.api.Assertions.*; + +import java.util.List; +import org.junit.jupiter.api.Test; + +class StringUtilTest { + @Test + void divideByOneThousand() { + assertThat(StringUtil.divideByOneThousand("2000")).isEqualTo(2); + } + + @Test + void splitByCommas() { + assertThat(StringUtil.splitByCommas("1,2,3,4,5,6")).isEqualTo(List.of("1", "2", "3", "4", "5", "6")); + } + + @Test + void divideNumsByCommas() { + assertThat(StringUtil.divideNumsByCommas(List.of(1, 2, 3, 4, 5, 6))).isEqualTo("1, 2, 3, 4, 5, 6"); + } + + @Test + void coverWithBrackets() { + assertThat(StringUtil.coverWithBrackets("1, 2, 3, 4, 5, 6")).isEqualTo("[1, 2, 3, 4, 5, 6]"); + } + + @Test + void toKoreanWon() { + assertThat(StringUtil.toKoreanWon(1000L)).isEqualTo("1,000"); + } +} \ No newline at end of file diff --git a/src/test/java/lotto/utils/ValidationUtilTest.java b/src/test/java/lotto/utils/ValidationUtilTest.java new file mode 100644 index 00000000000..d5d09032618 --- /dev/null +++ b/src/test/java/lotto/utils/ValidationUtilTest.java @@ -0,0 +1,69 @@ +package lotto.utils; + +import static org.junit.jupiter.api.Assertions.*; +import static org.assertj.core.api.Assertions.*; + +import java.util.List; +import org.junit.jupiter.api.Test; + +class ValidationUtilTest { + @Test + void validateThousandUnit() { + assertThatIllegalArgumentException() + .isThrownBy(() -> ValidationUtil.validateIsMoneyThousandUnit("2222")) + .withMessage("로또 구입 금액은 1000원 단위여야 합니다."); + } + + @Test + void validateIsMoneyDigit() { + assertThatIllegalArgumentException() + .isThrownBy(() -> ValidationUtil.validateIsMoneyDigit("notDigit")) + .withMessage("로또 구입 금액은 숫자여야 합니다."); + } + + @Test + void validateNoDuplicatedNumbers() { + assertThatIllegalArgumentException() + .isThrownBy(() -> ValidationUtil.validateNoDuplicatedNumbers(List.of(1, 2, 3, 4, 5, 5))) + .withMessage("로또 번호는 서로 중복되지 않은 숫자여야 합니다."); + } + + @Test + void validateIsBonusNumberDuplicated() { + assertThatIllegalArgumentException() + .isThrownBy(() -> ValidationUtil.validateIsBonusNumberDuplicated(List.of(1, 2, 3, 4, 5, 6), 6)) + .withMessage("해당 번호는 보너스 번호로 사용할 수 없습니다. 이미 로또 번호 안에 존재합니다."); + } + + @Test + void validateHasSixNumbers() { + assertThatIllegalArgumentException() + .isThrownBy(() -> ValidationUtil.validateHasSixNumbers(List.of(1, 2, 3, 4, 5, 6, 7))) + .withMessage("로또 번호의 개수는 6개여야 합니다."); + } + + @Test + void validateIsAllDigit() { + assertThatIllegalArgumentException() + .isThrownBy(() -> ValidationUtil.validateIsAllDigit(List.of("a", "1", "2", "3", "4", "5"))) + .withMessage("로또 번호는 숫자여야 합니다."); + } + + @Test + void validateIsDigit() { + assertThatIllegalArgumentException() + .isThrownBy(() -> ValidationUtil.validateIsDigit("a")) + .withMessage("로또 번호는 숫자여야 합니다."); + } + + @Test + void validateNumberRange() { + assertThatIllegalArgumentException() + .isThrownBy(() -> ValidationUtil.validateNumberInRange(46)) + .withMessage("로또 번호는 1과 45 사이여야 합니다."); + + assertThatIllegalArgumentException() + .isThrownBy(() -> ValidationUtil.validateNumberInRange(0)) + .withMessage("로또 번호는 1과 45 사이여야 합니다."); + } +}