- Playlists + PlaylistSongs tables with position-ordered join query - Favorites table with toggle + watch (reactive heart icon support) - PlaybackHistory table for resume-position tracking - Schema v2 with migration from v1 - Follows existing Songs/Folders sync-ready pattern (uuid id, updatedAtMs, deleted) Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
111 lines
3.4 KiB
Dart
111 lines
3.4 KiB
Dart
import 'package:drift/native.dart';
|
|
import 'package:flutter_test/flutter_test.dart';
|
|
import 'package:melo/library/database.dart';
|
|
|
|
void main() {
|
|
late MeloDb db;
|
|
|
|
setUp(() {
|
|
db = MeloDb(NativeDatabase.memory());
|
|
});
|
|
|
|
tearDown(() async {
|
|
await db.close();
|
|
});
|
|
|
|
group('Playlists', () {
|
|
test('createPlaylist inserts and watchPlaylists emits it', () async {
|
|
await db.createPlaylist('Workout');
|
|
final playlists = await db.watchPlaylists().first;
|
|
expect(playlists.length, 1);
|
|
expect(playlists.first.name, 'Workout');
|
|
});
|
|
|
|
test('deletePlaylist removes it', () async {
|
|
final id = await db.createPlaylist('Temp');
|
|
await db.deletePlaylist(id);
|
|
final playlists = await db.watchPlaylists().first;
|
|
expect(playlists, isEmpty);
|
|
});
|
|
});
|
|
|
|
group('PlaylistSongs', () {
|
|
test('addSongToPlaylist + watchPlaylistSongs returns song in order', () async {
|
|
final playlistId = await db.createPlaylist('My List');
|
|
await db.into(db.songs).insert(SongsCompanion.insert(
|
|
id: 'song-1',
|
|
path: '/a.mp3',
|
|
title: 'Song A',
|
|
dateAddedMs: 0,
|
|
updatedAtMs: 0,
|
|
));
|
|
|
|
await db.addSongToPlaylist(playlistId, 'song-1', 0);
|
|
final songs = await db.watchPlaylistSongs(playlistId).first;
|
|
expect(songs.length, 1);
|
|
expect(songs.first.title, 'Song A');
|
|
});
|
|
|
|
test('removeSongFromPlaylist removes it', () async {
|
|
final playlistId = await db.createPlaylist('My List');
|
|
await db.into(db.songs).insert(SongsCompanion.insert(
|
|
id: 'song-1',
|
|
path: '/a.mp3',
|
|
title: 'Song A',
|
|
dateAddedMs: 0,
|
|
updatedAtMs: 0,
|
|
));
|
|
await db.addSongToPlaylist(playlistId, 'song-1', 0);
|
|
await db.removeSongFromPlaylist(playlistId, 'song-1');
|
|
final songs = await db.watchPlaylistSongs(playlistId).first;
|
|
expect(songs, isEmpty);
|
|
});
|
|
});
|
|
|
|
group('Favorites', () {
|
|
test('toggleFavorite marks then unmarks a song', () async {
|
|
await db.into(db.songs).insert(SongsCompanion.insert(
|
|
id: 'song-1',
|
|
path: '/a.mp3',
|
|
title: 'Song A',
|
|
dateAddedMs: 0,
|
|
updatedAtMs: 0,
|
|
));
|
|
|
|
await db.toggleFavorite('song-1');
|
|
expect(await db.watchIsFavorite('song-1').first, true);
|
|
|
|
await db.toggleFavorite('song-1');
|
|
expect(await db.watchIsFavorite('song-1').first, false);
|
|
});
|
|
|
|
test('watchFavorites returns favorited songs only', () async {
|
|
await db.into(db.songs).insert(SongsCompanion.insert(
|
|
id: 'song-1', path: '/a.mp3', title: 'Fav', dateAddedMs: 0, updatedAtMs: 0,
|
|
));
|
|
await db.into(db.songs).insert(SongsCompanion.insert(
|
|
id: 'song-2', path: '/b.mp3', title: 'NotFav', dateAddedMs: 0, updatedAtMs: 0,
|
|
));
|
|
await db.toggleFavorite('song-1');
|
|
|
|
final favs = await db.watchFavorites().first;
|
|
expect(favs.length, 1);
|
|
expect(favs.first.title, 'Fav');
|
|
});
|
|
});
|
|
|
|
group('PlaybackHistory', () {
|
|
test('recordPlayback + lastPosition roundtrip', () async {
|
|
await db.into(db.songs).insert(SongsCompanion.insert(
|
|
id: 'song-1', path: '/a.mp3', title: 'A', dateAddedMs: 0, updatedAtMs: 0,
|
|
));
|
|
await db.recordPlayback('song-1', 45000);
|
|
expect(await db.lastPosition('song-1'), 45000);
|
|
});
|
|
|
|
test('lastPosition returns null when never played', () async {
|
|
expect(await db.lastPosition('unknown'), isNull);
|
|
});
|
|
});
|
|
}
|