Files
ReelShort/test/drama_detail/drama_detail_logic_test.dart
T

186 lines
5.4 KiB
Dart

import 'package:flutter_test/flutter_test.dart';
import 'package:fruitshorts/core/data/models/drama.dart';
import 'package:fruitshorts/core/data/models/watch_progress.dart';
import 'package:fruitshorts/core/data/remote/api_client.dart';
import 'package:fruitshorts/core/data/repositories/favorite_repository.dart';
import 'package:fruitshorts/core/data/repositories/playback_repository.dart';
import 'package:fruitshorts/core/data/services/favorite_controller.dart';
import 'package:fruitshorts/core/data/services/playback_position_cache.dart';
import 'package:fruitshorts/pages/drama_detail/drama_detail_logic.dart';
import '../support/fake_watch_history.dart';
/// 详情页逻辑依赖:票据仓库 + 收藏控制器(测试里都是内存实现)。
DramaDetailLogic _logic(
Drama drama,
FakeWatchHistory history, {
FavoriteController? favorites,
PlaybackPositionCache? positionCache,
}) =>
DramaDetailLogic(
drama: drama,
history: history,
playback: FakePlaybackRepository(),
favorites: favorites ?? FavoriteController(InMemoryFavoriteRepository()),
positionCache: positionCache ?? PlaybackPositionCache(),
);
Drama _card(String id) => Drama(
id: id,
title: id,
synopsis: 'synopsis',
posterUrl: '/files/$id.jpg',
totalEpisodes: 3,
episodes: [
Episode(
id: '$id-1',
number: 1,
title: '第 1 集',
isFree: true,
videoUrl: '/files/$id-1.mp4',
),
],
);
Drama _detail(String id) => Drama(
id: id,
title: id,
synopsis: 'synopsis',
posterUrl: '/files/$id.jpg',
totalEpisodes: 3,
detailLoaded: true,
episodes: [
for (var number = 1; number <= 3; number++)
Episode(
id: '$id-$number',
number: number,
title: '第 $number 集',
isFree: true,
videoUrl: '/files/$id-$number.mp4',
),
],
);
void main() {
test('should_upgrade_to_full_episode_list_when_detail_loads_for_list_card', () async {
var calls = 0;
final logic = _logic(_card('signal'), FakeWatchHistory());
await logic.load(() async {
calls++;
return _detail('signal');
});
expect(calls, 1);
expect(logic.loading, isFalse);
expect(logic.failed, isFalse);
expect(logic.drama.episodes, hasLength(3));
logic.dispose();
});
test('should_keep_card_and_report_reason_when_load_fails', () async {
final logic = _logic(_card('signal'), FakeWatchHistory());
await logic.load(() async => throw const ApiException(500, '服务异常'));
expect(logic.failed, isTrue);
expect(logic.failureMessage, '服务异常');
expect(logic.drama.episodes, hasLength(1));
await logic.load(() async => throw StateError('offline'));
expect(logic.failed, isTrue);
expect(logic.failureMessage, isNull);
logic.dispose();
});
test('should_skip_request_when_detail_is_already_loaded', () async {
var calls = 0;
final logic = _logic(_detail('signal'), FakeWatchHistory());
await logic.load(() async {
calls++;
return _detail('signal');
});
expect(calls, 0);
expect(logic.drama.episodes, hasLength(3));
logic.dispose();
});
test('should_prefer_recent_local_position_when_loading_continue', () async {
final cache = PlaybackPositionCache();
cache.save(
episodeId: 'signal-3',
dramaId: 'signal',
position: const Duration(minutes: 1, seconds: 20),
duration: const Duration(minutes: 3),
);
final history = FakeWatchHistory()
..records['signal'] = const WatchProgress(
dramaId: 'signal',
episodeId: 'signal-1',
position: Duration(seconds: 5),
duration: Duration(minutes: 3),
);
final logic = _logic(_detail('signal'), history, positionCache: cache);
await logic.load(() async => _detail('signal'));
expect(logic.continueIndex, 2);
expect(logic.progress?.position, const Duration(seconds: 80));
expect(history.reads, isEmpty);
logic.dispose();
});
test('should_adjust_favorite_count_only_after_successful_changes', () async {
final repository = InMemoryFavoriteRepository(cards: [_favoriteDrama]);
final favorites = FavoriteController(repository);
await favorites.load();
final logic = _logic(
_favoriteDrama,
FakeWatchHistory(),
favorites: favorites,
);
expect(logic.adjustedFavoriteCount, 2);
expect(await logic.toggleFavorite(), isTrue);
expect(logic.adjustedFavoriteCount, 3);
repository.failToggle = true;
expect(await logic.toggleFavorite(), isFalse);
expect(logic.adjustedFavoriteCount, 3);
repository.failToggle = false;
expect(await logic.toggleFavorite(), isTrue);
expect(logic.adjustedFavoriteCount, 2);
logic.dispose();
});
test('should_decrement_favorite_count_from_saved_state', () async {
final repository = InMemoryFavoriteRepository(
saved: {'favorite-detail'},
cards: [_favoriteDrama],
);
final favorites = FavoriteController(repository);
await favorites.load();
final logic = _logic(
_favoriteDrama,
FakeWatchHistory(),
favorites: favorites,
);
expect(await logic.toggleFavorite(), isTrue);
expect(logic.adjustedFavoriteCount, 1);
logic.dispose();
});
}
const _favoriteDrama = Drama(
id: 'favorite-detail',
title: 'Favorite detail',
synopsis: 'favorite count',
posterUrl: '/files/favorite-detail.jpg',
favoriteCount: 2,
detailLoaded: true,
);