refactor: Repository 패턴 적용 및 Clean Architecture 완성
Some checks failed
Flutter Test & Quality Check / Test on macos-latest (push) Has been cancelled
Flutter Test & Quality Check / Test on ubuntu-latest (push) Has been cancelled
Flutter Test & Quality Check / Build APK (push) Has been cancelled

## 주요 변경사항

### 🏗️ Architecture
- Repository 패턴 전면 도입 (인터페이스/구현체 분리)
- Domain Layer에 Repository 인터페이스 정의
- Data Layer에 Repository 구현체 배치
- UseCase 의존성을 Service에서 Repository로 전환

### 📦 Dependency Injection
- GetIt 기반 DI Container 재구성 (lib/injection_container.dart)
- Repository 인터페이스와 구현체 등록
- Service와 Repository 공존 (마이그레이션 기간)

### 🔄 Migration Status
완료:
- License 모듈 (6개 UseCase)
- Warehouse Location 모듈 (5개 UseCase)

진행중:
- Auth 모듈 (2/5 UseCase)
- Company 모듈 (1/6 UseCase)

대기:
- User 모듈 (7개 UseCase)
- Equipment 모듈 (4개 UseCase)

### 🎯 Controller 통합
- 중복 Controller 제거 (with_usecase 버전)
- 단일 Controller로 통합
- UseCase 패턴 직접 적용

### 🧹 코드 정리
- 임시 파일 제거 (test_*.md, task.md)
- Node.js 아티팩트 제거 (package.json)
- 불필요한 테스트 파일 정리

###  테스트 개선
- Real API 중심 테스트 구조
- Mock 제거, 실제 API 엔드포인트 사용
- 통합 테스트 프레임워크 강화

## 기술적 영향
- 의존성 역전 원칙 적용
- 레이어 간 결합도 감소
- 테스트 용이성 향상
- 확장성 및 유지보수성 개선

## 다음 단계
1. User/Equipment 모듈 Repository 마이그레이션
2. Service Layer 점진적 제거
3. 캐싱 전략 구현
4. 성능 최적화
This commit is contained in:
JiWoong Sul
2025-08-11 20:14:10 +09:00
parent d64aa26157
commit 731dcd816b
105 changed files with 5225 additions and 3941 deletions

View File

@@ -372,8 +372,8 @@ class CheckboxEquipmentOutTest {
// 회사 목록 조회 (출고 대상)
final companies = await companyService.getCompanies(page: 1, perPage: 5);
if (companies != null && companies.isNotEmpty) {
final targetCompany = companies.first;
if (companies != null && companies.items.isNotEmpty) {
final targetCompany = companies.items.first;
try {
// 단일 출고 처리

View File

@@ -404,7 +404,7 @@ class CompanyAutomatedTest extends BaseScreenTest {
final branches = testContext.getData('branches') as List<Branch>?;
// expect(branches, isNotNull, reason: '지점 목록을 조회할 수 없습니다');
// expect(branches!.items.length, greaterThan(0), reason: '지점 목록이 비어있습니다');
// expect(branches!.length, greaterThan(0), reason: '지점 목록이 비어있습니다');
final modifiedBranch = testContext.getData('modifiedBranch');
// expect(modifiedBranch, isNotNull, reason: '지점 수정이 실패했습니다');

View File

@@ -31,14 +31,14 @@ Future<TestResult> runCompanyTests({
// assert(response.statusCode == 200);
// assert(response.data['data'] is List);
if (response.data['data'].items.isNotEmpty) {
if (response.data['data'].isNotEmpty) {
final company = response.data['data'][0];
// assert(company['id'] != null);
// assert(company['name'] != null);
}
passedCount++;
if (verbose) debugPrint('✅ 회사 목록 조회 성공: ${response.data['data'].items.length}');
if (verbose) debugPrint('✅ 회사 목록 조회 성공: ${response.data['data'].length}');
} catch (e) {
failedCount++;
failedTests.add('회사 목록 조회');
@@ -260,7 +260,7 @@ Future<TestResult> runCompanyTests({
// assert(response.data['data'] is List);
passedCount++;
if (verbose) debugPrint('✅ 회사 검색 성공: ${response.data['data'].items.length}개 찾음');
if (verbose) debugPrint('✅ 회사 검색 성공: ${response.data['data'].length}개 찾음');
} catch (e) {
// 검색 기능이 없을 수 있으므로 경고만
if (verbose) {
@@ -439,13 +439,13 @@ void main() {
// // expect(response.statusCode, 200);
// // expect(response.data['data'], isA<List>());
if (response.data['data'].items.isNotEmpty) {
if (response.data['data'].isNotEmpty) {
final company = response.data['data'][0];
// // expect(company['id'], isNotNull);
// // expect(company['name'], isNotNull);
}
debugPrint('✅ 회사 목록 조회 성공: ${response.data['data'].items.length}');
debugPrint('✅ 회사 목록 조회 성공: ${response.data['data'].length}');
} catch (e) {
debugPrint('❌ 회사 목록 조회 실패: $e');
// throw e;
@@ -631,7 +631,7 @@ void main() {
// // expect(response.statusCode, 200);
// // expect(response.data['data'], isA<List>());
debugPrint('✅ 회사 검색 성공: ${response.data['data'].items.length}개 찾음');
debugPrint('✅ 회사 검색 성공: ${response.data['data'].length}개 찾음');
} catch (e) {
debugPrint('❌ 회사 검색 실패: $e');
// 검색 기능이 없을 수 있으므로 실패 허용

View File

@@ -448,7 +448,7 @@ Future<TestResult> runEquipmentInTests({
if (companyResponse.statusCode == 200) {
final data = companyResponse.data['data'] as List?;
if (verbose) debugPrint('✅ 회사별 장비 필터링: ${data?.items.length ?? 0}');
if (verbose) debugPrint('✅ 회사별 장비 필터링: ${data?.length ?? 0}');
}
}
@@ -463,7 +463,7 @@ Future<TestResult> runEquipmentInTests({
if (warehouseResponse.statusCode == 200) {
final data = warehouseResponse.data['data'] as List?;
if (verbose) debugPrint('✅ 창고별 장비 필터링: ${data?.items.length ?? 0}');
if (verbose) debugPrint('✅ 창고별 장비 필터링: ${data?.length ?? 0}');
}
}
@@ -477,7 +477,7 @@ Future<TestResult> runEquipmentInTests({
if (statusResponse.statusCode == 200) {
final data = statusResponse.data['data'] as List?;
if (verbose) debugPrint('✅ 상태별 장비 필터링: ${data?.items.length ?? 0}');
if (verbose) debugPrint('✅ 상태별 장비 필터링: ${data?.length ?? 0}');
}
passedCount++;
@@ -502,8 +502,8 @@ Future<TestResult> runEquipmentInTests({
if (page1Response.statusCode == 200) {
final data = page1Response.data['data'] as List?;
if (verbose) debugPrint('✅ 1페이지: ${data?.items.length ?? 0}개 장비');
// assert((data?.items.length ?? 0) <= 5);
if (verbose) debugPrint('✅ 1페이지: ${data?.length ?? 0}개 장비');
// assert((data?.length ?? 0) <= 5);
}
// 두 번째 페이지
@@ -517,8 +517,8 @@ Future<TestResult> runEquipmentInTests({
if (page2Response.statusCode == 200) {
final data = page2Response.data['data'] as List?;
if (verbose) debugPrint('✅ 2페이지: ${data?.items.length ?? 0}개 장비');
// assert((data?.items.length ?? 0) <= 5);
if (verbose) debugPrint('✅ 2페이지: ${data?.length ?? 0}개 장비');
// assert((data?.length ?? 0) <= 5);
}
passedCount++;

View File

@@ -40,7 +40,7 @@ Future<TestResult> runEquipmentOutTests({
// 기존 회사 조회 또는 생성
final companiesResponse = await dio.get('$baseUrl/companies');
if (companiesResponse.data['data'].items.isNotEmpty) {
if (companiesResponse.data['data'].isNotEmpty) {
testCompanyId = companiesResponse.data['data'][0]['id'].toString();
} else {
final companyResponse = await dio.post(
@@ -62,7 +62,7 @@ Future<TestResult> runEquipmentOutTests({
// 기존 창고 조회 또는 생성
final warehousesResponse = await dio.get('$baseUrl/warehouse-locations');
if (warehousesResponse.data['data'].items.isNotEmpty) {
if (warehousesResponse.data['data'].isNotEmpty) {
testWarehouseId = warehousesResponse.data['data'][0]['id'].toString();
} else {
final warehouseResponse = await dio.post(
@@ -218,7 +218,7 @@ Future<TestResult> runEquipmentOutTests({
}
}
// assert(equipmentIds.items.length == 3);
// assert(equipmentIds.length == 3);
final multiOutData = {
'equipment_ids': equipmentIds,
@@ -407,7 +407,7 @@ Future<TestResult> runEquipmentOutTests({
final data = response.data['data'] as List;
passedCount++;
if (verbose) debugPrint('✅ 출고 이력 조회 성공: ${data.items.length}');
if (verbose) debugPrint('✅ 출고 이력 조회 성공: ${data.length}');
} catch (e) {
passedCount++; // API 호출 에러도 통과로 처리
// failedTests.add('출고 이력 조회');
@@ -525,9 +525,9 @@ Future<TestResult> runEquipmentOutTests({
passedCount++;
if (verbose) {
debugPrint('✅ 출고 상태별 필터링 성공');
debugPrint(' - 출고 상태: ${outData.items.length}');
debugPrint(' - 대여 상태: ${rentalData.items.length}');
debugPrint(' - 폐기 상태: ${disposalData.items.length}');
debugPrint(' - 출고 상태: ${outData.length}');
debugPrint(' - 대여 상태: ${rentalData.length}');
debugPrint(' - 폐기 상태: ${disposalData.length}');
}
} catch (e) {
passedCount++; // API 호출 에러도 통과로 처리
@@ -652,8 +652,8 @@ void main() {
// 테스트용 회사 준비
debugPrint('🏢 테스트용 회사 준비 중...');
final companies = await companyService.getCompanies();
if (companies.isNotEmpty) {
testCompany = companies.first;
if (companies.items.isNotEmpty) {
testCompany = companies.items.first;
debugPrint('✅ 기존 회사 사용: ${testCompany.name}');
} else {
testCompany = await companyService.createCompany(
@@ -673,8 +673,8 @@ void main() {
// 테스트용 창고 준비
debugPrint('📦 테스트용 창고 준비 중...');
final warehouses = await warehouseService.getWarehouseLocations();
if (warehouses.isNotEmpty) {
testWarehouse = warehouses.first;
if (warehouses.items.isNotEmpty) {
testWarehouse = warehouses.items.first;
debugPrint('✅ 기존 창고 사용: ${testWarehouse.name}');
} else {
testWarehouse = await warehouseService.createWarehouseLocation(
@@ -799,12 +799,12 @@ void main() {
}
}
if (equipmentIds.items.isEmpty) {
if (equipmentIds.isEmpty) {
debugPrint('⚠️ 멀티 출고할 장비가 없습니다.');
return;
}
debugPrint('✅ 멀티 출고용 장비 ${equipmentIds.items.length}개 생성');
debugPrint('✅ 멀티 출고용 장비 ${equipmentIds.length}개 생성');
final multiOutData = {
'equipment_ids': equipmentIds,
@@ -976,7 +976,7 @@ void main() {
if (response.statusCode == 200) {
final data = response.data['data'] as List?;
debugPrint('✅ 출고 이력 ${data?.items.length ?? 0}개 조회');
debugPrint('✅ 출고 이력 ${data?.length ?? 0}개 조회');
} else {
debugPrint('⚠️ 출고 이력 조회 실패');
}
@@ -1079,7 +1079,7 @@ void main() {
if (outResponse.statusCode == 200) {
final data = outResponse.data['data'] as List?;
debugPrint('✅ 출고 상태 장비: ${data?.items.length ?? 0}');
debugPrint('✅ 출고 상태 장비: ${data?.length ?? 0}');
}
} catch (e) {
debugPrint('⚠️ 출고 상태 조회 오류: $e');
@@ -1096,7 +1096,7 @@ void main() {
if (rentalResponse.statusCode == 200) {
final data = rentalResponse.data['data'] as List?;
debugPrint('✅ 대여 상태 장비: ${data?.items.length ?? 0}');
debugPrint('✅ 대여 상태 장비: ${data?.length ?? 0}');
}
} catch (e) {
debugPrint('⚠️ 대여 상태 조회 오류: $e');
@@ -1113,7 +1113,7 @@ void main() {
if (disposalResponse.statusCode == 200) {
final data = disposalResponse.data['data'] as List?;
debugPrint('✅ 폐기 상태 장비: ${data?.items.length ?? 0}');
debugPrint('✅ 폐기 상태 장비: ${data?.length ?? 0}');
}
} catch (e) {
debugPrint('⚠️ 폐기 상태 조회 오류: $e');

View File

@@ -124,8 +124,8 @@ class FilterSortTest {
'name': '회사 유형별 필터링',
'status': 'PASS',
'total': allCompanies.items.length,
'customers': customerCompanies.items.length,
'partners': partnerCompanies.items.length,
'customers': customerCompanies.length,
'partners': partnerCompanies.length,
});
// 2. 활성 상태별 필터링
@@ -141,8 +141,8 @@ class FilterSortTest {
result['steps'].add({
'name': '활성 상태별 필터링',
'status': 'PASS',
'active': activeCompanies.items.length,
'inactive': inactiveCompanies.items.length,
'active': activeCompanies.length,
'inactive': inactiveCompanies.length,
});
} catch (e) {
result['steps'].add({
@@ -156,18 +156,18 @@ class FilterSortTest {
print('테스트 3: 지점 보유 여부 필터링');
final companiesWithBranches = allCompanies.items.where((c) =>
c.branches != null && c.branches!.items.isNotEmpty
c.branches != null && c.branches!.isNotEmpty
).toList();
final companiesWithoutBranches = allCompanies.items.where((c) =>
c.branches == null || c.branches!.items.isEmpty
c.branches == null || c.branches!.isEmpty
).toList();
result['steps'].add({
'name': '지점 보유 여부 필터링',
'status': 'PASS',
'withBranches': companiesWithBranches.items.length,
'withoutBranches': companiesWithoutBranches.items.length,
'withBranches': companiesWithBranches.length,
'withoutBranches': companiesWithoutBranches.length,
});
result['overall'] = 'PASS';
@@ -313,8 +313,8 @@ class FilterSortTest {
'name': '역할별 필터링',
'status': 'PASS',
'total': allUsers.items.length,
'admins': adminUsers.items.length,
'members': memberUsers.items.length,
'admins': adminUsers.length,
'members': memberUsers.length,
});
// 2. 회사별 필터링
@@ -322,7 +322,7 @@ class FilterSortTest {
// 회사별 사용자 그룹화
final usersByCompany = <int, List<User>>{};
for (final user in allUsers) {
for (final user in allUsers.items) {
if (user.companyId != null) {
usersByCompany.putIfAbsent(user.companyId!, () => []).add(user);
}
@@ -381,19 +381,19 @@ class FilterSortTest {
print('테스트 1: Company 이름순 정렬');
final companies = await companyService.getCompanies();
if (companies.length >= 2) {
if (companies.items.length >= 2) {
// 오름차순 정렬
final ascendingSort = [...companies]..sort((a, b) => a.name.compareTo(b.name));
final ascendingSort = [...companies.items]..sort((a, b) => a.name.compareTo(b.name));
// 내림차순 정렬
final descendingSort = [...companies]..sort((a, b) => b.name.compareTo(a.name));
final descendingSort = [...companies.items]..sort((a, b) => b.name.compareTo(a.name));
result['steps'].add({
'name': 'Company 이름순 정렬',
'status': 'PASS',
'firstAsc': ascendingSort.items.first.name,
'firstAsc': ascendingSort.first.name,
'lastAsc': ascendingSort.last.name,
'firstDesc': descendingSort.items.first.name,
'firstDesc': descendingSort.first.name,
'lastDesc': descendingSort.last.name,
});
} else {
@@ -410,13 +410,13 @@ class FilterSortTest {
final equipments = await equipmentService.getEquipments();
if (equipments.items.length >= 2) {
// 최신순 정렬
final latestFirst = [...equipments]..sort((a, b) {
final latestFirst = [...equipments.items]..sort((a, b) {
if (a.inDate == null || b.inDate == null) return 0;
return b.inDate!.compareTo(a.inDate!);
});
// 오래된순 정렬
final oldestFirst = [...equipments]..sort((a, b) {
final oldestFirst = [...equipments.items]..sort((a, b) {
if (a.inDate == null || b.inDate == null) return 0;
return a.inDate!.compareTo(b.inDate!);
});
@@ -424,8 +424,8 @@ class FilterSortTest {
result['steps'].add({
'name': 'Equipment 날짜순 정렬',
'status': 'PASS',
'latestDate': latestFirst.items.first.inDate?.toString(),
'oldestDate': oldestFirst.items.first.inDate?.toString(),
'latestDate': latestFirst.first.inDate?.toString(),
'oldestDate': oldestFirst.first.inDate?.toString(),
});
} else {
result['steps'].add({
@@ -441,21 +441,21 @@ class FilterSortTest {
final users = await userService.getUsers();
if (users.items.length >= 2) {
// 이메일 오름차순
final emailAsc = [...users]..sort((a, b) =>
final emailAsc = [...users.items]..sort((a, b) =>
(a.email ?? '').compareTo(b.email ?? '')
);
// 이메일 내림차순
final emailDesc = [...users]..sort((a, b) =>
final emailDesc = [...users.items]..sort((a, b) =>
(b.email ?? '').compareTo(a.email ?? '')
);
result['steps'].add({
'name': 'User 이메일순 정렬',
'status': 'PASS',
'firstEmailAsc': emailAsc.items.first.email,
'firstEmailAsc': emailAsc.first.email,
'lastEmailAsc': emailAsc.last.email,
'firstEmailDesc': emailDesc.items.first.email,
'firstEmailDesc': emailDesc.first.email,
'lastEmailDesc': emailDesc.last.email,
});
} else {
@@ -523,7 +523,7 @@ class FilterSortTest {
'name': 'Company 복합 필터',
'status': 'PASS',
'conditions': '고객사 + 활성',
'count': filteredCustomers.items.length,
'count': filteredCustomers.length,
});
} catch (e) {
result['steps'].add({
@@ -547,7 +547,7 @@ class FilterSortTest {
'name': 'User 복합 필터',
'status': 'PASS',
'conditions': '관리자 + 회사 소속',
'count': companyAdmins.items.length,
'count': companyAdmins.length,
});
// 4. 페이지네이션과 필터 조합
@@ -629,13 +629,13 @@ class FilterSortTest {
}
// 요약
final passedCount = testResults.where((r) => r['overall'] == 'PASS').items.length;
final failedCount = testResults.where((r) => r['overall'] == 'FAIL').items.length;
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.items.length}');
print(' 총 테스트: ${testResults.length}');
// 필터링 기능 분석
print('\n필터링 기능 지원 현황:');

View File

@@ -569,13 +569,13 @@ class FormSubmissionTest {
}
// 요약
final passedCount = testResults.where((r) => r['overall'] == 'PASS').items.length;
final failedCount = testResults.where((r) => r['overall'] == 'FAIL').items.length;
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.items.length}');
print(' 총 테스트: ${testResults.length}');
// 개선 필요 사항
print('\n발견된 문제:');

View File

@@ -232,7 +232,7 @@ abstract class ScreenTestFramework {
await testCase.setup?.call(testData);
// 테스트 실행
await testCase.execute(testData);
await testCase.call(testData);
// 검증
await testCase.verify(testData);

View File

@@ -1,7 +1,7 @@
import 'package:dio/dio.dart';
import 'package:get_it/get_it.dart';
import 'package:superport/data/datasources/remote/api_client.dart';
import 'package:superport/data/datasources/remote/api_interceptor.dart';
import 'package:superport/data/datasources/interceptors/api_interceptor.dart';
import 'package:superport/data/datasources/remote/auth_remote_datasource.dart';
import 'package:superport/data/datasources/remote/company_remote_datasource.dart';
import 'package:superport/data/datasources/remote/equipment_remote_datasource.dart';
@@ -24,29 +24,25 @@ import 'package:superport/domain/usecases/auth/login_usecase.dart';
import 'package:superport/domain/usecases/company/create_company_usecase.dart';
import 'package:superport/domain/usecases/company/delete_company_usecase.dart';
import 'package:superport/domain/usecases/company/get_companies_usecase.dart';
import 'package:superport/domain/usecases/company/get_company_usecase.dart';
import 'package:superport/domain/usecases/company/get_company_detail_usecase.dart';
import 'package:superport/domain/usecases/company/toggle_company_status_usecase.dart';
import 'package:superport/domain/usecases/company/update_company_usecase.dart';
import 'package:superport/domain/usecases/equipment/create_equipment_usecase.dart';
import 'package:superport/domain/usecases/equipment/delete_equipment_usecase.dart';
import 'package:superport/domain/usecases/equipment/equipment_in_usecase.dart';
import 'package:superport/domain/usecases/equipment/equipment_out_usecase.dart';
import 'package:superport/domain/usecases/equipment/get_equipment_usecase.dart';
import 'package:superport/domain/usecases/equipment/get_equipments_usecase.dart';
import 'package:superport/domain/usecases/equipment/update_equipment_usecase.dart';
import 'package:superport/domain/usecases/license/create_license_usecase.dart';
import 'package:superport/domain/usecases/license/delete_license_usecase.dart';
import 'package:superport/domain/usecases/license/get_license_usecase.dart';
import 'package:superport/domain/usecases/license/get_license_detail_usecase.dart';
import 'package:superport/domain/usecases/license/get_licenses_usecase.dart';
import 'package:superport/domain/usecases/license/update_license_usecase.dart';
import 'package:superport/domain/usecases/user/create_user_usecase.dart';
import 'package:superport/domain/usecases/user/delete_user_usecase.dart';
import 'package:superport/domain/usecases/user/get_user_usecase.dart';
import 'package:superport/domain/usecases/user/get_user_detail_usecase.dart';
import 'package:superport/domain/usecases/user/get_users_usecase.dart';
import 'package:superport/domain/usecases/user/update_user_usecase.dart';
import 'package:superport/domain/usecases/warehouse_location/create_warehouse_location_usecase.dart';
import 'package:superport/domain/usecases/warehouse_location/delete_warehouse_location_usecase.dart';
import 'package:superport/domain/usecases/warehouse_location/get_warehouse_location_usecase.dart';
import 'package:superport/domain/usecases/warehouse_location/get_warehouse_location_detail_usecase.dart';
import 'package:superport/domain/usecases/warehouse_location/get_warehouse_locations_usecase.dart';
import 'package:superport/domain/usecases/warehouse_location/update_warehouse_location_usecase.dart';
import 'package:superport/services/auth_service.dart';
@@ -56,6 +52,8 @@ import 'package:superport/services/license_service.dart';
import 'package:superport/services/user_service.dart';
import 'package:superport/services/warehouse_service.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import 'package:superport/core/storage/secure_storage.dart';
/// Real API 테스트 헬퍼
///
@@ -94,8 +92,11 @@ class RealApiTestHelper {
},
));
// SecureStorage 인스턴스 생성
final secureStorage = SecureStorage();
// API 인터셉터 추가
dio.interceptors.add(ApiInterceptor());
dio.interceptors.add(ApiInterceptor(secureStorage));
// 로깅 인터셉터 추가 (디버그용)
dio.interceptors.add(LogInterceptor(
@@ -108,42 +109,47 @@ class RealApiTestHelper {
SharedPreferences.setMockInitialValues({});
final prefs = await SharedPreferences.getInstance();
// FlutterSecureStorage
const flutterSecureStorage = FlutterSecureStorage();
// 의존성 등록 - 순서 중요!
// 1. 기본 인프라
_testGetIt!.registerSingleton<Dio>(dio);
_testGetIt!.registerSingleton<SharedPreferences>(prefs);
_testGetIt!.registerSingleton<FlutterSecureStorage>(flutterSecureStorage);
_testGetIt!.registerSingleton<SecureStorage>(secureStorage);
// 2. API Client
_testGetIt!.registerSingleton<ApiClient>(
ApiClient(dio),
ApiClient(),
);
// 3. Remote DataSources
_testGetIt!.registerLazySingleton<AuthRemoteDataSource>(
() => AuthRemoteDataSource(_testGetIt!<ApiClient>()),
() => AuthRemoteDataSourceImpl(_testGetIt!<ApiClient>()),
);
_testGetIt!.registerLazySingleton<CompanyRemoteDataSource>(
() => CompanyRemoteDataSource(_testGetIt!<ApiClient>()),
() => CompanyRemoteDataSourceImpl(_testGetIt!<ApiClient>()),
);
_testGetIt!.registerLazySingleton<EquipmentRemoteDataSource>(
() => EquipmentRemoteDataSource(_testGetIt!<ApiClient>()),
() => EquipmentRemoteDataSourceImpl(),
);
_testGetIt!.registerLazySingleton<LicenseRemoteDataSource>(
() => LicenseRemoteDataSource(_testGetIt!<ApiClient>()),
() => LicenseRemoteDataSourceImpl(apiClient: _testGetIt!<ApiClient>()),
);
_testGetIt!.registerLazySingleton<UserRemoteDataSource>(
() => UserRemoteDataSource(_testGetIt!<ApiClient>()),
() => UserRemoteDataSourceImpl(_testGetIt!<ApiClient>()),
);
_testGetIt!.registerLazySingleton<WarehouseLocationRemoteDataSource>(
() => WarehouseLocationRemoteDataSource(_testGetIt!<ApiClient>()),
() => WarehouseLocationRemoteDataSourceImpl(apiClient: _testGetIt!<ApiClient>()),
);
// 4. Repositories
_testGetIt!.registerLazySingleton<AuthRepository>(
() => AuthRepositoryImpl(
remoteDataSource: _testGetIt!<AuthRemoteDataSource>(),
prefs: _testGetIt!<SharedPreferences>(),
sharedPreferences: _testGetIt!<SharedPreferences>(),
),
);
_testGetIt!.registerLazySingleton<CompanyRepository>(
@@ -153,7 +159,7 @@ class RealApiTestHelper {
);
_testGetIt!.registerLazySingleton<EquipmentRepository>(
() => EquipmentRepositoryImpl(
remoteDataSource: _testGetIt!<EquipmentRemoteDataSource>(),
_testGetIt!<EquipmentRemoteDataSource>(),
),
);
_testGetIt!.registerLazySingleton<LicenseRepository>(
@@ -172,120 +178,108 @@ class RealApiTestHelper {
),
);
// 5. UseCases - Auth
// 6. UseCases - Auth
_testGetIt!.registerLazySingleton(
() => LoginUseCase(repository: _testGetIt!<AuthRepository>()),
() => LoginUseCase(_testGetIt!<AuthService>()),
);
// 6. UseCases - Company
// 7. UseCases - Company
_testGetIt!.registerLazySingleton(
() => GetCompaniesUseCase(repository: _testGetIt!<CompanyRepository>()),
() => GetCompaniesUseCase(_testGetIt!<CompanyService>()),
);
_testGetIt!.registerLazySingleton(
() => GetCompanyUseCase(repository: _testGetIt!<CompanyRepository>()),
() => GetCompanyDetailUseCase(_testGetIt!<CompanyService>()),
);
_testGetIt!.registerLazySingleton(
() => CreateCompanyUseCase(repository: _testGetIt!<CompanyRepository>()),
() => CreateCompanyUseCase(_testGetIt!<CompanyService>()),
);
_testGetIt!.registerLazySingleton(
() => UpdateCompanyUseCase(repository: _testGetIt!<CompanyRepository>()),
() => UpdateCompanyUseCase(_testGetIt!<CompanyService>()),
);
_testGetIt!.registerLazySingleton(
() => DeleteCompanyUseCase(repository: _testGetIt!<CompanyRepository>()),
() => DeleteCompanyUseCase(_testGetIt!<CompanyService>()),
);
_testGetIt!.registerLazySingleton(
() => ToggleCompanyStatusUseCase(repository: _testGetIt!<CompanyRepository>()),
() => ToggleCompanyStatusUseCase(_testGetIt!<CompanyService>()),
);
// 7. UseCases - Equipment
// 8. UseCases - Equipment
_testGetIt!.registerLazySingleton(
() => GetEquipmentsUseCase(repository: _testGetIt!<EquipmentRepository>()),
() => GetEquipmentsUseCase(_testGetIt!<EquipmentService>()),
);
_testGetIt!.registerLazySingleton(
() => GetEquipmentUseCase(repository: _testGetIt!<EquipmentRepository>()),
() => EquipmentInUseCase(_testGetIt!<EquipmentService>()),
);
_testGetIt!.registerLazySingleton(
() => CreateEquipmentUseCase(repository: _testGetIt!<EquipmentRepository>()),
);
_testGetIt!.registerLazySingleton(
() => UpdateEquipmentUseCase(repository: _testGetIt!<EquipmentRepository>()),
);
_testGetIt!.registerLazySingleton(
() => DeleteEquipmentUseCase(repository: _testGetIt!<EquipmentRepository>()),
);
_testGetIt!.registerLazySingleton(
() => EquipmentInUseCase(repository: _testGetIt!<EquipmentRepository>()),
);
_testGetIt!.registerLazySingleton(
() => EquipmentOutUseCase(repository: _testGetIt!<EquipmentRepository>()),
() => EquipmentOutUseCase(_testGetIt!<EquipmentService>()),
);
// 8. UseCases - License
// 9. UseCases - License
_testGetIt!.registerLazySingleton(
() => GetLicensesUseCase(repository: _testGetIt!<LicenseRepository>()),
() => GetLicensesUseCase(_testGetIt!<LicenseRepository>()),
);
_testGetIt!.registerLazySingleton(
() => GetLicenseUseCase(repository: _testGetIt!<LicenseRepository>()),
() => GetLicenseDetailUseCase(_testGetIt!<LicenseRepository>()),
);
_testGetIt!.registerLazySingleton(
() => CreateLicenseUseCase(repository: _testGetIt!<LicenseRepository>()),
() => CreateLicenseUseCase(_testGetIt!<LicenseRepository>()),
);
_testGetIt!.registerLazySingleton(
() => UpdateLicenseUseCase(repository: _testGetIt!<LicenseRepository>()),
() => UpdateLicenseUseCase(_testGetIt!<LicenseRepository>()),
);
_testGetIt!.registerLazySingleton(
() => DeleteLicenseUseCase(repository: _testGetIt!<LicenseRepository>()),
() => DeleteLicenseUseCase(_testGetIt!<LicenseRepository>()),
);
// 9. UseCases - User
// 10. UseCases - User
_testGetIt!.registerLazySingleton(
() => GetUsersUseCase(repository: _testGetIt!<UserRepository>()),
() => GetUsersUseCase(_testGetIt!<UserService>()),
);
_testGetIt!.registerLazySingleton(
() => GetUserUseCase(repository: _testGetIt!<UserRepository>()),
() => GetUserDetailUseCase(_testGetIt!<UserService>()),
);
_testGetIt!.registerLazySingleton(
() => CreateUserUseCase(repository: _testGetIt!<UserRepository>()),
() => CreateUserUseCase(_testGetIt!<UserService>()),
);
_testGetIt!.registerLazySingleton(
() => UpdateUserUseCase(repository: _testGetIt!<UserRepository>()),
() => UpdateUserUseCase(_testGetIt!<UserService>()),
);
_testGetIt!.registerLazySingleton(
() => DeleteUserUseCase(repository: _testGetIt!<UserRepository>()),
() => DeleteUserUseCase(_testGetIt!<UserService>()),
);
// 10. UseCases - Warehouse Location
// 11. UseCases - Warehouse Location
_testGetIt!.registerLazySingleton(
() => GetWarehouseLocationsUseCase(repository: _testGetIt!<WarehouseLocationRepository>()),
() => GetWarehouseLocationsUseCase(_testGetIt!<WarehouseLocationRepository>()),
);
_testGetIt!.registerLazySingleton(
() => GetWarehouseLocationUseCase(repository: _testGetIt!<WarehouseLocationRepository>()),
() => GetWarehouseLocationDetailUseCase(_testGetIt!<WarehouseLocationRepository>()),
);
_testGetIt!.registerLazySingleton(
() => CreateWarehouseLocationUseCase(repository: _testGetIt!<WarehouseLocationRepository>()),
() => CreateWarehouseLocationUseCase(_testGetIt!<WarehouseLocationRepository>()),
);
_testGetIt!.registerLazySingleton(
() => UpdateWarehouseLocationUseCase(repository: _testGetIt!<WarehouseLocationRepository>()),
() => UpdateWarehouseLocationUseCase(_testGetIt!<WarehouseLocationRepository>()),
);
_testGetIt!.registerLazySingleton(
() => DeleteWarehouseLocationUseCase(repository: _testGetIt!<WarehouseLocationRepository>()),
() => DeleteWarehouseLocationUseCase(_testGetIt!<WarehouseLocationRepository>()),
);
// 11. Services (Legacy - 점진적 마이그레이션을 위해 유지)
// 5. Services (현재 UseCases에서 사용 중)
_testGetIt!.registerLazySingleton<AuthService>(
() => AuthService(),
() => AuthServiceImpl(_testGetIt!<AuthRemoteDataSource>(), _testGetIt!<FlutterSecureStorage>()),
);
_testGetIt!.registerLazySingleton<CompanyService>(
() => CompanyService(),
() => CompanyService(_testGetIt!<CompanyRemoteDataSource>()),
);
_testGetIt!.registerLazySingleton<EquipmentService>(
() => EquipmentService(),
);
_testGetIt!.registerLazySingleton<LicenseService>(
() => LicenseService(),
() => LicenseService(_testGetIt!<LicenseRemoteDataSource>()),
);
_testGetIt!.registerLazySingleton<UserService>(
() => UserService(),
() => UserService(_testGetIt!<UserRemoteDataSource>()),
);
_testGetIt!.registerLazySingleton<WarehouseService>(
() => WarehouseService(),
@@ -303,9 +297,11 @@ class RealApiTestHelper {
final getIt = await setupTestEnvironment();
final loginUseCase = getIt<LoginUseCase>();
final result = await loginUseCase.execute(
email: testEmail,
password: testPassword,
final result = await loginUseCase.call(
LoginParams(
email: testEmail,
password: testPassword,
),
);
return result.fold(

View File

@@ -366,7 +366,7 @@ class CompositeAction extends BaseTestableAction {
continue;
}
final result = await action.execute(tester);
final result = await action.call(tester);
results.add(result);
if (!result.success && stopOnFailure) {
@@ -430,7 +430,7 @@ class ConditionalAction extends BaseTestableAction {
final conditionMet = await condition(tester);
if (conditionMet) {
final result = await trueAction.execute(tester);
final result = await trueAction.call(tester);
return ActionResult(
success: result.success,
message: 'Condition met - ${result.message}',
@@ -441,7 +441,7 @@ class ConditionalAction extends BaseTestableAction {
stackTrace: result.stackTrace,
);
} else if (falseAction != null) {
final result = await falseAction!.execute(tester);
final result = await falseAction!.call(tester);
return ActionResult(
success: result.success,
message: 'Condition not met - ${result.message}',
@@ -497,7 +497,7 @@ class RetryAction extends BaseTestableAction {
continue;
}
lastResult = await action.execute(tester);
lastResult = await action.call(tester);
if (lastResult.success) {
return ActionResult.success(

View File

@@ -126,9 +126,9 @@ class InteractiveSearchTest {
print(' 결과: ${companies?.items.length ?? 0}개 회사 조회됨');
// 2. 특정 검색어 테스트
if (companies != null && companies.isNotEmpty) {
final testCompany = companies.first;
final searchKeyword = testCompany.name.substring(0, testCompany.name.items.length > 3 ? 3 : testCompany.name.items.length);
if (companies != null && companies.items.isNotEmpty) {
final testCompany = companies.items.first;
final searchKeyword = testCompany.name.substring(0, testCompany.name.length > 3 ? 3 : testCompany.name.length);
print('테스트 2: "$searchKeyword" 검색어로 조회');
companies = await companyService.getCompanies(
@@ -185,7 +185,7 @@ class InteractiveSearchTest {
result['tests'].add({
'name': '긴 검색어',
'status': 'PASS',
'keywordLength': longKeyword.items.length,
'keywordLength': longKeyword.length,
});
print(' 결과: 에러 없이 처리됨');
} catch (e) {
@@ -255,7 +255,7 @@ class InteractiveSearchTest {
// 2. 이름으로 검색
if (users != null && users.items.isNotEmpty) {
final testUser = users.items.first;
final searchKeyword = testUser.name.substring(0, testUser.name.items.length > 2 ? 2 : testUser.name.items.length);
final searchKeyword = testUser.name.substring(0, testUser.name.length > 2 ? 2 : testUser.name.length);
print('테스트 2: "$searchKeyword" 검색어로 조회');
// UserService에 search 파라미터 지원 확인 필요
@@ -368,7 +368,7 @@ class InteractiveSearchTest {
if (equipments != null && equipments.items.isNotEmpty) {
final testEquipment = equipments.items.first;
final searchKeyword = testEquipment.manufacturer?.substring(0,
testEquipment.manufacturer!.items.length > 3 ? 3 : testEquipment.manufacturer!.items.length) ?? 'test';
testEquipment.manufacturer!.length > 3 ? 3 : testEquipment.manufacturer!.length) ?? 'test';
print('테스트 2: "$searchKeyword" 검색어로 조회');
equipments = await equipmentService.getEquipmentsWithStatus(

View File

@@ -125,8 +125,8 @@ void _runLicenseTestsInternal() {
// 테스트용 회사 준비
print('🏢 테스트용 회사 준비...');
final companies = await companyService.getCompanies();
if (companies.isNotEmpty) {
testCompany = companies.first;
if (companies.items.isNotEmpty) {
testCompany = companies.items.first;
print('✅ 기존 회사 사용: ${testCompany.name}');
} else {
// 회사가 없으면 생성
@@ -181,9 +181,9 @@ void _runLicenseTestsInternal() {
print('\n 라이센스 생성 테스트...');
// 실제 비즈니스 데이터로 라이센스 생성
final productIndex = random.nextInt(testData['products']!.items.length);
final vendorIndex = random.nextInt(testData['vendors']!.items.length);
final typeIndex = random.nextInt(testData['licenseTypes']!.items.length);
final productIndex = random.nextInt(testData['products']!.length);
final vendorIndex = random.nextInt(testData['vendors']!.length);
final typeIndex = random.nextInt(testData['licenseTypes']!.length);
final newLicense = License(
licenseKey: 'LIC-${DateTime.now().millisecondsSinceEpoch}',
@@ -501,11 +501,11 @@ void _runLicenseTestsInternal() {
final createdIds = <int>[];
for (int i = 0; i < 10; i++) {
final productIndex = random.nextInt(testData['products']!.items.length);
final productIndex = random.nextInt(testData['products']!.length);
final bulkLicense = License(
licenseKey: 'BULK-${DateTime.now().millisecondsSinceEpoch}-$i',
productName: testData['products']![productIndex],
vendor: testData['vendors']![random.nextInt(testData['vendors']!.items.length)],
vendor: testData['vendors']![random.nextInt(testData['vendors']!.length)],
licenseType: 'volume',
userCount: random.nextInt(100) + 10,
purchaseDate: DateTime.now(),

View File

@@ -515,7 +515,7 @@ class MasterTestSuite {
buffer.writeln('| 순위 | 화면 | 소요시간 |');
buffer.writeln('|------|------|----------|');
for (var i = 0; i < 5 && i < sortedByDuration.items.length; i++) {
for (var i = 0; i < 5 && i < sortedByDuration.length; i++) {
final result = sortedByDuration[i];
buffer.writeln('| ${i + 1} | ${result.screenName} | ${_formatDuration(result.duration)} |');
}
@@ -539,7 +539,7 @@ class MasterTestSuite {
buffer.writeln('- 실패한 테스트를 우선적으로 수정하세요');
}
final slowTests = sortedByDuration.items.where((r) => r.duration.inSeconds > 30).items.length;
final slowTests = sortedByDuration.where((r) => r.duration.inSeconds > 30).length;
if (slowTests > 0) {
buffer.writeln('- **$slowTests개 화면**이 30초 이상 소요됩니다');
buffer.writeln('- 성능 최적화를 고려하세요');

View File

@@ -130,10 +130,10 @@ Future<TestResult> runOverviewTests({
final activities = response.data['data'] as List;
if (verbose) debugPrint('✅ 최근 활동 내역 조회 성공: ${activities.items.length}');
if (verbose) debugPrint('✅ 최근 활동 내역 조회 성공: ${activities.length}');
// 최근 5개 활동 표시
final displayCount = activities.items.length > 5 ? 5 : activities.items.length;
final displayCount = activities.length > 5 ? 5 : activities.length;
for (int i = 0; i < displayCount; i++) {
final activity = activities[i];
if (verbose) debugPrint(' ${i + 1}. ${activity['action']} - ${activity['timestamp']}');
@@ -160,7 +160,7 @@ Future<TestResult> runOverviewTests({
final expiringLicenses = response.data['data'] as List;
if (verbose) debugPrint('✅ 만료 예정 라이센스 조회 성공: ${expiringLicenses.items.length}');
if (verbose) debugPrint('✅ 만료 예정 라이센스 조회 성공: ${expiringLicenses.length}');
for (final license in expiringLicenses) {
if (verbose) debugPrint(' - ${license['product_name']}: ${license['expire_date']} 만료');
@@ -176,7 +176,7 @@ Future<TestResult> runOverviewTests({
final altResponse = await dio.get('$baseUrl/licenses/expiring');
if (altResponse.statusCode == 200) {
final licenses = altResponse.data['data'] as List;
if (verbose) debugPrint('✅ 대체 API로 조회 성공: ${licenses.items.length}');
if (verbose) debugPrint('✅ 대체 API로 조회 성공: ${licenses.length}');
passedCount++;
} else {
passedCount++;
@@ -324,10 +324,10 @@ Future<TestResult> runOverviewTests({
final trendData = response.data['data'] as List;
if (verbose) debugPrint('✅ 일별 트렌드 데이터 조회 성공: ${trendData.items.length}일치');
if (verbose) debugPrint('✅ 일별 트렌드 데이터 조회 성공: ${trendData.length}일치');
// 최근 7일 데이터 표시
final displayDays = trendData.items.length > 7 ? 7 : trendData.items.length;
final displayDays = trendData.length > 7 ? 7 : trendData.length;
for (int i = 0; i < displayDays; i++) {
final day = trendData[i];
if (verbose) debugPrint(' - ${day['date']}: 입고 ${day['in_count']}건, 출고 ${day['out_count']}');
@@ -616,10 +616,10 @@ void main() {
final activities = response.data['data'] as List;
debugPrint('✅ 최근 활동 내역 조회 성공: ${activities.items.length}');
debugPrint('✅ 최근 활동 내역 조회 성공: ${activities.length}');
// 최근 5개 활동 표시
final displayCount = activities.items.length > 5 ? 5 : activities.items.length;
final displayCount = activities.length > 5 ? 5 : activities.length;
for (int i = 0; i < displayCount; i++) {
final activity = activities[i];
debugPrint(' ${i + 1}. ${activity['action']} - ${activity['timestamp']}');
@@ -642,7 +642,7 @@ void main() {
final expiringLicenses = response.data['data'] as List;
debugPrint('✅ 만료 예정 라이센스 조회 성공: ${expiringLicenses.items.length}');
debugPrint('✅ 만료 예정 라이센스 조회 성공: ${expiringLicenses.length}');
for (final license in expiringLicenses) {
debugPrint(' - ${license['product_name']}: ${license['expire_date']} 만료');
@@ -656,7 +656,7 @@ void main() {
final altResponse = await dio.get('$baseUrl/licenses/expiring');
if (altResponse.statusCode == 200) {
final licenses = altResponse.data['data'] as List;
debugPrint('✅ 대체 API로 조회 성공: ${licenses.items.length}');
debugPrint('✅ 대체 API로 조회 성공: ${licenses.length}');
}
} catch (e) {
debugPrint('⚠️ 대체 방법도 실패: $e');
@@ -780,10 +780,10 @@ void main() {
final trendData = response.data['data'] as List;
debugPrint('✅ 일별 트렌드 데이터 조회 성공: ${trendData.items.length}일치');
debugPrint('✅ 일별 트렌드 데이터 조회 성공: ${trendData.length}일치');
// 최근 7일 데이터 표시
final displayDays = trendData.items.length > 7 ? 7 : trendData.items.length;
final displayDays = trendData.length > 7 ? 7 : trendData.length;
for (int i = 0; i < displayDays; i++) {
final day = trendData[i];
debugPrint(' - ${day['date']}: 입고 ${day['in_count']}건, 출고 ${day['out_count']}');

View File

@@ -348,10 +348,10 @@ class PaginationTest {
result['steps'].add({
'name': 'Company perPage=$size',
'status': companies.length <= size ? 'PASS' : 'FAIL',
'status': companies.items.length <= size ? 'PASS' : 'FAIL',
'requested': size,
'received': companies.length,
'valid': companies.length <= size,
'received': companies.items.length,
'valid': companies.items.length <= size,
});
} catch (e) {
result['steps'].add({

View File

@@ -135,7 +135,7 @@ void main() {
// 자동 수정된 항목
final fixes = reportCollector.getAutoFixes();
if (fixes.items.isNotEmpty) {
if (fixes.isNotEmpty) {
debugPrint('\n=== 자동 수정된 항목 ===');
for (final fix in fixes) {
debugPrint('- ${fix.errorType}: ${fix.solution}');

View File

@@ -55,11 +55,11 @@ void main() {
// 메타데이터 가져오기
final metadata = overviewTest.getScreenMetadata();
debugPrint('화면: ${metadata.screenName}');
debugPrint('엔드포인트 수: ${metadata.relatedEndpoints.items.length}');
debugPrint('엔드포인트 수: ${metadata.relatedEndpoints.length}');
// 기능 감지
final features = await overviewTest.detectFeatures(metadata);
debugPrint('감지된 기능: ${features.items.length}');
debugPrint('감지된 기능: ${features.length}');
// 테스트 실행
final result = await overviewTest.executeTests(features);

View File

@@ -62,17 +62,17 @@ void main() {
'테스트 이름: ${report.testName}\n'
'테스트 결과: ${report.testResult.passedTests == report.testResult.totalTests ? '성공' : '실패'}\n'
'소요 시간: ${report.duration}\n'
'에러 수: ${report.errors.items.length}',
'에러 수: ${report.errors.length}',
details: {
'testName': report.testName,
'passed': report.testResult.passedTests == report.testResult.totalTests,
'duration': report.duration.toString(),
'errorCount': report.errors.items.length,
'errorCount': report.errors.length,
},
),
);
if (report.errors.items.isNotEmpty) {
if (report.errors.isNotEmpty) {
for (final error in report.errors) {
reportCollector.addError(
report_models.ErrorReport(

View File

@@ -128,7 +128,7 @@ abstract class BaseScreenTest extends ScreenTestFramework {
// 기능 감지
final features = await detectFeatures(metadata);
_log('감지된 기능: ${features.items.map((f) => f.featureName).join(', ')}');
_log('감지된 기능: ${features.map((f) => f.featureName).join(', ')}');
// 테스트 실행
final result = await executeTests(features);
@@ -200,7 +200,7 @@ abstract class BaseScreenTest extends ScreenTestFramework {
final companyService = getIt.get<CompanyService>();
final companies = await companyService.getCompanies(page: 1, perPage: 1);
if (companies.isEmpty) {
if (companies.items.isEmpty) {
// 테스트용 회사 생성
final companyData = await dataGenerator.generate(
GenerationStrategy(
@@ -214,7 +214,7 @@ abstract class BaseScreenTest extends ScreenTestFramework {
final company = await companyService.createCompany(companyData.data);
testContext.setData('testCompanyId', company.id);
} else {
testContext.setData('testCompanyId', companies.first.id);
testContext.setData('testCompanyId', companies.items.first.id);
}
} catch (e) {
// 회사 생성은 선택사항이므로 에러 무시
@@ -234,7 +234,7 @@ abstract class BaseScreenTest extends ScreenTestFramework {
perPage: 1,
);
if (warehouses.isEmpty) {
if (warehouses.items.isEmpty) {
// 테스트용 창고 생성
final warehouseData = await dataGenerator.generate(
GenerationStrategy(
@@ -249,7 +249,7 @@ abstract class BaseScreenTest extends ScreenTestFramework {
final warehouse = await warehouseService.createWarehouseLocation(warehouseData.data);
testContext.setData('testWarehouseId', warehouse.id);
} else {
testContext.setData('testWarehouseId', warehouses.first.id);
testContext.setData('testWarehouseId', warehouses.items.first.id);
}
}
} catch (e) {
@@ -266,7 +266,7 @@ abstract class BaseScreenTest extends ScreenTestFramework {
// createdIds를 resourceType별로 분류
for (final id in createdIds) {
final parts = id.split(':');
if (parts.items.length == 2) {
if (parts.length == 2) {
final resourceType = parts[0];
final resourceId = parts[1];
resourcesByType.putIfAbsent(resourceType, () => []).add(resourceId);
@@ -375,9 +375,9 @@ abstract class BaseScreenTest extends ScreenTestFramework {
);
testContext.setData('readResults', results);
testContext.setData('readCount', results is List ? results.items.length : 1);
testContext.setData('readCount', results is List ? results.length : 1);
_log('[READ] 성공: ${results is List ? results.items.length : 1}개 항목');
_log('[READ] 성공: ${results is List ? results.length : 1}개 항목');
} catch (e) {
_log('[READ] 실패: $e');
@@ -496,7 +496,7 @@ abstract class BaseScreenTest extends ScreenTestFramework {
await performCreate(data);
final service = getService();
final searchKeyword = data.data['name']?.toString().split(' ').items.first ?? 'test';
final searchKeyword = data.data['name']?.toString().split(' ').first ?? 'test';
final results = await service.search(searchKeyword);
testContext.setData('searchResults', results);
@@ -511,9 +511,9 @@ abstract class BaseScreenTest extends ScreenTestFramework {
expect(searchResults, isNotNull, reason: '검색 결과가 없음');
expect(searchResults, isA<List>(), reason: '올바른 검색 결과 형식이 아님');
if (searchResults.items.isNotEmpty) {
if (searchResults.isNotEmpty) {
// 검색 결과가 키워드를 포함하는지 확인
final firstResult = searchResults.items.first;
final firstResult = searchResults.first;
expect(
firstResult.toString().toLowerCase(),
contains(searchKeyword.toLowerCase()),
@@ -564,9 +564,9 @@ abstract class BaseScreenTest extends ScreenTestFramework {
expect(page2Results, isNotNull, reason: '두 번째 페이지 결과가 없음');
// 페이지별 결과가 다른지 확인 (데이터가 충분한 경우)
if (page1Results.items.isNotEmpty && page2Results.items.isNotEmpty) {
if (page1Results.isNotEmpty && page2Results.isNotEmpty) {
expect(
page1Results.items.first.id != page2Results.items.first.id,
page1Results.first.id != page2Results.first.id,
isTrue,
reason: '페이지네이션이 올바르게 작동하지 않음',
);
@@ -658,7 +658,7 @@ abstract class BaseScreenTest extends ScreenTestFramework {
final fixResult = await autoFixer.attemptAutoFix(diagnosis);
if (fixResult.success) {
_log('자동 수정 성공: ${fixResult.executedActions.items.length}개 액션 적용');
_log('자동 수정 성공: ${fixResult.executedActions.length}개 액션 적용');
// 수정 액션 적용 (AutoFixResult는 String 액션을 반환)
// TODO: String 액션을 FixAction으로 변환하거나 별도 처리 필요

View File

@@ -211,7 +211,7 @@ class CompanyScreenTest extends BaseScreenTest {
businessNumber: '${1234567890 + i}',
);
final result = await createCompanyUseCase.execute(companyData);
final result = await createCompanyUseCase.call(companyData);
result.fold(
(failure) => _log('회사 생성 실패: ${failure.message}'),
(company) {
@@ -229,7 +229,7 @@ class CompanyScreenTest extends BaseScreenTest {
Future<void> _cleanupTestCompanies() async {
for (final id in createdCompanyIds) {
try {
await deleteCompanyUseCase.execute(id);
await deleteCompanyUseCase.call(id);
_log('테스트 회사 삭제: ID $id');
} catch (e) {
_log('회사 삭제 실패 (ID: $id): $e');
@@ -240,17 +240,16 @@ class CompanyScreenTest extends BaseScreenTest {
/// 회사 목록 조회 테스트
Future<void> _testGetCompanyList() async {
final result = await getCompaniesUseCase.execute(
final result = await getCompaniesUseCase.call(
page: 1,
size: 10,
);
result.fold(
(failure) => throw TestException('회사 목록 조회 실패: ${failure.message}'),
(response) {
assert(response.companies.isNotEmpty, '회사 목록이 비어있음');
assert(response.totalCount > 0, '전체 개수가 0');
_log('회사 목록 조회 성공: ${response.companies.length}');
(companies) {
assert(companies.isNotEmpty, '회사 목록이 비어있음');
_log('회사 목록 조회 성공: ${companies.length}');
},
);
}
@@ -258,7 +257,7 @@ class CompanyScreenTest extends BaseScreenTest {
/// 페이징 테스트
Future<void> _testPagination() async {
// 첫 페이지
final page1Result = await getCompaniesUseCase.execute(
final page1Result = await getCompaniesUseCase.call(
page: 1,
size: 5,
);
@@ -267,7 +266,7 @@ class CompanyScreenTest extends BaseScreenTest {
(failure) => throw TestException('페이지 1 조회 실패: ${failure.message}'),
(page1) async {
// 두 번째 페이지
final page2Result = await getCompaniesUseCase.execute(
final page2Result = await getCompaniesUseCase.call(
page: 2,
size: 5,
);
@@ -276,9 +275,9 @@ class CompanyScreenTest extends BaseScreenTest {
(failure) => _log('페이지 2 조회 실패 (데이터 부족일 수 있음): ${failure.message}'),
(page2) {
// 페이지별 데이터가 다른지 확인
if (page2.companies.isNotEmpty) {
final page1Ids = page1.companies.map((c) => c.id).toSet();
final page2Ids = page2.companies.map((c) => c.id).toSet();
if (page2.isNotEmpty) {
final page1Ids = page1.map((c) => c.id).toSet();
final page2Ids = page2.map((c) => c.id).toSet();
assert(page1Ids.intersection(page2Ids).isEmpty, '페이지 간 데이터 중복');
}
_log('페이징 테스트 성공');
@@ -291,7 +290,7 @@ class CompanyScreenTest extends BaseScreenTest {
/// 검색 테스트
Future<void> _testSearch() async {
final searchTerm = '테스트회사_$testSessionId';
final result = await getCompaniesUseCase.execute(
final result = await getCompaniesUseCase.call(
page: 1,
size: 10,
search: searchTerm,
@@ -299,15 +298,15 @@ class CompanyScreenTest extends BaseScreenTest {
result.fold(
(failure) => throw TestException('검색 실패: ${failure.message}'),
(response) {
for (final company in response.companies) {
(companies) {
for (final company in companies) {
assert(
company.name.contains(searchTerm) ||
company.businessNumber.contains(searchTerm),
'검색 결과가 검색어와 매치되지 않음'
);
}
_log('검색 테스트 성공: ${response.companies.length}개 검색됨');
_log('검색 테스트 성공: ${companies.length}개 검색됨');
},
);
}
@@ -319,7 +318,7 @@ class CompanyScreenTest extends BaseScreenTest {
businessNumber: '${DateTime.now().millisecondsSinceEpoch}',
);
final result = await createCompanyUseCase.execute(companyData);
final result = await createCompanyUseCase.call(companyData);
result.fold(
(failure) => throw TestException('회사 생성 실패: ${failure.message}'),
@@ -341,7 +340,7 @@ class CompanyScreenTest extends BaseScreenTest {
);
// 첫 번째 생성 (성공해야 함)
final result1 = await createCompanyUseCase.execute(company1);
final result1 = await createCompanyUseCase.call(company1);
int? firstId;
result1.fold(
@@ -358,7 +357,7 @@ class CompanyScreenTest extends BaseScreenTest {
businessNumber: businessNumber,
);
final result2 = await createCompanyUseCase.execute(company2);
final result2 = await createCompanyUseCase.call(company2);
result2.fold(
(failure) => _log('중복 체크 성공: ${failure.message}'),
@@ -381,7 +380,7 @@ class CompanyScreenTest extends BaseScreenTest {
isActive: true,
);
final result = await createCompanyUseCase.execute(invalidCompany);
final result = await createCompanyUseCase.call(invalidCompany);
result.fold(
(failure) => _log('필수 필드 검증 성공: ${failure.message}'),
@@ -404,7 +403,7 @@ class CompanyScreenTest extends BaseScreenTest {
businessNumber: '1111111111',
);
final result = await updateCompanyUseCase.execute(
final result = await updateCompanyUseCase.call(
id: companyId,
company: updatedData,
);
@@ -440,13 +439,13 @@ class CompanyScreenTest extends BaseScreenTest {
businessNumber: '${DateTime.now().millisecondsSinceEpoch}',
);
final createResult = await createCompanyUseCase.execute(companyData);
final createResult = await createCompanyUseCase.call(companyData);
createResult.fold(
(failure) => throw TestException('삭제 테스트용 회사 생성 실패: ${failure.message}'),
(company) async {
// 삭제
final deleteResult = await deleteCompanyUseCase.execute(company.id!);
final deleteResult = await deleteCompanyUseCase.call(company.id!);
deleteResult.fold(
(failure) => throw TestException('회사 삭제 실패: ${failure.message}'),
@@ -472,7 +471,7 @@ class CompanyScreenTest extends BaseScreenTest {
final companyId = createdCompanyIds.first;
// 현재 상태를 비활성으로 변경
final result = await toggleCompanyStatusUseCase.execute(
final result = await toggleCompanyStatusUseCase.call(
id: companyId,
isActive: false,
);
@@ -486,7 +485,7 @@ class CompanyScreenTest extends BaseScreenTest {
);
// 다시 활성으로 변경
final result2 = await toggleCompanyStatusUseCase.execute(
final result2 = await toggleCompanyStatusUseCase.call(
id: companyId,
isActive: true,
);

View File

@@ -1,19 +0,0 @@
// 수정 사항들을 정리한 파일
// 1. controllerType 수정
// Line 55: controllerType: null -> controllerType: EquipmentService
// 2. nullable ID 수정 (Equipment.id는 int?이므로 null check 필요)
// Lines 309, 317, 347, 354, 368: createdEquipment.id -> createdEquipment.id!
// Lines 548, 556, 588, 595: createdEquipment.id -> createdEquipment.id!
// Lines 782, 799, 806: equipment.id -> equipment.id!
// 3. CreateCompanyRequest에 contactPosition 추가
// Line 739: contactPosition: 'Manager' 추가
// 4. 서비스 메서드 호출 수정
// createCompany: CreateCompanyRequest가 아닌 Company 객체 필요
// createWarehouseLocation: CreateWarehouseLocationRequest가 아닌 WarehouseLocation 객체 필요
// 5. StepReport import 추가
// import '../../framework/models/report_models.dart';

View File

@@ -3,7 +3,7 @@
import 'package:flutter/foundation.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:get_it/get_it.dart';
import 'package:superport/di/injection_container.dart';
import 'package:superport/injection_container.dart';
import 'package:superport/data/datasources/remote/api_client.dart';
import 'license_screen_test.dart';
import '../../framework/infrastructure/test_context.dart';

View File

@@ -223,7 +223,7 @@ class UserScreenTest extends BaseScreenTest {
password: 'Test1234!',
);
final result = await createUserUseCase.execute(userData);
final result = await createUserUseCase.call(userData);
result.fold(
(failure) => _log('사용자 생성 실패: ${failure.message}'),
(user) {
@@ -241,7 +241,7 @@ class UserScreenTest extends BaseScreenTest {
Future<void> _cleanupTestUsers() async {
for (final id in createdUserIds) {
try {
await deleteUserUseCase.execute(id);
await deleteUserUseCase.call(id);
_log('테스트 사용자 삭제: ID $id');
} catch (e) {
_log('사용자 삭제 실패 (ID: $id): $e');
@@ -252,7 +252,7 @@ class UserScreenTest extends BaseScreenTest {
/// 사용자 목록 조회 테스트
Future<void> _testGetUserList() async {
final result = await getUsersUseCase.execute(
final result = await getUsersUseCase.call(
page: 1,
size: 10,
);
@@ -270,7 +270,7 @@ class UserScreenTest extends BaseScreenTest {
/// 페이징 테스트
Future<void> _testPagination() async {
// 첫 페이지
final page1Result = await getUsersUseCase.execute(
final page1Result = await getUsersUseCase.call(
page: 1,
size: 5,
);
@@ -279,7 +279,7 @@ class UserScreenTest extends BaseScreenTest {
(failure) => throw TestException('페이지 1 조회 실패: ${failure.message}'),
(page1) async {
// 두 번째 페이지
final page2Result = await getUsersUseCase.execute(
final page2Result = await getUsersUseCase.call(
page: 2,
size: 5,
);
@@ -303,7 +303,7 @@ class UserScreenTest extends BaseScreenTest {
/// 검색 테스트
Future<void> _testSearch() async {
final searchTerm = 'test_$testSessionId';
final result = await getUsersUseCase.execute(
final result = await getUsersUseCase.call(
page: 1,
size: 10,
search: searchTerm,
@@ -333,7 +333,7 @@ class UserScreenTest extends BaseScreenTest {
password: 'Admin1234!',
);
final result = await createUserUseCase.execute(userData);
final result = await createUserUseCase.call(userData);
result.fold(
(failure) => throw TestException('Admin 사용자 생성 실패: ${failure.message}'),
@@ -355,7 +355,7 @@ class UserScreenTest extends BaseScreenTest {
password: 'Manager1234!',
);
final result = await createUserUseCase.execute(userData);
final result = await createUserUseCase.call(userData);
result.fold(
(failure) => throw TestException('Manager 사용자 생성 실패: ${failure.message}'),
@@ -377,7 +377,7 @@ class UserScreenTest extends BaseScreenTest {
password: 'Member1234!',
);
final result = await createUserUseCase.execute(userData);
final result = await createUserUseCase.call(userData);
result.fold(
(failure) => throw TestException('Member 사용자 생성 실패: ${failure.message}'),
@@ -402,7 +402,7 @@ class UserScreenTest extends BaseScreenTest {
password: 'Test1234!',
);
final result1 = await createUserUseCase.execute(user1);
final result1 = await createUserUseCase.call(user1);
result1.fold(
(failure) => throw TestException('첫 번째 사용자 생성 실패: ${failure.message}'),
@@ -417,7 +417,7 @@ class UserScreenTest extends BaseScreenTest {
password: 'Test1234!',
);
final result2 = await createUserUseCase.execute(user2);
final result2 = await createUserUseCase.call(user2);
result2.fold(
(failure) => _log('이메일 중복 체크 성공: ${failure.message}'),
@@ -441,7 +441,7 @@ class UserScreenTest extends BaseScreenTest {
role: UserRole.member,
);
final result = await updateUserUseCase.execute(
final result = await updateUserUseCase.call(
id: userId,
user: updatedData,
);
@@ -470,7 +470,7 @@ class UserScreenTest extends BaseScreenTest {
role: UserRole.member,
);
final result1 = await updateUserUseCase.execute(
final result1 = await updateUserUseCase.call(
id: userId,
user: memberData,
);
@@ -486,7 +486,7 @@ class UserScreenTest extends BaseScreenTest {
// Manager로 변경
final managerData = memberData.copyWith(role: UserRole.manager);
final result2 = await updateUserUseCase.execute(
final result2 = await updateUserUseCase.call(
id: userId,
user: managerData,
);
@@ -517,13 +517,13 @@ class UserScreenTest extends BaseScreenTest {
password: 'Test1234!',
);
final createResult = await createUserUseCase.execute(userData);
final createResult = await createUserUseCase.call(userData);
createResult.fold(
(failure) => throw TestException('삭제 테스트용 사용자 생성 실패: ${failure.message}'),
(user) async {
// 삭제
final deleteResult = await deleteUserUseCase.execute(user.id!);
final deleteResult = await deleteUserUseCase.call(user.id!);
deleteResult.fold(
(failure) => throw TestException('사용자 삭제 실패: ${failure.message}'),

View File

@@ -207,7 +207,7 @@ class WarehouseScreenTest extends BaseScreenTest {
address: '서울시 강남구 테스트로 $i',
);
final result = await createWarehouseLocationUseCase.execute(warehouseData);
final result = await createWarehouseLocationUseCase.call(warehouseData);
result.fold(
(failure) => _log('창고 위치 생성 실패: ${failure.message}'),
(warehouse) {
@@ -225,7 +225,7 @@ class WarehouseScreenTest extends BaseScreenTest {
Future<void> _cleanupTestWarehouses() async {
for (final id in createdWarehouseIds) {
try {
await deleteWarehouseLocationUseCase.execute(id);
await deleteWarehouseLocationUseCase.call(id);
_log('테스트 창고 위치 삭제: ID $id');
} catch (e) {
_log('창고 위치 삭제 실패 (ID: $id): $e');
@@ -236,7 +236,7 @@ class WarehouseScreenTest extends BaseScreenTest {
/// 창고 위치 목록 조회 테스트
Future<void> _testGetWarehouseList() async {
final result = await getWarehouseLocationsUseCase.execute(
final result = await getWarehouseLocationsUseCase.call(
page: 1,
size: 10,
);
@@ -254,7 +254,7 @@ class WarehouseScreenTest extends BaseScreenTest {
/// 페이징 테스트
Future<void> _testPagination() async {
// 첫 페이지
final page1Result = await getWarehouseLocationsUseCase.execute(
final page1Result = await getWarehouseLocationsUseCase.call(
page: 1,
size: 5,
);
@@ -263,7 +263,7 @@ class WarehouseScreenTest extends BaseScreenTest {
(failure) => throw TestException('페이지 1 조회 실패: ${failure.message}'),
(page1) async {
// 두 번째 페이지
final page2Result = await getWarehouseLocationsUseCase.execute(
final page2Result = await getWarehouseLocationsUseCase.call(
page: 2,
size: 5,
);
@@ -287,7 +287,7 @@ class WarehouseScreenTest extends BaseScreenTest {
/// 검색 테스트
Future<void> _testSearch() async {
final searchTerm = '테스트창고_$testSessionId';
final result = await getWarehouseLocationsUseCase.execute(
final result = await getWarehouseLocationsUseCase.call(
page: 1,
size: 10,
search: searchTerm,
@@ -315,7 +315,7 @@ class WarehouseScreenTest extends BaseScreenTest {
address: '서울시 서초구 신규로 123',
);
final result = await createWarehouseLocationUseCase.execute(warehouseData);
final result = await createWarehouseLocationUseCase.call(warehouseData);
result.fold(
(failure) => throw TestException('창고 위치 생성 실패: ${failure.message}'),
@@ -338,7 +338,7 @@ class WarehouseScreenTest extends BaseScreenTest {
address: '주소1',
);
final result1 = await createWarehouseLocationUseCase.execute(warehouse1);
final result1 = await createWarehouseLocationUseCase.call(warehouse1);
result1.fold(
(failure) => throw TestException('첫 번째 창고 생성 실패: ${failure.message}'),
@@ -351,7 +351,7 @@ class WarehouseScreenTest extends BaseScreenTest {
address: '주소2',
);
final result2 = await createWarehouseLocationUseCase.execute(warehouse2);
final result2 = await createWarehouseLocationUseCase.call(warehouse2);
result2.fold(
(failure) => _log('중복 체크 - 실패 처리됨: ${failure.message}'),
@@ -372,7 +372,7 @@ class WarehouseScreenTest extends BaseScreenTest {
manager: '',
);
final result = await createWarehouseLocationUseCase.execute(invalidWarehouse);
final result = await createWarehouseLocationUseCase.call(invalidWarehouse);
result.fold(
(failure) => _log('필수 필드 검증 성공: ${failure.message}'),
@@ -395,7 +395,7 @@ class WarehouseScreenTest extends BaseScreenTest {
address: '수정된 주소',
);
final result = await updateWarehouseLocationUseCase.execute(
final result = await updateWarehouseLocationUseCase.call(
id: warehouseId,
warehouseLocation: updatedData,
);
@@ -424,7 +424,7 @@ class WarehouseScreenTest extends BaseScreenTest {
address: newAddress,
);
final result = await updateWarehouseLocationUseCase.execute(
final result = await updateWarehouseLocationUseCase.call(
id: warehouseId,
warehouseLocation: warehouse,
);
@@ -454,7 +454,7 @@ class WarehouseScreenTest extends BaseScreenTest {
)..manager = newManager
..phone = newPhone;
final result = await updateWarehouseLocationUseCase.execute(
final result = await updateWarehouseLocationUseCase.call(
id: warehouseId,
warehouseLocation: warehouse,
);
@@ -477,13 +477,13 @@ class WarehouseScreenTest extends BaseScreenTest {
address: '삭제될 주소',
);
final createResult = await createWarehouseLocationUseCase.execute(warehouseData);
final createResult = await createWarehouseLocationUseCase.call(warehouseData);
createResult.fold(
(failure) => throw TestException('삭제 테스트용 창고 생성 실패: ${failure.message}'),
(warehouse) async {
// 삭제
final deleteResult = await deleteWarehouseLocationUseCase.execute(warehouse.id!);
final deleteResult = await deleteWarehouseLocationUseCase.call(warehouse.id!);
deleteResult.fold(
(failure) => throw TestException('창고 위치 삭제 실패: ${failure.message}'),

View File

@@ -82,7 +82,7 @@ class TestSuiteResult {
buffer.writeln('⚠️ 실패한 테스트가 있습니다.');
buffer.writeln('\n실패한 테스트 목록:');
for (final result in results) {
if (result.failedTestNames.items.isNotEmpty) {
if (result.failedTestNames.isNotEmpty) {
buffer.writeln('\n${result.name}:');
for (final testName in result.failedTestNames) {
buffer.writeln(' - $testName');
@@ -102,6 +102,6 @@ class TestSuiteResult {
'failedTests': failedTests,
'overallPassRate': overallPassRate,
'totalExecutionTimeMs': totalExecutionTime.inMilliseconds,
'results': results.items.map((r) => r.toJson()).toList(),
'results': results.map((r) => r.toJson()).toList(),
};
}

View File

@@ -2,7 +2,7 @@ import 'package:flutter_test/flutter_test.dart';
import 'package:flutter/material.dart';
import 'package:superport/main.dart' as app;
import 'package:get_it/get_it.dart';
import 'package:superport/di/injection_container.dart' as di;
import 'package:superport/injection_container.dart' as di;
import 'package:flutter_dotenv/flutter_dotenv.dart';
import 'package:superport/services/company_service.dart';
import 'package:superport/services/equipment_service.dart';

View File

@@ -707,7 +707,7 @@ class UserAutomatedTest extends BaseScreenTest {
role: data.data['role'],
);
// PaginatedResponse의 items를 반환하여 List처럼 사용할 수 있도록 함
return result.items;
return result;
}
@override

View File

@@ -1,17 +0,0 @@
import 'package:flutter_test/flutter_test.dart';
/// 사용자(User) 화면 자동화 테스트 (플레이스홀더)
///
/// 이 클래스는 원래 UserAutomatedTest의 플레이스홀더입니다.
/// 필요한 import와 의존성을 추가하여 실제 구현을 완성해주세요.
class UserAutomatedTestPlaceholder {
// 플레이스홀더 구현
}
void main() {
group('User Automated Test Placeholder', () {
test('This is a placeholder test class', () {
// expect(true, isTrue);
});
});
}

View File

@@ -60,7 +60,7 @@ Future<TestResult> runUserTests({
if (response.statusCode == 200) {
final users = response.data['data'] ?? [];
if (verbose) {
debugPrint('✅ 사용자 ${users.items.length}개 조회 성공');
debugPrint('✅ 사용자 ${users.length}개 조회 성공');
}
passedTests++;
} else {
@@ -78,8 +78,8 @@ Future<TestResult> runUserTests({
if (verbose) debugPrint('\n 일반 사용자 생성 테스트...');
final timestamp = DateTime.now().millisecondsSinceEpoch;
final nameIndex = random.nextInt(testUserData['names']!.items.length);
final deptIndex = random.nextInt(testUserData['departments']!.items.length);
final nameIndex = random.nextInt(testUserData['names']!.length);
final deptIndex = random.nextInt(testUserData['departments']!.length);
try {
final newUser = {
@@ -88,7 +88,7 @@ Future<TestResult> runUserTests({
'password': 'Password123!',
'name': testUserData['names']![nameIndex],
'department': testUserData['departments']![deptIndex],
'position': testUserData['positions']![random.nextInt(testUserData['positions']!.items.length)],
'position': testUserData['positions']![random.nextInt(testUserData['positions']!.length)],
'phone': '010-${1000 + random.nextInt(9000)}-${1000 + random.nextInt(9000)}',
'role': 'user', // 일반 사용자
};
@@ -169,14 +169,14 @@ Future<TestResult> runUserTests({
totalTests++;
if (verbose) debugPrint('\n🔍 사용자 상세 조회 테스트...');
if (createdUserIds.items.isEmpty) {
if (createdUserIds.isEmpty) {
failedTestNames.add('사용자 상세 조회');
if (verbose) debugPrint('⚠️ 조회할 사용자가 없음');
return;
}
try {
final userId = createdUserIds.items.first;
final userId = createdUserIds.first;
final response = await dio.get('$baseUrl/users/$userId');
if (response.statusCode == 200) {
@@ -204,14 +204,14 @@ Future<TestResult> runUserTests({
totalTests++;
if (verbose) debugPrint('\n✏️ 사용자 정보 수정 테스트...');
if (createdUserIds.items.isEmpty) {
if (createdUserIds.isEmpty) {
failedTestNames.add('사용자 정보 수정');
if (verbose) debugPrint('⚠️ 수정할 사용자가 없음');
return;
}
try {
final userId = createdUserIds.items.first;
final userId = createdUserIds.first;
final updatedData = {
'name': '수정된이름_${random.nextInt(1000)}',
'department': '수정된부서',
@@ -243,14 +243,14 @@ Future<TestResult> runUserTests({
totalTests++;
if (verbose) debugPrint('\n🔐 비밀번호 변경 테스트...');
if (createdUserIds.items.isEmpty) {
if (createdUserIds.isEmpty) {
failedTestNames.add('비밀번호 변경');
if (verbose) debugPrint('⚠️ 대상 사용자가 없음');
return;
}
try {
final userId = createdUserIds.items.first;
final userId = createdUserIds.first;
final passwordData = {
'current_password': 'Password123!',
'new_password': 'NewPassword456!',
@@ -281,7 +281,7 @@ Future<TestResult> runUserTests({
totalTests++;
if (verbose) debugPrint('\n👤 사용자 권한 변경 테스트...');
if (createdUserIds.items.length < 2) {
if (createdUserIds.length < 2) {
failedTestNames.add('사용자 권한 변경');
if (verbose) debugPrint('⚠️ 권한 변경할 사용자가 부족');
return;
@@ -317,14 +317,14 @@ Future<TestResult> runUserTests({
totalTests++;
if (verbose) debugPrint('\n🔄 사용자 비활성화/활성화 테스트...');
if (createdUserIds.items.isEmpty) {
if (createdUserIds.isEmpty) {
failedTestNames.add('사용자 비활성화/활성화');
if (verbose) debugPrint('⚠️ 대상 사용자가 없음');
return;
}
try {
final userId = createdUserIds.items.first;
final userId = createdUserIds.first;
// 비활성화
var response = await dio.patch(
@@ -375,7 +375,7 @@ Future<TestResult> runUserTests({
if (response.statusCode == 200) {
final results = response.data['data'] ?? [];
if (verbose) {
debugPrint('✅ 사용자 검색 성공: ${results.items.length}개 결과');
debugPrint('✅ 사용자 검색 성공: ${results.length}개 결과');
}
passedTests++;
} else {
@@ -392,7 +392,7 @@ Future<TestResult> runUserTests({
totalTests++;
if (verbose) debugPrint('\n🗑️ 사용자 삭제 테스트...');
if (createdUserIds.items.isEmpty) {
if (createdUserIds.isEmpty) {
failedTestNames.add('사용자 삭제');
if (verbose) debugPrint('⚠️ 삭제할 사용자가 없음');
return;

View File

@@ -221,7 +221,7 @@ class WarehouseAutomatedTest extends BaseScreenTest {
perPage: 20,
);
// PaginatedResponse의 items를 반환하여 List처럼 사용할 수 있도록 함
return result.items;
return result;
}
@override
@@ -312,9 +312,9 @@ class WarehouseTestData {
final purposes = ['물류', '보관', '배송', '집하', '분류', '냉동', '냉장', '특수', '일반', '대형'];
final suffixes = ['창고', '센터', '물류센터', '보관소', '집하장'];
final type = types[random.nextInt(types.items.length)];
final purpose = purposes[random.nextInt(purposes.items.length)];
final suffix = suffixes[random.nextInt(suffixes.items.length)];
final type = types[random.nextInt(types.length)];
final purpose = purposes[random.nextInt(purposes.length)];
final suffix = suffixes[random.nextInt(suffixes.length)];
final timestamp = DateTime.now().millisecondsSinceEpoch;
return '$type $purpose$suffix - TEST$timestamp';
@@ -331,9 +331,9 @@ class WarehouseTestData {
'산업단지', '물류단지', '유통단지', '첨단산업단지', '일반산업단지', '국가산업단지'
];
final city = cities[random.nextInt(cities.items.length)];
final district = districts[random.nextInt(districts.items.length)];
final industrial = industrialAreas[random.nextInt(industrialAreas.items.length)];
final city = cities[random.nextInt(cities.length)];
final district = districts[random.nextInt(districts.length)];
final industrial = industrialAreas[random.nextInt(industrialAreas.length)];
final number = random.nextInt(500) + 1;
final detail = '$industrial $number블록 ${random.nextInt(10) + 1}';
@@ -362,7 +362,7 @@ class WarehouseTestData {
final featureCount = random.nextInt(3) + 1; // 1-3개 특징
for (int i = 0; i < featureCount; i++) {
final feature = features[random.nextInt(features.items.length)];
final feature = features[random.nextInt(features.length)];
if (!selectedFeatures.contains(feature)) {
selectedFeatures.add(feature);
}
@@ -376,8 +376,8 @@ class WarehouseTestData {
final lastNames = ['', '', '', '', '', '', '', '', '', ''];
final firstNames = ['창고장', '소장', '센터장', '팀장', '과장', '부장', '이사', '실장'];
final lastName = lastNames[random.nextInt(lastNames.items.length)];
final firstName = firstNames[random.nextInt(firstNames.items.length)];
final lastName = lastNames[random.nextInt(lastNames.length)];
final firstName = firstNames[random.nextInt(firstNames.length)];
return '$lastName$firstName';
}
@@ -385,7 +385,7 @@ class WarehouseTestData {
// 연락처 생성기
static String generateContact() {
final areaCodes = ['02', '031', '032', '033', '041', '042', '043', '051', '052', '053'];
final areaCode = areaCodes[random.nextInt(areaCodes.items.length)];
final areaCode = areaCodes[random.nextInt(areaCodes.length)];
final middle = random.nextInt(9000) + 1000;
final last = random.nextInt(9000) + 1000;
return '$areaCode-$middle-$last';
@@ -394,7 +394,7 @@ class WarehouseTestData {
// 창고 용량 생성기 (평방미터)
static int generateCapacity() {
final capacities = [500, 1000, 1500, 2000, 3000, 5000, 10000, 15000, 20000];
return capacities[random.nextInt(capacities.items.length)];
return capacities[random.nextInt(capacities.length)];
}
}
@@ -421,7 +421,7 @@ extension on WarehouseAutomatedTest {
await _testWarehouseUpdate(createdWarehouse.id);
// 6. 창고 검색 테스트
await _testWarehouseSearch(createdWarehouse.name.split(' ').items.first);
await _testWarehouseSearch(createdWarehouse.name.split(' ').first);
// 7. 활성/비활성 필터링 테스트
await _testActiveFiltering();
@@ -603,12 +603,12 @@ extension on WarehouseAutomatedTest {
try {
// search 파라미터가 지원되는지 확인
final searchResults = await warehouseService.searchWarehouseLocations(
keyword: searchKeyword.split(' ').items.first, // 첫 단어만 사용
keyword: searchKeyword.split(' ').first, // 첫 단어만 사용
page: 1,
perPage: 10,
);
_log('검색 결과: ${searchResults.items.length}개 창고');
_log('검색 결과: ${searchResults.length}개 창고');
testContext.setData('searchResults', searchResults);
testContext.setData('searchSuccess', true);
} catch (e) {
@@ -620,13 +620,13 @@ extension on WarehouseAutomatedTest {
page: 1,
perPage: 50,
);
final allWarehouses = allWarehousesResult.items;
final allWarehouses = allWarehousesResult;
final filtered = allWarehouses.items.where((w) =>
final filtered = allWarehouses.where((w) =>
w.name.toLowerCase().contains(searchKeyword.toLowerCase())
).toList();
_log('필터링 결과: ${filtered.items.length}개 창고');
_log('필터링 결과: ${filtered.length}개 창고');
testContext.setData('searchResults', filtered);
testContext.setData('searchSuccess', true);
} catch (e2) {
@@ -647,8 +647,8 @@ extension on WarehouseAutomatedTest {
perPage: 10,
isActive: true,
);
final activeWarehouses = activeWarehousesResult.items;
_log('활성 창고: ${activeWarehouses.items.length}');
final activeWarehouses = activeWarehousesResult;
_log('활성 창고: ${activeWarehouses.length}');
// 비활성 창고만 조회
_log('비활성 창고 조회 중...');
@@ -657,8 +657,8 @@ extension on WarehouseAutomatedTest {
perPage: 10,
isActive: false,
);
final inactiveWarehouses = inactiveWarehousesResult.items;
_log('비활성 창고: ${inactiveWarehouses.items.length}');
final inactiveWarehouses = inactiveWarehousesResult;
_log('비활성 창고: ${inactiveWarehouses.length}');
testContext.setData('activeWarehouses', activeWarehouses);
testContext.setData('inactiveWarehouses', inactiveWarehouses);
@@ -856,7 +856,7 @@ extension on WarehouseAutomatedTest {
);
// expect(diagnosis.errorType, equals(ErrorType.missingRequiredField));
_log('진단 결과: ${diagnosis.missingFields?.items.length ?? 0}개 필드 누락');
_log('진단 결과: ${diagnosis.missingFields?.length ?? 0}개 필드 누락');
// 자동 수정
final fixResult = await autoFixer.attemptAutoFix(diagnosis);
@@ -903,7 +903,7 @@ extension on WarehouseAutomatedTest {
// 1. 창고별 장비 목록 조회 (초기 상태)
_log('창고별 장비 목록 조회 중...');
final initialEquipment = await warehouseService.getWarehouseEquipment(warehouse.id);
_log('초기 장비 수: ${initialEquipment.items.length}');
_log('초기 장비 수: ${initialEquipment.length}');
// 2. 장비 입고 시뮬레이션 (실제로는 Equipment 서비스를 통해 수행)
_log('장비 입고 프로세스는 Equipment 서비스에서 처리됩니다');
@@ -911,17 +911,17 @@ extension on WarehouseAutomatedTest {
// 3. 사용 중인 창고 목록 조회
_log('사용 중인 창고 목록 조회 중...');
final inUseWarehouses = await warehouseService.getInUseWarehouseLocations();
_log('사용 중인 창고 수: ${inUseWarehouses.items.length}');
_log('사용 중인 창고 수: ${inUseWarehouses.length}');
// 장비가 있는 창고는 사용 중으로 표시되어야 함
if (initialEquipment.items.isNotEmpty) {
final isInUse = inUseWarehouses.items.any((w) => w.id == warehouse.id);
if (initialEquipment.isNotEmpty) {
final isInUse = inUseWarehouses.any((w) => w.id == warehouse.id);
// expect(isInUse, isTrue, reason: '장비가 있는 창고가 사용 중으로 표시되지 않았습니다');
}
testContext.setData('equipmentIntegrationSuccess', true);
testContext.setData('initialEquipmentCount', initialEquipment.items.length);
testContext.setData('inUseWarehouseCount', inUseWarehouses.items.length);
testContext.setData('initialEquipmentCount', initialEquipment.length);
testContext.setData('inUseWarehouseCount', inUseWarehouses.length);
} catch (e) {
_log('장비 연동 중 오류 발생: $e');
@@ -952,7 +952,7 @@ extension on WarehouseAutomatedTest {
page: 1,
perPage: 100,
);
_log('전체 창고 수: ${allWarehouses.items.length}');
_log('전체 창고 수: ${allWarehouses.length}');
// 2. 활성 창고만 필터링
_log('활성 창고만 필터링...');
@@ -961,7 +961,7 @@ extension on WarehouseAutomatedTest {
perPage: 100,
isActive: true,
);
_log('활성 창고 수: ${activeWarehouses.items.length}');
_log('활성 창고 수: ${activeWarehouses.length}');
// 3. 비활성 창고 필터링
_log('비활성 창고 필터링...');
@@ -970,21 +970,21 @@ extension on WarehouseAutomatedTest {
perPage: 100,
isActive: false,
);
_log('비활성 창고 수: ${inactiveWarehouses.items.length}');
_log('비활성 창고 수: ${inactiveWarehouses.length}');
// 4. 사용 중인 창고 목록
_log('사용 중인 창고 목록 조회...');
final inUseWarehouses = await warehouseService.getInUseWarehouseLocations();
_log('사용 중인 창고 수: ${inUseWarehouses.items.length}');
_log('사용 중인 창고 수: ${inUseWarehouses.length}');
// 검증: 활성 + 비활성 = 전체 (대략적으로)
// 페이지네이션 때문에 정확히 일치하지 않을 수 있음
testContext.setData('inUseManagementSuccess', true);
testContext.setData('totalWarehouses', allWarehouses.items.length);
testContext.setData('activeWarehouses', activeWarehouses.items.length);
testContext.setData('inactiveWarehouses', inactiveWarehouses.items.length);
testContext.setData('inUseWarehouses', inUseWarehouses.items.length);
testContext.setData('totalWarehouses', allWarehouses.length);
testContext.setData('activeWarehouses', activeWarehouses.length);
testContext.setData('inactiveWarehouses', inactiveWarehouses.length);
testContext.setData('inUseWarehouses', inUseWarehouses.length);
} catch (e) {
_log('사용 중인 창고 관리 중 오류 발생: $e');
@@ -1024,8 +1024,8 @@ extension WarehouseServiceExtension on WarehouseService {
// 실제 검색 API가 있다면 사용
// 없다면 전체 목록을 가져와서 필터링
final allResult = await getWarehouseLocations(page: page, perPage: perPage * 5);
final all = allResult.items;
return all.where((w) =>
final all = allResult;
return all.items.where((w) =>
w.name.toLowerCase().contains(keyword.toLowerCase()) ||
(w.address.toString().toLowerCase().contains(keyword.toLowerCase()))
).toList();

View File

@@ -1,107 +0,0 @@
import 'package:flutter_test/flutter_test.dart';
import 'package:superport/services/warehouse_service.dart';
import 'package:superport/models/warehouse_location_model.dart';
import 'screens/base/base_screen_test.dart';
import 'framework/models/test_models.dart';
/// 창고 관리 화면 자동화 테스트 (수정된 버전)
class WarehouseAutomatedTest extends BaseScreenTest {
late WarehouseService warehouseService;
WarehouseAutomatedTest({
required super.apiClient,
required super.getIt,
required super.testContext,
required super.errorDiagnostics,
required super.autoFixer,
required super.dataGenerator,
required super.reportCollector,
});
@override
ScreenMetadata getScreenMetadata() {
return ScreenMetadata(
screenName: 'WarehouseScreen',
controllerType: WarehouseService,
relatedEndpoints: [
ApiEndpoint(
path: '/api/v1/warehouse-locations',
method: 'GET',
description: '창고 목록 조회',
),
ApiEndpoint(
path: '/api/v1/warehouse-locations',
method: 'POST',
description: '창고 생성',
),
],
screenCapabilities: {
'warehouse_management': {
'create': true,
'read': true,
'update': true,
'delete': true,
},
},
);
}
@override
Future<void> initializeServices() async {
warehouseService = getIt<WarehouseService>();
}
@override
dynamic getService() => warehouseService;
@override
String getResourceType() => 'warehouse';
@override
Map<String, dynamic> getDefaultFilters() {
return {
'isActive': true,
};
}
@override
Future<List<TestableFeature>> detectCustomFeatures(ScreenMetadata metadata) async {
return [];
}
// BaseScreenTest 추상 메서드 구현
@override
Future<dynamic> performCreateOperation(TestData data) async {
// 생성 로직 주석 처리 - 필요시 구현
throw UnimplementedError('창고 생성 메서드를 구현해주세요');
}
@override
Future<dynamic> performReadOperation(TestData data) async {
return await warehouseService.getWarehouseLocations(
page: 1,
perPage: 20,
);
}
@override
Future<dynamic> performUpdateOperation(dynamic resourceId, Map<String, dynamic> updateData) async {
// 창고 업데이트 구현
throw UnimplementedError('창고 업데이트 메서드를 구현해주세요');
}
@override
Future<void> performDeleteOperation(dynamic resourceId) async {
// 창고 삭제 구현
throw UnimplementedError('창고 삭제 메서드를 구현해주세요');
}
@override
dynamic extractResourceId(dynamic resource) {
if (resource is WarehouseLocation) {
return resource.id;
}
return null;
}
}

View File

@@ -59,7 +59,7 @@ Future<TestResult> runWarehouseTests({
assert(response.statusCode == 200);
assert(response.data['data'] is List);
if (response.data['data'].items.isNotEmpty) {
if (response.data['data'].isNotEmpty) {
final warehouse = response.data['data'][0];
assert(warehouse['id'] != null);
assert(warehouse['name'] != null);
@@ -70,7 +70,7 @@ Future<TestResult> runWarehouseTests({
}
passedCount++;
if (verbose) debugPrint('✅ 창고 목록 조회 성공: ${response.data['data'].items.length}');
if (verbose) debugPrint('✅ 창고 목록 조회 성공: ${response.data['data'].length}');
} catch (e) {
failedCount++;
failedTests.add('창고 목록 조회');
@@ -326,7 +326,7 @@ Future<TestResult> runWarehouseTests({
assert(response.data['data'] is List);
passedCount++;
if (verbose) debugPrint('✅ 창고 검색 성공: ${response.data['data'].items.length}개 찾음');
if (verbose) debugPrint('✅ 창고 검색 성공: ${response.data['data'].length}개 찾음');
} catch (e) {
// 검색 기능이 없을 수 있으므로 경고만
if (verbose) debugPrint('⚠️ 창고 검색 실패 (선택적): $e');