Files
lyssnarr/backend/test/search.test.js
claude 92d1a7b7ec
All checks were successful
build-and-push / build (push) Successful in 16s
Milstolpe 2+3: ikon, sök (artist/album/låt) och Hämta-till-Lidarr
- Ikon: SVG-favicon (indigo-gradient + balknot), används i topbar,
  login och som favicon
- GET /api/search: artister/album via Lidarrs metadata-API (funkar även
  när musicbrainz.org är nere), låtar via MusicBrainz recording-sök;
  503 med svenskt felmeddelande när källan inte svarar
- POST /api/add: slår upp via lidarr:<mbid>, artist läggs bevakad med
  searchForMissingAlbums, album bevakat med searchForNewAlbum (artisten
  följer med obevakad), låtar mappas till album/artist; 'already' när
  det redan finns
- Söksida med Jellyseerr-lik kortgrid: poster/fallback, typ-badge,
  Hämta-knapp med busy/klar/finns-lägen; sökfältet i topbaren aktivt
- 7 nya tester (mappning + rutter med stubbat Lidarr), totalt 11

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01824ZrvG2mDYYrmwqypLNup
2026-08-01 00:47:37 +02:00

148 lines
4.6 KiB
JavaScript

import test from 'node:test'
import assert from 'node:assert/strict'
import { buildApp } from '../src/app.js'
import { mapLidarrSearch } from '../src/mapping.js'
const config = {
appPassword: 'testlösen',
sessionSecret: 'x'.repeat(32),
lidarr: { url: 'http://stub', apiKey: 'k', qualityProfileId: 3, metadataProfileId: 1, rootFolder: '/music' },
jellyfin: { url: '', apiKey: '' },
gemini: { apiKey: '', model: '' },
}
const powerwolf = {
foreignArtistId: 'mbid-artist-1',
artistName: 'Powerwolf',
disambiguation: 'tysk powermetal',
images: [{ coverType: 'poster', remoteUrl: 'http://img/poster.jpg' }],
}
const album = {
foreignAlbumId: 'mbid-album-1',
title: 'The Sacrament of Sin',
releaseDate: '2018-07-20',
images: [],
artist: { ...powerwolf },
}
async function loggedIn(app) {
const login = await app.inject({ method: 'POST', url: '/api/login', payload: { password: 'testlösen' } })
return login.headers['set-cookie']
}
test('mapLidarrSearch formar artist- och albumkort', () => {
const results = [{ artist: powerwolf }, { album }]
const artists = mapLidarrSearch(results, 'artist')
assert.equal(artists.length, 1)
assert.deepEqual(artists[0], {
kind: 'artist',
name: 'Powerwolf',
secondary: 'tysk powermetal',
year: '',
mbid: 'mbid-artist-1',
poster: 'http://img/poster.jpg',
inLibrary: false,
})
const albums = mapLidarrSearch(results, 'album')
assert.equal(albums[0].year, '2018')
assert.equal(albums[0].secondary, 'Powerwolf')
})
test('GET /api/search kräver inloggning', async () => {
const app = buildApp(config, { lidarr: {}, musicbrainz: {} })
const res = await app.inject({ method: 'GET', url: '/api/search?q=powerwolf' })
assert.equal(res.statusCode, 401)
await app.close()
})
test('GET /api/search returnerar mappade träffar', async () => {
const app = buildApp(config, {
lidarr: { search: async () => [{ artist: powerwolf }] },
musicbrainz: {},
})
const cookie = await loggedIn(app)
const res = await app.inject({ method: 'GET', url: '/api/search?q=powerwolf&type=artist', headers: { cookie } })
assert.equal(res.statusCode, 200)
assert.equal(res.json()[0].name, 'Powerwolf')
await app.close()
})
test('GET /api/search ger 503 när källan är nere', async () => {
const app = buildApp(config, {
lidarr: { search: async () => { throw new Error('nere') } },
musicbrainz: {},
})
const cookie = await loggedIn(app)
const res = await app.inject({ method: 'GET', url: '/api/search?q=x&type=artist', headers: { cookie } })
assert.equal(res.statusCode, 503)
await app.close()
})
test('POST /api/add lägger till artist med profiler och bevakning', async () => {
let sent = null
const app = buildApp(config, {
lidarr: {
search: async (term) => {
assert.equal(term, 'lidarr:mbid-artist-1')
return [{ artist: { ...powerwolf } }]
},
addArtist: async (payload) => { sent = payload; return { id: 7 } },
},
musicbrainz: {},
})
const cookie = await loggedIn(app)
const res = await app.inject({
method: 'POST',
url: '/api/add',
headers: { cookie },
payload: { kind: 'artist', mbid: 'mbid-artist-1' },
})
assert.equal(res.statusCode, 200)
assert.deepEqual(res.json(), { ok: true, already: false })
assert.equal(sent.qualityProfileId, 3)
assert.equal(sent.rootFolderPath, '/music')
assert.equal(sent.monitored, true)
assert.equal(sent.addOptions.searchForMissingAlbums, true)
await app.close()
})
test('POST /api/add med låt går via albumet', async () => {
let sent = null
const app = buildApp(config, {
lidarr: {
search: async () => [{ album: { ...album } }],
addAlbum: async (payload) => { sent = payload; return { id: 9 } },
},
musicbrainz: {},
})
const cookie = await loggedIn(app)
const res = await app.inject({
method: 'POST',
url: '/api/add',
headers: { cookie },
payload: { kind: 'track', mbid: 'rec-1', artistMbid: 'mbid-artist-1', albumMbid: 'mbid-album-1' },
})
assert.equal(res.statusCode, 200)
assert.equal(sent.monitored, true)
assert.equal(sent.artist.rootFolderPath, '/music')
assert.equal(sent.addOptions.searchForNewAlbum, true)
await app.close()
})
test('POST /api/add svarar already för befintlig artist', async () => {
const app = buildApp(config, {
lidarr: { search: async () => [{ artist: { ...powerwolf, id: 12 } }] },
musicbrainz: {},
})
const cookie = await loggedIn(app)
const res = await app.inject({
method: 'POST',
url: '/api/add',
headers: { cookie },
payload: { kind: 'artist', mbid: 'mbid-artist-1' },
})
assert.deepEqual(res.json(), { ok: true, already: true })
await app.close()
})