import 'package:flutter_test/flutter_test.dart'; import 'package:melo/library/database.dart'; import 'package:melo/services/melo_cloud_service.dart'; import 'package:melo/services/sync_service.dart'; Song _song( String id, { String? cloudId, bool deleted = false, String titel = 'Lied', }) => Song( id: id, path: '/musik/$id.mp3', title: titel, cloudId: cloudId, dateAddedMs: 0, updatedAtMs: 0, deleted: deleted, playCount: 0, categoriesEdited: false, metadataEdited: false, ); CloudSong _cloud(String id, {bool geloescht = false}) => CloudSong(id: id, titel: 'Lied $id', geloescht: geloescht); void main() { group('planeSync', () { test('ohne beides passiert nichts', () { final plan = planeSync(lokal: const [], server: const []); expect(plan.istLeer, isTrue); }); test('ein Titel nur auf dem Gerät geht zum Server', () { final plan = planeSync(lokal: [_song('a')], server: const []); expect(plan.hochladen.map((s) => s.id), ['a']); expect(plan.herunterladen, isEmpty); expect(plan.lokalLoeschen, isEmpty); }); test('ein Titel nur am Server kommt aufs Gerät', () { final plan = planeSync(lokal: const [], server: [_cloud('c1')]); expect(plan.herunterladen.map((s) => s.id), ['c1']); expect(plan.hochladen, isEmpty); }); test('ein beidseitig bekannter Titel bleibt unangetastet', () { final plan = planeSync( lokal: [_song('a', cloudId: 'c1')], server: [_cloud('c1')], ); expect(plan.istLeer, isTrue); }); test('ein am Server gelöschter Titel verschwindet auch hier', () { final plan = planeSync( lokal: [_song('a', cloudId: 'c1')], server: [_cloud('c1', geloescht: true)], ); expect(plan.lokalLoeschen.map((s) => s.id), ['a']); // Nicht erneut herunterladen — sonst kommt der Titel ewig zurück. expect(plan.herunterladen, isEmpty); }); test('ein am Server gelöschter Titel wird nicht wieder hochgeladen', () { // Der Grabstein zählt als „bekannt": ohne diese Regel lädt jedes Gerät // den gelöschten Titel beim nächsten Abgleich erneut hoch. final plan = planeSync( lokal: [_song('a', cloudId: 'c1', deleted: true)], server: [_cloud('c1', geloescht: true)], ); expect(plan.istLeer, isTrue); }); test('ein lokal gelöschter Titel geht nicht zum Server', () { final plan = planeSync(lokal: [_song('a', deleted: true)], server: const []); expect(plan.hochladen, isEmpty); expect(plan.serverLoeschen, isEmpty); }); test('eine Löschung hier wird dem Server gemeldet', () { final plan = planeSync( lokal: [_song('a', cloudId: 'c1', deleted: true)], server: [_cloud('c1')], ); expect(plan.serverLoeschen.map((s) => s.id), ['a']); expect(plan.herunterladen, isEmpty); }); test('eine beidseitig bekannte Löschung wird nicht erneut gemeldet', () { final plan = planeSync( lokal: [_song('a', cloudId: 'c1', deleted: true)], server: [_cloud('c1', geloescht: true)], ); expect(plan.serverLoeschen, isEmpty); expect(plan.istLeer, isTrue); }); test('kennt der Server die Verknüpfung nicht mehr, geht der Titel erneut ' 'hoch statt verloren', () { final plan = planeSync( lokal: [_song('a', cloudId: 'verwaist')], server: [_cloud('c1')], ); expect(plan.hochladen.map((s) => s.id), ['a']); expect(plan.herunterladen.map((s) => s.id), ['c1']); }); test('gemischter Bestand wird vollständig aufgeteilt', () { final plan = planeSync( lokal: [ _song('a', cloudId: 'c1'), _song('b'), _song('c', cloudId: 'c3'), ], server: [_cloud('c1'), _cloud('c3', geloescht: true), _cloud('c9')], ); expect(plan.hochladen.map((s) => s.id), ['b']); expect(plan.lokalLoeschen.map((s) => s.id), ['c']); expect(plan.herunterladen.map((s) => s.id), ['c9']); expect(plan.gesamt, 3); }); }); group('sollAutoSync', () { final jetzt = DateTime(2026, 8, 21, 12); test('beim allerersten Mal wird abgeglichen', () { expect(sollAutoSync(null, jetzt), isTrue); }); test('direkt nach einem Lauf nicht noch einmal', () { expect(sollAutoSync(jetzt.subtract(const Duration(minutes: 1)), jetzt), isFalse); }); test('nach dem Abstand wieder', () { expect(sollAutoSync(jetzt.subtract(autoSyncAbstand), jetzt), isTrue); }); }); }