533 lines
16 KiB
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)));
|
|
});
|
|
} |