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/storage/storage.dart'; import 'package:friendly_map/modules/transport/transport.dart'; import 'support/app_device.dart'; 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(); } void main() { late AppDevice a, b; late Person aOfB; late String cardId; /// a 가 회사 명함을 [company] 로 고쳐 다시 보낸다. Future moveTo(String company, {bool former = false}) async { await a.app.cards.edit(cardId, fields: {'name': '김민수', 'company': company, if (former) 'former': 'true'}); for (final s in await a.app.cards.needingResend()) { await a.app.cards.resend(s.id); } await AppDevice.settleAll(); } setUp(() async { final hub = MemoryHub(); a = await AppDevice.create(hub, '010-1111-2222', '+821011112222', '김민수'); b = await AppDevice.create(hub, '010-2222-3333', '+821022223333', '나'); await befriend(a, b); final bOfA = (await a.app.friends.people()).single; cardId = (await a.app.cards.mine()).firstWhere((c) => c.kind == CardKinds.company).id; await a.app.cards.edit(cardId, fields: {'name': '김민수', 'company': '한빛전자'}); await a.app.cards.shareWith(bOfA.id, [cardId]); await AppDevice.settleAll(); aOfB = (await b.app.friends.people()).single; }); test('처음 받은 회사 명함은 알림이 없고, 회사가 바뀌면 이직 알림이 생긴다', () async { expect(await b.app.career.notices(), isEmpty); await moveTo('한빛전자'); expect(await b.app.career.notices(), isEmpty, reason: '회사가 그대로면 알림 없음'); await moveTo('누리소프트'); final n = (await b.app.career.notices()).single; expect((n.from, n.to), ('한빛전자', '누리소프트')); expect(await b.app.career.describe(n), '김민수님이 한빛전자에서 누리소프트로 옮겼어요'); expect(await b.app.career.notices(personId: aOfB.id), hasLength(1)); }); test('확인하면 이전 회사에서는 흐리게 남고 새 회사 아래로 옮겨지며, 연결 고리가 바뀐다', () async { final hb = (await b.app.groups.add('한빛전자', CardKinds.company, parent: CardKinds.company))!; final team = (await b.app.groups.add('결제팀', CardKinds.company, parent: hb.id))!; await b.app.relate.into(aOfB.id, team.id); await moveTo('누리소프트'); final n = (await b.app.career.notices()).single; expect(await b.app.career.accept(n.id), '회사 › 누리소프트'); expect(await b.app.groups.members(team.id), isEmpty); expect((await b.app.groups.formers(team.id))!.single.name, '김민수'); final links = {for (final l in await b.app.network.links(aOfB.id)) l.relation: l.detail}; expect(links, {'직장 동료': '회사 › 누리소프트', '전 직장 동료': '회사 › 한빛전자 › 결제팀'}); expect(await b.app.career.notices(), isEmpty); expect((await b.app.groups.view(team.id))!.memberCount, 0, reason: '이전 구성원은 수에 들지 않는다'); }); test('이전 회사에서 지우기를 고르면 흐리게 남기지 않는다, 이미 있는 새 회사 그룹을 쓴다', () async { final hb = (await b.app.groups.add('한빛전자', CardKinds.company, parent: CardKinds.company))!; final nuri = (await b.app.groups.add('누리소프트', CardKinds.company, parent: CardKinds.company))!; await b.app.relate.into(aOfB.id, hb.id); await moveTo('누리소프트'); final n = (await b.app.career.notices()).single; await b.app.career.accept(n.id, keepOld: false); expect(await b.app.groups.formers(hb.id), isEmpty); expect((await b.app.groups.members(nuri.id))!.single.id, aOfB.id); expect((await b.app.network.links(aOfB.id)).map((l) => l.relation), ['직장 동료']); }); test('퇴사(지금은 다니지 않음)는 떠남 알림, 새 회사로 넣지 않는다. 무시하면 알림만 지운다', () async { final hb = (await b.app.groups.add('한빛전자', CardKinds.company, parent: CardKinds.company))!; await b.app.relate.into(aOfB.id, hb.id); await moveTo('한빛전자', former: true); final n = (await b.app.career.notices()).single; expect(n.left, isTrue); expect(await b.app.career.describe(n), '김민수님이 한빛전자를 떠났어요'); expect(await b.app.career.accept(n.id), isNull); expect((await b.app.groups.formers(hb.id))!.single.id, aOfB.id); await moveTo('새회사'); final n2 = (await b.app.career.notices()).single; await b.app.career.dismiss(n2.id); expect(await b.app.career.notices(), isEmpty); expect((await b.app.groups.formers(hb.id))!.length, 1, reason: '무시하면 그룹은 그대로'); }); test('앱을 새로 열면 이미 받은 회사 명함으로 알던 회사를 채운다 (알림 없음)', () async { final fresh = CareerService(b.app.deps.storage, b.app.deps.clock, b.app.groups, b.app.relate, b.app.network, b.app.bus); await fresh.seed(); expect(await fresh.notices(), isEmpty); expect(currentCompany({'company': ' 한빛 '}), '한빛'); expect(currentCompany({'company': '한빛', 'former': 'true'}), ''); expect(withJosa('한빛', '으로', '로'), '한빛으로'); expect(withJosa('서울', '으로', '로'), '서울로'); expect(withJosa('누리', '으로', '로'), '누리로'); expect(withJosa('한빛', '을', '를'), '한빛을'); expect(withJosa('ABC', '으로', '로'), 'ABC(으)로'); expect(withJosa('ABC', '을', '를'), 'ABC을(를)'); }); group('이직 이력', () { final year = DateTime.now().year; Future mate(String name, String phone, String company) async { final p = (await b.app.friends.add(phone, name))!; final id = 'mate-${p.id}'; await b.app.deps.storage.receivedCards.put(ReceivedCard( id: id, personId: p.id, kind: CardKinds.company, fieldsJson: encodeFields({'company': company}), receivedAt: 1, updatedAt: 1)); await b.app.career.onCard(id); return p; } test('처음엔 지금 회사만 "~ 현재", 이직하면 이전 회사가 올해로 닫히고 새 회사가 올해부터', () async { var h = await b.app.career.history(aOfB.id); expect(h.map((s) => '${s.stint.company} ${s.stint.period}'), ['한빛전자 ~ 현재']); await moveTo('누리소프트'); h = await b.app.career.history(aOfB.id); expect(h.map((s) => '${s.stint.company} ${s.stint.period}'), ['누리소프트 $year ~ 현재', '한빛전자 ~ $year']); }); test('기간을 직접 고치면 반영되고, 같은 회사에 겹쳐 있던 내 인맥이 나온다', () async { final old = await mate('옛동료', '+821066667777', '한빛전자'); await moveTo('누리소프트'); expect(await b.app.career.setYears(aOfB.id, 1, from: 2015, to: 2020), isTrue); expect(await b.app.career.setYears(aOfB.id, 9, from: 1), isFalse); var h = await b.app.career.history(aOfB.id); expect(h.last.stint.period, '2015 ~ 2020'); expect(h.last.colleagues.map((p) => p.name), ['옛동료'], reason: '옛동료는 기간을 몰라 겹친 것으로 본다'); await b.app.career.setYears(old.id, 0, from: 2022, current: true); h = await b.app.career.history(aOfB.id); expect(h.last.colleagues, isEmpty, reason: '2022년부터 다닌 사람은 2015~2020 과 겹치지 않는다'); expect(h.first.colleagues, isEmpty); }); test('주변 사람: 같은 그룹, 같은 회사에 같이 있었던 사람 (이유가 많은 순)', () async { final x = await mate('같은팀', '+821055550001', '한빛전자'); final y = await mate('그냥동료', '+821055550002', '한빛전자'); final hb = (await b.app.groups.add('한빛전자', CardKinds.company, parent: CardKinds.company))!; await b.app.groups.addMembers(hb.id, [aOfB.id, x.id]); final around = await b.app.career.around(aOfB.id); expect(around.map((e) => e.person.name), ['같은팀', '그냥동료']); expect(around.first.reasons, contains('같은 그룹 · 회사 › 한빛전자')); expect(around.first.reasons, contains('같은 회사 · 한빛전자')); expect(around.last.reasons, ['같은 회사 · 한빛전자']); expect(y.name, '그냥동료'); }); test('퇴사(지금은 다니지 않음)하면 기간이 올해로 닫히고 지금 다니는 곳이 없다', () async { await moveTo('한빛전자', former: true); final h = await b.app.career.history(aOfB.id); expect(h.single.stint.period, '~ $year'); }); }); }