3e267918b7
LibraryApi wraps GET /api/home, /api/artists/{id}(/tracks),
/api/albums/{id}. dioProvider builds an authenticated dio (token
resolver reads session_token from secure storage on every request).
homeProvider, artistProvider(id), albumProvider(id) sit on top.
171 lines
5.1 KiB
Dart
171 lines
5.1 KiB
Dart
import 'dart:convert';
|
|
import 'dart:typed_data';
|
|
|
|
import 'package:dio/dio.dart';
|
|
import 'package:flutter_test/flutter_test.dart';
|
|
|
|
import 'package:minstrel/api/endpoints/library.dart';
|
|
|
|
/// Stub adapter that returns a fixed JSON response for any request. The
|
|
/// body argument can be either a Map or a List (the artist-tracks endpoint
|
|
/// emits a top-level JSON array). Per-test wiring keeps the routing trivial.
|
|
class _StubAdapter implements HttpClientAdapter {
|
|
_StubAdapter(this._body);
|
|
final Object _body;
|
|
|
|
@override
|
|
Future<ResponseBody> fetch(
|
|
RequestOptions options,
|
|
Stream<Uint8List>? requestStream,
|
|
Future<void>? cancelFuture,
|
|
) async {
|
|
return ResponseBody.fromString(
|
|
jsonEncode(_body),
|
|
200,
|
|
headers: const {
|
|
Headers.contentTypeHeader: ['application/json'],
|
|
},
|
|
);
|
|
}
|
|
|
|
@override
|
|
void close({bool force = false}) {}
|
|
}
|
|
|
|
Dio _dioWith(Object body) {
|
|
final d = Dio(BaseOptions(baseUrl: 'http://example.test'));
|
|
d.httpClientAdapter = _StubAdapter(body);
|
|
return d;
|
|
}
|
|
|
|
void main() {
|
|
group('LibraryApi', () {
|
|
test('getAlbum parses album fields and tracks list', () async {
|
|
final api = LibraryApi(_dioWith(<String, dynamic>{
|
|
'id': 'al-1',
|
|
'title': 'Geogaddi',
|
|
'sort_title': 'geogaddi',
|
|
'artist_id': 'art-1',
|
|
'artist_name': 'Boards of Canada',
|
|
'year': 2002,
|
|
'track_count': 1,
|
|
'duration_sec': 312,
|
|
'cover_url': '/api/albums/al-1/cover',
|
|
'tracks': [
|
|
{
|
|
'id': 't-1',
|
|
'title': 'Music Is Math',
|
|
'album_id': 'al-1',
|
|
'album_title': 'Geogaddi',
|
|
'artist_id': 'art-1',
|
|
'artist_name': 'Boards of Canada',
|
|
'duration_sec': 312,
|
|
'track_number': 4,
|
|
'stream_url': '/api/tracks/t-1/stream',
|
|
},
|
|
],
|
|
}));
|
|
final r = await api.getAlbum('al-1');
|
|
expect(r.album.id, 'al-1');
|
|
expect(r.album.title, 'Geogaddi');
|
|
expect(r.album.artistName, 'Boards of Canada');
|
|
expect(r.album.year, 2002);
|
|
expect(r.tracks, hasLength(1));
|
|
expect(r.tracks.single.title, 'Music Is Math');
|
|
expect(r.tracks.single.durationSec, 312);
|
|
expect(r.tracks.single.trackNumber, 4);
|
|
});
|
|
|
|
test('getArtist parses ArtistDetail body as ArtistRef', () async {
|
|
final api = LibraryApi(_dioWith(<String, dynamic>{
|
|
'id': 'art-1',
|
|
'name': 'Boards of Canada',
|
|
'sort_name': 'boards of canada',
|
|
'album_count': 5,
|
|
'cover_url': '/api/albums/al-1/cover',
|
|
'albums': const [],
|
|
}));
|
|
final a = await api.getArtist('art-1');
|
|
expect(a.id, 'art-1');
|
|
expect(a.name, 'Boards of Canada');
|
|
expect(a.albumCount, 5);
|
|
});
|
|
|
|
test('getArtistAlbums extracts the albums array', () async {
|
|
final api = LibraryApi(_dioWith(<String, dynamic>{
|
|
'id': 'art-1',
|
|
'name': 'Boards of Canada',
|
|
'albums': [
|
|
{
|
|
'id': 'al-1',
|
|
'title': 'Geogaddi',
|
|
'artist_id': 'art-1',
|
|
'artist_name': 'Boards of Canada',
|
|
},
|
|
{
|
|
'id': 'al-2',
|
|
'title': 'The Campfire Headphase',
|
|
'artist_id': 'art-1',
|
|
'artist_name': 'Boards of Canada',
|
|
},
|
|
],
|
|
}));
|
|
final albums = await api.getArtistAlbums('art-1');
|
|
expect(albums, hasLength(2));
|
|
expect(albums.first.id, 'al-1');
|
|
expect(albums.last.title, 'The Campfire Headphase');
|
|
});
|
|
|
|
test('getArtistTracks parses a top-level JSON array', () async {
|
|
// Server emits a bare []TrackRef, NOT {"tracks": [...]}.
|
|
final api = LibraryApi(_dioWith(<Map<String, dynamic>>[
|
|
{
|
|
'id': 't-1',
|
|
'title': 'Music Is Math',
|
|
'album_id': 'al-1',
|
|
'album_title': 'Geogaddi',
|
|
'artist_id': 'art-1',
|
|
'artist_name': 'Boards of Canada',
|
|
'duration_sec': 312,
|
|
},
|
|
{
|
|
'id': 't-2',
|
|
'title': 'Dawn Chorus',
|
|
'album_id': 'al-1',
|
|
'album_title': 'Geogaddi',
|
|
'artist_id': 'art-1',
|
|
'artist_name': 'Boards of Canada',
|
|
'duration_sec': 168,
|
|
},
|
|
]));
|
|
final tracks = await api.getArtistTracks('art-1');
|
|
expect(tracks, hasLength(2));
|
|
expect(tracks.first.id, 't-1');
|
|
expect(tracks.last.durationSec, 168);
|
|
});
|
|
|
|
test('getHome parses HomePayload sections', () async {
|
|
final api = LibraryApi(_dioWith(<String, dynamic>{
|
|
'recently_added_albums': [
|
|
{
|
|
'id': 'al-1',
|
|
'title': 'Geogaddi',
|
|
'artist_id': 'art-1',
|
|
'artist_name': 'Boards of Canada',
|
|
},
|
|
],
|
|
'rediscover_albums': const [],
|
|
'rediscover_artists': const [],
|
|
'most_played_tracks': const [],
|
|
'last_played_artists': [
|
|
{'id': 'art-1', 'name': 'Boards of Canada'},
|
|
],
|
|
}));
|
|
final home = await api.getHome();
|
|
expect(home.recentlyAddedAlbums.single.title, 'Geogaddi');
|
|
expect(home.lastPlayedArtists.single.name, 'Boards of Canada');
|
|
expect(home.rediscoverAlbums, isEmpty);
|
|
});
|
|
});
|
|
}
|