Files
GMW/services/backend/tests/chatbot-tools.test.ts
T
asepharyanaandClaude Opus 5 (Nous Research) b67856462f feat(chatbot): expand tool set to cover all server-watcher situations
The chatbot agent now has 14 tools (was 4) so it can answer about ANY
server situation from live data instead of a static snapshot:

- get_server_stats (now also returns clean count)
- get_top_channels, get_recent_activity, get_top_flagged
- search_messages (LIKE keyword search)
- get_user_messages, get_user_profile, get_user_reputation
- get_channel_culture
- get_message_detail (full AI analysis of one message)
- get_message_reviews (human moderation queue by status)
- get_voice_recordings (with transcriptions)
- get_moderation_timeline (daily flagged/warn/clean trend)
- get_corrections (AI false-positive correction history)

Security/quality:
- Every executor now uses parameterized drizzle queries (eq/like/and).
  The old code interpolated model-supplied IDs into sql.raw() — a SQL
  injection vector. Removed.
- Split static tool *definitions* into chatbot.toolDefs.ts (no DB import)
  so the LLM-facing schema can be unit-tested without loading the
  database/config layer. chatbot.tools.ts keeps only the executor.

Verified: tsc + biome clean, 40 backend tests pass (4 new covering the
tool-contract: names unique, required args declared, full situation
coverage).

Co-Authored-By: Claude Opus 5 (Nous Research)
2026-08-16 09:22:20 +07:00

58 lines
1.8 KiB
TypeScript

import { describe, expect, it } from "vitest";
import { tools } from "../src/modules/chatbot/chatbot.toolDefs.js";
const names = tools.map((t) => t.function.name);
describe("chatbot tool definitions", () => {
it("exposes a stable, non-empty tool set", () => {
expect(tools.length).toBeGreaterThanOrEqual(10);
expect(new Set(names).size).toBe(names.length); // no dup names
});
it("every tool declares a name, description, and object parameters", () => {
for (const t of tools) {
expect(t.type).toBe("function");
expect(typeof t.function.name).toBe("string");
expect(t.function.description.length).toBeGreaterThan(10);
expect(t.function.parameters.type).toBe("object");
}
});
it("required-only tools declare required args", () => {
const byName = new Map(tools.map((t) => [t.function.name, t]));
for (const [name, required] of [
["search_messages", "query"],
["get_user_messages", "userId"],
["get_user_profile", "userId"],
["get_user_reputation", "userId"],
["get_channel_culture", "channelId"],
["get_message_detail", "messageId"],
] as const) {
const tool = byName.get(name);
expect(tool, `missing tool ${name}`).toBeDefined();
expect(tool!.function.parameters.required).toContain(required);
}
});
it("covers the core server-watcher situations", () => {
for (const required of [
"get_server_stats",
"get_top_channels",
"get_recent_activity",
"get_top_flagged",
"search_messages",
"get_user_messages",
"get_user_profile",
"get_user_reputation",
"get_channel_culture",
"get_message_detail",
"get_message_reviews",
"get_voice_recordings",
"get_moderation_timeline",
"get_corrections",
]) {
expect(names, `missing ${required}`).toContain(required);
}
});
});