Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
63 changes: 44 additions & 19 deletions lib/app/v3/db/task_database.dart
Original file line number Diff line number Diff line change
Expand Up @@ -251,24 +251,46 @@ class TaskDatabase {
await ensureDatabaseIsOpen();

debugPrint('task in saveEditedTaskInDB: $uuid with due $newDue');
await _database!.update(
'Tasks',
{
'description': newDescription,
'project': newProject,
'status': newStatus,
'priority': newPriority,
'due': newDue,
'modified': DateTime.now().toIso8601String(),
},
where: 'uuid = ?',
whereArgs: [uuid],
);
debugPrint('task${uuid}edited');
if (newTags.isNotEmpty) {
TaskForC? task = await getTaskByUuid(uuid);
await setTagsForTask(uuid, task?.id ?? 0, newTags.toList());
}
// Keep task fields and tag replacement atomic so a crash between the
// former update() and setTagsForTask() cannot leave stale tags.
await _database!.transaction((txn) async {
await txn.update(
'Tasks',
{
'description': newDescription,
'project': newProject,
'status': newStatus,
'priority': newPriority,
'due': newDue,
'modified': DateTime.now().toIso8601String(),
},
where: 'uuid = ?',
whereArgs: [uuid],
);
debugPrint('task${uuid}edited');
final taskMaps = await txn.query(
'Tasks',
columns: ['id'],
where: 'uuid = ?',
whereArgs: [uuid],
limit: 1,
);
final taskId =
taskMaps.isNotEmpty ? (taskMaps.first['id'] as int? ?? 0) : 0;
await txn.delete(
'Tags',
where: 'task_uuid = ? AND task_id = ?',
whereArgs: [uuid, taskId],
);
for (final tag in newTags) {
if (tag.trim().isNotEmpty) {
await txn.insert(
'Tags',
{'name': tag, 'task_uuid': uuid, 'task_id': taskId},
);
}
}
});
}

Future<List<TaskForC>> findTasksWithoutUUIDs() async {
Expand Down Expand Up @@ -322,7 +344,10 @@ class TaskDatabase {
}

Future<void> close() async {
await _database!.close();
if (_database != null) {
await _database!.close();
_database = null;
}
}

Future<void> deleteTask({description, due, project, priority}) async {
Expand Down
103 changes: 102 additions & 1 deletion test/api_service_test.dart
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import 'dart:convert';
import 'dart:io';

import 'package:flutter/services.dart';
import 'package:flutter_test/flutter_test.dart';
Expand All @@ -24,10 +25,12 @@ void main() {

databaseFactory = databaseFactoryFfi;
MockClient mockClient = MockClient();
late Directory docsDir;

setUpAll(() {
sqfliteFfiInit();

docsDir = Directory.systemTemp.createTempSync('taskwarrior_test_docs_');

// Mock SharedPreferences plugin
const MethodChannel('plugins.flutter.io/shared_preferences')
.setMockMethodCallHandler((MethodCall methodCall) async {
Expand All @@ -36,6 +39,20 @@ void main() {
}
return null;
});

const MethodChannel('plugins.flutter.io/path_provider')
.setMockMethodCallHandler((MethodCall methodCall) async {
if (methodCall.method == 'getApplicationDocumentsDirectory') {
return docsDir.path;
}
return null;
});
});

tearDownAll(() {
if (docsDir.existsSync()) {
docsDir.deleteSync(recursive: true);
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.
});

group('Tasks model', () {
Expand Down Expand Up @@ -134,6 +151,10 @@ void main() {
await taskDatabase.open();
});

tearDown(() async {
await taskDatabase.close();
});

test('insertTask adds a task to the database', () async {
final task = TaskForC(
id: 1,
Expand Down Expand Up @@ -191,5 +212,85 @@ void main() {
// This will throw "Bad state: No element" when there are no tasks
expect(() => taskDatabase.fetchTasksFromDatabase(), throwsStateError);
});

test('saveEditedTaskInDB updates description and tags together', () async {
final task = TaskForC(
id: 7,
description: 'Old description',
project: 'Project 1',
status: 'pending',
uuid: 'edit-uuid',
urgency: 5.0,
priority: 'H',
due: '2024-12-31',
end: '',
entry: '2024-01-01',
modified: '2024-11-01',
tags: ['old'],
start: '',
wait: '',
rtype: '',
recur: '',
depends: [],
annotations: []);

await taskDatabase.insertTask(task);

await taskDatabase.saveEditedTaskInDB(
'edit-uuid',
'New description',
'Project 2',
'pending',
'M',
'2025-01-01',
['new-a', 'new-b'],
);

final edited = await taskDatabase.getTaskByUuid('edit-uuid');
expect(edited, isNotNull);
expect(edited!.description, 'New description');
expect(edited.project, 'Project 2');
expect(edited.priority, 'M');
expect(edited.due, '2025-01-01');
expect(edited.tags, unorderedEquals(['new-a', 'new-b']));
});

test('saveEditedTaskInDB clears tags when given an empty list', () async {
final task = TaskForC(
id: 8,
description: 'Tagged task',
project: 'Project 1',
status: 'pending',
uuid: 'clear-tags-uuid',
urgency: 5.0,
priority: 'H',
due: '2024-12-31',
end: '',
entry: '2024-01-01',
modified: '2024-11-01',
tags: ['keep-me-not'],
start: '',
wait: '',
rtype: '',
recur: '',
depends: [],
annotations: []);

await taskDatabase.insertTask(task);

await taskDatabase.saveEditedTaskInDB(
'clear-tags-uuid',
'Tagged task',
'Project 1',
'pending',
'H',
'2024-12-31',
const [],
);

final edited = await taskDatabase.getTaskByUuid('clear-tags-uuid');
expect(edited, isNotNull);
expect(edited!.tags, isEmpty);
});
});
}