daemon-guards.test.ts
1 import assert from 'node:assert/strict' 2 import fs from 'node:fs' 3 import os from 'node:os' 4 import path from 'node:path' 5 import { after, before, describe, it } from 'node:test' 6 7 const originalEnv = { 8 DATA_DIR: process.env.DATA_DIR, 9 WORKSPACE_DIR: process.env.WORKSPACE_DIR, 10 SWARMCLAW_BUILD_MODE: process.env.SWARMCLAW_BUILD_MODE, 11 SWARMCLAW_DAEMON_AUTOSTART: process.env.SWARMCLAW_DAEMON_AUTOSTART, 12 SWARMCLAW_DAEMON_BACKGROUND_SERVICES: process.env.SWARMCLAW_DAEMON_BACKGROUND_SERVICES, 13 } 14 15 let tempDir = '' 16 let mod: typeof import('@/lib/server/runtime/daemon-state') 17 18 before(async () => { 19 tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'swarmclaw-daemon-guards-')) 20 process.env.DATA_DIR = path.join(tempDir, 'data') 21 process.env.WORKSPACE_DIR = path.join(tempDir, 'workspace') 22 process.env.SWARMCLAW_BUILD_MODE = '1' 23 process.env.SWARMCLAW_DAEMON_AUTOSTART = '0' 24 mod = await import('@/lib/server/runtime/daemon-state') 25 }) 26 27 after(async () => { 28 try { await mod.stopDaemon({ source: 'test-cleanup' }) } catch { /* ignore */ } 29 for (const [key, val] of Object.entries(originalEnv)) { 30 if (val === undefined) delete process.env[key] 31 else process.env[key] = val 32 } 33 fs.rmSync(tempDir, { recursive: true, force: true }) 34 }) 35 36 // ── getDaemonStatus includes guards ────────────────────────────────────── 37 38 describe('getDaemonStatus guards field', () => { 39 it('includes guards key with expected shape', () => { 40 const status = mod.getDaemonStatus() 41 assert.ok('guards' in status, 'status should have guards field') 42 const { guards } = status 43 assert.equal(typeof guards.healthCheckRunning, 'boolean') 44 assert.equal(typeof guards.connectorHealthCheckRunning, 'boolean') 45 assert.equal(typeof guards.shuttingDown, 'boolean') 46 assert.equal(typeof guards.providerCircuitBreakers, 'number') 47 }) 48 49 it('guards defaults are all false/zero when idle', () => { 50 const { guards } = mod.getDaemonStatus() 51 assert.equal(guards.healthCheckRunning, false) 52 assert.equal(guards.connectorHealthCheckRunning, false) 53 assert.equal(guards.shuttingDown, false) 54 assert.equal(guards.providerCircuitBreakers, 0) 55 }) 56 }) 57 58 // ── shuttingDown flag lifecycle ────────────────────────────────────────── 59 60 describe('shuttingDown flag', () => { 61 it('resets to false after stopDaemon completes', async () => { 62 mod.startDaemon({ source: 'test', manualStart: true }) 63 await mod.stopDaemon({ source: 'test' }) 64 const { guards } = mod.getDaemonStatus() 65 assert.equal(guards.shuttingDown, false) 66 }) 67 68 it('double stopDaemon calls do not throw', async () => { 69 mod.startDaemon({ source: 'test', manualStart: true }) 70 await mod.stopDaemon({ source: 'first' }) 71 await assert.doesNotReject(() => mod.stopDaemon({ source: 'second' })) 72 assert.equal(mod.getDaemonStatus().running, false) 73 }) 74 })