Files
TanTan fa9c73363d
Dev Build / cleanup (push) Waiting to run
Dev Build / typecheck (push) Waiting to run
Publish MCP to npm / Build, test, and publish MCP (push) Waiting to run
Publish SDK to npm / Build, test, and publish SDK (push) Waiting to run
Sync action catalog to docs / sync (push) Waiting to run
Publish UI to npm / Build and publish UI (push) Waiting to run
Build and Release / preflight (push) Waiting to run
SnowLuma v2.4.2 source snapshot
2026-11-13 09:02:00 +08:00

83 lines
3.3 KiB
TypeScript

import { afterEach, describe, expect, it, vi } from 'vitest';
import { createRequestAbortState, raceWithAbort } from '../src/internal/abort';
afterEach(() => vi.useRealTimers());
describe('request cancellation', () => {
it('honors an existing cancellation before a completed operation', async () => {
const controller = new AbortController();
controller.abort('already canceled');
await expect(
raceWithAbort(Promise.resolve('done'), controller.signal),
).rejects.toMatchObject({
cause: 'already canceled',
});
});
it('releases its listener when cancellation wins against a pending operation', async () => {
const operation = Promise.withResolvers<void>();
const controller = new AbortController();
const remove = vi.spyOn(controller.signal, 'removeEventListener');
const result = raceWithAbort(operation.promise, controller.signal);
controller.abort('canceled');
await expect(result).rejects.toMatchObject({ cause: 'canceled' });
expect(remove).toHaveBeenCalledWith('abort', expect.any(Function));
operation.resolve();
});
it.each(['resolve', 'reject'] as const)(
'releases its listener when the operation will %s',
async (outcome) => {
const operation = Promise.withResolvers<string>();
const controller = new AbortController();
const remove = vi.spyOn(controller.signal, 'removeEventListener');
const result = raceWithAbort(operation.promise, controller.signal);
if (outcome === 'resolve') {
operation.resolve('done');
await expect(result).resolves.toBe('done');
} else {
operation.reject(new Error('failed'));
await expect(result).rejects.toThrow('failed');
}
expect(remove).toHaveBeenCalledWith('abort', expect.any(Function));
},
);
it('keeps caller cancellation as the cause when the timeout expires later', () => {
vi.useFakeTimers();
const caller = new AbortController();
const request = createRequestAbortState(100, caller.signal);
caller.abort('caller canceled');
vi.advanceTimersByTime(100);
expect(request.signal.reason).toBe('caller canceled');
expect(request.isExternallyAborted()).toBe(true);
expect(request.isTimedOut()).toBe(false);
request.cleanup();
});
it('keeps the timeout as the cause when the caller cancels later', () => {
vi.useFakeTimers();
const caller = new AbortController();
const request = createRequestAbortState(100, caller.signal);
vi.advanceTimersByTime(100);
caller.abort();
expect(request.isTimedOut()).toBe(true);
expect(request.isExternallyAborted()).toBe(false);
request.cleanup();
});
it('stops observing timeouts and caller cancellation after cleanup', () => {
vi.useFakeTimers();
const caller = new AbortController();
const request = createRequestAbortState(100, caller.signal);
request.cleanup();
caller.abort();
vi.advanceTimersByTime(100);
expect(request.signal.aborted).toBe(false);
expect(request.isTimedOut()).toBe(false);
expect(request.isExternallyAborted()).toBe(false);
});
});