import { describe, expect, it } from 'vitest'; import { applyPollResult } from '../src/services/nowPlaying'; import { bearer, createUser, follow, useTestApp } from './helpers'; const app = useTestApp(); function nextMessage(ws: { once(event: 'message', cb: (data: Buffer) => void): void }, timeoutMs = 3000) { return new Promise<{ type: string; data: { user: { username: string }; track: { title: string } } }>((resolve, reject) => { const timer = setTimeout(() => reject(new Error('no websocket message')), timeoutMs); ws.once('message', (data) => { clearTimeout(timer); resolve(JSON.parse(data.toString())); }); }); } describe('realtime', () => { it('pushes now-playing for people you follow', async () => { const alice = await createUser(app(), 'alice'); const bob = await createUser(app(), 'bob'); await follow(app(), alice.token, 'bob'); const ws = await app().injectWS('/ws', { headers: bearer(alice.token) }); await new Promise((r) => setTimeout(r, 200)); // let the server register the subscription const msg = nextMessage(ws); await applyPollResult(bob.user.id, 'spotify', { track: { title: 'Teardrop', artist: 'Massive Attack' }, isPlaying: true, progressMs: 0 }); expect(await msg).toMatchObject({ type: 'now_playing', data: { user: { username: 'bob' }, track: { title: 'Teardrop' } } }); ws.terminate(); }); it('starts delivering for someone followed after connecting', async () => { const alice = await createUser(app(), 'alice'); const carol = await createUser(app(), 'carol'); const ws = await app().injectWS('/ws', { headers: bearer(alice.token) }); await new Promise((r) => setTimeout(r, 200)); await follow(app(), alice.token, 'carol'); await new Promise((r) => setTimeout(r, 200)); const msg = nextMessage(ws); await applyPollResult(carol.user.id, 'spotify', { track: { title: 'Dreams', artist: 'Fleetwood Mac' }, isPlaying: true, progressMs: 0 }); expect((await msg).data.user.username).toBe('carol'); ws.terminate(); }); it("does not leak updates from people you don't follow", async () => { const alice = await createUser(app(), 'alice'); const stranger = await createUser(app(), 'stranger'); const ws = await app().injectWS('/ws', { headers: bearer(alice.token) }); await new Promise((r) => setTimeout(r, 200)); const msg = nextMessage(ws, 500); await applyPollResult(stranger.user.id, 'spotify', { track: { title: 'Secret', artist: 'X' }, isPlaying: true, progressMs: 0 }); await expect(msg).rejects.toThrow('no websocket message'); ws.terminate(); }); it('closes unauthenticated sockets with 4401', async () => { const ws = await app().injectWS('/ws'); const code = await new Promise((resolve) => ws.once('close', (c: number) => resolve(c))); expect(code).toBe(4401); }); });