Files
EasyCLIProxyAPI/tests/refreshScheduler.test.ts
T
lzt404 9cf3878b28 perf: reduce background overhead and improve UI responsiveness
- Move blocking usage queries and core lifecycle operations to background
  workers, limit concurrency, and prevent overlapping core operations
- Coalesce statistics refreshes and consolidate category aggregations,
  reducing unfiltered analysis queries from 11 to 4
- Cache bundled model prices, reuse management API connections, and reduce
  redundant checks in the usage collector
- Throttle download progress and guide positioning updates, and avoid
  redundant renders when core status is unchanged
- Bound and coalesce configuration watcher events, and reuse a single
  model catalog refresh worker
- Stop scheduling remaining health checks when the dialog closes and
  limit backend health-check concurrency
- Replace overlapping OAuth polling with sequential requests, retry
  backoff, timeouts, and stale callback protection
- Add regression tests and a statistics benchmark script, and move
  performance verification documentation to agent_docs
2026-09-06 01:28:58 +08:00

69 lines
2.6 KiB
TypeScript

import { describe, expect, it } from 'bun:test';
import { createRefreshScheduler } from '../src/services/refreshScheduler';
const deferred = () => {
let resolve!: () => void;
const promise = new Promise<void>((done) => { resolve = done; });
return { promise, resolve };
};
describe('refresh scheduler', () => {
it('runs one request at a time and retains only the latest trailing request', async () => {
const scheduler = createRefreshScheduler(0);
const firstGate = deferred();
const calls: number[] = [];
const first = scheduler.schedule(async () => {
calls.push(1);
await firstGate.promise;
});
const replaced = scheduler.schedule(async () => { calls.push(2); });
let latest = replaced;
for (let index = 3; index <= 1000; index += 1) {
latest = scheduler.schedule(async () => { calls.push(index); });
expect(latest).toBe(replaced);
}
expect(calls).toEqual([1]);
firstGate.resolve();
await Promise.all([first, replaced, latest]);
expect(calls).toEqual([1, 1000]);
});
it('waits between background refreshes but lets an explicit refresh bypass the delay', async () => {
const scheduler = createRefreshScheduler(30);
await scheduler.schedule(async () => {});
const calls: string[] = [];
const queued = scheduler.schedule(async () => { calls.push('background'); });
expect(calls).toEqual([]);
await scheduler.schedule(async () => { calls.push('manual'); }, true);
await queued;
expect(calls).toEqual(['manual']);
const started = performance.now();
await scheduler.schedule(async () => { calls.push('delayed'); });
expect(performance.now() - started).toBeGreaterThanOrEqual(25);
});
it('cancels queued work during navigation and accepts a fresh request', async () => {
const scheduler = createRefreshScheduler(0);
const gate = deferred();
const active = scheduler.schedule(() => gate.promise);
let obsoleteRan = false;
const obsolete = scheduler.schedule(async () => { obsoleteRan = true; });
scheduler.cancelPending();
await obsolete;
let freshRan = false;
const fresh = scheduler.schedule(async () => { freshRan = true; }, true);
gate.resolve();
await Promise.all([active, fresh]);
expect(obsoleteRan).toBe(false);
expect(freshRan).toBe(true);
});
it('continues after a failed refresh', async () => {
const scheduler = createRefreshScheduler(0);
await expect(scheduler.schedule(async () => { throw new Error('query failed'); })).rejects.toThrow('query failed');
let ran = false;
await scheduler.schedule(async () => { ran = true; });
expect(ran).toBe(true);
});
});