// 功能描述:验证工作人员会话失效的幂等清理、旧请求隔离和存储异常降级。 // 版本:1.0.0 import 'package:flutter_test/flutter_test.dart'; import 'package:service_app/app/dependencies.dart'; import 'package:service_app/data/services/secure_session_store.dart'; /// 提供可观测的内存会话存储,避免测试依赖真实平台插件。 class _MemorySessionStore implements SessionStore { _MemorySessionStore({Map? values, this.deleteError}) : values = {...?values}; final Map values; final Object? deleteError; final Map deleteCounts = {}; @override Future delete(String key) async { deleteCounts[key] = (deleteCounts[key] ?? 0) + 1; if (deleteError != null) throw deleteError!; values.remove(key); } @override Future read(String key) async => values[key]; @override Future write(String key, String value) async { values[key] = value; } } /// 覆盖并发 401、迟到旧请求、设备标识保留和安全存储删除失败。 void main() { const tokenKey = 'service_app_access_token'; const identityKey = 'service_app_identity'; const roleKey = 'service_app_role'; const deviceKey = 'service_app_device'; test('同一失效令牌只清理并通知一次且保留设备标识', () async { final store = _MemorySessionStore( values: const { tokenKey: 'JWT expired-token', identityKey: 'staff-1', roleKey: 'delivery', deviceKey: 'device-1', }, ); final session = StaffSession(store); await session.restore(); var notificationCount = 0; session.addListener(() => notificationCount++); session.invalidate('JWT expired-token'); session.invalidate('JWT expired-token'); await Future.delayed(Duration.zero); expect(session.token, isEmpty); expect(session.identity, isEmpty); expect(session.roleCode, isEmpty); expect(session.deviceIdentity, 'device-1'); expect(session.hasExpired, isTrue); expect(store.deleteCounts[tokenKey], 1); expect(store.deleteCounts[identityKey], 1); expect(store.deleteCounts[roleKey], 1); expect(store.deleteCounts[deviceKey], isNull); expect(store.values[deviceKey], 'device-1'); expect(notificationCount, 1); }); test('迟到的旧请求不得使当前会话失效', () async { final store = _MemorySessionStore( values: const { tokenKey: 'JWT current-token', identityKey: 'staff-1', roleKey: 'installer', deviceKey: 'device-1', }, ); final session = StaffSession(store); await session.restore(); session.invalidate('JWT old-token'); await Future.delayed(Duration.zero); expect(session.token, 'JWT current-token'); expect(session.identity, 'staff-1'); expect(session.roleCode, 'installer'); expect(session.hasExpired, isFalse); expect(store.deleteCounts, isEmpty); }); test('持久化删除失败也立即清空内存会话并保留设备标识', () async { final store = _MemorySessionStore( values: const { tokenKey: 'JWT expired-token', identityKey: 'staff-1', roleKey: 'operations', deviceKey: 'device-1', }, deleteError: StateError('storage unavailable'), ); final session = StaffSession(store); await session.restore(); session.invalidate('JWT expired-token'); await Future.delayed(Duration.zero); expect(session.token, isEmpty); expect(session.identity, isEmpty); expect(session.roleCode, isEmpty); expect(session.deviceIdentity, 'device-1'); expect(session.hasExpired, isTrue); expect(store.deleteCounts.keys, containsAll([tokenKey, identityKey, roleKey])); expect(store.deleteCounts[deviceKey], isNull); }); }