Add similar artists to Artist

This commit is contained in:
simojenki
2021-03-13 18:56:46 +11:00
parent d07efd97cf
commit 439c2eae87
4 changed files with 414 additions and 187 deletions

View File

@@ -42,6 +42,7 @@ export const NO_IMAGES: Images = {
export type Artist = ArtistSummary & { export type Artist = ArtistSummary & {
image: Images image: Images
albums: AlbumSummary[]; albums: AlbumSummary[];
similarArtists: ArtistSummary[]
}; };
export type AlbumSummary = { export type AlbumSummary = {

View File

@@ -24,6 +24,7 @@ import sharp from "sharp";
import axios, { AxiosRequestConfig } from "axios"; import axios, { AxiosRequestConfig } from "axios";
import { Encryption } from "./encryption"; import { Encryption } from "./encryption";
import randomString from "./random_string"; import randomString from "./random_string";
import { fold } from "fp-ts/lib/Option";
export const BROWSER_HEADERS = { export const BROWSER_HEADERS = {
accept: accept:
@@ -117,10 +118,12 @@ export type artistInfo = {
smallImageUrl: string | undefined; smallImageUrl: string | undefined;
mediumImageUrl: string | undefined; mediumImageUrl: string | undefined;
largeImageUrl: string | undefined; largeImageUrl: string | undefined;
similarArtist: artistSummary[]
}; };
export type ArtistInfo = { export type ArtistInfo = {
image: Images; image: Images;
similarArtist: {id:string, name:string}[]
}; };
export type GetArtistInfoResponse = SubsonicResponse & { export type GetArtistInfoResponse = SubsonicResponse & {
@@ -255,6 +258,7 @@ export class Navidrome implements MusicService {
"subsonic-response.albumList.album", "subsonic-response.albumList.album",
"subsonic-response.album.song", "subsonic-response.album.song",
"subsonic-response.genres.genre", "subsonic-response.genres.genre",
"subsonic-response.artistInfo.similarArtist"
], ],
}).xml2js(response.data) as SubconicEnvelope }).xml2js(response.data) as SubconicEnvelope
) )
@@ -301,6 +305,7 @@ export class Navidrome implements MusicService {
medium: validate(it.artistInfo.mediumImageUrl), medium: validate(it.artistInfo.mediumImageUrl),
large: validate(it.artistInfo.largeImageUrl), large: validate(it.artistInfo.largeImageUrl),
}, },
similarArtist: (it.artistInfo.similarArtist || []).map(artist => ({id: artist._id, name: artist._name}))
})); }));
getAlbum = (credentials: Credentials, id: string): Promise<Album> => getAlbum = (credentials: Credentials, id: string): Promise<Album> =>
@@ -341,6 +346,7 @@ export class Navidrome implements MusicService {
name: artist.name, name: artist.name,
image: artistInfo.image, image: artistInfo.image,
albums: artist.albums, albums: artist.albums,
similarArtists: artistInfo.similarArtist
})); }));
getCoverArt = (credentials: Credentials, id: string, size?: number) => getCoverArt = (credentials: Credentials, id: string, size?: number) =>
@@ -372,16 +378,15 @@ export class Navidrome implements MusicService {
albums: (q: AlbumQuery): Promise<Result<AlbumSummary>> => albums: (q: AlbumQuery): Promise<Result<AlbumSummary>> =>
navidrome navidrome
.getJSON<GetAlbumListResponse>(credentials, "/rest/getAlbumList", { .getJSON<GetAlbumListResponse>(credentials, "/rest/getAlbumList", {
...pipe( ...fold(
O.fromNullable(q.genre), () => ({
O.map<string, getAlbumListParams>((genre) => ({ type: "alphabeticalByArtist",
}),
(genre) => ({
type: "byGenre", type: "byGenre",
genre, genre,
})), })
O.getOrElse<getAlbumListParams>(() => ({ )(O.fromNullable(q.genre)),
type: "alphabeticalByArtist",
}))
),
size: MAX_ALBUM_LIST, size: MAX_ALBUM_LIST,
offset: 0, offset: 0,
}) })

View File

@@ -79,6 +79,10 @@ export function anArtist(fields: Partial<Artist> = {}): Artist {
medium: `/artist/art/${id}/small`, medium: `/artist/art/${id}/small`,
large: `/artist/art/${id}/large`, large: `/artist/art/${id}/large`,
}, },
similarArtists: [
{ id: uuid(), name: "Similar artist1"},
{ id: uuid(), name: "Similar artist2"},
],
...fields, ...fields,
}; };
} }
@@ -134,6 +138,7 @@ export const BLONDIE: Artist = {
medium: undefined, medium: undefined,
large: undefined, large: undefined,
}, },
similarArtists: []
}; };
export const BOB_MARLEY: Artist = { export const BOB_MARLEY: Artist = {
@@ -149,6 +154,7 @@ export const BOB_MARLEY: Artist = {
medium: "http://localhost/BOB_MARLEY/med", medium: "http://localhost/BOB_MARLEY/med",
large: "http://localhost/BOB_MARLEY/lge", large: "http://localhost/BOB_MARLEY/lge",
}, },
similarArtists: []
}; };
export const MADONNA: Artist = { export const MADONNA: Artist = {
@@ -160,6 +166,7 @@ export const MADONNA: Artist = {
medium: undefined, medium: undefined,
large: "http://localhost/MADONNA/lge", large: "http://localhost/MADONNA/lge",
}, },
similarArtists: []
}; };
export const METALLICA: Artist = { export const METALLICA: Artist = {
@@ -184,6 +191,7 @@ export const METALLICA: Artist = {
medium: "http://localhost/METALLICA/med", medium: "http://localhost/METALLICA/med",
large: "http://localhost/METALLICA/lge", large: "http://localhost/METALLICA/lge",
}, },
similarArtists: []
}; };
export const ALL_ARTISTS = [BOB_MARLEY, BLONDIE, MADONNA, METALLICA]; export const ALL_ARTISTS = [BOB_MARLEY, BLONDIE, MADONNA, METALLICA];

View File

@@ -68,15 +68,16 @@ const ok = (data: string) => ({
}); });
const artistInfoXml = ( const artistInfoXml = (
images: Images artist: Artist,
) => `<subsonic-response xmlns="http://subsonic.org/restapi" status="ok" version="1.16.1" type="navidrome" serverVersion="0.40.0 (8799358a)"> ) => `<subsonic-response xmlns="http://subsonic.org/restapi" status="ok" version="1.16.1" type="navidrome" serverVersion="0.40.0 (8799358a)">
<artistInfo> <artistInfo>
<biography></biography> <biography></biography>
<musicBrainzId></musicBrainzId> <musicBrainzId></musicBrainzId>
<lastFmUrl></lastFmUrl> <lastFmUrl></lastFmUrl>
<smallImageUrl>${images.small || ""}</smallImageUrl> <smallImageUrl>${artist.image.small || ""}</smallImageUrl>
<mediumImageUrl>${images.medium || ""}</mediumImageUrl> <mediumImageUrl>${artist.image.medium || ""}</mediumImageUrl>
<largeImageUrl>${images.large || ""}</largeImageUrl> <largeImageUrl>${artist.image.large || ""}</largeImageUrl>
${artist.similarArtists.map(it => `<similarArtist id="${it.id}" name="${it.name}" albumCount="3"></similarArtist>`)}
</artistInfo> </artistInfo>
</subsonic-response>`; </subsonic-response>`;
@@ -296,205 +297,412 @@ describe("Navidrome", () => {
}); });
describe("getting an artist", () => { describe("getting an artist", () => {
describe("when the artist exists and has dodgy looking artist image uris", () => { describe("when the artist exists", () => {
const album1: Album = anAlbum(); describe("and has many similar artists", () => {
const album1: Album = anAlbum();
const album2: Album = anAlbum(); const album2: Album = anAlbum();
const artist: Artist = anArtist({ const artist: Artist = anArtist({
albums: [album1, album2], albums: [album1, album2],
image: {
small: `http://localhost:80/${DODGY_IMAGE_NAME}`,
medium: `http://localhost:80/${DODGY_IMAGE_NAME}`,
large: `http://localhost:80/${DODGY_IMAGE_NAME}`,
},
});
beforeEach(() => {
mockGET
.mockImplementationOnce(() => Promise.resolve(ok(PING_OK)))
.mockImplementationOnce(() => Promise.resolve(ok(artistXml(artist))))
.mockImplementationOnce(() =>
Promise.resolve(ok(artistInfoXml(artist.image)))
);
});
it("should return remove the dodgy looking image uris and return undefined", async () => {
const result: Artist = await navidrome
.generateToken({ username, password })
.then((it) => it as AuthSuccess)
.then((it) => navidrome.login(it.authToken))
.then((it) => it.artist(artist.id));
expect(result).toEqual({
id: artist.id,
name: artist.name,
image: { image: {
small: undefined, small: `http://localhost:80/${DODGY_IMAGE_NAME}`,
medium: undefined, medium: `http://localhost:80/${DODGY_IMAGE_NAME}`,
large: undefined, large: `http://localhost:80/${DODGY_IMAGE_NAME}`,
}, },
albums: artist.albums, similarArtists: [{ id: "similar1.id", name: "similar1" }, { id: "similar2.id", name: "similar2" }],
}); });
expect(axios.get).toHaveBeenCalledWith(`${url}/rest/getArtist`, { beforeEach(() => {
params: { mockGET
.mockImplementationOnce(() => Promise.resolve(ok(PING_OK)))
.mockImplementationOnce(() =>
Promise.resolve(ok(artistXml(artist)))
)
.mockImplementationOnce(() =>
Promise.resolve(ok(artistInfoXml(artist)))
);
});
it("should return the similar artists", async () => {
const result: Artist = await navidrome
.generateToken({ username, password })
.then((it) => it as AuthSuccess)
.then((it) => navidrome.login(it.authToken))
.then((it) => it.artist(artist.id));
expect(result).toEqual({
id: artist.id, id: artist.id,
...authParams, name: artist.name,
image: {
small: undefined,
medium: undefined,
large: undefined,
},
albums: artist.albums,
similarArtists: artist.similarArtists
});
expect(axios.get).toHaveBeenCalledWith(`${url}/rest/getArtist`, {
params: {
id: artist.id,
...authParams,
},
headers,
});
expect(axios.get).toHaveBeenCalledWith(`${url}/rest/getArtistInfo`, {
params: {
id: artist.id,
...authParams,
},
headers,
});
});
});
describe("and has one similar artists", () => {
const album1: Album = anAlbum();
const album2: Album = anAlbum();
const artist: Artist = anArtist({
albums: [album1, album2],
image: {
small: `http://localhost:80/${DODGY_IMAGE_NAME}`,
medium: `http://localhost:80/${DODGY_IMAGE_NAME}`,
large: `http://localhost:80/${DODGY_IMAGE_NAME}`,
}, },
headers, similarArtists: [{ id: "similar1.id", name: "similar1" }],
}); });
expect(axios.get).toHaveBeenCalledWith(`${url}/rest/getArtistInfo`, { beforeEach(() => {
params: { mockGET
.mockImplementationOnce(() => Promise.resolve(ok(PING_OK)))
.mockImplementationOnce(() =>
Promise.resolve(ok(artistXml(artist)))
)
.mockImplementationOnce(() =>
Promise.resolve(ok(artistInfoXml(artist)))
);
});
it("should return the similar artists", async () => {
const result: Artist = await navidrome
.generateToken({ username, password })
.then((it) => it as AuthSuccess)
.then((it) => navidrome.login(it.authToken))
.then((it) => it.artist(artist.id));
expect(result).toEqual({
id: artist.id, id: artist.id,
...authParams, name: artist.name,
image: {
small: undefined,
medium: undefined,
large: undefined,
},
albums: artist.albums,
similarArtists: artist.similarArtists
});
expect(axios.get).toHaveBeenCalledWith(`${url}/rest/getArtist`, {
params: {
id: artist.id,
...authParams,
},
headers,
});
expect(axios.get).toHaveBeenCalledWith(`${url}/rest/getArtistInfo`, {
params: {
id: artist.id,
...authParams,
},
headers,
});
});
});
describe("and has no similar artists", () => {
const album1: Album = anAlbum();
const album2: Album = anAlbum();
const artist: Artist = anArtist({
albums: [album1, album2],
image: {
small: `http://localhost:80/${DODGY_IMAGE_NAME}`,
medium: `http://localhost:80/${DODGY_IMAGE_NAME}`,
large: `http://localhost:80/${DODGY_IMAGE_NAME}`,
}, },
headers, similarArtists: [],
});
});
});
describe("when the artist exists and has multiple albums", () => {
const album1: Album = anAlbum();
const album2: Album = anAlbum();
const artist: Artist = anArtist({
albums: [album1, album2],
});
beforeEach(() => {
mockGET
.mockImplementationOnce(() => Promise.resolve(ok(PING_OK)))
.mockImplementationOnce(() => Promise.resolve(ok(artistXml(artist))))
.mockImplementationOnce(() =>
Promise.resolve(ok(artistInfoXml(artist.image)))
);
});
it("should return it", async () => {
const result: Artist = await navidrome
.generateToken({ username, password })
.then((it) => it as AuthSuccess)
.then((it) => navidrome.login(it.authToken))
.then((it) => it.artist(artist.id));
expect(result).toEqual({
id: artist.id,
name: artist.name,
image: artist.image,
albums: artist.albums,
}); });
expect(axios.get).toHaveBeenCalledWith(`${url}/rest/getArtist`, { beforeEach(() => {
params: { mockGET
.mockImplementationOnce(() => Promise.resolve(ok(PING_OK)))
.mockImplementationOnce(() =>
Promise.resolve(ok(artistXml(artist)))
)
.mockImplementationOnce(() =>
Promise.resolve(ok(artistInfoXml(artist)))
);
});
it("should return the similar artists", async () => {
const result: Artist = await navidrome
.generateToken({ username, password })
.then((it) => it as AuthSuccess)
.then((it) => navidrome.login(it.authToken))
.then((it) => it.artist(artist.id));
expect(result).toEqual({
id: artist.id, id: artist.id,
...authParams, name: artist.name,
image: {
small: undefined,
medium: undefined,
large: undefined,
},
albums: artist.albums,
similarArtists: artist.similarArtists
});
expect(axios.get).toHaveBeenCalledWith(`${url}/rest/getArtist`, {
params: {
id: artist.id,
...authParams,
},
headers,
});
expect(axios.get).toHaveBeenCalledWith(`${url}/rest/getArtistInfo`, {
params: {
id: artist.id,
...authParams,
},
headers,
});
});
});
describe("and has dodgy looking artist image uris", () => {
const album1: Album = anAlbum();
const album2: Album = anAlbum();
const artist: Artist = anArtist({
albums: [album1, album2],
image: {
small: `http://localhost:80/${DODGY_IMAGE_NAME}`,
medium: `http://localhost:80/${DODGY_IMAGE_NAME}`,
large: `http://localhost:80/${DODGY_IMAGE_NAME}`,
}, },
headers, similarArtists: [],
}); });
expect(axios.get).toHaveBeenCalledWith(`${url}/rest/getArtistInfo`, { beforeEach(() => {
params: { mockGET
.mockImplementationOnce(() => Promise.resolve(ok(PING_OK)))
.mockImplementationOnce(() =>
Promise.resolve(ok(artistXml(artist)))
)
.mockImplementationOnce(() =>
Promise.resolve(ok(artistInfoXml(artist)))
);
});
it("should return remove the dodgy looking image uris and return undefined", async () => {
const result: Artist = await navidrome
.generateToken({ username, password })
.then((it) => it as AuthSuccess)
.then((it) => navidrome.login(it.authToken))
.then((it) => it.artist(artist.id));
expect(result).toEqual({
id: artist.id, id: artist.id,
...authParams, name: artist.name,
}, image: {
headers, small: undefined,
medium: undefined,
large: undefined,
},
albums: artist.albums,
similarArtists: []
});
expect(axios.get).toHaveBeenCalledWith(`${url}/rest/getArtist`, {
params: {
id: artist.id,
...authParams,
},
headers,
});
expect(axios.get).toHaveBeenCalledWith(`${url}/rest/getArtistInfo`, {
params: {
id: artist.id,
...authParams,
},
headers,
});
}); });
}); });
});
describe("when the artist exists and has only 1 album", () => { describe("and has multiple albums", () => {
const album: Album = anAlbum(); const album1: Album = anAlbum();
const artist: Artist = anArtist({ const album2: Album = anAlbum();
albums: [album],
});
beforeEach(() => { const artist: Artist = anArtist({
mockGET albums: [album1, album2],
.mockImplementationOnce(() => Promise.resolve(ok(PING_OK))) similarArtists: []
.mockImplementationOnce(() => Promise.resolve(ok(artistXml(artist))))
.mockImplementationOnce(() =>
Promise.resolve(ok(artistInfoXml(artist.image)))
);
});
it("should return it", async () => {
const result: Artist = await navidrome
.generateToken({ username, password })
.then((it) => it as AuthSuccess)
.then((it) => navidrome.login(it.authToken))
.then((it) => it.artist(artist.id));
expect(result).toEqual({
id: artist.id,
name: artist.name,
image: artist.image,
albums: artist.albums,
}); });
expect(axios.get).toHaveBeenCalledWith(`${url}/rest/getArtist`, { beforeEach(() => {
params: { mockGET
.mockImplementationOnce(() => Promise.resolve(ok(PING_OK)))
.mockImplementationOnce(() =>
Promise.resolve(ok(artistXml(artist)))
)
.mockImplementationOnce(() =>
Promise.resolve(ok(artistInfoXml(artist)))
);
});
it("should return it", async () => {
const result: Artist = await navidrome
.generateToken({ username, password })
.then((it) => it as AuthSuccess)
.then((it) => navidrome.login(it.authToken))
.then((it) => it.artist(artist.id));
expect(result).toEqual({
id: artist.id, id: artist.id,
...authParams, name: artist.name,
}, image: artist.image,
headers, albums: artist.albums,
similarArtists: []
});
expect(axios.get).toHaveBeenCalledWith(`${url}/rest/getArtist`, {
params: {
id: artist.id,
...authParams,
},
headers,
});
expect(axios.get).toHaveBeenCalledWith(`${url}/rest/getArtistInfo`, {
params: {
id: artist.id,
...authParams,
},
headers,
});
});
});
describe("and has only 1 album", () => {
const album: Album = anAlbum();
const artist: Artist = anArtist({
albums: [album],
similarArtists: []
}); });
expect(axios.get).toHaveBeenCalledWith(`${url}/rest/getArtistInfo`, { beforeEach(() => {
params: { mockGET
.mockImplementationOnce(() => Promise.resolve(ok(PING_OK)))
.mockImplementationOnce(() =>
Promise.resolve(ok(artistXml(artist)))
)
.mockImplementationOnce(() =>
Promise.resolve(ok(artistInfoXml(artist)))
);
});
it("should return it", async () => {
const result: Artist = await navidrome
.generateToken({ username, password })
.then((it) => it as AuthSuccess)
.then((it) => navidrome.login(it.authToken))
.then((it) => it.artist(artist.id));
expect(result).toEqual({
id: artist.id, id: artist.id,
...authParams, name: artist.name,
}, image: artist.image,
headers, albums: artist.albums,
similarArtists: []
});
expect(axios.get).toHaveBeenCalledWith(`${url}/rest/getArtist`, {
params: {
id: artist.id,
...authParams,
},
headers,
});
expect(axios.get).toHaveBeenCalledWith(`${url}/rest/getArtistInfo`, {
params: {
id: artist.id,
...authParams,
},
headers,
});
}); });
}); });
});
describe("when the artist exists and has no albums", () => { describe("and has no albums", () => {
const artist: Artist = anArtist({ const artist: Artist = anArtist({
albums: [],
});
beforeEach(() => {
mockGET
.mockImplementationOnce(() => Promise.resolve(ok(PING_OK)))
.mockImplementationOnce(() => Promise.resolve(ok(artistXml(artist))))
.mockImplementationOnce(() =>
Promise.resolve(ok(artistInfoXml(artist.image)))
);
});
it("should return it", async () => {
const result: Artist = await navidrome
.generateToken({ username, password })
.then((it) => it as AuthSuccess)
.then((it) => navidrome.login(it.authToken))
.then((it) => it.artist(artist.id));
expect(result).toEqual({
id: artist.id,
name: artist.name,
image: artist.image,
albums: [], albums: [],
similarArtists: []
}); });
expect(axios.get).toHaveBeenCalledWith(`${url}/rest/getArtist`, { beforeEach(() => {
params: { mockGET
id: artist.id, .mockImplementationOnce(() => Promise.resolve(ok(PING_OK)))
...authParams, .mockImplementationOnce(() =>
}, Promise.resolve(ok(artistXml(artist)))
headers, )
.mockImplementationOnce(() =>
Promise.resolve(ok(artistInfoXml(artist)))
);
}); });
expect(axios.get).toHaveBeenCalledWith(`${url}/rest/getArtistInfo`, { it("should return it", async () => {
params: { const result: Artist = await navidrome
.generateToken({ username, password })
.then((it) => it as AuthSuccess)
.then((it) => navidrome.login(it.authToken))
.then((it) => it.artist(artist.id));
expect(result).toEqual({
id: artist.id, id: artist.id,
...authParams, name: artist.name,
}, image: artist.image,
headers, albums: [],
similarArtists: []
});
expect(axios.get).toHaveBeenCalledWith(`${url}/rest/getArtist`, {
params: {
id: artist.id,
...authParams,
},
headers,
});
expect(axios.get).toHaveBeenCalledWith(`${url}/rest/getArtistInfo`, {
params: {
id: artist.id,
...authParams,
},
headers,
});
}); });
}); });
}); });
@@ -1388,7 +1596,7 @@ describe("Navidrome", () => {
data: Buffer.from("the image", "ascii"), data: Buffer.from("the image", "ascii"),
}; };
const artist = anArtist({ id: artistId }); const artist = anArtist({ id: artistId, image: images });
mockGET mockGET
.mockImplementationOnce(() => Promise.resolve(ok(PING_OK))) .mockImplementationOnce(() => Promise.resolve(ok(PING_OK)))
@@ -1396,7 +1604,7 @@ describe("Navidrome", () => {
Promise.resolve(ok(artistXml(artist))) Promise.resolve(ok(artistXml(artist)))
) )
.mockImplementationOnce(() => .mockImplementationOnce(() =>
Promise.resolve(ok(artistInfoXml(images))) Promise.resolve(ok(artistInfoXml(artist)))
) )
.mockImplementationOnce(() => Promise.resolve(streamResponse)); .mockImplementationOnce(() => Promise.resolve(streamResponse));
@@ -1454,6 +1662,7 @@ describe("Navidrome", () => {
const artist = anArtist({ const artist = anArtist({
id: artistId, id: artistId,
albums: [album1, album2], albums: [album1, album2],
image: images
}); });
mockGET mockGET
@@ -1462,7 +1671,7 @@ describe("Navidrome", () => {
Promise.resolve(ok(artistXml(artist))) Promise.resolve(ok(artistXml(artist)))
) )
.mockImplementationOnce(() => .mockImplementationOnce(() =>
Promise.resolve(ok(artistInfoXml(images))) Promise.resolve(ok(artistInfoXml(artist)))
) )
.mockImplementationOnce(() => Promise.resolve(streamResponse)); .mockImplementationOnce(() => Promise.resolve(streamResponse));
@@ -1528,7 +1737,7 @@ describe("Navidrome", () => {
data: Buffer.from("the image", "ascii"), data: Buffer.from("the image", "ascii"),
}; };
const artist = anArtist({ id: artistId, albums: [] }); const artist = anArtist({ id: artistId, albums: [], image: images });
mockGET mockGET
.mockImplementationOnce(() => Promise.resolve(ok(PING_OK))) .mockImplementationOnce(() => Promise.resolve(ok(PING_OK)))
@@ -1536,7 +1745,7 @@ describe("Navidrome", () => {
Promise.resolve(ok(artistXml(artist))) Promise.resolve(ok(artistXml(artist)))
) )
.mockImplementationOnce(() => .mockImplementationOnce(() =>
Promise.resolve(ok(artistInfoXml(images))) Promise.resolve(ok(artistInfoXml(artist)))
) )
.mockImplementationOnce(() => Promise.resolve(streamResponse)); .mockImplementationOnce(() => Promise.resolve(streamResponse));
@@ -1595,7 +1804,7 @@ describe("Navidrome", () => {
data: originalImage, data: originalImage,
}; };
const artist = anArtist({ id: artistId }); const artist = anArtist({ id: artistId, image: images });
mockGET mockGET
.mockImplementationOnce(() => Promise.resolve(ok(PING_OK))) .mockImplementationOnce(() => Promise.resolve(ok(PING_OK)))
@@ -1603,13 +1812,15 @@ describe("Navidrome", () => {
Promise.resolve(ok(artistXml(artist))) Promise.resolve(ok(artistXml(artist)))
) )
.mockImplementationOnce(() => .mockImplementationOnce(() =>
Promise.resolve(ok(artistInfoXml(images))) Promise.resolve(ok(artistInfoXml(artist)))
) )
.mockImplementationOnce(() => Promise.resolve(streamResponse)); .mockImplementationOnce(() => Promise.resolve(streamResponse));
const resize = jest.fn(); const resize = jest.fn();
(sharp as unknown as jest.Mock).mockReturnValue({ resize }); ((sharp as unknown) as jest.Mock).mockReturnValue({ resize });
resize.mockReturnValue({ toBuffer: () => Promise.resolve(resizedImage) }) resize.mockReturnValue({
toBuffer: () => Promise.resolve(resizedImage),
});
const result = await navidrome const result = await navidrome
.generateToken({ username, password }) .generateToken({ username, password })
@@ -1668,6 +1879,7 @@ describe("Navidrome", () => {
const artist = anArtist({ const artist = anArtist({
id: artistId, id: artistId,
albums: [album1, album2], albums: [album1, album2],
image: images
}); });
mockGET mockGET
@@ -1676,7 +1888,7 @@ describe("Navidrome", () => {
Promise.resolve(ok(artistXml(artist))) Promise.resolve(ok(artistXml(artist)))
) )
.mockImplementationOnce(() => .mockImplementationOnce(() =>
Promise.resolve(ok(artistInfoXml(images))) Promise.resolve(ok(artistInfoXml(artist)))
) )
.mockImplementationOnce(() => Promise.resolve(streamResponse)); .mockImplementationOnce(() => Promise.resolve(streamResponse));
@@ -1743,7 +1955,7 @@ describe("Navidrome", () => {
data: Buffer.from("the image", "ascii"), data: Buffer.from("the image", "ascii"),
}; };
const artist = anArtist({ id: artistId, albums: [] }); const artist = anArtist({ id: artistId, albums: [], image: images });
mockGET mockGET
.mockImplementationOnce(() => Promise.resolve(ok(PING_OK))) .mockImplementationOnce(() => Promise.resolve(ok(PING_OK)))
@@ -1751,7 +1963,7 @@ describe("Navidrome", () => {
Promise.resolve(ok(artistXml(artist))) Promise.resolve(ok(artistXml(artist)))
) )
.mockImplementationOnce(() => .mockImplementationOnce(() =>
Promise.resolve(ok(artistInfoXml(images))) Promise.resolve(ok(artistInfoXml(artist)))
) )
.mockImplementationOnce(() => Promise.resolve(streamResponse)); .mockImplementationOnce(() => Promise.resolve(streamResponse));
@@ -1810,6 +2022,7 @@ describe("Navidrome", () => {
const artist = anArtist({ const artist = anArtist({
id: artistId, id: artistId,
albums: [album1, album2], albums: [album1, album2],
image: images
}); });
mockGET mockGET
@@ -1818,7 +2031,7 @@ describe("Navidrome", () => {
Promise.resolve(ok(artistXml(artist))) Promise.resolve(ok(artistXml(artist)))
) )
.mockImplementationOnce(() => .mockImplementationOnce(() =>
Promise.resolve(ok(artistInfoXml(images))) Promise.resolve(ok(artistInfoXml(artist)))
) )
.mockImplementationOnce(() => Promise.resolve(streamResponse)); .mockImplementationOnce(() => Promise.resolve(streamResponse));
@@ -1885,7 +2098,7 @@ describe("Navidrome", () => {
data: Buffer.from("the image", "ascii"), data: Buffer.from("the image", "ascii"),
}; };
const artist = anArtist({ id: artistId, albums: [] }); const artist = anArtist({ id: artistId, albums: [], image: images });
mockGET mockGET
.mockImplementationOnce(() => Promise.resolve(ok(PING_OK))) .mockImplementationOnce(() => Promise.resolve(ok(PING_OK)))
@@ -1893,7 +2106,7 @@ describe("Navidrome", () => {
Promise.resolve(ok(artistXml(artist))) Promise.resolve(ok(artistXml(artist)))
) )
.mockImplementationOnce(() => .mockImplementationOnce(() =>
Promise.resolve(ok(artistInfoXml(images))) Promise.resolve(ok(artistInfoXml(artist)))
) )
.mockImplementationOnce(() => Promise.resolve(streamResponse)); .mockImplementationOnce(() => Promise.resolve(streamResponse));