import 'dart:io'; import 'package:flutter_test/flutter_test.dart'; import 'package:path/path.dart' as p; import 'package:ocideck/services/image_dedup_service.dart'; void main() { late Directory tmp; final service = ImageDedupService(); setUp(() => tmp = Directory.systemTemp.createTempSync('ocideck_dedup')); tearDown(() => tmp.deleteSync(recursive: true)); String write(String name, List bytes) { final file = File(p.join(tmp.path, name))..writeAsBytesSync(bytes); return file.path; } group('findDuplicateGroups', () { test('groups byte-identical files and leaves unique files out', () async { final a1 = write('a1.png', [1, 2, 3, 4]); final a2 = write('a2.png', [1, 2, 3, 4]); final b = write('b.png', [9, 9, 9]); final groups = await service.findDuplicateGroups([a1, a2, b]); expect(groups, hasLength(1)); expect(groups.single, unorderedEquals([a1, a2])); }); test('same size but different content is not a duplicate', () async { final a = write('a.png', [1, 2, 3, 4]); final b = write('b.png', [4, 3, 2, 1]); expect(await service.findDuplicateGroups([a, b]), isEmpty); }); test('finds multiple independent groups', () async { final a1 = write('a1.png', [1, 2, 3]); final a2 = write('a2.png', [1, 2, 3]); final b1 = write('b1.png', [7, 7, 7, 7]); final b2 = write('b2.png', [7, 7, 7, 7]); final b3 = write('b3.png', [7, 7, 7, 7]); final groups = await service.findDuplicateGroups([a1, a2, b1, b2, b3]); expect(groups, hasLength(2)); final bySize = {for (final g in groups) g.length: g}; expect(bySize[2], unorderedEquals([a1, a2])); expect(bySize[3], unorderedEquals([b1, b2, b3])); }); test('silently skips missing files', () async { final a1 = write('a1.png', [1, 2, 3]); final a2 = write('a2.png', [1, 2, 3]); final gone = p.join(tmp.path, 'bestaat-niet.png'); final groups = await service.findDuplicateGroups([a1, gone, a2]); expect(groups, hasLength(1)); expect(groups.single, unorderedEquals([a1, a2])); }); }); group('chooseKeeper', () { test('prefers the path with the most slide usages', () { final a = write('a.png', [1]); final b = write('b.png', [1]); final keeper = service.chooseKeeper( [a, b], usageCountOf: (path) => path == b ? 2 : 0, ); expect(keeper, b); }); test('falls back to the oldest file when usages are equal', () { final newer = write('newer.png', [1]); final older = write('older.png', [1]); File(older).setLastModifiedSync( DateTime.now().subtract(const Duration(days: 7)), ); expect(service.chooseKeeper([newer, older]), older); }); }); group('mergeMetadata', () { test('joins unique non-empty values', () { expect( service.mergeMetadata(['boot', null, '', 'haven'], separator: ', '), 'boot, haven', ); }); test('drops values already contained in an earlier one', () { expect( service.mergeMetadata(['Boot in de haven', 'boot']), 'Boot in de haven', ); }); test('returns empty string when nothing is set', () { expect(service.mergeMetadata([null, '', ' ']), ''); }); }); }