- 모든 서비스 메서드 시그니처를 실제 구현에 맞게 수정 - TestDataGenerator 제거하고 직접 객체 생성으로 변경 - 모델 필드명 및 타입 불일치 수정 - 불필요한 Either 패턴 사용 제거 - null safety 관련 이슈 해결 수정된 파일: - test/integration/screens/company_integration_test.dart - test/integration/screens/equipment_integration_test.dart - test/integration/screens/user_integration_test.dart - test/integration/screens/login_integration_test.dart
326 lines
11 KiB
Dart
326 lines
11 KiB
Dart
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<String, dynamic>;
|
|
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<String, dynamic>) {
|
|
final data = response.data as Map<String, dynamic>;
|
|
|
|
// 이미 정규화된 형식인지 확인
|
|
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<String, dynamic>);
|
|
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<String, dynamic>;
|
|
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));
|
|
});
|
|
});
|
|
} |