import { describe, expect, it, vi } from 'vitest' import { DEFAULT_CERT_RENEW_WINDOW_MS, certIsFresh, frpcProxyStarted, probeLoopbackBaseApp, renderHealthStatus, runHealthProbe, startHealthMonitor, type HealthProbeSeams, type HealthReport, type IntervalTimer, } from '../src/health/probe.js' /** All-passing seams; each test overrides exactly one to prove sub-check independence. */ function healthySeams(over: Partial = {}): HealthProbeSeams { return { isFrpcAlive: () => true, probeBaseApp: async () => true, readFrpcLog: () => 'proxy [web-terminal] start proxy success', certNotAfter: () => new Date('2026-08-01T00:00:00Z'), now: () => new Date('2026-07-08T00:00:00Z'), ...over, } } describe('frpcProxyStarted (log scan)', () => { it('detects the frpc start-proxy-success line', () => { expect(frpcProxyStarted('2026/07/08 [I] [proxy_manager] start proxy success')).toBe(true) }) it('is false before frpc reports success', () => { expect(frpcProxyStarted('login to server success\nstart proxy ...')).toBe(false) expect(frpcProxyStarted('')).toBe(false) }) }) describe('certIsFresh (near-expiry check)', () => { const now = new Date('2026-07-08T00:00:00Z') it('is fresh when notAfter is beyond the renewal window', () => { const notAfter = new Date(now.getTime() + DEFAULT_CERT_RENEW_WINDOW_MS + 60_000) expect(certIsFresh(notAfter, now, DEFAULT_CERT_RENEW_WINDOW_MS)).toBe(true) }) it('is NOT fresh when notAfter is inside the renewal window', () => { const notAfter = new Date(now.getTime() + DEFAULT_CERT_RENEW_WINDOW_MS - 60_000) expect(certIsFresh(notAfter, now, DEFAULT_CERT_RENEW_WINDOW_MS)).toBe(false) }) it('treats a missing cert (null notAfter) as not fresh', () => { expect(certIsFresh(null, now, DEFAULT_CERT_RENEW_WINDOW_MS)).toBe(false) }) }) describe('probeLoopbackBaseApp (loopback-only)', () => { it('targets 127.0.0.1:PORT and returns true on an ok response', async () => { const fetchImpl = vi.fn(async (url: string) => ({ ok: url.includes('127.0.0.1:3000') })) await expect(probeLoopbackBaseApp(3000, fetchImpl)).resolves.toBe(true) expect(fetchImpl).toHaveBeenCalledWith('http://127.0.0.1:3000/') }) it('returns false on a non-ok response', async () => { await expect(probeLoopbackBaseApp(3000, async () => ({ ok: false }))).resolves.toBe(false) }) it('swallows a rejected fetch (a probe never throws)', async () => { await expect( probeLoopbackBaseApp(3000, async () => { throw new Error('ECONNREFUSED') }), ).resolves.toBe(false) }) it('rejects an out-of-range port without fetching', async () => { const fetchImpl = vi.fn(async () => ({ ok: true })) await expect(probeLoopbackBaseApp(0, fetchImpl)).resolves.toBe(false) await expect(probeLoopbackBaseApp(70000, fetchImpl)).resolves.toBe(false) expect(fetchImpl).not.toHaveBeenCalled() }) }) describe('runHealthProbe (aggregate verdict)', () => { it('is healthy when all four sub-checks pass', async () => { const report = await runHealthProbe(healthySeams()) expect(report).toEqual({ frpcAlive: true, baseAppReachable: true, proxyStarted: true, certFresh: true, healthy: true, }) }) it('is unhealthy if frpc is dead', async () => { const report = await runHealthProbe(healthySeams({ isFrpcAlive: () => false })) expect(report.frpcAlive).toBe(false) expect(report.healthy).toBe(false) }) it('is unhealthy if the base app is unreachable', async () => { const report = await runHealthProbe(healthySeams({ probeBaseApp: async () => false })) expect(report.baseAppReachable).toBe(false) expect(report.healthy).toBe(false) }) it('is unhealthy if the proxy never started', async () => { const report = await runHealthProbe(healthySeams({ readFrpcLog: () => 'connecting...' })) expect(report.proxyStarted).toBe(false) expect(report.healthy).toBe(false) }) it('is unhealthy if the cert is near expiry', async () => { const report = await runHealthProbe( healthySeams({ certNotAfter: () => new Date('2026-07-08T01:00:00Z'), // 1h out, inside 8h window }), ) expect(report.certFresh).toBe(false) expect(report.healthy).toBe(false) }) }) describe('renderHealthStatus (INV9 — non-secret only)', () => { const report: HealthReport = { frpcAlive: true, baseAppReachable: true, proxyStarted: true, certFresh: true, healthy: true, } it('prints subdomain, host id, expiry date, and flags', () => { const lines = renderHealthStatus( { subdomain: 'alice', hostId: 'h-1', certNotAfter: new Date('2026-08-01T00:00:00Z') }, report, ) const joined = lines.join('\n') expect(joined).toContain('subdomain: alice') expect(joined).toContain('host_id: h-1') expect(joined).toContain('cert_expiry: 2026-08-01T00:00:00.000Z') expect(joined).toContain('healthy: true') }) it('leaks NO key/cert/token/CSR material', () => { const lines = renderHealthStatus( { subdomain: 'alice', hostId: 'h-1', certNotAfter: new Date('2026-08-01T00:00:00Z') }, report, ) const joined = lines.join('\n') expect(joined).not.toMatch(/PRIVATE KEY|BEGIN CERTIFICATE|BEGIN CERTIFICATE REQUEST/) expect(joined.toLowerCase()).not.toMatch(/token|secret|csr|pem/) }) it('renders (none)/(unknown) placeholders when identifiers are absent', () => { const lines = renderHealthStatus({ subdomain: null, hostId: null, certNotAfter: null }, report) const joined = lines.join('\n') expect(joined).toContain('subdomain: (none)') expect(joined).toContain('cert_expiry: (unknown)') }) }) describe('startHealthMonitor (periodic)', () => { function fakeTimer(): { timer: IntervalTimer; fire: () => void; cleared: boolean } { let cb: (() => void) | null = null const state = { cleared: false } return { timer: { setInterval: (fn) => { cb = fn return 1 }, clearInterval: () => { state.cleared = true }, }, fire: () => cb?.(), get cleared() { return state.cleared }, } } it('runs the probe on each tick and reports it', async () => { const report: HealthReport = { frpcAlive: true, baseAppReachable: true, proxyStarted: true, certFresh: true, healthy: true, } const probe = vi.fn(async () => report) const seen: HealthReport[] = [] const ft = fakeTimer() const monitor = startHealthMonitor(probe, (r) => seen.push(r), { timer: ft.timer }) ft.fire() await Promise.resolve() await Promise.resolve() expect(probe).toHaveBeenCalledTimes(1) expect(seen).toEqual([report]) monitor.stop() expect(ft.cleared).toBe(true) }) it('swallows a rejected probe (monitor never crashes)', async () => { const ft = fakeTimer() const onReport = vi.fn() startHealthMonitor(async () => Promise.reject(new Error('boom')), onReport, { timer: ft.timer }) ft.fire() await Promise.resolve() await Promise.resolve() expect(onReport).not.toHaveBeenCalled() }) })