Files
ReelShort/test/download/download_controller_local_test.dart

150 lines
4.1 KiB
Dart

// Author: fruit.com.cn
import 'package:flutter_test/flutter_test.dart';
import 'package:fruitshorts/core/data/models/download_license.dart';
import 'package:fruitshorts/core/data/models/download_policy.dart';
import 'package:fruitshorts/core/data/repositories/download_repository.dart';
import 'package:fruitshorts/core/data/services/download_controller.dart';
import 'package:fruitshorts/core/data/services/download_storage.dart';
void main() {
test('should_mark_episode_downloaded_and_report_progress_when_download_finishes',
() async {
final repository = _RecordingRepository(_license('ep-1'));
final storage = _FakeStorage();
final controller = DownloadController(repository, storage: storage);
await controller.load();
final result = await controller.startDownload('ep-1');
expect(result.ok, isTrue);
expect(controller.isDownloaded('ep-1'), isTrue);
expect(controller.localPathFor('ep-1'), 'file:///tmp/ep-1.mp4');
expect(repository.statuses, containsAll(['DOWNLOADING', 'COMPLETED']));
});
test('should_keep_local_file_when_backend_delete_fails', () async {
final repository = _RecordingRepository(_license('ep-1'));
final storage = _FakeStorage();
final controller = DownloadController(repository, storage: storage);
await controller.load();
await controller.startDownload('ep-1');
repository.failRemove = true;
final ok = await controller.remove('ep-1');
expect(ok, isFalse);
expect(controller.isDownloaded('ep-1'), isTrue);
});
}
DownloadLicense _license(String episodeId) => DownloadLicense(
licenseId: 'license-$episodeId',
dramaId: 'drama-1',
dramaTitle: 'The Signal',
episodeId: episodeId,
episodeNo: 1,
deviceId: 'device-1',
quality: 'AUTO',
playUrl: '/files/episode/ep-1.mp4?e=1&s=abc',
licenseKey: 'key',
status: 'ACTIVE',
grantedAt: DateTime(2026, 10, 19),
expireAt: DateTime(2026, 11, 18),
taskStatus: 'QUEUED',
progressPercent: 0,
downloadedBytes: 0,
totalBytes: 0,
errorMsg: '',
);
class _FakeStorage implements DownloadStorage {
final Set<String> files = {};
@override
Future<Map<String, String>> restore() async => {
for (final id in files) id: 'file:///tmp/$id.mp4',
};
@override
Future<String?> localPathFor(String episodeId) async =>
files.contains(episodeId) ? 'file:///tmp/$episodeId.mp4' : null;
@override
Future<DownloadedMedia> download({
required String episodeId,
required String url,
DownloadProgress? onProgress,
}) async {
onProgress?.call(50, 100);
onProgress?.call(100, 100);
files.add(episodeId);
return DownloadedMedia(
episodeId: episodeId,
filePath: 'file:///tmp/$episodeId.mp4',
bytes: 100,
);
}
@override
Future<void> delete(String episodeId) async => files.remove(episodeId);
@override
Future<void> clear() async => files.clear();
}
class _RecordingRepository implements DownloadRepository {
_RecordingRepository(this.license);
DownloadLicense license;
bool failRemove = false;
final List<String> statuses = [];
@override
Future<DownloadPolicy> fetchPolicy() async => const DownloadPolicy(
maxPerDevice: 20,
licenseDays: 30,
wifiOnly: false,
maxQuality: 'AUTO',
maxStorageBytes: 0,
used: 1,
remaining: 19,
expirePolicyText: '30 days',
);
@override
Future<List<DownloadLicense>> list() async => [license];
@override
Future<DownloadLicense> grant(String episodeId, {String? quality}) async =>
license;
@override
Future<void> updateSettings({bool? wifiOnly, String? maxQuality}) async {}
@override
Future<DownloadLicense> refreshUrl(String episodeId) async => license;
@override
Future<DownloadLicense> reportProgress(
String episodeId, {
String? deviceId,
String? status,
int? progressPercent,
int? downloadedBytes,
int? totalBytes,
String? errorMsg,
}) async {
if (status != null) statuses.add(status);
return license;
}
@override
Future<void> remove(String episodeId) async {
if (failRemove) throw StateError('remove failed');
}
@override
Future<int> clear() async => 0;
}