llm-semantic-visualizer / tests /bootProgress.test.js
hbauzan's picture
feat(ui): restore Arithmetic default and Galaxy progress overlay
ec03da9
Raw
History Blame Contribute Delete
4.14 kB
import { describe, expect, it, vi } from 'vitest';
import {
formatBootProgressLabel,
resolveBootProgressBar,
softWaitRatio,
softStepRatio,
withTimeout,
waitForBackendHealthy,
startProgressTicker,
withSoftStepProgress,
} from '../src/ui/BootProgress.js';
describe('BootProgress helpers', () => {
it('formatBootProgressLabel prefers statusText then k/n label', () => {
expect(formatBootProgressLabel({ statusText: '1/3 Encoding…' })).toBe('1/3 Encoding…');
expect(formatBootProgressLabel({ step: 2, total: 4, label: 'UMAP' })).toBe('2/4 UMAP');
expect(formatBootProgressLabel({ label: 'Connecting…' })).toBe('Connecting…');
expect(formatBootProgressLabel(null)).toBe('');
});
it('resolveBootProgressBar maps ratio and indeterminate', () => {
expect(resolveBootProgressBar({ ratio: 0.66 })).toEqual({
pct: 66,
indeterminate: false,
});
expect(resolveBootProgressBar({ step: 1, total: 4 })).toEqual({
pct: 25,
indeterminate: false,
});
expect(resolveBootProgressBar({
statusText: 'Connecting…',
indeterminate: true,
})).toEqual({ pct: 0, indeterminate: true });
});
it('softWaitRatio climbs then caps under 1', () => {
expect(softWaitRatio(0)).toBe(0);
expect(softWaitRatio(45_000, 90_000)).toBeCloseTo(0.5);
expect(softWaitRatio(200_000, 90_000)).toBe(0.92);
});
it('softStepRatio stays inside the step band and climbs', () => {
const early = softStepRatio(1, 3, 0, 30_000);
const mid = softStepRatio(1, 3, 15_000, 30_000);
const late = softStepRatio(1, 3, 60_000, 30_000);
expect(early).toBeCloseTo(0);
expect(mid).toBeGreaterThan(early);
expect(mid).toBeLessThan(1 / 3);
expect(late).toBeGreaterThan(mid);
expect(late).toBeLessThan(1 / 3);
});
it('withTimeout resolves fallback when promise is slow', async () => {
const slow = new Promise(() => {});
const result = await withTimeout(slow, 20, { ok: false });
expect(result).toEqual({ ok: false });
});
it('withTimeout returns promise value when fast', async () => {
const result = await withTimeout(Promise.resolve(42), 100, 0);
expect(result).toBe(42);
});
it('startProgressTicker fires while a hung await is in flight', async () => {
const ticks = [];
const stop = startProgressTicker(() => ticks.push(Date.now()), 40);
await new Promise((r) => setTimeout(r, 130));
stop();
expect(ticks.length).toBeGreaterThanOrEqual(3);
});
it('waitForBackendHealthy polls until ok and reports live progress during hung checks', async () => {
let n = 0;
const ticks = [];
const health = await waitForBackendHealthy({
attemptTimeoutMs: 80,
gapMs: 10,
tickMs: 30,
maxWaitMs: 3000,
checkHealth: async () => {
n += 1;
// First two attempts hang past abort window → ticker must still move.
if (n < 3) {
await new Promise(() => {});
}
return { ok: true, data: { model: 'x' } };
},
onProgress: (p) => ticks.push({ text: p.statusText, ratio: p.ratio }),
});
expect(health.ok).toBe(true);
expect(n).toBeGreaterThanOrEqual(3);
expect(ticks.length).toBeGreaterThanOrEqual(4);
expect(ticks[0].text).toMatch(/Waiting for backend/);
const ratios = ticks.map((t) => t.ratio);
expect(Math.max(...ratios)).toBeGreaterThan(ratios[0]);
});
it('withSoftStepProgress advances ratio while work runs', async () => {
const ticks = [];
const result = await withSoftStepProgress(
{
step: 2,
total: 4,
label: 'UMAP',
expectMs: 200,
tickMs: 30,
onProgress: (p) => ticks.push(p.ratio),
},
async () => {
await new Promise((r) => setTimeout(r, 100));
return 'done';
},
);
expect(result).toBe('done');
expect(ticks.length).toBeGreaterThanOrEqual(2);
expect(ticks[0]).toBeGreaterThanOrEqual(0.25);
expect(ticks[0]).toBeLessThan(0.5);
expect(Math.max(...ticks)).toBeGreaterThan(ticks[0]);
expect(Math.max(...ticks)).toBeLessThan(0.5);
});
});