This repository has been archived on 2026-08-19. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
melo-app/test/favoriten_sync_test.dart
T

125 lines
4.4 KiB
Dart
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import 'dart:convert';
import 'package:flutter_test/flutter_test.dart';
import 'package:http/http.dart' as http;
import 'package:http/testing.dart';
import 'package:melo_app/services/cloud_service.dart';
import 'package:melo_app/services/favoriten_sync.dart';
/// Tests für den bidirektionalen Favoriten-Sync (Sprint D v2.52).
///
/// Root-Cause-Regression: `_syncAlles` spiegelte Server-Favoriten nur zurück
/// (Server → Server) — lokale ⭐-Toggles gingen verloren. Der Fix merge-t
/// lokal Server (favoritenMerge) und pusht Toggles deterministisch via
/// `set` (kein Doppel-Toggle-Risiko).
void main() {
TestWidgetsFlutterBinding.ensureInitialized();
http.Response jsonOk(Map<String, dynamic> body) =>
http.Response(jsonEncode(body), 200,
headers: {'content-type': 'application/json'});
group('favoritenMerge (Vereinigung lokal Server)', () {
test('lokale Favoriten werden dem Server-Push hinzugefügt', () {
final merged = favoritenMerge(
lokal: {'cloud-1', 'cloud-2'},
server: {'cloud-9'},
);
expect(merged, containsAll(['cloud-1', 'cloud-2', 'cloud-9']));
expect(merged, hasLength(3));
});
test('Server-Favoriten bleiben erhalten (kein Datenverlust)', () {
final merged = favoritenMerge(
lokal: <String>{},
server: {'cloud-7', 'cloud-8'},
);
expect(merged, containsAll(['cloud-7', 'cloud-8']));
});
test('Doppelte IDs (lokal == server) werden dedupliziert', () {
final merged = favoritenMerge(
lokal: {'cloud-1', 'cloud-2'},
server: {'cloud-2', 'cloud-3'},
);
expect(merged, hasLength(3));
expect(merged.where((id) => id == 'cloud-2'), hasLength(1));
});
test('leere Mengen → leere Liste', () {
expect(favoritenMerge(lokal: {}, server: {}), isEmpty);
});
test('deterministisch sortiert (für Tests/Stabilität)', () {
final a = favoritenMerge(lokal: {'b', 'a'}, server: {'c'});
final b = favoritenMerge(lokal: {'c'}, server: {'b', 'a'});
expect(a, b);
expect(a, ['a', 'b', 'c']);
});
});
group('CloudService.setFavorite (deterministischer Push)', () {
test('sendet set:true und wertet status ok aus', () async {
late Map<String, dynamic> gesendeterBody;
String? pfad;
final cloud = CloudService(
client: MockClient((req) async {
pfad = req.url.path;
gesendeterBody = jsonDecode(req.body) as Map<String, dynamic>;
return jsonOk({'status': 'ok', 'favorited': true});
}),
);
final ok = await cloud.setFavorite('cloud-1', favorit: true);
expect(ok, isTrue);
expect(pfad, '/api/v1/cloud/favorites/toggle');
expect(gesendeterBody['song_id'], 'cloud-1');
expect(gesendeterBody['set'], isTrue);
});
test('sendet set:false beim Entfernen', () async {
late Map<String, dynamic> gesendeterBody;
final cloud = CloudService(
client: MockClient((req) async {
gesendeterBody = jsonDecode(req.body) as Map<String, dynamic>;
return jsonOk({'status': 'ok', 'favorited': false});
}),
);
final ok = await cloud.setFavorite('cloud-2', favorit: false);
expect(ok, isTrue);
expect(gesendeterBody['set'], isFalse);
});
test('Fehlerantwort (kein status ok) → false', () async {
final cloud = CloudService(
client: MockClient((req) async => jsonOk({'status': 'error'})),
);
expect(await cloud.setFavorite('cloud-3', favorit: true), isFalse);
});
test('Netzwerkfehler → false (kein Crash)', () async {
final cloud = CloudService(
client: MockClient(
(req) async => throw http.ClientException('offline')),
);
expect(await cloud.setFavorite('cloud-4', favorit: true), isFalse);
});
});
group('pusheFavoritAnServer (Feuer-und-Vergessen)', () {
test('leitet Toggle-Status an setFavorite weiter', () async {
final bodies = <Map<String, dynamic>>[];
final cloud = CloudService(
client: MockClient((req) async {
bodies.add(jsonDecode(req.body) as Map<String, dynamic>);
return jsonOk({'status': 'ok'});
}),
);
await pusheFavoritAnServer(cloud, 'cloud-5', true);
await pusheFavoritAnServer(cloud, 'cloud-5', false);
expect(bodies, hasLength(2));
expect(bodies[0]['set'], isTrue);
expect(bodies[1]['set'], isFalse);
expect(bodies[0]['song_id'], 'cloud-5');
});
});
}