Files
Melo/test/library/playlist_service_test.dart
T

196 lines
5.9 KiB
Dart

import 'dart:convert';
import 'package:drift/native.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:http/http.dart' as http;
import 'package:http/testing.dart';
import 'package:melo/library/database.dart';
import 'package:melo/library/playlist_service.dart';
import 'package:melo/services/baka_auth.dart';
import 'package:melo/services/melo_cloud_service.dart';
class _MemorySpeicher implements TokenSpeicher {
_MemorySpeicher(this.werte);
final Map<String, String> werte;
@override
Future<String?> lesen(String key) async => werte[key];
@override
Future<void> schreiben(String key, String wert) async => werte[key] = wert;
@override
Future<void> loeschen(String key) async => werte.remove(key);
}
Future<BakaAuth> _angemeldeteAuth() async {
final auth = BakaAuth(
speicher: _MemorySpeicher({'baka_token': 'tok', 'baka_user': 'Baka'}),
);
await auth.laden();
return auth;
}
void main() {
late MeloDb db;
late PlaylistService service;
setUp(() {
db = MeloDb(NativeDatabase.memory());
service = PlaylistService(db);
});
tearDown(() async => db.close());
test('createPlaylist delegates to db and notifies listeners', () async {
var notified = false;
service.addListener(() => notified = true);
await service.createPlaylist('Road Trip');
final playlists = await db.watchPlaylists().first;
expect(playlists.length, 1);
expect(playlists.first.name, 'Road Trip');
expect(notified, true);
});
test('toggleFavorite delegates to db and notifies listeners', () async {
await db.into(db.songs).insert(SongsCompanion.insert(
id: 'song-1', path: '/a.mp3', title: 'A', dateAddedMs: 0, updatedAtMs: 0,
));
var notified = false;
service.addListener(() => notified = true);
await service.toggleFavorite('song-1');
expect(await db.watchIsFavorite('song-1').first, true);
expect(notified, true);
});
test('reorderAll schreibt lückenlose Positionen 0..N-1 in neuer Reihenfolge', () async {
final playlistId = await db.createPlaylist('Mix');
for (final id in ['song-1', 'song-2', 'song-3']) {
await db.into(db.songs).insert(SongsCompanion.insert(
id: id, path: '/$id.mp3', title: id, dateAddedMs: 0, updatedAtMs: 0,
));
}
// Ursprüngliche (evtl. lückenhafte) Positionen.
await db.addSongToPlaylist(playlistId, 'song-1', 0);
await db.addSongToPlaylist(playlistId, 'song-2', 5);
await db.addSongToPlaylist(playlistId, 'song-3', 9);
var notified = false;
service.addListener(() => notified = true);
await service.reorderAll(playlistId, ['song-3', 'song-1', 'song-2']);
final songs = await db.watchPlaylistSongs(playlistId).first;
expect(songs.map((s) => s.id).toList(), ['song-3', 'song-1', 'song-2']);
expect(notified, true);
});
group('Sofort-Push der Favoriten', () {
Future<void> legeSongAn(MeloDb db, {String? cloudId}) async {
await db.into(db.songs).insert(SongsCompanion.insert(
id: 'song-1',
path: '/a.mp3',
title: 'A',
dateAddedMs: 0,
updatedAtMs: 0,
));
if (cloudId != null) await db.setCloudId('song-1', cloudId);
}
test('setzt deterministisch true beim Favorisieren', () async {
final db2 = MeloDb(NativeDatabase.memory());
addTearDown(db2.close);
await legeSongAn(db2, cloudId: 'c1');
final gesendet = <Map<String, dynamic>>[];
final dienst = PlaylistService(
db2,
cloud: MeloCloudService(
auth: await _angemeldeteAuth(),
client: MockClient((anfrage) async {
gesendet.add(jsonDecode(anfrage.body) as Map<String, dynamic>);
return http.Response(jsonEncode({'status': 'ok'}), 200);
}),
),
);
await dienst.toggleFavorite('song-1');
expect(gesendet, [
{'song_id': 'c1', 'set': true}
]);
});
test('setzt deterministisch false beim Ent-Favorisieren', () async {
final db2 = MeloDb(NativeDatabase.memory());
addTearDown(db2.close);
await legeSongAn(db2, cloudId: 'c1');
await db2.setFavorite('song-1', true);
final gesendet = <Map<String, dynamic>>[];
final dienst = PlaylistService(
db2,
cloud: MeloCloudService(
auth: await _angemeldeteAuth(),
client: MockClient((anfrage) async {
gesendet.add(jsonDecode(anfrage.body) as Map<String, dynamic>);
return http.Response(jsonEncode({'status': 'ok'}), 200);
}),
),
);
await dienst.toggleFavorite('song-1');
expect(gesendet, [
{'song_id': 'c1', 'set': false}
]);
});
test('ohne cloudId wird nichts gemeldet', () async {
final db2 = MeloDb(NativeDatabase.memory());
addTearDown(db2.close);
await legeSongAn(db2);
var anfragen = 0;
final dienst = PlaylistService(
db2,
cloud: MeloCloudService(
auth: await _angemeldeteAuth(),
client: MockClient((_) async {
anfragen++;
return http.Response(jsonEncode({'status': 'ok'}), 200);
}),
),
);
await dienst.toggleFavorite('song-1');
expect(anfragen, 0);
expect(await db2.watchIsFavorite('song-1').first, isTrue);
});
test('ein Fehler des Servers ändert lokal nichts und wirft nicht',
() async {
final db2 = MeloDb(NativeDatabase.memory());
addTearDown(db2.close);
await legeSongAn(db2, cloudId: 'c1');
final dienst = PlaylistService(
db2,
cloud: MeloCloudService(
auth: await _angemeldeteAuth(),
client: MockClient(
(_) async => http.Response(jsonEncode({'error': 'weg'}), 500)),
),
);
// Der nächste Voll-Abgleich holt den Push additiv nach.
await dienst.toggleFavorite('song-1');
expect(await db2.watchIsFavorite('song-1').first, isTrue);
});
});
}