agentTools.test.ts 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109
  1. import { after, before, test } from 'node:test';
  2. import assert from 'node:assert/strict';
  3. import { prepareTestDatabase, startServer, type RunningServer } from '../helpers/testServer.js';
  4. import { applyTestConfig } from '../helpers/testConfig.js';
  5. const dbUrl = prepareTestDatabase('test-agent-tools');
  6. const { createApp } = await import('../../src/http/app.js');
  7. const { disconnectDb } = await import('../../src/store/db.js');
  8. let running: RunningServer;
  9. let originalFetch: typeof globalThis.fetch;
  10. let flowiseCalls = 0;
  11. before(async () => {
  12. applyTestConfig({
  13. DATABASE_URL: dbUrl,
  14. AGENT_UI_TOKEN: 'test-agent-token',
  15. CHATWOOT_BASE_URL: 'https://eksupport.easyklima.com',
  16. FLOWISE_TRANSLATION_PREDICT_URL: 'https://flowise.test/api/v1/prediction/translation',
  17. });
  18. originalFetch = globalThis.fetch;
  19. globalThis.fetch = (async (input: Parameters<typeof fetch>[0], init?: Parameters<typeof fetch>[1]) => {
  20. const url = String(input);
  21. if (url.includes('flowise.test')) {
  22. flowiseCalls += 1;
  23. const body = JSON.parse(String(init?.body ?? '{}')) as Record<string, unknown>;
  24. assert.match(String(body.question), /Translate this|TRANSLATION_TASK/);
  25. return new Response(JSON.stringify({ text: 'Dzień dobry' }), {
  26. status: 200,
  27. headers: { 'Content-Type': 'application/json' },
  28. });
  29. }
  30. if (url.includes('eksupport.easyklima.com') && url.includes('/conversations/42/messages')) {
  31. assert.equal((init?.headers as Record<string, string>)?.api_access_token, 'test-chatwoot-token');
  32. return new Response(
  33. JSON.stringify({
  34. payload: [
  35. { id: 1, message_type: 'outgoing', content: 'Dzień dobry' },
  36. { id: 2, message_type: 0, private: false, created_at: 1, content: 'Guten Morgen, ich brauche Hilfe.' },
  37. ],
  38. }),
  39. { status: 200, headers: { 'Content-Type': 'application/json' } },
  40. );
  41. }
  42. return originalFetch(input, init);
  43. }) as typeof globalThis.fetch;
  44. running = await startServer(createApp());
  45. });
  46. after(async () => {
  47. globalThis.fetch = originalFetch;
  48. await running.close();
  49. await disconnectDb();
  50. });
  51. test('serves the Chatwoot injection asset without secrets', async () => {
  52. const res = await fetch(`${running.baseUrl}/chatwoot-ai-tools.js`);
  53. const text = await res.text();
  54. assert.equal(res.status, 200);
  55. assert.match(res.headers.get('content-type') ?? '', /application\/javascript/);
  56. assert.match(text, /AI tłumacz/);
  57. assert.ok(!text.includes('test-agent-token'));
  58. });
  59. test('agent translation endpoints require the agent UI token', async () => {
  60. const res = await fetch(`${running.baseUrl}/agent-tools/translate-message`, {
  61. method: 'POST',
  62. headers: { 'Content-Type': 'application/json' },
  63. body: JSON.stringify({ sourceText: 'Hello', targetLanguage: 'pl' }),
  64. });
  65. assert.equal(res.status, 401);
  66. });
  67. test('translates message text through Flowise and caches repeated requests', async () => {
  68. const payload = { sourceText: 'Good morning', targetLanguage: 'pl', conversationId: 42, messageId: 'm-1' };
  69. const first = await postTranslate('/agent-tools/translate-message', payload);
  70. const second = await postTranslate('/agent-tools/translate-message', payload);
  71. assert.equal(first.status, 200);
  72. assert.equal(second.status, 200);
  73. assert.equal(first.body.translatedText, 'Dzień dobry');
  74. assert.equal(first.body.cached, false);
  75. assert.equal(second.body.cached, true);
  76. assert.equal(flowiseCalls, 1);
  77. });
  78. test('translates draft text through the draft endpoint', async () => {
  79. const res = await postTranslate('/agent-tools/translate-draft', {
  80. draftText: 'Proszę podać numer zamówienia.',
  81. targetLanguage: 'auto',
  82. conversationId: 42,
  83. });
  84. assert.equal(res.status, 200);
  85. assert.equal(res.body.ok, true);
  86. assert.equal(res.body.provider, 'flowise');
  87. });
  88. async function postTranslate(path: string, body: Record<string, unknown>) {
  89. const res = await fetch(`${running.baseUrl}${path}`, {
  90. method: 'POST',
  91. headers: {
  92. 'Content-Type': 'application/json',
  93. Authorization: 'Bearer test-agent-token',
  94. Origin: 'https://eksupport.easyklima.com',
  95. },
  96. body: JSON.stringify(body),
  97. });
  98. return { status: res.status, body: (await res.json()) as Record<string, unknown> };
  99. }