import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:minstrel/api/endpoints/quarantine.dart'; import 'package:minstrel/models/quarantine_mine.dart'; import 'package:minstrel/models/track.dart'; import 'package:minstrel/quarantine/quarantine_provider.dart'; class _StubQuarantineApi implements QuarantineApi { _StubQuarantineApi({this.shouldThrow = false}); bool shouldThrow; int flagCalls = 0; int unflagCalls = 0; @override Future flag(String trackId, String reason, {String notes = ''}) async { flagCalls++; if (shouldThrow) throw Exception('simulated server failure'); } @override Future unflag(String trackId) async { unflagCalls++; if (shouldThrow) throw Exception('simulated server failure'); } } class _StubController extends MyQuarantineController { _StubController(this._initial); final List _initial; @override Future> build() async => _initial; } const _track = TrackRef( id: 't1', title: 'Roygbiv', albumId: 'a1', albumTitle: 'Geogaddi', artistId: 'ar1', artistName: 'Boards of Canada', durationSec: 137, trackNumber: 4, streamUrl: '', ); const _existing = QuarantineMineRow( trackId: 't1', reason: 'bad_rip', notes: null, createdAt: '2026-05-01T00:00:00Z', trackTitle: 'Roygbiv', trackDurationMs: 137000, albumId: 'a1', albumTitle: 'Geogaddi', artistId: 'ar1', artistName: 'Boards of Canada', ); void main() { test('flag prepends synthetic row and calls server', () async { final api = _StubQuarantineApi(); final container = ProviderContainer(overrides: [ quarantineApiProvider.overrideWith((ref) async => api), myQuarantineProvider.overrideWith(() => _StubController(const [])), ]); addTearDown(container.dispose); await container.read(myQuarantineProvider.future); await container.read(myQuarantineProvider.notifier).flag(_track, 'bad_rip', ''); expect(api.flagCalls, 1); final list = container.read(myQuarantineProvider).value!; expect(list, hasLength(1)); expect(list.first.trackId, 't1'); expect(list.first.reason, 'bad_rip'); }); test('flag rolls back on server failure', () async { final api = _StubQuarantineApi(shouldThrow: true); final container = ProviderContainer(overrides: [ quarantineApiProvider.overrideWith((ref) async => api), myQuarantineProvider.overrideWith(() => _StubController(const [])), ]); addTearDown(container.dispose); await container.read(myQuarantineProvider.future); await expectLater( () => container .read(myQuarantineProvider.notifier) .flag(_track, 'bad_rip', ''), throwsException, ); expect(container.read(myQuarantineProvider).value, isEmpty); }); test('unflag removes the row and calls server', () async { final api = _StubQuarantineApi(); final container = ProviderContainer(overrides: [ quarantineApiProvider.overrideWith((ref) async => api), myQuarantineProvider.overrideWith(() => _StubController(const [_existing])), ]); addTearDown(container.dispose); await container.read(myQuarantineProvider.future); await container.read(myQuarantineProvider.notifier).unflag('t1'); expect(api.unflagCalls, 1); expect(container.read(myQuarantineProvider).value, isEmpty); }); test('isHidden reflects current state', () async { final container = ProviderContainer(overrides: [ myQuarantineProvider.overrideWith(() => _StubController(const [_existing])), ]); addTearDown(container.dispose); await container.read(myQuarantineProvider.future); final ctrl = container.read(myQuarantineProvider.notifier); expect(ctrl.isHidden('t1'), isTrue); expect(ctrl.isHidden('t2'), isFalse); }); }