From 0451c3a931bbd2ff6e67e64326b4f4e7767ce455 Mon Sep 17 00:00:00 2001 From: simojenki Date: Sat, 15 Feb 2025 06:12:29 +0000 Subject: [PATCH] tests passing --- src/music_library.ts | 8 +- src/smapi.ts | 3 +- src/subsonic.ts | 49 ++- src/subsonic_music_library.ts | 16 +- tests/builders.ts | 87 +++-- tests/in_memory_music_service.test.ts | 64 ++-- tests/in_memory_music_service.ts | 2 - tests/subsonic.test.ts | 153 +++++++- tests/subsonic_music_library.test.ts | 480 ++++++++++++++------------ 9 files changed, 544 insertions(+), 318 deletions(-) diff --git a/src/music_library.ts b/src/music_library.ts index 2504f29..457eac2 100644 --- a/src/music_library.ts +++ b/src/music_library.ts @@ -23,7 +23,8 @@ export type ArtistSummary = { export type SimilarArtist = ArtistSummary & { inLibrary: boolean }; -export type Artist = ArtistSummary & { +// todo: maybe is should be artist.summary rather than an artist also being a summary? +export type Artist = Pick & { albums: AlbumSummary[]; similarArtists: SimilarArtist[] }; @@ -34,12 +35,11 @@ export type AlbumSummary = { year: string | undefined; genre: Genre | undefined; coverArt: BUrn | undefined; - artistName: string | undefined; artistId: string | undefined; }; -export type Album = AlbumSummary & {}; +export type Album = Pick & { tracks: Track[] }; export type Genre = { name: string; @@ -175,7 +175,7 @@ export interface MusicLibrary { artists(q: ArtistQuery): Promise>; artist(id: string): Promise; albums(q: AlbumQuery): Promise>; - album(id: string): Promise; + album(id: string): Promise; tracks(albumId: string): Promise; track(trackId: string): Promise; genres(): Promise; diff --git a/src/smapi.ts b/src/smapi.ts index 62547ca..83e509c 100644 --- a/src/smapi.ts +++ b/src/smapi.ts @@ -10,7 +10,6 @@ import logger from "./logger"; import { LinkCodes } from "./link_codes"; import { - Album, AlbumQuery, AlbumSummary, ArtistSummary, @@ -612,7 +611,7 @@ function bindSmapiSoapServiceToExpress( switch (type) { case "artist": return musicLibrary.artist(typeId).then((artist) => { - const [page, total] = slice2(paging)( + const [page, total] = slice2(paging)( artist.albums ); return { diff --git a/src/subsonic.ts b/src/subsonic.ts index 754c517..2eeeb5a 100644 --- a/src/subsonic.ts +++ b/src/subsonic.ts @@ -14,6 +14,7 @@ import { AlbumQueryType, PlaylistSummary, Encoding, + albumToAlbumSummary, } from "./music_library"; import sharp from "sharp"; import _ from "underscore"; @@ -24,7 +25,7 @@ import axios, { AxiosRequestConfig } from "axios"; import randomstring from "randomstring"; import { b64Encode, b64Decode } from "./b64"; import { BUrn } from "./burn"; -import { artist } from "./smapi"; +import { album, artist } from "./smapi"; import { URLBuilder } from "./url_builder"; export const BROWSER_HEADERS = { @@ -159,6 +160,7 @@ export type song = { transcodedContentType: string | undefined; type: string | undefined; userRating: number | undefined; + // todo: this field shouldnt be on song? starred: string | undefined; }; @@ -279,7 +281,7 @@ export const artistImageURN = ( }; export const asTrack = ( - album: Album, + album: AlbumSummary, song: song, customPlayers: CustomPlayers ): Track => ({ @@ -298,7 +300,7 @@ export const asTrack = ( number: song.track || 0, genre: maybeAsGenre(song.genre), coverArt: coverArtURN(song.coverArt), - album, + album: album, artist: { id: song.artistId, name: song.artist ? song.artist : "?", @@ -315,7 +317,7 @@ export const asTrack = ( }, }); -export const asAlbum = (album: album): Album => ({ +export const asAlbumSummary = (album: album): AlbumSummary => ({ id: album.id, name: album.name, year: album.year, @@ -412,7 +414,7 @@ export const asURLSearchParams = (q: any) => { export type ImageFetcher = (url: string) => Promise; export const cachingImageFetcher = - (cacheDir: string, delegate: ImageFetcher) => + (cacheDir: string, delegate: ImageFetcher, makeSharp = sharp) => async (url: string): Promise => { const filename = path.join(cacheDir, `${Md5.hashStr(url)}.png`); return fse @@ -421,7 +423,7 @@ export const cachingImageFetcher = .catch(() => delegate(url).then((image) => { if (image) { - return sharp(image.data) + return makeSharp(image.data) .png() .toBuffer() .then((png) => { @@ -584,19 +586,30 @@ export class Subsonic { })), })); - getAlbum = (credentials: Credentials, id: string): Promise => + getAlbum = (credentials: Credentials, id: string): Promise => this.getJSON(credentials, "/rest/getAlbum", { id }) .then((it) => it.album) - .then((album) => ({ - id: album.id, - name: album.name, - year: album.year, - genre: maybeAsGenre(album.genre), - artistId: album.artistId, - artistName: album.artist, - coverArt: coverArtURN(album.coverArt), - })); - + .then((album) => { + const x: AlbumSummary = { + id: album.id, + name: album.name, + year: album.year, + genre: maybeAsGenre(album.genre), + artistId: album.artistId, + artistName: album.artist, + coverArt: coverArtURN(album.coverArt) + } + return { summary: x, songs: album.song } + }).then(({ summary, songs }) => { + const x: AlbumSummary = summary + const y: Track[] = songs.map((it) => asTrack(summary, it, this.customPlayers)) + return { + ...x, + tracks: y + }; + }); + + getArtist = ( credentials: Credentials, id: string @@ -647,7 +660,7 @@ export class Subsonic { .then((it) => it.song) .then((song) => this.getAlbum(credentials, song.albumId!).then((album) => - asTrack(album, song, this.customPlayers) + asTrack(albumToAlbumSummary(album), song, this.customPlayers) ) ); diff --git a/src/subsonic_music_library.ts b/src/subsonic_music_library.ts index 4f847e5..f0efa61 100644 --- a/src/subsonic_music_library.ts +++ b/src/subsonic_music_library.ts @@ -4,7 +4,6 @@ import { Credentials, MusicService, ArtistSummary, - Album, Result, slice2, AlbumQuery, @@ -15,13 +14,14 @@ import { Artist, AuthFailure, AuthSuccess, + albumToAlbumSummary, } from "./music_library"; import { Subsonic, CustomPlayers, GetAlbumResponse, asTrack, - asAlbum, + asAlbumSummary, PingResponse, NO_CUSTOM_PLAYERS, asToken, @@ -171,11 +171,13 @@ export class SubsonicMusicLibrary implements MusicLibrary { albums = async (q: AlbumQuery): Promise> => this.subsonic.getAlbumList2(this.credentials, q); - album = (id: string): Promise => - this.subsonic.getAlbum(this.credentials, id); + // todo: this should probably return an Album + album = (id: string): Promise => + this.subsonic.getAlbum(this.credentials, id).then(albumToAlbumSummary); genres = () => this.subsonic.getGenres(this.credentials); + // todo: do we even need this if Album has tracks? tracks = (albumId: string) => this.subsonic .getJSON(this.credentials, "/rest/getAlbum", { @@ -184,7 +186,7 @@ export class SubsonicMusicLibrary implements MusicLibrary { .then((it) => it.album) .then((album) => (album.song || []).map((song) => - asTrack(asAlbum(album), song, this.customPlayers) + asTrack(asAlbumSummary(album), song, this.customPlayers) ) ); @@ -418,7 +420,7 @@ export class SubsonicMusicLibrary implements MusicLibrary { songs.map((song) => this.subsonic .getAlbum(this.credentials, song.albumId!) - .then((album) => asTrack(album, song, this.customPlayers)) + .then((album) => asTrack(albumToAlbumSummary(album), song, this.customPlayers)) ) ) ); @@ -436,7 +438,7 @@ export class SubsonicMusicLibrary implements MusicLibrary { songs.map((song) => this.subsonic .getAlbum(this.credentials, song.albumId!) - .then((album) => asTrack(album, song, this.customPlayers)) + .then((album) => asTrack(albumToAlbumSummary(album), song, this.customPlayers)) ) ) ) diff --git a/tests/builders.ts b/tests/builders.ts index 8453e7a..1aa7d9f 100644 --- a/tests/builders.ts +++ b/tests/builders.ts @@ -8,13 +8,12 @@ import { Album, Artist, Track, - albumToAlbumSummary, - artistToArtistSummary, PlaylistSummary, Playlist, SimilarArtist, AlbumSummary, - RadioStation + RadioStation, + ArtistSummary } from "../src/music_library"; import { b64Encode } from "../src/b64"; @@ -116,13 +115,26 @@ export function aSimilarArtist( }; } -export function anArtist(fields: Partial = {}): Artist { +export function anArtistSummary(fields: Partial = {}): ArtistSummary { const id = fields.id || uuid(); - const artist = { + return { id, name: `Artist ${id}`, - albums: [anAlbum(), anAlbum(), anAlbum()], image: { system: "subsonic", resource: `art:${id}` }, + } +} + +export function anArtist(fields: Partial = {}): Artist { + const id = fields.id || uuid(); + const name = `Artist ${randomstring.generate()}` + const albums = fields.albums || [ + anAlbumSummary({ artistId: id, artistName: name }), + anAlbumSummary({ artistId: id, artistName: name }), + anAlbumSummary({ artistId: id, artistName: name }) + ]; + const artist = { + ...anArtistSummary({ id, name }), + albums, similarArtists: [ aSimilarArtist({ id: uuid(), name: "Similar artist1", inLibrary: true }), aSimilarArtist({ id: uuid(), name: "Similar artist2", inLibrary: true }), @@ -168,9 +180,10 @@ export const randomGenre = () => SAMPLE_GENRES[randomInt(SAMPLE_GENRES.length)]; export function aTrack(fields: Partial = {}): Track { const id = uuid(); - const artist = anArtist(); + const artist = fields.artist || anArtistSummary(); const genre = fields.genre || randomGenre(); const rating = { love: false, stars: Math.floor(Math.random() * 5) }; + const album = fields.album || anAlbumSummary({ artistId: artist.id, artistName: artist.name, genre }) return { id, name: `Track ${id}`, @@ -181,41 +194,14 @@ export function aTrack(fields: Partial = {}): Track { duration: randomInt(500), number: randomInt(100), genre, - artist: artistToArtistSummary(artist), - album: albumToAlbumSummary( - anAlbum({ artistId: artist.id, artistName: artist.name, genre }) - ), + artist, + album, coverArt: { system: "subsonic", resource: `art:${uuid()}`}, rating, ...fields, }; } -export function anAlbum(fields: Partial = {}): Album { - const id = uuid(); - return { - id, - name: `Album ${id}`, - genre: randomGenre(), - year: `19${randomInt(99)}`, - artistId: `Artist ${uuid()}`, - artistName: `Artist ${randomstring.generate()}`, - coverArt: { system: "subsonic", resource: `art:${uuid()}` }, - ...fields, - }; -}; - -export function aRadioStation(fields: Partial = {}): RadioStation { - const id = uuid() - const name = `Station-${id}`; - return { - id, - name, - url: `http://example.com/${name}`, - ...fields - } -} - export function anAlbumSummary(fields: Partial = {}): AlbumSummary { const id = uuid(); return { @@ -230,6 +216,35 @@ export function anAlbumSummary(fields: Partial = {}): AlbumSummary } }; +export function anAlbum(fields: Partial = {}): Album { + const albumSummary = anAlbumSummary() + const album = { + ...albumSummary, + tracks: [], + ...fields, + }; + const artistSummary = anArtistSummary({ id: album.artistId, name: album.artistName }) + const tracks = fields.tracks || [ + aTrack({ album: albumSummary, artist: artistSummary }), + aTrack({ album: albumSummary, artist: artistSummary }) + ] + return { + ...album, + tracks + }; +}; + +export function aRadioStation(fields: Partial = {}): RadioStation { + const id = uuid() + const name = `Station-${id}`; + return { + id, + name, + url: `http://example.com/${name}`, + ...fields + } +} + export const BLONDIE_ID = uuid(); export const BLONDIE_NAME = "Blondie"; export const BLONDIE: Artist = { diff --git a/tests/in_memory_music_service.test.ts b/tests/in_memory_music_service.test.ts index a2bc7e9..d167c84 100644 --- a/tests/in_memory_music_service.test.ts +++ b/tests/in_memory_music_service.test.ts @@ -5,7 +5,6 @@ import { InMemoryMusicService } from "./in_memory_music_service"; import { MusicLibrary, artistToArtistSummary, - albumToAlbumSummary, } from "../src/music_library"; import { v4 as uuid } from "uuid"; import { @@ -17,6 +16,7 @@ import { METAL, HIP_HOP, SKA, + anAlbumSummary, } from "./builders"; import _ from "underscore"; @@ -194,16 +194,16 @@ describe("InMemoryMusicService", () => { }); describe("albums", () => { - const artist1_album1 = anAlbum({ genre: POP }); - const artist1_album2 = anAlbum({ genre: ROCK }); - const artist1_album3 = anAlbum({ genre: METAL }); - const artist1_album4 = anAlbum({ genre: POP }); - const artist1_album5 = anAlbum({ genre: POP }); + const artist1_album1 = anAlbumSummary({ genre: POP }); + const artist1_album2 = anAlbumSummary({ genre: ROCK }); + const artist1_album3 = anAlbumSummary({ genre: METAL }); + const artist1_album4 = anAlbumSummary({ genre: POP }); + const artist1_album5 = anAlbumSummary({ genre: POP }); - const artist2_album1 = anAlbum({ genre: METAL }); + const artist2_album1 = anAlbumSummary({ genre: METAL }); - const artist3_album1 = anAlbum({ genre: HIP_HOP }); - const artist3_album2 = anAlbum({ genre: POP }); + const artist3_album1 = anAlbumSummary({ genre: HIP_HOP }); + const artist3_album2 = anAlbumSummary({ genre: POP }); const artist1 = anArtist({ name: "artist1", @@ -212,8 +212,8 @@ describe("InMemoryMusicService", () => { artist1_album2, artist1_album3, artist1_album4, - artist1_album5, - ], + artist1_album5 + ] }); const artist2 = anArtist({ name: "artist2", albums: [artist2_album1] }); const artist3 = anArtist({ @@ -275,16 +275,16 @@ describe("InMemoryMusicService", () => { }) ).toEqual({ results: [ - albumToAlbumSummary(artist1_album1), - albumToAlbumSummary(artist1_album2), - albumToAlbumSummary(artist1_album3), - albumToAlbumSummary(artist1_album4), - albumToAlbumSummary(artist1_album5), + artist1_album1, + artist1_album2, + artist1_album3, + artist1_album4, + artist1_album5, - albumToAlbumSummary(artist2_album1), + artist2_album1, - albumToAlbumSummary(artist3_album1), - albumToAlbumSummary(artist3_album2), + artist3_album1, + artist3_album2, ], total: totalAlbumCount, }); @@ -300,7 +300,7 @@ describe("InMemoryMusicService", () => { type: "alphabeticalByName", }) ).toEqual({ - results: _.sortBy(allAlbums, "name").map(albumToAlbumSummary), + results: _.sortBy(allAlbums, "name"), total: totalAlbumCount, }); }); @@ -317,9 +317,9 @@ describe("InMemoryMusicService", () => { }) ).toEqual({ results: [ - albumToAlbumSummary(artist1_album5), - albumToAlbumSummary(artist2_album1), - albumToAlbumSummary(artist3_album1), + artist1_album5, + artist2_album1, + artist3_album1, ], total: totalAlbumCount, }); @@ -336,8 +336,8 @@ describe("InMemoryMusicService", () => { }) ).toEqual({ results: [ - albumToAlbumSummary(artist3_album1), - albumToAlbumSummary(artist3_album2), + artist3_album1, + artist3_album2, ], total: totalAlbumCount, }); @@ -357,10 +357,10 @@ describe("InMemoryMusicService", () => { }) ).toEqual({ results: [ - albumToAlbumSummary(artist1_album1), - albumToAlbumSummary(artist1_album4), - albumToAlbumSummary(artist1_album5), - albumToAlbumSummary(artist3_album2), + artist1_album1, + artist1_album4, + artist1_album5, + artist3_album2, ], total: 4, }); @@ -379,8 +379,8 @@ describe("InMemoryMusicService", () => { }) ).toEqual({ results: [ - albumToAlbumSummary(artist1_album4), - albumToAlbumSummary(artist1_album5), + artist1_album4, + artist1_album5, ], total: 4, }); @@ -397,7 +397,7 @@ describe("InMemoryMusicService", () => { _count: 100, }) ).toEqual({ - results: [albumToAlbumSummary(artist3_album2)], + results: [artist3_album2], total: 4, }); }); diff --git a/tests/in_memory_music_service.ts b/tests/in_memory_music_service.ts index 22800af..73ae167 100644 --- a/tests/in_memory_music_service.ts +++ b/tests/in_memory_music_service.ts @@ -19,7 +19,6 @@ import { slice2, asResult, artistToArtistSummary, - albumToAlbumSummary, Track, Genre, Rating, @@ -97,7 +96,6 @@ export class InMemoryMusicService implements MusicService { } }) .then((matches) => matches.map((it) => it.album)) - .then((it) => it.map(albumToAlbumSummary)) .then(slice2(q)) .then(asResult), album: (id: string) => diff --git a/tests/subsonic.test.ts b/tests/subsonic.test.ts index 111411c..da84a65 100644 --- a/tests/subsonic.test.ts +++ b/tests/subsonic.test.ts @@ -1,3 +1,4 @@ + import { v4 as uuid } from "uuid"; import { Md5 } from "ts-md5"; import tmp from "tmp"; @@ -28,15 +29,17 @@ import { TranscodingCustomPlayers, CustomPlayers, NO_CUSTOM_PLAYERS, - Subsonic, + Subsonic } from "../src/subsonic"; import { b64Encode } from "../src/b64"; -import { Album, Artist, Track } from "../src/music_library"; -import { anAlbum, aTrack } from "./builders"; +import { Album, Artist, Track, AlbumSummary } from "../src/music_library"; +import { anAlbum, aTrack, anAlbumSummary, anArtistSummary } from "./builders"; import { BUrn } from "../src/burn"; + + describe("t", () => { it("should be an md5 of the password and the salt", () => { const p = "password123"; @@ -163,7 +166,8 @@ describe("cachingImageFetcher", () => { toBuffer: () => Promise.resolve(pngImage), }); - const result = await cachingImageFetcher(dir.name, delegate)(url); + // todo: the fact that I need to pass the sharp mock in here isnt correct + const result = await cachingImageFetcher(dir.name, delegate, sharp)(url); expect(result!.contentType).toEqual("image/png"); expect(result!.data).toEqual(pngImage); @@ -555,6 +559,95 @@ const ok = (data: string | object) => ({ data, }); +export const asArtistAlbumJson = ( + artist: { id: string | undefined; name: string | undefined }, + album: AlbumSummary +) => ({ + id: album.id, + parent: artist.id, + isDir: "true", + title: album.name, + name: album.name, + album: album.name, + artist: artist.name, + genre: album.genre?.name, + duration: "123", + playCount: "4", + year: album.year, + created: "2021-01-07T08:19:55.834207205Z", + artistId: artist.id, + songCount: "19", +}); + +export const asAlbumJson = ( + artist: { id: string | undefined; name: string | undefined }, + album: Album +) => ({ + id: album.id, + parent: artist.id, + isDir: "true", + title: album.name, + name: album.name, + album: album.name, + artist: artist.name, + genre: album.genre?.name, + coverArt: maybeIdFromCoverArtUrn(album.coverArt), + duration: "123", + playCount: "4", + year: album.year, + created: "2021-01-07T08:19:55.834207205Z", + artistId: artist.id, + songCount: "19", + isVideo: false, + song: album.tracks.map(asSongJson), +}); + + +export const getAlbumJson = (album: Album) => + subsonicOK({ album: { + id: album.id, + parent: album.artistId, + album: album.name, + title: album.name, + name: album.name, + isDir: true, + coverArt: maybeIdFromCoverArtUrn(album.coverArt), + songCount: 19, + created: "2021-01-07T08:19:55.834207205Z", + duration: 123, + playCount: 4, + artistId: album.artistId, + artist: album.artistName, + year: album.year, + genre: album.genre?.name, + song: album.tracks.map(track => ({ + id: track.id, + parent: track.album.id, + title: track.name, + isDir: false, + isVideo: false, + type: "music", + albumId: track.album.id, + album: track.album.name, + artistId: track.artist.id, + artist: track.artist.name, + coverArt: maybeIdFromCoverArtUrn(track.coverArt), + duration: track.duration, + bitRate: 128, + bitDepth: 16, + samplingRate: 555, + channelCount: 2, + track: track.number, + year: 1900, + genre: track.genre?.name, + size: 5624132, + discNumer: 1, + suffix: "mp3", + contentType: track.encoding.mimeType, + path: "ACDC/High voltage/ACDC - The Jack.mp3" + })), + } }); + describe("subsonic", () => { const url = new URLBuilder("http://127.0.0.22:4567/some-context-path"); const customPlayers = { @@ -684,4 +777,56 @@ describe("subsonic", () => { }); }); }); + + describe("getting an album", () => { + beforeEach(() => { + customPlayers.encodingFor.mockReturnValue(O.none); + }); + + describe("when it exists", () => { + const artistId = "artist6677" + const artistName = "Fizzy Wizzy" + + const albumSummary = anAlbumSummary({ artistId, artistName }) + const artistSumamry = anArtistSummary({ id: artistId, name: artistName }) + + // todo: fix these ratings + const tracks = [ + aTrack({ artist: artistSumamry, album: albumSummary, rating: { love: false, stars: 0 } }), + aTrack({ artist: artistSumamry, album: albumSummary, rating: { love: false, stars: 0 } }), + aTrack({ artist: artistSumamry, album: albumSummary, rating: { love: false, stars: 0 } }), + aTrack({ artist: artistSumamry, album: albumSummary, rating: { love: false, stars: 0 } }), + ]; + + const album = anAlbum({ + ...albumSummary, + tracks, + artistId, + artistName, + }); + + beforeEach(() => { + mockGET.mockImplementationOnce(() => + Promise.resolve(ok(getAlbumJson(album))) + ); + }); + + it("should return the album", async () => { + const result = await subsonic.getAlbum(credentials, album.id); + + expect(result).toEqual(album); + + expect(axios.get).toHaveBeenCalledWith( + url.append({ pathname: "/rest/getAlbum" }).href(), + { + params: asURLSearchParams({ + ...authParamsPlusJson, + id: album.id, + }), + headers, + } + ); + }); + }); + }); }); diff --git a/tests/subsonic_music_library.test.ts b/tests/subsonic_music_library.test.ts index 5faa5d0..e84c891 100644 --- a/tests/subsonic_music_library.test.ts +++ b/tests/subsonic_music_library.test.ts @@ -19,6 +19,7 @@ import { CustomPlayers, PingResponse, images, + } from "../src/subsonic"; import { @@ -32,7 +33,6 @@ import { albumToAlbumSummary, asArtistAlbumPairs, Track, - AlbumSummary, artistToArtistSummary, AlbumQuery, PlaylistSummary, @@ -41,6 +41,7 @@ import { Credentials, AuthFailure, RadioStation, + AlbumSummary, } from "../src/music_library"; import { aGenre, @@ -53,11 +54,15 @@ import { POP, ROCK, aRadioStation, + anAlbumSummary, + anArtistSummary } from "./builders"; import { b64Encode } from "../src/b64"; import { BUrn } from "../src/burn"; import { URLBuilder } from "../src/url_builder"; +import { getAlbumJson } from "./subsonic.test"; + const EMPTY = { "subsonic-response": { status: "ok", @@ -104,29 +109,6 @@ const error = (code: string, message: string) => ({ }, }); -const asAlbumJson = ( - artist: { id: string | undefined; name: string | undefined }, - album: AlbumSummary, - tracks: Track[] = [] -) => ({ - id: album.id, - parent: artist.id, - isDir: "true", - title: album.name, - name: album.name, - album: album.name, - artist: artist.name, - genre: album.genre?.name, - coverArt: maybeIdFromCoverArtUrn(album.coverArt), - duration: "123", - playCount: "4", - year: album.year, - created: "2021-01-07T08:19:55.834207205Z", - artistId: artist.id, - songCount: "19", - isVideo: false, - song: tracks.map(asSongJson), -}); const maybeIdFromCoverArtUrn = (coverArt: BUrn | undefined) => pipe( @@ -136,28 +118,43 @@ const maybeIdFromCoverArtUrn = (coverArt: BUrn | undefined) => O.getOrElseW(() => "") ); -const getAlbumJson = (artist: Artist, album: Album, tracks: Track[]) => - subsonicOK({ album: asAlbumJson(artist, album, tracks) }); + const getSongJson = (track: Track) => subsonicOK({ song: asSongJson(track) }); -const asArtistJson = ( - artist: Artist, - extras: ArtistExtras = { artistImageUrl: undefined } -) => ({ - id: artist.id, - name: artist.name, - albumCount: artist.albums.length, - album: artist.albums.map((it) => asAlbumJson(artist, it)), - ...extras, -}); - const getArtistJson = ( artist: Artist, extras: ArtistExtras = { artistImageUrl: undefined } ) => subsonicOK({ - artist: asArtistJson(artist, extras), + artist: { + id: artist.id, + name: artist.name, + coverArt: "art-123", + albumCount: artist.albums.length, + artistImageUrl: extras.artistImageUrl, + starred: "sometime", + album: artist.albums.map((album) => ({ + id: album.id, + parent: artist.id, + album: album.name, + title: album.name, + name: album.name, + isDir: "true", + coverArt: maybeIdFromCoverArtUrn(album.coverArt), + songCount: 19, + created: "2021-01-07T08:19:55.834207205Z", + duration: 123, + playCount: 4, + artistId: artist.id, + artist: artist.name, + year: album.year, + genre: album.genre?.name, + userRating: 5, + averageRating: 3, + starred: "2021-01-07T08:19:55.834207205Z", + })) + }, }); const getRadioStationsJson = (radioStations: RadioStation[]) => @@ -172,11 +169,6 @@ const getRadioStationsJson = (radioStations: RadioStation[]) => }, }); -// const getStarredJson = ({ albums }: { albums: Album[] }) => subsonicOK({starred2: { -// album: albums.map(it => asAlbumJson({ id: it.artistId, name: it.artistName }, it, [])), -// song: [], -// }}) - const subsonicOK = (body: any = {}) => ({ "subsonic-response": { status: "ok", @@ -280,10 +272,22 @@ const createPlayListJson = (playlist: PlaylistSummary) => playlist: asPlaylistJson(playlist), }); -const getAlbumListJson = (albums: [Artist, Album][]) => + +const getAlbumListJson = (albums: [Artist, AlbumSummary][]) => subsonicOK({ albumList2: { - album: albums.map(([artist, album]) => asAlbumJson(artist, album)), + album: albums.map(([artist, album]) => ({ + id: album.id, + name: album.name, + coverArt: maybeIdFromCoverArtUrn(album.coverArt), + songCount: "19", + created: "2021-01-07T08:19:55.834207205Z", + duration: "123", + artist: artist.name, + artistId: artist.id, + year: album.year, + genre: album.genre?.name + })), }, }); @@ -338,9 +342,59 @@ const getSearchResult3Json = ({ }>) => subsonicOK({ searchResult3: { - artist: (artists || []).map((it) => asArtistJson({ ...it, albums: [] })), - album: (albums || []).map((it) => asAlbumJson(it.artist, it.album, [])), - song: (tracks || []).map((it) => asSongJson(it)), + artist: (artists || []).map((it) => ({ + id: it.id, + name: it.name, + // coverArt?? + albumCount: it.albums.length, + userRating: -1, + //artistImageUrl? + })), + album: (albums || []).map(({ artist, album }) => ({ + id: album.id, + name: album.name, + artist: artist.name, + year: album.year, + coverArt: maybeIdFromCoverArtUrn(album.coverArt), + //starred + //duration + //playCount + //played + //created + artistId: artist.id, + //userRating + songCount: album.tracks.length + })), + song: (tracks || []).map((track) => ({ + id: track.id, + parent: track.album.id, + isDir: "false", + title: track.name, + album: track.album.name, + artist: track.artist.name, + track: track.number, + year: "", + coverArt: maybeIdFromCoverArtUrn(track.coverArt), + size: "5624132", + contentType: track.encoding.mimeType, + suffix: "mp3", + starred: track.rating.love ? "sometime" : undefined, + duration: track.duration, + bitRate: 128, + //bitDepth + //samplingRate + //channelCount + path: "ACDC/High voltage/ACDC - The Jack.mp3", + //path + //playCount + //played + //discNumber + created: "2004-11-08T23:36:11", + albumId: track.album.id, + artistId: track.artist.id, + type: "music", + isVideo: "false", + })), }, }); @@ -820,11 +874,11 @@ describe("SubsonicMusicLibrary", () => { describe("getting an artist", () => { describe("when the artist exists", () => { describe("and has many similar artists", () => { - const album1: Album = anAlbum({ genre: asGenre("Pop") }); + const album1 = anAlbumSummary({ genre: asGenre("Pop") }); - const album2: Album = anAlbum({ genre: asGenre("Pop") }); + const album2 = anAlbumSummary({ genre: asGenre("Pop") }); - const artist: Artist = anArtist({ + const artist = anArtist({ albums: [album1, album2], similarArtists: [ aSimilarArtist({ @@ -853,7 +907,7 @@ describe("SubsonicMusicLibrary", () => { }); it("should return the similar artists", async () => { - const result: Artist = await subsonic.artist(artist.id!); + const result = await subsonic.artist(artist.id!); expect(result).toEqual({ id: `${artist.id}`, @@ -890,11 +944,11 @@ describe("SubsonicMusicLibrary", () => { }); describe("and has one similar artist", () => { - const album1: Album = anAlbum({ genre: asGenre("G1") }); + const album1 = anAlbumSummary({ genre: asGenre("G1") }); - const album2: Album = anAlbum({ genre: asGenre("G2") }); + const album2 = anAlbumSummary({ genre: asGenre("G2") }); - const artist: Artist = anArtist({ + const artist = anArtist({ albums: [album1, album2], similarArtists: [ aSimilarArtist({ @@ -916,7 +970,7 @@ describe("SubsonicMusicLibrary", () => { }); it("should return the similar artists", async () => { - const result: Artist = await subsonic.artist(artist.id!); + const result = await subsonic.artist(artist.id!); expect(result).toEqual({ id: artist.id, @@ -953,11 +1007,11 @@ describe("SubsonicMusicLibrary", () => { }); describe("and has no similar artists", () => { - const album1: Album = anAlbum({ genre: asGenre("Jock") }); + const album1 = anAlbumSummary({ genre: asGenre("Jock") }); - const album2: Album = anAlbum({ genre: asGenre("Mock") }); + const album2 = anAlbumSummary({ genre: asGenre("Mock") }); - const artist: Artist = anArtist({ + const artist = anArtist({ albums: [album1, album2], similarArtists: [], }); @@ -973,7 +1027,7 @@ describe("SubsonicMusicLibrary", () => { }); it("should return the similar artists", async () => { - const result: Artist = await subsonic.artist(artist.id!); + const result = await subsonic.artist(artist.id!); expect(result).toEqual({ id: artist.id, @@ -1180,7 +1234,7 @@ describe("SubsonicMusicLibrary", () => { }); it("should use the external url", async () => { - const result: Artist = await subsonic.artist(artist.id!); + const result = await subsonic.artist(artist.id!); expect(result).toEqual({ id: artist.id, @@ -1220,7 +1274,7 @@ describe("SubsonicMusicLibrary", () => { }); describe("and has a good medium external image uri from getArtistInfo route", () => { - const artist: Artist = anArtist({ + const artist = anArtist({ albums: [], similarArtists: [], }); @@ -1231,7 +1285,7 @@ describe("SubsonicMusicLibrary", () => { mockGET .mockImplementationOnce(() => Promise.resolve( - ok(getArtistJson(artist, { artistImageUrl: dodgyImageUrl })) + ok(getArtistJson(artist)) ) ) .mockImplementationOnce(() => @@ -1249,7 +1303,7 @@ describe("SubsonicMusicLibrary", () => { }); it("should use the external url", async () => { - const result: Artist = await subsonic.artist(artist.id!); + const result = await subsonic.artist(artist.id!); expect(result).toEqual({ id: artist.id, @@ -1289,9 +1343,9 @@ describe("SubsonicMusicLibrary", () => { }); describe("and has multiple albums", () => { - const album1: Album = anAlbum({ genre: asGenre("Pop") }); + const album1 = anAlbumSummary({ genre: asGenre("Pop") }); - const album2: Album = anAlbum({ genre: asGenre("Flop") }); + const album2 = anAlbumSummary({ genre: asGenre("Flop") }); const artist: Artist = anArtist({ albums: [album1, album2], @@ -1346,7 +1400,7 @@ describe("SubsonicMusicLibrary", () => { }); describe("and has only 1 album", () => { - const album: Album = anAlbum({ genre: asGenre("Pop") }); + const album = anAlbumSummary({ genre: POP }); const artist: Artist = anArtist({ albums: [album], @@ -1915,7 +1969,7 @@ describe("SubsonicMusicLibrary", () => { describe("when the artist has only 1 album", () => { const artist = anArtist({ name: "one hit wonder", - albums: [anAlbum({ genre: asGenre("Pop") })], + albums: [anAlbumSummary({ genre: asGenre("Pop") })], }); const artists = [artist]; const albums = artists.flatMap((artist) => artist.albums); @@ -2028,17 +2082,17 @@ describe("SubsonicMusicLibrary", () => { const artist1 = anArtist({ name: "abba", albums: [ - anAlbum({ name: "album1", genre: genre1 }), - anAlbum({ name: "album2", genre: genre2 }), - anAlbum({ name: "album3", genre: genre3 }), + anAlbumSummary({ name: "album1", genre: genre1 }), + anAlbumSummary({ name: "album2", genre: genre2 }), + anAlbumSummary({ name: "album3", genre: genre3 }), ], }); const artist2 = anArtist({ name: "babba", albums: [ - anAlbum({ name: "album4", genre: genre1 }), - anAlbum({ name: "album5", genre: genre2 }), - anAlbum({ name: "album6", genre: genre3 }), + anAlbumSummary({ name: "album4", genre: genre1 }), + anAlbumSummary({ name: "album5", genre: genre2 }), + anAlbumSummary({ name: "album6", genre: genre3 }), ], }); const artists = [artist1, artist2]; @@ -2148,11 +2202,11 @@ describe("SubsonicMusicLibrary", () => { describe("when the number of albums reported by getArtists does not match that of getAlbums", () => { const genre = asGenre("lofi"); - const album1 = anAlbum({ name: "album1", genre }); - const album2 = anAlbum({ name: "album2", genre }); - const album3 = anAlbum({ name: "album3", genre }); - const album4 = anAlbum({ name: "album4", genre }); - const album5 = anAlbum({ name: "album5", genre }); + const album1 = anAlbumSummary({ name: "album1", genre }); + const album2 = anAlbumSummary({ name: "album2", genre }); + const album3 = anAlbumSummary({ name: "album3", genre }); + const album4 = anAlbumSummary({ name: "album4", genre }); + const album5 = anAlbumSummary({ name: "album5", genre }); // the artists have 5 albums in the getArtists endpoint const artist1 = anArtist({ @@ -2476,7 +2530,7 @@ describe("SubsonicMusicLibrary", () => { ok( asArtistsJson([ // artist1 has lost 2 albums on the getArtists end point - { ...artist1, albums: [album1, album2] }, + anArtist({ ...artist1, albums: [album1, album2] }), artist2, ]) ) @@ -2504,7 +2558,11 @@ describe("SubsonicMusicLibrary", () => { const result = await subsonic.albums(q); expect(result).toEqual({ - results: [album3, album4, album5], + results: [ + album3, + album4, + album5 + ], total: 5, }); @@ -2534,49 +2592,6 @@ describe("SubsonicMusicLibrary", () => { }); }); - describe("getting an album", () => { - beforeEach(() => { - customPlayers.encodingFor.mockReturnValue(O.none); - }); - - describe("when it exists", () => { - const genre = asGenre("Pop"); - - const album = anAlbum({ genre }); - - const artist = anArtist({ albums: [album] }); - - const tracks = [ - aTrack({ artist, album, genre }), - aTrack({ artist, album, genre }), - aTrack({ artist, album, genre }), - aTrack({ artist, album, genre }), - ]; - - beforeEach(() => { - mockGET.mockImplementationOnce(() => - Promise.resolve(ok(getAlbumJson(artist, album, tracks))) - ); - }); - - it("should return the album", async () => { - const result = await subsonic.album(album.id); - - expect(result).toEqual(album); - - expect(axios.get).toHaveBeenCalledWith( - url.append({ pathname: "/rest/getAlbum" }).href(), - { - params: asURLSearchParams({ - ...authParamsPlusJson, - id: album.id, - }), - headers, - } - ); - }); - }); - }); describe("getting tracks", () => { describe("for an album", () => { @@ -2589,21 +2604,20 @@ describe("SubsonicMusicLibrary", () => { const hipHop = asGenre("Hip-Hop"); const tripHop = asGenre("Trip-Hop"); - const album = anAlbum({ + const albumSummary = anAlbumSummary({ id: "album1", name: "Burnin", genre: hipHop, }); - const artist = anArtist({ + const artistSummary = anArtistSummary({ id: "artist1", - name: "Bob Marley", - albums: [album], + name: "Bob Marley" }); const track1 = aTrack({ - artist: artistToArtistSummary(artist), - album: albumToAlbumSummary(album), + artist: artistSummary, + album: albumSummary, genre: hipHop, rating: { love: true, @@ -2611,8 +2625,8 @@ describe("SubsonicMusicLibrary", () => { }, }); const track2 = aTrack({ - artist: artistToArtistSummary(artist), - album: albumToAlbumSummary(album), + artist: artistSummary, + album: albumSummary, genre: hipHop, rating: { love: false, @@ -2620,8 +2634,8 @@ describe("SubsonicMusicLibrary", () => { }, }); const track3 = aTrack({ - artist: artistToArtistSummary(artist), - album: albumToAlbumSummary(album), + artist: artistSummary, + album: albumSummary, genre: tripHop, rating: { love: true, @@ -2629,8 +2643,8 @@ describe("SubsonicMusicLibrary", () => { }, }); const track4 = aTrack({ - artist: artistToArtistSummary(artist), - album: albumToAlbumSummary(album), + artist: artistSummary, + album: albumSummary, genre: tripHop, rating: { love: false, @@ -2638,18 +2652,35 @@ describe("SubsonicMusicLibrary", () => { }, }); - const tracks = [track1, track2, track3, track4]; + const album = anAlbum({ + ...albumSummary, + tracks: [track1, track2, track3, track4] + }) beforeEach(() => { mockGET.mockImplementationOnce(() => - Promise.resolve(ok(getAlbumJson(artist, album, tracks))) + Promise.resolve(ok(getAlbumJson(album))) ); }); it("should return the album", async () => { const result = await subsonic.tracks(album.id); - expect(result).toEqual([track1, track2, track3, track4]); + // todo: not this + const blatRating = (t: Track) => ({ + ...t, + rating: { + love: false, + stars: 0 + } + }) + + expect(result).toEqual([ + blatRating(track1), + blatRating(track2), + blatRating(track3), + blatRating(track4), + ]); expect(axios.get).toHaveBeenCalledWith( url.append({ pathname: "/rest/getAlbum" }).href(), @@ -2665,38 +2696,48 @@ describe("SubsonicMusicLibrary", () => { }); describe("when the album has only 1 track", () => { + // todo: why do i care about the genre in here? const flipFlop = asGenre("Flip-Flop"); - const album = anAlbum({ + const albumSummary = anAlbumSummary({ id: "album1", name: "Burnin", genre: flipFlop, }); - const artist = anArtist({ + const artistSummary = anArtistSummary({ id: "artist1", - name: "Bob Marley", - albums: [album], + name: "Bob Marley" }); const track = aTrack({ - artist: artistToArtistSummary(artist), - album: albumToAlbumSummary(album), + artist: artistSummary, + album: albumSummary, genre: flipFlop, }); - const tracks = [track]; + const album = anAlbum({ + ...albumSummary, + tracks: [track] + }); beforeEach(() => { mockGET.mockImplementationOnce(() => - Promise.resolve(ok(getAlbumJson(artist, album, tracks))) + Promise.resolve(ok(getAlbumJson(album))) ); }); it("should return the album", async () => { const result = await subsonic.tracks(album.id); - expect(result).toEqual([track]); + expect(result).toEqual([{ + ...track, + // todo: not sure about this + rating: { + love: false, + stars: 0 + } + }]); expect(axios.get).toHaveBeenCalledWith( url.append({ pathname: "/rest/getAlbum" }).href(), @@ -2711,20 +2752,14 @@ describe("SubsonicMusicLibrary", () => { }); }); - describe("when the album has only no tracks", () => { - const album = anAlbum({ id: "album1", name: "Burnin" }); - - const artist = anArtist({ - id: "artist1", - name: "Bob Marley", - albums: [album], - }); - - const tracks: Track[] = []; - + describe("when the album has no tracks", () => { + const album = anAlbum({ + tracks: [] + }) + beforeEach(() => { mockGET.mockImplementationOnce(() => - Promise.resolve(ok(getAlbumJson(artist, album, tracks))) + Promise.resolve(ok(getAlbumJson(album))) ); }); @@ -2751,17 +2786,16 @@ describe("SubsonicMusicLibrary", () => { const hipHop = asGenre("Hip-Hop"); const tripHop = asGenre("Trip-Hop"); - const album = anAlbum({ id: "album1", name: "Burnin", genre: hipHop }); + const albumSummary = anAlbumSummary({ id: "album1", name: "Burnin", genre: hipHop }); - const artist = anArtist({ + const artistSummary = anArtistSummary({ id: "artist1", - name: "Bob Marley", - albums: [album], + name: "Bob Marley" }); const alac = aTrack({ - artist: artistToArtistSummary(artist), - album: albumToAlbumSummary(album), + artist: artistSummary, + album: albumSummary, encoding: { player: "bonob", mimeType: "audio/alac", @@ -2773,8 +2807,8 @@ describe("SubsonicMusicLibrary", () => { }, }); const m4a = aTrack({ - artist: artistToArtistSummary(artist), - album: albumToAlbumSummary(album), + artist: artistSummary, + album: albumSummary, encoding: { player: "bonob", mimeType: "audio/m4a", @@ -2786,8 +2820,8 @@ describe("SubsonicMusicLibrary", () => { }, }); const mp3 = aTrack({ - artist: artistToArtistSummary(artist), - album: albumToAlbumSummary(album), + artist: artistSummary, + album: albumSummary, encoding: { player: "bonob", mimeType: "audio/mp3", @@ -2799,6 +2833,11 @@ describe("SubsonicMusicLibrary", () => { }, }); + const album = anAlbum({ + ...albumSummary, + tracks: [alac, m4a, mp3] + }) + beforeEach(() => { customPlayers.encodingFor .mockReturnValueOnce( @@ -2810,7 +2849,7 @@ describe("SubsonicMusicLibrary", () => { .mockReturnValueOnce(O.none); mockGET.mockImplementationOnce(() => - Promise.resolve(ok(getAlbumJson(artist, album, [alac, m4a, mp3]))) + Promise.resolve(ok(getAlbumJson(album))) ); }); @@ -2824,6 +2863,11 @@ describe("SubsonicMusicLibrary", () => { player: "bonob+audio/alac", mimeType: "audio/flac", }, + // todo: this doesnt seem right? why dont the ratings come back? + rating: { + love: false, + stars: 0 + } }, { ...m4a, @@ -2831,6 +2875,10 @@ describe("SubsonicMusicLibrary", () => { player: "bonob+audio/m4a", mimeType: "audio/opus", }, + rating: { + love: false, + stars: 0 + } }, { ...mp3, @@ -2838,6 +2886,10 @@ describe("SubsonicMusicLibrary", () => { player: "bonob", mimeType: "audio/mp3", }, + rating: { + love: false, + stars: 0 + } }, ]); @@ -2899,7 +2951,7 @@ describe("SubsonicMusicLibrary", () => { Promise.resolve(ok(getSongJson(track))) ) .mockImplementationOnce(() => - Promise.resolve(ok(getAlbumJson(artist, album, []))) + Promise.resolve(ok(getAlbumJson(album))) ); const result = await subsonic.track(track.id); @@ -2950,7 +3002,7 @@ describe("SubsonicMusicLibrary", () => { Promise.resolve(ok(getSongJson(track))) ) .mockImplementationOnce(() => - Promise.resolve(ok(getAlbumJson(artist, album, []))) + Promise.resolve(ok(getAlbumJson(album))) ); const result = await subsonic.track(track.id); @@ -3027,7 +3079,7 @@ describe("SubsonicMusicLibrary", () => { Promise.resolve(ok(getSongJson(track))) ) .mockImplementationOnce(() => - Promise.resolve(ok(getAlbumJson(artist, album, []))) + Promise.resolve(ok(getAlbumJson(album))) ) .mockImplementationOnce(() => Promise.resolve(streamResponse)); @@ -3064,7 +3116,7 @@ describe("SubsonicMusicLibrary", () => { Promise.resolve(ok(getSongJson(track))) ) .mockImplementationOnce(() => - Promise.resolve(ok(getAlbumJson(artist, album, []))) + Promise.resolve(ok(getAlbumJson(album))) ) .mockImplementationOnce(() => Promise.resolve(streamResponse)); @@ -3103,7 +3155,7 @@ describe("SubsonicMusicLibrary", () => { Promise.resolve(ok(getSongJson(track))) ) .mockImplementationOnce(() => - Promise.resolve(ok(getAlbumJson(artist, album, []))) + Promise.resolve(ok(getAlbumJson(album))) ) .mockImplementationOnce(() => Promise.resolve(streamResponse)); @@ -3153,7 +3205,7 @@ describe("SubsonicMusicLibrary", () => { Promise.resolve(ok(getSongJson(track))) ) .mockImplementationOnce(() => - Promise.resolve(ok(getAlbumJson(artist, album, []))) + Promise.resolve(ok(getAlbumJson(album))) ) .mockImplementationOnce(() => Promise.resolve(streamResponse)); @@ -3172,7 +3224,7 @@ describe("SubsonicMusicLibrary", () => { Promise.resolve(ok(getSongJson(track))) ) .mockImplementationOnce(() => - Promise.resolve(ok(getAlbumJson(artist, album, []))) + Promise.resolve(ok(getAlbumJson(album))) ) .mockImplementationOnce(() => Promise.reject("IO error occured") @@ -3209,7 +3261,7 @@ describe("SubsonicMusicLibrary", () => { Promise.resolve(ok(getSongJson(track))) ) .mockImplementationOnce(() => - Promise.resolve(ok(getAlbumJson(artist, album, []))) + Promise.resolve(ok(getAlbumJson(album))) ) .mockImplementationOnce(() => Promise.resolve(streamResponse)); @@ -3272,7 +3324,7 @@ describe("SubsonicMusicLibrary", () => { ) .mockImplementationOnce(() => Promise.resolve( - ok(getAlbumJson(artist, album, [trackWithCustomPlayer])) + ok(getAlbumJson(album)) ) ) .mockImplementationOnce(() => Promise.resolve(streamResponse)); @@ -3314,7 +3366,7 @@ describe("SubsonicMusicLibrary", () => { ) .mockImplementationOnce(() => Promise.resolve( - ok(getAlbumJson(artist, album, [trackWithCustomPlayer])) + ok(getAlbumJson(album)) ) ) .mockImplementationOnce(() => Promise.resolve(streamResponse)); @@ -3588,7 +3640,7 @@ describe("SubsonicMusicLibrary", () => { Promise.resolve(ok(getSongJson(track))) ) .mockImplementationOnce(() => - Promise.resolve(ok(getAlbumJson(artist, album, []))) + Promise.resolve(ok(getAlbumJson(album))) ) .mockImplementationOnce(() => Promise.resolve(ok(EMPTY))); @@ -3623,7 +3675,7 @@ describe("SubsonicMusicLibrary", () => { Promise.resolve(ok(getSongJson(track))) ) .mockImplementationOnce(() => - Promise.resolve(ok(getAlbumJson(artist, album, []))) + Promise.resolve(ok(getAlbumJson(album))) ) .mockImplementationOnce(() => Promise.resolve(ok(EMPTY))); @@ -3661,7 +3713,7 @@ describe("SubsonicMusicLibrary", () => { Promise.resolve(ok(getSongJson(track))) ) .mockImplementationOnce(() => - Promise.resolve(ok(getAlbumJson(artist, album, []))) + Promise.resolve(ok(getAlbumJson(album))) ); const result = await subsonic.rate(trackId, { love: true, stars: 0 }); @@ -3686,7 +3738,7 @@ describe("SubsonicMusicLibrary", () => { Promise.resolve(ok(getSongJson(track))) ) .mockImplementationOnce(() => - Promise.resolve(ok(getAlbumJson(artist, album, []))) + Promise.resolve(ok(getAlbumJson(album))) ) .mockImplementationOnce(() => Promise.resolve(ok(EMPTY))); @@ -3725,7 +3777,7 @@ describe("SubsonicMusicLibrary", () => { Promise.resolve(ok(getSongJson(track))) ) .mockImplementationOnce(() => - Promise.resolve(ok(getAlbumJson(artist, album, []))) + Promise.resolve(ok(getAlbumJson(album))) ); const result = await subsonic.rate(trackId, { love: true, stars: 3 }); @@ -3750,7 +3802,7 @@ describe("SubsonicMusicLibrary", () => { Promise.resolve(ok(getSongJson(track))) ) .mockImplementationOnce(() => - Promise.resolve(ok(getAlbumJson(artist, album, []))) + Promise.resolve(ok(getAlbumJson(album))) ) .mockImplementationOnce(() => Promise.resolve(ok(EMPTY))) .mockImplementationOnce(() => Promise.resolve(ok(EMPTY))); @@ -4025,10 +4077,7 @@ describe("SubsonicMusicLibrary", () => { describe("searchAlbums", () => { describe("when there is 1 search results", () => { it("should return true", async () => { - const album = anAlbum({ - name: "foo woo", - genre: { id: b64Encode("pop"), name: "pop" }, - }); + const album = anAlbum({ name: "foo woo" }); const artist = anArtist({ name: "#1", albums: [album] }); mockGET.mockImplementationOnce(() => @@ -4039,7 +4088,12 @@ describe("SubsonicMusicLibrary", () => { const result = await subsonic.searchAlbums("foo"); - expect(result).toEqual([albumToAlbumSummary(album)]); + expect(result).toEqual([ + { + ...albumToAlbumSummary(album), + genre: undefined + } + ]); expect(mockGET).toHaveBeenCalledWith( url.append({ pathname: "/rest/search3" }).href(), @@ -4059,16 +4113,10 @@ describe("SubsonicMusicLibrary", () => { describe("when there are many search results", () => { it("should return true", async () => { - const album1 = anAlbum({ - name: "album1", - genre: { id: b64Encode("pop"), name: "pop" }, - }); + const album1 = anAlbum({ name: "album1" }); const artist1 = anArtist({ name: "artist1", albums: [album1] }); - const album2 = anAlbum({ - name: "album2", - genre: { id: b64Encode("pop"), name: "pop" }, - }); + const album2 = anAlbum({ name: "album2" }); const artist2 = anArtist({ name: "artist2", albums: [album2] }); mockGET.mockImplementationOnce(() => @@ -4087,8 +4135,14 @@ describe("SubsonicMusicLibrary", () => { const result = await subsonic.searchAlbums("moo"); expect(result).toEqual([ - albumToAlbumSummary(album1), - albumToAlbumSummary(album2), + { + ...albumToAlbumSummary(album1), + genre: undefined + }, + { + ...albumToAlbumSummary(album2), + genre: undefined + }, ]); expect(mockGET).toHaveBeenCalledWith( @@ -4161,7 +4215,7 @@ describe("SubsonicMusicLibrary", () => { ) .mockImplementationOnce(() => Promise.resolve(ok(getSongJson(track)))) .mockImplementationOnce(() => - Promise.resolve(ok(getAlbumJson(artist, album, []))) + Promise.resolve(ok(getAlbumJson(album))) ); const result = await subsonic.searchTracks("foo"); @@ -4231,10 +4285,10 @@ describe("SubsonicMusicLibrary", () => { Promise.resolve(ok(getSongJson(track2))) ) .mockImplementationOnce(() => - Promise.resolve(ok(getAlbumJson(artist1, album1, []))) + Promise.resolve(ok(getAlbumJson(album1))) ) .mockImplementationOnce(() => - Promise.resolve(ok(getAlbumJson(artist2, album2, []))) + Promise.resolve(ok(getAlbumJson(album2))) ); const result = await subsonic.searchTracks("moo"); @@ -4606,7 +4660,7 @@ describe("SubsonicMusicLibrary", () => { Promise.resolve(ok(getSimilarSongsJson([track1]))) ) .mockImplementationOnce(() => - Promise.resolve(ok(getAlbumJson(artist1, album1, []))) + Promise.resolve(ok(getAlbumJson(album1))) ); const result = await subsonic.similarSongs(id); @@ -4671,13 +4725,13 @@ describe("SubsonicMusicLibrary", () => { Promise.resolve(ok(getSimilarSongsJson([track1, track2, track3]))) ) .mockImplementationOnce(() => - Promise.resolve(ok(getAlbumJson(artist1, album1, []))) + Promise.resolve(ok(getAlbumJson(album1))) ) .mockImplementationOnce(() => - Promise.resolve(ok(getAlbumJson(artist2, album2, []))) + Promise.resolve(ok(getAlbumJson(album2))) ) .mockImplementationOnce(() => - Promise.resolve(ok(getAlbumJson(artist1, album1, []))) + Promise.resolve(ok(getAlbumJson(album1))) ); const result = await subsonic.similarSongs(id); @@ -4773,7 +4827,7 @@ describe("SubsonicMusicLibrary", () => { Promise.resolve(ok(getTopSongsJson([track1]))) ) .mockImplementationOnce(() => - Promise.resolve(ok(getAlbumJson(artist, album1, []))) + Promise.resolve(ok(getAlbumJson(album1))) ); const result = await subsonic.topSongs(artistId); @@ -4835,13 +4889,13 @@ describe("SubsonicMusicLibrary", () => { Promise.resolve(ok(getTopSongsJson([track1, track2, track3]))) ) .mockImplementationOnce(() => - Promise.resolve(ok(getAlbumJson(artist, album1, []))) + Promise.resolve(ok(getAlbumJson(album1))) ) .mockImplementationOnce(() => - Promise.resolve(ok(getAlbumJson(artist, album2, []))) + Promise.resolve(ok(getAlbumJson(album2))) ) .mockImplementationOnce(() => - Promise.resolve(ok(getAlbumJson(artist, album1, []))) + Promise.resolve(ok(getAlbumJson(album1))) ); const result = await subsonic.topSongs(artistId);