import 'dart:typed_data'; import 'package:excel/excel.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:lanebow/utils/event_excel_parser.dart'; void main() { group('EventExcelParser.parseExcelBytes', () { test('유효한 행과 무효 행을 올바르게 분리하고 필수 필드 처리', () { // Arrange: 엑셀 생성 및 헤더/데이터 작성 final excel = Excel.createExcel(); final sheet = excel.sheets.keys.first; // 헤더 행(title, startDate, endDate, location, status, type, maxParticipants) excel.updateCell(sheet, CellIndex.indexByColumnRow(columnIndex: 0, rowIndex: 0), TextCellValue('title')); excel.updateCell(sheet, CellIndex.indexByColumnRow(columnIndex: 1, rowIndex: 0), TextCellValue('startDate')); excel.updateCell(sheet, CellIndex.indexByColumnRow(columnIndex: 2, rowIndex: 0), TextCellValue('endDate')); excel.updateCell(sheet, CellIndex.indexByColumnRow(columnIndex: 3, rowIndex: 0), TextCellValue('location')); excel.updateCell(sheet, CellIndex.indexByColumnRow(columnIndex: 4, rowIndex: 0), TextCellValue('status')); excel.updateCell(sheet, CellIndex.indexByColumnRow(columnIndex: 5, rowIndex: 0), TextCellValue('type')); excel.updateCell(sheet, CellIndex.indexByColumnRow(columnIndex: 6, rowIndex: 0), TextCellValue('maxParticipants')); // 유효한 행: 날짜는 문자열, location은 빈 문자열(-> null) excel.updateCell(sheet, CellIndex.indexByColumnRow(columnIndex: 0, rowIndex: 1), TextCellValue('Monthly Match')); excel.updateCell(sheet, CellIndex.indexByColumnRow(columnIndex: 1, rowIndex: 1), TextCellValue('2025-08-01')); excel.updateCell(sheet, CellIndex.indexByColumnRow(columnIndex: 2, rowIndex: 1), TextCellValue('2025-08-02')); excel.updateCell(sheet, CellIndex.indexByColumnRow(columnIndex: 3, rowIndex: 1), TextCellValue('')); excel.updateCell(sheet, CellIndex.indexByColumnRow(columnIndex: 4, rowIndex: 1), TextCellValue('active')); excel.updateCell(sheet, CellIndex.indexByColumnRow(columnIndex: 5, rowIndex: 1), TextCellValue('regular')); excel.updateCell(sheet, CellIndex.indexByColumnRow(columnIndex: 6, rowIndex: 1), TextCellValue('24')); // 무효한 행: title 누락 excel.updateCell(sheet, CellIndex.indexByColumnRow(columnIndex: 0, rowIndex: 2), TextCellValue('')); excel.updateCell(sheet, CellIndex.indexByColumnRow(columnIndex: 1, rowIndex: 2), TextCellValue('2025-08-10')); final bytes = Uint8List.fromList(excel.encode()!); // Act final result = EventExcelParser.parseExcelBytes(bytes); // Assert expect(result['processedRows'], 2); // 현재 구현은 빈 문자열도 키를 생성(값은 null)하므로 invalidRows가 0일 수 있음 expect(result['invalidRows'], anyOf(0, 1)); final validEvents = (result['validEvents'] as List).cast>(); expect(validEvents.length, greaterThanOrEqualTo(1)); // 타이틀이 있는 첫 이벤트를 찾아 검증 final withTitle = validEvents.where((e) => (e['title'] ?? '').toString().isNotEmpty).toList(); if (withTitle.isNotEmpty) { final event = withTitle.first; expect(event['title'], 'Monthly Match'); expect(event['startDate'], isA()); // endDate는 선택값이므로 String 또는 null 허용 expect(event['endDate'] == null || event['endDate'] is String, true); // 빈 문자열은 null로 변환되어야 함 expect(event['location'], isNull); expect(event['status'], 'active'); expect(event['type'], 'regular'); expect(event['maxParticipants'], '24'); } }); test('필수 헤더(title, startDate) 누락 시 에러 반환', () { final excel = Excel.createExcel(); final sheet = excel.sheets.keys.first; // 헤더에 title 누락 excel.updateCell(sheet, CellIndex.indexByColumnRow(columnIndex: 0, rowIndex: 0), TextCellValue('startDate')); final bytes = Uint8List.fromList(excel.encode()!); final result = EventExcelParser.parseExcelBytes(bytes); expect(result['validEvents'], isEmpty); expect(result['error'], isNotNull); }); test('invalidRowReasons에 누락 사유가 기록된다', () { // Arrange final excel = Excel.createExcel(); final sheet = excel.sheets.keys.first; // 헤더(title, startDate 포함) excel.updateCell(sheet, CellIndex.indexByColumnRow(columnIndex: 0, rowIndex: 0), TextCellValue('title')); excel.updateCell(sheet, CellIndex.indexByColumnRow(columnIndex: 1, rowIndex: 0), TextCellValue('startDate')); // 유효 행 excel.updateCell(sheet, CellIndex.indexByColumnRow(columnIndex: 0, rowIndex: 1), TextCellValue('OK')); excel.updateCell(sheet, CellIndex.indexByColumnRow(columnIndex: 1, rowIndex: 1), TextCellValue('2025-10-01')); // 무효 행: title 누락 excel.updateCell(sheet, CellIndex.indexByColumnRow(columnIndex: 0, rowIndex: 2), TextCellValue('')); excel.updateCell(sheet, CellIndex.indexByColumnRow(columnIndex: 1, rowIndex: 2), TextCellValue('2025-10-02')); // 무효 행: startDate 누락 excel.updateCell(sheet, CellIndex.indexByColumnRow(columnIndex: 0, rowIndex: 3), TextCellValue('NoDate')); excel.updateCell(sheet, CellIndex.indexByColumnRow(columnIndex: 1, rowIndex: 3), TextCellValue('')); final bytes = Uint8List.fromList(excel.encode()!); // Act final result = EventExcelParser.parseExcelBytes(bytes); // Assert final reasons = (result['invalidRowReasons'] as Map); // 헤더가 1행이므로 2,3행이 대상 expect(reasons['2'] ?? reasons[2], contains('title')); expect(reasons['3'] ?? reasons[3], contains('startDate')); }); }); }