import 'package:block_seasons/data/save_repository.dart'; import 'package:block_seasons/services/review_service.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:shared_preferences/shared_preferences.dart'; class _FakeReviewer implements Reviewer { _FakeReviewer({this.available = true, this.throwOnRequest = false}); bool available; bool throwOnRequest; int requestCalls = 0; @override Future isAvailable() async => available; @override Future requestReview() async { requestCalls++; if (throwOnRequest) throw Exception('store unavailable'); } } void main() { TestWidgetsFlutterBinding.ensureInitialized(); Future repoWithStagesCleared(int n) async { SharedPreferences.setMockInitialValues({}); final repo = SaveRepository(await SharedPreferences.getInstance()); for (var i = 0; i < n; i++) { await repo.recordResult( seasonId: 'season_001', stageId: 's$i', stars: 3, score: 100); } return repo; } test('requests a review and marks the flag on a qualifying win', () async { final repo = await repoWithStagesCleared(5); final reviewer = _FakeReviewer(); final service = ReviewService(save: repo, reviewer: reviewer); await service.maybeRequestAfterWin(stars: 3); expect(reviewer.requestCalls, 1); expect(repo.reviewRequested, isTrue); }); test('never requests twice', () async { final repo = await repoWithStagesCleared(5); await repo.markReviewRequested(); final reviewer = _FakeReviewer(); final service = ReviewService(save: repo, reviewer: reviewer); await service.maybeRequestAfterWin(stars: 3); expect(reviewer.requestCalls, 0); }); test('does not request when the policy is not satisfied', () async { final repo = await repoWithStagesCleared(2); // below the 5-stage gate final reviewer = _FakeReviewer(); final service = ReviewService(save: repo, reviewer: reviewer); await service.maybeRequestAfterWin(stars: 3); expect(reviewer.requestCalls, 0); expect(repo.reviewRequested, isFalse); }); test('does not burn the one-shot when the store is unavailable', () async { final repo = await repoWithStagesCleared(5); final reviewer = _FakeReviewer(available: false); final service = ReviewService(save: repo, reviewer: reviewer); await service.maybeRequestAfterWin(stars: 3); expect(reviewer.requestCalls, 0); expect(repo.reviewRequested, isFalse, reason: 'retry on a later win'); }); test('swallows reviewer errors without throwing', () async { final repo = await repoWithStagesCleared(5); final reviewer = _FakeReviewer(throwOnRequest: true); final service = ReviewService(save: repo, reviewer: reviewer); // Must complete normally despite the plugin throwing. await service.maybeRequestAfterWin(stars: 3); expect(reviewer.requestCalls, 1); }); }