211 lines
7.1 KiB
Dart
211 lines
7.1 KiB
Dart
import 'dart:io';
|
|
import 'package:test/test.dart';
|
|
import 'package:path/path.dart' as path;
|
|
import 'package:mumbullet/src/audio/downloader.dart';
|
|
import 'package:mumbullet/src/config/config.dart';
|
|
import 'package:mumbullet/src/storage/database.dart';
|
|
|
|
void main() {
|
|
group('YoutubeDownloader Simple Integration Tests', () {
|
|
late YoutubeDownloader downloader;
|
|
late DatabaseManager database;
|
|
late BotConfig config;
|
|
late Directory tempDir;
|
|
late String tempDbPath;
|
|
|
|
setUpAll(() async {
|
|
// Check if yt-dlp is available
|
|
try {
|
|
final result = await Process.run('yt-dlp', ['--version']);
|
|
if (result.exitCode != 0) {
|
|
throw Exception('yt-dlp not available');
|
|
}
|
|
} catch (e) {
|
|
print('Skipping yt-dlp tests: yt-dlp not found in PATH');
|
|
return;
|
|
}
|
|
});
|
|
|
|
setUp(() async {
|
|
// Create temporary directory for cache
|
|
tempDir = await Directory.systemTemp.createTemp('mumbullet_test_');
|
|
|
|
// Create temporary database
|
|
tempDbPath = path.join(tempDir.path, 'test.db');
|
|
|
|
// Create test configuration
|
|
config = BotConfig(
|
|
commandPrefix: '!',
|
|
defaultPermissionLevel: 1,
|
|
maxQueueSize: 10,
|
|
cacheDirectory: tempDir.path,
|
|
maxCacheSizeGb: 0.1, // 100MB for testing
|
|
);
|
|
|
|
// Initialize database
|
|
database = DatabaseManager(tempDbPath);
|
|
|
|
// Create downloader
|
|
downloader = YoutubeDownloader(config, database);
|
|
});
|
|
|
|
tearDown(() async {
|
|
// Clean up
|
|
try {
|
|
database.close();
|
|
} catch (e) {
|
|
// Ignore close errors
|
|
}
|
|
if (tempDir.existsSync()) {
|
|
await tempDir.delete(recursive: true);
|
|
}
|
|
});
|
|
|
|
group('Basic Functionality', () {
|
|
test('should download a valid YouTube video and return Song object', () async {
|
|
const testUrl = 'https://www.youtube.com/watch?v=jNQXAC9IVRw'; // "Me at the zoo"
|
|
|
|
final song = await downloader.download(testUrl);
|
|
|
|
// Verify basic song properties
|
|
expect(song.url, equals(testUrl));
|
|
expect(song.title, isNotEmpty);
|
|
expect(song.title, isNot(equals('Unknown Title')));
|
|
expect(song.duration, greaterThan(0));
|
|
expect(song.filePath, isNotEmpty);
|
|
expect(song.id, greaterThan(0));
|
|
expect(song.addedAt, isNotNull);
|
|
|
|
// Verify file path structure
|
|
expect(song.filePath, startsWith(tempDir.path));
|
|
expect(song.filePath, endsWith('.wav'));
|
|
|
|
print('Downloaded: ${song.title} (${song.duration}s) -> ${song.filePath}');
|
|
}, timeout: Timeout(Duration(minutes: 2)));
|
|
|
|
test('should use cache on second download', () async {
|
|
const testUrl = 'https://www.youtube.com/watch?v=jNQXAC9IVRw';
|
|
|
|
// First download
|
|
final song1 = await downloader.download(testUrl);
|
|
|
|
// Second download (should use cache)
|
|
final song2 = await downloader.download(testUrl);
|
|
|
|
// Should return same song from cache
|
|
expect(song1.id, equals(song2.id));
|
|
expect(song1.title, equals(song2.title));
|
|
expect(song1.filePath, equals(song2.filePath));
|
|
|
|
print('Cache test passed: ${song1.title}');
|
|
}, timeout: Timeout(Duration(minutes: 3)));
|
|
|
|
test('should handle invalid URLs gracefully', () async {
|
|
final invalidUrls = [
|
|
'not-a-url',
|
|
'https://invalid-domain.com/video',
|
|
'https://www.youtube.com/watch?v=InvalidVideoId123',
|
|
];
|
|
|
|
for (final url in invalidUrls) {
|
|
try {
|
|
await downloader.download(url);
|
|
fail('Should have thrown exception for invalid URL: $url');
|
|
} catch (e) {
|
|
expect(e, isA<Exception>());
|
|
print('Correctly handled invalid URL: $url - ${e.toString()}');
|
|
}
|
|
}
|
|
});
|
|
|
|
test('should provide cache statistics', () async {
|
|
// Initially empty
|
|
final initialStats = await downloader.getCacheStats();
|
|
expect(initialStats['songCount'], equals(0));
|
|
|
|
// Download a video
|
|
const testUrl = 'https://www.youtube.com/watch?v=jNQXAC9IVRw';
|
|
await downloader.download(testUrl);
|
|
|
|
// Check updated stats
|
|
final updatedStats = await downloader.getCacheStats();
|
|
expect(updatedStats['songCount'], equals(1));
|
|
expect(updatedStats['totalSizeBytes'], greaterThan(0));
|
|
|
|
print('Cache stats: ${updatedStats['songCount']} songs, ${updatedStats['totalSizeMb']} MB');
|
|
}, timeout: Timeout(Duration(minutes: 2)));
|
|
|
|
test('should clear cache', () async {
|
|
// Download a video
|
|
const testUrl = 'https://www.youtube.com/watch?v=jNQXAC9IVRw';
|
|
await downloader.download(testUrl);
|
|
|
|
// Verify cache has content
|
|
final statsBefore = await downloader.getCacheStats();
|
|
expect(statsBefore['songCount'], greaterThan(0));
|
|
|
|
// Clear cache
|
|
await downloader.clearCache();
|
|
|
|
// Verify cache is empty
|
|
final statsAfter = await downloader.getCacheStats();
|
|
expect(statsAfter['songCount'], equals(0));
|
|
expect(statsAfter['totalSizeBytes'], equals(0));
|
|
|
|
print('Cache cleared successfully');
|
|
}, timeout: Timeout(Duration(minutes: 2)));
|
|
});
|
|
|
|
group('URL Format Variations', () {
|
|
test('should handle different YouTube URL formats', () async {
|
|
final testUrls = [
|
|
'https://www.youtube.com/watch?v=jNQXAC9IVRw',
|
|
'https://youtu.be/jNQXAC9IVRw',
|
|
];
|
|
|
|
String? firstTitle;
|
|
|
|
for (final url in testUrls) {
|
|
final song = await downloader.download(url);
|
|
|
|
expect(song.title, isNotEmpty);
|
|
expect(song.duration, greaterThan(0));
|
|
|
|
if (firstTitle == null) {
|
|
firstTitle = song.title;
|
|
} else {
|
|
// All URLs should resolve to the same video
|
|
expect(song.title, equals(firstTitle));
|
|
}
|
|
|
|
print('URL format test: $url -> ${song.title}');
|
|
}
|
|
}, timeout: Timeout(Duration(minutes: 3)));
|
|
});
|
|
|
|
group('Edge Cases', () {
|
|
test('should handle re-download when cached file is missing', () async {
|
|
const testUrl = 'https://www.youtube.com/watch?v=jNQXAC9IVRw';
|
|
|
|
// Download a video
|
|
final song1 = await downloader.download(testUrl);
|
|
final originalPath = song1.filePath;
|
|
|
|
// Manually delete the file (simulating external deletion)
|
|
if (File(originalPath).existsSync()) {
|
|
await File(originalPath).delete();
|
|
}
|
|
|
|
// Try to download again - should re-download
|
|
final song2 = await downloader.download(testUrl);
|
|
|
|
// Should have same title but may have different file path
|
|
expect(song2.title, equals(song1.title));
|
|
expect(song2.filePath, isNotEmpty);
|
|
|
|
print('Re-download test passed: ${song2.title}');
|
|
}, timeout: Timeout(Duration(minutes: 3)));
|
|
});
|
|
}, skip: Platform.environment['SKIP_INTEGRATION_TESTS'] == 'true');
|
|
}
|