204 lines
5.6 KiB
Dart
204 lines
5.6 KiB
Dart
import 'package:flutter_test/flutter_test.dart';
|
|
import 'package:yx_tracking_flutter/src/config/analytics_config.dart';
|
|
import 'package:yx_tracking_flutter/src/config/config_manager.dart';
|
|
import 'package:yx_tracking_flutter/src/core/validator.dart';
|
|
import 'package:yx_tracking_flutter/src/model/device_info.dart';
|
|
import 'package:yx_tracking_flutter/src/model/event.dart';
|
|
import 'package:yx_tracking_flutter/src/model/system_dim_info.dart';
|
|
|
|
class StaticConfigManager extends ConfigManager {
|
|
StaticConfigManager({
|
|
required super.config,
|
|
required SystemDimInfo configValue,
|
|
}) : _configValue = configValue,
|
|
super(refreshInterval: Duration.zero);
|
|
|
|
final SystemDimInfo _configValue;
|
|
|
|
@override
|
|
SystemDimInfo? get currentConfig => _configValue;
|
|
|
|
@override
|
|
Future<void> init() async {}
|
|
|
|
@override
|
|
Future<void> fetchAndCacheConfig({bool force = false}) async {}
|
|
|
|
@override
|
|
Future<void> forceRefresh() async {}
|
|
|
|
@override
|
|
Future<void> dispose() async {}
|
|
}
|
|
|
|
const DeviceInfo _device = DeviceInfo(
|
|
os: 'test',
|
|
model: 'test',
|
|
screenResolution: '1x1',
|
|
);
|
|
|
|
AnalyticsConfig _config() {
|
|
return AnalyticsConfig(
|
|
systemCode: 'TEST_APP',
|
|
endpointBaseUrl: 'https://example.com',
|
|
enableMetrics: false,
|
|
);
|
|
}
|
|
|
|
Event _event({
|
|
required String type,
|
|
Map<String, dynamic>? customTags,
|
|
}) {
|
|
final now = DateTime.now();
|
|
final ts = now.millisecondsSinceEpoch;
|
|
return Event(
|
|
systemCode: 'TEST_APP',
|
|
eventType: type,
|
|
userInfo: null,
|
|
clientType: 3,
|
|
clientTimestamp: ts,
|
|
timestamp: now.toUtc().toIso8601String(),
|
|
deviceInfo: _device,
|
|
eventParams: null,
|
|
customTags: customTags,
|
|
createTime: now,
|
|
);
|
|
}
|
|
|
|
SystemDimInfo _dimInfo() {
|
|
return SystemDimInfo(
|
|
systemInfo: const SystemInfo(raw: <String, dynamic>{}),
|
|
eventDefinitions: const <EventDefinition>[
|
|
EventDefinition(eventCode: 'KNOWN'),
|
|
],
|
|
tagDefinitions: const <TagDefinition>[
|
|
TagDefinition(tagName: 'tenantId', tagType: 'string', isRequired: true),
|
|
TagDefinition(tagName: 'count', tagType: 'int', isRequired: false),
|
|
],
|
|
sdkStrategy: null,
|
|
lastFetchedAt: DateTime.now(),
|
|
);
|
|
}
|
|
|
|
SystemDimInfo _dimInfoWithTypes() {
|
|
return SystemDimInfo(
|
|
systemInfo: const SystemInfo(raw: <String, dynamic>{}),
|
|
eventDefinitions: const <EventDefinition>[
|
|
EventDefinition(eventCode: 'TYPED'),
|
|
],
|
|
tagDefinitions: const <TagDefinition>[
|
|
TagDefinition(tagName: 'intNum', tagType: 'int', isRequired: true),
|
|
TagDefinition(tagName: 'intStr', tagType: 'integer', isRequired: true),
|
|
TagDefinition(tagName: 'boolNum', tagType: 'bool', isRequired: true),
|
|
TagDefinition(tagName: 'boolStr', tagType: 'boolean', isRequired: true),
|
|
TagDefinition(tagName: 'doubleStr', tagType: 'double', isRequired: true),
|
|
TagDefinition(tagName: 'floatStr', tagType: 'float', isRequired: true),
|
|
TagDefinition(tagName: 'numStr', tagType: 'num', isRequired: true),
|
|
TagDefinition(tagName: 'unknown', tagType: 'mystery', isRequired: false),
|
|
],
|
|
sdkStrategy: null,
|
|
lastFetchedAt: DateTime.now(),
|
|
);
|
|
}
|
|
|
|
bool _isTypeMismatch(ValidationIssue issue) =>
|
|
issue.code == Validator.typeMismatch;
|
|
|
|
void main() {
|
|
group('Validator', () {
|
|
test('已配置事件且必填 tag 满足时无告警', () {
|
|
final manager = StaticConfigManager(
|
|
config: _config(),
|
|
configValue: _dimInfo(),
|
|
);
|
|
final validator = Validator(manager);
|
|
|
|
final result = validator.validate(
|
|
_event(
|
|
type: 'KNOWN',
|
|
customTags: const <String, dynamic>{'tenantId': 't1'},
|
|
),
|
|
);
|
|
|
|
expect(result.isEmpty, true);
|
|
});
|
|
|
|
test('未知事件会产生 error', () {
|
|
final manager = StaticConfigManager(
|
|
config: _config(),
|
|
configValue: _dimInfo(),
|
|
);
|
|
final validator = Validator(manager);
|
|
|
|
final result = validator.validate(_event(type: 'UNKNOWN'));
|
|
|
|
expect(result.hasErrors, true);
|
|
expect(result.errors.first.code, Validator.unknownEventType);
|
|
});
|
|
|
|
test('缺失必填 tag 会产生 warning', () {
|
|
final manager = StaticConfigManager(
|
|
config: _config(),
|
|
configValue: _dimInfo(),
|
|
);
|
|
final validator = Validator(manager);
|
|
|
|
final result = validator.validate(_event(type: 'KNOWN'));
|
|
|
|
expect(result.hasWarnings, true);
|
|
expect(result.warnings.first.code, Validator.missingRequiredTag);
|
|
});
|
|
|
|
test('类型不匹配会产生 warning', () {
|
|
final manager = StaticConfigManager(
|
|
config: _config(),
|
|
configValue: _dimInfo(),
|
|
);
|
|
final validator = Validator(manager);
|
|
|
|
final result = validator.validate(
|
|
_event(
|
|
type: 'KNOWN',
|
|
customTags: const <String, dynamic>{
|
|
'tenantId': 't1',
|
|
'count': 'not-an-int',
|
|
},
|
|
),
|
|
);
|
|
|
|
expect(result.hasWarnings, true);
|
|
expect(
|
|
result.warnings.any(_isTypeMismatch),
|
|
true,
|
|
);
|
|
});
|
|
|
|
test('多类型匹配路径均可通过', () {
|
|
final manager = StaticConfigManager(
|
|
config: _config(),
|
|
configValue: _dimInfoWithTypes(),
|
|
);
|
|
final validator = Validator(manager);
|
|
|
|
final result = validator.validate(
|
|
_event(
|
|
type: 'TYPED',
|
|
customTags: const <String, dynamic>{
|
|
'intNum': 1.0,
|
|
'intStr': '2',
|
|
'boolNum': 0,
|
|
'boolStr': 'no',
|
|
'doubleStr': '3.14',
|
|
'floatStr': '2.72',
|
|
'numStr': '42',
|
|
'unknown': <String, dynamic>{'any': 'value'},
|
|
},
|
|
),
|
|
);
|
|
|
|
expect(result.hasErrors, false);
|
|
expect(result.hasWarnings, false);
|
|
});
|
|
});
|
|
}
|