diff --git a/CHANGELOG.md b/CHANGELOG.md index 9217049..0f0195e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.1.0 + +- Implement permanently removing entries and groups. + ## 2.0.0+1 - Small Null-safety improvement. diff --git a/lib/src/kdbx_dao.dart b/lib/src/kdbx_dao.dart index a842291..be832a8 100644 --- a/lib/src/kdbx_dao.dart +++ b/lib/src/kdbx_dao.dart @@ -1,3 +1,5 @@ +import 'package:clock/clock.dart'; +import 'package:kdbx/src/kdbx_deleted_object.dart'; import 'package:kdbx/src/kdbx_entry.dart'; import 'package:kdbx/src/kdbx_file.dart'; import 'package:kdbx/src/kdbx_group.dart'; @@ -40,4 +42,26 @@ extension KdbxDao on KdbxFile { toGroup.addEntry(kdbxObject); } } + + void deletePermanently(KdbxObject kdbxObject) { + final parent = kdbxObject.parent; + if (parent == null) { + throw StateError( + 'Unable to delete object. Object as no parent, already deleted?'); + } + final now = clock.now().toUtc(); + if (kdbxObject is KdbxGroup) { + for (final object in kdbxObject.getAllGroupsAndEntries()) { + ctx.addDeletedObject(object.uuid, now); + } + parent.internalRemoveGroup(kdbxObject); + } else if (kdbxObject is KdbxEntry) { + ctx.addDeletedObject(kdbxObject.uuid, now); + parent.internalRemoveEntry(kdbxObject); + } else { + throw StateError('Invalid object type. ${kdbxObject.runtimeType}'); + } + kdbxObject.times.locationChanged.set(now); + kdbxObject.internalChangeParent(null); + } } diff --git a/lib/src/kdbx_deleted_object.dart b/lib/src/kdbx_deleted_object.dart index 37f355f..c2ce77a 100644 --- a/lib/src/kdbx_deleted_object.dart +++ b/lib/src/kdbx_deleted_object.dart @@ -1,12 +1,14 @@ +import 'package:clock/clock.dart'; import 'package:kdbx/src/kdbx_format.dart'; import 'package:kdbx/src/kdbx_object.dart'; import 'package:kdbx/src/kdbx_xml.dart'; import 'package:xml/xml.dart'; class KdbxDeletedObject extends KdbxNode implements KdbxNodeContext { - KdbxDeletedObject.create(this.ctx, KdbxUuid? uuid) : super.create(NODE_NAME) { + KdbxDeletedObject.create(this.ctx, KdbxUuid? uuid, [DateTime? now]) + : super.create(NODE_NAME) { _uuid.set(uuid); - deletionTime.setToNow(); + deletionTime.set(now ?? clock.now().toUtc()); } KdbxDeletedObject.read(XmlElement node, this.ctx) : super.read(node); diff --git a/lib/src/kdbx_format.dart b/lib/src/kdbx_format.dart index 29af7f7..c018614 100644 --- a/lib/src/kdbx_format.dart +++ b/lib/src/kdbx_format.dart @@ -138,6 +138,10 @@ class KdbxReadWriteContext { 'Tried to remove binary which is not in this file.'); } } + + void addDeletedObject(KdbxUuid uuid, [DateTime? now]) { + _deletedObjects.add(KdbxDeletedObject.create(this, uuid)); + } } abstract class CredentialsPart { diff --git a/lib/src/kdbx_group.dart b/lib/src/kdbx_group.dart index 602db3b..240d36a 100644 --- a/lib/src/kdbx_group.dart +++ b/lib/src/kdbx_group.dart @@ -57,6 +57,11 @@ class KdbxGroup extends KdbxObject { List getAllEntries() => getAllGroups().expand((g) => g.entries).toList(growable: false); + /// Returns all groups and entries. (Including the group itself). + Iterable getAllGroupsAndEntries() => [this] + .followedBy(entries) + .followedBy(groups.expand((g) => g.getAllGroupsAndEntries())); + List get groups => List.unmodifiable(_groups); final List _groups = []; diff --git a/lib/src/kdbx_object.dart b/lib/src/kdbx_object.dart index d6989f0..d40cf25 100644 --- a/lib/src/kdbx_object.dart +++ b/lib/src/kdbx_object.dart @@ -235,7 +235,8 @@ abstract class KdbxObject extends KdbxNode { return el; } - void internalChangeParent(KdbxGroup parent) { + @internal + void internalChangeParent(KdbxGroup? parent) { modify(() => _parent = parent); } diff --git a/pubspec.yaml b/pubspec.yaml index 18b8dfe..21e8125 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: kdbx description: KeepassX format implementation in pure dart. (kdbx 3.x and 4.x support). -version: 2.0.0+1 +version: 2.1.0 homepage: https://github.com/authpass/kdbx.dart environment: diff --git a/test/deleted_objects_test.dart b/test/deleted_objects_test.dart index c10d4f0..f8e35ba 100644 --- a/test/deleted_objects_test.dart +++ b/test/deleted_objects_test.dart @@ -1,24 +1,67 @@ @Tags(['kdbx4']) +import 'package:kdbx/kdbx.dart'; +import 'package:kdbx/src/kdbx_xml.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('deleted_objects_test'); void main() { - TestUtil.setupLogging(); + final testUtil = TestUtil(); _logger.finest('Running deleted objects tests.'); group('read tombstones', () { test('load/save keeps deleted objects.', () async { final orig = - await TestUtil.readKdbxFile('test/test_files/tombstonetest.kdbx'); + await testUtil.readKdbxFile('test/test_files/tombstonetest.kdbx'); expect(orig.body.deletedObjects, hasLength(1)); final dt = orig.body.deletedObjects.first.deletionTime.get()!; expect([dt.year, dt.month, dt.day], [2020, 8, 30]); - final reload = await TestUtil.saveAndRead(orig); + final reload = await testUtil.saveAndRead(orig); expect(reload.body.deletedObjects, hasLength(1)); }); }); + group('delete permanently', () { + test('delete entry', () async { + final file = testUtil.createEmptyFile(); + final g = file.body.rootGroup; + final entry = testUtil.createEntry(file, g, 'foo', 'bar'); + expect(g.getAllGroupsAndEntries().length, 2); + file.deleteEntry(entry); + // moved into trash bin + expect(g.getAllGroupsAndEntries().length, 3); + // now delete from trash + file.deletePermanently(entry); + expect(g.getAllGroupsAndEntries().length, 2); + final xml = file.body.generateXml(FakeProtectedSaltGenerator()); + final objects = xml.findAllElements(KdbxXml.NODE_DELETED_OBJECT); + expect(objects.length, 1); + expect(objects.first.findElements(KdbxXml.NODE_UUID).first.text, + entry.uuid.uuid); + }); + test('delete group', () async { + final file = testUtil.createEmptyFile(); + final rootGroup = file.body.rootGroup; + final g = file.createGroup(parent: rootGroup, name: 'group'); + final objs = [ + g, + testUtil.createEntry(file, g, 'foo', 'bar'), + testUtil.createEntry(file, g, 'foo2', 'bar2'), + testUtil.createEntry(file, g, 'foo3', 'bar3'), + ]; + + expect(rootGroup.getAllGroupsAndEntries().length, 5); + file.deletePermanently(g); + expect(rootGroup.getAllGroupsAndEntries().length, 1); + final xml = file.body.generateXml(FakeProtectedSaltGenerator()); + final objects = xml.findAllElements(KdbxXml.NODE_DELETED_OBJECT); + expect(objects.length, 4); + expect(objects.map((e) => e.findElements(KdbxXml.NODE_UUID).first.text), + objs.map((o) => o.uuid.uuid)); + }); + }); } diff --git a/test/icon/kdbx_customicon_test.dart b/test/icon/kdbx_customicon_test.dart index f35f00b..c2d31f1 100644 --- a/test/icon/kdbx_customicon_test.dart +++ b/test/icon/kdbx_customicon_test.dart @@ -3,9 +3,9 @@ import 'package:test/test.dart'; import '../internal/test_utils.dart'; void main() { - TestUtil.setupLogging(); + final testUtil = TestUtil(); test('load custom icons from file', () async { - final file = await TestUtil.readKdbxFile('test/icon/icontest.kdbx'); + final file = await testUtil.readKdbxFile('test/icon/icontest.kdbx'); final entry = file.body.rootGroup.entries.first; expect(entry.customIcon!.data, isNotNull); }); diff --git a/test/internal/test_utils.dart b/test/internal/test_utils.dart index 821aded..5e9846e 100644 --- a/test/internal/test_utils.dart +++ b/test/internal/test_utils.dart @@ -10,12 +10,21 @@ import 'package:logging_appenders/logging_appenders.dart'; final _logger = Logger('test_utils'); class TestUtil { + factory TestUtil() => instance; + TestUtil._() { + setupLogging(); + } + + static late final instance = TestUtil._(); + static final keyTitle = KdbxKey('Title'); static void setupLogging() => PrintAppender.setupLogging(stderrLevel: Level.WARNING); - static KdbxFormat kdbxFormat() { + late final kdbxFormat = _kdbxFormat(); + + static KdbxFormat _kdbxFormat() { Argon2.resolveLibraryForceDynamic = true; return KdbxFormat(Argon2FfiFlutter(resolveLibrary: (path) { final cwd = Directory('.').absolute.uri; @@ -26,42 +35,54 @@ class TestUtil { })); } - static Future readKdbxFile( + Future readKdbxFile( String filePath, { String password = 'asdf', }) async { - final kdbxFormat = TestUtil.kdbxFormat(); final data = await File(filePath).readAsBytes(); final file = await kdbxFormat.read( data, Credentials(ProtectedValue.fromString(password))); return file; } - static Future readKdbxFileBytes(Uint8List data, + Future readKdbxFileBytes(Uint8List data, {String password = 'asdf', Credentials? credentials}) async { - final kdbxFormat = TestUtil.kdbxFormat(); final file = await kdbxFormat.read( data, credentials ?? Credentials(ProtectedValue.fromString(password))); return file; } - static Future saveAndRead(KdbxFile file) async { + Future saveAndRead(KdbxFile file) async { return await readKdbxFileBytes(await file.save(), credentials: file.credentials); } - static Future saveTestOutput(String name, KdbxFile file) async { + Future saveTestOutput(String name, KdbxFile file) async { final bytes = await file.save(); final outFile = File('test_output_$name.kdbx'); await outFile.writeAsBytes(bytes); _logger.info('Written to $outFile'); } - static KdbxFile createEmptyFile() { - final file = kdbxFormat().create( + KdbxFile createEmptyFile() { + final file = kdbxFormat.create( Credentials.composite(ProtectedValue.fromString('asdf'), null), 'example'); return file; } + + 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; + } } diff --git a/test/kdbx4_test.dart b/test/kdbx4_test.dart index 8e1253d..f6a1782 100644 --- a/test/kdbx4_test.dart +++ b/test/kdbx4_test.dart @@ -1,11 +1,9 @@ @Tags(['kdbx4']) - import 'dart:io'; import 'package:kdbx/kdbx.dart'; import 'package:kdbx/src/kdbx_header.dart'; import 'package:logging/logging.dart'; -import 'package:logging_appenders/logging_appenders.dart'; import 'package:test/test.dart'; import 'internal/test_utils.dart'; @@ -15,9 +13,8 @@ final _logger = Logger('kdbx4_test'); // ignore_for_file: non_constant_identifier_names void main() { - Logger.root.level = Level.ALL; - PrintAppender().attachToLogger(Logger.root); - final kdbxFormat = TestUtil.kdbxFormat(); + final testUtil = TestUtil(); + final kdbxFormat = testUtil.kdbxFormat; group('Reading', () { test('bubb', () async { final data = await File('test/keepassxcpasswords.kdbx').readAsBytes(); @@ -36,7 +33,7 @@ void main() { expect(pwd, 'def'); }); test('Reading kdbx4_keeweb modification time', () async { - final file = await TestUtil.readKdbxFile('test/kdbx4_keeweb.kdbx'); + final file = await testUtil.readKdbxFile('test/kdbx4_keeweb.kdbx'); final firstEntry = file.body.rootGroup.entries.first; final createTime = firstEntry.times.creationTime.get(); expect(createTime, DateTime.utc(2020, 2, 26, 13, 40, 48)); @@ -44,13 +41,13 @@ void main() { expect(modTime, DateTime.utc(2020, 2, 26, 13, 40, 54)); }); test('Change kdbx4 modification time', () async { - final file = await TestUtil.readKdbxFile('test/kdbx4_keeweb.kdbx'); + final file = await testUtil.readKdbxFile('test/kdbx4_keeweb.kdbx'); final firstEntry = file.body.rootGroup.entries.first; final d = DateTime.utc(2020, 4, 5, 10, 0); firstEntry.times.lastModificationTime.set(d); final saved = await file.save(); { - final file2 = await TestUtil.readKdbxFileBytes(saved); + final file2 = await testUtil.readKdbxFileBytes(saved); final firstEntry = file2.body.rootGroup.entries.first; expect(firstEntry.times.lastModificationTime.get(), d); } @@ -116,12 +113,12 @@ void main() { }); group('recycle bin test', () { test('empty recycle bin with "zero" uuid', () async { - final file = await TestUtil.readKdbxFile('test/keepass2test.kdbx'); + final file = await testUtil.readKdbxFile('test/keepass2test.kdbx'); final recycleBin = file.recycleBin; expect(recycleBin, isNull); }); test('check deleting item', () async { - final file = await TestUtil.readKdbxFile('test/keepass2test.kdbx'); + final file = await testUtil.readKdbxFile('test/keepass2test.kdbx'); expect(file.recycleBin, isNull); final entry = file.body.rootGroup.getAllEntries().first; file.deleteEntry(entry); diff --git a/test/kdbx_binaries_test.dart b/test/kdbx_binaries_test.dart index f87c413..6fc4b41 100644 --- a/test/kdbx_binaries_test.dart +++ b/test/kdbx_binaries_test.dart @@ -18,7 +18,7 @@ void expectBinary(KdbxEntry entry, String key, dynamic matcher) { Future _testAddNewAttachment(String filePath) async { final saved = await (() async { - final f = await TestUtil.readKdbxFile(filePath); + final f = await TestUtil().readKdbxFile(filePath); final entry = KdbxEntry.create(f, f.body.rootGroup); entry.label = 'addattachment'; f.body.rootGroup.addEntry(entry); @@ -34,7 +34,7 @@ Future _testAddNewAttachment(String filePath) async { return await f.save(); })(); { - final file = await TestUtil.readKdbxFileBytes(saved); + final file = await TestUtil().readKdbxFileBytes(saved); final entry = file.body.rootGroup.entries .firstWhere((e) => e.label == 'addattachment'); final binaries = entry.binaryEntries.toList(); @@ -48,8 +48,7 @@ Future _testAddNewAttachment(String filePath) async { } void main() { - Logger.root.level = Level.ALL; - PrintAppender().attachToLogger(Logger.root); + final testUtil = TestUtil(); group('kdbx3 attachment', () { void expectKeepass2binariesContents(KdbxEntry entry) { @@ -73,28 +72,28 @@ void main() { } test('read binary', () async { - final file = await TestUtil.readKdbxFile('test/keepass2binaries.kdbx'); + final file = await testUtil.readKdbxFile('test/keepass2binaries.kdbx'); final entry = file.body.rootGroup.entries.first; expectKeepass2binariesContents(entry); }); test('read write read', () async { final fileRead = - await TestUtil.readKdbxFile('test/keepass2binaries.kdbx'); + await testUtil.readKdbxFile('test/keepass2binaries.kdbx'); final saved = await fileRead.save(); - final file = await TestUtil.readKdbxFileBytes(saved); + final file = await testUtil.readKdbxFileBytes(saved); final entry = file.body.rootGroup.entries.first; expectKeepass2binariesContents(entry); }); test('modify file with binary in history', () async { final fileRead = - await TestUtil.readKdbxFile('test/keepass2binaries.kdbx'); + await testUtil.readKdbxFile('test/keepass2binaries.kdbx'); final updateEntry = (KdbxFile file) { final entry = fileRead.body.rootGroup.entries.first; entry.setString(KdbxKeyCommon.TITLE, PlainValue('example')); }; updateEntry(fileRead); final saved = await fileRead.save(); - final file = await TestUtil.readKdbxFileBytes(saved); + final file = await testUtil.readKdbxFileBytes(saved); await file.save(); }); test('Add new attachment', () async { @@ -102,7 +101,7 @@ void main() { }); test('Remove attachment', () async { final saved = await (() async { - final file = await TestUtil.readKdbxFile('test/keepass2binaries.kdbx'); + final file = await testUtil.readKdbxFile('test/keepass2binaries.kdbx'); final entry = file.body.rootGroup.entries.first; expectKeepass2binariesContents(entry); expect(file.ctx.binariesIterable, hasLength(3)); @@ -110,7 +109,7 @@ void main() { expect(file.ctx.binariesIterable, hasLength(3)); return await file.save(); })(); - final file = await TestUtil.readKdbxFileBytes(saved); + final file = await testUtil.readKdbxFileBytes(saved); final entry = file.body.rootGroup.entries.first; expect(entry.binaryEntries, hasLength(2)); expect(entry.binaryEntries.map((e) => (e.key.key)), @@ -124,8 +123,8 @@ void main() { }); test('keepassxc compatibility', () async { // keepass has files in arbitrary sort order. - final file = await TestUtil.readKdbxFile( - 'test/test_files/binarytest-keepassxc.kdbx'); + final file = await testUtil + .readKdbxFile('test/test_files/binarytest-keepassxc.kdbx'); final entry = file.body.rootGroup.entries.first; for (final name in ['a', 'b', 'c', 'd', 'e']) { expect( @@ -138,7 +137,7 @@ void main() { group('kdbx4 attachment', () { test('read binary', () async { final file = - await TestUtil.readKdbxFile('test/keepass2kdbx4binaries.kdbx'); + await testUtil.readKdbxFile('test/keepass2kdbx4binaries.kdbx'); expect(file.body.rootGroup.entries, hasLength(2)); expectBinary(file.body.rootGroup.entries.first, 'example2.txt', @@ -148,9 +147,9 @@ void main() { }); test('read, write, read kdbx4', () async { final fileRead = - await TestUtil.readKdbxFile('test/keepass2kdbx4binaries.kdbx'); + await testUtil.readKdbxFile('test/keepass2kdbx4binaries.kdbx'); final saved = await fileRead.save(); - final file = await TestUtil.readKdbxFileBytes(saved); + final file = await testUtil.readKdbxFileBytes(saved); expect(file.body.rootGroup.entries, hasLength(2)); expectBinary(file.body.rootGroup.entries.first, 'example2.txt', IsUtf8String('content2 example\n\n')); @@ -160,7 +159,7 @@ void main() { test('remove attachment kdbx4', () async { final saved = await (() async { final file = - await TestUtil.readKdbxFile('test/keepass2kdbx4binaries.kdbx'); + await testUtil.readKdbxFile('test/keepass2kdbx4binaries.kdbx'); final entry = file.body.rootGroup.entries.first; expectBinary(file.body.rootGroup.entries.first, 'example2.txt', IsUtf8String('content2 example\n\n')); @@ -173,7 +172,7 @@ void main() { expect(file.dirtyObjects, [entry]); return await file.save(); })(); - final file = await TestUtil.readKdbxFileBytes(saved); + final file = await testUtil.readKdbxFileBytes(saved); final entry = file.body.rootGroup.entries.first; expect(entry.binaryEntries, hasLength(0)); expectBinary(file.body.rootGroup.entries.last, 'keepasslogo.jpeg', diff --git a/test/kdbx_history_test.dart b/test/kdbx_history_test.dart index 7cff462..a9b32a3 100644 --- a/test/kdbx_history_test.dart +++ b/test/kdbx_history_test.dart @@ -46,10 +46,10 @@ class StreamExpect { } void main() { - TestUtil.setupLogging(); + final testUtil = TestUtil(); group('test history for values', () { test('check history creation', () async { - final file = await TestUtil.readKdbxFile('test/keepass2test.kdbx'); + final file = await testUtil.readKdbxFile('test/keepass2test.kdbx'); const valueOrig = 'Sample Entry'; const value1 = 'new'; const value2 = 'new2'; @@ -64,7 +64,7 @@ void main() { } expect(file.dirtyObjects, hasLength(1)); final f2 = await dirtyExpect - .expectNext({}, () async => TestUtil.saveAndRead(file)); + .expectNext({}, () async => testUtil.saveAndRead(file)); expect(file.dirtyObjects, isEmpty); { final first = f2.body.rootGroup.entries.first; @@ -81,7 +81,7 @@ void main() { () async => first.setString(TestUtil.keyTitle, PlainValue(value2))); } final f3 = await dirtyExpect - .expectNext({}, () async => TestUtil.saveAndRead(file)); + .expectNext({}, () async => testUtil.saveAndRead(file)); expect(file.dirtyObjects, isEmpty); { final first = f3.body.rootGroup.entries.first; diff --git a/test/kdbx_test.dart b/test/kdbx_test.dart index d1bffd8..defa489 100644 --- a/test/kdbx_test.dart +++ b/test/kdbx_test.dart @@ -22,9 +22,8 @@ class FakeProtectedSaltGenerator implements ProtectedSaltGenerator { } void main() { - Logger.root.level = Level.ALL; - PrintAppender().attachToLogger(Logger.root); - final kdbxFormat = KdbxFormat(); + final testUtil = TestUtil(); + final kdbxFormat = testUtil.kdbxFormat; group('Reading', () { setUp(() {}); @@ -94,7 +93,7 @@ void main() { group('times', () { test('read mod date time', () async { - final file = await TestUtil.readKdbxFile('test/keepass2test.kdbx'); + final file = await testUtil.readKdbxFile('test/keepass2test.kdbx'); final first = file.body.rootGroup.entries.first; expect(file.header.version.major, 3); expect(first.getString(KdbxKeyCommon.TITLE)!.getText(), 'Sample Entry'); @@ -103,7 +102,7 @@ void main() { }); test('update mod date time', () async { final newModDate = DateTime.utc(2020, 1, 2, 3, 4, 5); - final file = await TestUtil.readKdbxFile('test/keepass2test.kdbx'); + final file = await testUtil.readKdbxFile('test/keepass2test.kdbx'); { final first = file.body.rootGroup.entries.first; expect(file.header.version.major, 3); @@ -112,7 +111,7 @@ void main() { } final saved = await file.save(); { - final file = await TestUtil.readKdbxFileBytes(saved); + final file = await testUtil.readKdbxFileBytes(saved); final first = file.body.rootGroup.entries.first; final modTime = first.times.lastModificationTime.get(); expect(modTime, newModDate); @@ -144,7 +143,7 @@ void main() { File('test.kdbx').writeAsBytesSync(saved); }); test('concurrent save test', () async { - final file = await TestUtil.readKdbxFile('test/keepass2test.kdbx'); + final file = await testUtil.readKdbxFile('test/keepass2test.kdbx'); final readLock = Lock(); Future doSave( Future byteFuture, String debug) async { @@ -152,7 +151,7 @@ void main() { final bytes = await byteFuture; return await readLock.synchronized(() { try { - final ret = TestUtil.readKdbxFileBytes(bytes); + final ret = testUtil.readKdbxFileBytes(bytes); _logger.fine('$debug FINISHED: success'); return ret; } catch (e, stackTrace) { diff --git a/test/kdbx_upgrade_test.dart b/test/kdbx_upgrade_test.dart index 144b691..bea6fb9 100644 --- a/test/kdbx_upgrade_test.dart +++ b/test/kdbx_upgrade_test.dart @@ -6,17 +6,17 @@ import 'package:test/test.dart'; import 'internal/test_utils.dart'; void main() { - TestUtil.setupLogging(); + final testUtil = TestUtil(); group('Test upgrade from v3 to v4', () { - final format = TestUtil.kdbxFormat(); + final format = testUtil.kdbxFormat; test('Read v3, write v4', () async { final file = - await TestUtil.readKdbxFile('test/FooBar.kdbx', password: 'FooBar'); + await testUtil.readKdbxFile('test/FooBar.kdbx', password: 'FooBar'); expect(file.header.version, KdbxVersion.V3_1); file.upgrade(KdbxVersion.V4.major); - final v4 = await TestUtil.saveAndRead(file); + final v4 = await testUtil.saveAndRead(file); expect(v4.header.version, KdbxVersion.V4); - await TestUtil.saveTestOutput('kdbx4upgrade', v4); + await testUtil.saveTestOutput('kdbx4upgrade', v4); }, tags: 'kdbx3'); test('kdbx4 is the new default', () async { final file = diff --git a/test/merge/kdbx_merge_test.dart b/test/merge/kdbx_merge_test.dart index a731635..5a24b40 100644 --- a/test/merge/kdbx_merge_test.dart +++ b/test/merge/kdbx_merge_test.dart @@ -9,7 +9,7 @@ import '../internal/test_utils.dart'; final _logger = Logger('kdbx_merge_test'); void main() { - TestUtil.setupLogging(); + final testUtil = TestUtil(); var now = DateTime.fromMillisecondsSinceEpoch(0); final fakeClock = Clock(() => now); @@ -22,18 +22,18 @@ void main() { }); group('Simple merges', () { Future createSimpleFile() async { - final file = TestUtil.createEmptyFile(); + 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); + return await testUtil.saveAndRead(file); } test('Noop merge', () async { final file = await createSimpleFile(); - final file2 = await TestUtil.saveAndRead(file); + final file2 = await testUtil.saveAndRead(file); final merge = file.merge(file2); final set = Set.from(merge.merged.keys); expect(set, hasLength(4)); @@ -43,7 +43,7 @@ void main() { await withClock(fakeClock, () async { final file = await createSimpleFile(); - final fileMod = await TestUtil.saveAndRead(file); + final fileMod = await testUtil.saveAndRead(file); fileMod.body.rootGroup.entries.first .setString(KdbxKeyCommon.USER_NAME, PlainValue('changed.')); @@ -51,7 +51,7 @@ void main() { fileMod.body.rootGroup.entries.first.times.lastModificationTime .get() .toString()); - final file2 = await TestUtil.saveAndRead(fileMod); + final file2 = await testUtil.saveAndRead(fileMod); _logger.info('\n\n\nstarting merge.\n'); final merge = file.merge(file2); @@ -66,10 +66,10 @@ void main() { () async => await withClock(fakeClock, () async { final file = await createSimpleFile(); - final fileMod = await TestUtil.saveAndRead(file); + final fileMod = await testUtil.saveAndRead(file); fileMod.body.rootGroup.groups.first.name.set('Sub Group New Name.'); - final file2 = await TestUtil.saveAndRead(fileMod); + final file2 = await testUtil.saveAndRead(fileMod); final merge = file.merge(file2); final set = Set.from(merge.merged.keys); expect(set, hasLength(4)); @@ -81,18 +81,19 @@ void main() { () async => await withClock(fakeClock, () async { final file = await createSimpleFile(); - final fileMod = await TestUtil.saveAndRead(file); + 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 file2 = await testUtil.saveAndRead(fileMod); final merge = file.merge(file2); _logger.info('Merged file:\n' '${KdbxPrintUtils().catGroupToString(file.body.rootGroup)}'); final set = Set.from(merge.merged.keys); expect(set, hasLength(5)); - expect(Set.from(merge.changes.map((e) => e.object)), + expect( + Set.from(merge.changes.map((e) => e.object)), hasLength(2)); }), );