import { jest } from '@jest/globals'; import { ServerRecordingPeer } from '../../public/call/media/server-recording-peer.js'; function createTrack(kind, id) { return { kind, id, readyState: 'live' }; } function createStream(tracks) { return { getTracks() { return tracks; } }; } describe('ServerRecordingPeer', () => { test('queues remote candidates until answer is applied', async () => { const originalRTCPeerConnection = window.RTCPeerConnection; const originalRTCSessionDescription = window.RTCSessionDescription; const originalRTCIceCandidate = window.RTCIceCandidate; class FakeRTCPeerConnection { constructor() { this.localDescription = null; this.remoteDescription = null; this.candidates = []; } addTransceiver() {} async createOffer() { return { type: 'offer', sdp: 'test-offer-sdp' }; } async setLocalDescription(description) { this.localDescription = description; } async setRemoteDescription(description) { this.remoteDescription = description; } async addIceCandidate(candidate) { if (!this.remoteDescription) { throw new Error('remote description missing'); } this.candidates.push(candidate); } close() {} } window.RTCPeerConnection = FakeRTCPeerConnection; window.RTCSessionDescription = class { constructor(init) { Object.assign(this, init); } }; window.RTCIceCandidate = class { constructor(init) { Object.assign(this, init); } }; const signaling = { sendRecordingOffer: jest.fn(), sendRecordingCandidate: jest.fn() }; const peer = new ServerRecordingPeer({ rtcConfiguration: {}, getLocalStream: () => createStream([createTrack('video', 'video-1')]), getSignaling: () => signaling, getConnectionId: () => 'room-1', getParticipantId: () => 'participant-1' }); await peer.start({ recordingId: 'recording-1', connectionId: 'room-1' }); await expect(peer.addIceCandidate({ recordingId: 'recording-1', candidate: 'candidate:1', sdpMid: '0', sdpMLineIndex: 0 })).resolves.toBeUndefined(); const state = peer.peers.get('recording-1'); expect(state.pendingCandidates).toHaveLength(1); expect(state.pc.candidates).toHaveLength(0); await peer.applyAnswer({ recordingId: 'recording-1', sdp: 'test-answer-sdp' }); expect(state.pendingCandidates).toHaveLength(0); expect(state.pc.candidates).toHaveLength(1); window.RTCPeerConnection = originalRTCPeerConnection; window.RTCSessionDescription = originalRTCSessionDescription; window.RTCIceCandidate = originalRTCIceCandidate; }); });