Something went wrong. Try again.
[READ-ONLY] Mirror of https://github.com/FoxxMD/multi-scrobbler. Scrobble plays from multiple sources to multiple clients docs.multi-scrobbler.app
deezer docker jellyfin koito lastfm listenbrainz maloja mopidy mpris music music-assistant plex scrobble self-hosted spotify subsonic tautulli youtube-music
Something went wrong. Try again.
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071import { loggerTest } from '@foxxmd/logging';import ScrobbleSources from '../../sources/ScrobbleSources.ts';import ScrobbleClients from '../../scrobblers/ScrobbleClients.ts';import { WildcardEmitter } from '../../common/WildcardEmitter.ts';import type { Express } from 'express';import request from 'supertest';import { initServer, type ServerOptions, type ServerLocals } from '../../server/index.ts';import { setUserPassword } from '../../common/auth/ownerPassword.ts';import type { PasswordHasher } from '../../common/auth/auth.ts';import { createHash, timingSafeEqual } from 'node:crypto';
const internalConfig = { localUrl: new URL('http://localhost:9078'), configDir: 'fake', dataDir: 'fake', logger: loggerTest, version: 'test' };
/** * Fast stand-in for better-auth's scrypt hashing (~50ms per hash and per verify, and most auth tests do both). * Still a real check (wrong passwords fail), just not a slow one. ownerPassword/hardening tests use createAuth directly and keep the real hasher. * */export const fastPasswordHasher: PasswordHasher = { hash: async (password) => `test-sha256:${createHash('sha256').update(password).digest('hex')}`, verify: async ({hash, password}) => { const expected = Buffer.from(hash); const actual = Buffer.from(await fastPasswordHasher.hash(password)); return expected.length === actual.length && timingSafeEqual(expected, actual); },};
/** Build a test-mode server app. Auth instance, settings and db are available on app.locals (ServerLocals) */export const buildAuthApp = async (opts: ServerOptions = {}, setup?: (sources: ScrobbleSources) => Promise<void>) => { const [app] = await buildAuthAppRouter(opts, setup); return app;};
export const buildAuthAppRouter = async (opts: ServerOptions = {}, setup?: (sources: ScrobbleSources) => Promise<void>) => { const sources = new ScrobbleSources(new WildcardEmitter(), internalConfig, loggerTest); if (setup !== undefined) { await setup(sources); } const clients = new ScrobbleClients(new WildcardEmitter(), new WildcardEmitter(), internalConfig, loggerTest); return initServer({ sources, clients }, { testMode: true, passwordHasher: fastPasswordHasher, ...opts });};
/** Give the owner a password so the instance is secured */export const secureApp = async (app: Express, password = 'ownerPassword') => { const {auth, authState} = app.locals as ServerLocals; await setUserPassword(auth, authState.ownerId, password); await authState.refresh(); return password;}
/** Sign in as owner and return the session cookie(s) */export const signIn = async (app: Express, password = 'ownerPassword') => { const res = await request(app).post('/api/auth/sign-in/username').send({username: 'admin', password}); if (res.status !== 200) { throw new Error(`Sign in failed with ${res.status}: ${JSON.stringify(res.body)}`); } return res.headers['set-cookie'] as unknown as string[];}
/** Create an API key for the signed in owner and return the full key */export const createApiKey = async (app: Express, cookie: string[], scope: unknown = 'read', name = 'test') => { const res = await request(app).post('/api/auth/api-key/create') .set('Host', 'localhost:9078') .set('Origin', 'http://localhost:9078') .set('Cookie', cookie) .send({name, metadata: {scope}}); if (res.status !== 200) { throw new Error(`Create API key failed with ${res.status}: ${JSON.stringify(res.body)}`); } return res.body.key as string;}