import 'package:flutter_test/flutter_test.dart'; import 'package:melo_app/services/recap_service.dart'; /// RecapService-Tests mit injiziertem Historie-Loader (kein DB-Zugriff). /// Abgedeckt: Zeitraum-Filter (Woche/Monat/Jahr), Fallback-Kette /// (Woche → Monat → Jahr → Gesamt), Aggregation + Label-Getter. void main() { // Fester Anker-Zeitpunkt: 5. August 2026, 15:00 Uhr final jetzt = DateTime(2026, 8, 5, 15, 0, 0); Map zeile( int id, String playedAt, { String titel = 'Song', String kuenstler = 'Künstler', int dauer = 200, }) => { 'song_id': id, 'played_at': playedAt, 'titel': titel, 'kuenstler': kuenstler, 'dauer_sekunden': dauer, }; RecapService service(List> rows) => RecapService(historie: () async => rows); test('leere History → null (alle Zeiträume)', () async { expect(await service([]).berechne(jetzt: jetzt), isNull); expect(await service([]).berechne(zeitraum: RecapZeitraum.woche, jetzt: jetzt), isNull); expect( await service([]) .berechne(zeitraum: RecapZeitraum.monat, jetzt: jetzt), isNull); }); group('Jahr', () { test('nur Einträge des aktuellen Jahres zählen', () async { final rows = [ zeile(1, '2026-03-01T10:00:00'), zeile(2, '2026-08-01T10:00:00'), zeile(3, '2025-12-31T23:59:00'), // anderes Jahr → raus ]; final d = await service(rows).berechne(jetzt: jetzt); expect(d, isNotNull); expect(d!.gesamtAnzahl, 2); expect(d.zeitraum, RecapZeitraum.jahr); }); test('Jahr ohne Daten → Gesamt-Historie (alle Einträge)', () async { final rows = [ zeile(1, '2024-01-01T10:00:00'), zeile(2, '2023-06-15T10:00:00'), ]; final d = await service(rows).berechne(jetzt: jetzt); expect(d, isNotNull); expect(d!.gesamtAnzahl, 2); // Fallback auf Gesamt }); }); group('Monat', () { test('nur Einträge des aktuellen Kalendermonats zählen', () async { final rows = [ zeile(1, '2026-08-01T10:00:00'), zeile(2, '2026-08-05T08:00:00'), zeile(3, '2026-07-31T23:59:00'), // Juli → raus zeile(4, '2026-01-15T10:00:00'), // Januar → raus ]; final d = await service(rows).berechne(zeitraum: RecapZeitraum.monat, jetzt: jetzt); expect(d, isNotNull); expect(d!.gesamtAnzahl, 2); expect(d.zeitraum, RecapZeitraum.monat); expect(d.monatsName, 'August'); }); test('Monat ohne Daten → nächstgrößerer Zeitraum (Jahr)', () async { final rows = [ zeile(1, '2026-03-01T10:00:00'), zeile(2, '2026-05-15T10:00:00'), ]; final d = await service(rows).berechne(zeitraum: RecapZeitraum.monat, jetzt: jetzt); expect(d, isNotNull); expect(d!.zeitraum, RecapZeitraum.jahr); // Fallback Monat → Jahr expect(d.gesamtAnzahl, 2); }); }); group('Woche', () { test('nur Einträge der letzten 7 Tage zählen', () async { final rows = [ zeile(1, '2026-08-04T10:00:00'), // innerhalb 7 Tage zeile(2, '2026-07-30T12:00:00'), // genau 6 Tage vorher → drin zeile(3, '2026-07-29T12:00:00'), // > 7 Tage → raus zeile(4, '2026-01-01T10:00:00'), // alt → raus ]; final d = await service(rows).berechne(zeitraum: RecapZeitraum.woche, jetzt: jetzt); expect(d, isNotNull); expect(d!.gesamtAnzahl, 2); expect(d.zeitraum, RecapZeitraum.woche); }); test('Woche ohne Daten → Monat (nächstgrößerer Zeitraum)', () async { // Anker 20.8.: 7-Tage-Grenze = 13.8. → 10.8. liegt im Monat, aber // außerhalb der letzten 7 Tage. final spaeter = DateTime(2026, 8, 20, 15, 0, 0); final rows = [ zeile(1, '2026-08-10T10:00:00'), ]; final d = await service(rows) .berechne(zeitraum: RecapZeitraum.woche, jetzt: spaeter); expect(d, isNotNull); expect(d!.zeitraum, RecapZeitraum.monat); expect(d.gesamtAnzahl, 1); }); test('Woche UND Monat ohne Daten → Jahr', () async { final rows = [ zeile(1, '2026-03-01T10:00:00'), ]; final d = await service(rows).berechne(zeitraum: RecapZeitraum.woche, jetzt: jetzt); expect(d, isNotNull); expect(d!.zeitraum, RecapZeitraum.jahr); }); }); group('Aggregation', () { test('Top 5 Songs + Top 3 Künstler + Hörzeit + Nacht-%', () async { final rows = [ // Song A 3×, Song B 2×, Song C 1× zeile(1, '2026-08-01T10:00:00', titel: 'A', kuenstler: 'K1', dauer: 300), zeile(1, '2026-08-02T10:00:00', titel: 'A', kuenstler: 'K1', dauer: 300), zeile(1, '2026-08-03T23:00:00', titel: 'A', kuenstler: 'K1', dauer: 300), // nach 22 Uhr zeile(2, '2026-08-01T11:00:00', titel: 'B', kuenstler: 'K1', dauer: 240), zeile(2, '2026-08-02T11:00:00', titel: 'B', kuenstler: 'K1', dauer: 240), zeile(3, '2026-08-01T12:00:00', titel: 'C', kuenstler: 'K2', dauer: 180), ]; final d = await service(rows).berechne(zeitraum: RecapZeitraum.monat, jetzt: jetzt); expect(d, isNotNull); expect(d!.gesamtAnzahl, 6); expect(d.topSongs.length, 3); expect(d.topSongs.first.song.titel, 'A'); expect(d.topSongs.first.anzahl, 3); expect(d.topKuenstler.length, 2); expect(d.topKuenstler.first.name, 'K1'); expect(d.topKuenstler.first.anzahl, 5); // Hörzeit: 3×300 + 2×240 + 1×180 = 1560s = 26 Min expect(d.gesamtSekunden, 1560); expect(d.hoerzeitFormatiert, '26 Min'); // 1 von 6 Abspielungen nach 22 Uhr → 17% expect(d.nachtProzent, 17); expect(d.haeufigsterTag, isNotEmpty); }); }); group('Label-Getter', () { test('Woche: Kopfzeile, Intro, Teilen', () { final d = RecapDaten( zeitraum: RecapZeitraum.woche, jahr: 2026, monat: 8, gesamtAnzahl: 12, gesamtSekunden: 3600, haeufigsterTag: 'Freitag', nachtProzent: 30, topSongs: [], topKuenstler: [], ); expect(d.kopfzeile, 'MELO RECAP · 7 TAGE'); expect(d.introTitel, '7 Tage'); expect(d.introUntertitel, 'Deine Woche in Musik'); expect(d.zeitraumLabel, 'Diese Woche'); expect(d.shareTitel, 'Melo Wochen-Recap'); }); test('Monat: Kopfzeile, Intro, Teilen', () { final d = RecapDaten( zeitraum: RecapZeitraum.monat, jahr: 2026, monat: 8, gesamtAnzahl: 12, gesamtSekunden: 3600, haeufigsterTag: 'Freitag', nachtProzent: 30, topSongs: [], topKuenstler: [], ); expect(d.kopfzeile, 'MELO RECAP · AUGUST'); expect(d.introTitel, 'August'); expect(d.introUntertitel, 'Dein Monat in Musik'); expect(d.zeitraumLabel, 'Dein Monat'); expect(d.shareTitel, 'Melo Monats-Recap August'); }); test('Jahr: Kopfzeile, Intro, Teilen', () { final d = RecapDaten( zeitraum: RecapZeitraum.jahr, jahr: 2026, monat: 8, gesamtAnzahl: 12, gesamtSekunden: 3600, haeufigsterTag: 'Freitag', nachtProzent: 30, topSongs: [], topKuenstler: [], ); expect(d.kopfzeile, 'MELO RECAP 2026'); expect(d.introTitel, '2026'); expect(d.introUntertitel, 'Dein Jahr in Musik'); expect(d.zeitraumLabel, 'Dein Jahr'); expect(d.shareTitel, 'Melo Jahres-Recap 2026'); }); }); }