LunchPick(오늘 뭐 먹Z?) Flutter 앱의 초기 구현입니다. 주요 기능: - 네이버 지도 연동 맛집 추가 - 랜덤 메뉴 추천 시스템 - 날씨 기반 거리 조정 - 방문 기록 관리 - Bluetooth 맛집 공유 - 다크모드 지원 기술 스택: - Flutter 3.8.1+ - Riverpod 상태 관리 - Hive 로컬 DB - Clean Architecture 🤖 Generated with Claude Code Co-Authored-By: Claude <noreply@anthropic.com>
193 lines
5.8 KiB
Dart
193 lines
5.8 KiB
Dart
import 'package:flutter_test/flutter_test.dart';
|
|
import 'package:lunchpick/domain/usecases/recommendation_engine.dart';
|
|
import 'package:lunchpick/domain/entities/restaurant.dart';
|
|
import 'package:lunchpick/domain/entities/visit_record.dart';
|
|
import 'package:lunchpick/domain/entities/user_settings.dart';
|
|
import 'package:uuid/uuid.dart';
|
|
|
|
void main() {
|
|
late RecommendationEngine engine;
|
|
late List<Restaurant> testRestaurants;
|
|
late List<VisitRecord> testVisitRecords;
|
|
|
|
setUp(() {
|
|
engine = RecommendationEngine();
|
|
|
|
// 테스트용 맛집 데이터 생성
|
|
testRestaurants = [
|
|
Restaurant(
|
|
id: '1',
|
|
name: '가까운 한식당',
|
|
category: '한식',
|
|
subCategory: '백반',
|
|
roadAddress: '서울 중구 세종대로 110',
|
|
jibunAddress: '서울 중구 태평로1가 31',
|
|
latitude: 37.5666,
|
|
longitude: 126.9784,
|
|
source: DataSource.USER_INPUT,
|
|
createdAt: DateTime.now(),
|
|
updatedAt: DateTime.now(),
|
|
visitCount: 0,
|
|
),
|
|
Restaurant(
|
|
id: '2',
|
|
name: '먼 중식당',
|
|
category: '중식',
|
|
subCategory: '짜장면',
|
|
roadAddress: '서울 강남구 테헤란로 123',
|
|
jibunAddress: '서울 강남구 역삼동 123',
|
|
latitude: 37.5012,
|
|
longitude: 127.0396,
|
|
source: DataSource.USER_INPUT,
|
|
createdAt: DateTime.now(),
|
|
updatedAt: DateTime.now(),
|
|
visitCount: 0,
|
|
),
|
|
Restaurant(
|
|
id: '3',
|
|
name: '최근 방문한 일식당',
|
|
category: '일식',
|
|
subCategory: '스시',
|
|
roadAddress: '서울 종로구 종로 123',
|
|
jibunAddress: '서울 종로구 종로1가 123',
|
|
latitude: 37.5702,
|
|
longitude: 126.9842,
|
|
source: DataSource.USER_INPUT,
|
|
createdAt: DateTime.now(),
|
|
updatedAt: DateTime.now(),
|
|
visitCount: 1,
|
|
),
|
|
];
|
|
|
|
// 테스트용 방문 기록 생성
|
|
testVisitRecords = [
|
|
VisitRecord(
|
|
id: const Uuid().v4(),
|
|
restaurantId: '3',
|
|
visitDate: DateTime.now().subtract(const Duration(days: 2)),
|
|
isConfirmed: true,
|
|
createdAt: DateTime.now(),
|
|
),
|
|
];
|
|
});
|
|
|
|
group('RecommendationEngine', () {
|
|
test('거리 필터링이 정상 작동해야 함', () async {
|
|
final config = RecommendationConfig(
|
|
userLatitude: 37.5665,
|
|
userLongitude: 126.9780,
|
|
maxDistance: 1.0, // 1km
|
|
selectedCategories: [],
|
|
userSettings: UserSettings(),
|
|
);
|
|
|
|
final result = await engine.generateRecommendation(
|
|
allRestaurants: testRestaurants,
|
|
recentVisits: [],
|
|
config: config,
|
|
);
|
|
|
|
// 1km 이내의 맛집만 추천되어야 함
|
|
expect(result, isNotNull);
|
|
expect(result!.id, isIn(['1', '3'])); // 가까운 한식당과 일식당만
|
|
expect(result.id, isNot('2')); // 먼 중식당은 제외
|
|
});
|
|
|
|
test('재방문 방지가 정상 작동해야 함', () async {
|
|
final settings = UserSettings();
|
|
final updatedSettings = settings.copyWith(revisitPreventionDays: 7);
|
|
|
|
final config = RecommendationConfig(
|
|
userLatitude: 37.5665,
|
|
userLongitude: 126.9780,
|
|
maxDistance: 10.0, // 10km
|
|
selectedCategories: [],
|
|
userSettings: updatedSettings,
|
|
);
|
|
|
|
final result = await engine.generateRecommendation(
|
|
allRestaurants: testRestaurants,
|
|
recentVisits: testVisitRecords,
|
|
config: config,
|
|
);
|
|
|
|
// 최근 방문한 일식당은 제외되어야 함
|
|
expect(result, isNotNull);
|
|
expect(result!.id, isNot('3'));
|
|
});
|
|
|
|
test('카테고리 필터링이 정상 작동해야 함', () async {
|
|
final config = RecommendationConfig(
|
|
userLatitude: 37.5665,
|
|
userLongitude: 126.9780,
|
|
maxDistance: 100.0, // 100km
|
|
selectedCategories: ['한식'],
|
|
userSettings: UserSettings(),
|
|
);
|
|
|
|
final result = await engine.generateRecommendation(
|
|
allRestaurants: testRestaurants,
|
|
recentVisits: [],
|
|
config: config,
|
|
);
|
|
|
|
// 한식만 추천되어야 함
|
|
expect(result, isNotNull);
|
|
expect(result!.category, '한식');
|
|
});
|
|
|
|
test('모든 조건을 만족하는 맛집이 없으면 null을 반환해야 함', () async {
|
|
final config = RecommendationConfig(
|
|
userLatitude: 37.5665,
|
|
userLongitude: 126.9780,
|
|
maxDistance: 0.1, // 100m - 너무 가까움
|
|
selectedCategories: [],
|
|
userSettings: UserSettings(),
|
|
);
|
|
|
|
final result = await engine.generateRecommendation(
|
|
allRestaurants: testRestaurants,
|
|
recentVisits: [],
|
|
config: config,
|
|
);
|
|
|
|
expect(result, isNull);
|
|
});
|
|
|
|
test('가중치 시스템이 정상 작동해야 함', () async {
|
|
// 한식에 높은 가중치 부여
|
|
final settings = UserSettings();
|
|
final updatedSettings = settings.copyWith(
|
|
categoryWeights: {
|
|
'한식': 2.0,
|
|
'중식': 0.5,
|
|
'일식': 1.0,
|
|
},
|
|
);
|
|
|
|
final config = RecommendationConfig(
|
|
userLatitude: 37.5665,
|
|
userLongitude: 126.9780,
|
|
maxDistance: 100.0,
|
|
selectedCategories: [],
|
|
userSettings: updatedSettings,
|
|
);
|
|
|
|
// 여러 번 실행하여 한식이 더 자주 추천되는지 확인
|
|
final results = <String, int>{};
|
|
for (int i = 0; i < 100; i++) {
|
|
final result = await engine.generateRecommendation(
|
|
allRestaurants: testRestaurants,
|
|
recentVisits: [],
|
|
config: config,
|
|
);
|
|
if (result != null) {
|
|
results[result.category] = (results[result.category] ?? 0) + 1;
|
|
}
|
|
}
|
|
|
|
// 한식이 다른 카테고리보다 더 많이 추천되어야 함
|
|
expect(results['한식'] ?? 0, greaterThan(results['중식'] ?? 0));
|
|
});
|
|
});
|
|
} |