공개페이지

This commit is contained in:
2025-10-29 00:25:03 +09:00
parent 19f99989b8
commit 63d0d383b0
28 changed files with 2691 additions and 0 deletions
@@ -0,0 +1,189 @@
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<dynamic>? lastResponse;
Map<String, dynamic>? lastHeaders;
String? lastPath;
dynamic lastData;
int statusCode = 200;
Map<String, dynamic> responseData = const {'ok': true};
@override
Future<Response<dynamic>> get(String path, {Map<String, dynamic>? 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<Response<dynamic>> post(String path, {data, Map<String, dynamic>? 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<Response<dynamic>> put(String path, {data, Map<String, dynamic>? 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<DioException>()),
);
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<DioException>()));
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<DioException>()),
);
});
});
}