// nodes_provider_test.dart — 节点选择 / 智能选择派生 import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:pangolin_vpn/l10n/app_text.dart'; import 'package:pangolin_vpn/models/node.dart'; import 'package:pangolin_vpn/services/token_store.dart'; import 'package:pangolin_vpn/state/auth_provider.dart'; import 'package:pangolin_vpn/state/nodes_provider.dart'; // ── Stub TokenStore(无平台依赖)────────────────────────────────── class _NullTokenStore implements TokenStore { const _NullTokenStore(); @override Future saveTokens({required String access, required String refresh}) async {} @override Future loadAccessToken() async => null; @override Future loadRefreshToken() async => null; @override Future clear() async {} } // ── 辅助:带 stub 覆盖的 ProviderContainer ────────────────────── ProviderContainer makeContainer({List overrides = const []}) => ProviderContainer(overrides: [ tokenStoreProvider.overrideWithValue(const _NullTokenStore()), // effectiveNodeProvider 在 nodesProvider 加载时退回 kDemoNodes, // 所以测试直接读 effectiveNodeProvider,无需等待 nodesProvider.future。 ...overrides, ]); void main() { test('默认智能选择(AUTO)', () { final c = makeContainer(); addTearDown(c.dispose); expect(c.read(selectedNodeCodeProvider), kSmartNodeCode); expect(c.read(isSmartSelectProvider), true); }); test('智能选择取延迟最小节点(退回 kDemoNodes)', () { final c = makeContainer(); addTearDown(c.dispose); // nodesProvider 加载中时退回 kDemoNodes,effectiveNodeProvider 立即可用。 final node = c.read(effectiveNodeProvider); final minPing = kDemoNodes.map((n) => n.ping).reduce((a, b) => a < b ? a : b); expect(node.ping, minPing); }); test('选中具体节点后生效节点随之改变', () { final c = makeContainer(); addTearDown(c.dispose); c.read(selectedNodeCodeProvider.notifier).state = 'JP'; expect(c.read(isSmartSelectProvider), false); expect(c.read(effectiveNodeProvider).code, 'JP'); }); test('localizedSub 不串语言:无标签用拉丁地名', () { const tw = Node(code: 'TW', nameZh: '台湾 台北', nameEn: 'Taipei', ping: 28); expect(tw.localizedSub(AppLang.zh), 'Taipei'); expect(tw.localizedSub(AppLang.en), 'Taipei'); const hk = Node(code: 'HK', nameZh: '香港 · 流媒体', nameEn: 'Hong Kong', ping: 18, tag: NodeTag.streaming); expect(hk.localizedSub(AppLang.zh), '流媒体优化'); expect(hk.localizedSub(AppLang.en), 'Streaming'); }); }