frindlymap/test/groups_test.dart

197 lines
8.6 KiB
Dart
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

import 'package:flutter_test/flutter_test.dart';
import 'package:friendly_map/modules/core/core.dart';
import 'package:friendly_map/modules/groups/groups.dart';
void main() {
group('그룹 목록', () {
test('처음에는 맨 위에 가족·학교·회사·그룹·여행 네 개', () {
final b = GroupBook();
expect(b.roots.map((g) => g.name), ['가족', '학교', '회사', '그룹·여행']);
expect(b.roots.map((g) => g.kind), [CardKinds.family, CardKinds.school, CardKinds.company, CardKinds.group]);
});
test('처음 하위 그룹: 학교는 초등~대학원, 가족은 친가·외가·처가·시가·촌수 모름', () {
final b = GroupBook();
expect(b.children(CardKinds.school).map((g) => g.name), ['초등학교', '중학교', '고등학교', '대학교', '대학원']);
expect(b.children(CardKinds.family).map((g) => g.name), ['친가', '외가', '처가·시가', '친척(촌수 모름)']);
expect(b.byId(Group.unknownRelatives)!.parent, CardKinds.family);
expect(b.children(CardKinds.company), isEmpty);
expect(b.groups.indexOf(b.byId('school.high')!), greaterThan(b.groups.indexOf(b.byId(CardKinds.school)!)));
});
test('더하기·이름 바꾸기·지우기, 빈 이름과 겹치는 이름은 거절', () {
final b = GroupBook();
final g = b.add(' 동호회 ', CardKinds.group)!;
expect(g.name, '동호회');
expect(b.add('', CardKinds.group), isNull);
expect(b.add('가족', CardKinds.family), isNull);
expect(b.rename(g.id, '등산 모임'), isTrue);
expect(b.rename(g.id, '학교'), isFalse);
expect(b.rename(g.id, '등산 모임'), isTrue, reason: '자기 이름 그대로는 괜찮다');
expect(b.byId(g.id)!.name, '등산 모임');
expect(b.remove(g.id), {g.id});
expect(b.byId(g.id), isNull);
expect(b.remove('없음'), isEmpty);
});
test('이름은 20자까지 자른다', () {
final b = GroupBook();
expect(b.add('가' * 30, CardKinds.group)!.name.length, 20);
});
test('순서 바꾸기', () {
final b = GroupBook();
b.move(CardKinds.group, 0);
expect(b.groups.first.id, CardKinds.group);
b.move(CardKinds.group, 99);
expect(b.groups.last.id, CardKinds.group);
});
test('구성원 더하기·빼기, 사람이 든 그룹, 사람 지우기', () {
final b = GroupBook();
expect(b.addMembers(CardKinds.family, ['a', 'b']), 2);
expect(b.addMembers(CardKinds.family, ['b', 'c']), 1, reason: '이미 든 사람은 다시 세지 않는다');
b.addMembers(CardKinds.school, ['a']);
expect(b.groupsOf('a').map((g) => g.id), [CardKinds.family, CardKinds.school]);
expect(b.removeMember(CardKinds.family, 'c'), isTrue);
expect(b.removeMember(CardKinds.family, 'c'), isFalse);
b.forgetPerson('a');
expect(b.groupsOf('a'), isEmpty);
expect(b.byId(CardKinds.family)!.members, {'b'});
});
test('잠긴 그룹이 닫혀 있으면 구성원을 숨긴다 (다른 그룹에도 있어도)', () async {
final b = GroupBook();
b.addMembers(CardKinds.family, ['a', 'b']);
b.addMembers(CardKinds.school, ['a', 'c']);
b.setLock(CardKinds.family, await Pin.make('1234', iterations: 10));
expect(b.hiddenPeople((g) => true), {'a', 'b'});
expect(b.hiddenPeople((g) => false), isEmpty);
});
test('글자로 저장했다 되살리면 같고, 깨진 글자는 처음 그룹', () async {
final b = GroupBook(relockMinutes: 10);
final g = b.add('동호회', CardKinds.group)!;
b.addMembers(g.id, ['x']);
b.setLock(g.id, await Pin.make('abcd', iterations: 10));
final c = GroupBook.fromJson(b.toJson());
expect(c.groups.map((x) => x.name), b.groups.map((x) => x.name));
expect(c.byId(g.id)!.members, {'x'});
expect(c.byId(g.id)!.locked, isTrue);
expect(c.relockMinutes, 10);
expect(c.add('새 그룹', CardKinds.group)!.id, isNot(g.id), reason: '새 번호표는 겹치지 않는다');
expect(GroupBook.fromJson('{깨짐').roots.length, 4);
expect(GroupBook.fromJson(null).roots.length, 4);
});
});
group('비밀번호', () {
test('맞는 비밀번호만 통과, 원문은 남지 않는다', () async {
final h = await Pin.make('2580', iterations: 100);
expect(await Pin.verify('2580', h), isTrue);
expect(await Pin.verify('2581', h), isFalse);
expect(h.toJson().toString(), isNot(contains('2580')));
final again = await Pin.make('2580', iterations: 100);
expect(again.salt, isNot(h.salt), reason: '같은 비밀번호도 소금이 달라 다른 값');
});
test('짧거나 길거나 앞뒤 빈칸이면 안내', () {
expect(Pin.problem('123'), isNotNull);
expect(Pin.problem('1' * 17), isNotNull);
expect(Pin.problem(' 1234'), isNotNull);
expect(Pin.problem('1234'), isNull);
});
test('깨진 저장값은 통과하지 않는다', () async {
expect(await Pin.verify('1234', const PinHash('!!', '??', 10)), isFalse);
expect(PinHash.fromJson({'salt': 1}), isNull);
});
});
group('여러 번 틀리면 기다리기', () {
test('5번까지는 바로, 그 뒤로 30초·60초…', () {
final g = UnlockGuard();
for (var i = 0; i < 4; i++) {
g.failure(0);
expect(g.waitLeft(0), 0);
}
expect(g.triesLeft(), 1);
g.failure(0);
expect(g.waitLeft(0), 3000);
expect(g.waitLeft(3000), 0);
g.failure(3000);
expect(g.waitLeft(3000), 6000);
g.success();
expect(g.waitLeft(3000), 0);
expect(g.triesLeft(), 5);
});
test('기다림은 1시간을 넘지 않는다', () {
final g = UnlockGuard();
for (var i = 0; i < 40; i++) {
g.failure(0);
}
expect(g.waitLeft(0), 360000);
});
});
group('하위 그룹', () {
test('회사 > 한빛전자 > 결제팀, 같은 이름은 부모가 다르면 괜찮다', () {
final b = GroupBook();
final hb = b.add('한빛전자', CardKinds.company, parent: CardKinds.company)!;
final pay = b.add('결제팀', CardKinds.company, parent: hb.id)!;
final other = b.add('누리소프트', CardKinds.company, parent: CardKinds.company)!;
expect(b.add('결제팀', CardKinds.company, parent: other.id), isNotNull);
expect(b.add('결제팀', CardKinds.company, parent: hb.id), isNull);
expect(b.path(pay.id), '회사 › 한빛전자 › 결제팀');
expect(b.depth(pay.id), 3);
expect(b.ancestors(pay.id).map((g) => g.id), [hb.id, CardKinds.company]);
expect(b.add('없는 부모 아래', CardKinds.group, parent: '없음'), isNull);
});
test('4단까지만 만들 수 있다', () {
final b = GroupBook();
final a = b.add('2단', CardKinds.group, parent: CardKinds.group)!;
final c = b.add('3단', CardKinds.group, parent: a.id)!;
final d = b.add('4단', CardKinds.group, parent: c.id)!;
expect(b.add('5단', CardKinds.group, parent: d.id), isNull);
});
test('하위까지 합친 구성원, 지우면 하위 그룹도 함께', () {
final b = GroupBook();
b.addMembers(CardKinds.school, ['a']);
b.addMembers('school.high', ['b', 'a']);
b.addMembers('school.university', ['c']);
expect(b.membersDeep(CardKinds.school), {'a', 'b', 'c'});
expect(b.remove(CardKinds.school), containsAll([CardKinds.school, 'school.high', 'school.graduate']));
expect(b.byId('school.high'), isNull);
expect(b.roots.length, 3);
});
test('잠긴 부모가 닫히면 하위 그룹 사람도 숨긴다', () async {
final b = GroupBook();
b.addMembers('family.maternal', ['m']);
b.setLock(CardKinds.family, await Pin.make('1234', iterations: 10));
expect(b.hiddenPeople((g) => true), {'m'});
});
test('같은 부모 아래에서만 순서가 바뀐다', () {
final b = GroupBook();
b.move('school.graduate', 0);
expect(b.children(CardKinds.school).first.id, 'school.graduate');
expect(b.roots.first.id, CardKinds.family);
});
test('저장하면 나무 모양이 유지되고, 없는 부모·돌고 도는 부모는 맨 위로', () {
final b = GroupBook();
final hb = b.add('한빛전자', CardKinds.company, parent: CardKinds.company)!;
final c = GroupBook.fromJson(b.toJson());
expect(c.byId(hb.id)!.parent, CardKinds.company);
const bad = '{"groups":[{"id":"x","name":"X","parent":"y"},{"id":"y","name":"Y","parent":"x"},{"id":"z","name":"Z","parent":"없음"}]}';
final d = GroupBook.fromJson(bad);
expect(d.groups.map((g) => g.id).toSet(), {'x', 'y', 'z'});
expect(d.byId('z')!.parent, isNull);
});
});
}