// 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; @override Future saveLastEmail(String email) async {} @override Future loadLastEmail() async => null; } 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); }); test('isExhausted:剩余 ≤0 为真', () { expect(const FreeQuotaState(remainingMinutes: 0).isExhausted, true); expect(const FreeQuotaState(remainingMinutes: 1).isExhausted, false); }); }); // 未登录默认态:总额 10 / 剩余 10 / 未耗尽。 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.isExhausted, false); }); test('markExhausted 本地立即置剩余 0', () { final c = _container(); addTearDown(c.dispose); c.read(quotaProvider.notifier).markExhausted(); expect(c.read(quotaProvider).remainingMinutes, 0); expect(c.read(quotaProvider).isExhausted, true); }); }); }