import assert from 'node:assert/strict'; globalThis.window = { EnhancedSecureCryptoUtils: {}, DEBUG_MODE: true, DEVELOPMENT_MODE: true, location: { hostname: 'localhost', search: '?debug' }, webpackHotUpdate: {} }; const { EnhancedSecureWebRTCManager } = await import('../src/network/EnhancedSecureWebRTCManager.js'); function fake(config = {}) { return { _config: { webrtc: { privacyMode: config.privacyMode ?? (config.relayOnly ? 'relay-only' : 'standard'), relayOnly: config.relayOnly ?? false, iceServers: config.iceServers ?? [{ urls: 'stun:stun.example.test:3478' }] } }, _ipLeakWarningShown: false, delivered: [], deliverMessageToUI(message, type) { this.delivered.push({ message, type }); }, _hasTurnServer: EnhancedSecureWebRTCManager.prototype._hasTurnServer, _isRelayOnlyMode: EnhancedSecureWebRTCManager.prototype._isRelayOnlyMode, _setRelayOnlyMode: EnhancedSecureWebRTCManager.prototype._setRelayOnlyMode }; } // Standard mode remains usable, but it is not relay-only. { const manager = fake(); const config = EnhancedSecureWebRTCManager.prototype._buildPeerConnectionConfig.call(manager); assert.equal(config.iceTransportPolicy, undefined); assert.equal(config.iceServers[0].urls, 'stun:stun.example.test:3478'); } // Explicit privacy mode uses relay-only transport, suppressing host/srflx usage. { const manager = fake({ privacyMode: 'relay-only', iceServers: [{ urls: 'turn:turn.example.test:3478' }] }); const config = EnhancedSecureWebRTCManager.prototype._buildPeerConnectionConfig.call(manager); assert.equal(config.iceTransportPolicy, 'relay'); } // Backward-compatible relayOnly alias still enables relay transport. { const manager = fake({ relayOnly: true, iceServers: [{ urls: 'turn:turn.example.test:3478' }] }); const config = EnhancedSecureWebRTCManager.prototype._buildPeerConnectionConfig.call(manager); assert.equal(config.iceTransportPolicy, 'relay'); } // Runtime toggles keep the canonical privacy state synchronized. { const manager = fake({ privacyMode: 'standard', iceServers: [{ urls: 'turn:turn.example.test:3478' }] }); manager._setRelayOnlyMode(true); assert.equal(manager._config.webrtc.privacyMode, 'relay-only'); assert.equal(manager._config.webrtc.relayOnly, true); assert.equal(EnhancedSecureWebRTCManager.prototype._buildPeerConnectionConfig.call(manager).iceTransportPolicy, 'relay'); manager._setRelayOnlyMode(false); assert.equal(manager._config.webrtc.privacyMode, 'standard'); assert.equal(manager._config.webrtc.relayOnly, false); assert.equal(EnhancedSecureWebRTCManager.prototype._buildPeerConnectionConfig.call(manager).iceTransportPolicy, undefined); } // Canonical privacyMode wins over a stale legacy alias. { const manager = fake({ privacyMode: 'standard', relayOnly: true, iceServers: [{ urls: 'turn:turn.example.test:3478' }] }); assert.equal(manager._isRelayOnlyMode(), false); assert.equal(EnhancedSecureWebRTCManager.prototype._buildPeerConnectionConfig.call(manager).iceTransportPolicy, undefined); } // Missing TURN in standard mode warns clearly and visibly. { const manager = fake(); EnhancedSecureWebRTCManager.prototype._warnIfTurnMissing.call(manager); assert.equal(manager.delivered[0].type, 'system'); assert.match(manager.delivered[0].message, /relay-only mode is disabled/i); assert.match(manager.delivered[0].message, /may expose host or server-reflexive IP addresses/i); } // STUN-only config does not claim IP protection, even with privacy mode selected. { const manager = fake({ privacyMode: 'relay-only', iceServers: [{ urls: 'stun:stun.example.test:3478' }] }); assert.equal(EnhancedSecureWebRTCManager.prototype._hasTurnServer.call(manager), false); EnhancedSecureWebRTCManager.prototype._warnIfTurnMissing.call(manager); assert.match(manager.delivered[0].message, /STUN alone does not hide IP addresses/i); } // Non-private mode warns even when TURN exists because direct candidates remain allowed. { const manager = fake({ iceServers: [{ urls: 'turn:turn.example.test:3478' }] }); EnhancedSecureWebRTCManager.prototype._warnIfTurnMissing.call(manager); assert.equal(manager.delivered[0].type, 'system'); assert.match(manager.delivered[0].message, /relay-only mode is disabled/i); assert.match(manager.delivered[0].message, /may expose host or server-reflexive IP addresses/i); } // ICE defaults are centralized and operator overrides remain untouched. { assert.equal(Array.isArray(EnhancedSecureWebRTCManager.DEFAULT_ICE_SERVERS), true); assert.equal( EnhancedSecureWebRTCManager.DEFAULT_ICE_SERVERS.some(server => server.urls === 'stun:stun.cloudflare.com:3478'), true ); const overrideServers = [{ urls: ['stun:operator.example.test:3478', 'turn:operator.example.test:3478'] }]; const manager = fake({ iceServers: overrideServers }); const config = EnhancedSecureWebRTCManager.prototype._buildPeerConnectionConfig.call(manager); assert.equal(config.iceServers, overrideServers); } // ICE config diagnostics reveal whether TURN credentials were loaded without // printing sensitive usernames or passwords. { const manager = fake({ iceServers: [ { urls: 'stun:stun.example.test:3478' }, { urls: ['turn:turn.example.test:3478?transport=udp', 'turns:turn.example.test:443?transport=tcp'], username: 'user', credential: 'secret' } ] }); const summary = EnhancedSecureWebRTCManager.prototype._summarizeIceServerConfig.call( manager, manager._config.webrtc.iceServers ); assert.deepEqual(summary, { serverCount: 2, stun: 1, turn: 1, turns: 1, hasCredentials: true }); } console.log('WebRTC privacy mode tests passed');