feat(app): add manual entry and sharing flows
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
@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';
|
||||
@@ -7,7 +8,7 @@ void main() {
|
||||
test('네이버 로컬 API 응답 시뮬레이션', () async {
|
||||
// 실제 네이버 로컬 API 응답 형식을 모방
|
||||
final mockApiClient = MockNaverApiClient();
|
||||
|
||||
|
||||
// HTML 응답 설정
|
||||
final htmlContent = '''
|
||||
<html>
|
||||
@@ -25,12 +26,12 @@ void main() {
|
||||
</body>
|
||||
</html>
|
||||
''';
|
||||
|
||||
|
||||
mockApiClient.setHtmlResponse(
|
||||
'https://map.naver.com/p/restaurant/1234567890',
|
||||
htmlContent,
|
||||
);
|
||||
|
||||
|
||||
// GraphQL 응답 설정
|
||||
mockApiClient.setGraphQLResponse({
|
||||
'place': {
|
||||
@@ -40,23 +41,18 @@ void main() {
|
||||
'address': '서울특별시 종로구 세종대로 110',
|
||||
'roadAddress': '서울특별시 종로구 세종대로 110',
|
||||
'phone': '02-1234-5678',
|
||||
'businessHours': {
|
||||
'description': '매일 10:30 - 21:00',
|
||||
},
|
||||
'location': {
|
||||
'lat': 37.5666805,
|
||||
'lng': 126.9784147,
|
||||
},
|
||||
'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, '한식');
|
||||
@@ -64,12 +60,12 @@ void main() {
|
||||
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 = [
|
||||
{
|
||||
@@ -85,11 +81,12 @@ void main() {
|
||||
'expectedLng': 127.0333333,
|
||||
},
|
||||
];
|
||||
|
||||
|
||||
for (final testCase in testCases) {
|
||||
final mockApiClient = MockNaverApiClient();
|
||||
|
||||
final htmlContent = '''
|
||||
|
||||
final htmlContent =
|
||||
'''
|
||||
<html>
|
||||
<head>
|
||||
<meta property="og:url" content="https://map.naver.com/p/restaurant/1234567890?y=${testCase['expectedLat']}&x=${testCase['expectedLng']}">
|
||||
@@ -99,12 +96,12 @@ void main() {
|
||||
</body>
|
||||
</html>
|
||||
''';
|
||||
|
||||
|
||||
mockApiClient.setHtmlResponse(
|
||||
'https://map.naver.com/p/restaurant/1234567890',
|
||||
htmlContent,
|
||||
);
|
||||
|
||||
|
||||
// GraphQL 응답도 설정
|
||||
mockApiClient.setGraphQLResponse({
|
||||
'place': {
|
||||
@@ -116,12 +113,12 @@ void main() {
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
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),
|
||||
@@ -134,7 +131,7 @@ void main() {
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
test('카테고리 정규화 테스트', () async {
|
||||
final categoryTests = [
|
||||
{'input': '한식>김치찌개', 'expectedMain': '한식', 'expectedSub': '김치찌개'},
|
||||
@@ -142,11 +139,12 @@ void main() {
|
||||
{'input': '양식 > 파스타', 'expectedMain': '양식', 'expectedSub': '파스타'},
|
||||
{'input': '중식', 'expectedMain': '중식', 'expectedSub': '중식'},
|
||||
];
|
||||
|
||||
|
||||
for (final test in categoryTests) {
|
||||
final mockApiClient = MockNaverApiClient();
|
||||
|
||||
final htmlContent = '''
|
||||
|
||||
final htmlContent =
|
||||
'''
|
||||
<html>
|
||||
<body>
|
||||
<span class="GHAhO">카테고리 테스트</span>
|
||||
@@ -154,17 +152,17 @@ void main() {
|
||||
</body>
|
||||
</html>
|
||||
''';
|
||||
|
||||
|
||||
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'],
|
||||
@@ -177,10 +175,10 @@ void main() {
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
test('HTML 엔티티 디코딩 테스트', () async {
|
||||
final mockApiClient = MockNaverApiClient();
|
||||
|
||||
|
||||
final htmlContent = '''
|
||||
<html>
|
||||
<body>
|
||||
@@ -190,23 +188,23 @@ void main() {
|
||||
</body>
|
||||
</html>
|
||||
''';
|
||||
|
||||
|
||||
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',
|
||||
@@ -215,11 +213,12 @@ void main() {
|
||||
'화요일 휴무, 그 외 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 = '''
|
||||
|
||||
final htmlContent =
|
||||
'''
|
||||
<html>
|
||||
<body>
|
||||
<span class="GHAhO">영업시간 테스트</span>
|
||||
@@ -227,25 +226,21 @@ void main() {
|
||||
</body>
|
||||
</html>
|
||||
''';
|
||||
|
||||
|
||||
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: '영업시간이 정확히 파싱되어야 함',
|
||||
);
|
||||
|
||||
expect(restaurant.businessHours, hours, reason: '영업시간이 정확히 파싱되어야 함');
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
test('Place ID 추출 패턴 테스트', () async {
|
||||
final urlPatterns = [
|
||||
{
|
||||
@@ -261,10 +256,10 @@ void main() {
|
||||
'expectedId': '1234567890',
|
||||
},
|
||||
];
|
||||
|
||||
|
||||
for (final pattern in urlPatterns) {
|
||||
final mockApiClient = MockNaverApiClient();
|
||||
|
||||
|
||||
final htmlContent = '''
|
||||
<html>
|
||||
<body>
|
||||
@@ -272,15 +267,12 @@ void main() {
|
||||
</body>
|
||||
</html>
|
||||
''';
|
||||
|
||||
mockApiClient.setHtmlResponse(
|
||||
pattern['url']!,
|
||||
htmlContent,
|
||||
);
|
||||
|
||||
|
||||
mockApiClient.setHtmlResponse(pattern['url']!, htmlContent);
|
||||
|
||||
final parser = NaverMapParser(apiClient: mockApiClient);
|
||||
final restaurant = await parser.parseRestaurantFromUrl(pattern['url']!);
|
||||
|
||||
|
||||
expect(
|
||||
restaurant.naverPlaceId,
|
||||
pattern['expectedId'],
|
||||
@@ -289,72 +281,69 @@ void main() {
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
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,
|
||||
'''
|
||||
mockApiClient.setHtmlResponse(url, '''
|
||||
<html>
|
||||
<body>
|
||||
<span class="GHAhO">동시성 테스트 식당 ${i + 1}</span>
|
||||
<span class="DJJvD">한식</span>
|
||||
</body>
|
||||
</html>
|
||||
''',
|
||||
);
|
||||
|
||||
''');
|
||||
|
||||
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',
|
||||
'<html><body><span class="GHAhO">Test</span></body></html>',
|
||||
);
|
||||
|
||||
|
||||
final parser = NaverMapParser(apiClient: mockApiClient);
|
||||
|
||||
|
||||
// 여러 번 사용
|
||||
for (int i = 0; i < 3; i++) {
|
||||
try {
|
||||
await parser.parseRestaurantFromUrl(
|
||||
'https://map.naver.com/p/restaurant/123456789'
|
||||
'https://map.naver.com/p/restaurant/123456789',
|
||||
);
|
||||
} catch (_) {
|
||||
// 에러 무시
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// dispose 호출
|
||||
parser.dispose();
|
||||
|
||||
|
||||
// dispose 후에는 사용할 수 없어야 함
|
||||
expect(
|
||||
() => parser.parseRestaurantFromUrl('https://map.naver.com/p/restaurant/999'),
|
||||
await expectLater(
|
||||
parser.parseRestaurantFromUrl('https://map.naver.com/p/restaurant/999'),
|
||||
throwsA(anything),
|
||||
);
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
@Skip('Requires live Naver API responses')
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:lunchpick/data/api/naver_api_client.dart';
|
||||
import 'package:lunchpick/data/datasources/remote/naver_map_parser.dart';
|
||||
@@ -21,14 +22,14 @@ void main() {
|
||||
test('단축 URL 자동 처리 테스트', () async {
|
||||
// 실제 단축 URL로 테스트
|
||||
const shortUrl = 'https://naver.me/example'; // 실제 URL로 교체 필요
|
||||
|
||||
|
||||
try {
|
||||
print('========== 단축 URL 자동 처리 테스트 ==========');
|
||||
print('입력 URL: $shortUrl');
|
||||
|
||||
|
||||
// NaverMapParser를 통한 자동 처리
|
||||
final restaurant = await parser.parseRestaurantFromUrl(shortUrl);
|
||||
|
||||
|
||||
print('\n【파싱 결과】');
|
||||
print('상호명: ${restaurant.name}');
|
||||
print('카테고리: ${restaurant.category} > ${restaurant.subCategory}');
|
||||
@@ -38,14 +39,14 @@ void main() {
|
||||
print('좌표: ${restaurant.latitude}, ${restaurant.longitude}');
|
||||
print('Place ID: ${restaurant.naverPlaceId}');
|
||||
print('URL: ${restaurant.naverUrl}');
|
||||
|
||||
|
||||
// 검증
|
||||
expect(restaurant.name, isNotEmpty);
|
||||
expect(restaurant.category, isNotEmpty);
|
||||
expect(restaurant.roadAddress, isNotEmpty);
|
||||
expect(restaurant.naverPlaceId, isNotEmpty);
|
||||
expect(restaurant.source.name, equals('NAVER'));
|
||||
|
||||
|
||||
print('\n✓ 테스트 성공');
|
||||
} catch (e) {
|
||||
print('\n❌ 테스트 실패: $e');
|
||||
@@ -71,36 +72,37 @@ void main() {
|
||||
</body>
|
||||
</html>
|
||||
''';
|
||||
|
||||
|
||||
print('\n========== HTML 추출기 테스트 ==========');
|
||||
|
||||
|
||||
// 한글 텍스트 추출
|
||||
final koreanTexts = NaverHtmlExtractor.extractAllValidKoreanTexts(testHtml);
|
||||
final koreanTexts = NaverHtmlExtractor.extractAllValidKoreanTexts(
|
||||
testHtml,
|
||||
);
|
||||
print('추출된 한글 텍스트: $koreanTexts');
|
||||
expect(koreanTexts, isNotEmpty);
|
||||
|
||||
|
||||
// JSON-LD 추출
|
||||
final jsonLdName = NaverHtmlExtractor.extractPlaceNameFromJsonLd(testHtml);
|
||||
final jsonLdName = NaverHtmlExtractor.extractPlaceNameFromJsonLd(
|
||||
testHtml,
|
||||
);
|
||||
print('JSON-LD 상호명: $jsonLdName');
|
||||
expect(jsonLdName, equals('테스트 식당'));
|
||||
|
||||
|
||||
print('\n✓ 테스트 성공');
|
||||
});
|
||||
|
||||
test('로컬 검색 API 테스트', () async {
|
||||
print('\n========== 로컬 검색 API 테스트 ==========');
|
||||
|
||||
|
||||
const query = '스타벅스 강남역점';
|
||||
|
||||
|
||||
try {
|
||||
final results = await apiClient.searchLocal(
|
||||
query: query,
|
||||
display: 5,
|
||||
);
|
||||
|
||||
final results = await apiClient.searchLocal(query: query, display: 5);
|
||||
|
||||
print('검색어: "$query"');
|
||||
print('결과 수: ${results.length}개\n');
|
||||
|
||||
|
||||
for (int i = 0; i < results.length; i++) {
|
||||
final result = results[i];
|
||||
print('${i + 1}. ${result.title}');
|
||||
@@ -108,7 +110,7 @@ void main() {
|
||||
print(' 주소: ${result.roadAddress}');
|
||||
print(' 좌표: ${result.mapx}, ${result.mapy}');
|
||||
}
|
||||
|
||||
|
||||
expect(results, isNotEmpty);
|
||||
print('\n✓ 테스트 성공');
|
||||
} catch (e) {
|
||||
@@ -119,21 +121,21 @@ void main() {
|
||||
|
||||
test('성능 테스트 - 단축 URL 처리 시간', () async {
|
||||
const shortUrl = 'https://naver.me/example'; // 실제 URL로 교체 필요
|
||||
|
||||
|
||||
print('\n========== 성능 테스트 ==========');
|
||||
|
||||
|
||||
final stopwatch = Stopwatch()..start();
|
||||
|
||||
|
||||
try {
|
||||
final restaurant = await parser.parseRestaurantFromUrl(shortUrl);
|
||||
stopwatch.stop();
|
||||
|
||||
|
||||
print('처리 완료: ${restaurant.name}');
|
||||
print('소요 시간: ${stopwatch.elapsedMilliseconds}ms');
|
||||
|
||||
|
||||
// 5초 이내 처리 확인
|
||||
expect(stopwatch.elapsedMilliseconds, lessThan(5000));
|
||||
|
||||
|
||||
print('\n✓ 테스트 성공');
|
||||
} catch (e) {
|
||||
stopwatch.stop();
|
||||
@@ -143,4 +145,4 @@ void main() {
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user