import assert from 'node:assert/strict'; import {readFileSync} from 'node:fs'; import {stripTypeScriptTypes} from 'node:module'; import {resolve} from 'node:path'; import {pathToFileURL} from 'node:url'; import vm from 'node:vm'; import {test} from 'node:test'; const source = resolve(process.env.FLUXER_SOURCE || 'fluxer-src'); const desktop = resolve(source, 'fluxer_desktop'); const read = (file) => readFileSync(resolve(desktop, file), 'utf8'); const {parseGscordRelease} = await import(pathToFileURL(resolve(desktop, 'src/main/GscordRelease.ts'))); const compatibility = read('src/preload/GscordCompatibility.ts'); function evaluate(globals = {}) { return vm.createContext({...globals}); } function run(ts, context) { vm.runInContext(stripTypeScriptTypes(ts).replace(/^export /gm, ''), context); } function rtcContext() { class Peer { constructor(config) { this.config = config; } setConfiguration(config) { this.config = config; } } const context = evaluate({window: {RTCPeerConnection: Peer}, console: {info() {}}}); run(compatibility, context); context.installForceAllIceTransportPolicy(); return {context, Peer, Wrapped: context.window.RTCPeerConnection}; } test('URLs, homepage and initial server use GSCORD', () => { const constants = read('src/common/Constants.ts'); for (const name of ['STABLE_APP_URL', 'CANARY_APP_URL', 'STABLE_MIGRATED_APP_ORIGIN', 'CANARY_MIGRATED_APP_ORIGIN']) { assert.ok(constants.includes(`export const ${name} = 'https://ds.gandc.ru';`)); } assert.equal(JSON.parse(read('package.json')).homepage, 'https://ds.gandc.ru'); const discovery = read('src/main/DesktopRuntimeDiscovery.ts'); const start = discovery.indexOf('export function desktopRuntimeInitialInput'); const code = discovery.slice(start, discovery.indexOf('\nasync function fetchRuntimePlan', start)); let override = null; const context = evaluate({BUILD_CHANNEL: 'stable', CHANNEL_APP_URLS: {stable: 'https://ds.gandc.ru'}, getLaunchInstanceEndpointOverride: () => override}); run(code, context); assert.equal(context.desktopRuntimeInitialInput(), 'https://ds.gandc.ru'); override = 'https://another.example'; assert.equal(context.desktopRuntimeInitialInput(), override); }); test('ICE constructor overrides relay without mutating caller configuration', () => { const {Peer, Wrapped} = rtcContext(); const config = Object.freeze({iceTransportPolicy: 'relay', iceServers: [{urls: 'stun:example.test'}]}); const peer = new Wrapped(config); assert.equal(peer.config.iceTransportPolicy, 'all'); assert.equal(config.iceTransportPolicy, 'relay'); assert.equal(peer.config.iceServers, config.iceServers); assert.ok(peer instanceof Peer); assert.ok(peer instanceof Wrapped); }); test('ICE setConfiguration overrides relay and preserves native validation path', () => { const {Wrapped} = rtcContext(); const peer = new Wrapped(); const config = Object.freeze({iceTransportPolicy: 'relay'}); peer.setConfiguration(config); assert.equal(peer.config.iceTransportPolicy, 'all'); assert.equal(config.iceTransportPolicy, 'relay'); const other = {iceTransportPolicy: 'all'}; peer.setConfiguration(other); assert.equal(peer.config, other); peer.setConfiguration(undefined); assert.equal(peer.config, undefined); }); test('ICE proxy preserves subclass construction and default configuration', () => { const {Wrapped} = rtcContext(); class Derived extends Wrapped {} const peer = new Derived(); assert.ok(peer instanceof Derived); assert.equal(peer.config, undefined); }); test('ICE installer tolerates missing WebRTC', () => { const context = evaluate({window: {}, console}); run(compatibility, context); context.installForceAllIceTransportPolicy(); }); test('LiveKit workaround restores compression for RTC socket and suppresses it on close', () => { class Compression {} class Socket { constructor(url) { this.url = url; } addEventListener(name, callback) { this[name] = callback; } } const window = {WebSocket: Socket, CompressionStream: Compression, location: {href: 'https://ds.gandc.ru'}}; const context = evaluate({window, URL, console: {info() {}}}); run(compatibility, context); context.installDisableLiveKitOfferWithJoin(); assert.equal(window.CompressionStream, undefined); new window.WebSocket('wss://ds.gandc.ru/gateway'); assert.equal(window.CompressionStream, undefined); const rtc = new window.WebSocket('wss://voice.example/rtc/v1?token=test'); assert.equal(window.CompressionStream, Compression); rtc.close(); assert.equal(window.CompressionStream, undefined); }); test('preload installs both self-contained callbacks in main world', () => { const preload = read('src/preload/index.ts'); for (const name of ['installForceAllIceTransportPolicy', 'installDisableLiveKitOfferWithJoin']) { assert.ok(preload.includes(`contextBridge.executeInMainWorld({func: ${name}});`)); } }); test('navigation allows exact SSO and callback origins but rejects lookalikes and reserved paths', () => { const windowCode = read('src/main/Window.ts'); const start = windowCode.indexOf('function isAllowedSsoNavigation'); const end = windowCode.indexOf('function preventUntrustedChildNavigation', start); const local = 'fluxer://app/'; const context = evaluate({URL, logger: {warn() {}}, isLocalAppURL: (url) => url.startsWith(local), isLocalAppRendererDocumentURL: (url) => url === `${local}channels/@me`, isTrustedOrigin: (url) => url === `${local}channels/@me`}); run(windowCode.slice(start, end), context); for (const [url, blocked] of [ ['https://sso.gandc.ru/login', false], ['https://ds.gandc.ru/callback', false], [`${local}channels/@me`, false], [`${local}__reserved`, true], ['https://sso.gandc.ru.evil.test', true], ['http://sso.gandc.ru', true], ['https://sso.gandc.ru:8443', true], ['https://sso.gandc.ru@evil.test', true], ['invalid', true], ]) { let prevented = false; context.preventUntrustedNavigation({preventDefault() { prevented = true; }}, url, true); assert.equal(prevented, blocked, url); } assert.equal((windowCode.match(/webContents.on\('will-redirect'/g) || []).length, 2); const trusted = windowCode.slice(windowCode.indexOf('export function isTrustedOrigin'), windowCode.indexOf('function getSanitizedPath')); assert.ok(!trusted.includes('isAllowedSsoNavigation')); }); const asset = (name) => ({name, browser_download_url: `https://git.gandc.ru/assets/${name}`}); const release = {tag_name: 'v1.0.99', published_at: '2026-10-11T00:00:00Z', assets: [ asset('Fluxer-1.0.99-linux-x86_64.AppImage'), asset('Fluxer-1.0.99-linux-amd64.deb'), asset('Fluxer-1.0.99-linux-arm64.AppImage'), asset('Fluxer-1.0.99-linux-arm64.deb'), asset('Fluxer-1.0.99-portable-win-x64.exe'), asset('Fluxer-1.0.99-win-arm64.exe'), asset('unrelated.zip'), null, {name: 'broken'}, ]}; test('Gitea parser selects only the matching platform and architecture', () => { for (const arch of ['x64', 'arm64']) { const linux = parseGscordRelease(release, 'linux', arch); assert.equal(linux.version, '1.0.99'); assert.equal(linux.pubDate, release.published_at); assert.deepEqual(Object.keys(linux.files).sort(), ['appimage', 'deb']); assert.ok(linux.files.appimage.url.includes(arch === 'x64' ? 'x86_64' : 'arm64')); const win = parseGscordRelease(release, 'win32', arch); assert.deepEqual(Object.keys(win.files), ['setup']); assert.ok(win.files.setup.url.endsWith(`win-${arch}.exe`)); } }); test('Gitea parser accepts current portable filename and handles absent or malformed assets', () => { const parsed = parseGscordRelease({tag_name: '1.0.100', assets: [asset('Fluxer-1.0.100-win-x64.exe')]}, 'win32', 'x64'); assert.ok(parsed.files.setup); assert.deepEqual(parseGscordRelease({tag_name: 'v1.0.1'}, 'linux', 'x64').files, {}); for (const payload of [null, {}, {tag_name: ' '}, {tag_name: 'v'}]) assert.throws(() => parseGscordRelease(payload)); }); test('manual updater offers actual Gitea assets rather than fabricated URLs', () => { let code = read('src/main/UpdaterDownloads.ts').replace(/^import[\s\S]*?from '[^']+';\n/gm, ''); const context = evaluate({process: {platform: 'linux'}, DESKTOP_DOWNLOAD_ARCH: 'x64', DESKTOP_ARTIFACT_PRODUCT_NAME: 'Fluxer', LINUX_MANUAL_ARCH_TOKENS: {deb: {x64: 'amd64'}}, LINUX_MANUAL_FORMAT_EXTENSIONS: {deb: '.deb'}, isLinuxManualDesktopFormat: () => true}); run(code, context); const info = {version: '1.0.99', files: {deb: {url: 'https://git.gandc.ru/assets/real.deb', sha256: null}}}; const options = context.getManualDownloadOptions(info); assert.equal(options.length, 1); assert.equal(options[0].url, info.files.deb.url); assert.equal(context.getManualDownloadUrl({version: '1.0.99', files: {}}), 'https://git.gandc.ru/gscord/gscord-desktop/releases'); }); test('GSCORD portable Windows and Linux including AppImage use manual updates', () => { const code = read('src/main/ShellUpdateCapability.ts'); const start = code.indexOf('export function decideShellUpdatePlan'); const context = evaluate({ShellUpdateCapability: {MANUAL_DOWNLOAD: 'manual', MANAGED_PACKAGE: 'managed', SELF_UPDATE: 'self'}}); run(code.slice(start, code.indexOf('function resolveRunningAppImage', start)), context); const runtime = {packaged: true, channel: 'stable', flatpak: false, portable: false, appImage: {path: 'test'}}; for (const platform of ['win32', 'linux']) assert.equal(context.decideShellUpdatePlan({...runtime, platform}).capability, 'manual'); assert.equal(context.decideShellUpdatePlan({...runtime, platform: 'linux', flatpak: true}).capability, 'managed'); assert.equal(context.decideShellUpdatePlan({...runtime, platform: 'linux', packaged: false}).reason, 'unpackaged'); });