openclaw/src/telegram/monitor.test.ts
Arne Moor 1f959a188f chore: apply biome formatting
Applied formatting fixes to 21 files:
- Fixed import ordering
- Normalized indentation
- Wrapped long lines

No logic changes, formatting only.
2025-12-06 18:45:25 +01:00

337 lines
9.7 KiB
TypeScript

/**
* Tests for Telegram monitor
*/
import { beforeEach, describe, expect, it, type Mock, vi } from "vitest";
import type { Provider } from "../providers/base/interface.js";
import type {
MessageHandler,
ProviderMessage,
} from "../providers/base/types.js";
import type { RuntimeEnv } from "../runtime.js";
import { monitorTelegramProvider } from "./monitor.js";
// Mock dependencies
vi.mock("../env.js", () => ({
readEnv: vi.fn(() => ({
accountSid: "test-sid",
whatsappFrom: "+1234567890",
auth: { accountSid: "test-sid", authToken: "test-token" },
telegram: {
apiId: 12345,
apiHash: "test-hash",
},
})),
}));
vi.mock("../config/config.js", () => ({
loadConfig: vi.fn(() => ({
inbound: {
allowFrom: ["telegram:@testuser"],
timestampPrefix: false,
reply: {
mode: "text",
text: "Auto-reply test",
},
},
})),
}));
vi.mock("../auto-reply/reply.js", () => ({
getReplyFromConfig: vi.fn(async () => ({
text: "Test reply",
})),
}));
vi.mock("../providers/factory.js", () => ({
createInitializedProvider: vi.fn(),
}));
describe("monitorTelegramProvider", () => {
let mockRuntime: RuntimeEnv;
let mockProvider: Provider;
let messageHandler: MessageHandler | null = null;
beforeEach(async () => {
vi.clearAllMocks();
messageHandler = null;
mockRuntime = {
log: vi.fn(),
error: vi.fn(),
exit: vi.fn(),
env: {},
};
mockProvider = {
kind: "telegram",
capabilities: {
supportsDeliveryReceipts: false,
supportsReadReceipts: false,
supportsTypingIndicator: true,
supportsReactions: false,
supportsReplies: true,
supportsEditing: true,
supportsDeleting: true,
maxMediaSize: 50 * 1024 * 1024,
supportedMediaTypes: ["image", "video", "audio", "document"],
canInitiateConversation: true,
},
initialize: vi.fn(),
isConnected: vi.fn(() => true),
disconnect: vi.fn(),
send: vi.fn(async () => ({
messageId: "msg-123",
status: "sent" as const,
})),
sendTyping: vi.fn(),
getDeliveryStatus: vi.fn(async () => ({
messageId: "msg-123",
status: "unknown" as const,
timestamp: Date.now(),
})),
onMessage: vi.fn((handler: MessageHandler) => {
messageHandler = handler;
}),
startListening: vi.fn(),
stopListening: vi.fn(),
isAuthenticated: vi.fn(async () => true),
login: vi.fn(),
logout: vi.fn(),
getSessionId: vi.fn(async () => "@testuser"),
};
const { createInitializedProvider } = await import(
"../providers/factory.js"
);
(createInitializedProvider as Mock).mockResolvedValue(mockProvider);
});
it("should throw if Telegram is not configured", async () => {
const { readEnv } = await import("../env.js");
(readEnv as Mock).mockReturnValueOnce({
accountSid: "test-sid",
whatsappFrom: "+1234567890",
auth: { accountSid: "test-sid", authToken: "test-token" },
// No telegram config
});
await expect(monitorTelegramProvider(false, mockRuntime)).rejects.toThrow(
"Telegram not configured",
);
});
it("should create and initialize provider with correct config", async () => {
const { createInitializedProvider } = await import(
"../providers/factory.js"
);
// Mock startListening to return immediately for test
(mockProvider.startListening as Mock).mockImplementationOnce(async () => {
// Immediately resolve to avoid hanging
});
// Start monitor in background
const _monitorPromise = monitorTelegramProvider(true, mockRuntime);
// Wait a bit for initialization
await new Promise((resolve) => setTimeout(resolve, 10));
expect(createInitializedProvider).toHaveBeenCalledWith("telegram", {
kind: "telegram",
apiId: 12345,
apiHash: "test-hash",
sessionDir: undefined,
allowFrom: ["telegram:@testuser"],
verbose: true,
});
expect(mockProvider.onMessage).toHaveBeenCalled();
expect(mockProvider.startListening).toHaveBeenCalled();
// monitorPromise will hang indefinitely, but we've verified the setup
});
it("should register message handler and handle inbound messages", async () => {
const { getReplyFromConfig } = await import("../auto-reply/reply.js");
// Mock startListening to call handler with test message
(mockProvider.startListening as Mock).mockImplementationOnce(async () => {
// Simulate receiving a message
if (messageHandler) {
const testMessage: ProviderMessage = {
id: "test-msg-123",
from: "telegram:@testuser",
to: "telegram:@botuser",
body: "Hello bot",
timestamp: Date.now(),
provider: "telegram",
};
await messageHandler(testMessage);
}
});
// Start monitor
const _monitorPromise = monitorTelegramProvider(false, mockRuntime);
// Wait for message to be processed
await new Promise((resolve) => setTimeout(resolve, 50));
// Verify reply was requested
expect(getReplyFromConfig).toHaveBeenCalledWith(
expect.objectContaining({
Body: "Hello bot",
From: "telegram:@testuser",
To: "telegram:@botuser",
}),
expect.objectContaining({
onReplyStart: expect.any(Function),
}),
expect.anything(),
);
// Verify reply was sent
expect(mockProvider.send).toHaveBeenCalledWith(
"telegram:@testuser",
"Test reply",
expect.anything(),
);
// monitorPromise will hang, but we've verified message handling
});
it("should filter messages based on allowFrom config", async () => {
const { loadConfig } = await import("../config/config.js");
const { getReplyFromConfig } = await import("../auto-reply/reply.js");
// Set allowFrom filter
(loadConfig as Mock).mockReturnValueOnce({
inbound: {
allowFrom: ["telegram:@alloweduser"],
timestampPrefix: false,
reply: {
mode: "text",
text: "Auto-reply test",
},
},
});
// Mock startListening to call handler with message from non-allowed user
(mockProvider.startListening as Mock).mockImplementationOnce(async () => {
if (messageHandler) {
const testMessage: ProviderMessage = {
id: "test-msg-123",
from: "telegram:@notallowed",
to: "telegram:@botuser",
body: "Hello",
timestamp: Date.now(),
provider: "telegram",
};
await messageHandler(testMessage);
}
});
// Start monitor
const _monitorPromise = monitorTelegramProvider(true, mockRuntime);
// Wait for processing
await new Promise((resolve) => setTimeout(resolve, 50));
// Verify reply was NOT attempted (filtered out)
expect(getReplyFromConfig).not.toHaveBeenCalled();
expect(mockProvider.send).not.toHaveBeenCalled();
});
it("should handle media messages", async () => {
const { getReplyFromConfig } = await import("../auto-reply/reply.js");
// Mock reply with media
(getReplyFromConfig as Mock).mockResolvedValueOnce({
text: "Here's an image",
mediaUrl: "https://example.com/image.jpg",
});
// Mock startListening to send message with media
(mockProvider.startListening as Mock).mockImplementationOnce(async () => {
if (messageHandler) {
const testMessage: ProviderMessage = {
id: "test-msg-456",
from: "telegram:@testuser",
to: "telegram:@botuser",
body: "Send me a picture",
timestamp: Date.now(),
provider: "telegram",
media: [
{
type: "image",
url: "https://example.com/input.jpg",
mimeType: "image/jpeg",
},
],
};
await messageHandler(testMessage);
}
});
// Start monitor
const _monitorPromise = monitorTelegramProvider(false, mockRuntime);
// Wait for processing
await new Promise((resolve) => setTimeout(resolve, 50));
// Verify reply with media was sent
expect(mockProvider.send).toHaveBeenCalledWith(
"telegram:@testuser",
"Here's an image",
expect.objectContaining({
media: expect.arrayContaining([
expect.objectContaining({
type: "image",
url: "https://example.com/image.jpg",
}),
]),
}),
);
});
it("should send typing indicator when onReplyStart is called", async () => {
const { getReplyFromConfig } = await import("../auto-reply/reply.js");
let capturedOnReplyStart: (() => Promise<void>) | null = null;
// Capture onReplyStart callback
(getReplyFromConfig as Mock).mockImplementationOnce(async (_ctx, opts) => {
capturedOnReplyStart = opts?.onReplyStart;
return { text: "Test reply" };
});
// Mock startListening
(mockProvider.startListening as Mock).mockImplementationOnce(async () => {
if (messageHandler) {
const testMessage: ProviderMessage = {
id: "test-msg-789",
from: "telegram:@testuser",
to: "telegram:@botuser",
body: "Hello",
timestamp: Date.now(),
provider: "telegram",
};
await messageHandler(testMessage);
}
});
// Start monitor
const _monitorPromise = monitorTelegramProvider(false, mockRuntime);
// Wait for processing
await new Promise((resolve) => setTimeout(resolve, 50));
// Call captured onReplyStart
if (capturedOnReplyStart) {
await capturedOnReplyStart();
}
// Verify typing indicator was sent
expect(mockProvider.sendTyping).toHaveBeenCalledWith("telegram:@testuser");
});
});