83 lines
3.2 KiB
Dart
83 lines
3.2 KiB
Dart
import 'package:flutter_test/flutter_test.dart';
|
|
import 'package:jj_mkvmaker/app/master_lock.dart';
|
|
import 'package:jj_mkvmaker/services/secret_store.dart';
|
|
|
|
/// 읽기가 될 때와 안 될 때를 바꿀 수 있는 안전 저장소 (기기 저장소가 잠깐 잠긴 때처럼)
|
|
class _FlakyStore implements SecretStore {
|
|
final Map<String, String> values = {};
|
|
bool readable = true;
|
|
@override
|
|
String? lostCopy;
|
|
@override
|
|
Future<Map<String, String>> readAll() async {
|
|
if (!readable) throw StateError('안전 저장소를 읽을 수 없음');
|
|
return Map.of(values);
|
|
}
|
|
|
|
@override
|
|
Future<void> write(String key, String value) async => values[key] = value;
|
|
@override
|
|
Future<void> delete(String key) async => values.remove(key);
|
|
}
|
|
|
|
/// M0 약속: 안전 저장소를 읽지 못하면 마스터 잠금은 '알 수 없음' - 문을 열지 않고 (한 번 다시 읽어 봄), 마스터 · 최상 · 묻는 때를 바꾸지 않는다.
|
|
/// 다시 읽히면 평소대로 (마스터가 있으면 묻는다)
|
|
void main() {
|
|
late _FlakyStore store;
|
|
late int asked;
|
|
setUp(() async {
|
|
store = _FlakyStore();
|
|
// 마스터가 정해진 기기
|
|
final setup = MasterLock(store);
|
|
await setup.load();
|
|
await setup.setMaster('right-pass');
|
|
asked = 0;
|
|
MasterLock.prompt = (_) async {
|
|
asked++;
|
|
return true;
|
|
};
|
|
});
|
|
tearDown(() => MasterLock.prompt = null);
|
|
|
|
test('읽지 못하면 문을 열지 않는다 (예전: 마스터가 없는 것으로 보여 열렸다)', () async {
|
|
store.readable = false;
|
|
final lock = MasterLock(store);
|
|
await lock.load(); // 예외를 내지 않는다 - 앱은 켜진다
|
|
expect([lock.storeUnreadable, lock.hasMaster], [true, false]);
|
|
expect(await lock.ensure(), isFalse);
|
|
expect(await lock.ensure(force: true), isFalse, reason: '사용자가 직접 눌러도 열지 않는다');
|
|
expect(asked, 0, reason: '알 수 없는 마스터를 묻지도 않는다');
|
|
});
|
|
|
|
test('알 수 없는 동안 마스터 · 최상 · 묻는 때를 바꾸지 않는다 (진짜 마스터를 덮어쓰지 않게)', () async {
|
|
final before = Map.of(store.values);
|
|
store.readable = false;
|
|
final lock = MasterLock(store);
|
|
await lock.load();
|
|
await expectLater(lock.setMaster('new-pass'), throwsA(isA<MasterStoreUnreadable>()));
|
|
await expectLater(lock.setSuper('s'), throwsA(isA<MasterStoreUnreadable>()));
|
|
await expectLater(lock.clearMaster(), throwsA(isA<MasterStoreUnreadable>()));
|
|
await expectLater(lock.setAsk('never'), throwsA(isA<MasterStoreUnreadable>()));
|
|
expect(store.values, before);
|
|
});
|
|
|
|
test('다시 읽히면 평소대로: 문을 열 때 한 번 다시 읽고, 마스터가 있으면 묻는다', () async {
|
|
store.readable = false;
|
|
final lock = MasterLock(store);
|
|
await lock.load();
|
|
store.readable = true;
|
|
expect(await lock.ensure(), isTrue);
|
|
expect([lock.storeUnreadable, lock.hasMaster, asked], [false, true, 1]);
|
|
});
|
|
|
|
test('reload: 읽혔는지 돌려준다', () async {
|
|
store.readable = false;
|
|
final lock = MasterLock(store);
|
|
await lock.load();
|
|
expect(await lock.reload(), isFalse);
|
|
store.readable = true;
|
|
expect(await lock.reload(), isTrue);
|
|
expect(lock.hasMaster, isTrue);
|
|
});
|
|
}
|