/** * Central instance id rotation invariant. * * `MeshCentralRegistry.upsert` is the single source of truth for the * locally-cached callback material. When central regenerates its * `instance_id` (factory reset, DB swap, container rebuild without volume), * the next bootstrap frame arrives with a different `centralInstanceId`. The * registry MUST: * - persist the new row, * - emit a single `central-instance-changed` event so subscribers (loud * log, dialer reset) can react. * * Failing this invariant means the peer would keep dialing back with the * old JWT (rejected with `instance_mismatch`) without anyone noticing. */ import { describe, it, expect, beforeAll, afterAll, beforeEach } from 'vitest'; import { setupTestDb, cleanupTestDb } from './helpers/setupTestDb'; import { MeshCentralRegistry } from '../services/MeshCentralRegistry'; import { DatabaseService } from '../services/DatabaseService'; let tmpDir: string; beforeAll(async () => { tmpDir = await setupTestDb(); }); afterAll(() => cleanupTestDb(tmpDir)); describe('Central instance id change', () => { beforeEach(() => { MeshCentralRegistry.resetForTest(); // Per-test cleanup of the mesh_centrals table. The DB lives once per // file (beforeAll); resetting setupTestDb per test would invalidate // the DatabaseService singleton's connection on Linux. DatabaseService.getInstance().getDb().prepare('DELETE FROM mesh_centrals').run(); }); it('drops the old row and accepts the new one with a central-instance-changed event', () => { const reg = MeshCentralRegistry.getInstance(); reg.upsert({ centralInstanceId: 'old-uuid', centralApiUrl: 'https://central.example.com', callbackJwt: 'jwt-old', jwtIssuedAt: 1, jwtExpiresAt: 999999, }); const events: Array<{ previousInstanceId: string; newInstanceId: string }> = []; reg.on('central-instance-changed', (e: { previousInstanceId: string; newInstanceId: string }) => events.push(e)); reg.upsert({ centralInstanceId: 'new-uuid', centralApiUrl: 'https://central.example.com', callbackJwt: 'jwt-new', jwtIssuedAt: 2, jwtExpiresAt: 999999, }); expect(events).toHaveLength(1); expect(events[0].previousInstanceId).toBe('old-uuid'); expect(events[0].newInstanceId).toBe('new-uuid'); // getActive returns the most-recently-bootstrapped row. expect(reg.getActive()?.centralInstanceId).toBe('new-uuid'); expect(reg.getActive()?.callbackJwt).toBe('jwt-new'); }); it('an upsert with the same instance id does NOT emit central-instance-changed', () => { const reg = MeshCentralRegistry.getInstance(); reg.upsert({ centralInstanceId: 'stable-uuid', centralApiUrl: 'https://central.example.com', callbackJwt: 'jwt-1', jwtIssuedAt: 1, jwtExpiresAt: 999999, }); const events: Array = []; reg.on('central-instance-changed', (e: unknown) => events.push(e)); reg.upsert({ centralInstanceId: 'stable-uuid', centralApiUrl: 'https://central.example.com', callbackJwt: 'jwt-2-rotated', jwtIssuedAt: 2, jwtExpiresAt: 999999, }); expect(events).toHaveLength(0); // Same id, the JWT material is overwritten in place. expect(reg.getActive()?.callbackJwt).toBe('jwt-2-rotated'); }); });