mirror of
https://github.com/tiennm99/miti99bot.git
synced 2026-04-17 15:20:58 +00:00
grammY-based bot with a module plugin system loaded from the MODULES env var. Three command visibility levels (public/protected/private) share a unified command namespace with conflict detection at registry build. - 4 initial modules (util, wordle, loldle, misc); util fully implemented, others are stubs proving the plugin system end-to-end - util: /info (chat/thread/sender ids) + /help (pure renderer over the registry, HTML parse mode, escapes user-influenced strings) - KVStore interface with CFKVStore and a per-module prefixing factory; getJSON/putJSON convenience helpers; other backends drop in via one file - Webhook at POST /webhook with secret-token validation via grammY's webhookCallback; no admin HTTP surface - Post-deploy register script (npm run deploy = wrangler deploy && node --env-file=.env.deploy scripts/register.js) for setWebhook and setMyCommands; --dry-run flag for preview - 56 vitest unit tests across 7 suites covering registry, db wrapper, dispatcher, help renderer, validators, and HTML escaper - biome for lint + format; phased implementation plan under plans/
57 lines
2.5 KiB
JavaScript
57 lines
2.5 KiB
JavaScript
import { beforeEach, describe, expect, it } from "vitest";
|
|
import { installDispatcher } from "../../src/modules/dispatcher.js";
|
|
import { resetRegistry } from "../../src/modules/registry.js";
|
|
import { makeFakeBot } from "../fakes/fake-bot.js";
|
|
import { makeFakeKv } from "../fakes/fake-kv-namespace.js";
|
|
import { makeCommand, makeFakeImportMap, makeModule } from "../fakes/fake-modules.js";
|
|
|
|
// The dispatcher imports the registry's default static map at module-load
|
|
// time, so to keep the test hermetic we need to stub env.MODULES with names
|
|
// that resolve in the REAL map — OR we call buildRegistry directly with an
|
|
// injected map and then simulate what installDispatcher does. Since phase-04
|
|
// made loadModules accept an importMap injection but installDispatcher
|
|
// internally calls buildRegistry WITHOUT an injection, we test the same
|
|
// effect by wiring the bot manually against a registry built with a fake map.
|
|
//
|
|
// Keeping it simple: call installDispatcher against the real module map
|
|
// (util + wordle + loldle + misc) so we exercise the actual production path.
|
|
|
|
describe("installDispatcher", () => {
|
|
beforeEach(() => resetRegistry());
|
|
|
|
it("registers EVERY command via bot.command() regardless of visibility", async () => {
|
|
const bot = makeFakeBot();
|
|
const env = { MODULES: "util,wordle,loldle,misc", KV: makeFakeKv() };
|
|
const reg = await installDispatcher(bot, env);
|
|
|
|
// Expect 11 total commands (5 public + 3 protected + 3 private).
|
|
expect(bot.commandCalls).toHaveLength(11);
|
|
expect(reg.allCommands.size).toBe(11);
|
|
|
|
const registeredNames = bot.commandCalls.map((c) => c.name).sort();
|
|
const expected = [...reg.allCommands.keys()].sort();
|
|
expect(registeredNames).toEqual(expected);
|
|
|
|
// Assert private commands ARE registered (the whole point of unified routing).
|
|
expect(registeredNames).toContain("konami");
|
|
expect(registeredNames).toContain("ggwp");
|
|
expect(registeredNames).toContain("fortytwo");
|
|
});
|
|
|
|
it("does NOT install any bot.on() middleware — dispatcher is minimal", async () => {
|
|
const bot = makeFakeBot();
|
|
const env = { MODULES: "util", KV: makeFakeKv() };
|
|
await installDispatcher(bot, env);
|
|
expect(bot.onCalls).toHaveLength(0);
|
|
});
|
|
|
|
it("each registered handler matches the source module command handler", async () => {
|
|
const bot = makeFakeBot();
|
|
const env = { MODULES: "util", KV: makeFakeKv() };
|
|
const reg = await installDispatcher(bot, env);
|
|
for (const { name, handler } of bot.commandCalls) {
|
|
expect(handler).toBe(reg.allCommands.get(name).cmd.handler);
|
|
}
|
|
});
|
|
});
|