Files
rea/src/server/environment/environmentCapacity.test.ts
T
2e6b38cef0 feat(environment): render more read tools from the state and describe responses (#5352)
* feat(environment): render more read tools from the state and describe responses

Keep main's environment serving and fold in coverage for non-entity reads,
response summaries, and MCP tool annotations.

Squashed rebase of #5352 onto main.

* fix(environment): keep rendered documents and quoted samples inside their budgets

A recorded body of exponential numbers can render past 256 KiB and turn a
working read into a state size error, so that document stays a recorded replay.
Description samples are measured after JSON quoting, and the replay-fallback
catalog check uses a tool count the longer response descriptions still separate.

Co-authored-by: morluto <morluto@users.noreply.github.com>

* test(environment): pin the replay card this branch serves

The replay-only card test pinned main's 11,131-byte card. This branch's descriptions name each tool's recorded responses, so the card it serves is 12,818 bytes; the pin and its name follow.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: morluto <morluto@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 07:08:44 +08:00

97 lines
3.0 KiB
TypeScript

import { expect, it } from "vitest";
import { environmentServeSource } from "../../../tests/fixtures/environmentServe.js";
import {
environmentCard,
environmentToolDefinitions,
ENVIRONMENT_CATALOG_LIMIT_BYTES,
} from "../../application/environment/environmentTools.js";
import { serveEnvironment } from "./serveEnvironment.js";
it("refuses a complete oversized discovery before trying to bind", async () => {
const source = environmentServeSource();
const tool = source.manifest.tools[0]!;
const manifest = {
...source.manifest,
tools: [
{
...tool,
query_parameter_names: Array.from(
{ length: 10000 },
(_, index) => `q_${index}`,
),
},
],
};
const result = await serveEnvironment({
source: { ...source, manifest },
host: "invalid.rea.invalid",
port: 0,
});
expect(result).toMatchObject({
ok: false,
error: {
_tag: "AnalysisResourceConstraintError",
resource: "capacity",
retryable: false,
reportedLimits: { max_catalog_bytes: ENVIRONMENT_CATALOG_LIMIT_BYTES },
},
});
expect(manifest.tools[0]?.query_parameter_names).toHaveLength(10000);
});
it("charges environment identity, aggregate tools and escaped text before producing a card", () => {
const source = environmentServeSource();
const tools = Array.from({ length: 1000 }, (_, index) => ({
...source.manifest.tools[0]!,
name: `tool_${index}`,
params: [],
query_parameter_names: [],
}));
expect(
environmentToolDefinitions({
...source,
manifest: { ...source.manifest, tools },
}).ok,
).toBe(false);
expect(
environmentToolDefinitions(
source,
"x".repeat(ENVIRONMENT_CATALOG_LIMIT_BYTES),
).ok,
).toBe(false);
const normal = environmentToolDefinitions(source);
expect(normal.ok).toBe(true);
if (!normal.ok) throw normal.error;
expect(
Buffer.byteLength(
JSON.stringify(
normal.value.map(({ tool: _, ...definition }) => definition),
),
),
).toBeLessThan(ENVIRONMENT_CATALOG_LIMIT_BYTES);
});
it("reserves the state card only when the state data plane is served", () => {
const source = environmentServeSource();
const manifest = { ...source.manifest, tools: [] };
const admits = (length: number, dataPlane: "none" | "state") =>
environmentToolDefinitions({ ...source, manifest }, "x".repeat(length), {
dataPlane,
}).ok;
// With no tools, the name and the card are the whole reservation: six bytes per name unit plus quotes.
const longest = (dataPlane: "none" | "state") =>
Math.floor(
(ENVIRONMENT_CATALOG_LIMIT_BYTES -
2 -
JSON.stringify(environmentCard("", [], dataPlane)).length) /
6,
);
const stateless = longest("none");
expect(admits(stateless, "none")).toBe(true);
expect(admits(stateless + 1, "none")).toBe(false);
expect(longest("state")).toBeLessThan(stateless);
expect(admits(stateless, "state")).toBe(false);
expect(admits(longest("state"), "state")).toBe(true);
expect(admits(longest("state") + 1, "state")).toBe(false);
});