import 'package:flutter_test/flutter_test.dart'; import 'package:friendly_map/modules/app/app.dart'; import 'package:friendly_map/modules/platform/platform.dart'; import 'package:friendly_map/modules/transport/transport.dart'; import 'package:friendly_map/modules/visibility/visibility.dart'; import 'support/app_device.dart'; const pa = '+821011112222'; const pb = '+821022223333'; class FakeLocation implements LocationProvider { GeoPoint? point = const GeoPoint(37.566536, 126.977966); // 서울시청 @override Future current() async => point; } Future<(AppDevice, AppDevice, FakeLocation)> scenario({String? relation}) async { final hub = MemoryHub(); final loc = FakeLocation(); final a = await AppDevice.create(hub, '010-1111-2222', pa, '가', location: loc); final b = await AppDevice.create(hub, '010-2222-3333', pb, '나'); final person = (await a.app.friends.add(b.phone, b.name))!; if (relation != null) await a.app.network.addLink(person.id, relation, '시험'); await AppDevice.settleAll(); await b.app.friends.accept(a.phone); await AppDevice.settleAll(); return (a, b, loc); } void main() { group('거리 계산', () { test('서울시청 → 부산시청은 약 325km', () { const seoul = GeoPoint(37.566536, 126.977966), busan = GeoPoint(35.179800, 129.075000); final d = distanceMeters(seoul, busan); expect(d > 320000 && d < 330000, true); }); test('같은 지점은 0, 방위는 북쪽 0도·동쪽 90도', () { const p = GeoPoint(37, 127); expect(distanceMeters(p, p), 0); expect(bearingDegrees(p, const GeoPoint(38, 127)).round(), 0); expect(bearingDegrees(p, const GeoPoint(37, 128)).round(), 90); }); test('읽기 쉬운 글자', () { expect(distanceLabel(350), '350 m'); expect(distanceLabel(2400), '2.4 km'); }); }); group('위치 공유', () { test('기본은 꺼짐: 켜기 전에는 아무도에게 보내지 않는다', () async { final (a, _, _) = await scenario(relation: '가족:동생'); expect(await a.app.location.sharingOn(), false); expect(await a.app.location.shareNow(), 0); }); test('가족에게는 정확한 위치가, 친구가 받은 목록에 들어간다', () async { final (a, b, loc) = await scenario(relation: '가족:동생'); await a.app.location.setSharing(true); expect(await a.app.location.shareNow(), 1); await AppDevice.settleAll(); final got = await b.app.location.friends(); expect(got.length, 1); expect(got.single.person.phone, pa); expect(got.single.point, loc.point); }); test('관계를 모르는 친구에게는 보내지 않는다', () async { final (a, b, _) = await scenario(); await a.app.location.setSharing(true); expect(await a.app.location.shareNow(), 0); await AppDevice.settleAll(); expect(await b.app.location.friends(), isEmpty); }); test('표에서 숨김인 관계(회사)에게는 보내지 않는다, 일부로 바꾸면 약 1km 단위로 뭉개서 보낸다', () async { final (a, b, _) = await scenario(relation: '전 직장 동료'); await a.app.location.setSharing(true); expect(await a.app.location.shareNow(), 0); var m = await a.app.visibility.cycle(VCategory.location, VAudience.company); // 숨김 → 공개 m = await a.app.visibility.cycle(VCategory.location, VAudience.company); // 공개 → 일부 expect(m.get(VCategory.location, VAudience.company), VLevel.partial); expect(await a.app.location.shareNow(), 1); await AppDevice.settleAll(); final p = (await b.app.location.friends()).single.point; expect(p, const GeoPoint(37.57, 126.98)); }); test('위치를 못 얻으면(권한 없음 등) 0명', () async { final (a, _, loc) = await scenario(relation: '가족:동생'); await a.app.location.setSharing(true); loc.point = null; expect(await a.app.location.shareNow(), 0); }); test('더 오래된 위치는 새 위치를 덮지 못한다', () async { final (a, b, loc) = await scenario(relation: '가족:동생'); await a.app.location.setSharing(true); await a.app.location.shareNow(); loc.point = const GeoPoint(35.1798, 129.075); await Future.delayed(const Duration(milliseconds: 30)); await a.app.location.shareNow(); await AppDevice.settleAll(); expect((await b.app.location.friends()).single.point, const GeoPoint(35.1798, 129.075)); }); test('위치 부품이 없는 기기는 available 이 false', () async { final (_, b, _) = await scenario(); expect(b.app.location.available, false); }); }); }