import { describe, expect, it } from 'vitest'; import { compareVersions, newestBuild, parseAdvertisedRefs, parseManifest, rawFileUrl, sameElectronMajor } from '../src/main/updates'; const sha = (c: string): string => c.repeat(40); describe('self-update helpers', () => { it('compares versions numerically', () => { expect(compareVersions('0.10.0', '0.9.9')).toBeGreaterThan(0); expect(compareVersions('1.2.3', '1.2.3')).toBe(0); expect(compareVersions('1.2.3', '1.3.0')).toBeLessThan(0); expect(() => compareVersions('v1.2.3', '1.0.0')).toThrow(); }); it('parses the refs advertised by git smart HTTP', () => { const body = '001e# service=git-upload-pack\n0000' + `00fa${sha('a')} refs/heads/main\0multi_ack thin-pack agent=git/2.47.3\n` + `003f${sha('b')} refs/builds/v0.2.0\n` + `003f${sha('c')} refs/tags/v0.2.0\n0000`; const refs = parseAdvertisedRefs(body); expect(refs.get('refs/heads/main')).toBe(sha('a')); expect(refs.get('refs/builds/v0.2.0')).toBe(sha('b')); expect(refs.get('refs/tags/v0.2.0')).toBe(sha('c')); }); it('picks the newest build above the running version', () => { const refs = new Map([ ['refs/builds/v0.2.0', sha('a')], ['refs/builds/v0.10.0', sha('b')], ['refs/builds/vbroken', sha('c')], ['refs/tags/v9.0.0', sha('d')], ]); expect(newestBuild(refs, '0.1.0')).toEqual({ version: '0.10.0', sha: sha('b') }); expect(newestBuild(refs, '0.10.0')).toBeNull(); }); it('rejects unsafe or incomplete manifests', () => { const file = { path: 'app.asar', size: 3, sha256: 'f'.repeat(64) }; const ok = { version: '0.2.0', electron: '38.8.6', files: [file] }; expect(parseManifest(ok, '0.2.0').files).toHaveLength(1); expect(() => parseManifest(ok, '0.3.0')).toThrow(/expected 0.3.0/); expect(() => parseManifest({ ...ok, files: [file, { ...file, path: '../evil.exe' }] }, '0.2.0')).toThrow(/unsafe/); expect(() => parseManifest({ ...ok, files: [file, { ...file, path: 'C:/evil.exe' }] }, '0.2.0')).toThrow(/unsafe/); expect(() => parseManifest({ ...ok, files: [{ ...file, path: 'assets/a.png' }] }, '0.2.0')).toThrow(/app.asar/); }); it('builds raw URLs and compares Electron runtimes', () => { expect(rawFileUrl('https://git.example/u/r', 'abc', 'app.asar.unpacked/node_modules/@scope/x.node')).toBe( 'https://git.example/u/r/raw/abc/app.asar.unpacked/node_modules/%40scope/x.node', ); expect(sameElectronMajor('38.8.6', '38.1.0')).toBe(true); expect(sameElectronMajor('39.0.0', '38.8.6')).toBe(false); }); });