import 'package:flutter_test/flutter_test.dart'; import 'package:tasq/providers/tasks_provider.dart'; // Minimal fake supabase client similar to integration test work, // only implements the methods used by TasksController. class _FakeClient { final Map>> tables = { 'tasks': [], 'task_activity_logs': [], }; _FakeQuery from(String table) => _FakeQuery(this, table); } class _FakeQuery { final _FakeClient client; final String table; Map? _eq; Map? _insertPayload; Map? _updatePayload; _FakeQuery(this.client, this.table); _FakeQuery select([String? _]) => this; Future?> maybeSingle() async { final rows = client.tables[table] ?? []; if (_eq != null) { final field = _eq!.keys.first; final value = _eq![field]; for (final r in rows) { if (r[field] == value) { return Map.from(r); } } return null; } return rows.isEmpty ? null : Map.from(rows.first); } _FakeQuery insert(Map payload) { _insertPayload = Map.from(payload); return this; } Future> single() async { if (_insertPayload != null) { final id = 'tsk-${client.tables['tasks']!.length + 1}'; final row = Map.from(_insertPayload!); row['id'] = id; client.tables[table]!.add(row); return Map.from(row); } throw Exception('unexpected single() call'); } _FakeQuery update(Map payload) { _updatePayload = payload; // don't apply yet; wait for eq to know which row return this; } _FakeQuery eq(String field, dynamic value) { _eq = {field: value}; // apply update payload now that we know which row to target if (_updatePayload != null) { final idx = client.tables[table]!.indexWhere((r) => r[field] == value); if (idx >= 0) { client.tables[table]![idx] = { ...client.tables[table]![idx], ..._updatePayload!, }; } // clear payload after applying so subsequent eq calls don't reapply _updatePayload = null; } return this; } } void main() { group('TasksController business rules', () { late _FakeClient fake; late TasksController controller; setUp(() { fake = _FakeClient(); controller = TasksController(fake as dynamic); // ignore: avoid_dynamic_calls // note: controller expects SupabaseClient; using dynamic bypass. }); test('cannot complete a task without request details', () async { // insert a task with no metadata final row = {'id': 'tsk-1', 'status': 'queued'}; fake.tables['tasks']!.add(row); expect( () => controller.updateTaskStatus(taskId: 'tsk-1', status: 'completed'), throwsA(isA()), ); }); test('completing after adding metadata succeeds', () async { // insert a task final row = {'id': 'tsk-2', 'status': 'queued'}; fake.tables['tasks']!.add(row); // update metadata via updateTask await controller.updateTask( taskId: 'tsk-2', requestType: 'Repair', requestCategory: 'Hardware', ); await controller.updateTaskStatus(taskId: 'tsk-2', status: 'completed'); expect( fake.tables['tasks']!.firstWhere((t) => t['id'] == 'tsk-2')['status'], 'completed', ); }); }); }