// quota_controller_test.dart — 免费额度状态(派生自 me/plans) import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:pangolin_vpn/services/token_store.dart'; import 'package:pangolin_vpn/state/auth_provider.dart'; import 'package:pangolin_vpn/state/quota_provider.dart'; // 避免触碰真实 FlutterSecureStorage 插件(单测无平台)。 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 {} @override Future markOnboarded() async {} @override Future isOnboarded() async => true; } ProviderContainer _container() => ProviderContainer( overrides: [tokenStoreProvider.overrideWithValue(const _NullTokenStore())], ); void main() { // 纯状态逻辑(与数据源无关)。 group('FreeQuotaState', () { test('progress = 剩余/总额', () { expect(const FreeQuotaState(totalMinutes: 10, remainingMinutes: 5).progress, closeTo(0.5, 1e-9)); }); test('isLow:剩余 ≤3 分钟为真', () { expect(const FreeQuotaState(remainingMinutes: 3).isLow, true); expect(const FreeQuotaState(remainingMinutes: 4).isLow, false); }); }); // 未登录默认态:总额 10 / 剩余 10 / 未解锁;watchAd 本地置位。 group('quotaProvider', () { test('默认 10/10 未解锁', () { final c = _container(); addTearDown(c.dispose); final q = c.read(quotaProvider); expect(q.totalMinutes, 10); expect(q.remainingMinutes, 10); expect(q.adUnlocked, false); }); test('watchAd 解锁今日使用', () { final c = _container(); addTearDown(c.dispose); c.read(quotaProvider.notifier).watchAd(); expect(c.read(quotaProvider).adUnlocked, true); }); }); }