fix: remove nested .git folders, re-add as normal directories
This commit is contained in:
@@ -0,0 +1,184 @@
|
||||
import { describe, test, expect, mock } from "bun:test";
|
||||
import { attachCwConcurrencyLimiter } from "../../src/modules/cw-utils/cwConcurrencyLimiter";
|
||||
|
||||
/**
|
||||
* Build a minimal fake Axios instance with interceptor registration.
|
||||
* Collect registered interceptors so we can invoke them in tests.
|
||||
*/
|
||||
function createMockAxios() {
|
||||
const requestHandlers: Array<(config: any) => any> = [];
|
||||
const responseSuccessHandlers: Array<(res: any) => any> = [];
|
||||
const responseErrorHandlers: Array<(err: any) => any> = [];
|
||||
|
||||
return {
|
||||
interceptors: {
|
||||
request: {
|
||||
use(fn: (config: any) => any) {
|
||||
requestHandlers.push(fn);
|
||||
},
|
||||
},
|
||||
response: {
|
||||
use(onSuccess: (res: any) => any, onError: (err: any) => any) {
|
||||
responseSuccessHandlers.push(onSuccess);
|
||||
responseErrorHandlers.push(onError);
|
||||
},
|
||||
},
|
||||
},
|
||||
_requestHandlers: requestHandlers,
|
||||
_responseSuccessHandlers: responseSuccessHandlers,
|
||||
_responseErrorHandlers: responseErrorHandlers,
|
||||
};
|
||||
}
|
||||
|
||||
describe("attachCwConcurrencyLimiter", () => {
|
||||
test("attaches request and response interceptors", () => {
|
||||
const api = createMockAxios();
|
||||
attachCwConcurrencyLimiter(api as any);
|
||||
expect(api._requestHandlers).toHaveLength(1);
|
||||
expect(api._responseSuccessHandlers).toHaveLength(1);
|
||||
expect(api._responseErrorHandlers).toHaveLength(1);
|
||||
});
|
||||
|
||||
test("request interceptor resolves immediately when under limit", async () => {
|
||||
const api = createMockAxios();
|
||||
attachCwConcurrencyLimiter(api as any, 2);
|
||||
const config = { url: "/test" };
|
||||
const result = await api._requestHandlers[0](config);
|
||||
expect(result).toEqual(config);
|
||||
});
|
||||
|
||||
test("response success interceptor passes through response", async () => {
|
||||
const api = createMockAxios();
|
||||
attachCwConcurrencyLimiter(api as any, 2);
|
||||
// Acquire a slot first
|
||||
await api._requestHandlers[0]({});
|
||||
const response = { data: "ok", status: 200 };
|
||||
const result = api._responseSuccessHandlers[0](response);
|
||||
expect(result).toEqual(response);
|
||||
});
|
||||
|
||||
test("response error interceptor rejects with the error and releases slot", async () => {
|
||||
const api = createMockAxios();
|
||||
attachCwConcurrencyLimiter(api as any, 2);
|
||||
// Acquire a slot
|
||||
await api._requestHandlers[0]({});
|
||||
const error = new Error("fail");
|
||||
try {
|
||||
await api._responseErrorHandlers[0](error);
|
||||
expect(true).toBe(false); // should not reach
|
||||
} catch (e) {
|
||||
expect(e).toBe(error);
|
||||
}
|
||||
});
|
||||
|
||||
test("queues requests when at max concurrency", async () => {
|
||||
const api = createMockAxios();
|
||||
attachCwConcurrencyLimiter(api as any, 1);
|
||||
|
||||
// First request acquires the single slot
|
||||
await api._requestHandlers[0]({ id: 1 });
|
||||
|
||||
// Second request should be queued (not resolved yet)
|
||||
let secondResolved = false;
|
||||
const secondPromise = api._requestHandlers[0]({ id: 2 }).then(
|
||||
(config: any) => {
|
||||
secondResolved = true;
|
||||
return config;
|
||||
},
|
||||
);
|
||||
|
||||
// Give the event loop a tick — second should still be pending
|
||||
await new Promise((r) => setTimeout(r, 10));
|
||||
expect(secondResolved).toBe(false);
|
||||
|
||||
// Release the first slot via response handler
|
||||
api._responseSuccessHandlers[0]({ status: 200 });
|
||||
|
||||
// Now the second should resolve
|
||||
const result = await secondPromise;
|
||||
expect(secondResolved).toBe(true);
|
||||
expect(result).toEqual({ id: 2 });
|
||||
});
|
||||
|
||||
test("multiple requests under limit all proceed immediately", async () => {
|
||||
const api = createMockAxios();
|
||||
attachCwConcurrencyLimiter(api as any, 3);
|
||||
|
||||
const results = await Promise.all([
|
||||
api._requestHandlers[0]({ id: 1 }),
|
||||
api._requestHandlers[0]({ id: 2 }),
|
||||
api._requestHandlers[0]({ id: 3 }),
|
||||
]);
|
||||
|
||||
expect(results).toEqual([{ id: 1 }, { id: 2 }, { id: 3 }]);
|
||||
});
|
||||
|
||||
test("FIFO ordering: queued requests resolve in order", async () => {
|
||||
const api = createMockAxios();
|
||||
attachCwConcurrencyLimiter(api as any, 1);
|
||||
|
||||
// Fill the single slot
|
||||
await api._requestHandlers[0]({ id: 1 });
|
||||
|
||||
const order: number[] = [];
|
||||
|
||||
const p2 = api._requestHandlers[0]({ id: 2 }).then(() => order.push(2));
|
||||
const p3 = api._requestHandlers[0]({ id: 3 }).then(() => order.push(3));
|
||||
|
||||
// Release slot → should wake request 2
|
||||
api._responseSuccessHandlers[0]({});
|
||||
await p2;
|
||||
|
||||
// Release again → should wake request 3
|
||||
api._responseSuccessHandlers[0]({});
|
||||
await p3;
|
||||
|
||||
expect(order).toEqual([2, 3]);
|
||||
});
|
||||
|
||||
test("error release also unblocks queued requests", async () => {
|
||||
const api = createMockAxios();
|
||||
attachCwConcurrencyLimiter(api as any, 1);
|
||||
|
||||
await api._requestHandlers[0]({ id: 1 });
|
||||
|
||||
let secondResolved = false;
|
||||
const secondPromise = api._requestHandlers[0]({ id: 2 }).then(() => {
|
||||
secondResolved = true;
|
||||
});
|
||||
|
||||
// Release via error path
|
||||
try {
|
||||
await api._responseErrorHandlers[0](new Error("fail"));
|
||||
} catch {}
|
||||
|
||||
await secondPromise;
|
||||
expect(secondResolved).toBe(true);
|
||||
});
|
||||
|
||||
test("defaults to max 6 concurrency", async () => {
|
||||
const api = createMockAxios();
|
||||
attachCwConcurrencyLimiter(api as any); // default max = 6
|
||||
|
||||
// 6 requests should all proceed immediately
|
||||
const promises = [];
|
||||
for (let i = 0; i < 6; i++) {
|
||||
promises.push(api._requestHandlers[0]({ id: i }));
|
||||
}
|
||||
const results = await Promise.all(promises);
|
||||
expect(results).toHaveLength(6);
|
||||
|
||||
// 7th should queue
|
||||
let seventhResolved = false;
|
||||
const seventh = api._requestHandlers[0]({ id: 7 }).then(() => {
|
||||
seventhResolved = true;
|
||||
});
|
||||
await new Promise((r) => setTimeout(r, 10));
|
||||
expect(seventhResolved).toBe(false);
|
||||
|
||||
// Release one to unblock
|
||||
api._responseSuccessHandlers[0]({});
|
||||
await seventh;
|
||||
expect(seventhResolved).toBe(true);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user