Files
superport/test/integration/automated/checkbox_equipment_out_test.dart

533 lines
16 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/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/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<Map<String, dynamic>> testResults = [];
CheckboxEquipmentOutTest({
required this.apiClient,
required this.getIt,
});
/// 서비스 초기화
Future<void> initialize() async {
print('\n${'=' * 60}');
print('체크박스 → 장비출고 테스트 시작');
print('${'=' * 60}\n');
// 서비스 초기화
equipmentService = getIt<EquipmentService>();
companyService = getIt<CompanyService>();
authService = getIt<AuthService>();
// Controller 초기화 (MockDataService 제거됨)
controller = EquipmentListController();
// 인증
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> 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<void> runAllTests() async {
await initialize();
// 1. 단일 선택 테스트
await testSingleSelection();
// 2. 다중 선택 테스트
await testMultipleSelection();
// 3. 전체 선택 테스트
await testSelectAll();
// 4. 상태별 필터링 후 선택 테스트
await testFilteredSelection();
// 5. 장비출고 프로세스 테스트
await testEquipmentOutProcess();
// 결과 출력
_printTestResults();
}
/// 단일 선택 테스트
Future<void> testSingleSelection() async {
print('\n--- 단일 선택 테스트 ---');
final result = <String, dynamic>{
'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<void> testMultipleSelection() async {
print('\n--- 다중 선택 테스트 ---');
final result = <String, dynamic>{
'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<void> testSelectAll() async {
print('\n--- 전체 선택 테스트 ---');
final result = <String, dynamic>{
'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<void> testFilteredSelection() async {
print('\n--- 상태별 필터링 후 선택 테스트 ---');
final result = <String, dynamic>{
'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<void> testEquipmentOutProcess() async {
print('\n--- 장비출고 프로세스 테스트 ---');
final result = <String, dynamic>{
'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.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<ApiClient>(),
getIt: getIt,
);
await tester.runAllTests();
}, timeout: Timeout(Duration(minutes: 5)));
});
}