Initial commit

This commit is contained in:
admin1
2026-07-14 11:11:36 +08:00
commit 656499cf94
604 changed files with 119518 additions and 0 deletions
@@ -0,0 +1,62 @@
import 'package:flutter_test/flutter_test.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:smplayer/providers/player_engine_override_provider.dart';
void main() {
TestWidgetsFlutterBinding.ensureInitialized();
const key = 'smplayer.player_engine_override';
ProviderContainer makeContainer() {
final c = ProviderContainer();
addTearDown(c.dispose);
return c;
}
test('默认(无持久化值)build 返回 auto', () async {
SharedPreferences.setMockInitialValues({});
final c = makeContainer();
final value = await c.read(playerEngineOverrideProvider.future);
expect(value, PlayerEngineOverride.auto);
});
test('setOverride(media3) 后 build 返回 media3 且持久化为 name', () async {
SharedPreferences.setMockInitialValues({});
final c = makeContainer();
await c.read(playerEngineOverrideProvider.future);
await c
.read(playerEngineOverrideProvider.notifier)
.setOverride(PlayerEngineOverride.media3);
expect(
c.read(playerEngineOverrideProvider).value,
PlayerEngineOverride.media3,
);
final prefs = await SharedPreferences.getInstance();
expect(prefs.getString(key), 'media3');
});
test('持久化值为 media3 时 build 读回 media3', () async {
SharedPreferences.setMockInitialValues({key: 'media3'});
final c = makeContainer();
final value = await c.read(playerEngineOverrideProvider.future);
expect(value, PlayerEngineOverride.media3);
});
test('损坏字符串容错返回 auto', () async {
SharedPreferences.setMockInitialValues({key: 'garbage-value'});
final c = makeContainer();
final value = await c.read(playerEngineOverrideProvider.future);
expect(value, PlayerEngineOverride.auto);
});
test('byName(media3) 不抛', () {
expect(() => PlayerEngineOverride.values.byName('media3'), returnsNormally);
expect(
PlayerEngineOverride.values.byName('media3'),
PlayerEngineOverride.media3,
);
});
}
@@ -0,0 +1,137 @@
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:smplayer/core/domain/ports/sm_account_gateway.dart';
import 'package:smplayer/providers/sm_account_provider.dart';
import 'package:smplayer/providers/tmdb_settings_provider.dart';
void main() {
TestWidgetsFlutterBinding.ensureInitialized();
const tmdbEnabledKey = 'smplayer.tmdb_enabled';
const tmdbAccessModeKey = 'smplayer.tmdb_access_mode';
const tmdbApiKeyKey = 'smplayer.tmdb_api_key';
const tmdbLanguageKey = 'smplayer.tmdb_language';
ProviderContainer makeContainer(SmLoginResult loginResult) {
final c = ProviderContainer(
overrides: [
smAccountGatewayProvider.overrideWithValue(
_FakeSmAccountGateway(loginResult),
),
],
);
addTearDown(c.dispose);
return c;
}
test('VIP login enables TMDB through sm-server proxy', () async {
SharedPreferences.setMockInitialValues({
tmdbEnabledKey: false,
tmdbAccessModeKey: TmdbAccessMode.official.name,
tmdbApiKeyKey: 'keep-me',
tmdbLanguageKey: 'en-US',
});
final c = makeContainer(
const SmLoginResult(
ok: true,
email: 'vip@example.com',
accessToken: 'access',
refreshToken: 'refresh',
expiresIn: 900,
isVip: true,
),
);
await c.read(smAccountProvider.future);
final result = await c
.read(smAccountProvider.notifier)
.login('vip@example.com', 'password');
expect(result.ok, isTrue);
final settings = await c.read(tmdbSettingsProvider.future);
expect(settings.enabled, isTrue);
expect(settings.accessMode, TmdbAccessMode.smAccount);
expect(settings.apiKey, 'keep-me');
expect(settings.language, 'en-US');
final prefs = await SharedPreferences.getInstance();
expect(prefs.getBool(tmdbEnabledKey), isTrue);
expect(prefs.getString(tmdbAccessModeKey), TmdbAccessMode.smAccount.name);
});
test('non-VIP login leaves TMDB settings unchanged', () async {
SharedPreferences.setMockInitialValues({
tmdbEnabledKey: false,
tmdbAccessModeKey: TmdbAccessMode.official.name,
});
final c = makeContainer(
const SmLoginResult(
ok: true,
email: 'user@example.com',
accessToken: 'access',
refreshToken: 'refresh',
expiresIn: 900,
),
);
await c.read(smAccountProvider.future);
final result = await c
.read(smAccountProvider.notifier)
.login('user@example.com', 'password');
expect(result.ok, isTrue);
final settings = await c.read(tmdbSettingsProvider.future);
expect(settings.enabled, isFalse);
expect(settings.accessMode, TmdbAccessMode.official);
final prefs = await SharedPreferences.getInstance();
expect(prefs.getBool(tmdbEnabledKey), isFalse);
expect(prefs.getString(tmdbAccessModeKey), TmdbAccessMode.official.name);
});
}
class _FakeSmAccountGateway implements SmAccountGateway {
final SmLoginResult loginResult;
_FakeSmAccountGateway(this.loginResult);
@override
Future<SmLoginResult> login({
required String baseUrl,
required String email,
required String password,
}) async => loginResult;
@override
Future<void> logout({
required String baseUrl,
required String refreshToken,
}) async {}
@override
Future<SmTokenRefreshResult> refreshAccessToken({
required String baseUrl,
required String refreshToken,
}) async => const SmTokenRefreshResult(ok: false);
@override
Future<SmActionResult> registerStart({
required String baseUrl,
required String email,
}) async => SmActionResult.failure('unused');
@override
Future<SmLoginResult> registerVerify({
required String baseUrl,
required String email,
required String password,
required String code,
}) async => loginResult;
@override
Future<SmActionResult> resendCode({
required String baseUrl,
required String email,
}) async => SmActionResult.failure('unused');
}