You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
taskbot/tests/unit/server.basic.test.ts

301 lines
9.2 KiB
TypeScript

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

/**
* Core HTTP/webhook validation and messagetype handling.
*
* These tests verify that WebhookServer correctly validates incoming
* requests before they reach command processing.
*/
import { describe, test, expect } from 'bun:test';
import { WebhookServer } from '../../src/server';
import { SimulatedResponseQueue } from '../helpers/queue';
import { createTestRequest, registerServerTestLifecycle } from '../helpers/server-test-harness';
const testDb = registerServerTestLifecycle();
// ── Tests ──────────────────────────────────────────────────────────────
describe('WebhookServer — Basic validation', () => {
test('should reject non-POST requests', async () => {
const request = new Request('http://localhost:3007', { method: 'GET' });
const response = await WebhookServer.handleRequest(request);
expect(response.status).toBe(405);
});
test('should require JSON content type', async () => {
const request = new Request('http://localhost:3007', {
method: 'POST',
headers: { 'Content-Type': 'text/plain' },
});
const response = await WebhookServer.handleRequest(request);
expect(response.status).toBe(400);
});
test('should validate payload structure', async () => {
const invalidPayloads = [
{},
{ event: null },
{ event: 'messages.upsert', instance: null },
];
for (const invalidPayload of invalidPayloads) {
const request = createTestRequest(invalidPayload);
const response = await WebhookServer.handleRequest(request);
expect(response.status).toBe(400);
}
});
test('should verify instance name', async () => {
process.env.TEST_VERIFY_INSTANCE = 'true';
const payload = {
event: 'messages.upsert',
instance: 'wrong-instance',
data: {},
};
const request = createTestRequest(payload);
const response = await WebhookServer.handleRequest(request);
expect(response.status).toBe(403);
delete process.env.TEST_VERIFY_INSTANCE;
});
test('should handle valid messages.upsert', async () => {
const payload = {
event: 'messages.upsert',
instance: 'test-instance',
data: {
key: {
remoteJid: 'group-id@g.us',
participant: 'sender-id@s.whatsapp.net',
},
message: { conversation: 'tarea nueva Test' },
},
};
const request = createTestRequest(payload);
const response = await WebhookServer.handleRequest(request);
expect(response.status).toBe(200);
expect(SimulatedResponseQueue.get().length).toBeGreaterThan(0);
});
test('should ignore empty message content', async () => {
const payload = {
event: 'messages.upsert',
instance: 'test-instance',
data: {
key: {
remoteJid: 'group-id@g.us',
participant: 'sender-id@s.whatsapp.net',
},
message: { conversation: '' },
},
};
const request = createTestRequest(payload);
const response = await WebhookServer.handleRequest(request);
expect(response.status).toBe(200);
expect(SimulatedResponseQueue.get().length).toBe(0);
});
test('should handle very long messages', async () => {
const longMessage = 'tarea nueva ' + 'A'.repeat(5000);
const payload = {
event: 'messages.upsert',
instance: 'test-instance',
data: {
key: {
remoteJid: 'group-id@g.us',
participant: 'sender-id@s.whatsapp.net',
},
message: { conversation: longMessage },
},
};
const request = createTestRequest(payload);
const response = await WebhookServer.handleRequest(request);
expect(response.status).toBe(200);
expect(SimulatedResponseQueue.get().length).toBeGreaterThan(0);
});
test('should handle messages with special characters and emojis', async () => {
const specialMessage = 'tarea nueva Test 😊 你好 @#$%^&*()';
const payload = {
event: 'messages.upsert',
instance: 'test-instance',
data: {
key: {
remoteJid: 'group-id@g.us',
participant: 'sender-id@s.whatsapp.net',
},
message: { conversation: specialMessage },
},
};
const request = createTestRequest(payload);
const response = await WebhookServer.handleRequest(request);
expect(response.status).toBe(200);
expect(SimulatedResponseQueue.get().length).toBeGreaterThan(0);
});
test('should ignore non-tarea commands', async () => {
const payload = {
event: 'messages.upsert',
instance: 'test-instance',
data: {
key: {
remoteJid: 'group-id@g.us',
participant: 'sender-id@s.whatsapp.net',
},
message: { conversation: '/othercommand test' },
},
};
const request = createTestRequest(payload);
const response = await WebhookServer.handleRequest(request);
expect(response.status).toBe(200);
expect(SimulatedResponseQueue.get().length).toBe(0);
});
test('should process command with uppercase T prefix (T area)', async () => {
const payload = {
event: 'messages.upsert',
instance: 'test-instance',
data: {
key: {
remoteJid: 'group-id@g.us',
participant: 'sender-id@s.whatsapp.net',
},
message: { conversation: 'T n Test uppercase T' },
},
};
const request = createTestRequest(payload);
const response = await WebhookServer.handleRequest(request);
expect(response.status).toBe(200);
expect(SimulatedResponseQueue.get().length).toBeGreaterThan(0);
});
test('should process command with uppercase Tarea prefix', async () => {
const payload = {
event: 'messages.upsert',
instance: 'test-instance',
data: {
key: {
remoteJid: 'group-id@g.us',
participant: 'sender-id@s.whatsapp.net',
},
message: { conversation: 'Tarea nueva Test uppercase Tarea' },
},
};
const request = createTestRequest(payload);
const response = await WebhookServer.handleRequest(request);
expect(response.status).toBe(200);
expect(SimulatedResponseQueue.get().length).toBeGreaterThan(0);
});
test('should ignore message with mentions but no command', async () => {
const payload = {
event: 'messages.upsert',
instance: 'test-instance',
data: {
key: {
remoteJid: 'group-id@g.us',
participant: 'sender-id@s.whatsapp.net',
},
message: {
conversation: 'Hello everyone!',
contextInfo: {
mentionedJid: ['1234567890@s.whatsapp.net'],
},
},
},
};
const request = createTestRequest(payload);
const response = await WebhookServer.handleRequest(request);
expect(response.status).toBe(200);
expect(SimulatedResponseQueue.get().length).toBe(0);
});
test('should ignore media attachment messages', async () => {
const payload = {
event: 'messages.upsert',
instance: 'test-instance',
data: {
key: {
remoteJid: 'group-id@g.us',
participant: 'sender-id@s.whatsapp.net',
},
message: {
imageMessage: { caption: 'This is an image' },
},
},
};
const request = createTestRequest(payload);
const response = await WebhookServer.handleRequest(request);
expect(response.status).toBe(200);
expect(SimulatedResponseQueue.get().length).toBe(0);
});
test('should process command from extendedTextMessage', async () => {
const payload = {
event: 'messages.upsert',
instance: 'test-instance',
data: {
key: {
remoteJid: 'group-id@g.us',
participant: 'sender-id@s.whatsapp.net',
},
message: {
extendedTextMessage: { text: 't n Test ext' },
},
},
};
const request = createTestRequest(payload);
const response = await WebhookServer.handleRequest(request);
expect(response.status).toBe(200);
expect(SimulatedResponseQueue.get().length).toBeGreaterThan(0);
});
test('should process command from image caption when caption starts with a command', async () => {
const payload = {
event: 'messages.upsert',
instance: 'test-instance',
data: {
key: {
remoteJid: 'group-id@g.us',
participant: 'sender-id@s.whatsapp.net',
},
message: {
imageMessage: { caption: 't n From caption' },
},
},
};
const request = createTestRequest(payload);
const response = await WebhookServer.handleRequest(request);
expect(response.status).toBe(200);
expect(SimulatedResponseQueue.get().length).toBeGreaterThan(0);
});
test('should handle requests on configured port', async () => {
const originalPort = process.env.PORT;
process.env.PORT = '3007';
const prevEnv = {
EVOLUTION_API_URL: process.env.EVOLUTION_API_URL,
EVOLUTION_API_KEY: process.env.EVOLUTION_API_KEY,
EVOLUTION_API_INSTANCE: process.env.EVOLUTION_API_INSTANCE,
CHATBOT_PHONE_NUMBER: process.env.CHATBOT_PHONE_NUMBER,
WEBHOOK_URL: process.env.WEBHOOK_URL,
};
process.env.EVOLUTION_API_URL = 'http://localhost:3000';
process.env.EVOLUTION_API_KEY = 'test-key';
process.env.EVOLUTION_API_INSTANCE = 'test-instance';
process.env.CHATBOT_PHONE_NUMBER = '9999999999';
process.env.WEBHOOK_URL = 'http://localhost:3007';
try {
const server = await WebhookServer.start();
const response = await fetch('http://localhost:3007/health');
expect(response.status).toBe(200);
await server.stop();
} finally {
process.env.PORT = originalPort;
process.env.EVOLUTION_API_URL = prevEnv.EVOLUTION_API_URL;
process.env.EVOLUTION_API_KEY = prevEnv.EVOLUTION_API_KEY;
process.env.EVOLUTION_API_INSTANCE = prevEnv.EVOLUTION_API_INSTANCE;
process.env.CHATBOT_PHONE_NUMBER = prevEnv.CHATBOT_PHONE_NUMBER;
process.env.WEBHOOK_URL = prevEnv.WEBHOOK_URL;
}
});
});