[FIX] bus: fix im_status during tests

Before this PR, the `_mockIrWebsocket__updatePresence` route of the
bus mock server could lead to errors when no im status ids were
provided. This commit fixes this issue by ensuring im status ids
were passed before calling `_mockIrWebsocket__getImStatus` method.

Moreover, old longpolling routes were still present on the bus mock
server. This commit removes these routes.

closes odoo/odoo#100583

Signed-off-by: Alexandre Kühn (aku) <aku@odoo.com>
This commit is contained in:
tsm-odoo
2022-09-21 16:02:41 +02:00
parent d7ec624d5a
commit 7422254b02
8 changed files with 106 additions and 32 deletions
+5 -3
View File
@@ -4,6 +4,7 @@ from odoo.service import security
from ..models.bus import dispatch
from ..websocket import wsrequest
class IrWebsocket(models.AbstractModel):
_name = 'ir.websocket'
_description = 'websocket message handling'
@@ -11,8 +12,9 @@ class IrWebsocket(models.AbstractModel):
def _get_im_status(self, im_status_ids_by_model):
im_status = {}
if 'res.partner' in im_status_ids_by_model:
im_status['partners'] = self.env['res.partner'].with_context(active_test=False).search(
[('id', 'in', im_status_ids_by_model['res.partner'])]).read(['im_status']
im_status['partners'] = self.env['res.partner'].with_context(active_test=False).search_read(
[('id', 'in', im_status_ids_by_model['res.partner'])],
['im_status']
)
return im_status
@@ -41,7 +43,7 @@ class IrWebsocket(models.AbstractModel):
identity_value=self.env.uid
)
im_status_notification = self._get_im_status(im_status_ids_by_model)
if im_status_ids_by_model:
if im_status_notification:
self.env['bus.bus']._sendone(self.env.user.partner_id, 'bus/im_status', im_status_notification)
@classmethod
@@ -4,9 +4,7 @@ import { TEST_USER_IDS } from "@bus/../tests/helpers/test_constants";
import { patchWebsocketWorkerWithCleanup } from '@bus/../tests/helpers/mock_websocket';
import { patch } from "@web/core/utils/patch";
import { ConnectionLostError } from "@web/core/network/rpc_service";
import { MockServer } from "@web/../tests/helpers/mock_server";
import { makeDeferred } from "@web/../tests/helpers/utils";
patch(MockServer.prototype, 'bus', {
init() {
@@ -40,26 +38,6 @@ patch(MockServer.prototype, 'bus', {
this._mockIrWebsocket__updatePresence(inactivity_period, im_status_ids_by_model);
}
},
/**
* @override
*/
async _performRPC(route, args) {
if (route === '/longpolling/poll') {
const longpollingPromise = makeDeferred();
if (this.hasLostConnection) {
longpollingPromise.reject(new ConnectionLostError());
this.hasLostConnection = false;
} else if (this.notificationsToBeResolved.length) {
longpollingPromise.resolve(this.notificationsToBeResolved);
this.notificationsToBeResolved = [];
} else {
this.pendingLongpollingPromise = longpollingPromise;
}
return longpollingPromise;
}
return this._super(route, args);
},
/**
* Simulates `_sendone` on `bus.bus`.
*
@@ -11,7 +11,10 @@ patch(MockServer.prototype, 'bus/models/ir_websocket', {
* @param imStatusIdsByModel
*/
_mockIrWebsocket__updatePresence(inactivityPeriod, imStatusIdsByModel) {
this._mockBusBus__sendone(this.currentPartnerId, 'bus/im_status', this._mockIrWebsocket__getImStatus(imStatusIdsByModel));
const imStatusNotifications = this._mockIrWebsocket__getImStatus(imStatusIdsByModel);
if (Object.keys(imStatusNotifications).length > 0) {
this._mockBusBus__sendone(this.currentPartnerId, 'bus/im_status', imStatusNotifications);
}
},
/**
* Simulates `_get_im_status` on `ir.websocket`.
@@ -20,9 +23,12 @@ patch(MockServer.prototype, 'bus/models/ir_websocket', {
* @param {Number[]|undefined} res.partner ids of res.partners whose im_status
* should be monitored.
*/
_mockIrWebsocket__getImStatus({ 'res.partner': partnerIds }) {
return {
'partners': this.mockSearchRead('res.partner', [[['id', 'in', partnerIds]]], { context: { 'active_test': false }, fields: ['im_status'] })
};
_mockIrWebsocket__getImStatus(imStatusIdsByModel) {
const imStatus = {};
const { 'res.partner': partnerIds } = imStatusIdsByModel;
if (partnerIds) {
imStatus['partners'] = this.mockSearchRead('res.partner', [[['id', 'in', partnerIds]]], { context: { 'active_test': false }, fields: ['im_status'] })
}
return imStatus;
},
});
+4 -1
View File
@@ -9,7 +9,10 @@ class IrWebsocket(models.AbstractModel):
def _get_im_status(self, data):
im_status = super()._get_im_status(data)
if 'mail.guest' in data:
im_status['guests'] = self.env['mail.guest'].sudo().with_context(active_test=False).search([('id', 'in', data['mail.guest'])]).read(['im_status'])
im_status['guests'] = self.env['mail.guest'].sudo().with_context(active_test=False).search_read(
[('id', 'in', data['mail.guest'])],
['im_status']
)
return im_status
def _build_bus_channel_list(self, channels):
@@ -1559,6 +1559,10 @@ patch(MockServer.prototype, 'mail', {
])[0];
response.subtype_description = subtype.description;
}
if (message.author_guest_id) {
const [guest] = this.pyEnv['mail.guest'].searchRead([['id', '=', message.author_guest_id]]);
response['guestAuthor'] = { id: guest.id, name: guest.name };
}
return response;
});
},
@@ -0,0 +1,25 @@
/** @odoo-module **/
import { patch } from "@web/core/utils/patch";
import { MockServer } from "@web/../tests/helpers/mock_server";
// ensure bus override is applied first.
import "@bus/../tests/helpers/mock_server";
patch(MockServer.prototype, 'mail/models/ir_websocket', {
/**
* Simulates `_get_im_status` on `ir.websocket`.
*
* @param {Object} imStatusIdsByModel
* @param {Number[]|undefined} mail.guest ids of mail.guest whose im_status
* should be monitored.
*/
_mockIrWebsocket__getImStatus(imStatusIdsByModel) {
const imStatus = this._super(imStatusIdsByModel);
const { 'mail.guest': guestIds } = imStatusIdsByModel;
if (guestIds) {
imStatus['guests'] = this.pyEnv['mail.guest'].searchRead([['id', 'in', guestIds]], { context: { 'active_test': false }, fields: ['im_status'] });
}
return imStatus;
},
});
@@ -14,7 +14,7 @@ import {
addModelNamesToFetch([
'mail.activity', 'mail.activity.type', 'mail.channel', 'mail.channel.member',
'mail.channel.rtc.session', 'mail.followers', 'mail.link.preview', 'mail.message',
'mail.channel.rtc.session', 'mail.followers', 'mail.guest', 'mail.link.preview', 'mail.message',
'mail.message.subtype', 'mail.notification', 'mail.shortcode', 'mail.template',
'mail.tracking.value', 'res.users.settings', 'res.users.settings.volumes'
]);
@@ -148,5 +148,61 @@ QUnit.test('change icon on change partner im_status', async function (assert) {
);
});
QUnit.test('change icon on change guest im_status', async function (assert) {
assert.expect(4);
const pyEnv = await startServer();
const guestId = pyEnv['mail.guest'].create({ im_status: 'online' });
const mailChannelId = pyEnv['mail.channel'].create({
channel_member_ids: [[0, 0, { partner_id: pyEnv.currentPartnerId }], [0, 0, { guest_id: guestId }]],
channel_type: 'group',
});
pyEnv['mail.message'].create({
author_guest_id: guestId,
author_id: false,
body: 'not empty',
model: 'mail.channel',
res_id: mailChannelId,
});
const { advanceTime, afterNextRender, openDiscuss } = await start({
discuss: {
params: {
default_active_id: mailChannelId,
},
},
hasTimeControl: true,
});
await openDiscuss();
assert.strictEqual(
document.querySelectorAll(`.o_PersonaImStatusIcon.o-online`).length,
1,
"persona IM status icon should have online status rendering"
);
pyEnv['mail.guest'].write([guestId], { im_status: 'offline' });
await afterNextRender(() => advanceTime(UPDATE_BUS_PRESENCE_DELAY));
assert.strictEqual(
document.querySelectorAll(`.o_PersonaImStatusIcon.o-offline`).length,
1,
"persona IM status icon should have offline status rendering"
);
pyEnv['mail.guest'].write([guestId], { im_status: 'away' });
await afterNextRender(() => advanceTime(UPDATE_BUS_PRESENCE_DELAY));
assert.strictEqual(
document.querySelectorAll(`.o_PersonaImStatusIcon.o-away`).length,
1,
"persona IM status icon should have away status rendering"
);
pyEnv['mail.guest'].write([guestId], { im_status: 'online' });
await afterNextRender(() => advanceTime(UPDATE_BUS_PRESENCE_DELAY));
assert.strictEqual(
document.querySelectorAll(`.o_PersonaImStatusIcon.o-online`).length,
1,
"persona IM status icon should have online status rendering in the end"
);
});
});
});