feat: 결재·마스터 실연동 업데이트

This commit is contained in:
JiWoong Sul
2025-10-14 18:10:24 +09:00
parent 1325109fba
commit 8067416c09
66 changed files with 2129 additions and 222 deletions

View File

@@ -0,0 +1,65 @@
import 'package:dio/dio.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:mocktail/mocktail.dart';
import 'package:superport_v2/core/network/api_client.dart';
import 'package:superport_v2/features/masters/user/data/repositories/user_repository_remote.dart';
class _MockApiClient extends Mock implements ApiClient {}
void main() {
late ApiClient apiClient;
late UserRepositoryRemote repository;
setUpAll(() {
registerFallbackValue(Options());
registerFallbackValue(CancelToken());
});
setUp(() {
apiClient = _MockApiClient();
repository = UserRepositoryRemote(apiClient: apiClient);
});
test('목록 조회 시 include=group 파라미터를 전달한다', () async {
when(
() => apiClient.get<Map<String, dynamic>>(
any(),
query: any(named: 'query'),
options: any(named: 'options'),
cancelToken: any(named: 'cancelToken'),
),
).thenAnswer(
(_) async => Response<Map<String, dynamic>>(
data: {
'items': [
{
'id': 1,
'employee_no': 'E-001',
'employee_name': '홍길동',
'group': {'id': 2, 'group_name': '관리자'},
},
],
},
requestOptions: RequestOptions(path: '/api/v1/employees'),
statusCode: 200,
),
);
await repository.list();
final captured = verify(
() => apiClient.get<Map<String, dynamic>>(
captureAny(),
query: captureAny(named: 'query'),
options: any(named: 'options'),
cancelToken: any(named: 'cancelToken'),
),
).captured;
final path = captured[0] as String;
final query = captured[1] as Map<String, dynamic>;
expect(path, equals('/api/v1/employees'));
expect(query['include'], 'group');
});
}

View File

@@ -2,8 +2,11 @@ import 'package:flutter_test/flutter_test.dart';
import 'package:mocktail/mocktail.dart';
import 'package:superport_v2/core/common/models/paginated_result.dart';
import 'package:superport_v2/core/permissions/permission_manager.dart';
import 'package:superport_v2/features/masters/group/domain/entities/group.dart';
import 'package:superport_v2/features/masters/group/domain/repositories/group_repository.dart';
import 'package:superport_v2/features/masters/group_permission/domain/entities/group_permission.dart';
import 'package:superport_v2/features/masters/group_permission/domain/repositories/group_permission_repository.dart';
import 'package:superport_v2/features/masters/user/domain/entities/user.dart';
import 'package:superport_v2/features/masters/user/domain/repositories/user_repository.dart';
import 'package:superport_v2/features/masters/user/presentation/controllers/user_controller.dart';
@@ -12,12 +15,17 @@ class _MockUserRepository extends Mock implements UserRepository {}
class _MockGroupRepository extends Mock implements GroupRepository {}
class _MockGroupPermissionRepository extends Mock
implements GroupPermissionRepository {}
class _FakeUserInput extends Fake implements UserInput {}
void main() {
late UserController controller;
late _MockUserRepository userRepository;
late _MockGroupRepository groupRepository;
late _MockGroupPermissionRepository permissionRepository;
late PermissionManager permissionManager;
final sampleUser = UserAccount(
id: 1,
@@ -44,9 +52,43 @@ void main() {
setUp(() {
userRepository = _MockUserRepository();
groupRepository = _MockGroupRepository();
permissionRepository = _MockGroupPermissionRepository();
permissionManager = PermissionManager();
when(
() => permissionRepository.list(
page: any(named: 'page'),
pageSize: any(named: 'pageSize'),
groupId: any(named: 'groupId'),
menuId: any(named: 'menuId'),
isActive: any(named: 'isActive'),
includeDeleted: any(named: 'includeDeleted'),
),
).thenAnswer(
(_) async => PaginatedResult<GroupPermission>(
items: [
GroupPermission(
id: 1,
group: GroupPermissionGroup(id: 1, groupName: '관리자'),
menu: GroupPermissionMenu(
id: 10,
menuCode: 'DASHBOARD',
menuName: '대시보드',
path: '/dashboard',
),
canCreate: true,
canRead: true,
),
],
page: 1,
pageSize: 200,
total: 1,
),
);
controller = UserController(
userRepository: userRepository,
groupRepository: groupRepository,
permissionRepository: permissionRepository,
permissionManager: permissionManager,
);
});
@@ -58,6 +100,8 @@ void main() {
query: any(named: 'query'),
isDefault: any(named: 'isDefault'),
isActive: any(named: 'isActive'),
includePermissions: any(named: 'includePermissions'),
includeEmployees: any(named: 'includeEmployees'),
),
).thenAnswer(
(_) async => PaginatedResult<Group>(
@@ -82,6 +126,8 @@ void main() {
query: any(named: 'query'),
isDefault: any(named: 'isDefault'),
isActive: any(named: 'isActive'),
includePermissions: any(named: 'includePermissions'),
includeEmployees: any(named: 'includeEmployees'),
),
).thenAnswer(
(_) async => PaginatedResult<Group>(
@@ -195,6 +241,24 @@ void main() {
verify(() => userRepository.delete(1)).called(1);
});
test('delete 이후 권한 동기화를 시도한다', () async {
when(() => userRepository.delete(any())).thenAnswer((_) async {});
await controller.fetch();
await controller.delete(sampleUser.id!);
verify(
() => permissionRepository.list(
page: any(named: 'page'),
pageSize: any(named: 'pageSize'),
groupId: sampleUser.group!.id,
menuId: any(named: 'menuId'),
isActive: any(named: 'isActive'),
includeDeleted: any(named: 'includeDeleted'),
),
).called(greaterThanOrEqualTo(1));
});
test('restore 성공', () async {
when(
() => userRepository.restore(any()),

View File

@@ -6,8 +6,11 @@ import 'package:mocktail/mocktail.dart';
import 'package:shadcn_ui/shadcn_ui.dart';
import 'package:superport_v2/core/common/models/paginated_result.dart';
import 'package:superport_v2/core/permissions/permission_manager.dart';
import 'package:superport_v2/features/masters/group/domain/entities/group.dart';
import 'package:superport_v2/features/masters/group/domain/repositories/group_repository.dart';
import 'package:superport_v2/features/masters/group_permission/domain/entities/group_permission.dart';
import 'package:superport_v2/features/masters/group_permission/domain/repositories/group_permission_repository.dart';
import 'package:superport_v2/features/masters/user/domain/entities/user.dart';
import 'package:superport_v2/features/masters/user/domain/repositories/user_repository.dart';
import 'package:superport_v2/features/masters/user/presentation/pages/user_page.dart';
@@ -16,16 +19,22 @@ class _MockUserRepository extends Mock implements UserRepository {}
class _MockGroupRepository extends Mock implements GroupRepository {}
class _MockGroupPermissionRepository extends Mock
implements GroupPermissionRepository {}
class _FakeUserInput extends Fake implements UserInput {}
Widget _buildApp(Widget child) {
return MaterialApp(
home: ShadTheme(
data: ShadThemeData(
colorScheme: const ShadSlateColorScheme.light(),
brightness: Brightness.light,
return PermissionScope(
manager: PermissionManager(),
child: MaterialApp(
home: ShadTheme(
data: ShadThemeData(
colorScheme: const ShadSlateColorScheme.light(),
brightness: Brightness.light,
),
child: Scaffold(body: child),
),
child: Scaffold(body: child),
),
);
}
@@ -55,12 +64,17 @@ void main() {
group('플래그 On', () {
late _MockUserRepository userRepository;
late _MockGroupRepository groupRepository;
late _MockGroupPermissionRepository permissionRepository;
setUp(() {
dotenv.testLoad(fileInput: 'FEATURE_USERS_ENABLED=true\n');
userRepository = _MockUserRepository();
groupRepository = _MockGroupRepository();
permissionRepository = _MockGroupPermissionRepository();
GetIt.I.registerLazySingleton<UserRepository>(() => userRepository);
GetIt.I.registerLazySingleton<GroupRepository>(() => groupRepository);
GetIt.I.registerLazySingleton<GroupPermissionRepository>(
() => permissionRepository,
);
when(
() => groupRepository.list(
@@ -69,6 +83,8 @@ void main() {
query: any(named: 'query'),
isDefault: any(named: 'isDefault'),
isActive: any(named: 'isActive'),
includePermissions: any(named: 'includePermissions'),
includeEmployees: any(named: 'includeEmployees'),
),
).thenAnswer(
(_) async => PaginatedResult<Group>(
@@ -78,6 +94,37 @@ void main() {
total: 1,
),
);
when(
() => permissionRepository.list(
page: any(named: 'page'),
pageSize: any(named: 'pageSize'),
groupId: any(named: 'groupId'),
menuId: any(named: 'menuId'),
isActive: any(named: 'isActive'),
includeDeleted: any(named: 'includeDeleted'),
),
).thenAnswer(
(_) async => PaginatedResult<GroupPermission>(
items: [
GroupPermission(
id: 1,
group: GroupPermissionGroup(id: 1, groupName: '관리자'),
menu: GroupPermissionMenu(
id: 10,
menuCode: 'DASHBOARD',
menuName: '대시보드',
path: '/dashboard',
),
canCreate: true,
canRead: true,
),
],
page: 1,
pageSize: 200,
total: 1,
),
);
});
testWidgets('목록 조회 후 테이블 렌더', (tester) async {