mirror of
https://github.com/wkulhanek/bonob.git
synced 2025-12-21 17:33:29 +01:00
Ability to browse Random Albums
This commit is contained in:
@@ -6,7 +6,16 @@ import {
|
||||
albumToAlbumSummary,
|
||||
} from "../src/music_service";
|
||||
import { v4 as uuid } from "uuid";
|
||||
import { anArtist, anAlbum, aTrack, POP, ROCK, METAL, HIP_HOP, SKA } from "./builders";
|
||||
import {
|
||||
anArtist,
|
||||
anAlbum,
|
||||
aTrack,
|
||||
POP,
|
||||
ROCK,
|
||||
METAL,
|
||||
HIP_HOP,
|
||||
SKA,
|
||||
} from "./builders";
|
||||
|
||||
describe("InMemoryMusicService", () => {
|
||||
const service = new InMemoryMusicService();
|
||||
@@ -147,7 +156,6 @@ describe("InMemoryMusicService", () => {
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
describe("tracks", () => {
|
||||
const artist1Album1 = anAlbum();
|
||||
const artist1Album2 = anAlbum();
|
||||
@@ -201,8 +209,6 @@ describe("InMemoryMusicService", () => {
|
||||
const artist3_album1 = anAlbum({ genre: HIP_HOP });
|
||||
const artist3_album2 = anAlbum({ genre: POP });
|
||||
|
||||
const totalAlbumCount = 8;
|
||||
|
||||
const artist1 = anArtist({
|
||||
albums: [
|
||||
artist1_album1,
|
||||
@@ -216,16 +222,56 @@ describe("InMemoryMusicService", () => {
|
||||
const artist3 = anArtist({ albums: [artist3_album1, artist3_album2] });
|
||||
const artistWithNoAlbums = anArtist({ albums: [] });
|
||||
|
||||
const allAlbums = [artist1, artist2, artist3, artistWithNoAlbums].flatMap(
|
||||
(it) => it.albums
|
||||
);
|
||||
const totalAlbumCount = allAlbums.length;
|
||||
|
||||
beforeEach(() => {
|
||||
service.hasArtists(artist1, artist2, artist3, artistWithNoAlbums);
|
||||
});
|
||||
|
||||
describe("fetching random albums", () => {
|
||||
describe("with no paging", () => {
|
||||
it("should return all albums for all the artists in a random order", async () => {
|
||||
const albums = await musicLibrary.albums({
|
||||
_index: 0,
|
||||
_count: 100,
|
||||
type: "random",
|
||||
});
|
||||
|
||||
expect(albums.total).toEqual(totalAlbumCount);
|
||||
expect(albums.results.map((it) => it.id).sort()).toEqual(
|
||||
allAlbums.map((it) => it.id).sort()
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe("with no paging", () => {
|
||||
it("should return only a page of results", async () => {
|
||||
const albums = await musicLibrary.albums({
|
||||
_index: 2,
|
||||
_count: 3,
|
||||
type: "random",
|
||||
});
|
||||
|
||||
expect(albums.total).toEqual(totalAlbumCount);
|
||||
expect(albums.results.length).toEqual(3)
|
||||
// cannot really assert the results and they will change every time
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("fetching multiple albums", () => {
|
||||
describe("with no filtering", () => {
|
||||
describe("fetching all on one page", () => {
|
||||
it("should return all the albums for all the artists", async () => {
|
||||
expect(
|
||||
await musicLibrary.albums({ _index: 0, _count: 100 })
|
||||
await musicLibrary.albums({
|
||||
_index: 0,
|
||||
_count: 100,
|
||||
type: "alphabeticalByArtist",
|
||||
})
|
||||
).toEqual({
|
||||
results: [
|
||||
albumToAlbumSummary(artist1_album1),
|
||||
@@ -233,9 +279,9 @@ describe("InMemoryMusicService", () => {
|
||||
albumToAlbumSummary(artist1_album3),
|
||||
albumToAlbumSummary(artist1_album4),
|
||||
albumToAlbumSummary(artist1_album5),
|
||||
|
||||
|
||||
albumToAlbumSummary(artist2_album1),
|
||||
|
||||
|
||||
albumToAlbumSummary(artist3_album1),
|
||||
albumToAlbumSummary(artist3_album2),
|
||||
],
|
||||
@@ -243,26 +289,34 @@ describe("InMemoryMusicService", () => {
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
describe("fetching a page", () => {
|
||||
it("should return only that page", async () => {
|
||||
expect(await musicLibrary.albums({ _index: 4, _count: 3 })).toEqual(
|
||||
{
|
||||
results: [
|
||||
albumToAlbumSummary(artist1_album5),
|
||||
albumToAlbumSummary(artist2_album1),
|
||||
albumToAlbumSummary(artist3_album1),
|
||||
],
|
||||
total: totalAlbumCount,
|
||||
}
|
||||
);
|
||||
expect(
|
||||
await musicLibrary.albums({
|
||||
_index: 4,
|
||||
_count: 3,
|
||||
type: "alphabeticalByArtist",
|
||||
})
|
||||
).toEqual({
|
||||
results: [
|
||||
albumToAlbumSummary(artist1_album5),
|
||||
albumToAlbumSummary(artist2_album1),
|
||||
albumToAlbumSummary(artist3_album1),
|
||||
],
|
||||
total: totalAlbumCount,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
describe("fetching the last page", () => {
|
||||
it("should return only that page", async () => {
|
||||
expect(
|
||||
await musicLibrary.albums({ _index: 6, _count: 100 })
|
||||
await musicLibrary.albums({
|
||||
_index: 6,
|
||||
_count: 100,
|
||||
type: "alphabeticalByArtist",
|
||||
})
|
||||
).toEqual({
|
||||
results: [
|
||||
albumToAlbumSummary(artist3_album1),
|
||||
@@ -273,12 +327,13 @@ describe("InMemoryMusicService", () => {
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
describe("filtering by genre", () => {
|
||||
describe("fetching all on one page", () => {
|
||||
it("should return all the albums of that genre for all the artists", async () => {
|
||||
expect(
|
||||
await musicLibrary.albums({
|
||||
type: "byGenre",
|
||||
genre: POP.id,
|
||||
_index: 0,
|
||||
_count: 100,
|
||||
@@ -294,12 +349,13 @@ describe("InMemoryMusicService", () => {
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
describe("when the genre has more albums than a single page", () => {
|
||||
describe("can fetch a single page", () => {
|
||||
it("should return only the albums for that page", async () => {
|
||||
expect(
|
||||
await musicLibrary.albums({
|
||||
type: "byGenre",
|
||||
genre: POP.id,
|
||||
_index: 1,
|
||||
_count: 2,
|
||||
@@ -313,11 +369,12 @@ describe("InMemoryMusicService", () => {
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
describe("can fetch the last page", () => {
|
||||
it("should return only the albums for the last page", async () => {
|
||||
expect(
|
||||
await musicLibrary.albums({
|
||||
type: "byGenre",
|
||||
genre: POP.id,
|
||||
_index: 3,
|
||||
_count: 100,
|
||||
@@ -329,10 +386,11 @@ describe("InMemoryMusicService", () => {
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
it("should return empty list if there are no albums for the genre", async () => {
|
||||
expect(
|
||||
await musicLibrary.albums({
|
||||
type: "byGenre",
|
||||
genre: "genre with no albums",
|
||||
_index: 0,
|
||||
_count: 100,
|
||||
@@ -353,7 +411,7 @@ describe("InMemoryMusicService", () => {
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
describe("when it doesnt exist", () => {
|
||||
it("should blow up", async () => {
|
||||
return expect(musicLibrary.album("-1")).rejects.toEqual(
|
||||
|
||||
@@ -3,6 +3,7 @@ import * as A from "fp-ts/Array";
|
||||
import { fromEquals } from "fp-ts/lib/Eq";
|
||||
import { pipe } from "fp-ts/lib/function";
|
||||
import { ordString, fromCompare } from "fp-ts/lib/Ord";
|
||||
import { shuffle } from "underscore";
|
||||
|
||||
import {
|
||||
MusicService,
|
||||
@@ -17,17 +18,10 @@ import {
|
||||
asResult,
|
||||
artistToArtistSummary,
|
||||
albumToAlbumSummary,
|
||||
Album,
|
||||
Track,
|
||||
Genre,
|
||||
} from "../src/music_service";
|
||||
|
||||
type P<T> = (t: T) => boolean;
|
||||
const all: P<any> = (_: any) => true;
|
||||
|
||||
const albumWithGenre = (genreId: string): P<[Artist, Album]> => ([_, album]) =>
|
||||
album.genre?.id === genreId;
|
||||
|
||||
export class InMemoryMusicService implements MusicService {
|
||||
users: Record<string, string> = {};
|
||||
artists: Artist[] = [];
|
||||
@@ -75,17 +69,25 @@ export class InMemoryMusicService implements MusicService {
|
||||
),
|
||||
albums: (q: AlbumQuery) =>
|
||||
Promise.resolve(
|
||||
this.artists
|
||||
.flatMap((artist) => artist.albums.map((album) => [artist, album]))
|
||||
.filter(
|
||||
pipe(
|
||||
O.fromNullable(q.genre),
|
||||
O.map(albumWithGenre),
|
||||
O.getOrElse(() => all)
|
||||
)
|
||||
)
|
||||
this.artists.flatMap((artist) =>
|
||||
artist.albums.map((album) => ({ artist, album }))
|
||||
)
|
||||
)
|
||||
.then((matches) => matches.map(([_, album]) => album as Album))
|
||||
.then((artist2Album) => {
|
||||
switch (q.type) {
|
||||
case "alphabeticalByArtist":
|
||||
return artist2Album;
|
||||
case "byGenre":
|
||||
return artist2Album.filter(
|
||||
(it) => it.album.genre?.id === q.genre
|
||||
);
|
||||
case "random":
|
||||
return shuffle(artist2Album);
|
||||
default:
|
||||
return [];
|
||||
}
|
||||
})
|
||||
.then((matches) => matches.map((it) => it.album))
|
||||
.then((it) => it.map(albumToAlbumSummary))
|
||||
.then(slice2(q))
|
||||
.then(asResult),
|
||||
|
||||
@@ -30,6 +30,7 @@ import {
|
||||
AlbumSummary,
|
||||
artistToArtistSummary,
|
||||
NO_IMAGES,
|
||||
AlbumQuery,
|
||||
} from "../src/music_service";
|
||||
import { anAlbum, anArtist, aTrack } from "./builders";
|
||||
|
||||
@@ -867,7 +868,7 @@ describe("Navidrome", () => {
|
||||
});
|
||||
|
||||
it("should pass the filter to navidrome", async () => {
|
||||
const q = { _index: 0, _count: 500, genre: "Pop" };
|
||||
const q: AlbumQuery = { _index: 0, _count: 500, genre: "Pop", type: 'byGenre' };
|
||||
const result = await navidrome
|
||||
.generateToken({ username, password })
|
||||
.then((it) => it as AuthSuccess)
|
||||
@@ -910,12 +911,12 @@ describe("Navidrome", () => {
|
||||
});
|
||||
|
||||
it("should return the album", async () => {
|
||||
const paging = { _index: 0, _count: 500 };
|
||||
const q: AlbumQuery = { _index: 0, _count: 500, type: 'alphabeticalByArtist' };
|
||||
const result = await navidrome
|
||||
.generateToken({ username, password })
|
||||
.then((it) => it as AuthSuccess)
|
||||
.then((it) => navidrome.login(it.authToken))
|
||||
.then((it) => it.albums(paging));
|
||||
.then((it) => it.albums(q));
|
||||
|
||||
expect(result).toEqual({
|
||||
results: albums,
|
||||
@@ -951,12 +952,12 @@ describe("Navidrome", () => {
|
||||
});
|
||||
|
||||
it("should return the album", async () => {
|
||||
const paging = { _index: 0, _count: 500 };
|
||||
const q: AlbumQuery = { _index: 0, _count: 500, type: 'alphabeticalByArtist' };
|
||||
const result = await navidrome
|
||||
.generateToken({ username, password })
|
||||
.then((it) => it as AuthSuccess)
|
||||
.then((it) => navidrome.login(it.authToken))
|
||||
.then((it) => it.albums(paging));
|
||||
.then((it) => it.albums(q));
|
||||
|
||||
expect(result).toEqual({
|
||||
results: albums,
|
||||
@@ -1001,12 +1002,12 @@ describe("Navidrome", () => {
|
||||
|
||||
describe("querying for all of them", () => {
|
||||
it("should return all of them with corrent paging information", async () => {
|
||||
const paging = { _index: 0, _count: 500 };
|
||||
const q : AlbumQuery= { _index: 0, _count: 500, type: 'alphabeticalByArtist' };
|
||||
const result = await navidrome
|
||||
.generateToken({ username, password })
|
||||
.then((it) => it as AuthSuccess)
|
||||
.then((it) => navidrome.login(it.authToken))
|
||||
.then((it) => it.albums(paging));
|
||||
.then((it) => it.albums(q));
|
||||
|
||||
expect(result).toEqual({
|
||||
results: albums,
|
||||
@@ -1027,12 +1028,12 @@ describe("Navidrome", () => {
|
||||
|
||||
describe("querying for a page of them", () => {
|
||||
it("should return the page with the corrent paging information", async () => {
|
||||
const paging = { _index: 2, _count: 2 };
|
||||
const q : AlbumQuery = { _index: 2, _count: 2, type: 'alphabeticalByArtist' };
|
||||
const result = await navidrome
|
||||
.generateToken({ username, password })
|
||||
.then((it) => it as AuthSuccess)
|
||||
.then((it) => navidrome.login(it.authToken))
|
||||
.then((it) => it.albums(paging));
|
||||
.then((it) => it.albums(q));
|
||||
|
||||
expect(result).toEqual({
|
||||
results: [albums[2], albums[3]],
|
||||
@@ -1042,8 +1043,8 @@ describe("Navidrome", () => {
|
||||
expect(axios.get).toHaveBeenCalledWith(`${url}/rest/getAlbumList`, {
|
||||
params: {
|
||||
type: "alphabeticalByArtist",
|
||||
size: 500,
|
||||
offset: 0,
|
||||
size: 2,
|
||||
offset: 2,
|
||||
...authParams,
|
||||
},
|
||||
headers,
|
||||
@@ -1079,12 +1080,12 @@ describe("Navidrome", () => {
|
||||
|
||||
describe("querying for all of them", () => {
|
||||
it("will return only the first 500 with the correct paging information", async () => {
|
||||
const paging = { _index: 0, _count: 1000 };
|
||||
const q: AlbumQuery = { _index: 0, _count: 1000, type: 'alphabeticalByArtist' };
|
||||
const result = await navidrome
|
||||
.generateToken({ username, password })
|
||||
.then((it) => it as AuthSuccess)
|
||||
.then((it) => navidrome.login(it.authToken))
|
||||
.then((it) => it.albums(paging));
|
||||
.then((it) => it.albums(q));
|
||||
|
||||
expect(result).toEqual({
|
||||
results: first500Albums.map(albumToAlbumSummary),
|
||||
|
||||
@@ -556,9 +556,10 @@ describe("api", () => {
|
||||
{ itemType: "container", id: "artists", title: "Artists" },
|
||||
{ itemType: "container", id: "albums", title: "Albums" },
|
||||
{ itemType: "container", id: "genres", title: "Genres" },
|
||||
{ itemType: "container", id: "randomAlbums", title: "Random" },
|
||||
],
|
||||
index: 0,
|
||||
total: 3,
|
||||
total: 4,
|
||||
})
|
||||
);
|
||||
});
|
||||
@@ -889,6 +890,19 @@ describe("api", () => {
|
||||
musicService.hasArtists(artist1, artist2, artist3, artist4);
|
||||
});
|
||||
|
||||
describe("asking for random albums", () => {
|
||||
it("should return some", async () => {
|
||||
const result = await ws.getMetadataAsync({
|
||||
id: "randomAlbums",
|
||||
index: 0,
|
||||
count: 100,
|
||||
});
|
||||
expect(result[0].getMetadataResult.index).toEqual(0);
|
||||
expect(result[0].getMetadataResult.count).toEqual(6);
|
||||
expect(result[0].getMetadataResult.total).toEqual(6);
|
||||
});
|
||||
});
|
||||
|
||||
describe("asking for all albums", () => {
|
||||
it("should return them all", async () => {
|
||||
const result = await ws.getMetadataAsync({
|
||||
|
||||
Reference in New Issue
Block a user