dew/packages/kanban/test/integration_test.dart
2026-05-02 14:02:39 -04:00

143 lines
4.4 KiB
Dart

import 'package:dew_kanban/dew_kanban.dart';
import 'package:file/memory.dart';
import 'package:test/test.dart';
const _testConfig = '''
dew:
kanban:
prefix: T
ticket_types:
- id: task
name: Task
columns:
- id: todo
name: To Do
color: blue
- id: doing
name: Doing
color: yellow
- id: done
name: Done
color: green
''';
TicketStore _makeStore(MemoryFileSystem fs) {
fs.directory('/.project/kanban').createSync(recursive: true);
fs.file('/.project/dew.yaml').writeAsStringSync(_testConfig);
return TicketStore(kanbanDir: '/.project/kanban', prefix: 'T', fs: fs);
}
void main() {
group('TicketStore integration', () {
late MemoryFileSystem fs;
late TicketStore store;
setUp(() {
fs = MemoryFileSystem();
store = _makeStore(fs);
});
test(
'create() writes file at correct path with YAML frontmatter',
() async {
final ticket = await store.create(
title: 'My ticket',
type: 'task',
column: 'todo',
);
expect(ticket.id, 'T-0001');
final file = fs.file('/.project/kanban/todo/T-0001.md');
expect(await file.exists(), isTrue);
final content = await file.readAsString();
expect(content, contains('title: My ticket'));
expect(content, contains('type: task'));
},
);
test('list() returns ticket created via create()', () async {
await store.create(title: 'Alpha', type: 'task', column: 'todo');
final tickets = await store.list();
expect(tickets, hasLength(1));
expect(tickets.first.id, 'T-0001');
expect(tickets.first.title, 'Alpha');
});
test('update(column:) moves ticket to correct directory', () async {
await store.create(title: 'Move me', type: 'task', column: 'todo');
await store.update('T-0001', column: 'doing');
// File removed from old location.
expect(
await fs.file('/.project/kanban/todo/T-0001.md').exists(),
isFalse,
);
// File present in new location.
expect(
await fs.file('/.project/kanban/doing/T-0001.md').exists(),
isTrue,
);
final ticket = await store.findById('T-0001');
expect(ticket!.column, 'doing');
});
test('linkTickets() adds link to both ticket files', () async {
await store.create(title: 'Source', type: 'task', column: 'todo');
await store.create(title: 'Target', type: 'task', column: 'todo');
await store.linkTickets('T-0001', 'T-0002', 'relates_to');
final sourceContent = await fs
.file('/.project/kanban/todo/T-0001.md')
.readAsString();
final targetContent = await fs
.file('/.project/kanban/todo/T-0002.md')
.readAsString();
expect(sourceContent, contains('T-0002'));
expect(targetContent, contains('T-0001'));
final source = await store.findById('T-0001');
final target = await store.findById('T-0002');
expect(source!.links.any((l) => l.targetId == 'T-0002'), isTrue);
expect(target!.links.any((l) => l.targetId == 'T-0001'), isTrue);
});
test('unlinkTickets() removes link from both ticket files', () async {
await store.create(title: 'Source', type: 'task', column: 'todo');
await store.create(title: 'Target', type: 'task', column: 'todo');
await store.linkTickets('T-0001', 'T-0002', 'relates_to');
await store.unlinkTickets('T-0001', 'T-0002');
final source = await store.findById('T-0001');
final target = await store.findById('T-0002');
expect(source!.links, isEmpty);
expect(target!.links, isEmpty);
final sourceContent = await fs
.file('/.project/kanban/todo/T-0001.md')
.readAsString();
final targetContent = await fs
.file('/.project/kanban/todo/T-0002.md')
.readAsString();
expect(sourceContent, isNot(contains('T-0002')));
expect(targetContent, isNot(contains('T-0001')));
});
test('delete() removes ticket file from disk', () async {
await store.create(title: 'Doomed', type: 'task', column: 'todo');
expect(await fs.file('/.project/kanban/todo/T-0001.md').exists(), isTrue);
await store.delete('T-0001');
expect(
await fs.file('/.project/kanban/todo/T-0001.md').exists(),
isFalse,
);
expect(await store.findById('T-0001'), isNull);
});
});
}