import 'package:dio/dio.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/model/system_dim_info.dart'; import 'package:yx_tracking_flutter/src/network/http_client.dart'; import 'package:yx_tracking_flutter/src/storage/config_storage.dart'; class InMemoryConfigStorage implements ConfigStorage { SystemDimInfo? _stored; @override Future init() async {} @override Future saveSystemDimInfo(SystemDimInfo info) async { _stored = info; } @override Future loadSystemDimInfo() async => _stored; @override Future clear() async { _stored = null; } @override Future dispose() async {} } class FakeHttpClient extends HttpClient { FakeHttpClient(super.config); int getCallCount = 0; dynamic responseData; Headers responseHeaders = Headers(); DioException? exceptionToThrow; @override Future> get( String path, { Map? queryParameters, Map? headers, CancelToken? cancelToken, }) async { getCallCount += 1; final exception = exceptionToThrow; if (exception != null) { throw exception; } return Response( data: responseData as T?, statusCode: 200, headers: responseHeaders, requestOptions: RequestOptions(path: path, queryParameters: queryParameters), ); } } AnalyticsConfig _config() { return const AnalyticsConfig( systemCode: 'TEST_APP', endpointBaseUrl: 'https://example.com/api/ExternalEventlogs', clientType: 3, enableMetrics: false, ); } SystemDimInfo _storedConfig(DateTime fetchedAt) { return SystemDimInfo( systemInfo: const SystemInfo(raw: {}), eventDefinitions: const [], tagDefinitions: const [], sdkStrategy: null, lastFetchedAt: fetchedAt, ); } void main() { group('ConfigManager', () { test('fetchAndCacheConfig 会解析配置并缓存', () async { final storage = InMemoryConfigStorage(); final httpClient = FakeHttpClient(_config()); httpClient.responseData = { 'data': { 'systemEventTypes': >[ {'eventCode': 'EVENT_A', 'eventName': 'A'}, ], 'systemCustonTas': >[ { 'tagName': 'tenantId', 'tagType': 'string', 'isRequired': true, }, ], 'sdkStrategy': { 'enabled': true, 'defaultSampleRate': 1.0, 'eventSettings': { 'EVENT_A': { 'enabled': true, 'sampleRate': 0.25, }, }, }, }, }; httpClient.responseHeaders = Headers.fromMap(>{ 'x-config-version': ['v1'], }); final manager = ConfigManager( config: _config(), refreshInterval: const Duration(hours: 1), storage: storage, httpClient: httpClient, ); await manager.init(); await manager.fetchAndCacheConfig(force: true); final current = manager.currentConfig; expect(current, isNotNull); expect(current!.eventDefinitions.single.eventCode, 'EVENT_A'); expect(current.requiredTags.single.tagName, 'tenantId'); expect(current.sampleRateFor('EVENT_A'), 0.25); expect(current.version, 'v1'); }); test('配置未过期时会跳过拉取', () async { final storage = InMemoryConfigStorage(); final httpClient = FakeHttpClient(_config()); final now = DateTime.now(); await storage.saveSystemDimInfo(_storedConfig(now)); final manager = ConfigManager( config: _config(), refreshInterval: const Duration(hours: 12), storage: storage, httpClient: httpClient, ); await manager.init(); await manager.fetchAndCacheConfig(force: false); expect(httpClient.getCallCount, 0); expect(manager.currentConfig, isNotNull); }); test('响应结构不可解析时保持现有配置', () async { final storage = InMemoryConfigStorage(); final httpClient = FakeHttpClient(_config()); final existing = _storedConfig(DateTime.now().subtract(const Duration(days: 1))); await storage.saveSystemDimInfo(existing); httpClient.responseData = 'not-a-map'; final manager = ConfigManager( config: _config(), refreshInterval: const Duration(hours: 1), storage: storage, httpClient: httpClient, ); await manager.init(); await manager.fetchAndCacheConfig(force: true); expect(manager.currentConfig, isNotNull); expect(manager.currentConfig!.lastFetchedAt, existing.lastFetchedAt); }); }); }