// Author: fruit.com.cn import 'package:flutter_test/flutter_test.dart'; import 'package:fruitshorts/core/data/services/playback_position_cache.dart'; void main() { test('should_clamp_invalid_or_out_of_range_position_when_caching', () { expect( clampPlaybackPosition( const Duration(seconds: -1), const Duration(seconds: 10), ), Duration.zero, ); expect( clampPlaybackPosition( const Duration(seconds: 12), const Duration(seconds: 10), ), const Duration(seconds: 10), ); expect( clampPlaybackPosition( const Duration(seconds: 5), const Duration(seconds: 10), ), const Duration(seconds: 5), ); final cache = PlaybackPositionCache(); cache.save( episodeId: 'episode-3', dramaId: 'drama-a', position: const Duration(minutes: 4), duration: const Duration(minutes: 3), ); expect( cache.read('episode-3', dramaId: 'drama-a')?.position, const Duration(minutes: 3), ); }); test('should_store_and_read_recent_position_when_drama_matches', () { final now = DateTime(2026, 10, 22, 12); final cache = PlaybackPositionCache(now: () => now); cache.save( episodeId: 'episode-3', dramaId: 'drama-a', position: const Duration(minutes: 1, seconds: 20), duration: const Duration(minutes: 3), ); final entry = cache.read('episode-3', dramaId: 'drama-a'); expect(entry, isNotNull); expect(entry!.position, const Duration(minutes: 1, seconds: 20)); expect(entry.duration, const Duration(minutes: 3)); expect(entry.updatedAt, now); }); test('should_reject_cached_position_when_drama_does_not_match', () { final cache = PlaybackPositionCache(); cache.save( episodeId: 'episode-3', dramaId: 'drama-a', position: const Duration(seconds: 80), duration: const Duration(minutes: 3), ); expect(cache.read('episode-3', dramaId: 'drama-b'), isNull); }); test('should_use_latest_saved_episode_when_timestamps_match', () { final now = DateTime(2026, 10, 22, 12); final cache = PlaybackPositionCache(now: () => now); cache.save( episodeId: 'episode-1', dramaId: 'drama-a', position: const Duration(seconds: 30), duration: const Duration(minutes: 3), ); cache.save( episodeId: 'episode-2', dramaId: 'drama-a', position: const Duration(seconds: 45), duration: const Duration(minutes: 3), ); expect(cache.latestForDrama('drama-a')?.episodeId, 'episode-2'); }); test('should_evict_oldest_entry_when_capacity_exceeded', () { var now = DateTime(2026, 10, 22, 12); final cache = PlaybackPositionCache(now: () => now, maxEntries: 2); for (final episodeId in ['episode-1', 'episode-2', 'episode-3']) { cache.save( episodeId: episodeId, dramaId: 'drama-a', position: const Duration(seconds: 30), duration: const Duration(minutes: 3), ); now = now.add(const Duration(minutes: 1)); } expect(cache.read('episode-1', dramaId: 'drama-a'), isNull); expect(cache.read('episode-2', dramaId: 'drama-a'), isNotNull); expect(cache.read('episode-3', dramaId: 'drama-a'), isNotNull); }); test('should_reject_expired_cached_position', () { var now = DateTime(2026, 10, 22, 12); final cache = PlaybackPositionCache(now: () => now); cache.save( episodeId: 'episode-3', dramaId: 'drama-a', position: const Duration(seconds: 80), duration: const Duration(minutes: 3), ); now = now.add(const Duration(days: 1)); expect(cache.read('episode-3', dramaId: 'drama-a'), isNull); }); }