moving
Some checks failed
Installer Smoke / installer-smoke (push) Has been cancelled

This commit is contained in:
Oleg Maslov
2026-09-02 10:10:29 +02:00
commit 0c3e2ead3b
3841 changed files with 970576 additions and 0 deletions

View File

@@ -0,0 +1,142 @@
import { describe, it, expect, vi, afterEach } from 'vitest';
import type { FastifyInstance } from 'fastify';
import {
telegramRoutes,
pushTelegramMessage,
BOT_TOKEN_PATTERN,
CHAT_ID_PATTERN,
} from '../../src/local/routes/telegram.js';
describe('telegram route exports', () => {
it('exports telegramRoutes + pushTelegramMessage', () => {
expect(typeof telegramRoutes).toBe('function');
expect(typeof pushTelegramMessage).toBe('function');
});
});
describe('BOT_TOKEN_PATTERN', () => {
it('accepts a well-formed BotFather token', () => {
expect(BOT_TOKEN_PATTERN.test('123456789:AAH-abcDEF_ghiJKLmnoPQRstuVWXyz01234')).toBe(true);
});
it('rejects a token with no colon', () => {
expect(BOT_TOKEN_PATTERN.test('123456789AAHabcdefghi')).toBe(false);
});
it('rejects a too-short secret half', () => {
expect(BOT_TOKEN_PATTERN.test('123456789:short')).toBe(false);
});
it('rejects free text', () => {
expect(BOT_TOKEN_PATTERN.test('not-a-real-token')).toBe(false);
});
it('rejects a secret with disallowed chars (spaces)', () => {
expect(BOT_TOKEN_PATTERN.test('123456789:AAH abcDEF ghiJKLmnoPQRstuVWXyz0')).toBe(false);
});
});
describe('CHAT_ID_PATTERN', () => {
it('accepts a positive user chat id', () => {
expect(CHAT_ID_PATTERN.test('123456789')).toBe(true);
});
it('accepts a negative group/supergroup chat id', () => {
expect(CHAT_ID_PATTERN.test('-1001234567890')).toBe(true);
});
it('rejects non-numeric', () => {
expect(CHAT_ID_PATTERN.test('abc123')).toBe(false);
});
it('rejects too-short ids', () => {
expect(CHAT_ID_PATTERN.test('12')).toBe(false);
});
});
// Minimal fake FastifyInstance carrying just a vault stub — pushTelegramMessage
// only reads server.vault.get(). Cast through unknown to avoid stubbing the
// entire Fastify surface.
function fakeServer(creds: { token?: string; chatId?: string }): FastifyInstance {
return {
vault: {
get: (key: string) => {
if (key === 'telegram_bot_token' && creds.token) return { value: creds.token };
if (key === 'telegram_chat_id' && creds.chatId) return { value: creds.chatId };
return null;
},
},
} as unknown as FastifyInstance;
}
describe('pushTelegramMessage', () => {
afterEach(() => { vi.unstubAllGlobals(); });
it('returns ok:false for empty text without touching the network', async () => {
const fetchSpy = vi.fn();
vi.stubGlobal('fetch', fetchSpy);
const res = await pushTelegramMessage(fakeServer({ token: 't', chatId: 'c' }), '');
expect(res.ok).toBe(false);
expect(res.reason).toBe('empty text');
expect(fetchSpy).not.toHaveBeenCalled();
});
it('returns ok:false when telegram is not configured', async () => {
const fetchSpy = vi.fn();
vi.stubGlobal('fetch', fetchSpy);
const res = await pushTelegramMessage(fakeServer({}), 'hello');
expect(res.ok).toBe(false);
expect(res.reason).toBe('telegram not configured');
expect(fetchSpy).not.toHaveBeenCalled();
});
it('posts to the Telegram API and returns ok:true on success', async () => {
const fetchSpy = vi.fn().mockResolvedValue({
json: async () => ({ ok: true, result: { message_id: 42 } }),
});
vi.stubGlobal('fetch', fetchSpy);
const res = await pushTelegramMessage(
fakeServer({ token: '123456789:AAH-abcDEF_ghiJKLmnoPQRstuVWXyz01234', chatId: '123456789' }),
'digest line',
);
expect(res.ok).toBe(true);
expect(res.messageId).toBe(42);
expect(fetchSpy).toHaveBeenCalledTimes(1);
const [url, init] = fetchSpy.mock.calls[0];
// Host is hard-coded — token only interpolates into the path (no SSRF).
expect(String(url)).toContain('https://api.telegram.org/bot');
expect(String(url)).toContain('/sendMessage');
expect(init.method).toBe('POST');
const body = JSON.parse(init.body);
expect(body.chat_id).toBe('123456789');
expect(body.text).toBe('digest line');
});
it('returns ok:false when the Telegram API rejects', async () => {
vi.stubGlobal('fetch', vi.fn().mockResolvedValue({
json: async () => ({ ok: false, description: 'chat not found' }),
}));
const res = await pushTelegramMessage(
fakeServer({ token: '123456789:AAH-abcDEF_ghiJKLmnoPQRstuVWXyz01234', chatId: '999999' }),
'hi',
);
expect(res.ok).toBe(false);
expect(res.reason).toBe('chat not found');
});
it('never throws when fetch rejects (cron-safe)', async () => {
vi.stubGlobal('fetch', vi.fn().mockRejectedValue(new Error('network down')));
const res = await pushTelegramMessage(
fakeServer({ token: '123456789:AAH-abcDEF_ghiJKLmnoPQRstuVWXyz01234', chatId: '123456789' }),
'hi',
);
expect(res.ok).toBe(false);
expect(res.reason).toContain('network down');
});
it('caps text at Telegram\'s 4096-char limit before sending', async () => {
const fetchSpy = vi.fn().mockResolvedValue({ json: async () => ({ ok: true, result: {} }) });
vi.stubGlobal('fetch', fetchSpy);
await pushTelegramMessage(
fakeServer({ token: '123456789:AAH-abcDEF_ghiJKLmnoPQRstuVWXyz01234', chatId: '1234' }),
'x'.repeat(5000),
);
const body = JSON.parse(fetchSpy.mock.calls[0][1].body);
expect(body.text.length).toBeLessThanOrEqual(4096);
});
});