import 'dart:convert'; import 'package:flutter_test/flutter_test.dart'; import 'package:mockito/mockito.dart'; import 'package:mockito/annotations.dart'; import 'package:http/http.dart' as http; import 'package:lanebow/services/subscription_service.dart'; import 'package:lanebow/services/event_bus.dart'; import 'package:lanebow/models/subscription_model.dart'; import 'package:lanebow/config/api_config.dart'; import 'package:lanebow/services/in_app_purchase_service.dart'; import 'package:lanebow/utils/test_utils.dart'; // 모킹 클래스 생성 @GenerateMocks([http.Client, InAppPurchaseService]) import 'subscription_service_test.mocks.dart'; void main() { late SubscriptionService subscriptionService; late MockClient mockClient; late MockInAppPurchaseService mockPurchaseService; final testToken = 'test_token'; final testClubId = 'test_club_id'; final testUserId = 'test_user_id'; // 테스트 구독 데이터 final testSubscription = { 'id': 'test_subscription_id', 'userId': testUserId, 'clubId': testClubId, 'planType': 'premium', 'status': 'active', 'startDate': DateTime.now().toIso8601String(), 'endDate': DateTime.now().add(Duration(days: 30)).toIso8601String(), 'price': 19900.0, 'currency': 'KRW', 'autoRenew': true, 'features': { 'maxMembers': 100, 'maxEvents': 100, }, 'paymentMethod': 'card', 'transactionId': 'test_transaction_id', 'createdAt': DateTime.now().subtract(Duration(days: 10)).toIso8601String(), 'updatedAt': DateTime.now().toIso8601String(), }; // 테스트 구독 플랜 데이터 final testPlans = [ { 'type': 'basic', 'name': '기본', 'description': '중소규모 클럽을 위한 확장 기능', 'monthlyPrice': 9900.0, 'yearlyPrice': 99000.0, 'currency': 'KRW', 'features': [ '최대 30명 회원 관리', '상세 점수 분석', '이벤트 관리', '회원 통계', ], 'maxMembers': 30, 'maxEvents': 20, 'hasAdvancedStats': true, 'hasCustomization': false, 'hasPriority': false, }, { 'type': 'premium', 'name': '프리미엄', 'description': '대규모 클럽을 위한 고급 기능', 'monthlyPrice': 19900.0, 'yearlyPrice': 199000.0, 'currency': 'KRW', 'features': [ '무제한 회원 관리', '고급 통계 및 분석', '무제한 이벤트', '맞춤형 보고서', '우선 지원', ], 'maxMembers': 100, 'maxEvents': 100, 'hasAdvancedStats': true, 'hasCustomization': true, 'hasPriority': true, } ]; setUp(() { // 테스트 모드 강제 설정 TestUtils.setTestMode(true); // 테스트별 고유 ID 생성 및 설정 final testId = 'subscription_service_test_${DateTime.now().millisecondsSinceEpoch}'; EventBus.setCurrentTestId(testId); // EventBus 완전 초기화 EventBus().reset(); // HTTP 클라이언트 모킹 mockClient = MockClient(); // InAppPurchaseService 모킹 mockPurchaseService = MockInAppPurchaseService(); // HTTP 클라이언트 요청 모킹 when(mockClient.post( any, headers: anyNamed('headers'), body: anyNamed('body'), )).thenAnswer((_) async => http.Response(jsonEncode(testSubscription), 200)); when(mockClient.get( any, headers: anyNamed('headers'), )).thenAnswer((_) async => http.Response(jsonEncode(testPlans), 200)); // 모킹 서비스에 대한 기본 동작 정의 when(mockPurchaseService.isLoading).thenReturn(false); when(mockPurchaseService.error).thenReturn(null); when(mockPurchaseService.products).thenReturn([]); when(mockPurchaseService.restorePurchases()).thenAnswer((_) async => true); // SubscriptionService 초기화 - 테스트용 생성자 사용 subscriptionService = SubscriptionService.forTest( client: mockClient, purchaseService: mockPurchaseService, userId: testUserId, token: testToken, clubId: testClubId, ); }); tearDown(() { // 테스트 종료 후 EventBus 초기화 EventBus().reset(); // 테스트에서 생성된 SubscriptionService 정리 subscriptionService.dispose(); // 테스트 ID 초기화 EventBus.clearCurrentTestId(); // 테스트 모드 해제 TestUtils.setTestMode(false); }); group('SubscriptionService 기본 기능 테스트', () { test('SubscriptionService 초기화 테스트', () { // 초기화 후 기본 상태 확인 expect(subscriptionService, isNotNull); expect(subscriptionService.currentSubscription, isNull); expect(subscriptionService.isLoading, isNotNull); expect(subscriptionService.availablePlans, isNotNull); }); test('loadCurrentSubscription 메서드가 현재 구독 정보를 가져와야 함', () async { // given when(mockClient.post( any, headers: anyNamed('headers'), body: anyNamed('body'), )).thenAnswer((_) async => http.Response(jsonEncode(testSubscription), 200)); // when await subscriptionService.loadCurrentSubscription(); // then expect(subscriptionService.currentSubscription, isNotNull); expect(subscriptionService.isLoading, isFalse); expect(subscriptionService.error, isNull); }); test('canUseFeature 메서드가 기능 사용 가능 여부를 확인해야 함', () { // 구독이 없는 경우 기능 사용 가능 여부 확인 expect(subscriptionService.canUseFeature('advancedStats'), isA()); }); test('getMaxMembers 메서드가 최대 회원 수를 반환해야 함', () { // 구독이 없는 경우 기본 무료 플랜의 최대 회원 수 반환 final maxMembers = subscriptionService.getMaxMembers(); expect(maxMembers, isNotNull); expect(maxMembers, isA()); }); test('getMaxEvents 메서드가 최대 이벤트 수를 반환해야 함', () { // 구독이 없는 경우 기본 무료 플랜의 최대 이벤트 수 반환 final maxEvents = subscriptionService.getMaxEvents(); expect(maxEvents, isNotNull); expect(maxEvents, isA()); }); test('purchaseService getter가 null이 아니어야 함', () { expect(subscriptionService.purchaseService, isNotNull); }); test('hasSubscription getter가 동작해야 함', () { expect(subscriptionService.hasSubscription, isA()); }); test('isActive getter가 동작해야 함', () { expect(subscriptionService.isActive, isA()); }); test('restoreSubscriptions 메서드가 구독을 복원해야 함', () async { // given when(mockPurchaseService.restorePurchases()).thenAnswer((_) async => true); when(mockClient.post( any, headers: anyNamed('headers'), body: anyNamed('body'), )).thenAnswer((_) async => http.Response(jsonEncode(testSubscription), 200)); // when final result = await subscriptionService.restoreSubscriptions(); // then expect(result, isTrue); expect(subscriptionService.isLoading, isFalse); }); }); }