todo-app/test/cli/cli_created_by_test.dart
Krzysztof kuhy Rudnicki 57019eec30
Some checks are pending
dependency freshness / dependencies on newest stable (push) Waiting to run
file-length / file length <= 250 lines (push) Waiting to run
md-naming / markdown naming convention (push) Waiting to run
release-apk / release (APK) (push) Waiting to run
repo-contract / repo contract (push) Waiting to run
Track who filed a note (created_by)
New optional note field created_by (kuhy|claude), stored like by/repo and
omitted when unset so older builds stay wire-compatible. App UI stamps kuhy;
MCP add_task and the CLI stamp claude. list/get/search output includes it.
Human notes sort above Claude's; Claude notes are hidden behind a per-device
eye toggle. CLI --backfill-created-by [--dry-run] classifies old notes by the
node that wrote created_at, writing only the created_by field.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-10 14:18:19 +01:00

86 lines
3.2 KiB
Dart

// created_by through the CLI: stamping, output, and the backfill command.
import 'dart:io';
import 'package:flutter_test/flutter_test.dart';
import 'cli_harness.dart';
void main() {
late CliHarness h;
setUp(() => h = CliHarness());
tearDown(() => h.dispose());
test('a CLI add is stamped claude, repo task or idea', () async {
final idea = await h.add('Plain idea');
final task = await h.add('Fix gate', ['--repo', 'kuhyx/todo']);
for (final id in [idea, task]) {
expect(await h.run(['--get', id, '--data-dir', h.dir('b')]), 0);
expect(h.out.toString(), contains('created_by: claude'));
}
expect(await h.run(['--list']), 0);
expect(
h.out.toString().trim().split('\n'),
everyElement(endsWith('\tclaude')),
);
});
test('a --new-note add is stamped claude', () async {
final id = await h.add('Own note', ['--repo', 'kuhyx/todo', '--new-note']);
expect(await h.run(['--get', id, '--data-dir', h.dir('b')]), 0);
expect(h.out.toString(), contains('created_by: claude'));
});
test('a peer note without created_by reads as kuhy', () async {
h.seedPeer([fixtureNote('peer-note-1', 'From the phone')]);
expect(await h.run(['--get', 'peer-note-1']), 0);
expect(h.out.toString(), contains('created_by: kuhy'));
expect(await h.run(['--list']), 0);
expect(h.out.toString(), endsWith('From the phone\tkuhy\n'));
});
group('--backfill-created-by', () {
void seed() {
final mcp = Directory('${h.env['XDG_DATA_HOME']}/todo-mcp')
..createSync(recursive: true);
File('${mcp.path}/node_id').writeAsStringSync('mcp-node\n');
h.seedPeer([
fixtureNote('mcp-note-0001', 'Make a text-free', repo: 'kuhyx/a'),
], node: 'mcp-node');
h.seedPeer([fixtureNote('idea-note-0001', 'My idea')], node: 'phone');
}
test('--dry-run lists the plan and writes nothing', () async {
seed();
expect(await h.run(['--backfill-created-by', '--dry-run']), 0);
final lines = h.out.toString().trim().split('\n');
expect(lines, contains('mcp-note\tclaude\tMake a text-free'));
expect(lines, contains('idea-not\tkuhy\tMy idea'));
expect(lines.last, 'dry run: claude 1, kuhy 1');
expect(h.db.methods, isNot(contains('PUT')), reason: 'nothing pushed');
});
test('a real run sets it once, pushes it, and is then a no-op', () async {
seed();
expect(await h.run(['--backfill-created-by']), 0);
expect(h.out.toString().trim().split('\n').last, 'set: claude 1, kuhy 1');
expect(
await h.run(['--get', 'mcp-note-0001', '--data-dir', h.dir('b')]),
0,
);
expect(h.out.toString(), contains('created_by: claude'));
expect(
await h.run(['--get', 'idea-note-0001', '--data-dir', h.dir('b')]),
0,
);
expect(h.out.toString(), contains('created_by: kuhy'));
expect(await h.run(['--backfill-created-by']), 0);
expect(h.out.toString().trim(), 'set: claude 0, kuhy 0');
});
test('--dry-run is refused without the backfill flag', () async {
expect(await h.run(['--dry-run']), 2);
expect(h.err.toString(), contains('--dry-run only applies'));
});
});
}