173 lines
5.4 KiB
Dart
173 lines
5.4 KiB
Dart
import 'package:flutter_test/flutter_test.dart';
|
|
import 'package:timbre/library/offline_library.dart';
|
|
import 'package:timbre/subsonic/models.dart';
|
|
|
|
/// Builds a downloaded-track [Song] with just the fields the offline
|
|
/// reconstruction reads. Everything else defaults to null.
|
|
Song _song(
|
|
String id, {
|
|
String? title,
|
|
String? album,
|
|
String? albumId,
|
|
String? artist,
|
|
String? artistId,
|
|
String? coverArt,
|
|
int? track,
|
|
int? discNumber,
|
|
int? year,
|
|
String? genre,
|
|
}) =>
|
|
Song(
|
|
id: id,
|
|
title: title,
|
|
album: album,
|
|
albumId: albumId,
|
|
artist: artist,
|
|
artistId: artistId,
|
|
coverArt: coverArt,
|
|
track: track,
|
|
discNumber: discNumber,
|
|
year: year,
|
|
genre: genre,
|
|
);
|
|
|
|
void main() {
|
|
group('offline_library — album reconstruction', () {
|
|
test('groups songs by albumId and synthesizes album metadata', () {
|
|
final songs = [
|
|
_song('1',
|
|
title: 'A',
|
|
album: 'Rumours',
|
|
albumId: 'alb1',
|
|
artist: 'Fleetwood Mac',
|
|
artistId: 'art1',
|
|
coverArt: 'cov1',
|
|
track: 2,
|
|
year: 1977,
|
|
genre: 'Rock'),
|
|
_song('2',
|
|
title: 'B',
|
|
album: 'Rumours',
|
|
albumId: 'alb1',
|
|
artist: 'Fleetwood Mac',
|
|
artistId: 'art1',
|
|
coverArt: 'cov1',
|
|
track: 1,
|
|
year: 1977,
|
|
genre: 'Rock'),
|
|
];
|
|
final albums = albumsFromSongs(songs);
|
|
expect(albums, hasLength(1));
|
|
final a = albums.single;
|
|
expect(a.id, 'alb1');
|
|
expect(a.name, 'Rumours');
|
|
expect(a.artist, 'Fleetwood Mac');
|
|
expect(a.artistId, 'art1');
|
|
expect(a.coverArt, 'cov1');
|
|
expect(a.year, 1977);
|
|
expect(a.genre, 'Rock');
|
|
expect(a.songCount, 2);
|
|
});
|
|
|
|
test('orders an album by disc then track, nulls last', () {
|
|
final songs = [
|
|
_song('1', album: 'X', albumId: 'x', title: 'no-track'),
|
|
_song('2', album: 'X', albumId: 'x', title: 'd1t2', discNumber: 1, track: 2),
|
|
_song('3', album: 'X', albumId: 'x', title: 'd2t1', discNumber: 2, track: 1),
|
|
_song('4', album: 'X', albumId: 'x', title: 'd1t1', discNumber: 1, track: 1),
|
|
];
|
|
final ids = albumsFromSongs(songs).single.songs.map((s) => s.title).toList();
|
|
expect(ids, ['d1t1', 'd1t2', 'd2t1', 'no-track']);
|
|
});
|
|
|
|
test('falls back to album name as key and id when albumId is missing', () {
|
|
final songs = [
|
|
_song('1', album: 'Untitled Sessions', title: 'A'),
|
|
_song('2', album: 'Untitled Sessions', title: 'B'),
|
|
];
|
|
final albums = albumsFromSongs(songs);
|
|
expect(albums, hasLength(1));
|
|
expect(albums.single.id, 'Untitled Sessions');
|
|
expect(albums.single.songCount, 2);
|
|
});
|
|
|
|
test('skips songs with no album identity', () {
|
|
final songs = [
|
|
_song('1', title: 'orphan'),
|
|
_song('2', album: 'Real', albumId: 'r', title: 'kept'),
|
|
];
|
|
final albums = albumsFromSongs(songs);
|
|
expect(albums, hasLength(1));
|
|
expect(albums.single.id, 'r');
|
|
});
|
|
|
|
test('sorts albums by name case-insensitively', () {
|
|
final songs = [
|
|
_song('1', album: 'zebra', albumId: 'z'),
|
|
_song('2', album: 'Apple', albumId: 'a'),
|
|
_song('3', album: 'mango', albumId: 'm'),
|
|
];
|
|
final names = albumsFromSongs(songs).map((a) => a.name).toList();
|
|
expect(names, ['Apple', 'mango', 'zebra']);
|
|
});
|
|
});
|
|
|
|
group('offline_library — artist reconstruction', () {
|
|
test('groups by artistId and nests albums', () {
|
|
final songs = [
|
|
_song('1',
|
|
album: 'One',
|
|
albumId: 'a1',
|
|
artist: 'Radiohead',
|
|
artistId: 'r',
|
|
coverArt: 'c1'),
|
|
_song('2',
|
|
album: 'Two',
|
|
albumId: 'a2',
|
|
artist: 'Radiohead',
|
|
artistId: 'r',
|
|
coverArt: 'c2'),
|
|
];
|
|
final artists = artistsFromSongs(songs);
|
|
expect(artists, hasLength(1));
|
|
final a = artists.single;
|
|
expect(a.id, 'r');
|
|
expect(a.name, 'Radiohead');
|
|
expect(a.albumCount, 2);
|
|
expect(a.albums.map((al) => al.id), containsAll(['a1', 'a2']));
|
|
expect(a.coverArt, isNotNull);
|
|
});
|
|
|
|
test('falls back to artist name as key and id', () {
|
|
final songs = [_song('1', album: 'X', albumId: 'x', artist: 'Nameless Band')];
|
|
final artists = artistsFromSongs(songs);
|
|
expect(artists.single.id, 'Nameless Band');
|
|
});
|
|
});
|
|
|
|
group('offline_library — keyed lookups', () {
|
|
final songs = [
|
|
_song('1', album: 'One', albumId: 'a1', artist: 'Band', artistId: 'b1'),
|
|
_song('2', album: 'Two', albumId: 'a2', artist: 'Band', artistId: 'b1'),
|
|
];
|
|
|
|
test('albumFromSongs returns the matching album or null', () {
|
|
expect(albumFromSongs(songs, 'a2')?.name, 'Two');
|
|
expect(albumFromSongs(songs, 'nope'), isNull);
|
|
});
|
|
|
|
test('artistFromSongs returns the matching artist or null', () {
|
|
final artist = artistFromSongs(songs, 'b1');
|
|
expect(artist?.name, 'Band');
|
|
expect(artist?.albumCount, 2);
|
|
expect(artistFromSongs(songs, 'nope'), isNull);
|
|
});
|
|
});
|
|
|
|
test('empty input yields empty output', () {
|
|
expect(albumsFromSongs(const []), isEmpty);
|
|
expect(artistsFromSongs(const []), isEmpty);
|
|
expect(albumFromSongs(const [], 'x'), isNull);
|
|
expect(artistFromSongs(const [], 'x'), isNull);
|
|
});
|
|
}
|