mirror of
https://gitlab.com/foxixus/neomovies_mobile.git
synced 2025-10-27 19:58:50 +05:00
WebView Player Integration: - Create PlayerEmbedService for API server integration - Update WebView players to use server embed URLs instead of direct links - Add fallback to direct URLs when server is unavailable - Support for both Vibix and Alloha players with server API - Include optional parameters (imdbId, season, episode) for TV shows - Add health check endpoint for server availability Mock Testing Infrastructure: - Add comprehensive TorrentPlatformService tests with mock platform channel - Test all torrent operations without requiring real Android engine - Mock platform channel responses for addTorrent, removeTorrent, pauseTorrent, resumeTorrent - Test error handling with PlatformException simulation - Validate torrent state detection (downloading, seeding, completed) - Test file priority management and video file detection PlayerEmbedService Testing: - Mock HTTP client tests for Vibix and Alloha embed URL generation - Test server API integration with success and failure scenarios - Validate URL encoding for special characters and non-ASCII titles - Test fallback behavior when server is unavailable or times out - Mock player configuration retrieval from server - Test server health check functionality Test Dependencies: - Add http_mock_adapter for HTTP testing - Ensure all tests work without real Flutter/Android environment - Support for testing platform channels and HTTP services This enables proper API server integration for WebView players while maintaining comprehensive test coverage for all torrent and player functionality without requiring Android hardware.
331 lines
11 KiB
Dart
331 lines
11 KiB
Dart
import 'dart:convert';
|
|
|
|
import 'package:flutter/services.dart';
|
|
import 'package:flutter_test/flutter_test.dart';
|
|
import 'package:neomovies_mobile/data/models/torrent_info.dart';
|
|
import 'package:neomovies_mobile/data/services/torrent_platform_service.dart';
|
|
|
|
void main() {
|
|
group('TorrentPlatformService Tests', () {
|
|
late TorrentPlatformService service;
|
|
late List<MethodCall> methodCalls;
|
|
|
|
setUp(() {
|
|
service = TorrentPlatformService();
|
|
methodCalls = [];
|
|
|
|
// Mock the platform channel
|
|
TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger
|
|
.setMockMethodCallHandler(
|
|
const MethodChannel('com.neo.neomovies_mobile/torrent'),
|
|
(MethodCall methodCall) async {
|
|
methodCalls.add(methodCall);
|
|
return _handleMethodCall(methodCall);
|
|
},
|
|
);
|
|
});
|
|
|
|
tearDown(() {
|
|
TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger
|
|
.setMockMethodCallHandler(
|
|
const MethodChannel('com.neo.neomovies_mobile/torrent'),
|
|
null,
|
|
);
|
|
});
|
|
|
|
group('Torrent Management', () {
|
|
test('addTorrent should call Android method with correct parameters', () async {
|
|
const magnetUri = 'magnet:?xt=urn:btih:test123&dn=test.movie.mkv';
|
|
const downloadPath = '/storage/emulated/0/Download/Torrents';
|
|
|
|
await service.addTorrent(magnetUri, downloadPath);
|
|
|
|
expect(methodCalls.length, 1);
|
|
expect(methodCalls.first.method, 'addTorrent');
|
|
expect(methodCalls.first.arguments, {
|
|
'magnetUri': magnetUri,
|
|
'downloadPath': downloadPath,
|
|
});
|
|
});
|
|
|
|
test('removeTorrent should call Android method with torrent hash', () async {
|
|
const torrentHash = 'abc123def456';
|
|
|
|
await service.removeTorrent(torrentHash);
|
|
|
|
expect(methodCalls.length, 1);
|
|
expect(methodCalls.first.method, 'removeTorrent');
|
|
expect(methodCalls.first.arguments, {'torrentHash': torrentHash});
|
|
});
|
|
|
|
test('pauseTorrent should call Android method with torrent hash', () async {
|
|
const torrentHash = 'abc123def456';
|
|
|
|
await service.pauseTorrent(torrentHash);
|
|
|
|
expect(methodCalls.length, 1);
|
|
expect(methodCalls.first.method, 'pauseTorrent');
|
|
expect(methodCalls.first.arguments, {'torrentHash': torrentHash});
|
|
});
|
|
|
|
test('resumeTorrent should call Android method with torrent hash', () async {
|
|
const torrentHash = 'abc123def456';
|
|
|
|
await service.resumeTorrent(torrentHash);
|
|
|
|
expect(methodCalls.length, 1);
|
|
expect(methodCalls.first.method, 'resumeTorrent');
|
|
expect(methodCalls.first.arguments, {'torrentHash': torrentHash});
|
|
});
|
|
});
|
|
|
|
group('Torrent Information', () {
|
|
test('getAllTorrents should return list of TorrentInfo objects', () async {
|
|
final torrents = await service.getAllTorrents();
|
|
|
|
expect(methodCalls.length, 1);
|
|
expect(methodCalls.first.method, 'getAllTorrents');
|
|
expect(torrents, isA<List<TorrentInfo>>());
|
|
expect(torrents.length, 2); // Based on mock data
|
|
|
|
final firstTorrent = torrents.first;
|
|
expect(firstTorrent.name, 'Test Movie 1080p.mkv');
|
|
expect(firstTorrent.infoHash, 'abc123def456');
|
|
expect(firstTorrent.state, 'downloading');
|
|
expect(firstTorrent.progress, 0.65);
|
|
});
|
|
|
|
test('getTorrentInfo should return specific torrent information', () async {
|
|
const torrentHash = 'abc123def456';
|
|
|
|
final torrent = await service.getTorrentInfo(torrentHash);
|
|
|
|
expect(methodCalls.length, 1);
|
|
expect(methodCalls.first.method, 'getTorrentInfo');
|
|
expect(methodCalls.first.arguments, {'torrentHash': torrentHash});
|
|
expect(torrent, isA<TorrentInfo>());
|
|
expect(torrent?.infoHash, torrentHash);
|
|
});
|
|
});
|
|
|
|
group('File Priority Management', () {
|
|
test('setFilePriority should call Android method with correct parameters', () async {
|
|
const torrentHash = 'abc123def456';
|
|
const fileIndex = 0;
|
|
const priority = FilePriority.high;
|
|
|
|
await service.setFilePriority(torrentHash, fileIndex, priority);
|
|
|
|
expect(methodCalls.length, 1);
|
|
expect(methodCalls.first.method, 'setFilePriority');
|
|
expect(methodCalls.first.arguments, {
|
|
'torrentHash': torrentHash,
|
|
'fileIndex': fileIndex,
|
|
'priority': priority.value,
|
|
});
|
|
});
|
|
|
|
test('getFilePriorities should return list of priorities', () async {
|
|
const torrentHash = 'abc123def456';
|
|
|
|
final priorities = await service.getFilePriorities(torrentHash);
|
|
|
|
expect(methodCalls.length, 1);
|
|
expect(methodCalls.first.method, 'getFilePriorities');
|
|
expect(methodCalls.first.arguments, {'torrentHash': torrentHash});
|
|
expect(priorities, isA<List<FilePriority>>());
|
|
expect(priorities.length, 3); // Based on mock data
|
|
});
|
|
});
|
|
|
|
group('Error Handling', () {
|
|
test('should handle PlatformException gracefully', () async {
|
|
// Override mock to throw exception
|
|
TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger
|
|
.setMockMethodCallHandler(
|
|
const MethodChannel('com.neo.neomovies_mobile/torrent'),
|
|
(MethodCall methodCall) async {
|
|
throw PlatformException(
|
|
code: 'TORRENT_ERROR',
|
|
message: 'Failed to add torrent',
|
|
details: 'Invalid magnet URI',
|
|
);
|
|
},
|
|
);
|
|
|
|
expect(
|
|
() => service.addTorrent('invalid-magnet', '/path'),
|
|
throwsA(isA<PlatformException>()),
|
|
);
|
|
});
|
|
|
|
test('should handle null response from platform', () async {
|
|
// Override mock to return null
|
|
TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger
|
|
.setMockMethodCallHandler(
|
|
const MethodChannel('com.neo.neomovies_mobile/torrent'),
|
|
(MethodCall methodCall) async => null,
|
|
);
|
|
|
|
final result = await service.getTorrentInfo('nonexistent');
|
|
expect(result, isNull);
|
|
});
|
|
});
|
|
|
|
group('State Management', () {
|
|
test('torrent states should be correctly identified', () async {
|
|
final torrents = await service.getAllTorrents();
|
|
|
|
// Find torrents with different states
|
|
final downloadingTorrent = torrents.firstWhere(
|
|
(t) => t.state == 'downloading',
|
|
);
|
|
final seedingTorrent = torrents.firstWhere(
|
|
(t) => t.state == 'seeding',
|
|
);
|
|
|
|
expect(downloadingTorrent.isDownloading, isTrue);
|
|
expect(downloadingTorrent.isSeeding, isFalse);
|
|
expect(downloadingTorrent.isCompleted, isFalse);
|
|
|
|
expect(seedingTorrent.isDownloading, isFalse);
|
|
expect(seedingTorrent.isSeeding, isTrue);
|
|
expect(seedingTorrent.isCompleted, isTrue);
|
|
});
|
|
|
|
test('progress calculation should be accurate', () async {
|
|
final torrents = await service.getAllTorrents();
|
|
final torrent = torrents.first;
|
|
|
|
expect(torrent.progress, inInclusiveRange(0.0, 1.0));
|
|
expect(torrent.formattedProgress, '65%');
|
|
});
|
|
});
|
|
|
|
group('Video File Detection', () {
|
|
test('should identify video files correctly', () async {
|
|
final torrents = await service.getAllTorrents();
|
|
final torrent = torrents.first;
|
|
|
|
final videoFiles = torrent.videoFiles;
|
|
expect(videoFiles.isNotEmpty, isTrue);
|
|
|
|
final videoFile = videoFiles.first;
|
|
expect(videoFile.name.toLowerCase(), contains('.mkv'));
|
|
expect(videoFile.isVideo, isTrue);
|
|
});
|
|
|
|
test('should find main video file', () async {
|
|
final torrents = await service.getAllTorrents();
|
|
final torrent = torrents.first;
|
|
|
|
final mainFile = torrent.mainVideoFile;
|
|
expect(mainFile, isNotNull);
|
|
expect(mainFile!.isVideo, isTrue);
|
|
expect(mainFile.size, greaterThan(0));
|
|
});
|
|
});
|
|
});
|
|
}
|
|
|
|
/// Mock method call handler for torrent platform channel
|
|
dynamic _handleMethodCall(MethodCall methodCall) {
|
|
switch (methodCall.method) {
|
|
case 'addTorrent':
|
|
return {'success': true, 'torrentHash': 'abc123def456'};
|
|
|
|
case 'removeTorrent':
|
|
case 'pauseTorrent':
|
|
case 'resumeTorrent':
|
|
return {'success': true};
|
|
|
|
case 'getAllTorrents':
|
|
return _getMockTorrentsData();
|
|
|
|
case 'getTorrentInfo':
|
|
final hash = methodCall.arguments['torrentHash'] as String;
|
|
final torrents = _getMockTorrentsData();
|
|
return torrents.firstWhere(
|
|
(t) => t['infoHash'] == hash,
|
|
orElse: () => null,
|
|
);
|
|
|
|
case 'setFilePriority':
|
|
return {'success': true};
|
|
|
|
case 'getFilePriorities':
|
|
return [
|
|
FilePriority.high.value,
|
|
FilePriority.normal.value,
|
|
FilePriority.low.value,
|
|
];
|
|
|
|
default:
|
|
throw PlatformException(
|
|
code: 'UNIMPLEMENTED',
|
|
message: 'Method ${methodCall.method} not implemented',
|
|
);
|
|
}
|
|
}
|
|
|
|
/// Mock torrents data for testing
|
|
List<Map<String, dynamic>> _getMockTorrentsData() {
|
|
return [
|
|
{
|
|
'name': 'Test Movie 1080p.mkv',
|
|
'infoHash': 'abc123def456',
|
|
'state': 'downloading',
|
|
'progress': 0.65,
|
|
'downloadSpeed': 2500000, // 2.5 MB/s
|
|
'uploadSpeed': 800000, // 800 KB/s
|
|
'totalSize': 4294967296, // 4 GB
|
|
'downloadedSize': 2791728742, // ~2.6 GB
|
|
'seeders': 15,
|
|
'leechers': 8,
|
|
'ratio': 1.2,
|
|
'addedTime': DateTime.now().subtract(const Duration(hours: 2)).millisecondsSinceEpoch,
|
|
'files': [
|
|
{
|
|
'name': 'Test Movie 1080p.mkv',
|
|
'size': 4294967296,
|
|
'path': '/storage/emulated/0/Download/Torrents/Test Movie 1080p.mkv',
|
|
'priority': FilePriority.high.value,
|
|
},
|
|
{
|
|
'name': 'subtitle.srt',
|
|
'size': 65536,
|
|
'path': '/storage/emulated/0/Download/Torrents/subtitle.srt',
|
|
'priority': FilePriority.normal.value,
|
|
},
|
|
{
|
|
'name': 'NFO.txt',
|
|
'size': 2048,
|
|
'path': '/storage/emulated/0/Download/Torrents/NFO.txt',
|
|
'priority': FilePriority.low.value,
|
|
},
|
|
],
|
|
},
|
|
{
|
|
'name': 'Another Movie 720p',
|
|
'infoHash': 'def456ghi789',
|
|
'state': 'seeding',
|
|
'progress': 1.0,
|
|
'downloadSpeed': 0,
|
|
'uploadSpeed': 500000, // 500 KB/s
|
|
'totalSize': 2147483648, // 2 GB
|
|
'downloadedSize': 2147483648,
|
|
'seeders': 25,
|
|
'leechers': 3,
|
|
'ratio': 2.5,
|
|
'addedTime': DateTime.now().subtract(const Duration(days: 1)).millisecondsSinceEpoch,
|
|
'files': [
|
|
{
|
|
'name': 'Another Movie 720p.mp4',
|
|
'size': 2147483648,
|
|
'path': '/storage/emulated/0/Download/Torrents/Another Movie 720p.mp4',
|
|
'priority': FilePriority.high.value,
|
|
},
|
|
],
|
|
},
|
|
];
|
|
} |