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 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 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 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점 }); }); }