33 lines
1.8 KiB
JavaScript
33 lines
1.8 KiB
JavaScript
import test from 'node:test';
|
|
import assert from 'node:assert/strict';
|
|
import { mkdtempSync, readFileSync, rmSync, statSync } from 'node:fs';
|
|
import { tmpdir } from 'node:os';
|
|
import { join } from 'node:path';
|
|
import { createHash } from 'node:crypto';
|
|
import { startSpotify, finishSpotify, spotifyStatus, searchSpotify } from '../server/spotify.mjs';
|
|
|
|
test('PKCE state is single use and token persists privately; exact artist ranks first', async () => {
|
|
const dir = mkdtempSync(join(tmpdir(), 'spotify-test-'));
|
|
process.env.DATA_DIR = dir; process.env.SPOTIFY_CLIENT_ID = 'test-client';
|
|
const original = globalThis.fetch;
|
|
let verifier;
|
|
globalThis.fetch = async (url, options) => {
|
|
if (String(url).includes('/api/token')) {
|
|
verifier = options.body.get('code_verifier');
|
|
return Response.json({ access_token: 'access', refresh_token: 'refresh', expires_in: 3600 });
|
|
}
|
|
return Response.json({ tracks: { items: [{ uri: 'spotify:track:1', name: 'Song', type: 'track' }] }, artists: { items: [{ uri: 'spotify:artist:1', name: 'Aphex Twin', type: 'artist' }] } });
|
|
};
|
|
try {
|
|
const url = new URL(startSpotify()), state = url.searchParams.get('state');
|
|
await assert.rejects(finishSpotify('wrong', 'code'));
|
|
await finishSpotify(state, 'code');
|
|
assert.equal(createHash('sha256').update(verifier).digest('base64url'), url.searchParams.get('code_challenge'));
|
|
await assert.rejects(finishSpotify(state, 'code'));
|
|
assert.equal(statSync(join(dir, 'spotify.json')).mode & 0o777, 0o600);
|
|
assert.equal(JSON.parse(readFileSync(join(dir, 'spotify.json'))).refresh_token, 'refresh');
|
|
assert.deepEqual(spotifyStatus(), { configured: true, connected: true });
|
|
assert.equal((await searchSpotify('aphex twin'))[0].type, 'artist');
|
|
} finally { globalThis.fetch = original; rmSync(dir, { recursive: true, force: true }); }
|
|
});
|