125 lines
3.8 KiB
TypeScript
125 lines
3.8 KiB
TypeScript
import { describe, it, expect } from 'vitest'
|
|
import { buildTestApp, setupAdmin, auth } from './helpers.js'
|
|
|
|
function subsonicStubFetch(ok: boolean): typeof fetch {
|
|
return (async (url: any) => {
|
|
if (String(url).includes('/rest/ping')) {
|
|
const body = ok
|
|
? { 'subsonic-response': { status: 'ok' } }
|
|
: {
|
|
'subsonic-response': {
|
|
status: 'failed',
|
|
error: { code: 40, message: 'Wrong username or password.' },
|
|
},
|
|
}
|
|
return new Response(JSON.stringify(body), {
|
|
status: 200,
|
|
headers: { 'content-type': 'application/json' },
|
|
})
|
|
}
|
|
return new Response('not found', { status: 404 })
|
|
}) as typeof fetch
|
|
}
|
|
|
|
describe('settings routes', () => {
|
|
it('starts empty and stores/clears tokens', async () => {
|
|
const app = await buildTestApp()
|
|
const cookie = await setupAdmin(app)
|
|
|
|
const empty = await app.inject({ method: 'GET', url: '/api/settings', ...auth(cookie) })
|
|
expect(empty.json()).toEqual({
|
|
hasDiscogsToken: false,
|
|
discogsTokenMasked: null,
|
|
subsonicUrl: null,
|
|
subsonicUsername: null,
|
|
hasSubsonicPassword: false,
|
|
})
|
|
|
|
const put = await app.inject({
|
|
method: 'PUT',
|
|
url: '/api/settings',
|
|
...auth(cookie),
|
|
payload: { discogsToken: 'abcdef0123456789' },
|
|
})
|
|
expect(put.statusCode).toBe(200)
|
|
expect(put.json()).toMatchObject({ hasDiscogsToken: true, discogsTokenMasked: '****56789' })
|
|
|
|
await app.inject({
|
|
method: 'PUT',
|
|
url: '/api/settings',
|
|
...auth(cookie),
|
|
payload: { discogsToken: '' },
|
|
})
|
|
const cleared = await app.inject({ method: 'GET', url: '/api/settings', ...auth(cookie) })
|
|
expect(cleared.json().hasDiscogsToken).toBe(false)
|
|
await app.close()
|
|
})
|
|
|
|
it('stores valid subsonic config after successful ping', async () => {
|
|
const app = await buildTestApp(subsonicStubFetch(true))
|
|
const cookie = await setupAdmin(app)
|
|
const put = await app.inject({
|
|
method: 'PUT',
|
|
url: '/api/settings',
|
|
...auth(cookie),
|
|
payload: {
|
|
subsonicUrl: 'http://navidrome.local',
|
|
subsonicUsername: 'sam',
|
|
subsonicPassword: 'pass',
|
|
},
|
|
})
|
|
expect(put.statusCode).toBe(200)
|
|
expect(put.json()).toMatchObject({
|
|
subsonicUrl: 'http://navidrome.local',
|
|
hasSubsonicPassword: true,
|
|
})
|
|
await app.close()
|
|
})
|
|
|
|
it('rejects bad subsonic credentials with 400 subsonic_auth', async () => {
|
|
const app = await buildTestApp(subsonicStubFetch(false))
|
|
const cookie = await setupAdmin(app)
|
|
const put = await app.inject({
|
|
method: 'PUT',
|
|
url: '/api/settings',
|
|
...auth(cookie),
|
|
payload: {
|
|
subsonicUrl: 'http://navidrome.local',
|
|
subsonicUsername: 'sam',
|
|
subsonicPassword: 'wrong',
|
|
},
|
|
})
|
|
expect(put.statusCode).toBe(400)
|
|
expect(put.json()).toMatchObject({ error: 'subsonic_auth' })
|
|
await app.close()
|
|
})
|
|
|
|
it('rejects unreachable subsonic with 400 subsonic_unreachable', async () => {
|
|
const failing = (async () => {
|
|
throw new TypeError('fetch failed')
|
|
}) as unknown as typeof fetch
|
|
const app = await buildTestApp(failing)
|
|
const cookie = await setupAdmin(app)
|
|
const put = await app.inject({
|
|
method: 'PUT',
|
|
url: '/api/settings',
|
|
...auth(cookie),
|
|
payload: {
|
|
subsonicUrl: 'http://nope.invalid',
|
|
subsonicUsername: 'sam',
|
|
subsonicPassword: 'pass',
|
|
},
|
|
})
|
|
expect(put.statusCode).toBe(400)
|
|
expect(put.json()).toMatchObject({ error: 'subsonic_unreachable' })
|
|
await app.close()
|
|
})
|
|
|
|
it('requires auth', async () => {
|
|
const app = await buildTestApp()
|
|
const res = await app.inject({ method: 'GET', url: '/api/settings' })
|
|
expect(res.statusCode).toBe(401)
|
|
await app.close()
|
|
})
|
|
})
|