112 lines
4.5 KiB
JavaScript
112 lines
4.5 KiB
JavaScript
import test from 'node:test';
|
|
import assert from 'node:assert/strict';
|
|
import { TelegramAlerter } from '../lib/alerts/telegram.mjs';
|
|
import { TelegramChatAssistant, buildTelegramChatContext } from '../lib/llm/telegram-chat.mjs';
|
|
import { extractBotChannelMessages } from '../apis/sources/telegram.mjs';
|
|
|
|
test('Telegram AI chat uses bounded history and current intelligence context', async () => {
|
|
const calls = [];
|
|
const provider = {
|
|
isConfigured: true,
|
|
async complete(systemPrompt, userMessage, options) {
|
|
calls.push({ systemPrompt, userMessage, options });
|
|
return { text: calls.length === 1 ? 'First answer' : 'Second answer' };
|
|
},
|
|
};
|
|
const assistant = new TelegramChatAssistant({
|
|
provider,
|
|
getContext: () => '{"direction":"risk-off"}',
|
|
historyMessages: 4,
|
|
maxInputChars: 200,
|
|
maxTokens: 1024,
|
|
timeoutMs: 120000,
|
|
});
|
|
|
|
assert.equal(await assistant.reply('What changed today?', { chatId: 42 }), 'First answer');
|
|
assert.equal(await assistant.reply('Explain the implications in detail', { chatId: 42 }), 'Second answer');
|
|
|
|
assert.match(calls[0].systemPrompt, /untrusted evidence/i);
|
|
assert.match(calls[0].systemPrompt, /Your name is DAVE/);
|
|
assert.match(calls[0].systemPrompt, /ADAPTIVE WRITING STYLE/);
|
|
assert.match(calls[0].userMessage, /risk-off/);
|
|
assert.deepEqual(calls[0].options, { maxTokens: 1024, timeout: 120000 });
|
|
assert.match(calls[1].userMessage, /User: What changed today\?/);
|
|
assert.match(calls[1].userMessage, /Assistant: First answer/);
|
|
assert.match(calls[1].userMessage, /NEW USER MESSAGE: Explain the implications in detail/);
|
|
assert.equal(assistant.historySize(42), 4);
|
|
|
|
assistant.reset(42);
|
|
assert.equal(assistant.historySize(42), 0);
|
|
});
|
|
|
|
test('Telegram AI chat reports missing LLM configuration', async () => {
|
|
const assistant = new TelegramChatAssistant({ provider: null });
|
|
assert.match(await assistant.reply('hello', { chatId: 1 }), /unavailable/i);
|
|
});
|
|
|
|
test('Telegram chat context is compact and operationally useful', () => {
|
|
const context = JSON.parse(buildTelegramChatContext({
|
|
meta: { generatedAt: '2026-07-05T10:00:00Z' },
|
|
delta: { summary: { direction: 'risk-off', totalChanges: 3, criticalChanges: 1 } },
|
|
ideas: [{ title: 'Gold hedge', type: 'HEDGE', ticker: 'GLD', confidence: 'HIGH' }],
|
|
news: [{ title: 'Headline', source: 'Feed', url: 'https://example.test/story' }],
|
|
sourceHealth: [{ name: 'ACLED', status: 'degraded', error: 'missing credentials' }],
|
|
}, { status: 'degraded', sourcesOk: 22, sourcesDegraded: 1 }));
|
|
|
|
assert.equal(context.direction, 'risk-off');
|
|
assert.equal(context.ideas[0].ticker, 'GLD');
|
|
assert.equal(context.news[0].url, 'https://example.test/story');
|
|
assert.equal(context.degradedSources[0].name, 'ACLED');
|
|
});
|
|
|
|
test('Telegram transport routes authorized free text as plain-text AI reply', async () => {
|
|
const alerter = new TelegramAlerter({ botToken: 'test-token', chatId: '42' });
|
|
let handled = 0;
|
|
const requests = [];
|
|
alerter.onMessage(async (text) => {
|
|
handled++;
|
|
return { text: `Answer: ${text}`, parseMode: null };
|
|
});
|
|
|
|
const originalFetch = globalThis.fetch;
|
|
globalThis.fetch = async (url, options = {}) => {
|
|
requests.push({ url, body: options.body ? JSON.parse(options.body) : null });
|
|
if (url.includes('/getUpdates')) {
|
|
return {
|
|
ok: true,
|
|
json: async () => ({
|
|
ok: true,
|
|
result: [
|
|
{ update_id: 1, message: { message_id: 10, text: 'ignore me', chat: { id: 99 } } },
|
|
{ update_id: 2, message: { message_id: 11, text: 'What changed?', chat: { id: 42 } } },
|
|
],
|
|
}),
|
|
};
|
|
}
|
|
return { ok: true, json: async () => ({ ok: true, result: { message_id: 12 } }) };
|
|
};
|
|
|
|
try {
|
|
await alerter._pollUpdates();
|
|
} finally {
|
|
globalThis.fetch = originalFetch;
|
|
}
|
|
|
|
assert.equal(handled, 1);
|
|
const sent = requests.find(request => request.url.includes('/sendMessage'));
|
|
assert.equal(sent.body.text, 'Answer: What changed?');
|
|
assert.equal('parse_mode' in sent.body, false);
|
|
assert.equal(sent.body.reply_to_message_id, 11);
|
|
});
|
|
|
|
test('Telegram OSINT extraction excludes private AI chat messages', () => {
|
|
const messages = extractBotChannelMessages([
|
|
{ update_id: 1, message: { text: 'private question', chat: { id: 42, type: 'private' } } },
|
|
{ update_id: 2, channel_post: { text: 'public channel report', chat: { title: 'OSINT', type: 'channel' } } },
|
|
]);
|
|
|
|
assert.equal(messages.length, 1);
|
|
assert.equal(messages[0].text, 'public channel report');
|
|
assert.equal(messages[0].chat, 'OSINT');
|
|
});
|