@Tags(['drift']) library; import 'dart:io'; import 'package:dio/dio.dart'; import 'package:drift/native.dart' show NativeDatabase; import 'package:flutter_test/flutter_test.dart'; import 'package:minstrel/cache/audio_cache_manager.dart'; import 'package:minstrel/cache/db.dart'; // See sync_controller_test.dart for the same skip rationale. const _skipDrift = 'libsqlite3 missing on flutter-ci runner; on-device covers'; AppDb _testDb() => AppDb(NativeDatabase.memory()); void main() { test('isCached returns false when no row exists', skip: _skipDrift, () async { final db = _testDb(); addTearDown(db.close); final mgr = AudioCacheManager( db: db, dioFactory: () async => Dio(), cacheDirFactory: () async => Directory.systemTemp.createTempSync(), ); expect(await mgr.isCached('nonexistent'), false); expect(await mgr.pathFor('nonexistent'), null); }); test('usageBytes sums sizeBytes across rows', skip: _skipDrift, () async { final db = _testDb(); addTearDown(db.close); final tmp = Directory.systemTemp.createTempSync(); final mgr = AudioCacheManager( db: db, dioFactory: () async => Dio(), cacheDirFactory: () async => tmp, ); await db.batch((b) { b.insertAll(db.audioCacheIndex, [ AudioCacheIndexCompanion.insert( trackId: 'a', path: 'p', sizeBytes: 100, source: CacheSource.manual), AudioCacheIndexCompanion.insert( trackId: 'b', path: 'p', sizeBytes: 250, source: CacheSource.incidental), ]); }); expect(await mgr.usageBytes(), 350); }); test('eviction order: incidental first, manual never', skip: _skipDrift, () async { final db = _testDb(); addTearDown(db.close); final tmp = Directory.systemTemp.createTempSync(); final mgr = AudioCacheManager( db: db, dioFactory: () async => Dio(), cacheDirFactory: () async => tmp, ); final f1 = File('${tmp.path}/audio_cache/inc.mp3'); await f1.create(recursive: true); await f1.writeAsBytes(List.filled(100, 0)); final f2 = File('${tmp.path}/audio_cache/man.mp3'); await f2.create(recursive: true); await f2.writeAsBytes(List.filled(100, 0)); await db.batch((b) { b.insertAll(db.audioCacheIndex, [ AudioCacheIndexCompanion.insert( trackId: 'inc', path: f1.path, sizeBytes: 100, source: CacheSource.incidental), AudioCacheIndexCompanion.insert( trackId: 'man', path: f2.path, sizeBytes: 100, source: CacheSource.manual), ]); }); expect(await mgr.usageBytes(), 200); await mgr.evict(targetBytes: 100); expect(await mgr.isCached('inc'), false); expect(await mgr.isCached('man'), true); }); test('clearAll removes everything including manual', skip: _skipDrift, () async { final db = _testDb(); addTearDown(db.close); final tmp = Directory.systemTemp.createTempSync(); final mgr = AudioCacheManager( db: db, dioFactory: () async => Dio(), cacheDirFactory: () async => tmp, ); final f = File('${tmp.path}/audio_cache/man.mp3'); await f.create(recursive: true); await f.writeAsBytes(List.filled(50, 0)); await db.into(db.audioCacheIndex).insertOnConflictUpdate( AudioCacheIndexCompanion.insert( trackId: 'man', path: f.path, sizeBytes: 50, source: CacheSource.manual), ); expect(await mgr.usageBytes(), 50); await mgr.clearAll(); expect(await mgr.usageBytes(), 0); expect(await mgr.isCached('man'), false); }); test('unpin removes index row + deletes file', skip: _skipDrift, () async { final db = _testDb(); addTearDown(db.close); final tmp = Directory.systemTemp.createTempSync(); final mgr = AudioCacheManager( db: db, dioFactory: () async => Dio(), cacheDirFactory: () async => tmp, ); final f = File('${tmp.path}/audio_cache/x.mp3'); await f.create(recursive: true); await f.writeAsBytes(List.filled(10, 0)); await db.into(db.audioCacheIndex).insertOnConflictUpdate( AudioCacheIndexCompanion.insert( trackId: 'x', path: f.path, sizeBytes: 10, source: CacheSource.autoPrefetch), ); expect(await mgr.isCached('x'), true); await mgr.unpin('x'); expect(await mgr.isCached('x'), false); expect(f.existsSync(), false); }); }