agentTools.test.ts 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117
  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. const question = String(body.question);
  26. const text = question.includes('AGENT_REPLY_POLISH') ? 'Please provide the order number.' : 'Dzień dobry';
  27. if (question.includes('AGENT_REPLY_POLISH')) {
  28. assert.match(question, /English \(en\)/);
  29. assert.match(question, /output MUST be English/);
  30. }
  31. return new Response(JSON.stringify({ text }), {
  32. status: 200,
  33. headers: { 'Content-Type': 'application/json' },
  34. });
  35. }
  36. if (url.includes('eksupport.easyklima.com') && url.includes('/conversations/42/messages')) {
  37. assert.equal((init?.headers as Record<string, string>)?.api_access_token, 'test-chatwoot-token');
  38. return new Response(
  39. JSON.stringify({
  40. payload: [
  41. { id: 1, message_type: 'outgoing', content: 'Dzień dobry' },
  42. { id: 2, message_type: 0, private: false, created_at: 1, content: 'Hey, I have not received my order. What is going on?' },
  43. ],
  44. }),
  45. { status: 200, headers: { 'Content-Type': 'application/json' } },
  46. );
  47. }
  48. return originalFetch(input, init);
  49. }) as typeof globalThis.fetch;
  50. running = await startServer(createApp());
  51. });
  52. after(async () => {
  53. globalThis.fetch = originalFetch;
  54. await running.close();
  55. await disconnectDb();
  56. });
  57. test('serves the Chatwoot injection asset without secrets', async () => {
  58. const res = await fetch(`${running.baseUrl}/chatwoot-ai-tools.js`);
  59. const text = await res.text();
  60. assert.equal(res.status, 200);
  61. assert.match(res.headers.get('content-type') ?? '', /application\/javascript/);
  62. assert.match(text, /AI tłumacz/);
  63. assert.ok(!text.includes('test-agent-token'));
  64. });
  65. test('agent translation endpoints require the agent UI token', async () => {
  66. const res = await fetch(`${running.baseUrl}/agent-tools/translate-message`, {
  67. method: 'POST',
  68. headers: { 'Content-Type': 'application/json' },
  69. body: JSON.stringify({ sourceText: 'Hello', targetLanguage: 'pl' }),
  70. });
  71. assert.equal(res.status, 401);
  72. });
  73. test('translates message text through Flowise and caches repeated requests', async () => {
  74. const payload = { sourceText: 'Good morning', targetLanguage: 'pl', conversationId: 42, messageId: 'm-1' };
  75. const first = await postTranslate('/agent-tools/translate-message', payload);
  76. const second = await postTranslate('/agent-tools/translate-message', payload);
  77. assert.equal(first.status, 200);
  78. assert.equal(second.status, 200);
  79. assert.equal(first.body.translatedText, 'Dzień dobry');
  80. assert.equal(first.body.cached, false);
  81. assert.equal(second.body.cached, true);
  82. assert.equal(flowiseCalls, 1);
  83. });
  84. test('translates draft text through the draft endpoint', async () => {
  85. const res = await postTranslate('/agent-tools/translate-draft', {
  86. draftText: 'Proszę podać numer zamówienia.',
  87. targetLanguage: 'auto',
  88. conversationId: 42,
  89. });
  90. assert.equal(res.status, 200);
  91. assert.equal(res.body.ok, true);
  92. assert.equal(res.body.provider, 'flowise');
  93. assert.equal(res.body.targetLanguage, 'en');
  94. assert.equal(res.body.translatedText, 'Please provide the order number.');
  95. });
  96. async function postTranslate(path: string, body: Record<string, unknown>) {
  97. const res = await fetch(`${running.baseUrl}${path}`, {
  98. method: 'POST',
  99. headers: {
  100. 'Content-Type': 'application/json',
  101. Authorization: 'Bearer test-agent-token',
  102. Origin: 'https://eksupport.easyklima.com',
  103. },
  104. body: JSON.stringify(body),
  105. });
  106. return { status: res.status, body: (await res.json()) as Record<string, unknown> };
  107. }