import 'package:dio/dio.dart'; import 'package:lunchpick/data/api/naver_api_client.dart'; import 'package:lunchpick/data/api/naver/naver_local_search_api.dart'; import 'package:lunchpick/core/errors/network_exceptions.dart'; /// 테스트용 모의 네이버 API 클라이언트 class MockNaverApiClient extends NaverApiClient { final Map _urlMappings = {}; final Map _htmlResponses = {}; final Map _searchResults = {}; final Map _graphqlResponses = {}; /// URL 리다이렉션 매핑 설정 void setUrlRedirect(String fromUrl, String toUrl) { _urlMappings[fromUrl] = toUrl; } /// HTML 응답 설정 void setHtmlResponse(String url, String html) { _htmlResponses[url] = html; } /// 검색 결과 설정 void setSearchResults(String query, List results) { _searchResults[query] = results; } /// GraphQL 응답 설정 void setGraphQLResponse(Map response) { _graphqlResponses['default'] = response; } /// 에러 시뮬레이션 설정 bool shouldThrowError = false; String errorMessage = '테스트 에러'; @override Future resolveShortUrl(String shortUrl) async { if (shouldThrowError && !_throw429) { throw Exception(errorMessage); } // 설정된 매핑이 있으면 반환 if (_urlMappings.containsKey(shortUrl)) { return _urlMappings[shortUrl]!; } // 기본적으로 원본 URL 반환 return shortUrl; } @override Future fetchMapPageHtml(String url) async { if (shouldThrowError || _throw429) { throw Exception(errorMessage); } // 설정된 HTML이 있으면 반환 if (_htmlResponses.containsKey(url)) { return _htmlResponses[url]!; } // 기본 HTML 반환 return ''' 기본 테스트 식당 '''; } @override Future> searchLocal({ required String query, double? latitude, double? longitude, int display = 20, int start = 1, String sort = 'random', }) async { if (shouldThrowError) { throw Exception(errorMessage); } // 설정된 검색 결과가 있으면 반환 if (_searchResults.containsKey(query)) { return _searchResults[query] as List; } // 기본 검색 결과 반환 return [ NaverLocalSearchResult.fromJson({ 'title': '$query 테스트', 'link': 'https://map.naver.com/p/restaurant/1234567890', 'category': '한식>김치찌개', 'description': '테스트 설명', 'telephone': '02-1234-5678', 'address': '서울시 종로구', 'roadAddress': '서울시 종로구 세종대로 110', 'mapx': 1269784147, 'mapy': 375666805, }), ]; } @override Future> fetchGraphQL({ required String operationName, Map? variables, required String query, }) async { if (shouldThrowError || _throw429) { throw Exception(errorMessage); } // 설정된 GraphQL 응답이 있으면 반환 if (_graphqlResponses.containsKey('default')) { return { 'data': _graphqlResponses['default'], }; } // 기본 응답 반환 (places 배열 형태로 반환) return { 'data': { 'places': [{ 'id': '1', 'name': '기본 테스트 식당', 'category': '한식', 'address': '서울시 종로구', }], }, }; } @override Future fetchPlaceNameFromPcmap(String placeId) async { if (shouldThrowError || _throw429) { throw Exception(errorMessage); } // 테스트에서 설정한 값이 있으면 반환 if (_placeNames.containsKey(placeId)) { return _placeNames[placeId]; } // 기본값 반환 return '기본 테스트 식당'; } // fetchPlaceNameFromPcmap용 응답 저장소 final Map _placeNames = {}; /// 장소명 설정 void setPlaceName(String placeId, String placeName) { _placeNames[placeId] = placeName; } // V2 확장 메서드들 final Map _finalRedirectUrls = {}; final Map _secondKoreanTexts = {}; bool _throw429 = false; void setFinalRedirectUrl(String from, String to) { _finalRedirectUrls[from] = to; } void setSecondKoreanText(String url, String text) { _secondKoreanTexts[url] = text; } void setThrow429Error() { _throw429 = true; } @override Future getFinalRedirectUrl(String url) async { if (_throw429) { throw RateLimitException( retryAfter: '60', originalError: '429 Too Many Requests', ); } await Future.delayed(const Duration(milliseconds: 500)); return _finalRedirectUrls[url] ?? url; } @override Future extractSecondKoreanText(String url) async { if (_throw429) { throw Exception('429 Too Many Requests'); } await Future.delayed(const Duration(milliseconds: 500)); return _secondKoreanTexts[url]; } // fetchKoreanTextsFromPcmap 구현 final Map> _koreanTextsData = {}; void setKoreanTextsData(String placeId, Map data) { _koreanTextsData[placeId] = data; } @override Future> fetchKoreanTextsFromPcmap(String placeId) async { if (shouldThrowError || _throw429) { throw Exception(errorMessage); } // 설정된 데이터가 있으면 반환 if (_koreanTextsData.containsKey(placeId)) { return _koreanTextsData[placeId]!; } // 기본 데이터 반환 return { 'success': true, 'koreanTexts': ['기본 테스트 식당'], 'jsonLdName': '기본 테스트 식당', 'apolloStateName': null, }; } } // NaverLocalSearchResult는 이미 naver_api_client.dart에 정의되어 있음