Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feature: adding tests for whatsapp plugin #2213

Merged
merged 1 commit into from
Jan 12, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
120 changes: 120 additions & 0 deletions packages/plugin-whatsapp/__tests__/client.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
import { describe, it, expect, vi, beforeEach } from 'vitest';
import axios from 'axios';
import { WhatsAppClient } from '../src/client';
import { WhatsAppConfig, WhatsAppMessage } from '../src/types';

vi.mock('axios', () => {
const mockPost = vi.fn();
return {
default: {
create: () => ({
post: mockPost
})
}
};
});

describe('WhatsAppClient', () => {
let client: WhatsAppClient;
let mockPost: any;

const mockConfig: WhatsAppConfig = {
accessToken: 'test-token',
phoneNumberId: 'test-phone-id',
webhookVerifyToken: 'test-webhook-token',
businessAccountId: 'test-business-id'
};

beforeEach(() => {
vi.clearAllMocks();
client = new WhatsAppClient(mockConfig);
mockPost = (axios.create() as any).post;
});

describe('sendMessage', () => {
it('should send a text message correctly', async () => {
const mockMessage: WhatsAppMessage = {
type: 'text',
to: '1234567890',
content: 'Hello, World!'
};

const expectedPayload = {
messaging_product: 'whatsapp',
recipient_type: 'individual',
to: mockMessage.to,
type: mockMessage.type,
text: { body: mockMessage.content }
};

const mockResponse = { data: { message_id: 'test-id' } };
mockPost.mockResolvedValue(mockResponse);

const response = await client.sendMessage(mockMessage);

expect(mockPost).toHaveBeenCalledWith(`/${mockConfig.phoneNumberId}/messages`, expectedPayload);
expect(response).toEqual(mockResponse);
});

it('should send a template message correctly', async () => {
const mockMessage: WhatsAppMessage = {
type: 'template',
to: '1234567890',
content: {
name: 'test_template',
language: {
code: 'en'
},
components: [{
type: 'body',
parameters: [{
type: 'text',
text: 'Test Parameter'
}]
}]
}
};

const expectedPayload = {
messaging_product: 'whatsapp',
recipient_type: 'individual',
to: mockMessage.to,
type: mockMessage.type,
template: mockMessage.content
};

const mockResponse = { data: { message_id: 'test-id' } };
mockPost.mockResolvedValue(mockResponse);

const response = await client.sendMessage(mockMessage);

expect(mockPost).toHaveBeenCalledWith(`/${mockConfig.phoneNumberId}/messages`, expectedPayload);
expect(response).toEqual(mockResponse);
});

it('should handle API errors correctly', async () => {
const mockMessage: WhatsAppMessage = {
type: 'text',
to: '1234567890',
content: 'Hello, World!'
};

const mockError = new Error('API Error');
mockPost.mockRejectedValue(mockError);

await expect(client.sendMessage(mockMessage)).rejects.toThrow('API Error');
});
});

describe('verifyWebhook', () => {
it('should verify webhook token correctly', async () => {
const result = await client.verifyWebhook(mockConfig.webhookVerifyToken!);
expect(result).toBe(true);
});

it('should reject invalid webhook token', async () => {
const result = await client.verifyWebhook('invalid-token');
expect(result).toBe(false);
});
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import { describe, it, expect, vi, beforeEach } from 'vitest';
import { MessageHandler } from '../../src/handlers/message.handler';
import { WhatsAppClient } from '../../src/client';
import { WhatsAppMessage } from '../../src/types';

describe('MessageHandler', () => {
let messageHandler: MessageHandler;
let mockClient: WhatsAppClient;

beforeEach(() => {
mockClient = {
sendMessage: vi.fn(),
} as any as WhatsAppClient;

messageHandler = new MessageHandler(mockClient);
});

it('should successfully send a message', async () => {
const mockMessage: WhatsAppMessage = {
type: 'text',
to: '1234567890',
content: 'Test message'
};

const mockResponse = {
messaging_product: 'whatsapp',
contacts: [{ input: '1234567890', wa_id: 'WHATSAPP_ID' }],
messages: [{ id: 'MESSAGE_ID' }]
};

(mockClient.sendMessage as any).mockResolvedValue({ data: mockResponse });

const result = await messageHandler.send(mockMessage);

expect(mockClient.sendMessage).toHaveBeenCalledWith(mockMessage);
expect(result).toEqual(mockResponse);
});

it('should handle client errors with error message', async () => {
const mockMessage: WhatsAppMessage = {
type: 'text',
to: '1234567890',
content: 'Test message'
};

const errorMessage = 'API Error';
(mockClient.sendMessage as any).mockRejectedValue(new Error(errorMessage));

await expect(messageHandler.send(mockMessage))
.rejects
.toThrow(`Failed to send WhatsApp message: ${errorMessage}`);
});

it('should handle unknown errors', async () => {
const mockMessage: WhatsAppMessage = {
type: 'text',
to: '1234567890',
content: 'Test message'
};

(mockClient.sendMessage as any).mockRejectedValue('Unknown error');

await expect(messageHandler.send(mockMessage))
.rejects
.toThrow('Failed to send WhatsApp message');
});
});
134 changes: 134 additions & 0 deletions packages/plugin-whatsapp/__tests__/handlers/webhook.handler.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
import { WebhookHandler } from '../../src/handlers/webhook.handler';
import { WhatsAppClient } from '../../src/client';
import { WhatsAppWebhookEvent } from '../../src/types';

describe('WebhookHandler', () => {
let webhookHandler: WebhookHandler;
let mockClient: WhatsAppClient;
let consoleSpy: any;

beforeEach(() => {
mockClient = {} as WhatsAppClient;
webhookHandler = new WebhookHandler(mockClient);
consoleSpy = vi.spyOn(console, 'log');
});

afterEach(() => {
consoleSpy.mockRestore();
});

it('should handle message events correctly', async () => {
const mockMessage = {
from: '1234567890',
id: 'msg_id',
timestamp: '1234567890',
text: {
body: 'Test message'
}
};

const mockEvent: WhatsAppWebhookEvent = {
object: 'whatsapp_business_account',
entry: [{
id: 'BUSINESS_ID',
changes: [{
value: {
messaging_product: 'whatsapp',
metadata: {
display_phone_number: '1234567890',
phone_number_id: 'PHONE_ID'
},
messages: [mockMessage]
}
}]
}]
};

await webhookHandler.handle(mockEvent);

expect(consoleSpy).toHaveBeenCalledWith('Received message:', mockMessage);
});

it('should handle status updates correctly', async () => {
const mockStatus = {
id: 'status_id',
status: 'delivered',
timestamp: '1234567890',
recipient_id: '1234567890'
};

const mockEvent: WhatsAppWebhookEvent = {
object: 'whatsapp_business_account',
entry: [{
id: 'BUSINESS_ID',
changes: [{
value: {
messaging_product: 'whatsapp',
metadata: {
display_phone_number: '1234567890',
phone_number_id: 'PHONE_ID'
},
statuses: [mockStatus]
},
field: ''
}]
}]
};

await webhookHandler.handle(mockEvent);

expect(consoleSpy).toHaveBeenCalledWith('Received status update:', mockStatus);
});

it('should handle events with both messages and statuses', async () => {
const mockMessage = {
from: '1234567890',
id: 'msg_id',
timestamp: '1234567890',
text: {
body: 'Test message'
}
};

const mockStatus = {
id: 'status_id',
status: 'delivered',
timestamp: '1234567890',
recipient_id: '1234567890'
};

const mockEvent: WhatsAppWebhookEvent = {
object: 'whatsapp_business_account',
entry: [{
id: 'BUSINESS_ID',
changes: [{
value: {
messaging_product: 'whatsapp',
metadata: {
display_phone_number: '1234567890',
phone_number_id: 'PHONE_ID'
},
messages: [mockMessage],
statuses: [mockStatus]
}
}]
}]
};

await webhookHandler.handle(mockEvent);

expect(consoleSpy).toHaveBeenCalledWith('Received message:', mockMessage);
expect(consoleSpy).toHaveBeenCalledWith('Received status update:', mockStatus);
});

it('should handle errors correctly', async () => {
const mockEvent = {} as WhatsAppWebhookEvent;

// The handler should not throw an error for an empty event
await expect(webhookHandler.handle(mockEvent)).resolves.not.toThrow();

// Verify that no messages or statuses were processed
expect(consoleSpy).not.toHaveBeenCalled();
});
});
9 changes: 4 additions & 5 deletions packages/plugin-whatsapp/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,20 +22,19 @@
"scripts": {
"build": "tsup --format esm --dts",
"dev": "tsup --format esm --dts --watch",
"test": "jest",
"test": "vitest run",
"coverage": "vitest run --coverage",
"lint": "eslint --fix --cache ."
},
"dependencies": {
"@elizaos/core": "workspace:*",
"axios": "1.7.8"
},
"devDependencies": {
"@types/jest": "29.5.14",
"@types/node": "20.17.9",
"@typescript-eslint/eslint-plugin": "8.16.0",
"@typescript-eslint/parser": "8.16.0",
"jest": "29.7.0",
"ts-jest": "29.2.5",
"typescript": "5.6.3"
"typescript": "5.6.3",
"vitest": "^1.2.1"
}
}
Loading