import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:mocktail/mocktail.dart'; import 'package:todo_app/features/todo/domain/entities/todo.dart'; import 'package:todo_app/features/todo/domain/entities/create_todo_params.dart'; import 'package:todo_app/features/todo/domain/repositories/todo_repository.dart'; import 'package:todo_app/features/todo/presentation/providers/todo_providers.dart'; class MockTodoRepository extends Mock implements TodoRepository {} class FakeCreateTodoParams extends Fake implements CreateTodoParams {} void main() { late MockTodoRepository repository; late ProviderContainer container; final testTodo = Todo( id: '1', title: 'Test Todo', priority: 'medium', sortOrder: 0, createdAt: DateTime(2026, 1, 1), updatedAt: DateTime(2026, 1, 1), ); setUpAll(() { registerFallbackValue(FakeCreateTodoParams()); }); setUp(() { repository = MockTodoRepository(); container = ProviderContainer(overrides: [ todoRepositoryProvider.overrideWithValue(repository), ]); addTearDown(container.dispose); }); group('todoListProvider', () { test('builds with todo list from repository', () async { when(() => repository.getTodos( priorityFilter: any(named: 'priorityFilter'), completedFilter: any(named: 'completedFilter'), )).thenAnswer((_) async => [testTodo]); await container.read(todoListProvider.future); expect(container.read(todoListProvider).value, equals([testTodo])); }); test('handles error state', () async { when(() => repository.getTodos( priorityFilter: any(named: 'priorityFilter'), completedFilter: any(named: 'completedFilter'), )).thenThrow(Exception('Failed')); try { await container.read(todoListProvider.future); fail('Should have thrown'); } catch (_) { expect(container.read(todoListProvider).hasError, isTrue); } }); }); group('todoDetailProvider', () { test('returns todo by id', () async { when(() => repository.getTodo('1')).thenAnswer((_) async => testTodo); final result = await container.read(todoDetailProvider('1').future); expect(result, equals(testTodo)); }); }); group('TodoListNotifier.createTodo', () { test('creates todo and refreshes list', () async { when(() => repository.getTodos( priorityFilter: any(named: 'priorityFilter'), completedFilter: any(named: 'completedFilter'), )).thenAnswer((_) async => [testTodo]); when(() => repository.createTodo(any())).thenAnswer((_) async => testTodo); await container.read(todoListProvider.notifier).createTodo( const CreateTodoParams(title: 'New'), ); verify(() => repository.createTodo(any())).called(1); }); }); group('TodoListNotifier.toggleTodo', () { test('toggles and refreshes', () async { when(() => repository.getTodos( priorityFilter: any(named: 'priorityFilter'), completedFilter: any(named: 'completedFilter'), )).thenAnswer((_) async => [testTodo]); when(() => repository.toggleTodo('1')).thenAnswer((_) async => testTodo); await container.read(todoListProvider.notifier).toggleTodo('1'); verify(() => repository.toggleTodo('1')).called(1); }); }); group('TodoListNotifier.deleteTodo', () { test('deletes and refreshes', () async { when(() => repository.getTodos( priorityFilter: any(named: 'priorityFilter'), completedFilter: any(named: 'completedFilter'), )).thenAnswer((_) async => []); when(() => repository.deleteTodo('1')).thenAnswer((_) async {}); await container.read(todoListProvider.notifier).deleteTodo('1'); verify(() => repository.deleteTodo('1')).called(1); }); }); }