Skip to content

Commit e2e9828

Browse files
committed
Add sidecar-bundle loader test
Instantiate the gmail bundle with a real BaseEnv, as the Interchange tools-mail bundle test does, and check that its static declaration matches the emitted tool names and gates exactly the mailbox mutations. The approval check moves here from create-tools.test.ts.
1 parent 81709be commit e2e9828

4 files changed

Lines changed: 198 additions & 16 deletions

File tree

‎bun.lock‎

Lines changed: 125 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎package.json‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@
6969
},
7070
"devDependencies": {
7171
"@intx/agent": "0.4.0",
72+
"@intx/storage-isogit": "0.4.0",
7273
"@intx/types": "0.4.0",
7374
"@types/bun": "1.3.14",
7475
"@types/node": "22.10.5",

‎src/sidecar-bundle.test.ts‎

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
// The deploy-time capability walk reads `gmail.definitions` without
2+
// invoking the factory, so the static declaration must match what the
3+
// instantiated bundle emits.
4+
5+
import { afterAll, beforeAll, describe, expect, test } from "bun:test";
6+
import { mkdtemp, rm } from "node:fs/promises";
7+
import { tmpdir } from "node:os";
8+
import { join } from "node:path";
9+
10+
import { createDefaultDirectorRegistry } from "@intx/agent";
11+
import { noopAuditStore, permissiveAuthorize } from "@intx/agent/testing";
12+
import { createIsogitStore } from "@intx/storage-isogit/node";
13+
import type { InferenceSource } from "@intx/types/runtime";
14+
import { createRuntimeCapabilities } from "@intx/types/runtime-capabilities";
15+
16+
import { gmail, type GmailToolEnv } from "./sidecar-bundle.js";
17+
18+
const SOURCE: InferenceSource = {
19+
id: "anthropic:mock-model",
20+
provider: "anthropic",
21+
baseURL: "https://api.anthropic.com",
22+
credentialId: "test-credential",
23+
model: "mock-model",
24+
};
25+
26+
const GATED_TOOL_NAMES = new Set([
27+
"gmail_create_draft",
28+
"gmail_label_message",
29+
"gmail_unlabel_message",
30+
"gmail_label_thread",
31+
"gmail_unlabel_thread",
32+
]);
33+
34+
let tmpDir: string;
35+
let env: GmailToolEnv;
36+
37+
beforeAll(async () => {
38+
tmpDir = await mkdtemp(join(tmpdir(), "google-tools-sidecar-bundle-test-"));
39+
env = {
40+
sources: [SOURCE],
41+
defaultSource: SOURCE.id,
42+
storage: await createIsogitStore(tmpDir),
43+
workdir: tmpDir,
44+
audit: noopAuditStore(),
45+
authorize: permissiveAuthorize(),
46+
directors: createDefaultDirectorRegistry(),
47+
capabilities: createRuntimeCapabilities({}),
48+
};
49+
});
50+
51+
afterAll(async () => {
52+
await rm(tmpDir, { recursive: true, force: true });
53+
});
54+
55+
describe("gmail sidecar-bundle static declaration", () => {
56+
test("declared definition names match the instantiated bundle's names", async () => {
57+
const bundle = gmail(env);
58+
const declared = new Set(gmail.definitions.map((definition) => definition.name));
59+
const emitted = new Set(bundle.definitions.map((definition) => definition.name));
60+
expect(emitted).toEqual(declared);
61+
await bundle.dispose?.();
62+
});
63+
64+
test("gates exactly the mailbox mutations behind approval", () => {
65+
expect(gmail.definitions.length).toBeGreaterThan(0);
66+
for (const definition of gmail.definitions) {
67+
expect(definition.approval).toBe(
68+
GATED_TOOL_NAMES.has(definition.name) ? "ask" : undefined,
69+
);
70+
}
71+
});
72+
});

‎src/tools/create-tools.test.ts‎

Lines changed: 0 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ import type { GmailFetch } from "../client/index.js";
55
import { GMAIL_CREDENTIAL_HANDLE, TOOL_DEFINITIONS } from "./definitions.js";
66
import { createGmailTools } from "./create-tools.js";
77
import { createRawDraft } from "./drafts.js";
8-
import { gmail } from "../sidecar-bundle.js";
98

109
function testCapabilities(fetchImpl: GmailFetch, handle = GMAIL_CREDENTIAL_HANDLE) {
1110
return createRuntimeCapabilities({
@@ -92,21 +91,6 @@ describe("Gmail tool definitions", () => {
9291
},
9392
});
9493
});
95-
96-
test("requires approval for every mailbox mutation", () => {
97-
const approvals = new Map(
98-
gmail.definitions.map((definition) => [definition.name, definition.approval]),
99-
);
100-
for (const name of [
101-
"gmail_create_draft",
102-
"gmail_label_message",
103-
"gmail_unlabel_message",
104-
"gmail_label_thread",
105-
"gmail_unlabel_thread",
106-
]) {
107-
expect(approvals.get(name)).toBe("ask");
108-
}
109-
});
11094
});
11195

11296
describe("createGmailTools", () => {

0 commit comments

Comments
 (0)