import { describe, expect, it } from 'vitest'; import { resolve } from 'node:path'; import { fileURLToPath } from 'node:url'; import { LaunchdServiceHost } from '../service-host/launchd.js'; import { SystemdServiceHost } from '../service-host/systemd.js'; import { NssmServiceHost } from '../service-host/nssm.js'; import { renderTemplate } from '../service-host/template.js'; import type { ServiceHostContext } from '../service-host/types.js'; const here = fileURLToPath(new URL('.', import.meta.url)); const serviceDir = resolve(here, '..', '..', '..', 'service'); function ctx(): ServiceHostContext { return { nodePath: '/usr/local/bin/node', hostJs: '/opt/cadre-host/dist/bin/host.js', dataDir: '/var/data/cadre-host', serviceDir, }; } describe('renderTemplate', () => { it('substitutes @TOKEN@ values', () => { expect(renderTemplate('@A@ x @B@', { A: 'foo', B: 'bar' })).toBe('foo x bar'); }); it('throws on unknown tokens', () => { expect(() => renderTemplate('@A@ @MISSING@', { A: 'foo' })).toThrow(/missing value for token @MISSING@/); }); }); describe('SystemdServiceHost.renderUnit', () => { it('produces an enable-able [Install] section and substitutes ExecStart', () => { const unit = new SystemdServiceHost().renderUnit(ctx()); expect(unit).toContain('[Install]'); expect(unit).toContain('WantedBy=default.target'); expect(unit).toContain('ExecStart=/usr/local/bin/node /opt/cadre-host/dist/bin/host.js start --no-tui --data-dir /var/data/cadre-host'); expect(unit).toContain('RestartPreventExitStatus=0'); }); }); describe('LaunchdServiceHost.renderUnit', () => { it('produces a plist with the substituted ProgramArguments', () => { const unit = new LaunchdServiceHost().renderUnit(ctx()); expect(unit).toContain('Label'); expect(unit).toContain('com.serfab.cadre-host'); expect(unit).toContain('/usr/local/bin/node'); expect(unit).toContain('/opt/cadre-host/dist/bin/host.js'); expect(unit).toContain('--data-dir'); expect(unit).toContain('/var/data/cadre-host'); expect(unit).toContain('KeepAlive'); expect(unit).toContain('SuccessfulExit'); expect(unit).toContain('Crashed'); }); }); describe('NssmServiceHost', () => { it('returns null from renderUnit (NSSM stores config in the registry)', () => { expect(new NssmServiceHost().renderUnit(ctx())).toBeNull(); }); });