mobile-music/test/offline_library_test.dart
2026-08-16 12:46:30 -04:00

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);
});
}