TDD 작성

This commit is contained in:
2025-08-12 03:28:08 +09:00
parent 6033d59590
commit 107abc963f
156 changed files with 70906 additions and 642 deletions
@@ -0,0 +1,521 @@
import 'package:flutter_test/flutter_test.dart';
import 'package:lanebow/models/member_model.dart';
import 'package:lanebow/models/score_model.dart';
import 'package:lanebow/models/tie_breaker_option.dart';
import 'package:lanebow/utils/tie_breaker_util.dart';
void main() {
group('TieBreakerUtil 테스트', () {
// 테스트용 점수 데이터 생성
List<Score> createTestScores() {
return [
// 1위: 100점
Score(
id: '1',
memberId: '1',
eventId: '1',
clubId: '1',
frames: [10, 10, 10, 10, 10, 10, 10, 10, 10, 10],
totalScore: 100,
handicap: 0,
date: DateTime.now(),
notes: '',
participantName: '참가자 1',
),
// 공동 2위: 90점, 핸디캡 10
Score(
id: '2',
memberId: '2',
eventId: '1',
clubId: '1',
frames: [9, 9, 9, 9, 9, 9, 9, 9, 9, 9],
totalScore: 90,
handicap: 10, // 핸디캡 포함 시 100점
date: DateTime.now(),
notes: '',
participantName: '참가자 2',
),
// 공동 2위: 90점, 핸디캡 0
Score(
id: '3',
memberId: '3',
eventId: '1',
clubId: '1',
frames: [9, 9, 9, 9, 9, 9, 9, 9, 9, 9],
totalScore: 90,
handicap: 0,
date: DateTime.now(),
notes: '',
participantName: '참가자 3',
),
// 4위: 80점, 핸디캡 20
Score(
id: '4',
memberId: '4',
eventId: '1',
clubId: '1',
frames: [8, 8, 8, 8, 8, 8, 8, 8, 8, 8],
totalScore: 80,
handicap: 20, // 핸디캡 포함 시 100점
date: DateTime.now(),
notes: '',
participantName: '참가자 4',
),
];
}
// 테스트용 회원 데이터 생성
List<Member> createTestMembers() {
return [
Member(
id: '1',
userId: 'user1',
clubId: '1',
name: '참가자 1',
email: 'user1@example.com',
isActive: true,
birthDate: DateTime(1990, 1, 1), // 1990년생
),
Member(
id: '2',
userId: 'user2',
clubId: '1',
name: '참가자 2',
email: 'user2@example.com',
isActive: true,
birthDate: DateTime(1985, 1, 1), // 1985년생 (더 연장자)
),
Member(
id: '3',
userId: 'user3',
clubId: '1',
name: '참가자 3',
email: 'user3@example.com',
isActive: true,
birthDate: DateTime(1995, 1, 1), // 1995년생
),
Member(
id: '4',
userId: 'user4',
clubId: '1',
name: '참가자 4',
email: 'user4@example.com',
isActive: true,
birthDate: DateTime(1980, 1, 1), // 1980년생 (가장 연장자)
),
];
}
// 점수 격차가 다른 테스트 점수 데이터 생성
List<Score> createScoreGapTestScores() {
return [
// 모두 동일한 총점 100점
Score(
id: '1',
memberId: '1',
eventId: '1',
clubId: '1',
frames: [10, 10, 10, 10, 10, 10, 10, 10, 10, 10], // 격차 0
totalScore: 100,
handicap: 0,
date: DateTime.now(),
notes: '',
participantName: '참가자 1',
),
Score(
id: '2',
memberId: '2',
eventId: '1',
clubId: '1',
frames: [5, 15, 5, 15, 5, 15, 5, 15, 5, 15], // 격차 10
totalScore: 100,
handicap: 0,
date: DateTime.now(),
notes: '',
participantName: '참가자 2',
),
Score(
id: '3',
memberId: '3',
eventId: '1',
clubId: '1',
frames: [0, 20, 0, 20, 0, 20, 0, 20, 0, 20], // 격차 20
totalScore: 100,
handicap: 0,
date: DateTime.now(),
notes: '',
participantName: '참가자 3',
),
];
}
test('기본 점수 정렬 테스트 (핸디캡 미포함)', () {
// given
final scores = createTestScores();
// when
final sortedScores = TieBreakerUtil.sortScoresByOptions(
scores,
null, // 옵션 없음
null,
false // 핸디캡 미포함
);
// then
expect(sortedScores[0].id, '1'); // 100점
expect(sortedScores[1].id, '2'); // 90점
expect(sortedScores[2].id, '3'); // 90점
expect(sortedScores[3].id, '4'); // 80점
// 순위 계산 확인
final ranks = TieBreakerUtil.calculateRanks(sortedScores, false, options: null);
expect(ranks['1'], 1); // 1위
expect(ranks['2'], 2); // 2위
expect(ranks['3'], 2); // 2위
expect(ranks['4'], 4); // 4위
});
test('기본 점수 정렬 테스트 (핸디캡 포함)', () {
// given
final scores = createTestScores();
// when
final sortedScores = TieBreakerUtil.sortScoresByOptions(
scores,
null, // 옵션 없음
null,
true // 핸디캡 포함
);
// then
// 핸디캡 포함 시: 참가자1(100), 참가자2(100), 참가자4(100), 참가자3(90)
expect(sortedScores[0].id, equals('1')); // 100점
expect(sortedScores[1].id, equals('2')); // 100점
expect(sortedScores[2].id, equals('4')); // 100점
expect(sortedScores[3].id, equals('3')); // 90점
// 순위 계산 확인
final ranks = TieBreakerUtil.calculateRanks(sortedScores, true);
expect(ranks['1'], 1); // 1위
expect(ranks['2'], 1); // 1위
expect(ranks['4'], 1); // 1위
expect(ranks['3'], 4); // 4위
});
test('동점자 처리 옵션: 핸디캡이 적은 순 (lowerHandicap)', () {
// given
final scores = createTestScores();
final options = [TieBreakerOption.lowerHandicap];
// when
final sortedScores = TieBreakerUtil.sortScoresByOptions(
scores,
options,
null,
true // 핸디캡 포함
);
// then
// 핸디캡 포함 시 동점인 경우, 핸디캡이 적은 순으로 정렬
// 참가자1(100점, 핸디캡0), 참가자2(100점, 핸디캡10), 참가자4(100점, 핸디캡20), 참가자3(90점)
expect(sortedScores[0].id, equals('1')); // 핸디캡 0
expect(sortedScores[1].id, equals('2')); // 핸디캡 10
expect(sortedScores[2].id, equals('4')); // 핸디캡 20
expect(sortedScores[3].id, equals('3')); // 90점
// 순위 계산 확인
final ranks = TieBreakerUtil.calculateRanks(sortedScores, true);
expect(ranks['1'], 1); // 1위
expect(ranks['2'], 1); // 1위
expect(ranks['4'], 1); // 1위
expect(ranks['3'], 4); // 4위
});
test('동점자 처리 옵션: 점수 격차가 적은 순 (lowerScoreGap)', () {
// given
final scores = createScoreGapTestScores();
final options = [TieBreakerOption.lowerScoreGap];
// when
final sortedScores = TieBreakerUtil.sortScoresByOptions(
scores,
options,
null,
false // 핸디캡 미포함
);
// then
// 모두 100점으로 동점이지만, 점수 격차가 적은 순으로 정렬
// 참가자1(격차0), 참가자2(격차10), 참가자3(격차20)
expect(sortedScores[0].id, equals('1')); // 격차 0
expect(sortedScores[1].id, equals('2')); // 격차 10
expect(sortedScores[2].id, equals('3')); // 격차 20
// 순위 계산 확인
final ranks = TieBreakerUtil.calculateRanks(sortedScores, false, options: options);
expect(ranks['1'], 1); // 1위
expect(ranks['2'], 1); // 1위
expect(ranks['3'], 1); // 1위
});
test('동점자 처리 옵션: 연장자 우선 (olderAge)', () {
// given
final scores = createTestScores();
final members = createTestMembers();
final options = [TieBreakerOption.olderAge];
// 테스트 데이터 확인용 로그
print('\n===== 테스트 데이터 확인 =====');
for (var score in scores) {
print('Score ID: ${score.id}, 총점: ${score.totalScore}, 핸디캡: ${score.handicap}');
}
for (var member in members) {
print('Member ID: ${member.id}, 생년월일: ${member.birthDate}');
}
// when
final sortedScores = TieBreakerUtil.sortScoresByOptions(
scores,
options,
members,
true // 핸디캡 포함
);
// 정렬 결과 확인용 로그
print('\n===== 정렬 결과 확인 =====');
for (var score in sortedScores) {
print('Sorted Score ID: ${score.id}, 총점: ${score.totalScore}, 핸디캡: ${score.handicap}');
}
// then
// 핸디캡 포함 시 동점인 경우, 연장자 우선으로 정렬
// 참가자4(1980년생), 참가자2(1985년생), 참가자1(1990년생), 참가자3(90점)
expect(sortedScores[0].id, equals('4')); // 1980년생
expect(sortedScores[1].id, equals('2')); // 1985년생
expect(sortedScores[2].id, equals('1')); // 1990년생
expect(sortedScores[3].id, equals('3')); // 90점
// 순위 계산 확인
final ranks = TieBreakerUtil.calculateRanks(sortedScores, true, options: options);
// 순위 계산 결과 확인용 로그
print('\n===== 순위 계산 결과 확인 =====');
for (var id in ranks.keys) {
print('ID: $id, 순위: ${ranks[id]}');
}
expect(ranks['4'], 1); // 1위
expect(ranks['2'], 2); // 2위
expect(ranks['1'], 3); // 3위
expect(ranks['3'], 4); // 4위
});
test('동점자 처리 옵션 우선순위 적용 (lowerHandicap -> olderAge)', () {
// given
// 핸디캐프이 같은 경우 연장자 우선
final options = [TieBreakerOption.lowerHandicap, TieBreakerOption.olderAge];
final members = createTestMembers();
print('\n===== lowerHandicap -> olderAge 테스트 데이터 확인 =====');
for (var member in members) {
print('Member ID: ${member.id}, 생년월일: ${member.birthDate}');
}
// 핸디캐프이 서로 다른 새 점수 데이터 생성
final scores = [
// 참가자1: 100점, 핸디캐프 0, 1990년생
Score(
id: '1',
memberId: '1',
eventId: '1',
clubId: '1',
frames: [10, 10, 10, 10, 10, 10, 10, 10, 10, 10],
totalScore: 100,
handicap: 0,
date: DateTime.now(),
notes: '',
participantName: '참가자 1',
),
// 참가자2: 90점, 핸디캐프 10, 1985년생
Score(
id: '2',
memberId: '2',
eventId: '1',
clubId: '1',
frames: [9, 9, 9, 9, 9, 9, 9, 9, 9, 9],
totalScore: 90,
handicap: 10,
date: DateTime.now(),
notes: '',
participantName: '참가자 2',
),
// 참가자3: 90점, 핸디캐프 0, 1995년생
Score(
id: '3',
memberId: '3',
eventId: '1',
clubId: '1',
frames: [9, 9, 9, 9, 9, 9, 9, 9, 9, 9],
totalScore: 90,
handicap: 0,
date: DateTime.now(),
notes: '',
participantName: '참가자 3',
),
];
// 점수 데이터 확인
print('\n===== 점수 데이터 확인 =====');
for (var score in scores) {
print('Score ID: ${score.id}, 총점: ${score.totalScore}, 핸디캡: ${score.handicap}');
}
// when
final sortedScores = TieBreakerUtil.sortScoresByOptions(
scores,
options,
members,
false // 핸디캡 미포함
);
// 정렬 결과 확인
print('\n===== 정렬 결과 확인 =====');
for (var score in sortedScores) {
print('Sorted Score ID: ${score.id}, 총점: ${score.totalScore}, 핸디캡: ${score.handicap}');
}
// then
// 기본 점수 정렬 후 핸디캡이 낮은 순, 연장자 순으로 정렬
expect(sortedScores[0].id, equals('1')); // 100점
expect(sortedScores[1].id, equals('3')); // 90점, 핸디캡 0
expect(sortedScores[2].id, equals('2')); // 90점, 핸디캡 10
// 순위 계산 확인
final ranks = TieBreakerUtil.calculateRanks(sortedScores, false, options: options);
// 순위 계산 결과 확인
print('\n===== 순위 계산 결과 확인 =====');
for (var id in ranks.keys) {
print('ID: $id, 순위: ${ranks[id]}');
}
expect(ranks['1'], 1); // 1위
expect(ranks['3'], 2); // 2위
expect(ranks['2'], 3); // 3위
});
test('동점자 처리 옵션 우선순위 적용 (lowerScoreGap -> lowerHandicap -> olderAge)', () {
// given
final scores = [
Score(
id: '1',
memberId: '1',
eventId: '1',
clubId: '1',
frames: [5, 15, 5, 15, 5, 15, 5, 15, 5, 15], // 격차 10
totalScore: 100,
handicap: 10,
date: DateTime.now(),
notes: '',
participantName: '참가자 1',
),
Score(
id: '2',
memberId: '2',
eventId: '1',
clubId: '1',
frames: [10, 10, 10, 10, 10, 10, 10, 10, 10, 10], // 격차 0
totalScore: 100,
handicap: 0,
date: DateTime.now(),
notes: '',
participantName: '참가자 2',
),
Score(
id: '3',
memberId: '3',
eventId: '1',
clubId: '1',
frames: [5, 15, 5, 15, 5, 15, 5, 15, 5, 15], // 격차 10
totalScore: 100,
handicap: 0,
date: DateTime.now(),
notes: '',
participantName: '참가자 3',
),
];
final members = createTestMembers();
// 점수 격차 -> 핸디캡 -> 연장자 순으로 우선순위 적용
final options = [
TieBreakerOption.lowerScoreGap,
TieBreakerOption.lowerHandicap,
TieBreakerOption.olderAge
];
// 테스트 데이터 확인용 로그
print('\n===== lowerScoreGap -> lowerHandicap -> olderAge 테스트 데이터 확인 =====');
for (var score in scores) {
print('Score ID: ${score.id}, 총점: ${score.totalScore}, 핸디캡: ${score.handicap}, 프레임: ${score.frames}');
}
for (var member in members) {
print('Member ID: ${member.id}, 생년월일: ${member.birthDate}');
}
// when
final sortedScores = TieBreakerUtil.sortScoresByOptions(
scores,
options,
members,
false // 핸디캡 미포함
);
// 정렬 결과 확인용 로그
print('\n===== 정렬 결과 확인 =====');
for (var score in sortedScores) {
print('Sorted Score ID: ${score.id}, 총점: ${score.totalScore}, 핸디캡: ${score.handicap}, 프레임: ${score.frames}');
}
// then
// 1. 점수 격차가 적은 순: 참가자2(격차0) > 참가자1,3(격차10)
// 2. 핸디캡이 적은 순: 참가자3(핸디캡0) > 참가자1(핸디캡10)
expect(sortedScores[0].id, equals('2')); // 격차0, 핸디캡0
expect(sortedScores[1].id, equals('3')); // 격차10, 핸디캡0
expect(sortedScores[2].id, equals('1')); // 격차10, 핸디캡10
// 순위 계산 확인
final ranks = TieBreakerUtil.calculateRanks(sortedScores, false, options: options);
// 순위 계산 결과 확인용 로그
print('\n===== 순위 계산 결과 확인 =====');
for (var id in ranks.keys) {
print('ID: $id, 순위: ${ranks[id]}');
}
expect(ranks['2'], 1); // 1위
expect(ranks['3'], 1); // 1위 (lowerScoreGap 옵션에 의해 동일 순위)
expect(ranks['1'], 3); // 3위
});
test('옵션이 없는 경우 기본 정렬만 적용', () {
// given
final scores = createTestScores();
final options = [TieBreakerOption.none];
// when
final sortedScores = TieBreakerUtil.sortScoresByOptions(
scores,
options,
null,
false // 핸디캡 미포함
);
// then
// 기본 점수 정렬만 적용
expect(sortedScores[0].id, equals('1')); // 100점
expect(sortedScores[1].id, equals('2')); // 90점
expect(sortedScores[2].id, equals('3')); // 90점
expect(sortedScores[3].id, equals('4')); // 80점
});
});
}