Files
platforms/apps/service_app/test/app/staff_session_test.dart

116 lines
3.8 KiB
Dart
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// 功能描述:验证工作人员会话失效的幂等清理、旧请求隔离和存储异常降级。
// 版本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<String, String>? values, this.deleteError}) : values = {...?values};
final Map<String, String> values;
final Object? deleteError;
final Map<String, int> deleteCounts = {};
@override
Future<void> delete(String key) async {
deleteCounts[key] = (deleteCounts[key] ?? 0) + 1;
if (deleteError != null) throw deleteError!;
values.remove(key);
}
@override
Future<String?> read(String key) async => values[key];
@override
Future<void> 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<void>.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<void>.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<void>.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);
});
}