[IMP] *: add generic make_jsonrpc method in tests

*: bus, crm_livechat, crm_mail_plugin, im_livechat, payment, payment_demo,
pos_online_payment, test_discuss_full, test_mail_full, tests, website_livechat,
website_sale.

Several python tests have their own way to make jsonrpc requests. This PR adds a
generic `make_jsonrpc_request` method to the `HttpCase` in order to provide a
generic way to do so.

At the same time, calls to `_open_livechat_channel` are removed in favor of
jsonrpc request to `get_session`. This makes the tests more realistics (some
incoherences were present like passing `country_id` to the open channel method
while the user country id is not set...)

Finally, this will ease the diff in the PR introducing livechat visitors as mail
guests.

part of task-3332628

closes odoo/odoo#130036

Related: odoo/enterprise#44840
Signed-off-by: Sébastien Theys (seb) <seb@odoo.com>
This commit is contained in:
tsm-odoo
2023-07-31 23:21:10 +02:00
parent 8f68a146bf
commit b5896c9d03
20 changed files with 221 additions and 261 deletions
+22 -44
View File
@@ -2,32 +2,19 @@
import json
from odoo.tests import common
from odoo.tests import common, JsonRpcException
class TestWebsocketController(common.HttpCase):
def _make_rpc(self, route, params, headers=None):
data = json.dumps({
'id': 0,
'jsonrpc': '2.0',
'method': 'call',
'params': params,
}).encode()
headers = headers or {}
headers['Content-Type'] = 'application/json'
return self.url_open(route, data, headers=headers)
def test_websocket_peek(self):
response = json.loads(
self._make_rpc('/websocket/peek_notifications', {
'channels': [],
'last': 0,
'is_first_poll': True,
}).content.decode()
)
result = self.make_jsonrpc_request('/websocket/peek_notifications', {
'channels': [],
'last': 0,
'is_first_poll': True,
})
# Response containing channels/notifications is retrieved and is
# conform to excpectations.
result = response.get('result')
self.assertIsNotNone(result)
channels = result.get('channels')
self.assertIsNotNone(channels)
@@ -36,20 +23,19 @@ class TestWebsocketController(common.HttpCase):
self.assertIsNotNone(notifications)
self.assertIsInstance(notifications, list)
response = json.loads(
self._make_rpc('/websocket/peek_notifications', {
'channels': [],
'last': 0,
'is_first_poll': False,
}).content.decode()
)
result = self.make_jsonrpc_request('/websocket/peek_notifications', {
'channels': [],
'last': 0,
'is_first_poll': False,
})
# Reponse is received as long as the session is valid.
self.assertIn('result', response)
self.assertIsNotNone(result)
def test_websocket_peek_session_expired_login(self):
session = self.authenticate(None, None)
# first rpc should be fine
self._make_rpc('/websocket/peek_notifications', {
self.make_jsonrpc_request('/websocket/peek_notifications', {
'channels': [],
'last': 0,
'is_first_poll': True,
@@ -58,21 +44,17 @@ class TestWebsocketController(common.HttpCase):
self.authenticate('admin', 'admin')
# rpc with outdated session should lead to error.
headers = {'Cookie': f'session_id={session.sid};'}
response = json.loads(
self._make_rpc('/websocket/peek_notifications', {
with self.assertRaises(JsonRpcException, msg='odoo.http.SessionExpiredException'):
self.make_jsonrpc_request('/websocket/peek_notifications', {
'channels': [],
'last': 0,
'is_first_poll': False,
}, headers=headers).content.decode()
)
error = response.get('error')
self.assertIsNotNone(error, 'Sending a poll with an outdated session should lead to error')
self.assertEqual('odoo.http.SessionExpiredException', error['data']['name'])
}, headers=headers)
def test_websocket_peek_session_expired_logout(self):
session = self.authenticate('demo', 'demo')
# first rpc should be fine
self._make_rpc('/websocket/peek_notifications', {
self.make_jsonrpc_request('/websocket/peek_notifications', {
'channels': [],
'last': 0,
'is_first_poll': True,
@@ -80,13 +62,9 @@ class TestWebsocketController(common.HttpCase):
self.url_open('/web/session/logout')
# rpc with outdated session should lead to error.
headers = {'Cookie': f'session_id={session.sid};'}
response = json.loads(
self._make_rpc('/websocket/peek_notifications', {
with self.assertRaises(JsonRpcException, msg='odoo.http.SessionExpiredException'):
self.make_jsonrpc_request('/websocket/peek_notifications', {
'channels': [],
'last': 0,
'is_first_poll': False,
}, headers=headers).content.decode()
)
error = response.get('error')
self.assertIsNotNone(error, 'Sending a poll with an outdated session should lead to error')
self.assertEqual('odoo.http.SessionExpiredException', error['data']['name'])
}, headers=headers)
@@ -33,8 +33,12 @@ class CrmChatbotCase(chatbot_common.CrmChatbotCase):
self.assertEqual(created_lead.type, 'lead')
def _chatbot_create_lead(self, user):
channel_info = self.livechat_channel._open_livechat_discuss_channel(
anonymous_name='Test Visitor', chatbot_script=self.chatbot_script, user_id=user.id)
channel_info = self.make_jsonrpc_request("/im_livechat/get_session", {
'anonymous_name': 'Test Visitor',
'channel_id': self.livechat_channel.id,
'chatbot_script_id': self.chatbot_script.id,
'user_id': user.id,
})
discuss_channel = self.env['discuss.channel'].sudo().browse(channel_info['id'])
self._post_answer_and_trigger_next_step(
@@ -15,7 +15,7 @@ class TestCrmMailPlugin(TestMailPluginControllerCommon):
{"name": "Partner 2"},
])
result = self._make_rpc_call("/mail_plugin/partner/get", {"partner_id": partner.id})
result = self.make_jsonrpc_request("/mail_plugin/partner/get", {"partner_id": partner.id})
self.assertNotIn("leads", result,
msg="The user has no access to crm.lead, the leads section should not be visible")
@@ -27,7 +27,7 @@ class TestCrmMailPlugin(TestMailPluginControllerCommon):
{"name": "Lead Partner 2", "partner_id": partner_2.id},
])
result = self._make_rpc_call("/mail_plugin/partner/get", {"partner_id": partner.id})
result = self.make_jsonrpc_request("/mail_plugin/partner/get", {"partner_id": partner.id})
self.assertIn(
"leads",
@@ -71,7 +71,7 @@ class TestCrmMailPlugin(TestMailPluginControllerCommon):
'email_subject': 'test subject',
}
result = self._make_rpc_call('/mail_plugin/lead/create', params)
result = self.make_jsonrpc_request('/mail_plugin/lead/create', params)
# Check that the created lead record has the correct company and return the lead_id
self.assertIn(
@@ -87,27 +87,3 @@ class TestCrmMailPlugin(TestMailPluginControllerCommon):
company_b,
msg='The created record should belong to company_B',
)
def _make_rpc_call(self, url, params):
"""
Makes an RPC call to the specified URL with the given parameters, and returns the 'result' key from the response.
:param params (dict): A dictionary containing the parameters to send in the RPC call.
:param url (str): The URL to send the RPC call to.
:return (dict): The 'result' key from the response.
"""
data = {
'id': 0,
'jsonrpc': '2.0',
'method': 'call',
'params': params,
}
response = self.url_open(
url,
data=json.dumps(data).encode(),
headers={'Content-Type': 'application/json'}
).json()
return response['result']
+1 -1
View File
@@ -4,7 +4,7 @@
from odoo.tests import common
class ChatbotCase(common.TransactionCase):
class ChatbotCase(common.HttpCase):
@classmethod
def setUpClass(cls):
+4 -2
View File
@@ -1,16 +1,18 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo.tests.common import TransactionCase
from odoo.tests.common import HttpCase
class TestImLivechatCommon(TransactionCase):
class TestImLivechatCommon(HttpCase):
@classmethod
def setUpClass(cls):
super().setUpClass()
cls.password = 'Pl1bhD@2!kXZ'
cls.operators = cls.env['res.users'].create([{
'name': 'Michel',
'login': 'michel',
'password': cls.password,
'livechat_username': "Michel Operator",
'email': 'michel@example.com',
}, {
@@ -44,8 +44,11 @@ class ChatbotCase(chatbot_common.ChatbotCase):
"Only step 'step_no_one_available' should be flagged as forward operator child.")
def test_chatbot_steps(self):
channel_info = self.livechat_channel._open_livechat_discuss_channel(
anonymous_name='Test Visitor', chatbot_script=self.chatbot_script)
channel_info = self.make_jsonrpc_request("/im_livechat/get_session", {
'anonymous_name': 'Test Visitor',
'chatbot_script_id': self.chatbot_script.id,
'channel_id': self.livechat_channel.id,
})
discuss_channel = self.env['discuss.channel'].browse(channel_info['id'])
self.assertEqual(discuss_channel.chatbot_current_step_id, self.step_dispatch)
@@ -2,6 +2,7 @@
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from datetime import timedelta
from freezegun import freeze_time
from unittest.mock import patch, PropertyMock
from odoo import fields
from odoo.addons.im_livechat.tests.common import TestImLivechatCommon
@@ -22,11 +23,20 @@ class TestGetDiscussChannel(TestImLivechatCommon):
def test_channel_get_livechat_visitor_info(self):
belgium = self.env.ref('base.be')
public_user = self.env.ref('base.public_user')
test_user = self.env['res.users'].create({'name': 'Roger', 'login': 'roger', 'country_id': belgium.id})
test_user = self.env['res.users'].create({'name': 'Roger', 'login': 'roger', 'password': self.password, 'country_id': belgium.id})
# ensure visitor info are correct with anonymous
operator = self.operators[0]
channel_info = self.livechat_channel.with_user(public_user).sudo()._open_livechat_discuss_channel(anonymous_name='Visitor 22', previous_operator_id=operator.partner_id.id, country_id=belgium.id)
with patch('odoo.http.GeoIP.country_code', new_callable=PropertyMock(return_value=belgium.code)):
channel_info = self.make_jsonrpc_request(
'/im_livechat/get_session',
{
'anonymous_name': 'Visitor 22',
'previous_operator_id': operator.partner_id.id,
'channel_id': self.livechat_channel.id,
'country_id': belgium.id,
},
)
self.assertEqual(channel_info['channel']['anonymous_name'], "Visitor 22")
self.assertEqual(channel_info['channel']['anonymous_country'], {'code': 'BE', 'id': belgium.id, 'name': 'Belgium'})
@@ -34,7 +44,7 @@ class TestGetDiscussChannel(TestImLivechatCommon):
# shape of channelMembers is [('insert', data...)], [0][1] accesses the data
self.assertEqual(sorted(map(lambda m: m['persona']['partner'], channel_info['channel']['channelMembers'][0][1]), key=lambda m: m['id']), sorted([{
'active': True,
'country': [('clear',)],
'country': [['clear']],
'id': operator.partner_id.id,
'is_bot': False,
'is_public': False,
@@ -48,17 +58,23 @@ class TestGetDiscussChannel(TestImLivechatCommon):
}], key=lambda m: m['id']))
# ensure visitor info are correct with real user
channel_info = self.livechat_channel.with_user(test_user).sudo()._open_livechat_discuss_channel(anonymous_name='whatever', previous_operator_id=operator.partner_id.id, user_id=test_user.id)
self.authenticate(test_user.login, self.password)
channel_info = self.make_jsonrpc_request('/im_livechat/get_session', {
'anonymous_name': 'whatever',
'previous_operator_id': operator.partner_id.id,
'user_id': test_user.id,
'channel_id': self.livechat_channel.id,
})
self.assertFalse(channel_info['channel']['anonymous_name'])
self.assertEqual(channel_info['channel']['anonymous_country'], [('clear',)])
self.assertEqual(channel_info['channel']['channelMembers'], [('insert', [
self.assertEqual(channel_info['channel']['anonymous_country'], {'code': 'BE', 'id': belgium.id, 'name': 'Belgium'})
self.assertEqual(channel_info['channel']['channelMembers'], [['insert', [
{
'channel': {'id': channel_info['id']},
'id': self.env['discuss.channel.member'].search([('channel_id', '=', channel_info['id']), ('partner_id', '=', operator.partner_id.id)]).id,
'persona': {
'partner': {
'active': True,
'country': [('clear',)],
'country': [['clear']],
'id': operator.partner_id.id,
'is_bot': False,
'is_public': False,
@@ -84,22 +100,28 @@ class TestGetDiscussChannel(TestImLivechatCommon):
},
},
},
])])
]]])
# ensure visitor info are correct when operator is testing themselves
operator = self.operators[0]
channel_info = self.livechat_channel.with_user(operator)._open_livechat_discuss_channel(anonymous_name='whatever', previous_operator_id=operator.partner_id.id, user_id=operator.id)
self.assertEqual(channel_info['operator_pid'], (operator.partner_id.id, "Michel Operator"))
self.authenticate(operator.login, self.password)
channel_info = self.make_jsonrpc_request('/im_livechat/get_session', {
'anonymous_name': 'whatever',
'previous_operator_id': operator.partner_id.id,
'user_id': operator.id,
'channel_id': self.livechat_channel.id,
})
self.assertEqual(channel_info['operator_pid'], [operator.partner_id.id, "Michel Operator"])
self.assertFalse(channel_info['channel']['anonymous_name'])
self.assertEqual(channel_info['channel']['anonymous_country'], [('clear',)])
self.assertEqual(channel_info['channel']['channelMembers'], [('insert', [
self.assertEqual(channel_info['channel']['anonymous_country'], [['clear']])
self.assertEqual(channel_info['channel']['channelMembers'], [['insert', [
{
'channel': {'id': channel_info['id']},
'id': self.env['discuss.channel.member'].search([('channel_id', '=', channel_info['id']), ('partner_id', '=', operator.partner_id.id)]).id,
'persona': {
'partner': {
'active': True,
'country': [('clear',)],
'country': [['clear']],
'id': operator.partner_id.id,
'is_bot': False,
'is_public': False,
@@ -107,13 +129,13 @@ class TestGetDiscussChannel(TestImLivechatCommon):
},
},
},
])])
]]])
def _open_livechat_discuss_channel(self):
discuss_channels = []
for i in range(5):
discuss_channel = self.livechat_channel._open_livechat_discuss_channel('Anonymous')
discuss_channel = self.make_jsonrpc_request('/im_livechat/get_session', {'anonymous_name': 'Anonymous', 'channel_id': self.livechat_channel.id})
discuss_channels.append(discuss_channel)
# send a message to mark this channel as 'active'
self.env['discuss.channel'].browse(discuss_channel['id']).message_post(body='cc')
@@ -122,7 +144,7 @@ class TestGetDiscussChannel(TestImLivechatCommon):
def test_channel_not_pinned_for_operator_before_first_message(self):
public_user = self.env.ref('base.public_user')
channel_info = self.livechat_channel.with_user(public_user).sudo()._open_livechat_discuss_channel(anonymous_name='whatever')
channel_info = self.make_jsonrpc_request('/im_livechat/get_session', {'anonymous_name': 'whatever', 'channel_id': self.livechat_channel.id})
operator_channel_member = self.env['discuss.channel.member'].search([('channel_id', '=', channel_info['id']), ('partner_id', 'in', self.operators.partner_id.ids)])
self.assertEqual(len(operator_channel_member), 1, "operator should be member of channel")
self.assertFalse(operator_channel_member.is_pinned, "channel should not be pinned for operator initially")
@@ -139,7 +161,7 @@ class TestGetDiscussChannel(TestImLivechatCommon):
'email': 'michel@example.com',
'livechat_username': 'Michel at your service',
})
channel_info = self.livechat_channel.with_user(public_user).sudo()._open_livechat_discuss_channel(anonymous_name='whatever')
channel_info = self.make_jsonrpc_request('/im_livechat/get_session', {'anonymous_name': 'whatever', 'channel_id': self.livechat_channel.id})
channel = self.env['discuss.channel'].browse(channel_info['id'])
channel.with_user(operator).message_post(body='Hello', message_type='comment', subtype_xmlid='mail.mt_comment')
message_formats = channel.with_user(public_user).sudo()._channel_fetch_message()
@@ -150,7 +172,7 @@ class TestGetDiscussChannel(TestImLivechatCommon):
def test_read_channel_unpined_for_operator_after_one_day(self):
public_user = self.env.ref('base.public_user')
channel_info = self.livechat_channel.with_user(public_user).sudo()._open_livechat_discuss_channel(anonymous_name='visitor')
channel_info = self.make_jsonrpc_request('/im_livechat/get_session', {'anonymous_name': 'visitor', 'channel_id': self.livechat_channel.id})
member_of_operator = self.env['discuss.channel.member'].search([('channel_id', '=', channel_info['id']), ('partner_id', 'in', self.operators.partner_id.ids)])
message = self.env['discuss.channel'].browse(channel_info['id']).message_post(body='cc')
member_of_operator.channel_id.with_user(self.operators.filtered(
@@ -162,7 +184,7 @@ class TestGetDiscussChannel(TestImLivechatCommon):
def test_unread_channel_not_unpined_for_operator_after_autovacuum(self):
public_user = self.env.ref('base.public_user')
channel_info = self.livechat_channel.with_user(public_user).sudo()._open_livechat_discuss_channel(anonymous_name='visitor')
channel_info = self.make_jsonrpc_request('/im_livechat/get_session', {'anonymous_name': 'visitor', 'channel_id': self.livechat_channel.id})
member_of_operator = self.env['discuss.channel.member'].search([('channel_id', '=', channel_info['id']), ('partner_id', 'in', self.operators.partner_id.ids)])
self.env['discuss.channel'].browse(channel_info['id']).message_post(body='cc')
with freeze_time(fields.Datetime.to_string(fields.datetime.now() + timedelta(days=1))):
@@ -7,31 +7,30 @@ from odoo.addons.im_livechat.tests.common import TestImLivechatCommon
class TestImLivechatReport(TestImLivechatCommon):
@classmethod
def setUpClass(cls):
super().setUpClass()
cls.env['discuss.channel'].search([('livechat_channel_id', '!=', False)]).unlink()
def setUp(self):
super().setUp()
self.env['discuss.channel'].search([('livechat_channel_id', '!=', False)]).unlink()
def _compute_available_operator_ids(channel_self):
for record in channel_self:
record.available_operator_ids = cls.operators
record.available_operator_ids = self.operators
with patch.object(type(cls.env['im_livechat.channel']), '_compute_available_operator_ids', _compute_available_operator_ids):
channel_id = cls.livechat_channel._open_livechat_discuss_channel('Anonymous')['id']
with patch.object(type(self.env['im_livechat.channel']), '_compute_available_operator_ids', _compute_available_operator_ids):
channel_id = self.make_jsonrpc_request("/im_livechat/get_session", {'anonymous_name': 'Anonymous', 'channel_id': self.livechat_channel.id})['id']
channel = cls.env['discuss.channel'].browse(channel_id)
cls.operator = channel.livechat_operator_id
channel = self.env['discuss.channel'].browse(channel_id)
self.operator = channel.livechat_operator_id
cls._create_message(channel, cls.visitor_user.partner_id, '2023-03-17 06:05:54')
cls._create_message(channel, cls.operator, '2023-03-17 08:15:54')
cls._create_message(channel, cls.operator, '2023-03-17 08:45:54')
self._create_message(channel, self.visitor_user.partner_id, '2023-03-17 06:05:54')
self._create_message(channel, self.operator, '2023-03-17 08:15:54')
self._create_message(channel, self.operator, '2023-03-17 08:45:54')
# message with the same record id, but with a different model
# should not be taken into account for statistics
partner_message = cls._create_message(channel, cls.operator, '2023-03-17 05:05:54')
partner_message |= cls._create_message(channel, cls.operator, '2023-03-17 09:15:54')
partner_message = self._create_message(channel, self.operator, '2023-03-17 05:05:54')
partner_message |= self._create_message(channel, self.operator, '2023-03-17 09:15:54')
partner_message.model = 'res.partner'
cls.env['mail.message'].flush_model()
self.env['mail.message'].flush_model()
def test_im_livechat_report_channel(self):
report = self.env['im_livechat.report.channel'].search([('livechat_channel_id', '=', self.livechat_channel.id)])
+11 -4
View File
@@ -4,13 +4,14 @@
from markupsafe import Markup
from odoo import Command
from odoo.tests.common import users, tagged, TransactionCase
from odoo.tests.common import users, tagged, HttpCase
@tagged('post_install', '-at_install')
class TestImLivechatMessage(TransactionCase):
class TestImLivechatMessage(HttpCase):
def setUp(self):
super().setUp()
self.password = 'Pl1bhD@2!kXZ'
self.users = self.env['res.users'].create([
{
'email': 'e.e@example.com',
@@ -21,14 +22,20 @@ class TestImLivechatMessage(TransactionCase):
'odoobot_state': 'disabled',
'signature': '--\nErnest',
},
{'name': 'test1', 'login': 'test1', 'email': 'test1@example.com'},
{'name': 'test1', 'login': 'test1', 'password': self.password, 'email': 'test1@example.com'},
])
@users('emp')
def test_message_format(self):
im_livechat_channel = self.env['im_livechat.channel'].sudo().create({'name': 'support', 'user_ids': [Command.link(self.users[0].id)]})
self.env['bus.presence'].create({'user_id': self.users[0].id, 'status': 'online'}) # make available for livechat (ignore leave)
channel_livechat_1 = self.env['discuss.channel'].browse(im_livechat_channel._open_livechat_discuss_channel(anonymous_name='anon 1', previous_operator_id=self.users[0].partner_id.id, user_id=self.users[1].id, country_id=self.env.ref('base.in').id)['id'])
self.authenticate(self.users[1].login, self.password)
channel_livechat_1 = self.env['discuss.channel'].browse(self.make_jsonrpc_request("/im_livechat/get_session", {
'anonymous_name': 'anon 1',
'previous_operator_id': self.users[0].partner_id.id,
'country_id': self.env.ref('base.in').id,
'channel_id': im_livechat_channel.id,
})['id'])
record_rating = self.env['rating.rating'].create({
'res_model_id': self.env['ir.model']._get('discuss.channel').id,
'res_id': channel_livechat_1.id,
+9 -25
View File
@@ -1,12 +1,13 @@
# Part of Odoo. See LICENSE file for full copyright and licensing details.
import json
from contextlib import contextmanager
from uuid import uuid4
from lxml import etree, objectify
from werkzeug import urls
from odoo.tests import HttpCase
from odoo.tests import HttpCase, JsonRpcException
from odoo.addons.payment.tests.common import PaymentCommon
@@ -69,20 +70,11 @@ class PaymentHttpCommon(PaymentCommon, HttpCase):
"""
return self.opener.post(url, json=data)
def _make_json_rpc_request(self, url, data=None):
""" Make a JSON-RPC request to the provided URL.
:param str url: The URL to make the request to
:param dict data: The data to be send in the request body in JSON-RPC 2.0 format
:return: The response of the request
:rtype: :class:`requests.models.Response`
"""
return self.opener.post(url, json={
'jsonrpc': '2.0',
'method': 'call',
'id': str(uuid4()),
'params': data,
})
@contextmanager
def _assertNotFound(self):
with self.assertRaises(JsonRpcException) as cm:
yield
self.assertEqual(cm.exception.code, 404)
def _get_tx_context(self, response, form_name):
"""Extracts txContext & other form info (provider & token ids)
@@ -226,15 +218,7 @@ class PaymentHttpCommon(PaymentCommon, HttpCase):
"""
uri = '/payment/transaction'
url = self._build_url(uri)
response = self._make_json_rpc_request(url, route_kwargs)
self.assertEqual(response.status_code, 200) # Check the request went through.
return response
return self.make_jsonrpc_request(url, route_kwargs)
def _get_processing_values(self, **route_kwargs):
response = self._portal_transaction(**route_kwargs)
self.assertEqual(response.status_code, 200)
resp_content = json.loads(response.content)
return resp_content['result']
return self._portal_transaction(**route_kwargs)
+8 -11
View File
@@ -5,7 +5,7 @@ from unittest.mock import patch
from freezegun import freeze_time
from odoo.tests import tagged
from odoo.tests import tagged, JsonRpcException
from odoo.tools import mute_logger
from odoo.addons.payment.controllers.portal import PaymentPortal
@@ -287,22 +287,19 @@ class TestFlows(PaymentHttpCommon):
'landing_route': 'whatever',
})
# Transaction step with a wrong flow --> UserError
with mute_logger('odoo.http'):
response = self._portal_transaction(**transaction_values)
self.assertIn(
"odoo.exceptions.UserError: The payment should either be direct, with redirection, or made by a token.",
response.text)
with mute_logger("odoo.http"), self.assertRaises(
JsonRpcException,
msg='odoo.exceptions.UserError: The payment should either be direct, with redirection, or made by a token.',
):
self._portal_transaction(**transaction_values)
def test_transaction_wrong_token(self):
route_values = self._prepare_pay_values()
route_values['access_token'] = "abcde"
# Transaction step with a wrong access token --> ValidationError
with mute_logger('odoo.http'):
response = self._portal_transaction(**route_values)
self.assertIn(
"odoo.exceptions.ValidationError: The access token is invalid.",
response.text)
with mute_logger('odoo.http'), self.assertRaises(JsonRpcException, msg='odoo.exceptions.ValidationError: The access token is invalid.'):
self._portal_transaction(**route_values)
def test_access_disabled_providers_tokens(self):
self.partner = self.portal_partner
@@ -128,6 +128,6 @@ class TestMultiCompanyFlows(PaymentHttpCommon):
# Archive token in company A
url = self._build_url('/payment/archive_token')
self._make_json_rpc_request(url, {'token_id': token.id})
self.make_jsonrpc_request(url, {'token_id': token.id})
self.assertFalse(token.active)
@@ -20,5 +20,5 @@ class TestProcessingFlows(PaymentDemoCommon, PaymentHttpCommon):
'odoo.addons.payment.models.payment_transaction.PaymentTransaction'
'._handle_notification_data'
) as handle_notification_data_mock:
self._make_json_rpc_request(url, data=self.notification_data)
self.make_jsonrpc_request(url, params=self.notification_data)
self.assertEqual(handle_notification_data_mock.call_count, 1)
@@ -23,10 +23,7 @@ class OnlinePaymentCommon(PaymentHttpCommon):
def _fake_request_pos_order_pay_transaction_page(self, pos_order_id, route_values):
uri = f'/pos/pay/transaction/{pos_order_id}'
url = self._build_url(uri)
response = self._make_json_rpc_request(url, route_values)
self.assertEqual(response.status_code, 200)
resp_content = json.loads(response.content)
return resp_content['result']
return self.make_jsonrpc_request(url, route_values)
def _fake_open_pos_order_pay_confirmation_page(self, pos_order_id, access_token, tx_id):
self._fake_http_get_request(PaymentPortal._get_landing_route(pos_order_id, access_token, tx_id=tx_id))
@@ -3,17 +3,19 @@
from datetime import date
from dateutil.relativedelta import relativedelta
from unittest.mock import patch, PropertyMock
from odoo import Command
from odoo.tests.common import users, tagged, TransactionCase, warmup
from odoo.tests.common import users, tagged, HttpCase, warmup
from odoo.tools.misc import DEFAULT_SERVER_DATETIME_FORMAT
@tagged('post_install', '-at_install')
class TestDiscussFullPerformance(TransactionCase):
class TestDiscussFullPerformance(HttpCase):
def setUp(self):
super().setUp()
self.group_user = self.env.ref('base.group_user')
self.password = 'Pl1bhD@2!kXZ'
self.users = self.env['res.users'].create([
{
'email': 'e.e@example.com',
@@ -24,7 +26,7 @@ class TestDiscussFullPerformance(TransactionCase):
'odoobot_state': 'disabled',
'signature': '--\nErnest',
},
{'name': 'test1', 'login': 'test1', 'email': 'test1@example.com'},
{'name': 'test1', 'login': 'test1', 'password': self.password, 'email': 'test1@example.com', 'country_id': self.env.ref('base.in').id},
{'name': 'test2', 'login': 'test2', 'email': 'test2@example.com'},
{'name': 'test3', 'login': 'test3'},
{'name': 'test4', 'login': 'test4'},
@@ -82,9 +84,20 @@ class TestDiscussFullPerformance(TransactionCase):
# create livechats
im_livechat_channel = self.env['im_livechat.channel'].sudo().create({'name': 'support', 'user_ids': [Command.link(self.users[0].id)]})
self.env['bus.presence'].create({'user_id': self.users[0].id, 'status': 'online'}) # make available for livechat (ignore leave)
self.channel_livechat_1 = self.env['discuss.channel'].browse(im_livechat_channel._open_livechat_discuss_channel(anonymous_name='anon 1', previous_operator_id=self.users[0].partner_id.id, user_id=self.users[1].id, country_id=self.env.ref('base.in').id)['id'])
self.authenticate('test1', self.password)
self.channel_livechat_1 = self.env['discuss.channel'].browse(self.make_jsonrpc_request("/im_livechat/get_session", {
'anonymous_name': 'anon 1',
'channel_id': im_livechat_channel.id,
'previous_operator_id': self.users[0].partner_id.id,
})['id'])
self.channel_livechat_1.with_user(self.users[1]).message_post(body="test")
self.channel_livechat_2 = self.env['discuss.channel'].browse(im_livechat_channel.with_user(self.env.ref('base.public_user'))._open_livechat_discuss_channel(anonymous_name='anon 2', previous_operator_id=self.users[0].partner_id.id, country_id=self.env.ref('base.be').id)['id'])
self.authenticate(None, None)
with patch("odoo.http.GeoIP.country_code", new_callable=PropertyMock(return_value=self.env.ref('base.be').code)):
self.channel_livechat_2 = self.env['discuss.channel'].browse(self.make_jsonrpc_request("/im_livechat/get_session", {
'anonymous_name': 'anon 2',
'channel_id': im_livechat_channel.id,
'previous_operator_id': self.users[0].partner_id.id,
})['id'])
self.channel_livechat_2.with_user(self.env.ref('base.public_user')).sudo().message_post(body="test")
# add needaction
self.users[0].notification_type = 'inbox'
@@ -830,7 +843,11 @@ class TestDiscussFullPerformance(TransactionCase):
'persona': {
'partner': {
'active': True,
'country': [('clear',)],
'country': {
'code': 'IN',
'id': self.env.ref('base.in').id,
'name': 'India',
},
'id': self.users[1].partner_id.id,
'is_bot': False,
'is_public': False,
@@ -844,7 +861,7 @@ class TestDiscussFullPerformance(TransactionCase):
'memberCount': 2,
'message_unread_counter': 0,
},
'create_uid': self.env.user.id,
'create_uid': self.users[1].id,
'defaultDisplayMode': False,
'description': False,
'group_based_subscription': False,
@@ -1016,4 +1033,4 @@ class TestDiscussFullPerformance(TransactionCase):
Returns the expected query count.
The point of having a separate getter is to allow it to be overriden.
"""
return 68
return 71
+4 -15
View File
@@ -81,24 +81,13 @@ class TestPortalControllers(TestPortal):
"""Test retrieving chatter messages through the portal controller"""
self.authenticate(None, None)
message_fetch_url = '/mail/chatter_fetch'
payload = json.dumps({
'jsonrpc': '2.0',
'method': 'call',
'id': 0,
'params': {
def get_chatter_message_count():
return self.make_jsonrpc_request(message_fetch_url, {
'res_model': 'mail.test.portal',
'res_id': self.record_portal.id,
'token': self.record_portal.access_token,
},
})
def get_chatter_message_count():
res = self.url_open(
url=message_fetch_url,
data=payload,
headers={'Content-Type': 'application/json'}
)
return res.json().get('result', {}).get('message_count', 0)
}).get('message_count', 0)
self.assertEqual(get_chatter_message_count(), 0)
@@ -7,7 +7,7 @@ from odoo.addons.website_livechat.tests.common import TestLivechatCommon
@tests.tagged('post_install', '-at_install')
class TestLivechatChatbotUI(tests.HttpCase, TestLivechatCommon, ChatbotCase):
class TestLivechatChatbotUI(TestLivechatCommon, ChatbotCase):
def setUp(self):
super().setUp()
self.env['im_livechat.channel'].search([
@@ -11,6 +11,7 @@ from odoo.tests import tagged
from odoo.addons.base.tests.common import HttpCaseWithUserDemo
from odoo.addons.website_sale.controllers.delivery import WebsiteSaleDelivery as WebsiteSaleDeliveryController
from odoo.addons.website_sale.controllers.main import WebsiteSale
@tagged('post_install', '-at_install')
@@ -107,31 +108,6 @@ class TestWebsiteSaleDeliveryExpressCheckoutFlows(HttpCaseWithUserDemo):
'state': self.user_demo.partner_id.state_id.code,
}
def _make_json_rpc_request(self, url, data=None):
""" Make a JSON-RPC request to the provided URL.
:param str url: The URL to make the request to
:param dict data: The data to be send in the request body in JSON-RPC 2.0 format
:return dict: The result of the JSON-RPC request
"""
rpc_request = {
"jsonrpc": "2.0",
"method": "call",
"id": str(uuid4()),
"params": data,
}
result = self.url_open(
url,
data=json.dumps(rpc_request).encode(),
headers={"Content-Type": "application/json"},
)
if not result.ok:
return {}
return result.json().get("result", {})
def test_express_checkout_public_user_shipping_address_change(self):
""" Test that when using express checkout as a public user and selecting a shipping address,
a new partner is created if the partner of the SO is the public partner.
@@ -143,10 +119,10 @@ class TestWebsiteSaleDeliveryExpressCheckoutFlows(HttpCaseWithUserDemo):
'odoo.addons.delivery.models.delivery_carrier.DeliveryCarrier.rate_shipment',
return_value=self.rate_shipment_result
):
self._make_json_rpc_request(
self.make_jsonrpc_request(
urls.url_join(
self.base_url(), WebsiteSaleDeliveryController._express_checkout_shipping_route
), data={
), params={
'partial_shipping_address': dict(self.express_checkout_anonymized_shipping_values)
}
)
@@ -175,18 +151,18 @@ class TestWebsiteSaleDeliveryExpressCheckoutFlows(HttpCaseWithUserDemo):
'odoo.addons.delivery.models.delivery_carrier.DeliveryCarrier.rate_shipment',
return_value=self.rate_shipment_result
):
self._make_json_rpc_request(
self.make_jsonrpc_request(
urls.url_join(
self.base_url(), WebsiteSaleDeliveryController._express_checkout_shipping_route
), data={
), params={
'partial_shipping_address': dict(self.express_checkout_anonymized_shipping_values)
}
)
new_partner = self.sale_order.partner_shipping_id
self._make_json_rpc_request(
self.make_jsonrpc_request(
urls.url_join(
self.base_url(), WebsiteSaleDeliveryController._express_checkout_shipping_route
), data={
), params={
'partial_shipping_address': dict(self.express_checkout_anonymized_shipping_values_2)
}
)
@@ -213,10 +189,10 @@ class TestWebsiteSaleDeliveryExpressCheckoutFlows(HttpCaseWithUserDemo):
'odoo.addons.delivery.models.delivery_carrier.DeliveryCarrier.rate_shipment',
return_value=self.rate_shipment_result
):
self._make_json_rpc_request(
self.make_jsonrpc_request(
urls.url_join(
self.base_url(), WebsiteSaleDeliveryController._express_checkout_shipping_route
), data={
), params={
'partial_shipping_address': dict(self.express_checkout_anonymized_shipping_values)
}
)
@@ -235,10 +211,10 @@ class TestWebsiteSaleDeliveryExpressCheckoutFlows(HttpCaseWithUserDemo):
'odoo.addons.delivery.models.delivery_carrier.DeliveryCarrier.rate_shipment',
return_value=self.rate_shipment_result
):
self._make_json_rpc_request(
self.make_jsonrpc_request(
urls.url_join(
self.base_url(), WebsiteSaleDeliveryController._express_checkout_shipping_route
), data={
), params={
'partial_shipping_address': dict(self.express_checkout_anonymized_shipping_values)
}
)
@@ -269,18 +245,18 @@ class TestWebsiteSaleDeliveryExpressCheckoutFlows(HttpCaseWithUserDemo):
'odoo.addons.delivery.models.delivery_carrier.DeliveryCarrier.rate_shipment',
return_value=self.rate_shipment_result
):
self._make_json_rpc_request(
self.make_jsonrpc_request(
urls.url_join(
self.base_url(), WebsiteSaleDeliveryController._express_checkout_shipping_route
), data={
), params={
'partial_shipping_address': dict(self.express_checkout_anonymized_shipping_values)
}
)
new_partner = self.sale_order.partner_shipping_id
self._make_json_rpc_request(
self.make_jsonrpc_request(
urls.url_join(
self.base_url(), WebsiteSaleDeliveryController._express_checkout_shipping_route
), data={
), params={
'partial_shipping_address': dict(self.express_checkout_anonymized_shipping_values_2)
}
)
@@ -325,16 +301,16 @@ class TestWebsiteSaleDeliveryExpressCheckoutFlows(HttpCaseWithUserDemo):
'odoo.addons.delivery.models.delivery_carrier.DeliveryCarrier.rate_shipment',
return_value=self.rate_shipment_result
):
shipping_options = self._make_json_rpc_request(
shipping_options = self.make_jsonrpc_request(
urls.url_join(
self.base_url(), WebsiteSaleDeliveryController._express_checkout_shipping_route
), data={
), params={
'partial_shipping_address': dict(self.express_checkout_anonymized_demo_shipping_values)
}
)
self.assertEqual(self.sale_order.partner_id.id, self.user_demo.partner_id.id)
self._make_json_rpc_request(urls.url_join(self.base_url(), '/shop/express'), data={
self.make_jsonrpc_request(urls.url_join(self.base_url(), WebsiteSale._express_checkout_route), params={
'billing_address': dict(self.express_checkout_billing_values),
'shipping_address': dict(self.express_checkout_demo_shipping_values),
'shipping_option': shipping_options[0],
@@ -355,10 +331,10 @@ class TestWebsiteSaleDeliveryExpressCheckoutFlows(HttpCaseWithUserDemo):
):
# Won't create a new partner because the partial information are the same the an
# exisiting partner linked to the SO
shipping_options = self._make_json_rpc_request(
shipping_options = self.make_jsonrpc_request(
urls.url_join(
self.base_url(), WebsiteSaleDeliveryController._express_checkout_shipping_route
), data={
), params={
'partial_shipping_address': dict(self.express_checkout_anonymized_demo_shipping_values)
}
)
@@ -366,11 +342,11 @@ class TestWebsiteSaleDeliveryExpressCheckoutFlows(HttpCaseWithUserDemo):
# Will create a new partner because the complete shipping information are different than
# the partner actually selected.
self._make_json_rpc_request(
self.make_jsonrpc_request(
urls.url_join(
self.base_url(),
WebsiteSaleDeliveryController._express_checkout_route
), data={
), params={
'billing_address': dict(self.express_checkout_billing_values),
'shipping_address': dict(self.express_checkout_demo_shipping_values_2),
'shipping_option': shipping_options[0],
@@ -47,41 +47,16 @@ class TestWebsiteSaleExpressCheckoutFlows(HttpCaseWithUserDemo):
'state': 'AL',
}
def _make_json_rpc_request(self, url, data=None):
""" Make a JSON-RPC request to the provided URL.
:param str url: The URL to make the request to
:param dict data: The data to be send in the request body in JSON-RPC 2.0 format
:return dict: The result of the JSON-RPC request
"""
rpc_request = {
"jsonrpc": "2.0",
"method": "call",
"id": str(uuid4()),
"params": data,
}
result = self.url_open(
url,
data=json.dumps(rpc_request).encode(),
headers={"Content-Type": "application/json"},
)
if not result.ok:
return {}
return result.json().get("result", {})
def test_express_checkout_public_user(self):
""" Test that when using express checkout as a public user, a new partner is created. """
session = self.authenticate(None, None)
session['sale_order_id'] = self.sale_order.id
root.session_store.save(session)
self._make_json_rpc_request(
self.make_jsonrpc_request(
urls.url_join(
self.base_url(), WebsiteSaleController._express_checkout_route
), data={
), params={
'billing_address': dict(self.express_checkout_billing_values)
}
)
@@ -105,10 +80,10 @@ class TestWebsiteSaleExpressCheckoutFlows(HttpCaseWithUserDemo):
session['sale_order_id'] = self.sale_order.id
root.session_store.save(session)
self._make_json_rpc_request(
self.make_jsonrpc_request(
urls.url_join(
self.base_url(), WebsiteSaleController._express_checkout_route
), data={
), params={
'billing_address': {
'name': self.user_demo.partner_id.name,
'email': self.user_demo.partner_id.email,
@@ -152,10 +127,10 @@ class TestWebsiteSaleExpressCheckoutFlows(HttpCaseWithUserDemo):
session['sale_order_id'] = self.sale_order.id
root.session_store.save(session)
self._make_json_rpc_request(
self.make_jsonrpc_request(
urls.url_join(
self.base_url(), WebsiteSaleController._express_checkout_route
), data={
), params={
'billing_address': dict(self.express_checkout_billing_values)
}
)
@@ -173,10 +148,10 @@ class TestWebsiteSaleExpressCheckoutFlows(HttpCaseWithUserDemo):
session['sale_order_id'] = self.sale_order.id
root.session_store.save(session)
self._make_json_rpc_request(
self.make_jsonrpc_request(
urls.url_join(
self.base_url(), WebsiteSaleController._express_checkout_route
), data={
), params={
'billing_address': dict(self.express_checkout_billing_values)
}
)
+34
View File
@@ -1610,6 +1610,12 @@ class Transport(xmlrpclib.Transport):
return super().request(*args, **kwargs)
class JsonRpcException(Exception):
def __init__(self, code, message):
super().__init__(message)
self.code = code
class HttpCase(TransactionCase):
""" Transactional HTTP TestCase with url_open and Chrome headless helpers. """
registry_test_mode = True
@@ -1812,6 +1818,34 @@ class HttpCase(TransactionCase):
return sup.profile(description=request.httprequest.full_path)
return profiler.Nested(_profiler, patch('odoo.http.Request._get_profiler_context_manager', route_profiler))
def make_jsonrpc_request(self, route, params=None, headers=None):
"""Make a JSON-RPC request to the server.
:param str route: the route to request
:param dict params: the parameters to send
:raises requests.HTTPError: if one occurred
:raises JsonRpcException: if the response contains an error
:return: The 'result' key from the response if any.
"""
data = json.dumps({
'id': 0,
'jsonrpc': '2.0',
'method': 'call',
'params': params,
}).encode()
headers = headers or {}
headers['Content-Type'] = 'application/json'
response = self.url_open(route, data, headers=headers)
response.raise_for_status()
decoded_response = response.json()
if 'result' in decoded_response:
return decoded_response['result']
if 'error' in decoded_response:
raise JsonRpcException(
code=decoded_response['error']['code'],
message=decoded_response['error']['data']['name']
)
def no_retry(arg):
"""Disable auto retry on decorated test method or test class"""