253 lines
8.6 KiB
Dart
253 lines
8.6 KiB
Dart
import 'package:flutter_test/flutter_test.dart';
|
|
import 'package:yx_tracking_flutter/src/model/system_dim_info.dart';
|
|
|
|
SystemDimInfo _emptyInfo({SdkStrategy? strategy}) {
|
|
return SystemDimInfo(
|
|
systemInfo: const SystemInfo(raw: <String, Object?>{'k': 'v'}),
|
|
eventDefinitions: const <EventDefinition>[],
|
|
tagDefinitions: const <TagDefinition>[],
|
|
sdkStrategy: strategy,
|
|
lastFetchedAt: DateTime.fromMillisecondsSinceEpoch(1),
|
|
version: 'v1',
|
|
);
|
|
}
|
|
|
|
String _eventCodeOf(EventDefinition event) => event.eventCode;
|
|
|
|
String _tagNameOf(TagDefinition tag) => tag.tagName;
|
|
|
|
void main() {
|
|
group('SystemDimInfo helpers', () {
|
|
test('hasEvent/requiredTags/strategy helpers', () {
|
|
final info = SystemDimInfo(
|
|
systemInfo: const SystemInfo(raw: <String, Object?>{'k': 'v'}),
|
|
eventDefinitions: const <EventDefinition>[
|
|
EventDefinition(eventCode: 'A', eventName: 'A1'),
|
|
],
|
|
tagDefinitions: const <TagDefinition>[
|
|
TagDefinition(tagName: 't1', tagType: 'string', isRequired: true),
|
|
TagDefinition(tagName: 't2', tagType: 'int', isRequired: false),
|
|
],
|
|
sdkStrategy: const SdkStrategy(
|
|
enabled: true,
|
|
defaultSampleRate: 0.5,
|
|
eventSettings: <String, EventStrategy>{
|
|
'A': EventStrategy(enabled: false, sampleRate: 0.1),
|
|
},
|
|
),
|
|
lastFetchedAt: DateTime.fromMillisecondsSinceEpoch(2),
|
|
);
|
|
|
|
expect(info.hasEvent('A'), isTrue);
|
|
expect(info.hasEvent('B'), isFalse);
|
|
|
|
expect(info.requiredTags.map((e) => e.tagName), <String>['t1']);
|
|
|
|
expect(info.isStrategyEnabled, isTrue);
|
|
expect(info.strategyFor('A')?.enabled, isFalse);
|
|
expect(info.strategyFor('B'), isNull);
|
|
|
|
expect(info.sampleRateFor('A'), 0.1);
|
|
expect(info.sampleRateFor('B'), 0.5);
|
|
|
|
expect(info.isEventEnabledByStrategy('A'), isFalse);
|
|
expect(info.isEventEnabledByStrategy('B'), isTrue);
|
|
});
|
|
|
|
test('无策略时走默认值', () {
|
|
final info = _emptyInfo();
|
|
|
|
expect(info.isStrategyEnabled, isTrue);
|
|
expect(info.strategyFor('X'), isNull);
|
|
expect(info.sampleRateFor('X'), 1);
|
|
expect(info.isEventEnabledByStrategy('X'), isTrue);
|
|
});
|
|
});
|
|
|
|
group('SystemDimInfo cache/response 解析', () {
|
|
test('toCacheJson/fromCacheJson 往返', () {
|
|
final original = SystemDimInfo(
|
|
systemInfo: const SystemInfo(raw: <String, Object?>{'a': 1}),
|
|
eventDefinitions: const <EventDefinition>[
|
|
EventDefinition(eventCode: 'E', eventName: 'Event'),
|
|
],
|
|
tagDefinitions: const <TagDefinition>[
|
|
TagDefinition(tagName: 'tag', tagType: 'string', isRequired: true),
|
|
],
|
|
sdkStrategy: const SdkStrategy(
|
|
enabled: false,
|
|
defaultSampleRate: 0.3,
|
|
eventSettings: <String, EventStrategy>{
|
|
'E': EventStrategy(enabled: true, sampleRate: 1),
|
|
},
|
|
),
|
|
lastFetchedAt: DateTime.fromMillisecondsSinceEpoch(10),
|
|
version: 'ver',
|
|
);
|
|
|
|
final cached = original.toCacheJson();
|
|
final parsed = SystemDimInfo.fromCacheJson(cached);
|
|
|
|
expect(parsed.systemInfo?.raw['a'], 1);
|
|
expect(parsed.eventDefinitions.single.eventCode, 'E');
|
|
expect(parsed.tagDefinitions.single.tagName, 'tag');
|
|
expect(parsed.sdkStrategy?.enabled, isFalse);
|
|
expect(parsed.sdkStrategy?.eventSettings['E']?.sampleRate, 1);
|
|
expect(parsed.version, 'ver');
|
|
expect(parsed.lastFetchedAt.millisecondsSinceEpoch, 10);
|
|
});
|
|
|
|
test('fromResponse 兼容字段差异与异常输入', () {
|
|
final fetchedAt = DateTime.fromMillisecondsSinceEpoch(99);
|
|
final info = SystemDimInfo.fromResponse(
|
|
<String, Object?>{
|
|
'systemInfo': <String, Object?>{'sys': true},
|
|
'systemEventTypes': <Object?>[
|
|
<String, Object?>{'event_code': 'E1', 'event_name': 'Name'},
|
|
<String, Object?>{'eventType': 'E2'},
|
|
<String, Object?>{'eventCode': ''},
|
|
123,
|
|
],
|
|
// 使用拼写错误字段名
|
|
'systemCustonTas': <Object?>[
|
|
<String, Object?>{
|
|
'tag_name': 'req',
|
|
'tag_type': 'int',
|
|
'is_required': '1',
|
|
'desc': 'd',
|
|
},
|
|
<String, Object?>{'name': 'opt', 'type': 'string'},
|
|
<String, Object?>{'tagName': ''},
|
|
'bad',
|
|
],
|
|
// 使用 sdk_strategy 字段名
|
|
'sdk_strategy': <String, Object?>{
|
|
'enabled': 'false',
|
|
'defaultSampleRate': 2,
|
|
'eventSettings': <String, Object?>{
|
|
'E1': <String, Object?>{'enabled': 0, 'sampleRate': -1},
|
|
'E2': <String, Object?>{
|
|
'enabled': true,
|
|
'sampleRate': double.infinity,
|
|
},
|
|
'bad': 1,
|
|
},
|
|
},
|
|
},
|
|
fetchedAt: fetchedAt,
|
|
version: 'v2',
|
|
);
|
|
|
|
expect(info.systemInfo?.raw['sys'], isTrue);
|
|
expect(
|
|
info.eventDefinitions.map(_eventCodeOf).toList(growable: false),
|
|
containsAll(<String>['E1', 'E2']),
|
|
);
|
|
expect(
|
|
info.tagDefinitions.map(_tagNameOf).toList(growable: false),
|
|
containsAll(<String>['req', 'opt']),
|
|
);
|
|
expect(info.tagDefinitions.first.isRequired, isTrue);
|
|
|
|
// defaultSampleRate=2 会被 clamp 为 1
|
|
expect(info.sdkStrategy?.defaultSampleRate, 1);
|
|
// -1 会被 clamp 为 0
|
|
expect(info.sdkStrategy?.eventSettings['E1']?.sampleRate, 0);
|
|
// infinity 会被 clamp 为 1
|
|
expect(info.sdkStrategy?.eventSettings['E2']?.sampleRate, 1);
|
|
expect(info.isStrategyEnabled, isFalse);
|
|
expect(info.lastFetchedAt, fetchedAt);
|
|
expect(info.version, 'v2');
|
|
});
|
|
|
|
test('异常类型输入会返回空集合/空策略', () {
|
|
final info = SystemDimInfo.fromResponse(
|
|
<String, Object?>{
|
|
'systemEventTypes': 'not-list',
|
|
'systemCustomTags': 123,
|
|
'sdkStrategy': 'bad',
|
|
},
|
|
fetchedAt: DateTime.fromMillisecondsSinceEpoch(1),
|
|
);
|
|
|
|
expect(info.eventDefinitions, isEmpty);
|
|
expect(info.tagDefinitions, isEmpty);
|
|
expect(info.sdkStrategy, isNull);
|
|
});
|
|
|
|
test('fromCacheJson 支持 num 与 string 数值', () {
|
|
final info = SystemDimInfo.fromCacheJson(
|
|
<String, Object?>{
|
|
'systemInfo': <String, Object?>{'k': 'v'},
|
|
'eventDefinitions': const <Object?>[],
|
|
'tagDefinitions': const <Object?>[],
|
|
'sdkStrategy': <String, Object?>{
|
|
'enabled': true,
|
|
'defaultSampleRate': '0.25',
|
|
'eventSettings': <String, Object?>{
|
|
'E': <String, Object?>{'enabled': true, 'sampleRate': '0.75'},
|
|
},
|
|
},
|
|
'lastFetchedAt': 1.5,
|
|
},
|
|
);
|
|
|
|
expect(info.lastFetchedAt.millisecondsSinceEpoch, 1);
|
|
expect(info.sdkStrategy?.defaultSampleRate, 0.25);
|
|
expect(info.sdkStrategy?.eventSettings['E']?.sampleRate, 0.75);
|
|
});
|
|
|
|
test('fromCacheJson 的 lastFetchedAt 支持字符串整数', () {
|
|
final info = SystemDimInfo.fromCacheJson(
|
|
<String, Object?>{
|
|
'systemInfo': const <String, Object?>{},
|
|
'eventDefinitions': const <Object?>[],
|
|
'tagDefinitions': const <Object?>[],
|
|
'lastFetchedAt': '2',
|
|
},
|
|
);
|
|
|
|
expect(info.lastFetchedAt.millisecondsSinceEpoch, 2);
|
|
});
|
|
});
|
|
|
|
group('Model toJson', () {
|
|
test('SystemInfo/EventDefinition/TagDefinition/SdkStrategy/EventStrategy', () {
|
|
const system = SystemInfo(raw: <String, Object?>{'k': 'v'});
|
|
const event = EventDefinition(eventCode: 'E', eventName: 'N');
|
|
const tag = TagDefinition(
|
|
tagName: 't',
|
|
tagType: 'string',
|
|
isRequired: true,
|
|
);
|
|
const strategy = SdkStrategy(
|
|
enabled: true,
|
|
defaultSampleRate: 0.7,
|
|
eventSettings: <String, EventStrategy>{
|
|
'E': EventStrategy(enabled: true, sampleRate: 0.7),
|
|
},
|
|
);
|
|
|
|
expect(SystemInfo.fromJson(system.toJson()).raw['k'], 'v');
|
|
expect(event.toJson()['eventCode'], 'E');
|
|
expect(tag.toJson()['isRequired'], isTrue);
|
|
expect(strategy.toJson()['eventSettings'], contains('E'));
|
|
});
|
|
|
|
test('defaultSampleRate 为 NaN 时会回落到 1', () {
|
|
final info = SystemDimInfo.fromResponse(
|
|
<String, Object?>{
|
|
'sdkStrategy': <String, Object?>{
|
|
'enabled': true,
|
|
'defaultSampleRate': double.nan,
|
|
'eventSettings': const <String, Object?>{},
|
|
},
|
|
},
|
|
fetchedAt: DateTime.fromMillisecondsSinceEpoch(1),
|
|
);
|
|
|
|
expect(info.sdkStrategy?.defaultSampleRate, 1);
|
|
});
|
|
});
|
|
}
|