84 lines
4.7 KiB
TypeScript
84 lines
4.7 KiB
TypeScript
import { describe, expect, test } from 'bun:test';
|
|
import { appExe, listCommand, logDir, matchByPath, mismatchMessage, notOpenedMessage, parseProcs, peVersion, startedHidden, stopCommand, versionMatches } from '../cli/lib/desktop-proc';
|
|
|
|
/** A tiny fake PE: "MZ", padding, then a VS_FIXEDFILEINFO with the given product version. */
|
|
function fakePe(a: number, b: number, c: number, d = 0, at = 128): Uint8Array {
|
|
const buf = new Uint8Array(at + 52);
|
|
const v = new DataView(buf.buffer);
|
|
v.setUint16(0, 0x5a4d, true);
|
|
v.setUint32(at, 0xfeef04bd, true);
|
|
v.setUint32(at + 4, 0x00010000, true);
|
|
v.setUint32(at + 8, (9 << 16) | 9, true); // file version differs on purpose
|
|
v.setUint32(at + 16, (a << 16) | b, true);
|
|
v.setUint32(at + 20, (c << 16) | d, true);
|
|
return buf;
|
|
}
|
|
|
|
describe('desktop install: real version of the installed files', () => {
|
|
test('PE product version from VS_FIXEDFILEINFO', () => {
|
|
expect(peVersion(fakePe(0, 8, 46))).toBe('0.8.46');
|
|
expect(peVersion(fakePe(2, 0, 7, 5))?.split(".")).toEqual(["2", "0", "7", "5"]);
|
|
expect(peVersion(new Uint8Array(200))).toBeNull(); // no MZ
|
|
const noInfo = new Uint8Array(200); noInfo[0] = 0x4d; noInfo[1] = 0x5a;
|
|
expect(peVersion(noInfo)).toBeNull();
|
|
});
|
|
test('the stale 0.8.38 exe is a mismatch for 0.8.46', () => {
|
|
expect(versionMatches('0.8.38', '0.8.46', true)).toBe(false);
|
|
expect(versionMatches('0.8.46', '0.8.46', true)).toBe(true);
|
|
expect(versionMatches('0.8.46', '0.8.46-beta.1', true)).toBe(true);
|
|
expect(versionMatches('0.8.46', '0.8.46')).toBe(true);
|
|
expect(versionMatches('0.8.45', '0.8.46')).toBe(false);
|
|
const m = mismatchMessage('0.8.38', '0.8.46', 'C:\\Users\\u\\AppData\\Local\\storebound\\storebound-desktop.exe');
|
|
expect(m).toContain('is 0.8.38, expected 0.8.46');
|
|
expect(m).toContain('Close storebound desktop');
|
|
expect(mismatchMessage(null, '0.8.46', 'x')).toContain('unreadable');
|
|
});
|
|
});
|
|
|
|
describe('desktop install: the running app by exact path', () => {
|
|
const exe = 'C:\\Users\\u\\AppData\\Local\\storebound\\storebound-desktop.exe';
|
|
test('Windows: CIM JSON, one row or many, case-insensitive exact path only', () => {
|
|
expect(listCommand('win32')[0]).toBe('powershell');
|
|
const one = parseProcs('win32', JSON.stringify({ ProcessId: 42, ExecutablePath: exe.toUpperCase(), CommandLine: `"${exe}" --hidden` }));
|
|
expect(matchByPath('win32', one, exe).map(p => p.pid)).toEqual([42]);
|
|
expect(startedHidden(one[0]!)).toBe(true);
|
|
const many = parseProcs('win32', JSON.stringify([
|
|
{ ProcessId: 1, ExecutablePath: 'D:\\other\\storebound-desktop.exe', CommandLine: '' },
|
|
{ ProcessId: 2, ExecutablePath: exe, CommandLine: `"${exe}"` },
|
|
{ ProcessId: 3, ExecutablePath: null, CommandLine: null },
|
|
]));
|
|
expect(matchByPath('win32', many, exe).map(p => p.pid)).toEqual([2]);
|
|
expect(startedHidden(many[1]!)).toBe(false);
|
|
expect(parseProcs('win32', '')).toEqual([]);
|
|
expect(parseProcs('win32', 'not json')).toEqual([]);
|
|
});
|
|
test('macOS / Linux: ps args, the bundle binary, no prefix lookalikes', () => {
|
|
const mac = appExe('darwin', '/Applications/storebound.app');
|
|
expect(mac).toBe('/Applications/storebound.app/Contents/MacOS/storebound-desktop');
|
|
const procs = parseProcs('darwin', [
|
|
` 101 ${mac}`,
|
|
` 102 ${mac} --hidden`,
|
|
` 103 ${mac}-helper`,
|
|
` 104 /tmp/h/Applications/storebound.app/Contents/MacOS/storebound-desktop`,
|
|
'',
|
|
].join('\n'));
|
|
expect(matchByPath('darwin', procs, mac).map(p => [p.pid, startedHidden(p)])).toEqual([[101, false], [102, true]]);
|
|
expect(appExe('linux', '/tmp/h/.local/bin/storebound.AppImage')).toBe('/tmp/h/.local/bin/storebound.AppImage');
|
|
});
|
|
test('stop: graceful first, then forced, by PID', () => {
|
|
expect(stopCommand('win32', 42, false)).toEqual(['taskkill', '/PID', '42']);
|
|
expect(stopCommand('win32', 42, true)).toEqual(['taskkill', '/F', '/PID', '42']);
|
|
expect(stopCommand('darwin', 7, false)).toEqual(['kill', '-TERM', '7']);
|
|
expect(stopCommand('linux', 7, true)).toEqual(['kill', '-KILL', '7']);
|
|
});
|
|
});
|
|
|
|
describe('desktop open: the log the message points to', () => {
|
|
test('per OS', () => {
|
|
expect(logDir('win32', { APPDATA: 'C:\\Users\\u\\AppData\\Roaming' }, 'C:\\Users\\u')).toBe('C:\\Users\\u\\AppData\\Roaming\\dev.storebound.desktop\\logs');
|
|
expect(logDir('darwin', {}, '/tmp/h')).toBe('/tmp/h/Library/Application Support/dev.storebound.desktop/logs');
|
|
expect(logDir('linux', {}, '/tmp/h')).toBe('/tmp/h/.local/share/dev.storebound.desktop/logs');
|
|
expect(notOpenedMessage('none', 'x.exe', 'L')).toContain('did not open: no process');
|
|
expect(notOpenedMessage('hidden', 'x.exe', 'L')).toContain('hidden (tray)');
|
|
});
|
|
});
|