import 'package:flutter_test/flutter_test.dart'; import 'package:get_it/get_it.dart'; import 'package:superport/data/datasources/remote/api_client.dart'; import 'package:superport/services/equipment_service.dart'; import 'package:superport/services/company_service.dart'; import 'package:superport/services/auth_service.dart'; import 'package:superport/data/models/auth/login_request.dart'; import 'package:superport/data/models/equipment/equipment_out_request.dart'; import 'package:superport/models/equipment_unified_model.dart'; import 'package:superport/screens/equipment/controllers/equipment_list_controller.dart'; // MockDataService는 제거됨 import '../real_api/test_helper.dart'; /// 체크박스 → 장비출고 인터랙티브 기능 테스트 /// /// 장비 목록에서 체크박스를 선택하고 /// 출고 프로세스를 테스트합니다. class CheckboxEquipmentOutTest { final ApiClient apiClient; final GetIt getIt; late EquipmentService equipmentService; late CompanyService companyService; late AuthService authService; late EquipmentListController controller; // 테스트 결과 final List> testResults = []; CheckboxEquipmentOutTest({ required this.apiClient, required this.getIt, }); /// 서비스 초기화 Future initialize() async { print('\n${'=' * 60}'); print('체크박스 → 장비출고 테스트 시작'); print('${'=' * 60}\n'); // 서비스 초기화 equipmentService = getIt(); companyService = getIt(); authService = getIt(); // Controller 초기화 (MockDataService 제거됨) controller = EquipmentListController(); // 인증 await _ensureAuthenticated(); } /// 인증 확인 Future _ensureAuthenticated() async { try { final isAuthenticated = await authService.isLoggedIn(); if (!isAuthenticated) { print('로그인 시도...'); final loginRequest = LoginRequest( email: 'admin@superport.kr', password: 'admin123!', ); await authService.login(loginRequest); print('로그인 성공'); } } catch (e) { print('인증 실패: $e'); // throw e; } } /// 대량 장비 입고 헬퍼 함수 Future createBulkEquipmentIn(int count) async { print('\n대량 장비 입고 시작: $count개'); final manufacturers = ['삼성전자', 'LG전자', 'Apple', '델', 'HP', '레노버']; final categories = ['노트북', '모니터', '서버', '프린터', '네트워크장비']; final subCategories = ['일반형', '고급형', '전문가용']; int successCount = 0; int failCount = 0; for (int i = 0; i < count; i++) { try { // 1. 장비 생성 final equipment = Equipment( name: 'TEST-EQUIP-${DateTime.now().millisecondsSinceEpoch}-$i', serialNumber: 'SN-${DateTime.now().millisecondsSinceEpoch}-$i', manufacturer: manufacturers[i % manufacturers.length], category: categories[i % categories.length], subCategory: subCategories[i % subCategories.length], subSubCategory: '일반', inDate: DateTime.now(), quantity: 1, ); final createdEquipment = await equipmentService.createEquipment(equipment); // 2. 장비 입고 처리 if (createdEquipment.id != null) { await equipmentService.equipmentIn( equipmentId: createdEquipment.id!, quantity: 1, notes: '자동 테스트 입고 #$i', ); successCount++; if ((i + 1) % 10 == 0) { print(' 진행상황: ${i + 1}/$count 완료'); } } } catch (e) { failCount++; print(' 장비 #$i 입고 실패: $e'); } } print('대량 장비 입고 완료: 성공 $successCount개, 실패 $failCount개'); } /// 모든 테스트 실행 Future runAllTests() async { await initialize(); // 1. 단일 선택 테스트 await testSingleSelection(); // 2. 다중 선택 테스트 await testMultipleSelection(); // 3. 전체 선택 테스트 await testSelectAll(); // 4. 상태별 필터링 후 선택 테스트 await testFilteredSelection(); // 5. 장비출고 프로세스 테스트 await testEquipmentOutProcess(); // 결과 출력 _printTestResults(); } /// 단일 선택 테스트 Future testSingleSelection() async { print('\n--- 단일 선택 테스트 ---'); final result = { 'test': '단일 선택', 'steps': [], }; try { // 장비 목록 로드 await controller.loadData(isRefresh: true); result['steps'].add({ 'name': '장비 목록 로드', 'status': 'PASS', 'count': controller.equipments.length, }); if (controller.equipments.isNotEmpty) { // 첫 번째 장비 선택 final equipment = controller.equipments.first; controller.selectEquipment(equipment); final isSelected = controller.selectedEquipmentIds.contains('${equipment.id}:${equipment.status}'); result['steps'].add({ 'name': '장비 선택', 'status': isSelected ? 'PASS' : 'FAIL', 'equipmentId': equipment.id, 'selected': isSelected, }); // 선택 해제 controller.selectEquipment(equipment); // Toggle to deselect final isDeselected = !controller.selectedEquipmentIds.contains('${equipment.id}:${equipment.status}'); result['steps'].add({ 'name': '장비 선택 해제', 'status': isDeselected ? 'PASS' : 'FAIL', 'deselected': isDeselected, }); } result['overall'] = 'PASS'; } catch (e) { result['overall'] = 'FAIL'; result['error'] = e.toString(); } testResults.add(result); } /// 다중 선택 테스트 Future testMultipleSelection() async { print('\n--- 다중 선택 테스트 ---'); final result = { 'test': '다중 선택', 'steps': [], }; try { await controller.loadData(isRefresh: true); if (controller.equipments.length >= 3) { // 상위 3개 장비 선택 final equipmentsToSelect = controller.equipments.take(3).toList(); for (final equipment in equipmentsToSelect) { controller.selectEquipment(equipment); } final selectedCount = controller.getSelectedEquipmentCount(); result['steps'].add({ 'name': '3개 장비 선택', 'status': selectedCount == 3 ? 'PASS' : 'FAIL', 'expectedCount': 3, 'actualCount': selectedCount, }); // 선택된 장비 목록 확인 final selectedEquipments = controller.getSelectedEquipments(); result['steps'].add({ 'name': '선택된 장비 목록 확인', 'status': selectedEquipments.length == 3 ? 'PASS' : 'FAIL', 'selectedIds': selectedEquipments.map((e) => e.id).toList(), }); } result['overall'] = 'PASS'; } catch (e) { result['overall'] = 'FAIL'; result['error'] = e.toString(); } testResults.add(result); } /// 전체 선택 테스트 Future testSelectAll() async { print('\n--- 전체 선택 테스트 ---'); final result = { 'test': '전체 선택', 'steps': [], }; try { await controller.loadData(isRefresh: true); // 전체 선택 시뮬레이션 for (final equipment in controller.equipments) { controller.selectEquipment(equipment); } final selectedCount = controller.getSelectedEquipmentCount(); final totalCount = controller.equipments.length; result['steps'].add({ 'name': '전체 선택', 'status': selectedCount == totalCount ? 'PASS' : 'FAIL', 'totalCount': totalCount, 'selectedCount': selectedCount, }); // 전체 해제 for (final equipment in controller.equipments) { controller.selectEquipment(equipment); // Toggle to deselect } final afterDeselectCount = controller.getSelectedEquipmentCount(); result['steps'].add({ 'name': '전체 선택 해제', 'status': afterDeselectCount == 0 ? 'PASS' : 'FAIL', 'remainingCount': afterDeselectCount, }); result['overall'] = 'PASS'; } catch (e) { result['overall'] = 'FAIL'; result['error'] = e.toString(); } testResults.add(result); } /// 상태별 필터링 후 선택 테스트 Future testFilteredSelection() async { print('\n--- 상태별 필터링 후 선택 테스트 ---'); final result = { 'test': '필터링 후 선택', 'steps': [], }; try { // 입고 상태 필터 적용 controller.changeStatusFilter('available'); result['steps'].add({ 'name': '입고 상태 필터 적용', 'status': 'PASS', 'filter': 'available', 'count': controller.equipments.length, }); // 필터링된 장비 중 선택 if (controller.equipments.isNotEmpty) { final availableEquipments = controller.equipments .where((e) => e.status == 'available') .take(2) .toList(); for (final equipment in availableEquipments) { controller.selectEquipment(equipment); } final selectedInStockCount = controller.getSelectedEquipmentCountByStatus('available'); result['steps'].add({ 'name': '입고 장비만 선택', 'status': selectedInStockCount > 0 ? 'PASS' : 'FAIL', 'selectedCount': selectedInStockCount, }); } result['overall'] = 'PASS'; } catch (e) { result['overall'] = 'FAIL'; result['error'] = e.toString(); } testResults.add(result); } /// 장비출고 프로세스 테스트 Future testEquipmentOutProcess() async { print('\n--- 장비출고 프로세스 테스트 ---'); final result = { 'test': '장비출고 프로세스', 'steps': [], }; try { // 기존 장비 활용 (이미 available 상태 장비가 충분히 있음) print('\n출고 테스트 시작...'); // 1. 입고 상태 장비 로드 await controller.loadData(isRefresh: true); controller.changeStatusFilter('available'); final availableCount = controller.equipments .where((e) => e.status == 'available') .length; result['steps'].add({ 'name': '입고 상태 장비 확인', 'status': availableCount > 0 ? 'PASS' : 'FAIL', 'availableCount': availableCount, }); if (availableCount > 0) { // 2. 단일 장비 출고 final singleEquipment = controller.equipments .where((e) => e.status == 'available') .first; controller.selectEquipment(singleEquipment); result['steps'].add({ 'name': '단일 장비 선택', 'status': 'PASS', 'equipmentId': singleEquipment.id, }); // 회사 목록 조회 (출고 대상) final companies = await companyService.getCompanies(page: 1, perPage: 5); if (companies != null && companies.items.isNotEmpty) { final targetCompany = companies.items.first; try { // 단일 출고 처리 await equipmentService.equipmentOut( equipmentId: singleEquipment.id!, quantity: 1, companyId: targetCompany.id!, notes: '자동 테스트 - 단일 출고', ); result['steps'].add({ 'name': '단일 장비 출고 처리', 'status': 'PASS', 'companyId': targetCompany.id, }); } catch (e) { result['steps'].add({ 'name': '단일 장비 출고 처리', 'status': 'FAIL', 'error': e.toString(), }); } // 3. 다중 장비 출고 (10개) controller.selectedEquipmentIds.clear(); // 선택 초기화 await controller.loadData(isRefresh: true); // 목록 새로고침 final multipleEquipments = controller.equipments .where((e) => e.status == 'available') .take(10) .toList(); if (multipleEquipments.length >= 10) { int outSuccessCount = 0; int outFailCount = 0; for (final equipment in multipleEquipments) { try { await equipmentService.equipmentOut( equipmentId: equipment.id!, quantity: 1, companyId: targetCompany.id!, notes: '자동 테스트 - 대량 출고', ); outSuccessCount++; } catch (e) { outFailCount++; } } result['steps'].add({ 'name': '대량 장비 출고 (10개)', 'status': outSuccessCount >= 8 ? 'PASS' : 'FAIL', 'successCount': outSuccessCount, 'failCount': outFailCount, }); } // 4. 출고 후 상태 확인 await controller.loadData(isRefresh: true); controller.changeStatusFilter('inuse'); final inUseCount = controller.equipments .where((e) => e.status == 'inuse') .length; result['steps'].add({ 'name': '출고 후 상태 변경 확인', 'status': inUseCount > 0 ? 'PASS' : 'FAIL', 'inUseCount': inUseCount, }); } else { result['steps'].add({ 'name': '출고 대상 회사 조회', 'status': 'FAIL', 'note': '회사 목록이 비어있음', }); } } else { result['steps'].add({ 'name': '출고 가능 장비 확인', 'status': 'FAIL', 'note': '입고 상태 장비가 없음', }); } result['overall'] = result['steps'].every((s) => s['status'] == 'PASS') ? 'PASS' : 'PARTIAL'; } catch (e) { result['overall'] = 'FAIL'; result['error'] = e.toString(); } testResults.add(result); } /// 테스트 결과 출력 void _printTestResults() { print('\n${'=' * 60}'); print('체크박스 → 장비출고 테스트 결과'); print('${'=' * 60}\n'); for (final result in testResults) { print('테스트: ${result['test']}'); print('결과: ${result['overall']}'); if (result['steps'] != null) { for (final step in result['steps']) { print(' - ${step['name']}: ${step['status']}'); if (step['error'] != null) { print(' 에러: ${step['error']}'); } if (step['note'] != null) { print(' 참고: ${step['note']}'); } } } print(''); } // 요약 final passedCount = testResults.where((r) => r['overall'] == 'PASS').length; final failedCount = testResults.where((r) => r['overall'] == 'FAIL').length; final partialCount = testResults.where((r) => r['overall'] == 'PARTIAL').length; print('테스트 요약:'); print(' 성공: $passedCount'); print(' 실패: $failedCount'); print(' 부분 성공: $partialCount'); } } /// 테스트 실행 void main() async { // 실제 API 환경 설정 await RealApiTestHelper.setupTestEnvironment(); final getIt = GetIt.instance; group('체크박스 → 장비출고 테스트', () { setUpAll(() async { // 로그인 및 토큰 설정 await RealApiTestHelper.loginAndGetToken(); }); tearDownAll(() async { await RealApiTestHelper.teardownTestEnvironment(); }); test('체크박스 선택 및 장비출고 프로세스', () async { final tester = CheckboxEquipmentOutTest( apiClient: getIt.get(), getIt: getIt, ); await tester.runAllTests(); }, timeout: Timeout(Duration(minutes: 5))); }); }