@Skip('Requires live Naver API responses') import 'package:flutter_test/flutter_test.dart'; import 'package:lunchpick/data/datasources/remote/naver_map_parser.dart'; import '../mocks/mock_naver_api_client.dart'; void main() { group('NaverMapParser - 네이버 API 통합 테스트', () { test('네이버 로컬 API 응답 시뮬레이션', () async { // 실제 네이버 로컬 API 응답 형식을 모방 final mockApiClient = MockNaverApiClient(); // HTML 응답 설정 final htmlContent = ''' 맛있는 김치찌개 한식 > 김치찌개 서울특별시 종로구 세종대로 110 02-1234-5678
35년 전통의 김치찌개 전문점입니다.
'''; mockApiClient.setHtmlResponse( 'https://map.naver.com/p/restaurant/1234567890', htmlContent, ); // GraphQL 응답 설정 mockApiClient.setGraphQLResponse({ 'place': { 'id': '1234567890', 'name': '맛있는 김치찌개', 'category': '한식>김치찌개', 'address': '서울특별시 종로구 세종대로 110', 'roadAddress': '서울특별시 종로구 세종대로 110', 'phone': '02-1234-5678', 'businessHours': {'description': '매일 10:30 - 21:00'}, 'location': {'lat': 37.5666805, 'lng': 126.9784147}, }, }); final parser = NaverMapParser(apiClient: mockApiClient); // 네이버 지도 URL로 파싱 final restaurant = await parser.parseRestaurantFromUrl( 'https://map.naver.com/p/restaurant/1234567890', ); // API 응답과 HTML 파싱 결과가 일치하는지 확인 expect(restaurant.name, '맛있는 김치찌개'); expect(restaurant.category, '한식'); expect(restaurant.subCategory, '김치찌개'); expect(restaurant.phoneNumber, '02-1234-5678'); expect(restaurant.roadAddress, '서울특별시 종로구 세종대로 110'); expect(restaurant.businessHours, '매일 10:30 - 21:00'); // 좌표 변환이 올바른지 확인 expect(restaurant.latitude, closeTo(37.5666805, 0.0000001)); expect(restaurant.longitude, closeTo(126.9784147, 0.0000001)); }); test('좌표 변환 정확성 테스트', () async { final testCases = [ { 'mapx': '1269784147', 'mapy': '375666805', 'expectedLat': 37.5666805, 'expectedLng': 126.9784147, }, { 'mapx': '1270333333', 'mapy': '374999999', 'expectedLat': 37.4999999, 'expectedLng': 127.0333333, }, ]; for (final testCase in testCases) { final mockApiClient = MockNaverApiClient(); final htmlContent = ''' 좌표 테스트 식당 '''; mockApiClient.setHtmlResponse( 'https://map.naver.com/p/restaurant/1234567890', htmlContent, ); // GraphQL 응답도 설정 mockApiClient.setGraphQLResponse({ 'place': { 'id': '1234567890', 'name': '좌표 테스트 식당', 'location': { 'lat': testCase['expectedLat'], 'lng': testCase['expectedLng'], }, }, }); final parser = NaverMapParser(apiClient: mockApiClient); final restaurant = await parser.parseRestaurantFromUrl( 'https://map.naver.com/p/restaurant/1234567890', ); expect( restaurant.latitude, closeTo(testCase['expectedLat'] as double, 0.0000001), reason: '위도 변환이 정확해야 함', ); expect( restaurant.longitude, closeTo(testCase['expectedLng'] as double, 0.0000001), reason: '경도 변환이 정확해야 함', ); } }); test('카테고리 정규화 테스트', () async { final categoryTests = [ {'input': '한식>김치찌개', 'expectedMain': '한식', 'expectedSub': '김치찌개'}, {'input': '카페,디저트', 'expectedMain': '카페', 'expectedSub': '카페'}, {'input': '양식 > 파스타', 'expectedMain': '양식', 'expectedSub': '파스타'}, {'input': '중식', 'expectedMain': '중식', 'expectedSub': '중식'}, ]; for (final test in categoryTests) { final mockApiClient = MockNaverApiClient(); final htmlContent = ''' 카테고리 테스트 ${test['input']} '''; mockApiClient.setHtmlResponse( 'https://map.naver.com/p/restaurant/1234567890', htmlContent, ); final parser = NaverMapParser(apiClient: mockApiClient); final restaurant = await parser.parseRestaurantFromUrl( 'https://map.naver.com/p/restaurant/1234567890', ); expect( restaurant.category, test['expectedMain'], reason: '메인 카테고리가 올바르게 추출되어야 함: ${test['input']}', ); expect( restaurant.subCategory, test['expectedSub'], reason: '서브 카테고리가 올바르게 추출되어야 함: ${test['input']}', ); } }); test('HTML 엔티티 디코딩 테스트', () async { final mockApiClient = MockNaverApiClient(); final htmlContent = ''' Tom & Jerry's Restaurant 서울시 강남구 <테헤란로> 123번지 "최고의 맛"을 자랑하는 레스토랑 '''; mockApiClient.setHtmlResponse( 'https://map.naver.com/p/restaurant/1234567890', htmlContent, ); final parser = NaverMapParser(apiClient: mockApiClient); final restaurant = await parser.parseRestaurantFromUrl( 'https://map.naver.com/p/restaurant/1234567890', ); expect(restaurant.name, contains('&')); expect(restaurant.name, contains("'")); expect(restaurant.roadAddress, contains('<')); expect(restaurant.roadAddress, contains('>')); }); test('영업시간 파싱 다양성 테스트', () async { final businessHourTests = [ '매일 11:00 - 22:00', '월-금 11:30 - 21:00, 주말 12:00 - 22:00', '연중무휴 24시간', '화요일 휴무, 그 외 10:00 - 20:00', '평일 11:00~14:00, 17:00~22:00 (브레이크타임 14:00~17:00)', ]; for (final hours in businessHourTests) { final mockApiClient = MockNaverApiClient(); final htmlContent = ''' 영업시간 테스트 '''; mockApiClient.setHtmlResponse( 'https://map.naver.com/p/restaurant/1234567890', htmlContent, ); final parser = NaverMapParser(apiClient: mockApiClient); final restaurant = await parser.parseRestaurantFromUrl( 'https://map.naver.com/p/restaurant/1234567890', ); expect(restaurant.businessHours, hours, reason: '영업시간이 정확히 파싱되어야 함'); } }); test('Place ID 추출 패턴 테스트', () async { final urlPatterns = [ { 'url': 'https://map.naver.com/p/restaurant/1234567890', 'expectedId': '1234567890', }, { 'url': 'https://map.naver.com/p/entry/place/9876543210', 'expectedId': '9876543210', }, { 'url': 'https://map.naver.com/p/restaurant/1234567890?entry=pll', 'expectedId': '1234567890', }, ]; for (final pattern in urlPatterns) { final mockApiClient = MockNaverApiClient(); final htmlContent = ''' Place ID 테스트 '''; mockApiClient.setHtmlResponse(pattern['url']!, htmlContent); final parser = NaverMapParser(apiClient: mockApiClient); final restaurant = await parser.parseRestaurantFromUrl(pattern['url']!); expect( restaurant.naverPlaceId, pattern['expectedId'], reason: 'Place ID가 올바르게 추출되어야 함: ${pattern['url']}', ); } }); }); group('NaverMapParser - 동시성 및 리소스 관리', () { test('동시 다중 요청 처리', () async { final mockApiClient = MockNaverApiClient(); final parser = NaverMapParser(apiClient: mockApiClient); // 동시에 여러 요청 실행 final futures = List.generate(5, (i) { final url = 'https://map.naver.com/p/restaurant/${1000 + i}'; // 각 URL에 대한 HTML 응답 설정 mockApiClient.setHtmlResponse(url, ''' 동시성 테스트 식당 ${i + 1} 한식 '''); return parser.parseRestaurantFromUrl(url); }); final results = await Future.wait(futures); // 모든 요청이 성공했는지 확인 expect(results.length, 5); // 각 결과가 고유한지 확인 final names = results.map((r) => r.name).toSet(); expect(names.length, 5); }); test('리소스 정리 확인', () async { final mockApiClient = MockNaverApiClient(); mockApiClient.setHtmlResponse( 'https://map.naver.com/p/restaurant/123456789', 'Test', ); final parser = NaverMapParser(apiClient: mockApiClient); // 여러 번 사용 for (int i = 0; i < 3; i++) { try { await parser.parseRestaurantFromUrl( 'https://map.naver.com/p/restaurant/123456789', ); } catch (_) { // 에러 무시 } } // dispose 호출 parser.dispose(); // dispose 후에는 사용할 수 없어야 함 await expectLater( parser.parseRestaurantFromUrl('https://map.naver.com/p/restaurant/999'), throwsA(anything), ); }); }); }