mumbullet/test/audio/downloader_simple_test.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');
}