app scaffold

This commit is contained in:
Guangfei.Zhao
2026-08-17 15:29:55 +08:00
commit 681688dfae
301 changed files with 18414 additions and 0 deletions
@@ -0,0 +1,137 @@
import 'package:core_foundation/core_foundation.dart';
import 'package:core_logging/core_logging.dart';
import 'package:flutter_test/flutter_test.dart';
void main() {
group('scrubber', () {
test('敏感 key 整体抹掉', () {
final Map<String, Object?> out = scrubMap(<String, Object?>{
'accessToken': 'eyJhbGciOi...',
'Authorization': 'Bearer abc',
'ticket': 'T-123',
'path': '/api/v1/stores',
});
expect(out['accessToken'], redacted);
expect(out['Authorization'], redacted);
expect(out['ticket'], redacted);
expect(out['path'], '/api/v1/stores');
});
test('手机号走掩码而不是整体抹掉', () {
expect(scrubMap(<String, Object?>{'phone': '13812345678'})['phone'], '138****5678');
expect(maskPhone('123'), '***');
});
test('嵌套结构递归处理', () {
final Map<String, Object?> out = scrubMap(<String, Object?>{
'user': <String, Object?>{'userId': 'u1', 'password': 'p'},
'list': <Object?>[
<String, Object?>{'refreshToken': 'r'},
],
});
final Map<String, Object?> user = out['user']! as Map<String, Object?>;
expect(user['userId'], 'u1');
expect(user['password'], redacted);
final List<Object?> list = out['list']! as List<Object?>;
expect((list.first! as Map<String, Object?>)['refreshToken'], redacted);
});
test('原始 Map 不被修改', () {
final Map<String, Object?> src = <String, Object?>{'token': 'x'};
scrubMap(src);
expect(src['token'], 'x');
});
test('URL 去掉 query 和 fragment', () {
expect(scrubUrl('https://h5.example.com/p?ticket=abc&x=1#frag'), 'https://h5.example.com/p');
});
test('URL 解析失败时整条抹掉,而不是原样返回', () {
expect(scrubUrl('http://[bad'), redacted);
});
});
group('LogBuffer', () {
test('超出容量丢最旧的', () {
final LogBuffer buffer = LogBuffer(capacity: 3);
for (int i = 0; i < 5; i++) {
buffer.add(LogRecord(level: 'I', message: '$i', timestamp: DateTime(2026)));
}
expect(buffer.length, 3);
expect(buffer.recent().map((LogRecord r) => r.message), <String>['2', '3', '4']);
});
test('recent 默认只取 30 条', () {
final LogBuffer buffer = LogBuffer();
for (int i = 0; i < 500; i++) {
buffer.add(LogRecord(level: 'I', message: '$i', timestamp: DateTime(2026)));
}
expect(buffer.length, 500);
expect(buffer.recent().length, 30);
expect(buffer.recent().first.message, '470');
});
});
group('LoggerAppLogger', () {
AppEnv envOf(AppFlavor flavor) => AppEnv(
flavor: flavor,
apiBaseUrl: 'https://example.com',
enableLog: true,
sentryDsn: '',
h5AllowedHosts: const <String>{},
);
test('prod 只收 warning 及以上', () {
final LogBuffer buffer = LogBuffer();
final AppLogger log = LoggerAppLogger(env: envOf(AppFlavor.prod), buffer: buffer);
log
..d('debug')
..i('info')
..w('warn')
..e('error');
expect(buffer.recent().map((LogRecord r) => r.message), <String>['warn', 'error']);
});
test('dev 收全部级别', () {
final LogBuffer buffer = LogBuffer();
LoggerAppLogger(env: envOf(AppFlavor.dev), buffer: buffer)
..d('debug')
..i('info');
expect(buffer.length, 2);
});
test('结构化 data 进缓冲前已脱敏', () {
final LogBuffer buffer = LogBuffer();
LoggerAppLogger(
env: envOf(AppFlavor.uat),
buffer: buffer,
).i('login', data: <String, Object?>{'phone': '13812345678'});
expect(buffer.recent().single.message, contains('138****5678'));
expect(buffer.recent().single.message, isNot(contains('13812345678')));
});
});
group('throwTestException', () {
AppEnv envOf(AppFlavor flavor) => AppEnv(
flavor: flavor,
apiBaseUrl: 'https://example.com',
enableLog: false,
sentryDsn: '',
h5AllowedHosts: const <String>{},
);
test('dev 抛测试异常', () {
expect(() => throwTestException(envOf(AppFlavor.dev)), throwsA(isA<Exception>()));
});
test('prod 拒绝调用', () {
expect(() => throwTestException(envOf(AppFlavor.prod)), throwsA(isA<StateError>()));
});
});
}