import 'package:flutter_test/flutter_test.dart'; import 'package:dio/dio.dart'; import 'package:superport/core/utils/debug_logger.dart'; import 'package:superport/core/utils/login_diagnostics.dart'; import 'package:superport/data/models/auth/login_response.dart'; import 'package:superport/data/models/auth/auth_user.dart'; /// API 에러 진단을 위한 테스트 /// 실제 API 호출 시 발생하는 타입 에러와 응답 형식 문제를 파악합니다. void main() { group('API 응답 형식 및 타입 에러 진단', () { test('로그인 응답 JSON 파싱 - snake_case 필드명', () { // API가 snake_case로 응답하는 경우 final snakeCaseResponse = { 'access_token': 'test_token_123', 'refresh_token': 'refresh_token_456', 'token_type': 'Bearer', 'expires_in': 3600, 'user': { 'id': 1, 'username': 'testuser', 'email': 'test@example.com', 'name': '테스트 사용자', 'role': 'USER', }, }; // 파싱 시도 try { final loginResponse = LoginResponse.fromJson(snakeCaseResponse); print('[성공] snake_case 응답 파싱 성공'); print('Access Token: ${loginResponse.accessToken}'); print('User Email: ${loginResponse.user.email}'); // 검증 expect(loginResponse.accessToken, 'test_token_123'); expect(loginResponse.refreshToken, 'refresh_token_456'); expect(loginResponse.user.email, 'test@example.com'); } catch (e, stackTrace) { print('[실패] snake_case 응답 파싱 실패'); print('에러: $e'); print('스택 트레이스: $stackTrace'); fail('snake_case 응답 파싱에 실패했습니다: $e'); } }); test('로그인 응답 JSON 파싱 - camelCase 필드명', () { // API가 camelCase로 응답하는 경우 final camelCaseResponse = { 'accessToken': 'test_token_123', 'refreshToken': 'refresh_token_456', 'tokenType': 'Bearer', 'expiresIn': 3600, 'user': { 'id': 1, 'username': 'testuser', 'email': 'test@example.com', 'name': '테스트 사용자', 'role': 'USER', }, }; // 파싱 시도 try { final loginResponse = LoginResponse.fromJson(camelCaseResponse); print('[성공] camelCase 응답 파싱 성공'); print('Access Token: ${loginResponse.accessToken}'); // 이 테스트는 실패할 것으로 예상됨 (현재 모델이 snake_case 기준) fail('camelCase 응답이 성공하면 안됩니다 (모델이 snake_case 기준)'); } catch (e) { print('[예상된 실패] camelCase 응답 파싱 실패 (정상)'); print('에러: $e'); // 이는 예상된 동작임 expect(e, isNotNull); } }); test('다양한 API 응답 형식 처리 테스트', () { // 테스트 케이스들 final testCases = [ { 'name': '형식 1: success/data 래핑', 'response': { 'success': true, 'data': { 'access_token': 'token1', 'refresh_token': 'refresh1', 'token_type': 'Bearer', 'expires_in': 3600, 'user': { 'id': 1, 'username': 'user1', 'email': 'user1@test.com', 'name': '사용자1', 'role': 'USER', }, }, }, 'expectSuccess': false, // 직접 파싱은 실패해야 함 }, { 'name': '형식 2: 직접 응답', 'response': { 'access_token': 'token2', 'refresh_token': 'refresh2', 'token_type': 'Bearer', 'expires_in': 3600, 'user': { 'id': 2, 'username': 'user2', 'email': 'user2@test.com', 'name': '사용자2', 'role': 'ADMIN', }, }, 'expectSuccess': true, }, { 'name': '형식 3: 필수 필드 누락', 'response': { 'access_token': 'token3', // refresh_token 누락 'token_type': 'Bearer', 'expires_in': 3600, 'user': { 'id': 3, 'username': 'user3', 'email': 'user3@test.com', 'name': '사용자3', 'role': 'USER', }, }, 'expectSuccess': false, }, ]; for (final testCase in testCases) { print('\n테스트: ${testCase['name']}'); final response = testCase['response'] as Map; final expectSuccess = testCase['expectSuccess'] as bool; try { final loginResponse = LoginResponse.fromJson(response); if (expectSuccess) { print('✅ 파싱 성공 (예상대로)'); expect(loginResponse.accessToken, isNotNull); } else { print('❌ 파싱 성공 (실패해야 하는데 성공함)'); fail('${testCase['name']} - 파싱이 실패해야 하는데 성공했습니다'); } } catch (e) { if (!expectSuccess) { print('✅ 파싱 실패 (예상대로): $e'); } else { print('❌ 파싱 실패 (성공해야 하는데 실패함): $e'); fail('${testCase['name']} - 파싱이 성공해야 하는데 실패했습니다: $e'); } } } }); test('AuthUser 모델 파싱 테스트', () { final testUser = { 'id': 100, 'username': 'johndoe', 'email': 'john@example.com', 'name': 'John Doe', 'role': 'ADMIN', }; try { final user = AuthUser.fromJson(testUser); expect(user.id, 100); expect(user.username, 'johndoe'); expect(user.email, 'john@example.com'); expect(user.name, 'John Doe'); expect(user.role, 'ADMIN'); print('✅ AuthUser 파싱 성공'); } catch (e) { fail('AuthUser 파싱 실패: $e'); } }); test('실제 API 응답 시뮬레이션', () async { // 실제 API가 반환할 수 있는 다양한 응답들 final possibleResponses = [ // Spring Boot 기본 응답 Response( data: { 'timestamp': '2024-01-31T10:00:00', 'status': 200, 'data': { 'access_token': 'jwt_token_here', 'refresh_token': 'refresh_token_here', 'token_type': 'Bearer', 'expires_in': 3600, 'user': { 'id': 1, 'username': 'admin', 'email': 'admin@superport.com', 'name': '관리자', 'role': 'ADMIN', }, }, }, statusCode: 200, requestOptions: RequestOptions(path: '/auth/login'), ), // FastAPI 스타일 응답 Response( data: { 'access_token': 'jwt_token_here', 'refresh_token': 'refresh_token_here', 'token_type': 'bearer', 'expires_in': 3600, 'user': { 'id': 1, 'username': 'admin', 'email': 'admin@superport.com', 'name': '관리자', 'role': 'ADMIN', }, }, statusCode: 200, requestOptions: RequestOptions(path: '/auth/login'), ), ]; for (var i = 0; i < possibleResponses.length; i++) { final response = possibleResponses[i]; print('\n응답 형식 ${i + 1} 테스트:'); print('응답 데이터: ${response.data}'); // ResponseInterceptor 시뮬레이션 if (response.data is Map) { final data = response.data as Map; // 이미 정규화된 형식인지 확인 if (data.containsKey('success') && data.containsKey('data')) { print('이미 정규화된 형식'); try { LoginResponse.fromJson(data['data']); print('✅ 정규화된 형식 파싱 성공'); } catch (e) { print('❌ 정규화된 형식 파싱 실패: $e'); } } else if (data.containsKey('access_token') || data.containsKey('accessToken')) { print('직접 데이터 형식 - 정규화 필요'); // 정규화 final normalizedData = { 'success': true, 'data': data, }; try { LoginResponse.fromJson(normalizedData['data'] as Map); print('✅ 직접 데이터 형식 파싱 성공'); } catch (e) { print('❌ 직접 데이터 형식 파싱 실패: $e'); } } } } }); }); group('로그인 진단 도구 테스트', () { test('전체 진단 실행', () async { print('\n=== 로그인 진단 시작 ===\n'); final diagnostics = await LoginDiagnostics.runFullDiagnostics(); final report = LoginDiagnostics.formatDiagnosticsReport(diagnostics); print(report); // 진단 결과 검증 expect(diagnostics, isNotNull); expect(diagnostics['environment'], isNotNull); expect(diagnostics['serialization'], isNotNull); // 직렬화 테스트 결과 확인 final serialization = diagnostics['serialization'] as Map; expect(serialization['loginRequestValid'], true); expect(serialization['format1Valid'], true); expect(serialization['format2Valid'], true); }); test('DebugLogger 기능 테스트', () { // API 요청 로깅 DebugLogger.logApiRequest( method: 'POST', url: '/auth/login', data: {'email': 'test@example.com', 'password': '***'}, ); // API 응답 로깅 DebugLogger.logApiResponse( url: '/auth/login', statusCode: 200, data: {'success': true}, ); // 에러 로깅 DebugLogger.logError( 'API 호출 실패', error: Exception('Network error'), additionalData: {'endpoint': '/auth/login'}, ); // JSON 파싱 로깅 final testJson = { 'id': 1, 'name': 'Test', }; final result = DebugLogger.parseJsonWithLogging( testJson, (json) => json, objectName: 'TestObject', ); expect(result, isNotNull); expect(result, equals(testJson)); }); }); }