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: {'k': 'v'}), eventDefinitions: const [], tagDefinitions: const [], 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: {'k': 'v'}), eventDefinitions: const [ EventDefinition(eventCode: 'A', eventName: 'A1'), ], tagDefinitions: const [ TagDefinition(tagName: 't1', tagType: 'string', isRequired: true), TagDefinition(tagName: 't2', tagType: 'int', isRequired: false), ], sdkStrategy: const SdkStrategy( enabled: true, defaultSampleRate: 0.5, eventSettings: { '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), ['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: {'a': 1}), eventDefinitions: const [ EventDefinition(eventCode: 'E', eventName: 'Event'), ], tagDefinitions: const [ TagDefinition(tagName: 'tag', tagType: 'string', isRequired: true), ], sdkStrategy: const SdkStrategy( enabled: false, defaultSampleRate: 0.3, eventSettings: { '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( { 'systemInfo': {'sys': true}, 'systemEventTypes': [ {'event_code': 'E1', 'event_name': 'Name'}, {'eventType': 'E2'}, {'eventCode': ''}, 123, ], // 使用拼写错误字段名 'systemCustonTas': [ { 'tag_name': 'req', 'tag_type': 'int', 'is_required': '1', 'desc': 'd', }, {'name': 'opt', 'type': 'string'}, {'tagName': ''}, 'bad', ], // 使用 sdk_strategy 字段名 'sdk_strategy': { 'enabled': 'false', 'defaultSampleRate': 2, 'eventSettings': { 'E1': {'enabled': 0, 'sampleRate': -1}, 'E2': { '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(['E1', 'E2']), ); expect( info.tagDefinitions.map(_tagNameOf).toList(growable: false), containsAll(['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( { '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( { 'systemInfo': {'k': 'v'}, 'eventDefinitions': const [], 'tagDefinitions': const [], 'sdkStrategy': { 'enabled': true, 'defaultSampleRate': '0.25', 'eventSettings': { 'E': {'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( { 'systemInfo': const {}, 'eventDefinitions': const [], 'tagDefinitions': const [], 'lastFetchedAt': '2', }, ); expect(info.lastFetchedAt.millisecondsSinceEpoch, 2); }); }); group('Model toJson', () { test('SystemInfo/EventDefinition/TagDefinition/SdkStrategy/EventStrategy', () { const system = SystemInfo(raw: {'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: { '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( { 'sdkStrategy': { 'enabled': true, 'defaultSampleRate': double.nan, 'eventSettings': const {}, }, }, fetchedAt: DateTime.fromMillisecondsSinceEpoch(1), ); expect(info.sdkStrategy?.defaultSampleRate, 1); }); }); }