KeepassX format implementation in pure dart.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

140 lines
5.0 KiB

import 'package:clock/clock.dart';
import 'package:kdbx/kdbx.dart';
import 'package:kdbx/src/kdbx_xml.dart';
import 'package:kdbx/src/utils/print_utils.dart';
import 'package:logging/logging.dart';
import 'package:test/test.dart';
import 'package:xml/xml.dart';
import '../internal/test_utils.dart';
import '../kdbx_test.dart';
final _logger = Logger('kdbx_merge_test');
void main() {
final testUtil = TestUtil();
var now = DateTime.fromMillisecondsSinceEpoch(0);
final fakeClock = Clock(() => now);
void proceedSeconds(int seconds) {
now = now.add(Duration(seconds: seconds));
}
setUp(() {
now = DateTime.fromMillisecondsSinceEpoch(0);
});
group('Simple merges', () {
Future<KdbxFile> createSimpleFile() async {
final file = testUtil.createEmptyFile();
_createEntry(file, file.body.rootGroup, 'test1', 'test1');
final subGroup =
file.createGroup(parent: file.body.rootGroup, name: 'Sub Group');
_createEntry(file, subGroup, 'test2', 'test2');
proceedSeconds(10);
return await testUtil.saveAndRead(file);
}
test('Noop merge', () async {
final file = await createSimpleFile();
final file2 = await testUtil.saveAndRead(file);
final merge = file.merge(file2);
final set = Set<KdbxUuid>.from(merge.merged.keys);
expect(set, hasLength(4));
expect(merge.changes, isEmpty);
});
test('Username change', () async {
await withClock(fakeClock, () async {
final file = await createSimpleFile();
final fileMod = await testUtil.saveAndRead(file);
fileMod.body.rootGroup.entries.first
.setString(KdbxKeyCommon.USER_NAME, PlainValue('changed.'));
_logger.info('mod date: ' +
fileMod.body.rootGroup.entries.first.times.lastModificationTime
.get()
.toString());
final file2 = await testUtil.saveAndRead(fileMod);
_logger.info('\n\n\nstarting merge.\n');
final merge = file.merge(file2);
expect(file.body.rootGroup.entries.first.history, hasLength(1));
final set = Set<KdbxUuid>.from(merge.merged.keys);
expect(set, hasLength(4));
expect(merge.changes, hasLength(1));
});
});
test(
'Change Group Name',
() async => await withClock(fakeClock, () async {
final file = await createSimpleFile();
final fileMod = await testUtil.saveAndRead(file);
fileMod.body.rootGroup.groups.first.name.set('Sub Group New Name.');
final file2 = await testUtil.saveAndRead(fileMod);
final merge = file.merge(file2);
final set = Set<KdbxUuid>.from(merge.merged.keys);
expect(set, hasLength(4));
expect(merge.changes, hasLength(1));
}),
);
test(
'Move Entry to recycle bin',
() async => await withClock(fakeClock, () async {
final file = await createSimpleFile();
final fileMod = await testUtil.saveAndRead(file);
expect(fileMod.recycleBin, isNull);
fileMod.deleteEntry(fileMod.body.rootGroup.entries.first);
expect(fileMod.recycleBin, isNotNull);
final file2 = await testUtil.saveAndRead(fileMod);
final merge = file.merge(file2);
_logger.info('Merged file:\n'
'${KdbxPrintUtils().catGroupToString(file.body.rootGroup)}');
final set = Set<KdbxUuid>.from(merge.merged.keys);
expect(set, hasLength(5));
expect(
Set<KdbxNode>.from(merge.changes.map<KdbxNode?>((e) => e.object)),
hasLength(2));
}),
);
test(
'permanently delete an entry',
() async => await withClock(fakeClock, () async {
final file = await createSimpleFile();
final objCount = file.body.rootGroup.getAllGroupsAndEntries().length;
final fileMod = await testUtil.saveAndRead(file);
final entryDelete = fileMod.body.rootGroup.entries.first;
fileMod.deletePermanently(entryDelete);
expect(fileMod.body.rootGroup.getAllGroupsAndEntries(),
hasLength(objCount - 1));
final file2 = await testUtil.saveAndRead(fileMod);
final merge = file.merge(file2);
_logger.info('Merged file:\n'
'${KdbxPrintUtils().catGroupToString(file.body.rootGroup)}');
expect(merge.deletedObjects, hasLength(1));
expect(
file.body.rootGroup.getAllGroupsAndEntries().length, objCount - 1);
final xml = file.body.generateXml(FakeProtectedSaltGenerator());
final deleted = xml.findAllElements(KdbxXml.NODE_DELETED_OBJECT);
expect(deleted, hasLength(1));
expect(
deleted.first.findAllElements(KdbxXml.NODE_UUID).map((e) => e.text),
[entryDelete.uuid.uuid]);
}),
);
});
}
KdbxEntry _createEntry(
KdbxFile file, KdbxGroup group, String username, String password) {
final entry = KdbxEntry.create(file, group);
group.addEntry(entry);
entry.setString(KdbxKeyCommon.USER_NAME, PlainValue(username));
entry.setString(KdbxKeyCommon.PASSWORD, ProtectedValue.fromString(password));
return entry;
}