mirror of
https://github.com/claude-code-best/claude-code.git
synced 2026-06-19 23:05:51 +00:00
test: Phase 2-4 — 添加 12 个测试文件 (+321 tests, 968 total)
Phase 2 (轻 Mock): envUtils, sleep/sequential, memoize, groupToolUses, dangerousPatterns, outputLimits Phase 3 (补全): zodToJsonSchema, PermissionMode, envValidation Phase 4 (工具模块): mcpStringUtils, destructiveCommandWarning, commandSemantics Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
130
src/utils/__tests__/sleep.test.ts
Normal file
130
src/utils/__tests__/sleep.test.ts
Normal file
@@ -0,0 +1,130 @@
|
||||
import { describe, expect, test } from "bun:test";
|
||||
import { sleep, withTimeout } from "../sleep";
|
||||
import { sequential } from "../sequential";
|
||||
|
||||
// ─── sleep ─────────────────────────────────────────────────────────────
|
||||
|
||||
describe("sleep", () => {
|
||||
test("resolves after timeout", async () => {
|
||||
const start = Date.now();
|
||||
await sleep(50);
|
||||
expect(Date.now() - start).toBeGreaterThanOrEqual(40);
|
||||
});
|
||||
|
||||
test("resolves immediately when signal already aborted", async () => {
|
||||
const ac = new AbortController();
|
||||
ac.abort();
|
||||
const start = Date.now();
|
||||
await sleep(10_000, ac.signal);
|
||||
expect(Date.now() - start).toBeLessThan(50);
|
||||
});
|
||||
|
||||
test("resolves early on abort (default: no throw)", async () => {
|
||||
const ac = new AbortController();
|
||||
const start = Date.now();
|
||||
const p = sleep(10_000, ac.signal);
|
||||
setTimeout(() => ac.abort(), 30);
|
||||
await p;
|
||||
expect(Date.now() - start).toBeLessThan(200);
|
||||
});
|
||||
|
||||
test("rejects on abort with throwOnAbort", async () => {
|
||||
const ac = new AbortController();
|
||||
ac.abort();
|
||||
await expect(
|
||||
sleep(10_000, ac.signal, { throwOnAbort: true })
|
||||
).rejects.toThrow("aborted");
|
||||
});
|
||||
|
||||
test("rejects with custom abortError", async () => {
|
||||
const ac = new AbortController();
|
||||
ac.abort();
|
||||
const customErr = () => new Error("custom abort");
|
||||
await expect(
|
||||
sleep(10_000, ac.signal, { abortError: customErr })
|
||||
).rejects.toThrow("custom abort");
|
||||
});
|
||||
|
||||
test("throwOnAbort rejects on mid-sleep abort", async () => {
|
||||
const ac = new AbortController();
|
||||
const p = sleep(10_000, ac.signal, { throwOnAbort: true });
|
||||
setTimeout(() => ac.abort(), 20);
|
||||
await expect(p).rejects.toThrow("aborted");
|
||||
});
|
||||
|
||||
test("works without signal", async () => {
|
||||
await sleep(10);
|
||||
// just verify it resolves
|
||||
});
|
||||
});
|
||||
|
||||
// ─── withTimeout ───────────────────────────────────────────────────────
|
||||
|
||||
describe("withTimeout", () => {
|
||||
test("resolves when promise completes before timeout", async () => {
|
||||
const result = await withTimeout(
|
||||
Promise.resolve(42),
|
||||
1000,
|
||||
"timed out"
|
||||
);
|
||||
expect(result).toBe(42);
|
||||
});
|
||||
|
||||
test("rejects when promise takes too long", async () => {
|
||||
const slow = new Promise((resolve) => setTimeout(resolve, 5000));
|
||||
await expect(
|
||||
withTimeout(slow, 50, "operation timed out")
|
||||
).rejects.toThrow("operation timed out");
|
||||
});
|
||||
|
||||
test("rejects propagate through", async () => {
|
||||
await expect(
|
||||
withTimeout(Promise.reject(new Error("inner")), 1000, "timeout")
|
||||
).rejects.toThrow("inner");
|
||||
});
|
||||
});
|
||||
|
||||
// ─── sequential ────────────────────────────────────────────────────────
|
||||
|
||||
describe("sequential", () => {
|
||||
test("executes calls in order", async () => {
|
||||
const order: number[] = [];
|
||||
const fn = sequential(async (n: number) => {
|
||||
await sleep(10);
|
||||
order.push(n);
|
||||
return n;
|
||||
});
|
||||
|
||||
const results = await Promise.all([fn(1), fn(2), fn(3)]);
|
||||
expect(order).toEqual([1, 2, 3]);
|
||||
expect(results).toEqual([1, 2, 3]);
|
||||
});
|
||||
|
||||
test("returns correct result for each call", async () => {
|
||||
const fn = sequential(async (x: number) => x * 2);
|
||||
const r1 = await fn(5);
|
||||
const r2 = await fn(10);
|
||||
expect(r1).toBe(10);
|
||||
expect(r2).toBe(20);
|
||||
});
|
||||
|
||||
test("propagates errors without blocking queue", async () => {
|
||||
const fn = sequential(async (x: number) => {
|
||||
if (x === 2) throw new Error("fail");
|
||||
return x;
|
||||
});
|
||||
|
||||
const p1 = fn(1);
|
||||
const p2 = fn(2);
|
||||
const p3 = fn(3);
|
||||
|
||||
expect(await p1).toBe(1);
|
||||
await expect(p2).rejects.toThrow("fail");
|
||||
expect(await p3).toBe(3);
|
||||
});
|
||||
|
||||
test("handles single call", async () => {
|
||||
const fn = sequential(async (s: string) => s.toUpperCase());
|
||||
expect(await fn("hello")).toBe("HELLO");
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user