/** * @fileoverview Runs the real lid helper of the Codeman app for Mac * (packages/desktop/macos/keep-awake-lid.sh) in bash against a stub `pmset` that * records what it was asked to do. The helper runs as root from a LaunchDaemon, so the * cases that matter are the ones where it must NOT act: a stale, dead, foreign or * symlinked request, a Mac on battery, and an administrator's own `disablesleep 1`, * which it must never switch off. * * Only the BSD/GNU-portable tools the script uses (find -mmin, ps -o uid=, ls -ln) run * here, so this passes on Linux and macOS alike. The app embeds the same file * (include_str! in packages/desktop/src/keep_awake.rs), so this is the script that * gets installed. Port: none. */ import { spawnSync } from 'node:child_process'; import { chmodSync, existsSync, mkdtempSync, readFileSync, rmSync, symlinkSync, utimesSync, writeFileSync, } from 'node:fs'; import { tmpdir } from 'node:os'; import { join } from 'node:path'; import { fileURLToPath } from 'node:url'; import { afterEach, beforeEach, describe, expect, it } from 'vitest'; const HELPER = fileURLToPath(new URL('../packages/desktop/macos/keep-awake-lid.sh', import.meta.url)); let dir: string; let stateDir: string; let request: string; /** * Stub pmset: `-g` prints the stored SleepDisabled, `-g batt` the stored power source, * `-a disablesleep N` stores N and logs it. */ function writeStubPmset(initial: '0' | '1' | null, power: 'AC Power' | 'Battery Power' | null = 'AC Power') { const value = join(dir, 'sleepdisabled'); if (initial !== null) writeFileSync(value, initial); const source = join(dir, 'power'); if (power !== null) writeFileSync(source, power); const stub = join(dir, 'pmset'); writeFileSync( stub, `#!/bin/bash if [ "$1" = "-g" ] && [ "$2" = "batt" ]; then [ -f "${source}" ] || exit 1 echo "Now drawing from '$(cat "${source}")'" exit 0 fi if [ "$1" = "-g" ]; then echo "System-wide power settings:" [ -f "${value}" ] && printf ' SleepDisabled\\t\\t%s\\n' "$(cat "${value}")" echo "Currently in use:" echo " sleep 1" exit 0 fi if [ "$1" = "-a" ] && [ "$2" = "disablesleep" ]; then printf '%s' "$3" > "${value}" echo "disablesleep $3" >> "${join(dir, 'pmset.log')}" exit 0 fi exit 1 ` ); chmodSync(stub, 0o755); return stub; } const setPower = (power: 'AC Power' | 'Battery Power') => writeFileSync(join(dir, 'power'), power); function run() { const result = spawnSync('bash', [HELPER, request], { encoding: 'utf-8', env: { ...process.env, CODEMAN_KEEPAWAKE_PMSET: join(dir, 'pmset'), CODEMAN_KEEPAWAKE_STATE_DIR: stateDir, }, }); expect(result.status, result.stderr).toBe(0); } const calls = () => (existsSync(join(dir, 'pmset.log')) ? readFileSync(join(dir, 'pmset.log'), 'utf-8') : ''); const sleepDisabled = () => readFileSync(join(dir, 'sleepdisabled'), 'utf-8'); const owned = () => existsSync(join(stateDir, 'keep-awake.owned')); /** What the app writes: its own pid and a newline. This test process stands in for it. */ const requestFromLiveApp = () => writeFileSync(request, `${process.pid}\n`); beforeEach(() => { dir = mkdtempSync(join(tmpdir(), 'keep-awake-lid-')); stateDir = join(dir, 'state'); request = join(dir, 'keep-awake-lid.pid'); }); afterEach(() => { rmSync(dir, { recursive: true, force: true }); }); describe('keep-awake-lid.sh', () => { it('does nothing without a request', () => { writeStubPmset('0'); run(); expect(calls()).toBe(''); expect(owned()).toBe(false); }); it('applies disablesleep for a fresh request from a live process, then undoes it when the request goes', () => { writeStubPmset('0'); requestFromLiveApp(); run(); expect(sleepDisabled()).toBe('1'); expect(owned()).toBe(true); run(); // steady state: no repeated pmset call expect(calls()).toBe('disablesleep 1\n'); rmSync(request); run(); expect(sleepDisabled()).toBe('0'); expect(owned()).toBe(false); expect(calls()).toBe('disablesleep 1\ndisablesleep 0\n'); }); it('never acts on battery, and lets go when the adapter is pulled', () => { writeStubPmset('0', 'Battery Power'); requestFromLiveApp(); run(); expect(calls()).toBe(''); setPower('AC Power'); run(); expect(sleepDisabled()).toBe('1'); setPower('Battery Power'); run(); expect(sleepDisabled()).toBe('0'); expect(owned()).toBe(false); }); it('reads an unanswerable power source as battery', () => { writeStubPmset('0', null); requestFromLiveApp(); run(); expect(calls()).toBe(''); }); it("never switches off an administrator's own disablesleep", () => { writeStubPmset('1'); requestFromLiveApp(); run(); expect(calls()).toBe(''); expect(owned()).toBe(false); rmSync(request); run(); expect(sleepDisabled()).toBe('1'); expect(calls()).toBe(''); }); it("leaves an administrator's own disablesleep on when the adapter is pulled", () => { writeStubPmset('1', 'Battery Power'); requestFromLiveApp(); run(); expect(calls()).toBe(''); expect(sleepDisabled()).toBe('1'); }); it('treats an absent SleepDisabled line as 0', () => { writeStubPmset(null); requestFromLiveApp(); run(); expect(sleepDisabled()).toBe('1'); expect(owned()).toBe(true); }); it('re-applies after a reboot or OS update reset it, while the request stands', () => { writeStubPmset('0'); requestFromLiveApp(); run(); writeFileSync(join(dir, 'sleepdisabled'), '0'); // the reset run(); expect(sleepDisabled()).toBe('1'); }); it('ignores a stale request (an app that hangs) and releases', () => { writeStubPmset('0'); requestFromLiveApp(); run(); expect(sleepDisabled()).toBe('1'); const threeMinutesAgo = (Date.now() - 3 * 60_000) / 1000; utimesSync(request, threeMinutesAgo, threeMinutesAgo); run(); expect(sleepDisabled()).toBe('0'); expect(owned()).toBe(false); }); it('releases at the next run once the app is gone, however fresh its file', () => { writeStubPmset('0'); requestFromLiveApp(); run(); expect(sleepDisabled()).toBe('1'); const gone = spawnSync('true').pid; writeFileSync(request, `${gone}\n`); run(); expect(sleepDisabled()).toBe('0'); expect(owned()).toBe(false); }); it('ignores a request whose pid is not running', () => { writeStubPmset('0'); const gone = spawnSync('true').pid; writeFileSync(request, `${gone}\n`); run(); expect(calls()).toBe(''); }); it('ignores garbage and refuses a symlinked request', () => { writeStubPmset('0'); writeFileSync(request, 'not-a-pid\n'); run(); expect(calls()).toBe(''); rmSync(request); const real = join(dir, 'elsewhere.pid'); writeFileSync(real, `${process.pid}\n`); symlinkSync(real, request); run(); expect(calls()).toBe(''); }); });