238 lines
12 KiB
Dart
238 lines
12 KiB
Dart
import 'package:flutter_test/flutter_test.dart';
|
|
import 'package:friendly_map/modules/platform/platform.dart';
|
|
import 'package:friendly_map/modules/storage/storage.dart';
|
|
|
|
class FakeProbe implements DeviceStorageProbe {
|
|
FakeProbe(this.percent);
|
|
int percent;
|
|
@override
|
|
Future<int> usedPercent() async => percent;
|
|
}
|
|
|
|
MyCard card(String id, String kind, {int at = 1}) =>
|
|
MyCard(id: id, kind: kind, title: '$kind 명함', fieldsJson: '{}', updatedAt: at);
|
|
|
|
Message msg(String id, int at, {String body = 'hi', String room = 'r1'}) =>
|
|
Message(id: id, roomId: room, senderId: 's', body: body, sentAt: at, updatedAt: at);
|
|
|
|
/// 저장 방식(메모리, SQLite)이 같은 약속을 지키는지 확인하는 공통 시험.
|
|
/// 새 저장 방식을 만들면 이 시험에 넣어 보면 된다.
|
|
void storageContract(String label, Future<Storage> Function() make) {
|
|
group('$label 저장소', () {
|
|
late Storage s;
|
|
setUp(() async => s = await make());
|
|
|
|
test('내 명함: 저장, 읽기, 종류별 찾기, 같은 id 는 덮어씀', () async {
|
|
await s.myCards.put(card('a', 'company'));
|
|
await s.myCards.put(card('b', 'school'));
|
|
await s.myCards.put(card('a', 'company', at: 5));
|
|
expect((await s.myCards.get('a'))!.updatedAt, 5);
|
|
expect((await s.myCards.all()).length, 2);
|
|
expect((await s.myCards.byKind('school')).single.id, 'b');
|
|
expect(await s.myCards.updatedAtOf('a'), 5);
|
|
expect(await s.myCards.updatedAtOf('none'), null);
|
|
expect(await s.myCards.get('none'), null);
|
|
});
|
|
|
|
test('지우기: 명함·받은 명함·연결 고리, 없는 것을 지워도 문제 없음', () async {
|
|
await s.myCards.put(card('a', 'company'));
|
|
await s.myCards.remove('a');
|
|
await s.myCards.remove('none');
|
|
expect(await s.myCards.get('a'), null);
|
|
await s.links.put(const Link(id: 'l', personId: 'p', relation: '동창', detail: '', updatedAt: 1));
|
|
await s.links.remove('l');
|
|
expect(await s.links.byPerson('p'), isEmpty);
|
|
await s.receivedCards.put(ReceivedCard(
|
|
id: 'r', personId: 'p', kind: 'school', fieldsJson: '{}', receivedAt: 1, updatedAt: 1));
|
|
await s.receivedCards.remove('r');
|
|
expect(await s.receivedCards.byPerson('p'), isEmpty);
|
|
});
|
|
|
|
test('받은 명함은 사람별로 최근 것부터', () async {
|
|
ReceivedCard rc(String id, String p, int at) => ReceivedCard(
|
|
id: id, personId: p, kind: 'school', fieldsJson: '{}', receivedAt: at, updatedAt: at);
|
|
await s.receivedCards.put(rc('1', 'p', 10));
|
|
await s.receivedCards.put(rc('2', 'p', 30));
|
|
await s.receivedCards.put(rc('3', 'q', 20));
|
|
expect((await s.receivedCards.byPerson('p')).map((c) => c.id).toList(), ['2', '1']);
|
|
});
|
|
|
|
test('사람: 번호가 같으면 같은 사람, 이름만 다르면 이름 변경 후보', () async {
|
|
final p = await s.people.register('+821011112222', '정민호', 100);
|
|
expect(p, isNotNull);
|
|
expect((await s.people.match('+821011112222', '정민호')).kind, MatchKind.same);
|
|
final m = await s.people.match('+821011112222', '정민우');
|
|
expect(m.kind, MatchKind.nameDiffers);
|
|
expect(m.candidates.single.id, p!.id);
|
|
});
|
|
|
|
test('사람: 이름이 같고 번호가 처음이면 번호 변경 후보, 둘 다 처음이면 새 사람', () async {
|
|
await s.people.register('+821011112222', '정민호', 100);
|
|
final m = await s.people.match('+821033334444', '정민호');
|
|
expect(m.kind, MatchKind.phoneDiffers);
|
|
expect(m.candidates, hasLength(1));
|
|
expect((await s.people.match('+821055556666', '김하늘')).kind, MatchKind.none);
|
|
});
|
|
|
|
test('사람: 같은 번호 중복 등록은 막힘', () async {
|
|
await s.people.register('+821011112222', '정민호', 100);
|
|
expect(await s.people.register('+821011112222', '다른이름', 101), null);
|
|
});
|
|
|
|
test('사람: 번호를 바꿔도 id 는 그대로, 이전 번호로도 찾아짐', () async {
|
|
final p = (await s.people.register('+821011112222', '정민호', 100))!;
|
|
expect(await s.people.changePhone(p.id, '+821099998888', 200), true);
|
|
final now = (await s.people.get(p.id))!;
|
|
expect(now.phone, '+821099998888');
|
|
expect((await s.people.byPhone('+821011112222'))!.id, p.id);
|
|
expect((await s.people.byPhone('+821099998888'))!.id, p.id);
|
|
expect((await s.people.match('+821011112222', '정민호')).kind, MatchKind.same);
|
|
});
|
|
|
|
test('사람: 새 번호가 다른 사람 것이면 번호 변경 거절', () async {
|
|
final a = (await s.people.register('+821011112222', '가', 100))!;
|
|
await s.people.register('+821033334444', '나', 100);
|
|
expect(await s.people.changePhone(a.id, '+821033334444', 200), false);
|
|
expect((await s.people.get(a.id))!.phone, '+821011112222');
|
|
});
|
|
|
|
test('사람: 이름을 바꿔도 이전 이름으로 찾아지고 같은 사람으로 봄', () async {
|
|
final p = (await s.people.register('+821011112222', '정민호', 100))!;
|
|
expect(await s.people.changeName(p.id, '정민우', 200), true);
|
|
expect((await s.people.get(p.id))!.name, '정민우');
|
|
expect((await s.people.byName('정민호')).single.id, p.id);
|
|
expect((await s.people.byName('정민우')).single.id, p.id);
|
|
expect((await s.people.match('+821011112222', '정민호')).kind, MatchKind.same);
|
|
});
|
|
|
|
test('사람: 공개 키를 붙인다', () async {
|
|
final p = (await s.people.register('+821011112222', '정민호', 100))!;
|
|
await s.people.setPublicKey(p.id, 'KEY', 300);
|
|
expect((await s.people.get(p.id))!.publicKey, 'KEY');
|
|
});
|
|
|
|
test('대화: 시간순, before, limit, 별도 저장 제외', () async {
|
|
for (var i = 1; i <= 5; i++) {
|
|
await s.messages.put(msg('m$i', i * 10));
|
|
}
|
|
await s.messages.put(msg('x', 5, room: 'r2'));
|
|
expect((await s.messages.page('r1')).map((m) => m.id).toList(), ['m1', 'm2', 'm3', 'm4', 'm5']);
|
|
expect((await s.messages.page('r1', limit: 2)).map((m) => m.id).toList(), ['m4', 'm5']);
|
|
expect((await s.messages.page('r1', before: 30, limit: 10)).map((m) => m.id).toList(), ['m1', 'm2']);
|
|
});
|
|
|
|
test('대화: 텍스트 크기는 UTF-8 바이트, 범위는 처음~끝', () async {
|
|
await s.messages.put(msg('a', 10, body: 'abc'));
|
|
await s.messages.put(msg('b', 20, body: '가나'));
|
|
expect(await s.messages.textBytes('r1'), 3 + 6);
|
|
expect(await s.messages.span('r1'), (10, 20));
|
|
expect(await s.messages.span('빈방'), null);
|
|
expect(await s.messages.textBytes('빈방'), 0);
|
|
});
|
|
|
|
test('대화: 범위를 별도 저장으로 나누고 되돌림 (지우지 않음)', () async {
|
|
for (var i = 1; i <= 4; i++) {
|
|
await s.messages.put(msg('m$i', i * 10, body: 'xx'));
|
|
}
|
|
expect(await s.messages.setArchivedBetween('r1', 10, 20, 99), 2);
|
|
expect((await s.messages.page('r1')).map((m) => m.id).toList(), ['m3', 'm4']);
|
|
expect(await s.messages.textBytes('r1'), 4);
|
|
expect(await s.messages.span('r1'), (30, 40));
|
|
expect((await s.messages.get('m1'))!.archived, true);
|
|
expect(await s.messages.setArchivedBetween('r1', 10, 20, 100, archived: false), 2);
|
|
expect((await s.messages.page('r1')).length, 4);
|
|
});
|
|
|
|
test('대화방: 최근 순서, 별도 저장된 방은 기본에서 빠짐', () async {
|
|
ChatRoom room(String id, int at, {bool archived = false}) => ChatRoom(
|
|
id: id, title: id, kind: 'direct', contextKind: 'personal',
|
|
membersJson: '[]', archived: archived, updatedAt: at);
|
|
await s.rooms.put(room('a', 10));
|
|
await s.rooms.put(room('b', 30));
|
|
await s.rooms.put(room('c', 20, archived: true));
|
|
expect((await s.rooms.recent()).map((r) => r.id).toList(), ['b', 'a']);
|
|
expect((await s.rooms.recent(includeArchived: true)).map((r) => r.id).toList(), ['b', 'c', 'a']);
|
|
});
|
|
|
|
test('공유: 지금 공유 중인 것만 찾고, 끝내면 빠짐', () async {
|
|
ShareRecord sr(String id, String unit, String target) => ShareRecord(
|
|
id: id, unit: unit, targetId: target, peerId: 'p', startedAt: 1, updatedAt: 1);
|
|
await s.shares.put(sr('1', 'card', 'c1'));
|
|
await s.shares.put(sr('2', 'room', 'r1'));
|
|
expect((await s.shares.activeFor('c1')).single.id, '1');
|
|
expect((await s.shares.activeOf('room')).single.id, '2');
|
|
await s.shares.end('1', 50);
|
|
expect(await s.shares.activeFor('c1'), isEmpty);
|
|
final ended = (await s.shares.get('1'))!;
|
|
expect(ended.endedAt, 50);
|
|
expect(ended.active, false);
|
|
});
|
|
|
|
test('보낼함: 오래된 것부터, 갱신, 삭제', () async {
|
|
OutboxItem o(String id, int at) =>
|
|
OutboxItem(id: id, peerPhone: '+82', envelopeJson: '{}', createdAt: at);
|
|
await s.outbox.add(o('b', 20));
|
|
await s.outbox.add(o('a', 10));
|
|
expect((await s.outbox.pending()).map((x) => x.id).toList(), ['a', 'b']);
|
|
await s.outbox.update(o('a', 10).tried());
|
|
expect((await s.outbox.pending()).first.attempts, 1);
|
|
await s.outbox.remove('a');
|
|
expect((await s.outbox.pending()).single.id, 'b');
|
|
});
|
|
|
|
test('친구 요청: 대기 중인 것만, 상태 바꾸면 빠짐', () async {
|
|
final r = FriendRequest(
|
|
id: '+821011112222', name: '정민호', publicKey: 'K', receivedAt: 5, updatedAt: 5);
|
|
await s.friendRequests.put(r);
|
|
expect((await s.friendRequests.pending()).single.phone, '+821011112222');
|
|
await s.friendRequests.put(r.withStatus('accepted', 9));
|
|
expect(await s.friendRequests.pending(), isEmpty);
|
|
});
|
|
|
|
test('연결 고리: 사람별', () async {
|
|
await s.links.put(const Link(id: '1', personId: 'p', relation: '동창', detail: '가람초', updatedAt: 1));
|
|
await s.links.put(const Link(id: '2', personId: 'q', relation: '동문', detail: '한국대', updatedAt: 1));
|
|
expect((await s.links.byPerson('p')).single.relation, '동창');
|
|
});
|
|
|
|
test('설정: 기본값, 저장 후 읽기, 내 정보', () async {
|
|
expect((await s.settings.load()).separateTextBytes, 1024 * 1024);
|
|
expect(await s.settings.loadProfile(), null);
|
|
await s.settings.save(const AppSettings(storageUsagePercent: 90, exchangeMode: 'auto'), 1);
|
|
final back = await s.settings.load();
|
|
expect(back.storageUsagePercent, 90);
|
|
expect(back.exchangeMode, 'auto');
|
|
await s.settings.saveProfile(const MyProfile(phone: '+82101', name: '나'), 2);
|
|
expect((await s.settings.loadProfile())!.name, '나');
|
|
await s.settings.putText('home_layout', '{"a":1}', 3);
|
|
expect(await s.settings.getText('home_layout'), '{"a":1}');
|
|
});
|
|
|
|
test('이름-값: 봉투 처리 기록과 충돌 기록', () async {
|
|
expect(await s.seen.get('e1'), null);
|
|
await s.seen.put('e1', '1', 1);
|
|
expect(await s.seen.get('e1'), '1');
|
|
await s.meta.put('row1', 'author|5', 1);
|
|
expect(await s.meta.get('row1'), 'author|5');
|
|
});
|
|
|
|
test('별도 저장 권고와 나누기', () async {
|
|
for (var i = 1; i <= 3; i++) {
|
|
await s.messages.put(msg('m$i', i * 10, body: 'x' * 400000));
|
|
}
|
|
final probe = FakeProbe(10);
|
|
final svc = ArchiveService(
|
|
messages: s.messages, settings: s.settings, clock: Clock(), probe: probe);
|
|
final advice = await svc.adviseRoom('r1');
|
|
expect(advice.overText, true);
|
|
expect(advice.overDisk, false);
|
|
expect(await svc.selectableSpan('r1'), (10, 30));
|
|
expect(await svc.archiveRange('r1', 10, 20), 2);
|
|
expect((await svc.adviseRoom('r1')).overText, false);
|
|
probe.percent = 85;
|
|
expect((await svc.adviseRoom('r1')).overDisk, true);
|
|
expect(await svc.restoreRange('r1', 10, 20), 2);
|
|
});
|
|
});
|
|
}
|