import 'package:flutter_test/flutter_test.dart'; import 'package:dio/dio.dart'; import 'package:lanebow/services/public_event_service.dart'; import 'package:lanebow/utils/web_storage.dart'; class FakeHttp implements PublicHttpClient { Response? lastResponse; Map? lastHeaders; String? lastPath; dynamic lastData; int statusCode = 200; Map responseData = const {'ok': true}; @override Future> get(String path, {Map? headers}) async { lastPath = path; lastData = null; lastHeaders = headers; if (statusCode >= 400) { throw DioException( requestOptions: RequestOptions(path: path), response: Response(requestOptions: RequestOptions(path: path), statusCode: statusCode, data: responseData), type: DioExceptionType.badResponse, ); } lastResponse = Response(requestOptions: RequestOptions(path: path), statusCode: statusCode, data: responseData); return lastResponse!; } @override Future> post(String path, {data, Map? headers}) async { lastPath = path; lastData = data; lastHeaders = headers; if (statusCode >= 400) { throw DioException( requestOptions: RequestOptions(path: path), response: Response(requestOptions: RequestOptions(path: path), statusCode: statusCode, data: responseData), type: DioExceptionType.badResponse, ); } lastResponse = Response(requestOptions: RequestOptions(path: path), statusCode: statusCode, data: responseData); return lastResponse!; } @override Future> put(String path, {data, Map? headers}) async { lastPath = path; lastData = data; lastHeaders = headers; if (statusCode >= 400) { throw DioException( requestOptions: RequestOptions(path: path), response: Response(requestOptions: RequestOptions(path: path), statusCode: statusCode, data: responseData), type: DioExceptionType.badResponse, ); } lastResponse = Response(requestOptions: RequestOptions(path: path), statusCode: statusCode, data: responseData); return lastResponse!; } } void main() { group('PublicEventService.enter', () { test('saves token when response includes token', () async { final fake = FakeHttp() ..statusCode = 200 ..responseData = {'token': 'TKN', 'event': {'id': 1}}; final svc = PublicEventService.forTest(fake); final hash = 'abc123'; // ensure empty WebStorage.removeItem(PublicEventService.tokenKey(hash)); final data = await svc.enter(hash, password: 'pw'); expect(data['token'], 'TKN'); expect(WebStorage.getItem(PublicEventService.tokenKey(hash)), 'TKN'); expect(fake.lastPath, '/api/public/$hash'); }); test('removes saved token on 401/403', () async { final fake = FakeHttp() ..statusCode = 401 ..responseData = {'message': 'unauthorized'}; final svc = PublicEventService.forTest(fake); final hash = 'abc401'; // preset token WebStorage.setItem(PublicEventService.tokenKey(hash), 'OLD'); await expectLater( svc.enter(hash, password: 'wrong'), throwsA(isA()), ); expect(WebStorage.getItem(PublicEventService.tokenKey(hash)), isNull); }); }); group('PublicEventService.updateScore', () { test('sends Authorization header when token saved', () async { final fake = FakeHttp() ..statusCode = 200 ..responseData = {'ok': true}; final svc = PublicEventService.forTest(fake); final hash = 'hhh'; WebStorage.setItem(PublicEventService.tokenKey(hash), 'TKN2'); await svc.updateScore( hash, participantId: '10', gameNumber: 1, score: 200, handicap: 0, ); expect(fake.lastPath, '/api/public/$hash/score'); expect(fake.lastHeaders?['Authorization'], 'Bearer TKN2'); expect(fake.lastData['participantId'], '10'); expect(fake.lastData['gameNumber'], 1); expect(fake.lastData['score'], 200); }); }); group('PublicEventService.fetchEvent', () { test('uses GET and Authorization header when token saved', () async { final fake = FakeHttp() ..statusCode = 200 ..responseData = {'event': {'id': 'E1'}}; final svc = PublicEventService.forTest(fake); final hash = 'fetch1'; WebStorage.setItem(PublicEventService.tokenKey(hash), 'TKF'); final data = await svc.fetchEvent(hash); expect(data['event']['id'], 'E1'); expect(fake.lastPath, '/api/public/$hash'); expect(fake.lastHeaders?['Authorization'], 'Bearer TKF'); }); }); group('PublicEventService.enter edge cases', () { test('404 does not remove saved token', () async { final fake = FakeHttp() ..statusCode = 404 ..responseData = {'message': 'not found'}; final svc = PublicEventService.forTest(fake); final hash = 'nf404'; WebStorage.setItem(PublicEventService.tokenKey(hash), 'KEEP'); await expectLater(svc.enter(hash), throwsA(isA())); expect(WebStorage.getItem(PublicEventService.tokenKey(hash)), 'KEEP'); }); }); group('PublicEventService.addGuest', () { test('sends payload and header', () async { final fake = FakeHttp() ..statusCode = 200 ..responseData = {'ok': true}; final svc = PublicEventService.forTest(fake); final hash = 'guest'; WebStorage.setItem(PublicEventService.tokenKey(hash), 'TG'); await svc.addGuest(hash, name: '홍길동', phone: '010', gender: 'M', average: 190); expect(fake.lastPath, '/api/public/$hash/guest'); expect(fake.lastHeaders?['Authorization'], 'Bearer TG'); expect(fake.lastData['name'], '홍길동'); expect(fake.lastData['phone'], '010'); expect(fake.lastData['gender'], 'M'); expect(fake.lastData['average'], 190); }); }); group('PublicEventService.updateScore errors', () { test('propagates server error 500', () async { final fake = FakeHttp() ..statusCode = 500 ..responseData = {'message': 'oops'}; final svc = PublicEventService.forTest(fake); final hash = 'err'; WebStorage.setItem(PublicEventService.tokenKey(hash), 'TE'); await expectLater( svc.updateScore(hash, participantId: 'p1', gameNumber: 1, score: 100), throwsA(isA()), ); }); }); }