Files
fanuzu/app/test/hero_participation_test.dart

99 lines
4.6 KiB
Dart

import 'package:sensors_plus/sensors_plus.dart';
import 'package:fake_cloud_firestore/fake_cloud_firestore.dart';
import 'package:firebase_auth_mocks/firebase_auth_mocks.dart';
import 'package:fancake/core/data/backend.dart';
import 'package:fancake/features/planet/planet_repository_firestore.dart';
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:fancake/core/data/planet_visual_state.dart';
import 'package:fancake/features/planet/hero_participation_sheet.dart';
import 'package:fancake/features/planet/planet_data.dart';
class _Claims extends SamplePlanetRepository {
final ids = <String>[];
final replies = <Completer<HeroDropReceipt>>[];
@override
Future<HeroDropReceipt> claimHeroDrop(String id) {
ids.add(id);
final reply = Completer<HeroDropReceipt>();
replies.add(reply);
return reply.future;
}
}
class _HeroBackend extends Backend {
_HeroBackend() : super(firestore: FakeFirebaseFirestore(),
auth: MockFirebaseAuth(signedIn: true, mockUser: MockUser(uid: 'viewer')));
Map<String, dynamic> response = {'claimedPop': 5, 'txId': 'receipt'};
Map<String, dynamic>? payload;
@override
Future<Map<String, dynamic>> call(String name, [Map<String, dynamic> data = const {}]) async {
expect(name, 'fnHeroDropClaim');
payload = data;
return response;
}
}
void main() {
for (final code in ['E-POP-02', 'E-POP-03', 'E-AUTH-03', 'E-PERM-01']) {
testWidgets('서버 확정 거절 $code는 성공이나 무한 재시도로 표시하지 않는다', (tester) async {
final repo = _Claims();
await tester.pumpWidget(MaterialApp(home: Scaffold(body: HeroParticipationSheet(
fandomId: 'target', repository: repo, accelerometer: const Stream.empty(),
watch: (_) => Stream.value(PlanetVisualState(isHeroTime: true,
heroEventIds: const {'gift'}, heroEndsAt: DateTime.now().add(const Duration(minutes: 1)))),
))));
await tester.pumpAndSettle();
await tester.tap(find.text('흔들기 대신 버튼으로 참여'));
repo.replies.single.completeError(BackendException(code, 'internal details'));
await tester.pumpAndSettle();
expect(find.text('같은 선물 다시 확인'), findsNothing);
expect(find.textContaining('POP 수령 확인'), findsNothing);
expect(find.text('internal details'), findsNothing);
expect(tester.widget<FilledButton>(find.byType(FilledButton)).onPressed, isNull);
expect(repo.ids, ['gift']);
await tester.pumpWidget(const SizedBox());
});
}
test('수령 요청에 서버 판정용 팬덤·이벤트만 보내며 잘못된 응답은 성공으로 읽지 않는다', () async {
final backend = _HeroBackend();
final repo = FirestorePlanetRepository(backend, fandomId: 'target');
expect((await repo.claimHeroDrop('gift')).amount, 5);
expect(backend.payload, {'fandomId': 'target', 'dropId': 'gift'});
backend.response = {'claimedPop': 0, 'txId': 'receipt'};
await expectLater(repo.claimHeroDrop('gift'), throwsStateError);
});
testWidgets('지급 전 성공 없음·연타 중복 없음·종료 후에도 같은 선물 재확인', (tester) async {
final repo = _Claims();
final sensor = StreamController<AccelerometerEvent>();
addTearDown(sensor.close);
final states = StreamController<PlanetVisualState>.broadcast();
addTearDown(states.close);
await tester.pumpWidget(MaterialApp(home: Scaffold(body: HeroParticipationSheet(
fandomId: 'test-fandom', watch: (_) => states.stream, repository: repo,
accelerometer: sensor.stream,
))));
states.add(PlanetVisualState(isHeroTime: true, heroEventIds: const {'gift'},
heroEndsAt: DateTime.now().add(const Duration(minutes: 1))));
await tester.pumpAndSettle();
sensor.add(AccelerometerEvent(25, 0, 0, DateTime.now()));
await tester.pump();
await tester.pump();
await tester.tap(find.text('지급 확인 중…'));
expect(repo.ids, ['gift']);
expect(find.textContaining('POP 수령 확인'), findsNothing);
repo.replies.first.completeError(StateError('response lost'));
await tester.pumpAndSettle();
states.add(const PlanetVisualState(isHeroTime: false));
await tester.pumpAndSettle();
await tester.tap(find.text('같은 선물 다시 확인'));
expect(repo.ids, ['gift', 'gift']);
repo.replies.last.complete(const HeroDropReceipt(txId: 'confirmed', amount: 5));
await tester.pumpAndSettle();
expect(find.text('5 POP 수령 확인'), findsOneWidget);
expect(tester.widget<FilledButton>(find.byType(FilledButton)).onPressed, isNull);
await tester.pumpWidget(const SizedBox());
});
}