import fs from 'node:fs/promises'; import os from 'node:os'; import path from 'node:path'; import { afterEach, describe, expect, it, vi } from 'vitest'; import { fallbackDashboardSnapshot } from '../../src/data/mockDashboard'; import { AuthService } from './auth'; import { buildRuntimePaths } from './config'; import { LiveSyncServer, buildSnapshotPatch } from './liveSync'; const cleanupDirs: string[] = []; afterEach(async () => { vi.useRealTimers(); await Promise.all(cleanupDirs.splice(0).map((dir) => fs.rm(dir, { recursive: true, force: true }))); }); describe('buildSnapshotPatch', () => { it('returns only changed top-level dashboard sections', () => { const nextSnapshot = { ...fallbackDashboardSnapshot, traffic: { ...fallbackDashboardSnapshot.traffic, totalBytes: fallbackDashboardSnapshot.traffic.totalBytes + 512, }, }; expect(buildSnapshotPatch(fallbackDashboardSnapshot, nextSnapshot)).toEqual({ traffic: nextSnapshot.traffic, }); }); it('returns a full patch when no previous snapshot exists', () => { expect(buildSnapshotPatch(null, fallbackDashboardSnapshot)).toEqual({ service: fallbackDashboardSnapshot.service, traffic: fallbackDashboardSnapshot.traffic, users: fallbackDashboardSnapshot.users, attention: fallbackDashboardSnapshot.attention, userRecords: fallbackDashboardSnapshot.userRecords, system: fallbackDashboardSnapshot.system, }); }); it('refreshes snapshots on the heartbeat even without file changes', async () => { vi.useFakeTimers(); const rootDir = await fs.mkdtemp(path.join(os.tmpdir(), '3proxy-ui-live-sync-')); cleanupDirs.push(rootDir); const snapshotReader = vi.fn().mockResolvedValue(fallbackDashboardSnapshot); const server = new LiveSyncServer({ auth: new AuthService({ login: 'admin', password: 'proxy-ui-demo', ttlMs: 60_000, }), heartbeatMs: 50, runtime: {} as never, runtimePaths: buildRuntimePaths(rootDir), snapshotReader, store: {} as never, }); await server.initialize(); expect(snapshotReader).not.toHaveBeenCalled(); await vi.advanceTimersByTimeAsync(50); expect(snapshotReader).toHaveBeenCalledTimes(1); server.close(); }); });