feat(database): add Playlists, Favorites, PlaybackHistory tables
- 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>
This commit is contained in:
co-authored by
Claude Haiku 4.5
parent
bc8f3bfe77
commit
d09a677315
@@ -0,0 +1,110 @@
|
||||
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);
|
||||
});
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user