- 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
228 lines
7.7 KiB
Dart
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>()),
|
|
);
|
|
});
|
|
});
|
|
});
|
|
}
|