Skip to content

Commit f3facd6

Browse files
committed
feature: adding tests for whatsapp plugin
1 parent 7df280d commit f3facd6

File tree

4 files changed

+325
-5
lines changed

4 files changed

+325
-5
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
import { describe, it, expect, vi, beforeEach } from 'vitest';
2+
import axios from 'axios';
3+
import { WhatsAppClient } from '../src/client';
4+
import { WhatsAppConfig, WhatsAppMessage } from '../src/types';
5+
6+
vi.mock('axios', () => {
7+
const mockPost = vi.fn();
8+
return {
9+
default: {
10+
create: () => ({
11+
post: mockPost
12+
})
13+
}
14+
};
15+
});
16+
17+
describe('WhatsAppClient', () => {
18+
let client: WhatsAppClient;
19+
let mockPost: any;
20+
21+
const mockConfig: WhatsAppConfig = {
22+
accessToken: 'test-token',
23+
phoneNumberId: 'test-phone-id',
24+
webhookVerifyToken: 'test-webhook-token',
25+
businessAccountId: 'test-business-id'
26+
};
27+
28+
beforeEach(() => {
29+
vi.clearAllMocks();
30+
client = new WhatsAppClient(mockConfig);
31+
mockPost = (axios.create() as any).post;
32+
});
33+
34+
describe('sendMessage', () => {
35+
it('should send a text message correctly', async () => {
36+
const mockMessage: WhatsAppMessage = {
37+
type: 'text',
38+
to: '1234567890',
39+
content: 'Hello, World!'
40+
};
41+
42+
const expectedPayload = {
43+
messaging_product: 'whatsapp',
44+
recipient_type: 'individual',
45+
to: mockMessage.to,
46+
type: mockMessage.type,
47+
text: { body: mockMessage.content }
48+
};
49+
50+
const mockResponse = { data: { message_id: 'test-id' } };
51+
mockPost.mockResolvedValue(mockResponse);
52+
53+
const response = await client.sendMessage(mockMessage);
54+
55+
expect(mockPost).toHaveBeenCalledWith(`/${mockConfig.phoneNumberId}/messages`, expectedPayload);
56+
expect(response).toEqual(mockResponse);
57+
});
58+
59+
it('should send a template message correctly', async () => {
60+
const mockMessage: WhatsAppMessage = {
61+
type: 'template',
62+
to: '1234567890',
63+
content: {
64+
name: 'test_template',
65+
language: {
66+
code: 'en'
67+
},
68+
components: [{
69+
type: 'body',
70+
parameters: [{
71+
type: 'text',
72+
text: 'Test Parameter'
73+
}]
74+
}]
75+
}
76+
};
77+
78+
const expectedPayload = {
79+
messaging_product: 'whatsapp',
80+
recipient_type: 'individual',
81+
to: mockMessage.to,
82+
type: mockMessage.type,
83+
template: mockMessage.content
84+
};
85+
86+
const mockResponse = { data: { message_id: 'test-id' } };
87+
mockPost.mockResolvedValue(mockResponse);
88+
89+
const response = await client.sendMessage(mockMessage);
90+
91+
expect(mockPost).toHaveBeenCalledWith(`/${mockConfig.phoneNumberId}/messages`, expectedPayload);
92+
expect(response).toEqual(mockResponse);
93+
});
94+
95+
it('should handle API errors correctly', async () => {
96+
const mockMessage: WhatsAppMessage = {
97+
type: 'text',
98+
to: '1234567890',
99+
content: 'Hello, World!'
100+
};
101+
102+
const mockError = new Error('API Error');
103+
mockPost.mockRejectedValue(mockError);
104+
105+
await expect(client.sendMessage(mockMessage)).rejects.toThrow('API Error');
106+
});
107+
});
108+
109+
describe('verifyWebhook', () => {
110+
it('should verify webhook token correctly', async () => {
111+
const result = await client.verifyWebhook(mockConfig.webhookVerifyToken!);
112+
expect(result).toBe(true);
113+
});
114+
115+
it('should reject invalid webhook token', async () => {
116+
const result = await client.verifyWebhook('invalid-token');
117+
expect(result).toBe(false);
118+
});
119+
});
120+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
import { describe, it, expect, vi, beforeEach } from 'vitest';
2+
import { MessageHandler } from '../../src/handlers/message.handler';
3+
import { WhatsAppClient } from '../../src/client';
4+
import { WhatsAppMessage } from '../../src/types';
5+
6+
describe('MessageHandler', () => {
7+
let messageHandler: MessageHandler;
8+
let mockClient: WhatsAppClient;
9+
10+
beforeEach(() => {
11+
mockClient = {
12+
sendMessage: vi.fn(),
13+
} as any as WhatsAppClient;
14+
15+
messageHandler = new MessageHandler(mockClient);
16+
});
17+
18+
it('should successfully send a message', async () => {
19+
const mockMessage: WhatsAppMessage = {
20+
type: 'text',
21+
to: '1234567890',
22+
content: 'Test message'
23+
};
24+
25+
const mockResponse = {
26+
messaging_product: 'whatsapp',
27+
contacts: [{ input: '1234567890', wa_id: 'WHATSAPP_ID' }],
28+
messages: [{ id: 'MESSAGE_ID' }]
29+
};
30+
31+
(mockClient.sendMessage as any).mockResolvedValue({ data: mockResponse });
32+
33+
const result = await messageHandler.send(mockMessage);
34+
35+
expect(mockClient.sendMessage).toHaveBeenCalledWith(mockMessage);
36+
expect(result).toEqual(mockResponse);
37+
});
38+
39+
it('should handle client errors with error message', async () => {
40+
const mockMessage: WhatsAppMessage = {
41+
type: 'text',
42+
to: '1234567890',
43+
content: 'Test message'
44+
};
45+
46+
const errorMessage = 'API Error';
47+
(mockClient.sendMessage as any).mockRejectedValue(new Error(errorMessage));
48+
49+
await expect(messageHandler.send(mockMessage))
50+
.rejects
51+
.toThrow(`Failed to send WhatsApp message: ${errorMessage}`);
52+
});
53+
54+
it('should handle unknown errors', async () => {
55+
const mockMessage: WhatsAppMessage = {
56+
type: 'text',
57+
to: '1234567890',
58+
content: 'Test message'
59+
};
60+
61+
(mockClient.sendMessage as any).mockRejectedValue('Unknown error');
62+
63+
await expect(messageHandler.send(mockMessage))
64+
.rejects
65+
.toThrow('Failed to send WhatsApp message');
66+
});
67+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
2+
import { WebhookHandler } from '../../src/handlers/webhook.handler';
3+
import { WhatsAppClient } from '../../src/client';
4+
import { WhatsAppWebhookEvent } from '../../src/types';
5+
6+
describe('WebhookHandler', () => {
7+
let webhookHandler: WebhookHandler;
8+
let mockClient: WhatsAppClient;
9+
let consoleSpy: any;
10+
11+
beforeEach(() => {
12+
mockClient = {} as WhatsAppClient;
13+
webhookHandler = new WebhookHandler(mockClient);
14+
consoleSpy = vi.spyOn(console, 'log');
15+
});
16+
17+
afterEach(() => {
18+
consoleSpy.mockRestore();
19+
});
20+
21+
it('should handle message events correctly', async () => {
22+
const mockMessage = {
23+
from: '1234567890',
24+
id: 'msg_id',
25+
timestamp: '1234567890',
26+
text: {
27+
body: 'Test message'
28+
}
29+
};
30+
31+
const mockEvent: WhatsAppWebhookEvent = {
32+
object: 'whatsapp_business_account',
33+
entry: [{
34+
id: 'BUSINESS_ID',
35+
changes: [{
36+
value: {
37+
messaging_product: 'whatsapp',
38+
metadata: {
39+
display_phone_number: '1234567890',
40+
phone_number_id: 'PHONE_ID'
41+
},
42+
messages: [mockMessage]
43+
}
44+
}]
45+
}]
46+
};
47+
48+
await webhookHandler.handle(mockEvent);
49+
50+
expect(consoleSpy).toHaveBeenCalledWith('Received message:', mockMessage);
51+
});
52+
53+
it('should handle status updates correctly', async () => {
54+
const mockStatus = {
55+
id: 'status_id',
56+
status: 'delivered',
57+
timestamp: '1234567890',
58+
recipient_id: '1234567890'
59+
};
60+
61+
const mockEvent: WhatsAppWebhookEvent = {
62+
object: 'whatsapp_business_account',
63+
entry: [{
64+
id: 'BUSINESS_ID',
65+
changes: [{
66+
value: {
67+
messaging_product: 'whatsapp',
68+
metadata: {
69+
display_phone_number: '1234567890',
70+
phone_number_id: 'PHONE_ID'
71+
},
72+
statuses: [mockStatus]
73+
},
74+
field: ''
75+
}]
76+
}]
77+
};
78+
79+
await webhookHandler.handle(mockEvent);
80+
81+
expect(consoleSpy).toHaveBeenCalledWith('Received status update:', mockStatus);
82+
});
83+
84+
it('should handle events with both messages and statuses', async () => {
85+
const mockMessage = {
86+
from: '1234567890',
87+
id: 'msg_id',
88+
timestamp: '1234567890',
89+
text: {
90+
body: 'Test message'
91+
}
92+
};
93+
94+
const mockStatus = {
95+
id: 'status_id',
96+
status: 'delivered',
97+
timestamp: '1234567890',
98+
recipient_id: '1234567890'
99+
};
100+
101+
const mockEvent: WhatsAppWebhookEvent = {
102+
object: 'whatsapp_business_account',
103+
entry: [{
104+
id: 'BUSINESS_ID',
105+
changes: [{
106+
value: {
107+
messaging_product: 'whatsapp',
108+
metadata: {
109+
display_phone_number: '1234567890',
110+
phone_number_id: 'PHONE_ID'
111+
},
112+
messages: [mockMessage],
113+
statuses: [mockStatus]
114+
}
115+
}]
116+
}]
117+
};
118+
119+
await webhookHandler.handle(mockEvent);
120+
121+
expect(consoleSpy).toHaveBeenCalledWith('Received message:', mockMessage);
122+
expect(consoleSpy).toHaveBeenCalledWith('Received status update:', mockStatus);
123+
});
124+
125+
it('should handle errors correctly', async () => {
126+
const mockEvent = {} as WhatsAppWebhookEvent;
127+
128+
// The handler should not throw an error for an empty event
129+
await expect(webhookHandler.handle(mockEvent)).resolves.not.toThrow();
130+
131+
// Verify that no messages or statuses were processed
132+
expect(consoleSpy).not.toHaveBeenCalled();
133+
});
134+
});

packages/plugin-whatsapp/package.json

+4-5
Original file line numberDiff line numberDiff line change
@@ -22,20 +22,19 @@
2222
"scripts": {
2323
"build": "tsup --format esm --dts",
2424
"dev": "tsup --format esm --dts --watch",
25-
"test": "jest",
25+
"test": "vitest run",
26+
"coverage": "vitest run --coverage",
2627
"lint": "eslint --fix --cache ."
2728
},
2829
"dependencies": {
2930
"@elizaos/core": "workspace:*",
3031
"axios": "1.7.8"
3132
},
3233
"devDependencies": {
33-
"@types/jest": "29.5.14",
3434
"@types/node": "20.17.9",
3535
"@typescript-eslint/eslint-plugin": "8.16.0",
3636
"@typescript-eslint/parser": "8.16.0",
37-
"jest": "29.7.0",
38-
"ts-jest": "29.2.5",
39-
"typescript": "5.6.3"
37+
"typescript": "5.6.3",
38+
"vitest": "^1.2.1"
4039
}
4140
}

0 commit comments

Comments
 (0)