mirror of
https://github.com/kuhyx/utils.git
synced 2026-11-10 20:07:18 +01:00
Groundwork for moving diet-guard, screen-locker and wake-alarm off fixed
role ids ('pc'/'phone'/'desktop') onto per-install uuids, the scheme todo
and home_inventory already use. A role constant collides the moment a
second machine takes the same role, and a reinstall silently inherits the
previous install's CRDT identity.
The migration cannot rewrite history: the device id is baked into every HLC
stamp already written, is the record path segment and the revision key. So
a device that has just switched ids must still recognise its former id as
its own -- otherwise skip-own-writes stops matching the old path, and the
device pulls back and re-merges its own pre-migration log as though a peer
had written it. Idempotent under LWW, but pure wasted transfer on every
tick.
Both halves therefore take an optional legacy id and test set membership
(own_ids / ownIds) rather than equality against a single id:
- crdt-sync (Python) 0.5.1 -> 0.6.0: new crdt_sync._device with
DeviceIdentity + load_device_identity (generate-or-load, persisted); the
mkdir is deliberately unguarded, since handing back an unpersisted id
would mint a fresh uuid every restart and strand a device dir each time.
sync_log gains legacy_device_id.
- crdt_sync_dart 0.6.0 -> 0.7.0: DeviceIdentity value type; syncLog gains
legacyDeviceId. Persistence stays app-side, where the existing uuid apps
already keep it in SharedPreferences under 'crdt.nodeId'.
Both changes are backward compatible: the parameter defaults to null, which
reproduces the previous single-id behaviour exactly.
Each half has a positive test and a negative control proving the old path
*is* pulled when no legacy id is declared -- so the skip is verified to be
the parameter's doing, not an artifact of the fixture.
Python: 239 tests, 100% branch coverage. Dart: 248 tests, analyzer clean,
device.dart 11/11 and sync.dart 57/57 lines covered.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GZH89WEqAjuJT8WZdqc7AP
47 lines
1.5 KiB
Dart
47 lines
1.5 KiB
Dart
import 'package:crdt_sync/crdt_sync.dart';
|
|
import 'package:test/test.dart';
|
|
|
|
void main() {
|
|
group('DeviceIdentity', () {
|
|
test('ownIds includes both ids while migrating', () {
|
|
const identity = DeviceIdentity(deviceId: 'new-uuid', legacyId: 'phone');
|
|
|
|
expect(identity.ownIds, {'new-uuid', 'phone'});
|
|
});
|
|
|
|
test('ownIds is just the uuid once the old path is reclaimed', () {
|
|
const identity = DeviceIdentity(deviceId: 'new-uuid');
|
|
|
|
expect(identity.ownIds, {'new-uuid'});
|
|
});
|
|
|
|
test('isOwn matches either id, and nothing else', () {
|
|
const identity = DeviceIdentity(deviceId: 'new-uuid', legacyId: 'phone');
|
|
|
|
expect(identity.isOwn('new-uuid'), isTrue);
|
|
expect(identity.isOwn('phone'), isTrue);
|
|
expect(identity.isOwn('pc'), isFalse);
|
|
});
|
|
|
|
test('value equality covers both fields', () {
|
|
const a = DeviceIdentity(deviceId: 'u', legacyId: 'phone');
|
|
const b = DeviceIdentity(deviceId: 'u', legacyId: 'phone');
|
|
const differentLegacy = DeviceIdentity(deviceId: 'u', legacyId: 'pc');
|
|
const noLegacy = DeviceIdentity(deviceId: 'u');
|
|
|
|
expect(a, equals(b));
|
|
expect(a.hashCode, equals(b.hashCode));
|
|
expect(a, isNot(equals(differentLegacy)));
|
|
expect(a, isNot(equals(noLegacy)));
|
|
expect(a, isNot(equals(Object())));
|
|
});
|
|
|
|
test('toString names both ids', () {
|
|
const identity = DeviceIdentity(deviceId: 'u', legacyId: 'phone');
|
|
|
|
expect(identity.toString(), contains('u'));
|
|
expect(identity.toString(), contains('phone'));
|
|
});
|
|
});
|
|
}
|