import 'package:flutter_test/flutter_test.dart'; import 'package:dio/dio.dart'; import 'package:get_it/get_it.dart'; import 'package:mockito/mockito.dart'; import 'package:superport/models/equipment_unified_model.dart'; import 'package:superport/data/models/equipment/equipment_response.dart'; import 'package:superport/data/models/equipment/equipment_io_response.dart'; import 'package:superport/data/models/company/company_dto.dart'; import 'package:superport/data/models/warehouse/warehouse_dto.dart'; import '../helpers/simple_mock_services.mocks.dart'; import '../helpers/simple_mock_services.dart'; import '../helpers/mock_data_helpers.dart'; // AutoFixer import import '../integration/automated/framework/core/auto_fixer.dart'; import '../integration/automated/framework/core/api_error_diagnostics.dart'; import '../integration/automated/framework/models/error_models.dart'; /// 장비 입고 데모 테스트 /// /// 이 테스트는 에러 자동 진단 및 수정 기능을 데모합니다. void main() { late MockEquipmentService mockEquipmentService; late MockCompanyService mockCompanyService; late MockWarehouseService mockWarehouseService; late ApiAutoFixer autoFixer; late ApiErrorDiagnostics diagnostics; setUpAll(() { // GetIt 초기화 GetIt.instance.reset(); }); setUp(() { mockEquipmentService = MockEquipmentService(); mockCompanyService = MockCompanyService(); mockWarehouseService = MockWarehouseService(); // 자동 수정 시스템 초기화 diagnostics = ApiErrorDiagnostics(); autoFixer = ApiAutoFixer(diagnostics: diagnostics); // Mock 서비스 기본 설정 SimpleMockServiceHelpers.setupCompanyServiceMock(mockCompanyService); SimpleMockServiceHelpers.setupWarehouseServiceMock(mockWarehouseService); SimpleMockServiceHelpers.setupEquipmentServiceMock(mockEquipmentService); }); tearDown(() { GetIt.instance.reset(); }); group('장비 입고 성공 시나리오', () { test('정상적인 장비 입고 프로세스', () async { // Given: 정상적인 테스트 데이터 const testCompanyId = 1; const testWarehouseId = 1; final testEquipment = Equipment( manufacturer: 'Samsung', name: 'Galaxy Book Pro', category: '노트북', subCategory: '업무용', subSubCategory: '고성능', serialNumber: 'SN123456', quantity: 1, ); // When: 테스트 실행 print('\n=== 정상적인 장비 입고 프로세스 시작 ==='); // 1. 회사 확인 print('\n[1단계] 회사 정보 확인'); final company = await mockCompanyService.getCompanyDetail(testCompanyId); print('✅ 회사 조회 성공: ${company.name} (ID: ${company.id})'); // 2. 창고 확인 print('\n[2단계] 창고 정보 확인'); final warehouse = await mockWarehouseService.getWarehouseLocationById(testWarehouseId); print('✅ 창고 조회 성공: ${warehouse.name} (ID: ${warehouse.id})'); // 3. 장비 생성 print('\n[3단계] 장비 생성'); final createdEquipment = await mockEquipmentService.createEquipment(testEquipment); print('✅ 장비 생성 성공: ${createdEquipment.name} (ID: ${createdEquipment.id})'); // 4. 장비 입고 print('\n[4단계] 장비 입고'); final inResult = await mockEquipmentService.equipmentIn( equipmentId: createdEquipment.id!, quantity: 1, warehouseLocationId: testWarehouseId, notes: '테스트 입고', ); print('✅ 장비 입고 성공!'); print(' - 트랜잭션 ID: ${inResult.transactionId}'); print(' - 장비 ID: ${inResult.equipmentId}'); print(' - 수량: ${inResult.quantity}'); print(' - 타입: ${inResult.transactionType}'); print(' - 메시지: ${inResult.message}'); // Then: 검증 expect(inResult.success, isTrue); expect(inResult.transactionType, equals('IN')); expect(inResult.quantity, equals(1)); }); }); group('에러 자동 진단 및 수정 데모', () { test('필수 필드 누락 시 자동 수정', () async { print('\n=== 에러 자동 진단 및 수정 데모 시작 ==='); // Given: 필수 필드가 누락된 장비 (manufacturer가 비어있음) final incompleteEquipment = Equipment( manufacturer: '', // 빈 제조사 - 에러 발생 name: 'Test Equipment', category: '노트북', subCategory: '업무용', subSubCategory: '일반', quantity: 1, ); // Mock이 특정 에러를 던지도록 설정 when(mockEquipmentService.createEquipment(any)) .thenThrow(DioException( requestOptions: RequestOptions(path: '/equipment'), response: Response( requestOptions: RequestOptions(path: '/equipment'), statusCode: 400, data: { 'error': 'VALIDATION_ERROR', 'message': 'Required field missing: manufacturer', 'field': 'manufacturer' }, ), type: DioExceptionType.badResponse, )); print('\n[1단계] 불완전한 장비 생성 시도'); print(' - 제조사: ${incompleteEquipment.manufacturer} (비어있음)'); print(' - 이름: ${incompleteEquipment.name}'); try { await mockEquipmentService.createEquipment(incompleteEquipment); } catch (e) { if (e is DioException) { print('\n❌ 예상된 에러 발생!'); print(' - 상태 코드: ${e.response?.statusCode}'); print(' - 에러 메시지: ${e.response?.data['message']}'); print(' - 문제 필드: ${e.response?.data['field']}'); // 에러 진단 print('\n[2단계] 에러 자동 진단 시작...'); final apiError = ApiError( originalError: e, requestUrl: e.requestOptions.path, requestMethod: e.requestOptions.method, statusCode: e.response?.statusCode, serverMessage: e.response?.data['message'], requestBody: incompleteEquipment.toJson(), ); final diagnosis = await diagnostics.diagnoseError(apiError); print('\n📋 진단 결과:'); print(' - 에러 타입: ${diagnosis.type}'); print(' - 심각도: ${diagnosis.severity}'); print(' - 누락된 필드: ${diagnosis.missingFields}'); print(' - 자동 수정 가능: ${diagnosis.isAutoFixable ? "예" : "아니오"}'); if (diagnosis.isAutoFixable) { // 자동 수정 시도 print('\n[3단계] 자동 수정 시작...'); final fixResult = await autoFixer.attemptAutoFix(diagnosis); if (fixResult.success) { print('\n✅ 자동 수정 성공!'); print(' - 수정 ID: ${fixResult.fixId}'); print(' - 실행된 액션 수: ${fixResult.executedActions.length}'); print(' - 소요 시간: ${fixResult.duration}ms'); // 수정된 데이터로 재시도 final fixedEquipment = Equipment( manufacturer: '미지정', // 자동으로 기본값 설정 name: incompleteEquipment.name, category: incompleteEquipment.category, subCategory: incompleteEquipment.subCategory, subSubCategory: incompleteEquipment.subSubCategory, quantity: incompleteEquipment.quantity, ); // Mock이 수정된 요청에는 성공하도록 설정 when(mockEquipmentService.createEquipment(argThat( predicate((eq) => eq.manufacturer.isNotEmpty), ))).thenAnswer((_) async => MockDataHelpers.createMockEquipmentModel( id: DateTime.now().millisecondsSinceEpoch, manufacturer: '미지정', name: fixedEquipment.name, )); print('\n[4단계] 수정된 데이터로 재시도'); print(' - 제조사: ${fixedEquipment.manufacturer} (자동 설정됨)'); final createdEquipment = await mockEquipmentService.createEquipment(fixedEquipment); print('\n✅ 장비 생성 성공!'); print(' - ID: ${createdEquipment.id}'); print(' - 제조사: ${createdEquipment.manufacturer}'); print(' - 이름: ${createdEquipment.name}'); expect(createdEquipment, isNotNull); expect(createdEquipment.manufacturer, isNotEmpty); } else { print('\n❌ 자동 수정 실패'); print(' - 에러: ${fixResult.error}'); } } } } }); test('API 서버 연결 실패 시 재시도', () async { print('\n=== API 서버 연결 실패 재시도 데모 ==='); var attemptCount = 0; // 처음 2번은 실패, 3번째는 성공하도록 설정 when(mockEquipmentService.createEquipment(any)).thenAnswer((_) async { attemptCount++; if (attemptCount < 3) { print('\n❌ 시도 $attemptCount: 서버 연결 실패'); throw DioException( requestOptions: RequestOptions(path: '/equipment'), type: DioExceptionType.connectionTimeout, message: 'Connection timeout', ); } else { print('\n✅ 시도 $attemptCount: 서버 연결 성공!'); return MockDataHelpers.createMockEquipmentModel(); } }); final equipment = Equipment( manufacturer: 'Samsung', name: 'Test Equipment', category: '노트북', subCategory: '업무용', subSubCategory: '일반', quantity: 1, ); print('[1단계] 장비 생성 시도 (네트워크 불안정 상황 시뮬레이션)'); Equipment? createdEquipment; for (int i = 1; i <= 3; i++) { try { createdEquipment = await mockEquipmentService.createEquipment(equipment); break; } catch (e) { if (i == 3) rethrow; await Future.delayed(Duration(seconds: 1)); // 재시도 전 대기 } } expect(createdEquipment, isNotNull); expect(attemptCount, equals(3)); }); }); group('자동 수정 통계', () { test('수정 이력 및 통계 확인', () async { print('\n=== 자동 수정 통계 ==='); // 여러 에러 시나리오 실행 후 통계 확인 final stats = autoFixer.getSuccessStatistics(); print('\n📊 자동 수정 통계:'); print(' - 총 시도 횟수: ${stats['totalAttempts']}'); print(' - 성공한 수정: ${stats['successfulFixes']}'); print(' - 성공률: ${(stats['successRate'] * 100).toStringAsFixed(1)}%'); print(' - 학습된 패턴 수: ${stats['learnedPatterns']}'); print(' - 평균 수정 시간: ${stats['averageFixDuration']}'); // 수정 이력 확인 final history = autoFixer.getFixHistory(); if (history.isNotEmpty) { print('\n📜 최근 수정 이력:'); for (final fix in history.take(5)) { print(' - ${fix.timestamp}: ${fix.fixResult.fixId} (${fix.action})'); } } }); }); }