import 'dart:async'; import 'package:flutter_test/flutter_test.dart'; import 'package:friendly_map/modules/discovery/discovery.dart'; import 'package:friendly_map/modules/platform/platform.dart'; class _FakeLan implements LanDiscovery { final StreamController ctl = StreamController.broadcast(sync: true); String? advertised; bool started = false; @override Future advertise({required String phone, required int port}) async => advertised = '$phone:$port'; @override Stream get peers => ctl.stream; @override Future start() async => started = true; @override Future stop() async => started = false; } void main() { group('QR', () { test('담았다가 읽으면 같다 (한글·특수문자 포함)', () { const p = QrPayload(phone: '+821011112222', name: '정민호 😀', publicKey: 'a+b/c=='); final back = QrPayload.decode(p.encode())!; expect(back.phone, p.phone); expect(back.name, p.name); expect(back.publicKey, p.publicKey); }); test('우리 앱 QR 이 아니거나 깨졌으면 null', () { expect(QrPayload.decode('https://example.com'), null); expect(QrPayload.decode('fm1:@@@'), null); expect(QrPayload.decode('fm1:'), null); expect(QrPayload.decode(''), null); }); }); group('흔들기', () { test('짧은 시간에 세 번 세게 흔들면 한 번 알림', () { final d = ShakeDetector(); var fired = 0; var t = 0; for (var i = 0; i < 3; i++) { if (d.feed(25, t)) fired++; t += 100; if (d.feed(9.8, t)) fired++; t += 100; } expect(fired, 1); }); test('가만히 있거나 한 번만 움직이면 알리지 않음', () { final d = ShakeDetector(); for (var t = 0; t < 3000; t += 50) { expect(d.feed(9.8, t), false); } expect(d.feed(30, 3100), false); }); test('느리게 세 번이면 알리지 않고, 알린 뒤 바로 또 알리지 않음', () { final d = ShakeDetector(); var t = 0; var fired = false; for (var i = 0; i < 3; i++) { fired = d.feed(25, t) || fired; d.feed(9.8, t + 50); t += 1500; } expect(fired, false); final e = ShakeDetector(); var n = 0; for (var i = 0; i < 12; i++) { if (e.feed(25, i * 100)) n++; e.feed(9.8, i * 100 + 50); } expect(n, 1); }); }); group('같은 네트워크 주소록', () { test('발견한 상대를 번호로 찾고, 나 자신은 제외, 주소가 바뀌면 다시 알림', () async { final lan = _FakeLan(); final dir = LanDirectory(lan); final seen = []; dir.seen.listen(seen.add); await dir.start(myPhone: '+821011112222', myPort: 4000); expect(lan.advertised, '+821011112222:4000'); expect(lan.started, true); lan.ctl.add(const LanPeer(phone: '+821011112222', host: '10.0.0.1', port: 4000)); lan.ctl.add(const LanPeer(phone: '+821022223333', host: '10.0.0.2', port: 4001)); lan.ctl.add(const LanPeer(phone: '+821022223333', host: '10.0.0.2', port: 4001)); lan.ctl.add(const LanPeer(phone: '+821022223333', host: '10.0.0.9', port: 4001)); expect(seen.length, 2); expect(dir.find('+821022223333')!.host, '10.0.0.9'); expect(dir.find('+821011112222'), null); dir.forget('+821022223333'); expect(dir.all, isEmpty); await dir.stop(); expect(lan.started, false); }); }); }