mirror of
https://github.com/kuhyx/utils.git
synced 2026-11-10 19:07:17 +01:00
`crdt_sync` is pure Dart so it can run from CLI tools and headless systemd jobs, which means it takes closures instead of depending on flutter_secure_storage and google_sign_in. Every Flutter app then wrote the same ~390 lines to supply them, and five apps ended up carrying near-identical copies of firebase_backend.dart. Those copies drifted. `todo` gained an opt-out flag and a seeded-session path that home_inventory and wake_alarm never received, so the same bug was fixed in one copy and left live in the others -- which is the actual cost being paid here, not the line count. This package owns that glue once: the keystore adapter, the persisted per-install device id, the account store and a one-call bootstrap. A new app needs a SyncApp const and `openSync(kApp)`. The wrapper routes are a parameter rather than a constant, because todo's copy hardcoded its own `~/.config/todo/firebase_auth.json`. An extraction only one app could adopt would be worse than the copies. Also documents what made this hard to find: crdt_sync_dart's README covered only the *old* GitHub transport and never mentioned the Firebase surface at all, and sync_settings_ui -- a complete drop-in settings screen -- had no README whatsoever. The five existing apps stay on their copies and on crdt_sync_dart-v0.10.0. Migration is forward-only and those fallbacks are load-bearing; converging them is a separate task, and the README says so. 35 tests, 100% line coverage, no suppressions. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CFSGeWxHPY5rHUGrPqhcmG
59 lines
1.9 KiB
Dart
59 lines
1.9 KiB
Dart
/// An in-memory stand-in for the `flutter_secure_storage` platform channel.
|
|
///
|
|
/// Shipped from the package rather than copied into each app's `test/`: the
|
|
/// glue this package owns is exactly the glue that cannot be tested without
|
|
/// it, and a consumer adopting the package should not have to reinvent the
|
|
/// fake to test its own sync wiring.
|
|
library;
|
|
|
|
import 'package:flutter/services.dart';
|
|
import 'package:flutter_test/flutter_test.dart';
|
|
|
|
/// Installs the fake for the duration of the current test.
|
|
///
|
|
/// Auto-removes on tear down. Pass [initial] to pre-seed stored values, and
|
|
/// [throwing] to simulate a host with no secret service -- every call then
|
|
/// raises a [PlatformException], which is what a Linux box with no libsecret
|
|
/// actually does.
|
|
void installFakeSecureStorage({
|
|
Map<String, String>? initial,
|
|
bool throwing = false,
|
|
}) {
|
|
const channel = MethodChannel('plugins.it_nomads.com/flutter_secure_storage');
|
|
final store = <String, String>{...?initial};
|
|
final messenger =
|
|
TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger;
|
|
|
|
void setHandler(Future<Object?>? Function(MethodCall)? handler) =>
|
|
messenger.setMockMethodCallHandler(channel, handler);
|
|
|
|
setHandler((call) async {
|
|
if (throwing) {
|
|
throw PlatformException(code: 'unavailable');
|
|
}
|
|
final args = (call.arguments as Map?) ?? const <Object?, Object?>{};
|
|
final key = args['key'] as String?;
|
|
switch (call.method) {
|
|
case 'read':
|
|
return store[key];
|
|
case 'write':
|
|
store[key!] = args['value']! as String;
|
|
return null;
|
|
case 'delete':
|
|
store.remove(key);
|
|
return null;
|
|
case 'containsKey':
|
|
return store.containsKey(key);
|
|
case 'readAll':
|
|
return Map<String, String>.from(store);
|
|
case 'deleteAll':
|
|
store.clear();
|
|
return null;
|
|
default:
|
|
return null;
|
|
}
|
|
});
|
|
|
|
addTearDown(() => setHandler(null));
|
|
}
|