134 lines
5.6 KiB
Python

# -*- coding: utf-8 -*-
import hashlib
import hmac
from unittest.mock import patch, MagicMock
from odoo.tests.common import TransactionCase
class TestWhatsAppIntegration(TransactionCase):
def setUp(self):
super().setUp()
self.company = self.env.company
self.company.write({
'whatsapp_enabled': True,
'whatsapp_phone_number_id': '10987654321',
'whatsapp_access_token': 'test_wa_access_token_abc',
'whatsapp_verify_token': 'my_custom_verify_token_123',
'whatsapp_business_phone_number': '15550001111',
'meta_app_secret': 'super_secret_app_key',
})
self.partner = self.env['res.partner'].create({
'name': 'Sarah Customer',
'mobile': '+1 (555) 234-5678',
'email': 'sarah@example.com',
})
self.product = self.env['product.product'].create({
'name': 'Pasta Primavera',
'lst_price': 18.00,
})
# Ensure default template exists
self.template_confirmed = self.env['social.whatsapp.template'].search([
('event_type', '=', 'order_confirmed'),
('company_id', '=', self.company.id)
], limit=1)
if not self.template_confirmed:
self.template_confirmed = self.env['social.whatsapp.template'].create({
'name': 'Test Order Confirmed',
'event_type': 'order_confirmed',
'template_name': 'order_confirmation_v1',
'language_code': 'en_US',
'company_id': self.company.id,
'is_active': True,
})
def test_01_phone_sanitization(self):
"""Verify phone numbers are correctly sanitized to international digits."""
wa_api = self.env['social.whatsapp.api']
self.assertEqual(wa_api.format_phone('+1 (555) 234-5678'), '15552345678')
self.assertEqual(wa_api.format_phone('0555-123-456'), '0555123456')
def test_02_webhook_signature_verification(self):
"""Verify HMAC-SHA256 signature verification."""
wa_api = self.env['social.whatsapp.api']
app_secret = "super_secret_app_key"
payload = b'{"object": "whatsapp_business_account"}'
mac = hmac.new(app_secret.encode('utf-8'), payload, hashlib.sha256)
valid_header = f"sha256={mac.hexdigest()}"
self.assertTrue(wa_api.verify_webhook_signature(payload, valid_header, app_secret))
self.assertFalse(wa_api.verify_webhook_signature(payload, "sha256=invalid_hash", app_secret))
@patch('requests.post')
def test_03_order_confirmation_notification_dispatch(self, mock_post):
"""Verify confirming a sale order creates a notification and prevents duplicates."""
mock_response = MagicMock()
mock_response.status_code = 200
mock_response.json.return_value = {'messages': [{'id': 'wamid.HBgLMTU1NTIzNDU2NzgVAgASGBQzQTEx'}]}
mock_post.return_value = mock_response
order = self.env['sale.order'].create({
'partner_id': self.partner.id,
'order_line': [(0, 0, {
'product_id': self.product.id,
'product_uom_qty': 2,
'price_unit': 18.00,
})],
})
order.action_confirm()
notif = self.env['social.whatsapp.notification'].search([
('sale_order_id', '=', order.id),
('event_type', '=', 'order_confirmed')
], limit=1)
self.assertTrue(notif, "A WhatsApp notification record should be created upon order confirmation.")
self.assertEqual(notif.phone_number, '15552345678')
self.assertEqual(notif.state, 'sent')
self.assertEqual(notif.meta_message_id, 'wamid.HBgLMTU1NTIzNDU2NzgVAgASGBQzQTEx')
# Test Deduplication: Calling send_order_notification again must return existing record without creating a second one
duplicate_attempt = self.env['social.whatsapp.notification'].send_order_notification(
order=order, event_type='order_confirmed'
)
self.assertEqual(duplicate_attempt.id, notif.id)
count = self.env['social.whatsapp.notification'].search_count([
('sale_order_id', '=', order.id),
('event_type', '=', 'order_confirmed')
])
self.assertEqual(count, 1, "Duplicate notifications must be suppressed.")
def test_04_inbound_message_processing_and_order_matching(self):
"""Verify customer inbound WhatsApp messages are linked to partner and identified order."""
order = self.env['sale.order'].create({
'partner_id': self.partner.id,
'name': 'SO9999',
'order_line': [(0, 0, {
'product_id': self.product.id,
'product_uom_qty': 1,
'price_unit': 18.00,
})],
})
msg_payload = {
'id': 'wamid.HBgLMTU1NTIzNDU2NzgVAgASGBQzQTEx',
'from': '15552345678',
'type': 'text',
'text': {'body': 'Hello, where is my order SO9999?'}
}
msg_model = self.env['social.whatsapp.message']
msg = msg_model.process_incoming_message(msg_payload, company=self.company)
self.assertTrue(msg)
self.assertEqual(msg.partner_id.id, self.partner.id, "Customer partner should be automatically identified.")
self.assertEqual(msg.sale_order_id.id, order.id, "Order SO9999 should be automatically matched from message text.")
# Test duplicate webhook idempotence
dup_msg = msg_model.process_incoming_message(msg_payload, company=self.company)
self.assertEqual(dup_msg.id, msg.id, "Duplicate webhook events with same wamid must be ignored.")