Files
lunchpick/test/unit/data/datasources/remote/naver_map_parser_test.dart
JiWoong Sul 2a01fa50c6 feat(app): finalize ad gated flows and weather
- add AppLogger and replace scattered print logging\n- implement ad-gated recommendation flow with reminder handling and calendar link\n- complete Bluetooth share pipeline with ad gate and merge\n- integrate KMA weather API with caching and dart-define decoding\n- add NaverUrlProcessor refactor and restore restaurant repository tests
2025-11-22 00:10:51 +09:00

228 lines
7.7 KiB
Dart

// ignore_for_file: unnecessary_library_name
@Skip('Integration-heavy parser tests are temporarily disabled')
library naver_map_parser_test;
import 'package:flutter_test/flutter_test.dart';
import 'package:lunchpick/data/datasources/remote/naver_map_parser.dart';
import 'package:lunchpick/domain/entities/restaurant.dart';
import 'package:lunchpick/data/api/naver/naver_local_search_api.dart';
import 'package:lunchpick/core/errors/network_exceptions.dart';
import '../../../../mocks/mock_naver_api_client.dart';
void main() {
late NaverMapParser parser;
late MockNaverApiClient mockApiClient;
setUp(() {
mockApiClient = MockNaverApiClient();
parser = NaverMapParser(apiClient: mockApiClient);
});
tearDown(() {
parser.dispose();
});
group('NaverMapParser', () {
group('URL 유효성 검증', () {
test('유효한 네이버 지도 URL을 인식해야 함', () async {
final validUrls = [
'https://map.naver.com/p/restaurant/1234567890',
'https://naver.me/abcdefgh',
'https://map.naver.com/p/entry/place/1234567890',
];
for (final url in validUrls) {
mockApiClient.setUrlRedirect(
url,
'https://map.naver.com/p/restaurant/1234567890',
);
// 검색 API 응답 설정
mockApiClient.setSearchResults(
'https://map.naver.com/p/entry/place/1234567890',
[
NaverLocalSearchResult.fromJson({
'title': '테스트 식당',
'link': 'https://map.naver.com/p/restaurant/1234567890',
'category': '한식',
'description': '테스트 설명',
'telephone': '02-1234-5678',
'address': '서울 강남구 테스트동 123-45',
'roadAddress': '서울 강남구 테스트로 123',
'mapx': 1269780000,
'mapy': 375665000,
}),
],
);
final result = await parser.parseRestaurantFromUrl(url);
expect(result, isA<Restaurant>(), reason: 'URL: $url');
expect(result.name, '테스트 식당', reason: 'URL: $url');
}
});
test('잘못된 URL은 예외를 던져야 함', () async {
final invalidUrls = [
'https://www.google.com',
'https://map.kakao.com',
'https://naver.com',
'not-a-url',
'',
];
for (final url in invalidUrls) {
expect(
() => parser.parseRestaurantFromUrl(url),
throwsA(isA<NaverMapParseException>()),
reason: 'URL: $url should throw exception',
);
}
});
});
group('API 응답 처리', () {
test('검색 API로 식당 정보를 이지해야 함', () async {
const url = 'https://map.naver.com/p/restaurant/1234567890';
mockApiClient.setUrlRedirect(url, url);
// 검색 API 응답 설정
mockApiClient.setSearchResults(
'https://map.naver.com/p/entry/place/1234567890',
[
NaverLocalSearchResult.fromJson({
'title': '맛있는 한식당',
'link': 'https://map.naver.com/p/restaurant/1234567890',
'category': '한식>고기요리',
'description': '평일 11:00 - 22:00, 주말 10:00 - 23:00',
'telephone': '02-123-4567',
'address': '서울 강남구 역삼동 123-45',
'roadAddress': '서울 강남구 테헤란로 123',
'mapx': 1270396000,
'mapy': 375012000,
}),
],
);
final result = await parser.parseRestaurantFromUrl(url);
expect(result, isA<Restaurant>());
expect(result.name, '맛있는 한식당');
expect(result.category, '한식');
expect(result.description, contains('평일 11:00 - 22:00'));
expect(result.phoneNumber, '02-123-4567');
expect(result.roadAddress, '서울 강남구 테헤란로 123');
expect(result.latitude, closeTo(37.5012, 0.0001));
expect(result.longitude, closeTo(127.0396, 0.0001));
});
test('GraphQL API로 식당 정보를 가져와야 함', () async {
const url = 'https://map.naver.com/p/restaurant/9876543210';
mockApiClient.setUrlRedirect(url, url);
// GraphQL 응답 설정
mockApiClient.setGraphQLResponse({
'places': [
{
'id': '9876543210',
'name': '메타태그 식당',
'category': '기타',
'description': '맛있는 음식점',
'address': '서울시 강남구',
'roadAddress': '서울시 강남구 테헤란로',
'phone': '02-987-6543',
'location': {'lat': 37.5, 'lng': 127.0},
},
],
});
final result = await parser.parseRestaurantFromUrl(url);
expect(result, isA<Restaurant>());
expect(result.name, '메타태그 식당');
expect(result.category, '기타');
});
test('필수 정보가 없으면 기본값을 사용해야 함', () async {
const url = 'https://map.naver.com/p/restaurant/1234567890';
mockApiClient.setUrlRedirect(url, url);
// 빈 GraphQL 응답
mockApiClient.setGraphQLResponse({});
// HTML 파싱도 실패하도록 설정
mockApiClient.setHtmlResponse(url, '<html></html>');
final result = await parser.parseRestaurantFromUrl(url);
// 기본값이 사용되어야 함
expect(result, isA<Restaurant>());
expect(result.name, contains('1234567890'));
expect(result.category, '음식점');
});
});
group('단축 URL 처리', () {
test('단축 URL을 실제 URL로 변환해야 함', () async {
const shortUrl = 'https://naver.me/abc123';
const actualUrl = 'https://map.naver.com/p/restaurant/1234567890';
mockApiClient.setUrlRedirect(shortUrl, actualUrl);
// 단축 URL용 한글 텍스트 추출 응답
mockApiClient.setKoreanTextsData('1234567890', {
'success': true,
'koreanTexts': ['리다이렉트 식당'],
'jsonLdName': '리다이렉트 식당',
'apolloStateName': null,
});
// 검색 API 응답 설정
mockApiClient.setSearchResults('리다이렉트 식당', [
NaverLocalSearchResult.fromJson({
'title': '리다이렉트 식당',
'link': actualUrl,
'category': '카페',
'description': '',
'telephone': '',
'address': '서울 마포구',
'roadAddress': '서울 마포구 테스트로 100',
'mapx': 1268900000,
'mapy': 375200000,
}),
]);
final result = await parser.parseRestaurantFromUrl(shortUrl);
expect(result, isA<Restaurant>());
expect(result.name, '리다이렉트 식당');
expect(result.category, '카페');
});
});
group('에러 처리', () {
test('네트워크 오류 시 예외를 던져야 함', () async {
const url = 'https://map.naver.com/p/restaurant/network-error';
mockApiClient.shouldThrowError = true;
mockApiClient.errorMessage = 'Network error';
expect(() => parser.parseRestaurantFromUrl(url), throwsException);
});
test('429 에러 시 적절한 예외를 던져야 함', () async {
const url = 'https://map.naver.com/p/restaurant/1234567890';
mockApiClient.setUrlRedirect(url, url);
// 429 에러 설정
mockApiClient.setThrow429Error();
expect(
() => parser.parseRestaurantFromUrl(url),
throwsA(isA<RateLimitException>()),
);
});
});
});
}