feat(app): add manual entry and sharing flows

This commit is contained in:
JiWoong Sul
2025-11-19 16:36:39 +09:00
parent 5ade584370
commit 947fe59486
110 changed files with 5937 additions and 3781 deletions

View File

@@ -1,315 +0,0 @@
import 'package:flutter_test/flutter_test.dart';
import 'package:hive_flutter/hive_flutter.dart';
import 'package:lunchpick/data/repositories/restaurant_repository_impl.dart';
import 'package:lunchpick/domain/entities/restaurant.dart';
import 'package:mockito/mockito.dart';
// Mock Hive Box
class MockBox<T> extends Mock implements Box<T> {
final Map<dynamic, T> _storage = {};
@override
Future<void> put(key, T value) async {
_storage[key] = value;
}
@override
T? get(key, {T? defaultValue}) {
return _storage[key] ?? defaultValue;
}
@override
Future<void> delete(key) async {
_storage.remove(key);
}
@override
Iterable<T> get values => _storage.values;
@override
Stream<BoxEvent> watch({key}) {
return Stream.empty();
}
}
void main() {
TestWidgetsFlutterBinding.ensureInitialized();
group('RestaurantRepositoryImpl', () {
late RestaurantRepositoryImpl repository;
late MockBox<Restaurant> mockBox;
setUp(() async {
// Hive 초기화
await Hive.initFlutter();
// Mock Box 생성
mockBox = MockBox<Restaurant>();
// Repository 생성 (실제로는 DI를 통해 Box를 주입해야 함)
repository = RestaurantRepositoryImpl();
});
test('getAllRestaurants returns all restaurants', () async {
// Arrange
final restaurant1 = Restaurant(
id: '1',
name: '맛집1',
category: 'korean',
subCategory: '한식',
roadAddress: '서울시 중구',
jibunAddress: '서울시 중구',
latitude: 37.5,
longitude: 127.0,
source: DataSource.USER_INPUT,
createdAt: DateTime.now(),
updatedAt: DateTime.now(),
);
final restaurant2 = Restaurant(
id: '2',
name: '맛집2',
category: 'japanese',
subCategory: '일식',
roadAddress: '서울시 강남구',
jibunAddress: '서울시 강남구',
latitude: 37.4,
longitude: 127.1,
source: DataSource.USER_INPUT,
createdAt: DateTime.now(),
updatedAt: DateTime.now(),
);
await mockBox.put(restaurant1.id, restaurant1);
await mockBox.put(restaurant2.id, restaurant2);
// Act
// 실제 테스트에서는 repository가 mockBox를 사용하도록 설정 필요
// final restaurants = await repository.getAllRestaurants();
// Assert
// expect(restaurants.length, 2);
// expect(restaurants.any((r) => r.name == '맛집1'), true);
// expect(restaurants.any((r) => r.name == '맛집2'), true);
});
test('addRestaurant adds a new restaurant', () async {
// Arrange
final restaurant = Restaurant(
id: '1',
name: '새로운 맛집',
category: 'korean',
subCategory: '한식',
roadAddress: '서울시 종로구',
jibunAddress: '서울시 종로구',
latitude: 37.6,
longitude: 127.0,
source: DataSource.USER_INPUT,
createdAt: DateTime.now(),
updatedAt: DateTime.now(),
);
// Act
// await repository.addRestaurant(restaurant);
// Assert
// final savedRestaurant = await repository.getRestaurantById('1');
// expect(savedRestaurant?.name, '새로운 맛집');
});
test('updateRestaurant updates existing restaurant', () async {
// Arrange
final restaurant = Restaurant(
id: '1',
name: '기존 맛집',
category: 'korean',
subCategory: '한식',
roadAddress: '서울시 종로구',
jibunAddress: '서울시 종로구',
latitude: 37.6,
longitude: 127.0,
source: DataSource.USER_INPUT,
createdAt: DateTime.now(),
updatedAt: DateTime.now(),
);
// await repository.addRestaurant(restaurant);
final updatedRestaurant = Restaurant(
id: '1',
name: '수정된 맛집',
category: 'korean',
subCategory: '한식',
roadAddress: '서울시 종로구',
jibunAddress: '서울시 종로구',
latitude: 37.6,
longitude: 127.0,
source: DataSource.USER_INPUT,
createdAt: restaurant.createdAt,
updatedAt: DateTime.now(),
);
// Act
// await repository.updateRestaurant(updatedRestaurant);
// Assert
// final savedRestaurant = await repository.getRestaurantById('1');
// expect(savedRestaurant?.name, '수정된 맛집');
});
test('deleteRestaurant removes restaurant', () async {
// Arrange
final restaurant = Restaurant(
id: '1',
name: '삭제할 맛집',
category: 'korean',
subCategory: '한식',
roadAddress: '서울시 종로구',
jibunAddress: '서울시 종로구',
latitude: 37.6,
longitude: 127.0,
source: DataSource.USER_INPUT,
createdAt: DateTime.now(),
updatedAt: DateTime.now(),
);
// await repository.addRestaurant(restaurant);
// Act
// await repository.deleteRestaurant('1');
// Assert
// final deletedRestaurant = await repository.getRestaurantById('1');
// expect(deletedRestaurant, null);
});
test('getRestaurantsByCategory returns filtered restaurants', () async {
// Arrange
final koreanRestaurant = Restaurant(
id: '1',
name: '한식당',
category: 'korean',
subCategory: '한식',
roadAddress: '서울시',
jibunAddress: '서울시',
latitude: 37.5,
longitude: 127.0,
source: DataSource.USER_INPUT,
createdAt: DateTime.now(),
updatedAt: DateTime.now(),
);
final japaneseRestaurant = Restaurant(
id: '2',
name: '일식당',
category: 'japanese',
subCategory: '일식',
roadAddress: '서울시',
jibunAddress: '서울시',
latitude: 37.5,
longitude: 127.0,
source: DataSource.USER_INPUT,
createdAt: DateTime.now(),
updatedAt: DateTime.now(),
);
// await repository.addRestaurant(koreanRestaurant);
// await repository.addRestaurant(japaneseRestaurant);
// Act
// final koreanRestaurants = await repository.getRestaurantsByCategory('korean');
// Assert
// expect(koreanRestaurants.length, 1);
// expect(koreanRestaurants.first.name, '한식당');
});
test('searchRestaurants returns matching restaurants', () async {
// Arrange
final restaurant1 = Restaurant(
id: '1',
name: '김치찌개 맛집',
category: 'korean',
subCategory: '한식',
description: '맛있는 김치찌개',
roadAddress: '서울시 종로구',
jibunAddress: '서울시 종로구',
latitude: 37.5,
longitude: 127.0,
source: DataSource.USER_INPUT,
createdAt: DateTime.now(),
updatedAt: DateTime.now(),
);
final restaurant2 = Restaurant(
id: '2',
name: '스시집',
category: 'japanese',
subCategory: '일식',
description: '신선한 스시',
roadAddress: '서울시 강남구',
jibunAddress: '서울시 강남구',
latitude: 37.5,
longitude: 127.0,
source: DataSource.USER_INPUT,
createdAt: DateTime.now(),
updatedAt: DateTime.now(),
);
// await repository.addRestaurant(restaurant1);
// await repository.addRestaurant(restaurant2);
// Act
// final searchResults = await repository.searchRestaurants('김치');
// Assert
// expect(searchResults.length, 1);
// expect(searchResults.first.name, '김치찌개 맛집');
});
test('getRestaurantsWithinDistance returns restaurants within range', () async {
// Arrange
final nearRestaurant = Restaurant(
id: '1',
name: '가까운 맛집',
category: 'korean',
subCategory: '한식',
roadAddress: '서울시',
jibunAddress: '서울시',
latitude: 37.5665,
longitude: 126.9780,
source: DataSource.USER_INPUT,
createdAt: DateTime.now(),
updatedAt: DateTime.now(),
);
final farRestaurant = Restaurant(
id: '2',
name: '먼 맛집',
category: 'korean',
subCategory: '한식',
roadAddress: '서울시',
jibunAddress: '서울시',
latitude: 35.1795,
longitude: 129.0756,
source: DataSource.USER_INPUT,
createdAt: DateTime.now(),
updatedAt: DateTime.now(),
);
// await repository.addRestaurant(nearRestaurant);
// await repository.addRestaurant(farRestaurant);
// Act
// final nearbyRestaurants = await repository.getRestaurantsWithinDistance(
// userLatitude: 37.5665,
// userLongitude: 126.9780,
// maxDistanceInMeters: 1000, // 1km
// );
// Assert
// expect(nearbyRestaurants.length, 1);
// expect(nearbyRestaurants.first.name, '가까운 맛집');
});
});
}