import 'package:flutter_test/flutter_test.dart'; import 'package:friendly_map/modules/app/app.dart'; import 'package:friendly_map/modules/core/core.dart'; import 'package:friendly_map/modules/platform/platform.dart'; import 'package:friendly_map/modules/transport/transport.dart'; import 'package:friendly_map/modules/work/work.dart'; import 'support/app_device.dart'; class FakeInstant implements InstantNotifier { final shown = []; @override Future showNow(String title, String text) async => shown.add('$title|$text'); } Future befriend(AppDevice a, AppDevice b) async { await a.app.friends.add(b.phone, b.name); await AppDevice.settleAll(); await b.app.friends.accept(a.phone); await AppDevice.settleAll(); } Future shareCompany(AppDevice from, AppDevice to) async { final peer = (await from.app.deps.storage.people.byPhone(to.phone))!; final company = (await from.app.cards.mine()).firstWhere((c) => c.kind == CardKinds.company); await from.app.cards.shareWith(peer.id, [company.id]); await AppDevice.settleAll(); } void main() { late AppDevice a, b, c; late FakeInstant bAlerts; setUp(() async { final hub = MemoryHub(); bAlerts = FakeInstant(); a = await AppDevice.create(hub, '010-1111-2222', '+821011112222', '팀장'); b = await AppDevice.create(hub, '010-2222-3333', '+821022223333', '김민수', instant: bAlerts); c = await AppDevice.create(hub, '010-3333-4444', '+821033334444', '이영희'); await befriend(a, b); await befriend(a, c); }); Future team(List members) async { final hb = (await a.app.groups.add('한빛전자', CardKinds.company, parent: CardKinds.company))!; final t = (await a.app.groups.add('결제팀', CardKinds.company, parent: hb.id))!; final ids = [for (final m in members) (await a.app.deps.storage.people.byPhone(m.phone))!.id]; await a.app.groups.addMembers(t.id, ids); return t.id; } test('팀 대화방: 회사 명함을 공유하지 않은 사람이 있으면 알려 주고, 모두 공유하면 방이 생긴다', () async { final t = await team([b, c]); await shareCompany(a, b); final r1 = await a.app.work.teamRoom(t); expect(r1.room, isNull); expect(r1.missing.map((p) => p.name), ['이영희']); await shareCompany(a, c); final r2 = await a.app.work.teamRoom(t); expect(r2.room!.title, '회사 › 한빛전자 › 결제팀'); expect(r2.room!.contextKind, CardKinds.company); expect((await a.app.work.teamRoom(t)).room!.id, r2.room!.id, reason: '두 번째부터는 같은 방'); expect(await a.app.work.groupOfRoom(r2.room!.id), t); }); test('업무를 맡기면 내 목록에 들어가고, 받는 쪽 목록에도 "내 업무"로 들어간다', () async { final t = await team([b, c]); await shareCompany(a, b); await shareCompany(a, c); final room = (await a.app.work.teamRoom(t)).room!; await AppDevice.settleAll(); final bId = (await a.app.deps.storage.people.byPhone(b.phone))!.id; final task = (await a.app.work.assign(room.id, '결제 API 정리', assigneeId: bId, due: DateTime(2026, 10, 30)))!; expect(task.assigneeName, '김민수'); expect((await a.app.work.tasks(roomId: room.id)).single.title, '결제 API 정리'); await AppDevice.settleAll(); final mine = await b.app.work.tasks(onlyMine: true); expect(mine.single.title, '결제 API 정리'); expect(mine.single.due, DateTime(2026, 10, 30)); final cTasks = await c.app.work.tasks(); expect(cTasks.single.assigneeName, '김민수'); expect(cTasks.single.mine, isFalse); await b.app.work.setDone(mine.single.id, true); expect(await b.app.work.tasks(onlyMine: true), isEmpty); expect((await b.app.work.tasks(includeDone: true)).single.done, isTrue); }); test('긴급 호출은 받는 쪽에서 바로 알림이 뜨고, 보통 대화는 뜨지 않는다', () async { await shareCompany(a, b); final bOfA = (await a.app.deps.storage.people.byPhone(b.phone))!; final room = (await a.app.chats.openDirect(bOfA.id, CardKinds.company))!; await a.app.chats.send(room.id, '보통 대화'); await a.app.work.urgent(room.id, '서버가 멈췄어요'); await AppDevice.settleAll(); expect(bAlerts.shown, ['🚨 긴급 호출 · 팀장|서버가 멈췄어요']); expect(await a.app.work.tasks(), isEmpty); }); test('지난 대화를 업무로 정리하기 (같은 대화는 한 번만)', () async { final bOfA = (await a.app.deps.storage.people.byPhone(b.phone))!; final room = (await a.app.chats.openDirect(bOfA.id, CardKinds.personal))!; final m = (await a.app.chats.send(room.id, '금요일까지 견적서 보내 주세요'))!; final t = (await a.app.work.fromMessage(m.id, assigneeId: Task.me))!; expect(t.title, '금요일까지 견적서 보내 주세요'); expect(t.mine, isTrue); expect(await a.app.work.fromMessage(m.id), isNull); expect(WorkService.noteOf('🚨 긴급 호출: x'), isA()); }); test('닫힌 잠긴 그룹은 팀 대화방을 만들 수 없다', () async { final t = await team([b]); await a.app.groups.setPin(CardKinds.company, '1234'); a.app.groups.lockAll(); expect((await a.app.work.teamRoom(t)).closed, isTrue); }); }