import 'package:flutter_test/flutter_test.dart'; import 'package:patbond_flutter/core/network/api_exception.dart'; import 'package:patbond_flutter/features/community/feed_analytics.dart'; void main() { late List<(String, Map?)> events; late FeedAnalytics analytics; setUp(() { events = []; analytics = FeedAnalytics( (name, [props]) async => events.add((name, props)), ); }); test('feed_viewed:五个专有属性逐一上报(06 §1.4 白名单)', () { analytics.feedViewed( feedTab: FeedTab.home, durationMs: 12345, impressionCount: 7, loadMoreCount: 2, refreshCount: 1, ); expect(events, hasLength(1)); expect(events.single.$1, 'feed_viewed'); expect(events.single.$2, { 'feedTab': 'home', 'durationMs': 12345, 'impressionCount': 7, 'loadMoreCount': 2, 'refreshCount': 1, }); }); test('feed_load_failed:业务码带 errorCode 并推导 httpStatus', () { analytics.feedLoadFailed( feedTab: FeedTab.home, loadType: FeedLoadType.loadMore, reason: FeedLoadFailureReason.serverError, errorCode: 40403, ); expect(events.single.$1, 'feed_load_failed'); expect(events.single.$2, { 'feedTab': 'home', 'loadType': 'load_more', 'failureReason': 'server_error', 'errorCode': 40403, 'httpStatus': 404, }); }); test('feed_load_failed:网络错误缺席 errorCode/httpStatus', () { analytics.feedLoadFailedFrom( const ApiNetworkException(), feedTab: FeedTab.home, loadType: FeedLoadType.refresh, ); expect(events.single.$2, { 'feedTab': 'home', 'loadType': 'refresh', 'failureReason': 'network_error', }); }); test('异常映射:限流/网络/业务兜底;会话失效不上报', () { expect( feedLoadFailureReasonOf(const ApiRateLimitException()), FeedLoadFailureReason.rateLimited, ); expect( feedLoadFailureReasonOf(const ApiNetworkException()), FeedLoadFailureReason.networkError, ); expect( feedLoadFailureReasonOf( const ApiBusinessException(code: 40000, message: 'x'), ), FeedLoadFailureReason.serverError, ); expect(feedLoadFailureReasonOf(const SessionExpiredException()), isNull); analytics.feedLoadFailedFrom( const SessionExpiredException(), feedTab: FeedTab.home, loadType: FeedLoadType.refresh, ); expect(events, isEmpty); }); }