Fresh history cut from cynarAI/evoo antigravity-handoff/project-timeline (Voice First + project timeline). Brand docs/titles as evOo; keep @rakazo/* packages as tech debt. Excludes node_modules, caches, secrets, voice blobs.
339 lines
12 KiB
TypeScript
339 lines
12 KiB
TypeScript
import { Domain } from "chat-adapter-lark";
|
|
import { describe, expect, it, vi } from "vitest";
|
|
import {
|
|
enrichSlackTeamRoom,
|
|
isMessagingEnabled,
|
|
isMessagingSurfaceEnabled,
|
|
type MessagingEnvironmentValues,
|
|
messagingEnvFromProcess,
|
|
messagingPlatformsFromEnv,
|
|
parseSendblueStatus,
|
|
} from "./messaging-platforms.js";
|
|
|
|
// Fake credentials: adapters are constructed offline, never called.
|
|
const fullEnv: MessagingEnvironmentValues = {
|
|
sendblueApiKeyId: "sb-key-id",
|
|
sendblueApiSecret: "sb-secret",
|
|
sendblueSigningSecret: "sb-signing",
|
|
sendbluePhoneNumber: "+15550009999",
|
|
slackBotToken: "xoxb-fake",
|
|
slackSigningSecret: "slack-signing",
|
|
whatsappAccessToken: "wa-token",
|
|
whatsappPhoneNumberId: "wa-phone-id",
|
|
whatsappAppSecret: "wa-app-secret",
|
|
whatsappVerifyToken: "wa-verify",
|
|
telegramBotToken: "tg-token",
|
|
telegramWebhookSecret: "tg-webhook-secret",
|
|
larkAppId: "cli-fake",
|
|
larkAppSecret: "lark-secret",
|
|
larkVerificationToken: "lark-verify",
|
|
};
|
|
|
|
function providers(env: MessagingEnvironmentValues): string[] {
|
|
return messagingPlatformsFromEnv(env).map((platform) => platform.provider);
|
|
}
|
|
|
|
describe("messagingPlatformsFromEnv", () => {
|
|
it("mounts nothing without credentials and everything with full credentials", () => {
|
|
expect(providers({})).toEqual([]);
|
|
expect(providers(fullEnv)).toEqual(["sendblue", "slack", "whatsapp", "telegram", "lark"]);
|
|
});
|
|
|
|
it("requires all four sendblue values", () => {
|
|
for (const key of [
|
|
"sendblueApiKeyId",
|
|
"sendblueApiSecret",
|
|
"sendblueSigningSecret",
|
|
"sendbluePhoneNumber",
|
|
] as const) {
|
|
expect(providers({ ...fullEnv, [key]: undefined })).not.toContain("sendblue");
|
|
}
|
|
});
|
|
|
|
it("requires each platform's full credential set", () => {
|
|
expect(providers({ ...fullEnv, slackSigningSecret: undefined })).not.toContain("slack");
|
|
expect(providers({ ...fullEnv, slackBotToken: undefined })).not.toContain("slack");
|
|
for (const key of [
|
|
"whatsappAccessToken",
|
|
"whatsappPhoneNumberId",
|
|
"whatsappAppSecret",
|
|
"whatsappVerifyToken",
|
|
] as const) {
|
|
expect(providers({ ...fullEnv, [key]: undefined })).not.toContain("whatsapp");
|
|
}
|
|
expect(providers({ ...fullEnv, telegramBotToken: undefined })).not.toContain("telegram");
|
|
// Without the secret token the adapter would accept unsigned webhook
|
|
// posts, so the secret is a mount gate, not optional hardening.
|
|
expect(providers({ ...fullEnv, telegramWebhookSecret: undefined })).not.toContain("telegram");
|
|
expect(providers({ telegramBotToken: "tg-token" })).toEqual([]);
|
|
expect(
|
|
providers({ telegramBotToken: "tg-token", telegramWebhookSecret: "tg-webhook-secret" }),
|
|
).toEqual(["telegram"]);
|
|
expect(providers({ ...fullEnv, larkAppId: undefined })).not.toContain("lark");
|
|
expect(providers({ ...fullEnv, larkAppSecret: undefined })).not.toContain("lark");
|
|
// Without the verification token the adapter would accept unsigned
|
|
// webhook posts, so the token is a mount gate, not optional hardening.
|
|
expect(providers({ ...fullEnv, larkVerificationToken: undefined })).not.toContain("lark");
|
|
expect(providers({ larkAppId: "cli-fake", larkAppSecret: "lark-secret" })).toEqual([]);
|
|
expect(
|
|
providers({
|
|
larkAppId: "cli-fake",
|
|
larkAppSecret: "lark-secret",
|
|
larkVerificationToken: "lark-verify",
|
|
}),
|
|
).toEqual(["lark"]);
|
|
});
|
|
|
|
it("forces Telegram into webhook mode so worker initialize cannot long-poll", () => {
|
|
const telegram = messagingPlatformsFromEnv({
|
|
telegramBotToken: "tg-token",
|
|
telegramWebhookSecret: "tg-webhook-secret",
|
|
})[0]!;
|
|
// mode is protected on the adapter class but readable at runtime.
|
|
expect((telegram.adapter as unknown as { mode: string }).mode).toBe("webhook");
|
|
});
|
|
|
|
it("forces Lark into webhook inbound so worker initialize cannot open a long connection", () => {
|
|
const lark = messagingPlatformsFromEnv({
|
|
larkAppId: "cli-fake",
|
|
larkAppSecret: "lark-secret",
|
|
larkVerificationToken: "lark-verify",
|
|
})[0]!;
|
|
const incoming = lark.adapter as unknown as {
|
|
incomingConfig: { events: string; callbacks: string };
|
|
shouldStartWsClient: () => boolean;
|
|
};
|
|
expect(incoming.incomingConfig).toEqual({ events: "webhook", callbacks: "webhook" });
|
|
expect(incoming.shouldStartWsClient()).toBe(false);
|
|
});
|
|
|
|
it("maps LARK_* process env and accepts the international domain switch", () => {
|
|
expect(
|
|
messagingEnvFromProcess({
|
|
LARK_APP_ID: " cli-fake ",
|
|
LARK_APP_SECRET: " lark-secret ",
|
|
LARK_VERIFICATION_TOKEN: " lark-verify ",
|
|
LARK_ENCRYPT_KEY: " lark-encrypt ",
|
|
LARK_DOMAIN: " Lark ",
|
|
}),
|
|
).toMatchObject({
|
|
larkAppId: "cli-fake",
|
|
larkAppSecret: "lark-secret",
|
|
larkVerificationToken: "lark-verify",
|
|
larkEncryptKey: "lark-encrypt",
|
|
larkDomain: "Lark",
|
|
});
|
|
const international = messagingPlatformsFromEnv({
|
|
larkAppId: "cli-fake",
|
|
larkAppSecret: "lark-secret",
|
|
larkVerificationToken: "lark-verify",
|
|
larkDomain: "Lark",
|
|
})[0]!;
|
|
expect((international.adapter as unknown as { config: { domain: Domain } }).config.domain).toBe(
|
|
Domain.Lark,
|
|
);
|
|
});
|
|
|
|
it.each(["", "unknown", "feishu"])("uses normalized Lark defaults for domain %s", (domain) => {
|
|
vi.stubEnv("LARK_DOMAIN", domain);
|
|
vi.stubEnv("LARK_ENCRYPT_KEY", " ");
|
|
try {
|
|
const lark = messagingPlatformsFromEnv({
|
|
...messagingEnvFromProcess(process.env),
|
|
larkAppId: "cli-fake",
|
|
larkAppSecret: "lark-secret",
|
|
larkVerificationToken: "lark-verify",
|
|
}).find((platform) => platform.provider === "lark")!;
|
|
const config = (
|
|
lark.adapter as unknown as {
|
|
config: { domain: Domain; encryptKey: string };
|
|
}
|
|
).config;
|
|
expect(config.domain).toBe(Domain.Feishu);
|
|
expect(config.encryptKey).toBe("");
|
|
} finally {
|
|
vi.unstubAllEnvs();
|
|
}
|
|
});
|
|
|
|
it("declares group and typing support only for sendblue", () => {
|
|
const platforms = messagingPlatformsFromEnv(fullEnv);
|
|
const capabilities = Object.fromEntries(
|
|
platforms.map((platform) => [platform.provider, platform.capabilities]),
|
|
);
|
|
expect(capabilities.sendblue).toEqual({ direct: true, groups: true, typing: true });
|
|
expect(capabilities.slack).toEqual({ direct: true, groups: true, typing: false });
|
|
expect(capabilities.whatsapp).toEqual({ direct: true, groups: false, typing: false });
|
|
expect(capabilities.telegram).toEqual({ direct: true, groups: false, typing: false });
|
|
expect(capabilities.lark).toEqual({ direct: true, groups: false, typing: false });
|
|
});
|
|
});
|
|
|
|
describe("sendblue platform hooks", () => {
|
|
const sendblue = messagingPlatformsFromEnv(fullEnv)[0]!;
|
|
|
|
it("filters the deployment line and non-string entries out of the roster", () => {
|
|
expect(
|
|
sendblue.participants!({
|
|
participants: ["+15551111111", "+15550009999", 42, null, "+15552222222"],
|
|
}),
|
|
).toEqual(["+15551111111", "+15552222222"]);
|
|
expect(sendblue.participants!({ participants: "not-a-list" })).toEqual([]);
|
|
expect(sendblue.participants!(null)).toEqual([]);
|
|
});
|
|
|
|
it("reads the group display name only when present", () => {
|
|
expect(sendblue.channelName!({ group_display_name: "Family" })).toBe("Family");
|
|
expect(sendblue.channelName!({ group_display_name: "" })).toBeNull();
|
|
expect(sendblue.channelName!({})).toBeNull();
|
|
expect(sendblue.channelName!(null)).toBeNull();
|
|
});
|
|
|
|
it("accepts only supported per-message transports", () => {
|
|
for (const service of ["iMessage", "SMS", "RCS"]) {
|
|
expect(sendblue.transport!({ service })).toBe(service);
|
|
}
|
|
expect(sendblue.transport!({ service: "email" })).toBeNull();
|
|
expect(sendblue.transport!({ service: 42 })).toBeNull();
|
|
expect(sendblue.transport!(null)).toBeNull();
|
|
});
|
|
|
|
it("derives deterministic provider-prefixed direct thread ids", () => {
|
|
expect(sendblue.directThreadId!("+15551234567")).toMatch(/^sendblue:/);
|
|
expect(sendblue.adapter.isDM?.(sendblue.directThreadId!("+15551234567"))).toBe(true);
|
|
});
|
|
});
|
|
|
|
describe("parseSendblueStatus", () => {
|
|
const statusPayload = {
|
|
content: "",
|
|
is_outbound: true,
|
|
status: "DELIVERED",
|
|
message_handle: "handle-1",
|
|
from_number: "+15550009999",
|
|
};
|
|
|
|
it("normalizes outbound delivery webhooks", () => {
|
|
expect(parseSendblueStatus(statusPayload)).toEqual({
|
|
type: "status",
|
|
provider: "sendblue",
|
|
handle: "handle-1",
|
|
status: "DELIVERED",
|
|
});
|
|
expect(parseSendblueStatus({ ...statusPayload, status: 7 })).toEqual(
|
|
expect.objectContaining({ status: "" }),
|
|
);
|
|
});
|
|
|
|
it("ignores inbound and malformed payloads", () => {
|
|
expect(parseSendblueStatus({ ...statusPayload, is_outbound: false })).toBeNull();
|
|
expect(parseSendblueStatus({ ...statusPayload, message_handle: "" })).toBeNull();
|
|
const { message_handle: _dropped, ...withoutHandle } = statusPayload;
|
|
expect(parseSendblueStatus(withoutHandle)).toBeNull();
|
|
expect(parseSendblueStatus(null)).toBeNull();
|
|
expect(parseSendblueStatus("nope")).toBeNull();
|
|
});
|
|
});
|
|
|
|
describe("isMessagingEnabled", () => {
|
|
it("requires at least one platform", () => {
|
|
vi.stubEnv("VITEST", "");
|
|
expect(isMessagingEnabled(messagingPlatformsFromEnv(fullEnv))).toBe(true);
|
|
expect(isMessagingEnabled([])).toBe(false);
|
|
vi.unstubAllEnvs();
|
|
});
|
|
|
|
it("is disabled under vitest even with platforms configured", () => {
|
|
expect(process.env.VITEST).toBeTruthy();
|
|
expect(isMessagingEnabled(messagingPlatformsFromEnv(fullEnv))).toBe(false);
|
|
});
|
|
|
|
it.each(["0", "false"])("does not treat VITEST=%s as an active test runner", (value) => {
|
|
vi.stubEnv("VITEST", value);
|
|
expect(isMessagingEnabled(messagingPlatformsFromEnv(fullEnv))).toBe(true);
|
|
vi.unstubAllEnvs();
|
|
});
|
|
});
|
|
|
|
describe("isMessagingSurfaceEnabled", () => {
|
|
it("requires the deployment model key only for open signup", () => {
|
|
vi.stubEnv("VITEST", "");
|
|
const platforms = messagingPlatformsFromEnv(fullEnv);
|
|
const key = (deploymentModelKey: string | undefined, openSignup: boolean) => ({
|
|
deploymentModelKey,
|
|
openSignup,
|
|
});
|
|
// Open signup provisions users with no credentials of their own.
|
|
expect(isMessagingSurfaceEnabled(platforms, key("model-key", true))).toBe(true);
|
|
expect(isMessagingSurfaceEnabled(platforms, key(undefined, true))).toBe(false);
|
|
// Linking-only deployments run linked users on their own credentials.
|
|
expect(isMessagingSurfaceEnabled(platforms, key(undefined, false))).toBe(true);
|
|
expect(isMessagingSurfaceEnabled([], key("model-key", true))).toBe(false);
|
|
vi.unstubAllEnvs();
|
|
});
|
|
});
|
|
|
|
describe("enrichSlackTeamRoom", () => {
|
|
const base = {
|
|
type: "message" as const,
|
|
provider: "slack",
|
|
handle: "Ev1",
|
|
threadId: "slack:C1",
|
|
isDirect: false,
|
|
from: "U_OTHER",
|
|
fromLabel: "Ada",
|
|
channelName: "launch",
|
|
participants: ["U_OTHER"],
|
|
content: "hello <@U_SOMEONE>",
|
|
mediaUrl: null,
|
|
};
|
|
|
|
it("marks app_mention events as mention", () => {
|
|
const enrichment = enrichSlackTeamRoom(
|
|
{
|
|
team_id: "T1",
|
|
authorizations: [{ user_id: "U_BOT", is_bot: true }],
|
|
event: { type: "app_mention", channel: "C1", text: "<@U_BOT> ship it", user: "U_OTHER" },
|
|
},
|
|
base,
|
|
);
|
|
expect(enrichment.kind).toBe("mention");
|
|
expect(enrichment.workspaceId).toBe("T1");
|
|
expect(enrichment.conversationKey).toBe("C1");
|
|
});
|
|
|
|
it("keeps ambient when another user is mentioned, not the bot", () => {
|
|
const enrichment = enrichSlackTeamRoom(
|
|
{
|
|
team_id: "T1",
|
|
authorizations: [{ user_id: "U_BOT", is_bot: true }],
|
|
event: {
|
|
type: "message",
|
|
channel: "C1",
|
|
text: "hey <@U_SOMEONE> can you look?",
|
|
user: "U_OTHER",
|
|
},
|
|
},
|
|
base,
|
|
);
|
|
expect(enrichment.kind).toBe("ambient");
|
|
});
|
|
|
|
it("marks message events that mention the authorized bot as mention", () => {
|
|
const enrichment = enrichSlackTeamRoom(
|
|
{
|
|
team_id: "T1",
|
|
authorizations: [{ user_id: "U_BOT", is_bot: true }],
|
|
event: {
|
|
type: "message",
|
|
channel: "C1",
|
|
text: "hey <@U_BOT> ship Friday?",
|
|
user: "U_OTHER",
|
|
},
|
|
},
|
|
base,
|
|
);
|
|
expect(enrichment.kind).toBe("mention");
|
|
});
|
|
});
|