Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 58 additions & 0 deletions src/app/api/songs/[[...path]]/route.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import { beforeEach, describe, expect, it, vi } from 'vitest'
import { GET } from './route'

const { mockGetSongsByFolder, mockCountSongsByFolder } = vi.hoisted(() => ({
mockGetSongsByFolder: vi.fn(),
mockCountSongsByFolder: vi.fn()
}))

vi.mock('@/features/songs/song-query.repository', () => ({
PAGE_SIZE: 50,
getSongsByFolder: (...args: unknown[]) => mockGetSongsByFolder(...args),
countSongsByFolder: (...args: unknown[]) => mockCountSongsByFolder(...args)
}))

function get(url: string, path?: string[]) {
return GET(new Request(url), { params: Promise.resolve({ path }) })
}

beforeEach(() => {
vi.clearAllMocks()
mockGetSongsByFolder.mockResolvedValue([])
mockCountSongsByFolder.mockResolvedValue(0)
})

describe('GET /api/songs', () => {
it('reads a single-segment folder from the folderPath query parameter', async () => {
const res = await get('http://localhost/api/songs?folderPath=%2Fmusic')
const body = await res.json()

expect(res.status).toBe(200)
expect(body.folderPath).toBe('/music')
expect(mockGetSongsByFolder.mock.calls[0][0]).toBe('/music')
expect(mockCountSongsByFolder.mock.calls[0][0]).toBe('/music')
})

it('keeps special characters of the folderPath query parameter', async () => {
const folder = '/music/Albums/Amrinder Gill/Judaa & Co #1'
await get(`http://localhost/api/songs?folderPath=${encodeURIComponent(folder)}`)

expect(mockGetSongsByFolder.mock.calls[0][0]).toBe(folder)
})

it('still accepts the folder as path segments', async () => {
const res = await get('http://localhost/api/songs/music/Albums', ['music', 'Albums'])
const body = await res.json()

expect(body.folderPath).toBe('/music/Albums')
expect(mockGetSongsByFolder.mock.calls[0][0]).toBe('/music/Albums')
})

it('lists all songs when no folder is given', async () => {
const res = await get('http://localhost/api/songs')
const body = await res.json()

expect(body.folderPath).toBe('all')
expect(mockGetSongsByFolder.mock.calls[0][0]).toBeNull()
})
})
7 changes: 5 additions & 2 deletions src/app/api/songs/[[...path]]/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,12 @@ type FilesResponse = FilesSuccessResponse | FilesErrorResponse

export async function GET(request: Request, { params }: RouteParams): Promise<NextResponse<FilesResponse>> {
const { path } = await params

const folderPath = !!path?.length ? '/' + path.join('/') : null
const { searchParams } = new URL(request.url)

// The folder comes as a query parameter. As path segments, a single-segment folder such as /music
// (MUSIC_FOLDERS=/music,/audiobooks) is routed to /api/songs/[id] instead of this route.
const folderParam = getSearchParam(searchParams, 'folderPath', 'string', '')
const folderPath = folderParam || (!!path?.length ? '/' + path.join('/') : null)
const search = getSearchParam(searchParams, 'search', 'string', '') || undefined
const sortFieldParam = getSearchParam(searchParams, 'sortField', 'string', 'title') as ColumnField
const sortParam = getSearchParam(searchParams, 'sort', 'string', 'asc') as SongSortDirection
Expand Down
12 changes: 3 additions & 9 deletions src/features/songs/hooks/use-songs-by-folder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,15 +34,9 @@ async function fetchSongsByFolder(
): Promise<SongsResponse> {
const isAll = folderPath === ALL_SONGS_FOLDER_ID

const encodedPath = isAll
? ''
: folderPath
.replace(/^\//, '')
.split('/')
.map(segment => encodeURIComponent(segment))
.join('/')

// As a query parameter, not path segments: /api/songs/music would be taken for a song ID.
const params: Record<string, string | number | undefined> = {
...(!isAll && { folderPath }),
search,
...sorting,
limit,
Expand All @@ -56,7 +50,7 @@ async function fetchSongsByFolder(
}
}

const { data } = await api.get<SongsResponse>(`/songs/${encodedPath}`, {
const { data } = await api.get<SongsResponse>('/songs', {
params
})

Expand Down
Loading