Skip to content
Open
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# 자동차 경주 미션
# 로또 미션

## 참고링크 및 저장소

Expand Down
94 changes: 94 additions & 0 deletions src/__tests__/lotto.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
import Lotto from '../domain/Lotto.js';
import { lottoValidations } from '../validations/lotto.js';
import LOTTO_TYPE from '../constants/lottoType.js';

describe('로또 테스트', () => {
test('로또 번호는 [당첨, 티켓] 두가지 유형을 가질 수 있다.', () => {
expect(
() =>
new Lotto({
type: 'otherType',
numbers: [1, 2, 3, 4, 5, 6],
}),
).toThrow(lottoValidations.lottoType.errorMessage);
});

test('로또 번호는 6개여야 한다.', () => {
expect(
() =>
new Lotto({
type: LOTTO_TYPE.TICKET,
numbers: [1, 2, 3, 4, 5],
}),
).toThrow(lottoValidations.lottoNumbersLength.errorMessage);
});

test('당첨 로또 번호는 보너스 번호를 가져야 한다.', () => {
expect(
() =>
new Lotto({
type: LOTTO_TYPE.WINNING,
numbers: [1, 2, 3, 4, 5, 6],
}),
).toThrow(lottoValidations.winningLottoHasBonus.errorMessage);
});

test('티켓 로또 번호는 보너스 번호가 없어야 한다.', () => {
expect(
() =>
new Lotto({
type: LOTTO_TYPE.TICKET,
numbers: [1, 2, 3, 4, 5, 6],
bonusNumber: 7,
}),
).toThrow(lottoValidations.ticketLottoBonusNull.errorMessage);
});

test('로또 번호는 각각 달라야 한다.', () => {
expect(
() =>
new Lotto({
type: LOTTO_TYPE.WINNING,
numbers: [1, 2, 3, 4, 5, 6],
bonusNumber: 6,
}),
).toThrow(lottoValidations.lottoEachUnique.errorMessage);

expect(
() =>
new Lotto({
type: LOTTO_TYPE.TICKET,
numbers: [1, 2, 3, 3, 5, 6],
}),
).toThrow(lottoValidations.lottoEachUnique.errorMessage);
});

test('모든 로또 번호는 정수여야 한다.', () => {
expect(
() =>
new Lotto({
type: LOTTO_TYPE.TICKET,
numbers: [1, 2, 3, 4, 5, '6'],
}),
).toThrow(lottoValidations.lottoInteger.errorMessage);
});

test('모든 로또 번호는 1 이상 45 이하여야 한다.', () => {
expect(
() =>
new Lotto({
type: LOTTO_TYPE.WINNING,
numbers: [1, 2, 3, 4, 5, 45],
bonusNumber: 46,
}),
).toThrow(lottoValidations.lottoRange.errorMessage);

expect(
() =>
new Lotto({
type: LOTTO_TYPE.TICKET,
numbers: [0, 1, 2, 3, 4, 5],
}),
Comment on lines +79 to +91

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

중복을 줄이는 용도로 팩토리 메서드를 만들어도 괜찮을듯합니다!

function createWinningLotto(numbers, bonusNumbers) { ... }
function createLotto(numbers) { ... }

).toThrow(lottoValidations.lottoRange.errorMessage);
});
});
72 changes: 72 additions & 0 deletions src/__tests__/lottoMatcher.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import { createLottoTicket, createWinningLotto } from '../domain/Lotto.js';
import LottoMatcher from '../domain/LottoMatcher.js';
import { lottoMatcherValidations } from '../validations/lottoMatcher.js';
import LOTTO_TYPE from '../constants/lottoType.js';

describe('로또 번호 일치여부 계산 테스트', () => {
test('유효한 당첨 로또로만 계산할 수 있다.', () => {
const validLottoTicket = createLottoTicket([11, 12, 13, 14, 15, 16]);

expect(() =>
LottoMatcher.matchLotto({
winningLotto: { type: LOTTO_TYPE.WINNING, numbers: [1, 2, 3, 4, 5, 6], bonusNumber: 8 },
lottoTickets: [validLottoTicket],
}),
).toThrow(lottoMatcherValidations.validWinningLotto.errorMessage);

expect(() =>
LottoMatcher.matchLotto({
winningLotto: validLottoTicket,
lottoTickets: [validLottoTicket],
}),
).toThrow(lottoMatcherValidations.validWinningLotto.errorMessage);
});

test('유효한 로또 티켓들만 계산할 수 있다.', () => {
const validWinningLotto = createWinningLotto([1, 2, 3, 4, 5, 6], 7);

expect(() =>
LottoMatcher.matchLotto({
winningLotto: validWinningLotto,
lottoTickets: [{ type: LOTTO_TYPE.TICKET, numbers: [1, 2, 3, 4, 5, 6] }],
}),
);

expect(() =>
LottoMatcher.matchLotto({
winningLotto: validWinningLotto,
lottoTickets: [validWinningLotto],
}),
).toThrow(lottoMatcherValidations.validLottoTickets.errorMessage);
});

test('로또 티켓들의 [당첨 번호 일치 개수, 보너스 번호 일치 여부]를 반환한다.', () => {
const winningLotto = createWinningLotto([1, 2, 3, 4, 5, 6], 7);
const lottoTicket1 = createLottoTicket([1, 2, 3, 14, 15, 16]); // 3, false
const lottoTicket2 = createLottoTicket([11, 12, 13, 14, 15, 16]); // 0, false
const lottoTicket3 = createLottoTicket([1, 2, 3, 4, 7, 8]); // 4, true

expect(
LottoMatcher.matchLotto({
winningLotto,
lottoTickets: [lottoTicket1, lottoTicket2, lottoTicket3],
}),
).toEqual([
{
lotto: lottoTicket1,
matchCount: 3,
bonusMatch: false,
},
{
lotto: lottoTicket2,
matchCount: 0,
bonusMatch: false,
},
{
lotto: lottoTicket3,
matchCount: 4,
bonusMatch: true,
},
]);
});
});
27 changes: 27 additions & 0 deletions src/__tests__/lottoPayment.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import LottoPayment from '../domain/LottoPayment.js';
import { lottoPaymentValidations } from '../validations/lottoPayment.js';

describe('로또 결제 테스트', () => {
test('로또 결제 금액은 정수여야 한다.', () => {
expect(() =>
LottoPayment.createLottoTickets({
payAmount: '1000',
}),
).toThrow(lottoPaymentValidations.payAmountInteger.errorMessage);
});

test('로또 결제는 1000원 단위로만 가능하다.', () => {
expect(() =>
LottoPayment.createLottoTickets({
payAmount: 1100,
}),
).toThrow(lottoPaymentValidations.payAmountUnit1000.errorMessage);
});

test('1000원 당 1장의 로또 티켓을 발행해야 한다.', () => {
const { lottoTickets } = LottoPayment.createLottoTickets({
payAmount: 8000,
})
expect(lottoTickets.length).toBe(8);
});
});
145 changes: 145 additions & 0 deletions src/__tests__/lottoSystem.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
import LottoSystem from '../domain/LottoSystem.js';
import { lottoSystemValidations } from '../validations/lottoSystem.js';
import createLottoNumbers from '../domain/createLottoNumbers.js';

vi.mock('../domain/createLottoNumbers');

describe('로또 시스템 테스트', async () => {
const originalCreateLottoNumbers = await vi
.importActual('../domain/createLottoNumbers')
.then((module) => module.default);

beforeEach(() => {
createLottoNumbers.mockReset();
createLottoNumbers.mockImplementation(originalCreateLottoNumbers);
});

test('로또 시스템은 순위가 올바르게 매겨진 랭킹 규칙만 사용한다.', () => {
const rankingRuleHasInvalidRank1 = [
{
matchCount: 3,
bonusMatch: false,
profit: 1000,
rank: -1,
},
];
expect(
() =>
new LottoSystem({
rankingRule: rankingRuleHasInvalidRank1,
}),
).toThrow(lottoSystemValidations.validRanks.errorMessage);

const rankingRuleHasInvalidRank2 = [
{
matchCount: 3,
bonusMatch: false,
profit: 1000,
rank: 3,
},
];
expect(
() =>
new LottoSystem({
rankingRule: rankingRuleHasInvalidRank2,
}),
).toThrow(lottoSystemValidations.hasAllRanks.errorMessage);
});

test('로또 시스템은 유효한 matchCount, bonus, profit 이 포함된 랭킹 규칙만 사용한다.', () => {
const rankingRuleHasInvalidMatchCount = [
{
bonusMatch: true,
profit: 1000,
rank: 1,
matchCount: 100,
},
];
expect(
() =>
new LottoSystem({
rankingRule: rankingRuleHasInvalidMatchCount,
}),
).toThrow(lottoSystemValidations.validMatchCounts.errorMessage);

const rankingRuleHasInvalidBonusMatch = [
{
profit: 1000,
rank: 1,
matchCount: 3,
bonusMatch: 'false',
},
];
expect(
() =>
new LottoSystem({
rankingRule: rankingRuleHasInvalidBonusMatch,
}),
).toThrow(lottoSystemValidations.validBonusMatches.errorMessage);

const rankingRuleHasInvalidProfit = [
{
bonusMatch: true,
profit: '1000',
rank: 1,
matchCount: 3,
},
];
expect(
() =>
new LottoSystem({
rankingRule: rankingRuleHasInvalidProfit,
}),
).toThrow(lottoSystemValidations.validProfits.errorMessage);
});

test('로또 시스템은 정확한 개수의 로또를 구매한다.', () => {
const lottoSystem = new LottoSystem();
lottoSystem.payLottoTicket(10000);

expect(lottoSystem.ticketCount).toBe(10);
expect(lottoSystem.paidAmount).toBe(10000);
});

test('로또 시스템은 발행한 로또들을 등수 별로 분류할 수 있다.', () => {
createLottoNumbers
.mockReturnValueOnce([1, 2, 3, 4, 5, 6]) // 1등
.mockReturnValueOnce([1, 2, 3, 4, 5, 7]) // 2등
.mockReturnValueOnce([1, 2, 3, 4, 5, 7]) // 2등
.mockReturnValueOnce([1, 2, 3, 4, 15, 16]) // 4등
.mockReturnValueOnce([1, 2, 3, 4, 15, 16]) // 4등
.mockReturnValueOnce([1, 2, 3, 4, 15, 16]); // 4등

const lottoSystem = new LottoSystem();
lottoSystem.setWinningLotto([1, 2, 3, 4, 5, 6], 7);
lottoSystem.payLottoTicket(6000);

expect(lottoSystem.lottoRankingResult.find(({ rank }) => rank === 1).ticketList.length).toBe(1);
expect(lottoSystem.lottoRankingResult.find(({ rank }) => rank === 2).ticketList.length).toBe(2);
expect(lottoSystem.lottoRankingResult.find(({ rank }) => rank === 4).ticketList.length).toBe(3);
});

test('로또 시스템은 총 수익을 계산할 수 있다.', () => {
createLottoNumbers
.mockReturnValueOnce([1, 2, 3, 4, 5, 6]) // 1등
.mockReturnValueOnce([1, 2, 3, 4, 15, 16]); // 4등

const lottoSystem = new LottoSystem();
lottoSystem.setWinningLotto([1, 2, 3, 4, 5, 6], 7);
lottoSystem.payLottoTicket(2000);

expect(lottoSystem.profitAmount).toBe(2000050000);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

2_000_050_000 더 보기 편할 것 같아요..!

});

test('로또 시스템은 수익율을 계산할 수 있다.', () => {
createLottoNumbers
.mockReturnValueOnce([1, 2, 3, 4, 15, 16]) // 4등 = 50000
.mockImplementation(() => [11, 12, 13, 14, 15, 16]);

const lottoSystem = new LottoSystem();
lottoSystem.setWinningLotto([1, 2, 3, 4, 5, 6], 7);
lottoSystem.payLottoTicket(100000);

expect(lottoSystem.profitRatio).toBe(50);
});
});
11 changes: 0 additions & 11 deletions src/__tests__/sum.test.js

This file was deleted.

32 changes: 32 additions & 0 deletions src/constants/lottoRankingRule.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
const LOTTO_RANKING_DATA = [
{
rank: 1,
matchCount: 6,
profit: 2000000000,
},
{
rank: 2,
matchCount: 5,
bonusMatch: true,
profit: 30000000,
},
{
rank: 3,
matchCount: 5,
bonusMatch: false,
profit: 1500000,
},
{
rank: 4,
matchCount: 4,
profit: 50000,
},
{
rank: 5,
matchCount: 3,
profit: 5000,
},
];
Comment on lines +1 to +29

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

오호! 저도 이런 데이터를 만들어놓고 시작해야겠어요!



export default LOTTO_RANKING_DATA;
6 changes: 6 additions & 0 deletions src/constants/lottoType.js

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

일반 텍스트로 타입을 구분한 것과 Symbol 을 사용해서 구분하는 것의 차이점이 무엇인가요...?!

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

오오 독립성을 보장해준다?
디테일이 돋보이네요..!

'lottoTicket' === 'lottoTicket' // true
Symbol('lottoTicket') === Symbol('lottoTicket') // false

Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
const LOTTO_TYPE = {
TICKET: Symbol('lottoTicket'),
WINNING: Symbol('winningLotto'),
};

export default LOTTO_TYPE;
Loading