import 'package:dio/dio.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:mocktail/mocktail.dart'; import 'package:social_app/features/todo/data/todo_api.dart'; import 'package:social_app/core/api/i_api_client.dart'; class MockApiClient extends Mock implements IApiClient {} class FakeRequestOptions extends Fake implements RequestOptions {} void main() { late TodoApi todoApi; late MockApiClient mockClient; setUpAll(() { registerFallbackValue(FakeRequestOptions()); }); setUp(() { mockClient = MockApiClient(); todoApi = TodoApi(mockClient); }); group('TodoApi.updateTodo - cross-quadrant drag', () { test( 'calls PATCH with priority when moving to different quadrant', () async { const todoId = 'todo-123'; const targetPriority = 2; const targetOrder = 0; when( () => mockClient.patch(any(), data: any(named: 'data')), ).thenAnswer( (_) async => Response( requestOptions: RequestOptions(path: '/api/v1/todos/$todoId'), data: { 'id': todoId, 'owner_id': 'user-1', 'title': 'Test Todo', 'priority': targetPriority, 'order': targetOrder, 'status': 'pending', 'created_at': '2024-01-01T00:00:00Z', 'updated_at': '2024-01-01T00:00:00Z', }, ), ); final result = await todoApi.updateTodo( todoId, priority: targetPriority, order: targetOrder, ); expect(result.priority, targetPriority); expect(result.order, targetOrder); verify( () => mockClient.patch( '/api/v1/todos/$todoId', data: {'priority': targetPriority, 'order': targetOrder}, ), ).called(1); }, ); test('throws when API fails - triggers rollback', () async { const todoId = 'todo-123'; when( () => mockClient.patch(any(), data: any(named: 'data')), ).thenThrow(Exception('Network error')); expect( () => todoApi.updateTodo(todoId, priority: 2, order: 0), throwsException, ); }); }); group('Quadrant priority mapping', () { test('priority 1 = important urgent (Q1)', () async { when(() => mockClient.patch(any(), data: any(named: 'data'))).thenAnswer( (_) async => Response( requestOptions: RequestOptions(path: '/api/v1/todos/todo-1'), data: { 'id': 'todo-1', 'owner_id': 'user-1', 'title': 'Q1 Todo', 'priority': 1, 'order': 1, 'status': 'pending', 'created_at': '2024-01-01T00:00:00Z', 'updated_at': '2024-01-01T00:00:00Z', }, ), ); final result = await todoApi.updateTodo('todo-1', priority: 1, order: 1); expect(result.priority, 1); expect(result.order, 1); }); test('priority 2 = important not urgent (Q3)', () async { when(() => mockClient.patch(any(), data: any(named: 'data'))).thenAnswer( (_) async => Response( requestOptions: RequestOptions(path: '/api/v1/todos/todo-2'), data: { 'id': 'todo-2', 'owner_id': 'user-1', 'title': 'Q3 Todo', 'priority': 2, 'order': 2, 'status': 'pending', 'created_at': '2024-01-01T00:00:00Z', 'updated_at': '2024-01-01T00:00:00Z', }, ), ); final result = await todoApi.updateTodo('todo-2', priority: 2, order: 2); expect(result.priority, 2); expect(result.order, 2); }); test('priority 3 = urgent not important (Q2)', () async { when(() => mockClient.patch(any(), data: any(named: 'data'))).thenAnswer( (_) async => Response( requestOptions: RequestOptions(path: '/api/v1/todos/todo-3'), data: { 'id': 'todo-3', 'owner_id': 'user-1', 'title': 'Q2 Todo', 'priority': 3, 'order': 0, 'status': 'pending', 'created_at': '2024-01-01T00:00:00Z', 'updated_at': '2024-01-01T00:00:00Z', }, ), ); final result = await todoApi.updateTodo('todo-3', priority: 3, order: 0); expect(result.priority, 3); expect(result.order, 0); }); }); group('TodoApi.reorderTodos', () { test('calls batch reorder endpoint once', () async { when(() => mockClient.patch(any(), data: any(named: 'data'))).thenAnswer( (_) async => Response( requestOptions: RequestOptions(path: '/api/v1/todos/reorder'), data: {}, ), ); await todoApi.reorderTodos(const [ TodoReorderItemPayload(id: 'todo-1', priority: 1, order: 0), TodoReorderItemPayload(id: 'todo-2', priority: 1, order: 1), ]); verify( () => mockClient.patch( '/api/v1/todos/reorder', data: { 'items': [ {'id': 'todo-1', 'priority': 1, 'order': 0}, {'id': 'todo-2', 'priority': 1, 'order': 1}, ], }, ), ).called(1); }); }); }