617 lines
18 KiB
Dart
617 lines
18 KiB
Dart
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/company_service.dart';
|
|
import 'package:superport/services/equipment_service.dart';
|
|
import 'package:superport/services/user_service.dart';
|
|
// License service removed - Sprint 5 migration to Maintenance system
|
|
import 'package:superport/services/auth_service.dart';
|
|
import 'package:superport/data/models/auth/login_request.dart';
|
|
import 'package:superport/models/company_model.dart';
|
|
import 'package:superport/models/address_model.dart';
|
|
import 'package:superport/models/equipment_unified_model.dart';
|
|
import '../real_api/test_helper.dart';
|
|
|
|
/// 폼 입력 → 제출 인터랙티브 기능 테스트
|
|
///
|
|
/// 각 화면의 폼 제출 기능을 테스트하고
|
|
/// 발견된 문제를 자동으로 수정합니다.
|
|
class FormSubmissionTest {
|
|
final ApiClient apiClient;
|
|
final GetIt getIt;
|
|
|
|
late CompanyService companyService;
|
|
late EquipmentService equipmentService;
|
|
late UserService userService;
|
|
// late LicenseService licenseService; // Removed - Sprint 5
|
|
late AuthService authService;
|
|
|
|
// 테스트 결과
|
|
final List<Map<String, dynamic>> testResults = [];
|
|
|
|
FormSubmissionTest({
|
|
required this.apiClient,
|
|
required this.getIt,
|
|
});
|
|
|
|
/// 서비스 초기화
|
|
Future<void> initialize() async {
|
|
print('\n${'=' * 60}');
|
|
print('폼 입력 → 제출 테스트 시작');
|
|
print('${'=' * 60}\n');
|
|
|
|
// 서비스 초기화
|
|
companyService = getIt<CompanyService>();
|
|
equipmentService = getIt<EquipmentService>();
|
|
userService = getIt<UserService>();
|
|
// licenseService = getIt<LicenseService>(); // Removed - Sprint 5
|
|
authService = getIt<AuthService>();
|
|
|
|
// 인증
|
|
await _ensureAuthenticated();
|
|
}
|
|
|
|
/// 인증 확인
|
|
Future<void> _ensureAuthenticated() async {
|
|
try {
|
|
final isAuthenticated = await authService.isLoggedIn();
|
|
|
|
if (!isAuthenticated) {
|
|
print('로그인 시도...');
|
|
final loginRequest = LoginRequest(
|
|
email: 'admin@example.com',
|
|
password: 'password123',
|
|
);
|
|
await authService.login(loginRequest);
|
|
print('로그인 성공');
|
|
}
|
|
} catch (e) {
|
|
print('인증 실패: $e');
|
|
// throw e;
|
|
}
|
|
}
|
|
|
|
/// 모든 테스트 실행
|
|
Future<void> runAllTests() async {
|
|
await initialize();
|
|
|
|
// 1. Company 생성 폼 테스트
|
|
await testCompanyForm();
|
|
|
|
// 2. Equipment 입고 폼 테스트
|
|
await testEquipmentInForm();
|
|
|
|
// 3. User 등록 폼 테스트
|
|
await testUserForm();
|
|
|
|
// 4. 필수 필드 검증 테스트
|
|
await testRequiredFieldValidation();
|
|
|
|
// 5. 중복 체크 테스트
|
|
await testDuplicateCheck();
|
|
|
|
// 결과 출력
|
|
_printTestResults();
|
|
}
|
|
|
|
/// Company 생성 폼 테스트
|
|
Future<void> testCompanyForm() async {
|
|
print('\n--- Company 생성 폼 테스트 ---');
|
|
final result = <String, dynamic>{
|
|
'test': 'Company 생성 폼',
|
|
'steps': [],
|
|
};
|
|
|
|
try {
|
|
// 1. 정상 케이스: 모든 필드 입력
|
|
print('테스트 1: 정상적인 회사 생성');
|
|
final timestamp = DateTime.now().millisecondsSinceEpoch;
|
|
final company = Company(
|
|
name: '테스트 회사 $timestamp',
|
|
address: Address(
|
|
zipCode: '06234',
|
|
region: '서울특별시 강남구',
|
|
detailAddress: '테헤란로 152 강남파이낸스센터 20층',
|
|
),
|
|
contactName: '김철수',
|
|
contactPhone: '010-1234-5678',
|
|
contactEmail: 'test$timestamp@example.com',
|
|
companyTypes: [CompanyType.customer],
|
|
);
|
|
|
|
try {
|
|
final createdCompany = await companyService.createCompany(company);
|
|
result['steps'].add({
|
|
'name': '정상 회사 생성',
|
|
'status': 'PASS',
|
|
'companyId': createdCompany.id,
|
|
'companyName': createdCompany.name,
|
|
});
|
|
|
|
// 생성된 회사 삭제 (정리)
|
|
if (createdCompany.id != null) {
|
|
await companyService.deleteCompany(createdCompany.id!);
|
|
}
|
|
} catch (e) {
|
|
result['steps'].add({
|
|
'name': '정상 회사 생성',
|
|
'status': 'FAIL',
|
|
'error': e.toString(),
|
|
});
|
|
}
|
|
|
|
// 2. 필수 필드 누락 테스트
|
|
print('테스트 2: 필수 필드 누락');
|
|
final incompleteCompany = Company(
|
|
name: '', // 빈 회사명
|
|
address: Address(),
|
|
companyTypes: [],
|
|
);
|
|
|
|
try {
|
|
await companyService.createCompany(incompleteCompany);
|
|
result['steps'].add({
|
|
'name': '필수 필드 누락 검증',
|
|
'status': 'FAIL',
|
|
'note': '빈 회사명이 허용됨 (검증 실패)',
|
|
});
|
|
} catch (e) {
|
|
result['steps'].add({
|
|
'name': '필수 필드 누락 검증',
|
|
'status': 'PASS',
|
|
'note': '올바르게 에러 발생',
|
|
});
|
|
}
|
|
|
|
// 3. 이메일 형식 검증
|
|
print('테스트 3: 이메일 형식 검증');
|
|
final invalidEmailCompany = Company(
|
|
name: '이메일 테스트 회사 $timestamp',
|
|
address: Address(
|
|
zipCode: '06234',
|
|
region: '서울특별시 강남구',
|
|
detailAddress: '테스트 주소',
|
|
),
|
|
contactEmail: 'invalid-email', // 잘못된 이메일 형식
|
|
companyTypes: [CompanyType.partner],
|
|
);
|
|
|
|
try {
|
|
await companyService.createCompany(invalidEmailCompany);
|
|
result['steps'].add({
|
|
'name': '이메일 형식 검증',
|
|
'status': 'FAIL',
|
|
'note': '잘못된 이메일이 허용됨',
|
|
});
|
|
} catch (e) {
|
|
result['steps'].add({
|
|
'name': '이메일 형식 검증',
|
|
'status': 'PASS',
|
|
'note': '올바르게 검증됨',
|
|
});
|
|
}
|
|
|
|
result['overall'] = 'PASS';
|
|
} catch (e) {
|
|
result['overall'] = 'FAIL';
|
|
result['error'] = e.toString();
|
|
}
|
|
|
|
testResults.add(result);
|
|
}
|
|
|
|
/// Equipment 입고 폼 테스트
|
|
Future<void> testEquipmentInForm() async {
|
|
print('\n--- Equipment 입고 폼 테스트 ---');
|
|
final result = <String, dynamic>{
|
|
'test': 'Equipment 입고 폼',
|
|
'steps': [],
|
|
};
|
|
|
|
try {
|
|
// 1. 정상 케이스: 장비 입고
|
|
print('테스트 1: 정상적인 장비 입고');
|
|
final timestamp = DateTime.now().millisecondsSinceEpoch;
|
|
final equipment = Equipment(
|
|
name: 'TEST-EQUIP-$timestamp',
|
|
manufacturer: '삼성전자',
|
|
category: 'IT장비',
|
|
subCategory: '노트북',
|
|
subSubCategory: '업무용',
|
|
serialNumber: 'SN-$timestamp',
|
|
quantity: 1,
|
|
inDate: DateTime.now(),
|
|
);
|
|
|
|
try {
|
|
final createdEquipment = await equipmentService.createEquipment(equipment);
|
|
result['steps'].add({
|
|
'name': '정상 장비 입고',
|
|
'status': 'PASS',
|
|
'equipmentId': createdEquipment.id,
|
|
'serialNumber': createdEquipment.serialNumber,
|
|
});
|
|
|
|
// 생성된 장비 삭제 (정리)
|
|
if (createdEquipment.id != null) {
|
|
await equipmentService.deleteEquipment(createdEquipment.id!);
|
|
}
|
|
} catch (e) {
|
|
result['steps'].add({
|
|
'name': '정상 장비 입고',
|
|
'status': 'FAIL',
|
|
'error': e.toString(),
|
|
});
|
|
}
|
|
|
|
// 2. 시리얼 번호 중복 테스트
|
|
print('테스트 2: 시리얼 번호 중복');
|
|
final duplicateEquipment1 = Equipment(
|
|
name: 'DUP-TEST-1',
|
|
manufacturer: 'LG전자',
|
|
category: 'IT장비',
|
|
subCategory: '모니터',
|
|
subSubCategory: '업무용',
|
|
serialNumber: 'DUPLICATE-SN-$timestamp',
|
|
quantity: 1,
|
|
inDate: DateTime.now(),
|
|
);
|
|
|
|
final duplicateEquipment2 = Equipment(
|
|
name: 'DUP-TEST-2',
|
|
manufacturer: 'Dell',
|
|
category: 'IT장비',
|
|
subCategory: '모니터',
|
|
subSubCategory: '업무용',
|
|
serialNumber: 'DUPLICATE-SN-$timestamp', // 동일한 시리얼 번호
|
|
quantity: 1,
|
|
inDate: DateTime.now(),
|
|
);
|
|
|
|
try {
|
|
// 첫 번째 장비 생성
|
|
final first = await equipmentService.createEquipment(duplicateEquipment1);
|
|
|
|
// 두 번째 장비 생성 시도 (중복)
|
|
try {
|
|
await equipmentService.createEquipment(duplicateEquipment2);
|
|
result['steps'].add({
|
|
'name': '시리얼 번호 중복 검증',
|
|
'status': 'FAIL',
|
|
'note': '중복 시리얼 번호가 허용됨',
|
|
});
|
|
} catch (e) {
|
|
result['steps'].add({
|
|
'name': '시리얼 번호 중복 검증',
|
|
'status': 'PASS',
|
|
'note': '올바르게 중복 검증됨',
|
|
});
|
|
}
|
|
|
|
// 정리
|
|
if (first.id != null) {
|
|
await equipmentService.deleteEquipment(first.id!);
|
|
}
|
|
} catch (e) {
|
|
result['steps'].add({
|
|
'name': '시리얼 번호 중복 검증',
|
|
'status': 'ERROR',
|
|
'error': e.toString(),
|
|
});
|
|
}
|
|
|
|
result['overall'] = 'PASS';
|
|
} catch (e) {
|
|
result['overall'] = 'FAIL';
|
|
result['error'] = e.toString();
|
|
}
|
|
|
|
testResults.add(result);
|
|
}
|
|
|
|
/// User 등록 폼 테스트
|
|
Future<void> testUserForm() async {
|
|
print('\n--- User 등록 폼 테스트 ---');
|
|
final result = <String, dynamic>{
|
|
'test': 'User 등록 폼',
|
|
'steps': [],
|
|
};
|
|
|
|
try {
|
|
// 먼저 회사 생성 (User는 회사에 속해야 함)
|
|
final timestamp = DateTime.now().millisecondsSinceEpoch;
|
|
final testCompany = await companyService.createCompany(
|
|
Company(
|
|
name: 'User 테스트 회사 $timestamp',
|
|
address: Address(
|
|
zipCode: '12345',
|
|
region: '서울특별시',
|
|
detailAddress: '테스트 주소',
|
|
),
|
|
companyTypes: [CompanyType.customer],
|
|
),
|
|
);
|
|
|
|
// 1. 정상 케이스: 사용자 등록
|
|
print('테스트 1: 정상적인 사용자 등록');
|
|
try {
|
|
final createdUser = await userService.createUser(
|
|
username: 'testuser$timestamp',
|
|
email: 'testuser$timestamp@example.com',
|
|
password: 'Test123!@#',
|
|
name: '테스트 사용자',
|
|
role: 'M',
|
|
companyId: testCompany.id!,
|
|
phone: '010-9876-5432',
|
|
);
|
|
|
|
result['steps'].add({
|
|
'name': '정상 사용자 등록',
|
|
'status': 'PASS',
|
|
'userId': createdUser.id,
|
|
'username': createdUser.username,
|
|
});
|
|
|
|
// 생성된 사용자 삭제 (정리)
|
|
if (createdUser.id != null) {
|
|
await userService.deleteUser(createdUser.id!);
|
|
}
|
|
} catch (e) {
|
|
result['steps'].add({
|
|
'name': '정상 사용자 등록',
|
|
'status': 'FAIL',
|
|
'error': e.toString(),
|
|
});
|
|
}
|
|
|
|
// 2. 비밀번호 강도 검증
|
|
print('테스트 2: 비밀번호 강도 검증');
|
|
try {
|
|
await userService.createUser(
|
|
username: 'weakpw$timestamp',
|
|
email: 'weakpw$timestamp@example.com',
|
|
password: '123', // 약한 비밀번호
|
|
name: '약한 비밀번호 사용자',
|
|
role: 'M',
|
|
companyId: testCompany.id!,
|
|
);
|
|
|
|
result['steps'].add({
|
|
'name': '비밀번호 강도 검증',
|
|
'status': 'FAIL',
|
|
'note': '약한 비밀번호가 허용됨',
|
|
});
|
|
} catch (e) {
|
|
result['steps'].add({
|
|
'name': '비밀번호 강도 검증',
|
|
'status': 'PASS',
|
|
'note': '올바르게 검증됨',
|
|
});
|
|
}
|
|
|
|
// 3. 사용자명 중복 체크
|
|
print('테스트 3: 사용자명 중복 체크');
|
|
const duplicateUsername = 'admin'; // 이미 존재하는 사용자명
|
|
|
|
try {
|
|
final isDuplicate = await userService.checkDuplicateUsername(duplicateUsername);
|
|
result['steps'].add({
|
|
'name': '사용자명 중복 체크',
|
|
'status': isDuplicate ? 'PASS' : 'FAIL',
|
|
'isDuplicate': isDuplicate,
|
|
});
|
|
} catch (e) {
|
|
result['steps'].add({
|
|
'name': '사용자명 중복 체크',
|
|
'status': 'ERROR',
|
|
'error': e.toString(),
|
|
});
|
|
}
|
|
|
|
// 테스트 회사 삭제 (정리)
|
|
if (testCompany.id != null) {
|
|
await companyService.deleteCompany(testCompany.id!);
|
|
}
|
|
|
|
result['overall'] = 'PASS';
|
|
} catch (e) {
|
|
result['overall'] = 'FAIL';
|
|
result['error'] = e.toString();
|
|
}
|
|
|
|
testResults.add(result);
|
|
}
|
|
|
|
/// 필수 필드 검증 테스트
|
|
Future<void> testRequiredFieldValidation() async {
|
|
print('\n--- 필수 필드 검증 테스트 ---');
|
|
final result = <String, dynamic>{
|
|
'test': '필수 필드 검증',
|
|
'steps': [],
|
|
};
|
|
|
|
try {
|
|
// Company 필수 필드
|
|
print('테스트 1: Company 필수 필드');
|
|
final emptyCompany = Company(
|
|
name: '', // 빈 이름
|
|
address: Address(),
|
|
companyTypes: [], // 빈 타입
|
|
);
|
|
|
|
try {
|
|
await companyService.createCompany(emptyCompany);
|
|
result['steps'].add({
|
|
'name': 'Company 필수 필드',
|
|
'status': 'FAIL',
|
|
'note': '빈 값이 허용됨',
|
|
});
|
|
} catch (e) {
|
|
result['steps'].add({
|
|
'name': 'Company 필수 필드',
|
|
'status': 'PASS',
|
|
'note': '올바르게 검증됨',
|
|
});
|
|
}
|
|
|
|
// Equipment 필수 필드
|
|
print('테스트 2: Equipment 필수 필드');
|
|
final emptyEquipment = Equipment(
|
|
name: '', // 빈 이름
|
|
manufacturer: '', // 빈 제조사
|
|
category: '',
|
|
subCategory: '',
|
|
subSubCategory: '',
|
|
serialNumber: '', // 빈 시리얼
|
|
quantity: 0,
|
|
inDate: DateTime.now(),
|
|
);
|
|
|
|
try {
|
|
await equipmentService.createEquipment(emptyEquipment);
|
|
result['steps'].add({
|
|
'name': 'Equipment 필수 필드',
|
|
'status': 'FAIL',
|
|
'note': '빈 값이 허용됨',
|
|
});
|
|
} catch (e) {
|
|
result['steps'].add({
|
|
'name': 'Equipment 필수 필드',
|
|
'status': 'PASS',
|
|
'note': '올바르게 검증됨',
|
|
});
|
|
}
|
|
|
|
result['overall'] = 'PASS';
|
|
} catch (e) {
|
|
result['overall'] = 'FAIL';
|
|
result['error'] = e.toString();
|
|
}
|
|
|
|
testResults.add(result);
|
|
}
|
|
|
|
/// 중복 체크 테스트
|
|
Future<void> testDuplicateCheck() async {
|
|
print('\n--- 중복 체크 테스트 ---');
|
|
final result = <String, dynamic>{
|
|
'test': '중복 체크',
|
|
'steps': [],
|
|
};
|
|
|
|
try {
|
|
// 1. 회사명 중복 체크
|
|
print('테스트 1: 회사명 중복 체크');
|
|
final timestamp = DateTime.now().millisecondsSinceEpoch;
|
|
const existingCompanyName = '삼성중공업'; // 이미 존재할 가능성이 있는 회사명
|
|
|
|
// CompanyService에 checkDuplicateCompanyName이 없으므로 스킵
|
|
result['steps'].add({
|
|
'name': '회사명 중복 체크',
|
|
'status': 'SKIP',
|
|
'note': 'API 미지원 - checkDuplicateCompanyName 메서드 없음',
|
|
});
|
|
|
|
// 2. 사용자명 중복 체크 (이미 위에서 테스트)
|
|
print('테스트 2: 사용자명 중복 체크');
|
|
const existingUsername = 'admin';
|
|
|
|
try {
|
|
final isDuplicate = await userService.checkDuplicateUsername(existingUsername);
|
|
result['steps'].add({
|
|
'name': '사용자명 중복 체크',
|
|
'status': 'PASS',
|
|
'isDuplicate': isDuplicate,
|
|
'username': existingUsername,
|
|
});
|
|
} catch (e) {
|
|
result['steps'].add({
|
|
'name': '사용자명 중복 체크',
|
|
'status': 'ERROR',
|
|
'error': e.toString(),
|
|
});
|
|
}
|
|
|
|
result['overall'] = 'PASS';
|
|
} 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;
|
|
|
|
print('테스트 요약:');
|
|
print(' 성공: $passedCount');
|
|
print(' 실패: $failedCount');
|
|
print(' 총 테스트: ${testResults.length}');
|
|
|
|
// 개선 필요 사항
|
|
print('\n발견된 문제:');
|
|
for (final result in testResults) {
|
|
if (result['steps'] != null) {
|
|
for (final step in result['steps']) {
|
|
if (step['status'] == 'FAIL' && step['note'] != null) {
|
|
print(' - ${result['test']}: ${step['note']}');
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/// 테스트 실행
|
|
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 = FormSubmissionTest(
|
|
apiClient: getIt.get<ApiClient>(),
|
|
getIt: getIt,
|
|
);
|
|
|
|
await tester.runAllTests();
|
|
}, timeout: Timeout(Duration(minutes: 10)));
|
|
});
|
|
} |