diff --git a/flutter_client/test/likes/like_button_test.dart b/flutter_client/test/likes/like_button_test.dart index 5eb1ba9b..f17c7310 100644 --- a/flutter_client/test/likes/like_button_test.dart +++ b/flutter_client/test/likes/like_button_test.dart @@ -1,17 +1,31 @@ +import 'package:drift/native.dart' show NativeDatabase; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:minstrel/api/endpoints/likes.dart'; +import 'package:minstrel/auth/auth_provider.dart' show authControllerProvider, AuthController; +import 'package:minstrel/cache/audio_cache_manager.dart' show appDbProvider; +import 'package:minstrel/cache/connectivity_provider.dart'; +import 'package:minstrel/cache/db.dart'; import 'package:minstrel/likes/like_button.dart'; import 'package:minstrel/likes/likes_provider.dart'; import 'package:minstrel/models/album.dart'; import 'package:minstrel/models/artist.dart'; import 'package:minstrel/models/page.dart'; import 'package:minstrel/models/track.dart'; +import 'package:minstrel/models/user.dart'; import 'package:minstrel/shared/widgets/lucide_heart.dart'; import 'package:minstrel/theme/theme_data.dart'; +/// LikesController.toggle() early-returns if authControllerProvider has +/// no user, so the test needs a stub controller that yields one. +class _FakeAuthController extends AuthController { + @override + Future build() async => + const User(id: 'u1', username: 'tester', isAdmin: false); +} + class _ThrowingLikesApi implements LikesApi { bool throwOnNext = false; @@ -47,8 +61,19 @@ class _ThrowingLikesApi implements LikesApi { void main() { testWidgets('tap toggles icon optimistically; rollback on error', (tester) async { final api = _ThrowingLikesApi(); + // LikesController.toggle reaches authControllerProvider (for user.id), + // appDbProvider (for drift), and likedIdsProvider (which transitively + // reads connectivityProvider via cacheFirst). All four must be + // stubbed for the optimistic-update path to actually run. final container = ProviderContainer(overrides: [ likesApiProvider.overrideWith((ref) async => api), + authControllerProvider.overrideWith(_FakeAuthController.new), + appDbProvider.overrideWith((ref) { + final db = AppDb(NativeDatabase.memory()); + ref.onDispose(db.close); + return db; + }), + connectivityProvider.overrideWith((ref) => Stream.value(true)), ]); addTearDown(container.dispose); diff --git a/flutter_client/test/quarantine/quarantine_provider_test.dart b/flutter_client/test/quarantine/quarantine_provider_test.dart index d35691df..dc1c155e 100644 --- a/flutter_client/test/quarantine/quarantine_provider_test.dart +++ b/flutter_client/test/quarantine/quarantine_provider_test.dart @@ -4,6 +4,7 @@ import 'package:flutter_test/flutter_test.dart'; import 'package:minstrel/api/endpoints/quarantine.dart'; import 'package:minstrel/cache/audio_cache_manager.dart' show appDbProvider; +import 'package:minstrel/cache/connectivity_provider.dart'; import 'package:minstrel/cache/db.dart'; import 'package:minstrel/models/track.dart'; import 'package:minstrel/quarantine/quarantine_provider.dart'; @@ -43,6 +44,12 @@ ProviderContainer _container({required AppDb db, required QuarantineApi api}) { return ProviderContainer(overrides: [ appDbProvider.overrideWithValue(db), quarantineApiProvider.overrideWith((ref) async => api), + // _refreshFromServer() in build() reads connectivityProvider; in tests + // without this override it's a StreamProvider that never emits, so + // tearDown trips Riverpod's "disposed during loading" — visible on + // the throwing-API variant where the catch path's await mutationQueue + // .enqueue advances _refreshFromServer's chain enough to surface it. + connectivityProvider.overrideWith((ref) => Stream.value(true)), ]); } @@ -102,12 +109,6 @@ void main() { expect(mutations, hasLength(1), reason: 'failed flag should have been enqueued for replay'); expect(mutations.first.kind, 'quarantine.flag'); - // Let in-flight drift watch() streams settle before tearDown - // disposes the container — otherwise Riverpod's StreamProvider - // fires "disposed during loading state" on the queued-mutation - // path (the success path resolves before the awaits above - // complete, so it doesn't need this). - await Future.delayed(Duration.zero); }); test('unflag deletes the drift row and calls the server',