Skip to navigation

Test a TypeScript Adapter

Test a TypeScript adapter locally before connecting it to Band. This guide uses @band-ai/sdk 1.0.0.

Set Up the Test Project

Use Node.js 22.12 or later on the 22.x line, 24.x, or 26 and later, matching Vitest 5’s supported runtimes. From a new project directory:

npm init -y && npm pkg set type=module
npm install @band-ai/sdk@1.0.0
npm install -D vitest@5.0.5 typescript@7.0.2

Record Tool Calls

@band-ai/sdk/testing exports FakeAgentTools for @band-ai/sdk 1.0.0. It has no assertion helpers, so use your test runner’s matchers on what it records:

PropertyRecords
messagesSent{ content, mentions } for each sendMessage() and sendNotice() call
eventsSent{ content, messageType, metadata } for each sendEvent() call, including the error event a reported failure posts
toolCalls{ toolName, arguments } for each executeToolCall() call, which returns { status: "ok" } without running the tool
participantsAdded, participantsRemovedParticipant changes
turnThe record the SDK uses to judge whether the turn replied

Unlike Band, sendMessage() accepts a call without mentions, so assert the mentions too. getToolSchemas() returns an empty list, and the contact and memory methods return empty results. new FakeAgentTools({ failOn: ["sendMessage"] }) makes the named methods throw, and reset() clears every record.

Call onEvent() rather than onMessage() to include the SDK’s turn judgment. This Vitest test checks both a reply and a turn that ends without one:

tests/adapter.test.ts
import { expect, it } from "vitest";
import { GenericAdapter, type PlatformMessage } from "@band-ai/sdk";
import { HistoryProvider } from "@band-ai/sdk/runtime";
import { FakeAgentTools } from "@band-ai/sdk/testing";
const message: PlatformMessage = {
id: "msg-1",
roomId: "room-1",
content: "What is the weather in Lisbon?",
senderId: "user-1",
senderType: "User",
senderName: "Alice",
messageType: "text",
metadata: {},
createdAt: new Date(),
};
function input(tools: FakeAgentTools) {
return {
message,
tools,
history: new HistoryProvider([]),
participantsMessage: null,
contactsMessage: null,
isSessionBootstrap: true,
roomId: "room-1",
};
}
it("replies to the sender", async () => {
const adapter = new GenericAdapter(async ({ message, tools }) => {
await tools.sendMessage("Sunny, 22 C", [{ id: message.senderId }]);
});
const tools = new FakeAgentTools();
await adapter.onEvent(input(tools));
expect(tools.messagesSent).toEqual([{ content: "Sunny, 22 C", mentions: [{ id: "user-1" }] }]);
});
it("fails a turn that ends without a reply", async () => {
const adapter = new GenericAdapter(async () => {});
const tools = new FakeAgentTools();
await expect(adapter.onEvent(input(tools))).rejects.toThrow("ended its turn without replying");
expect(tools.eventsSent[0]?.messageType).toBe("error");
});

Import HistoryProvider from @band-ai/sdk/runtime: the root entry exports it as a type only. For an integration test, start a dedicated test agent with agent.withLifecycle(async () => { ... }), check agent.state.status is "running" inside it, and "stopped" after it.

TypeScript reference.

REST Test Helper

@band-ai/sdk/testing also exports StubRestApi, a REST client that returns fixed responses.