test(core): cover CappedTextBuffer and ToolCallIdAllocator (#9)

This commit is contained in:
Zhang Jason
2026-07-22 22:06:58 +08:00
committed by GitHub
parent 1425b2fb5f
commit 7e0525af9f
2 changed files with 133 additions and 0 deletions
+62
View File
@@ -0,0 +1,62 @@
/**
* CappedTextBuffer unit tests: capacity drop keeps the tail, the omitted tally accumulates,
* drain() prefixes the drop marker and clears the buffer, and isEmpty tracks both text and tally.
*/
import { describe, expect, it } from "vitest";
import { CappedTextBuffer } from "../src/environment/tools/background/index.js";
describe("CappedTextBuffer", () => {
it("starts empty and drains to an empty string", () => {
const buf = new CappedTextBuffer(10, "earlier output");
expect(buf.isEmpty).toBe(true);
expect(buf.drain()).toBe("");
});
it("keeps content under capacity verbatim", () => {
const buf = new CappedTextBuffer(10, "earlier output");
buf.append("hello");
expect(buf.isEmpty).toBe(false);
expect(buf.drain()).toBe("hello");
expect(buf.isEmpty).toBe(true);
});
it("drops the oldest chars past capacity and keeps the tail", () => {
const buf = new CappedTextBuffer(5, "earlier output");
buf.append("abcdefg"); // 7 chars, cap 5 -> drop "ab", keep "cdefg"
expect(buf.drain()).toBe("[... 2 chars of earlier output dropped ...]\ncdefg");
});
it("handles a single append larger than the cap", () => {
const buf = new CappedTextBuffer(3, "earlier output");
buf.append("0123456789"); // drop 7, keep "789"
expect(buf.drain()).toBe("[... 7 chars of earlier output dropped ...]\n789");
});
it("accumulates the omitted tally across multiple over-cap appends", () => {
const buf = new CappedTextBuffer(4, "earlier output");
buf.append("aaaaaa"); // drop 2, keep "aaaa"
buf.append("bb"); // now "aaaabb" -> drop 2 more, keep "aabb"
expect(buf.drain()).toBe("[... 4 chars of earlier output dropped ...]\naabb");
});
it("clears text and tally after drain", () => {
const buf = new CappedTextBuffer(2, "earlier output");
buf.append("xyz"); // drop 1, keep "yz"
buf.drain();
expect(buf.isEmpty).toBe(true);
expect(buf.drain()).toBe("");
});
it("uses the drop label in the marker", () => {
const buf = new CappedTextBuffer(1, "earlier subagent output");
buf.append("ab"); // drop 1
expect(buf.drain()).toBe("[... 1 chars of earlier subagent output dropped ...]\nb");
});
it("stays non-empty when only dropped chars remain unread", () => {
const buf = new CappedTextBuffer(0, "earlier output");
buf.append("ab"); // cap 0 -> everything dropped, text empty but tally=2
expect(buf.isEmpty).toBe(false);
expect(buf.drain()).toBe("[... 2 chars of earlier output dropped ...]\n");
});
});
+71
View File
@@ -0,0 +1,71 @@
/**
* ToolCallIdAllocator / stripToolCallIdSuffix unit tests: unique ids pass through, collisions
* get a `#n` suffix (n from 2, first free slot), markUsed seeds the taken set, and the suffix
* strip is shape-based and idempotent.
*/
import { describe, expect, it } from "vitest";
import { ToolCallIdAllocator, stripToolCallIdSuffix } from "../src/llm/tool-call-ids.js";
describe("ToolCallIdAllocator", () => {
it("passes a unique id through unchanged", () => {
const a = new ToolCallIdAllocator();
expect(a.allocate("call_abc")).toBe("call_abc");
});
it("disambiguates repeats with #n starting at 2", () => {
const a = new ToolCallIdAllocator();
expect(a.allocate("get_weather")).toBe("get_weather");
expect(a.allocate("get_weather")).toBe("get_weather#2");
expect(a.allocate("get_weather")).toBe("get_weather#3");
});
it("keeps distinct base ids independent", () => {
const a = new ToolCallIdAllocator();
expect(a.allocate("f")).toBe("f");
expect(a.allocate("g")).toBe("g");
expect(a.allocate("f")).toBe("f#2");
});
it("skips ids already reserved by markUsed", () => {
const a = new ToolCallIdAllocator();
a.markUsed("dupe");
a.markUsed("dupe#2");
expect(a.allocate("dupe")).toBe("dupe#3");
});
it("markUsed twice is harmless", () => {
const a = new ToolCallIdAllocator();
a.markUsed("x");
a.markUsed("x");
expect(a.allocate("x")).toBe("x#2");
});
});
describe("stripToolCallIdSuffix", () => {
it("strips a #n suffix", () => {
expect(stripToolCallIdSuffix("get_weather#2")).toBe("get_weather");
});
it("returns an unsuffixed id as-is", () => {
expect(stripToolCallIdSuffix("call_abc")).toBe("call_abc");
expect(stripToolCallIdSuffix("toolu_123")).toBe("toolu_123");
});
it("is idempotent", () => {
const once = stripToolCallIdSuffix("f#3");
expect(stripToolCallIdSuffix(once)).toBe("f");
});
it("only strips a trailing #<digits>, not other hashes", () => {
expect(stripToolCallIdSuffix("a#b")).toBe("a#b");
expect(stripToolCallIdSuffix("a#2b")).toBe("a#2b");
});
it("round-trips an allocated collision id back to the provider id", () => {
const a = new ToolCallIdAllocator();
a.allocate("name");
const dup = a.allocate("name");
expect(dup).toBe("name#2");
expect(stripToolCallIdSuffix(dup)).toBe("name");
});
});