31122d2409
Deferred the status-line quota refresh off the render path and raced it against a short startup timeout so slow Anthropic usage lookups cannot pin interactive startup. Added regression coverage for non-synchronous refresh startup and timeout backoff.\n\nFixes #3057
88 lines
2.2 KiB
TypeScript
88 lines
2.2 KiB
TypeScript
import { afterEach, beforeEach, describe, expect, it, vi } from "bun:test";
|
|
import { resetSettingsForTest, Settings } from "@oh-my-pi/pi-coding-agent/config/settings";
|
|
import { StatusLineComponent } from "@oh-my-pi/pi-coding-agent/modes/components/status-line";
|
|
import type { AgentSession } from "@oh-my-pi/pi-coding-agent/session/agent-session";
|
|
|
|
async function flushMicrotasks(): Promise<void> {
|
|
await Promise.resolve();
|
|
await Promise.resolve();
|
|
}
|
|
|
|
function makeSession(fetchUsageReports: (signal?: AbortSignal) => Promise<unknown>): AgentSession {
|
|
return { fetchUsageReports } as unknown as AgentSession;
|
|
}
|
|
|
|
describe("StatusLineComponent usage refresh", () => {
|
|
beforeEach(async () => {
|
|
resetSettingsForTest();
|
|
await Settings.init({ inMemory: true });
|
|
vi.useFakeTimers();
|
|
});
|
|
|
|
afterEach(() => {
|
|
vi.useRealTimers();
|
|
resetSettingsForTest();
|
|
});
|
|
|
|
it("does not invoke usage fetching synchronously on the render path", async () => {
|
|
let calls = 0;
|
|
const component = new StatusLineComponent(
|
|
makeSession(async () => {
|
|
calls++;
|
|
return [];
|
|
}),
|
|
);
|
|
|
|
component.refreshUsageInBackground();
|
|
expect(calls).toBe(0);
|
|
|
|
vi.advanceTimersByTime(0);
|
|
await flushMicrotasks();
|
|
|
|
expect(calls).toBe(1);
|
|
});
|
|
|
|
it("passes a startup timeout signal to the background usage fetch", async () => {
|
|
let signal: AbortSignal | undefined;
|
|
const component = new StatusLineComponent(
|
|
makeSession(async nextSignal => {
|
|
signal = nextSignal;
|
|
return [];
|
|
}),
|
|
);
|
|
|
|
component.refreshUsageInBackground();
|
|
vi.advanceTimersByTime(0);
|
|
await flushMicrotasks();
|
|
|
|
expect(signal).toBeInstanceOf(AbortSignal);
|
|
});
|
|
|
|
it("backs off after the startup timeout when usage fetching hangs", async () => {
|
|
let calls = 0;
|
|
const component = new StatusLineComponent(
|
|
makeSession(() => {
|
|
calls++;
|
|
return Promise.withResolvers<unknown>().promise;
|
|
}),
|
|
);
|
|
|
|
component.refreshUsageInBackground();
|
|
vi.advanceTimersByTime(0);
|
|
await flushMicrotasks();
|
|
expect(calls).toBe(1);
|
|
|
|
component.refreshUsageInBackground();
|
|
expect(calls).toBe(1);
|
|
|
|
vi.advanceTimersByTime(2_000);
|
|
await flushMicrotasks();
|
|
|
|
component.refreshUsageInBackground();
|
|
vi.advanceTimersByTime(0);
|
|
await flushMicrotasks();
|
|
|
|
expect(calls).toBe(1);
|
|
});
|
|
});
|