// device_identity_test.dart — 稳定 device_id 的生成/持久/读失败语义 import 'package:flutter_test/flutter_test.dart'; import 'package:pangolin_vpn/services/device_identity.dart'; /// 内存版 SecureKV:可模拟读失败,记录写入。 class _FakeKV implements SecureKV { _FakeKV({this.failRead = false}); final Map map = {}; bool failRead; int writes = 0; @override Future read(String key) async { if (failRead) throw Exception('platform read failed'); return map[key]; } @override Future write(String key, String value) async { writes++; map[key] = value; } } void main() { test('首次:生成并持久化;再读复用同一个', () async { final kv = _FakeKV(); final d1 = DeviceIdentity(store: kv); final id1 = await d1.deviceId(); expect(id1, isNotEmpty); expect(kv.writes, 1, reason: '首次应写一次'); expect(kv.map['pangolin_device_id'], id1); // 同实例缓存:不再写。 expect(await d1.deviceId(), id1); expect(kv.writes, 1); // 新实例、同存储:读出同一个,不重新生成。 final d2 = DeviceIdentity(store: kv); expect(await d2.deviceId(), id1); expect(kv.writes, 1, reason: '已存在不应再写'); }); test('读失败 ≠ 不存在:退回临时 id,不写库(不冲掉真实 id)', () async { final kv = _FakeKV(failRead: true)..map['pangolin_device_id'] = 'real-id'; final d = DeviceIdentity(store: kv); final id = await d.deviceId(); expect(id, isNotEmpty); expect(id, isNot('real-id'), reason: '读失败拿不到真实值,用临时'); expect(kv.writes, 0, reason: '读失败绝不写,避免覆盖真实 id'); // 真实 id 仍在存储里,未被破坏。 expect(kv.map['pangolin_device_id'], 'real-id'); }); test('生成的是 UUID v4 形态', () async { final id = await DeviceIdentity(store: _FakeKV()).deviceId(); expect(RegExp(r'^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$').hasMatch(id), isTrue); }); }