# Part of Odoo. See LICENSE file for full copyright and licensing details. from dateutil.relativedelta import relativedelta from freezegun import freeze_time from unittest.mock import patch from odoo import fields from odoo.addons.mail.tests.common import MailCommon from odoo.addons.mail.tools.discuss import Store from odoo.tests.common import HttpCase, new_test_user, tagged, users from odoo.tools.misc import mute_logger @tagged("RTC", "post_install", "-at_install") class TestChannelRTC(MailCommon, HttpCase): @users('employee') @mute_logger('odoo.models.unlink') @freeze_time("2023-03-15 12:34:56") def test_01_join_call(self): """Join call should remove existing sessions, remove invitation, create a new session, and return data.""" self.maxDiff = None channel = self.env['discuss.channel']._create_channel(name='Test Channel', group_id=self.env.ref('base.group_user').id) channel_member = channel.sudo().channel_member_ids.filtered(lambda channel_member: channel_member.partner_id == self.user_employee.partner_id) channel_member._rtc_join_call() with self.assertBus( [ # delete of old sessions (self.cr.dbname, "discuss.channel", channel.id), # end of old sessions (self.cr.dbname, "res.partner", self.user_employee.partner_id.id), # update history with duration of previous session (self.cr.dbname, "discuss.channel", channel.id), # insert new session (self.cr.dbname, "discuss.channel", channel.id), # message unread counter (message post) (self.cr.dbname, "res.partner", self.user_employee.partner_id.id), # start call notification message post (self.cr.dbname, "discuss.channel", channel.id), # new call history (not asserted below) (self.cr.dbname, "discuss.channel", channel.id), ], [ { "type": "discuss.channel.rtc.session/ended", "payload": {"sessionId": channel_member.rtc_session_ids.id}, }, { "type": "mail.record/insert", "payload": { "discuss.channel": [ { "id": channel.id, "rtc_session_ids": [("DELETE", [channel_member.rtc_session_ids.id])], }, ], }, }, { "type": "mail.record/insert", "payload": { "discuss.channel": [ { "id": channel.id, "rtc_session_ids": [("ADD", [channel_member.rtc_session_ids.id + 1])], }, ], "discuss.channel.member": [ { "id": channel_member.id, "partner_id": channel_member.partner_id.id, "channel_id": { "id": channel_member.channel_id.id, "model": "discuss.channel", }, }, ], "discuss.channel.rtc.session": [ { "channel_member_id": channel_member.id, "id": channel_member.rtc_session_ids.id + 1, }, ], "res.partner": self._filter_partners_fields( { "avatar_128_access_token": channel_member.partner_id._get_avatar_128_access_token(), "id": channel_member.partner_id.id, "im_status": channel_member.partner_id.im_status, "im_status_access_token": channel_member.partner_id._get_im_status_access_token(), "mention_token": channel_member.partner_id._get_mention_token(), "name": channel_member.partner_id.name, "write_date": fields.Datetime.to_string( channel_member.partner_id.write_date ), }, ), }, }, { "type": "mail.record/insert", "payload": { "discuss.channel.member": [ { "id": channel_member.id, "new_message_separator": channel_member.new_message_separator + 1, "partner_id": channel_member.partner_id.id, "channel_id": {"id": channel.id, "model": "discuss.channel"} } ] }, }, ], ): store = Store() channel_member._rtc_join_call(store) res = store.get_result() self.assertEqual( res, { "discuss.channel": [ { "id": channel.id, "rtc_session_ids": [ ("ADD", [channel_member.rtc_session_ids.id]), ("DELETE", [channel_member.rtc_session_ids.id - 1]), ], }, ], "discuss.channel.member": [ { "id": channel_member.id, "partner_id": channel_member.partner_id.id, "channel_id": { "id": channel_member.channel_id.id, "model": "discuss.channel", }, }, ], "discuss.channel.rtc.session": [ { "channel_member_id": channel_member.id, "id": channel_member.rtc_session_ids.id, }, ], "res.partner": self._filter_partners_fields( { "avatar_128_access_token": channel_member.partner_id._get_avatar_128_access_token(), "id": channel_member.partner_id.id, "im_status": channel_member.partner_id.im_status, "im_status_access_token": channel_member.partner_id._get_im_status_access_token(), "mention_token": channel_member.partner_id._get_mention_token(), "name": channel_member.partner_id.name, "write_date": fields.Datetime.to_string( channel_member.partner_id.write_date ), }, ), "Rtc": { "iceServers": False, "localSession": channel_member.rtc_session_ids.id, "serverInfo": None, }, }, ) @users('employee') @mute_logger('odoo.models.unlink') def test_10_start_call_in_chat_should_invite_all_members_to_call(self): test_user = self.env['res.users'].sudo().create({'name': "Test User", 'login': 'test'}) channel = self.env['discuss.channel']._get_or_create_chat(partners_to=(self.user_employee.partner_id + test_user.partner_id).ids) channel_member = channel.sudo().channel_member_ids.filtered(lambda channel_member: channel_member.partner_id == self.user_employee.partner_id) channel_member_test_user = channel.sudo().channel_member_ids.filtered(lambda channel_member: channel_member.partner_id == test_user.partner_id) channel_member._rtc_join_call() last_rtc_session_id = channel_member.rtc_session_ids.id channel_member._rtc_leave_call() with self.assertBus( [ # update new session (self.cr.dbname, "discuss.channel", channel.id), # update new message separator (self.cr.dbname, "res.partner", self.user_employee.partner_id.id), # message_post "started a live conference" (not asserted below) (self.cr.dbname, "discuss.channel", channel.id), # update call history (not asserted below) (self.cr.dbname, "discuss.channel", channel.id), # incoming invitation (self.cr.dbname, "res.partner", test_user.partner_id.id), # update list of invitations (self.cr.dbname, "discuss.channel", channel.id), ], [ { "type": "mail.record/insert", "payload": { "discuss.channel": [ { "id": channel.id, "rtc_session_ids": [("ADD", [last_rtc_session_id + 1])], }, ], "discuss.channel.member": [ { "id": channel_member.id, "partner_id": channel_member.partner_id.id, "channel_id": { "id": channel_member.channel_id.id, "model": "discuss.channel", }, }, ], "discuss.channel.rtc.session": [ { "channel_member_id": channel_member.id, "id": last_rtc_session_id + 1, }, ], "res.partner": self._filter_partners_fields( { "avatar_128_access_token": channel_member.partner_id._get_avatar_128_access_token(), "id": channel_member.partner_id.id, "im_status": channel_member.partner_id.im_status, "im_status_access_token": channel_member.partner_id._get_im_status_access_token(), "mention_token": channel_member.partner_id._get_mention_token(), "name": channel_member.partner_id.name, "write_date": fields.Datetime.to_string( channel_member.partner_id.write_date ), }, ), }, }, { "type": "mail.record/insert", "payload": { "discuss.channel": [ { "id": channel.id, "invited_member_ids": [("ADD", [channel_member_test_user.id])], } ], "discuss.channel.member": [ { "id": channel_member_test_user.id, "partner_id": channel_member_test_user.partner_id.id, "channel_id": { "id": channel_member_test_user.channel_id.id, "model": "discuss.channel", }, }, ], "res.partner": self._filter_partners_fields( { "avatar_128_access_token": channel_member_test_user.partner_id._get_avatar_128_access_token(), "id": channel_member_test_user.partner_id.id, "im_status": channel_member_test_user.partner_id.im_status, "im_status_access_token": channel_member_test_user.partner_id._get_im_status_access_token(), "mention_token": channel_member_test_user.partner_id._get_mention_token(), "name": channel_member_test_user.partner_id.name, "write_date": fields.Datetime.to_string( channel_member_test_user.partner_id.write_date ), }, ), }, }, ], ): now = fields.Datetime.now() with patch.object(fields.Datetime, 'now', lambda: now + relativedelta(seconds=5)): channel_member._rtc_join_call() @users('employee') @mute_logger('odoo.models.unlink') def test_11_start_call_in_group_should_invite_all_members_to_call(self): test_user = self.env['res.users'].sudo().create({'name': "Test User", 'login': 'test'}) test_guest = self.env['mail.guest'].sudo().create({'name': "Test Guest"}) self.env["mail.presence"]._update_presence(test_guest) channel = self.env['discuss.channel']._create_group(partners_to=(self.user_employee.partner_id + test_user.partner_id).ids) channel._add_members(guests=test_guest) channel_member_test_user = channel.sudo().channel_member_ids.filtered(lambda channel_member: channel_member.partner_id == test_user.partner_id) channel_member_test_guest = channel.sudo().channel_member_ids.filtered(lambda channel_member: channel_member.guest_id == test_guest) channel_member = channel.sudo().channel_member_ids.filtered(lambda channel_member: channel_member.partner_id == self.user_employee.partner_id) channel_member._rtc_join_call() last_rtc_session_id = channel_member.rtc_session_ids.id channel_member._rtc_leave_call() with self.assertBus( [ # update new session (self.cr.dbname, "discuss.channel", channel.id), # update new message separator (self.cr.dbname, "res.partner", self.user_employee.partner_id.id), # message_post "started a live conference" (not asserted below) (self.cr.dbname, "discuss.channel", channel.id), # update call history (not asserted below) (self.cr.dbname, "discuss.channel", channel.id), # incoming invitation (self.cr.dbname, "res.partner", test_user.partner_id.id), # incoming invitation (self.cr.dbname, "mail.guest", test_guest.id), # update list of invitations (self.cr.dbname, "discuss.channel", channel.id), ], [ { "type": "mail.record/insert", "payload": { "discuss.channel": [ { "id": channel.id, "rtc_session_ids": [("ADD", [last_rtc_session_id + 1])], }, ], "discuss.channel.member": [ { "id": channel_member.id, "partner_id": channel_member.partner_id.id, "channel_id": { "id": channel_member.channel_id.id, "model": "discuss.channel", }, }, ], "discuss.channel.rtc.session": [ { "channel_member_id": channel_member.id, "id": last_rtc_session_id + 1, }, ], "res.partner": self._filter_partners_fields( { "avatar_128_access_token": channel_member.partner_id._get_avatar_128_access_token(), "id": channel_member.partner_id.id, "im_status": channel_member.partner_id.im_status, "im_status_access_token": channel_member.partner_id._get_im_status_access_token(), "mention_token": channel_member.partner_id._get_mention_token(), "name": channel_member.partner_id.name, "write_date": fields.Datetime.to_string( channel_member.partner_id.write_date ), }, ), }, }, { "type": "mail.record/insert", "payload": { "discuss.channel": [ { "id": channel.id, "rtc_session_ids": [("ADD", [last_rtc_session_id + 1])], }, ], "discuss.channel.member": [ { "id": channel_member.id, "partner_id": channel_member.partner_id.id, "channel_id": { "id": channel_member.channel_id.id, "model": "discuss.channel", }, }, ], "discuss.channel.rtc.session": [ { "channel_member_id": channel_member.id, "id": last_rtc_session_id + 1, }, ], "res.partner": self._filter_partners_fields( { "avatar_128_access_token": channel_member.partner_id._get_avatar_128_access_token(), "id": channel_member.partner_id.id, "im_status": channel_member.partner_id.im_status, "im_status_access_token": channel_member.partner_id._get_im_status_access_token(), "mention_token": channel_member.partner_id._get_mention_token(), "name": channel_member.partner_id.name, "write_date": fields.Datetime.to_string( channel_member.partner_id.write_date ), }, ), }, }, { "type": "mail.record/insert", "payload": { "discuss.channel": [ { "id": channel.id, "invited_member_ids": [ ( "ADD", [channel_member_test_user.id, channel_member_test_guest.id], ) ], }, ], "discuss.channel.member": [ { "id": channel_member_test_user.id, "partner_id": channel_member_test_user.partner_id.id, "channel_id": { "id": channel_member_test_user.channel_id.id, "model": "discuss.channel", }, }, { "id": channel_member_test_guest.id, "guest_id": channel_member_test_guest.guest_id.id, "channel_id": { "id": channel_member_test_guest.channel_id.id, "model": "discuss.channel", }, }, ], "mail.guest": [ { "avatar_128_access_token": channel_member_test_guest.guest_id._get_avatar_128_access_token(), "id": channel_member_test_guest.guest_id.id, "im_status": channel_member_test_guest.guest_id.im_status, "im_status_access_token": channel_member_test_guest.guest_id._get_im_status_access_token(), "name": channel_member_test_guest.guest_id.name, "write_date": fields.Datetime.to_string( channel_member_test_guest.guest_id.write_date ), }, ], "res.partner": self._filter_partners_fields( { "avatar_128_access_token": channel_member_test_user.partner_id._get_avatar_128_access_token(), "id": channel_member_test_user.partner_id.id, "im_status": channel_member_test_user.partner_id.im_status, "im_status_access_token": channel_member_test_user.partner_id._get_im_status_access_token(), "mention_token": channel_member_test_user.partner_id._get_mention_token(), "name": channel_member_test_user.partner_id.name, "write_date": fields.Datetime.to_string( channel_member_test_user.partner_id.write_date ), }, ), }, }, ], ): now = fields.Datetime.now() with patch.object(fields.Datetime, 'now', lambda: now + relativedelta(seconds=5)): channel_member._rtc_join_call() @users('employee') @mute_logger('odoo.models.unlink') def test_20_join_call_should_cancel_pending_invitations(self): test_user = self.env['res.users'].sudo().create({'name': "Test User", 'login': 'test'}) test_guest = self.env['mail.guest'].sudo().create({'name': "Test Guest"}) self.env["mail.presence"]._update_presence(test_guest) channel = self.env['discuss.channel']._create_group(partners_to=(self.user_employee.partner_id + test_user.partner_id).ids) channel._add_members(guests=test_guest) channel_member = channel.sudo().channel_member_ids.filtered(lambda channel_member: channel_member.partner_id == self.user_employee.partner_id) channel_member._rtc_join_call() channel_member_test_user = channel.sudo().channel_member_ids.filtered(lambda channel_member: channel_member.partner_id == test_user.partner_id) with self.assertBus( [ # update invitation (self.cr.dbname, "res.partner", test_user.partner_id.id), # update list of invitations (self.cr.dbname, "discuss.channel", channel.id), # update sessions (self.cr.dbname, "discuss.channel", channel.id), ], [ { "type": "mail.record/insert", "payload": { "discuss.channel.member": [ { "channel_id": {"id": channel.id, "model": "discuss.channel"}, "id": channel_member_test_user.id, "partner_id": channel_member_test_user.partner_id.id, "rtc_inviting_session_id": False, }, ], }, }, { "type": "mail.record/insert", "payload": { "discuss.channel": [ { "id": channel.id, "invited_member_ids": [("DELETE", [channel_member_test_user.id])], }, ], "discuss.channel.member": [ { "id": channel_member_test_user.id, "partner_id": channel_member_test_user.partner_id.id, "channel_id": { "id": channel_member_test_user.channel_id.id, "model": "discuss.channel", }, }, ], "res.partner": self._filter_partners_fields( { "avatar_128_access_token": channel_member_test_user.partner_id._get_avatar_128_access_token(), "id": channel_member_test_user.partner_id.id, "im_status": channel_member_test_user.partner_id.im_status, "im_status_access_token": channel_member_test_user.partner_id._get_im_status_access_token(), "mention_token": channel_member_test_user.partner_id._get_mention_token(), "name": channel_member_test_user.partner_id.name, "write_date": fields.Datetime.to_string( channel_member_test_user.partner_id.write_date ), }, ), }, }, { "type": "mail.record/insert", "payload": { "discuss.channel": [ { "id": channel.id, "rtc_session_ids": [("ADD", [channel_member.rtc_session_ids.id + 1])], }, ], "discuss.channel.member": [ { "id": channel_member_test_user.id, "partner_id": channel_member_test_user.partner_id.id, "channel_id": { "id": channel_member_test_user.channel_id.id, "model": "discuss.channel", }, }, ], "discuss.channel.rtc.session": [ { "channel_member_id": channel_member_test_user.id, "id": channel_member.rtc_session_ids.id + 1, }, ], "res.partner": self._filter_partners_fields( { "avatar_128_access_token": channel_member_test_user.partner_id._get_avatar_128_access_token(), "id": channel_member_test_user.partner_id.id, "im_status": channel_member_test_user.partner_id.im_status, "im_status_access_token": channel_member_test_user.partner_id._get_im_status_access_token(), "mention_token": channel_member_test_user.partner_id._get_mention_token(), "name": channel_member_test_user.partner_id.name, "write_date": fields.Datetime.to_string( channel_member_test_user.partner_id.write_date ), }, ), }, }, ], ): channel_member_test_user._rtc_join_call() channel_member_test_guest = channel.sudo().channel_member_ids.filtered(lambda channel_member: channel_member.guest_id == test_guest) with self.assertBus( [ # update invitation (self.cr.dbname, "mail.guest", test_guest.id), # update list of invitations (self.cr.dbname, "discuss.channel", channel.id), # update sessions (self.cr.dbname, "discuss.channel", channel.id), ], [ { "type": "mail.record/insert", "payload": { "discuss.channel.member": [ { "channel_id": {"id": channel.id, "model": "discuss.channel"}, "guest_id": channel_member_test_guest.guest_id.id, "id": channel_member_test_guest.id, "rtc_inviting_session_id": False, }, ], }, }, { "type": "mail.record/insert", "payload": { "discuss.channel": [ { "id": channel.id, "invited_member_ids": [("DELETE", [channel_member_test_guest.id])], }, ], "discuss.channel.member": [ { "id": channel_member_test_guest.id, "guest_id": channel_member_test_guest.guest_id.id, "channel_id": { "id": channel_member_test_guest.channel_id.id, "model": "discuss.channel", }, }, ], "mail.guest": [ { "avatar_128_access_token": channel_member_test_guest.guest_id._get_avatar_128_access_token(), "id": channel_member_test_guest.guest_id.id, "im_status": channel_member_test_guest.guest_id.im_status, "im_status_access_token": channel_member_test_guest.guest_id._get_im_status_access_token(), "name": channel_member_test_guest.guest_id.name, "write_date": fields.Datetime.to_string( channel_member_test_guest.guest_id.write_date ), }, ], }, }, { "type": "mail.record/insert", "payload": { "discuss.channel": [ { "id": channel.id, "rtc_session_ids": [ ("ADD", [channel_member.rtc_session_ids.id + 2]) ], }, ], "discuss.channel.member": [ { "id": channel_member_test_guest.id, "guest_id": channel_member_test_guest.guest_id.id, "channel_id": { "id": channel_member_test_guest.channel_id.id, "model": "discuss.channel", }, }, ], "discuss.channel.rtc.session": [ { "channel_member_id": channel_member_test_guest.id, "id": channel_member.rtc_session_ids.id + 2, }, ], "mail.guest": [ { "avatar_128_access_token": channel_member_test_guest.guest_id._get_avatar_128_access_token(), "id": channel_member_test_guest.guest_id.id, "im_status": channel_member_test_guest.guest_id.im_status, "im_status_access_token": channel_member_test_guest.guest_id._get_im_status_access_token(), "name": channel_member_test_guest.guest_id.name, "write_date": fields.Datetime.to_string( channel_member_test_guest.guest_id.write_date ), }, ], }, }, ], ): channel_member_test_guest._rtc_join_call() @users('employee') @mute_logger('odoo.models.unlink') def test_21_leave_call_should_cancel_pending_invitations(self): test_user = self.env['res.users'].sudo().create({'name': "Test User", 'login': 'test'}) test_guest = self.env['mail.guest'].sudo().create({'name': "Test Guest"}) self.env["mail.presence"]._update_presence(test_guest) channel = self.env['discuss.channel']._create_group(partners_to=(self.user_employee.partner_id + test_user.partner_id).ids) channel._add_members(guests=test_guest) channel_member = channel.sudo().channel_member_ids.filtered(lambda channel_member: channel_member.partner_id == self.user_employee.partner_id) channel_member._rtc_join_call() channel_member_test_user = channel.sudo().channel_member_ids.filtered(lambda channel_member: channel_member.partner_id == test_user.partner_id) with self.assertBus( [ # update invitation (self.cr.dbname, "res.partner", test_user.partner_id.id), # update list of invitations (self.cr.dbname, "discuss.channel", channel.id), ], [ { "type": "mail.record/insert", "payload": { "discuss.channel.member": [ { "channel_id": {"id": channel.id, "model": "discuss.channel"}, "id": channel_member_test_user.id, "partner_id": channel_member_test_user.partner_id.id, "rtc_inviting_session_id": False, }, ], }, }, { "type": "mail.record/insert", "payload": { "discuss.channel": [ { "id": channel.id, "invited_member_ids": [("DELETE", [channel_member_test_user.id])], }, ], "discuss.channel.member": [ { "id": channel_member_test_user.id, "partner_id": channel_member_test_user.partner_id.id, "channel_id": { "id": channel_member_test_user.channel_id.id, "model": "discuss.channel", }, }, ], "res.partner": self._filter_partners_fields( { "avatar_128_access_token": channel_member_test_user.partner_id._get_avatar_128_access_token(), "id": channel_member_test_user.partner_id.id, "im_status": channel_member_test_user.partner_id.im_status, "im_status_access_token": channel_member_test_user.partner_id._get_im_status_access_token(), "mention_token": channel_member_test_user.partner_id._get_mention_token(), "name": channel_member_test_user.partner_id.name, "write_date": fields.Datetime.to_string( channel_member_test_user.partner_id.write_date ), }, ), }, }, ], ): channel_member_test_user._rtc_leave_call() channel_member_test_guest = channel.sudo().channel_member_ids.filtered(lambda channel_member: channel_member.guest_id == test_guest) with self.assertBus( [ # update invitation (self.cr.dbname, "mail.guest", test_guest.id), # update list of invitations (self.cr.dbname, "discuss.channel", channel.id), ], [ { "type": "mail.record/insert", "payload": { "discuss.channel.member": [ { "channel_id": {"id": channel.id, "model": "discuss.channel"}, "guest_id": channel_member_test_guest.guest_id.id, "id": channel_member_test_guest.id, "rtc_inviting_session_id": False, }, ], }, }, { "type": "mail.record/insert", "payload": { "discuss.channel": [ { "id": channel.id, "invited_member_ids": [("DELETE", [channel_member_test_guest.id])], }, ], "discuss.channel.member": [ { "id": channel_member_test_guest.id, "guest_id": channel_member_test_guest.guest_id.id, "channel_id": { "id": channel_member_test_guest.channel_id.id, "model": "discuss.channel", }, }, ], "mail.guest": [ { "avatar_128_access_token": channel_member_test_guest.guest_id._get_avatar_128_access_token(), "id": channel_member_test_guest.guest_id.id, "im_status": channel_member_test_guest.guest_id.im_status, "im_status_access_token": channel_member_test_guest.guest_id._get_im_status_access_token(), "name": channel_member_test_guest.guest_id.name, "write_date": fields.Datetime.to_string( channel_member_test_guest.guest_id.write_date ), }, ], }, }, ], ): channel_member_test_guest._rtc_leave_call() @users('employee') @mute_logger('odoo.models.unlink') def test_25_lone_call_participant_leaving_call_should_cancel_pending_invitations(self): test_user = self.env['res.users'].sudo().create({'name': "Test User", 'login': 'test'}) test_guest = self.env['mail.guest'].sudo().create({'name': "Test Guest"}) self.env["mail.presence"]._update_presence(test_guest) channel = self.env['discuss.channel']._create_group(partners_to=(self.user_employee.partner_id + test_user.partner_id).ids) channel._add_members(guests=test_guest) channel_member = channel.sudo().channel_member_ids.filtered(lambda channel_member: channel_member.partner_id == self.user_employee.partner_id) channel_member_test_user = channel.sudo().channel_member_ids.filtered(lambda channel_member: channel_member.partner_id == test_user.partner_id) channel_member_test_guest = channel.sudo().channel_member_ids.filtered(lambda channel_member: channel_member.guest_id == test_guest) channel_member._rtc_join_call() with self.assertBus( [ # update invitation (self.cr.dbname, "res.partner", test_user.partner_id.id), # update invitation (self.cr.dbname, "mail.guest", test_guest.id), # update list of invitations (self.cr.dbname, "discuss.channel", channel.id), # update sessions (self.cr.dbname, "discuss.channel", channel.id), # end session (self.cr.dbname, "res.partner", self.user_employee.partner_id.id), # update call history (not asserted below) (self.cr.dbname, "discuss.channel", channel.id), ], [ { "type": "discuss.channel.rtc.session/ended", "payload": {"sessionId": channel_member.rtc_session_ids.id}, }, { "type": "mail.record/insert", "payload": { "discuss.channel.member": [ { "channel_id": {"id": channel.id, "model": "discuss.channel"}, "id": channel_member_test_user.id, "partner_id": channel_member_test_user.partner_id.id, "rtc_inviting_session_id": False, }, ], }, }, { "type": "mail.record/insert", "payload": { "discuss.channel.member": [ { "channel_id": {"id": channel.id, "model": "discuss.channel"}, "guest_id": channel_member_test_guest.guest_id.id, "id": channel_member_test_guest.id, "rtc_inviting_session_id": False, }, ], }, }, { "type": "mail.record/insert", "payload": { "discuss.channel": [ { "id": channel.id, "invited_member_ids": [ ( "DELETE", [channel_member_test_user.id, channel_member_test_guest.id], ) ], }, ], "discuss.channel.member": [ { "id": channel_member_test_user.id, "partner_id": channel_member_test_user.partner_id.id, "channel_id": { "id": channel_member_test_user.channel_id.id, "model": "discuss.channel", }, }, { "id": channel_member_test_guest.id, "guest_id": channel_member_test_guest.guest_id.id, "channel_id": { "id": channel_member_test_guest.channel_id.id, "model": "discuss.channel", }, }, ], "mail.guest": [ { "avatar_128_access_token": channel_member_test_guest.guest_id._get_avatar_128_access_token(), "id": channel_member_test_guest.guest_id.id, "im_status": channel_member_test_guest.guest_id.im_status, "im_status_access_token": channel_member_test_guest.guest_id._get_im_status_access_token(), "name": channel_member_test_guest.guest_id.name, "write_date": fields.Datetime.to_string( channel_member_test_guest.guest_id.write_date ), }, ], "res.partner": self._filter_partners_fields( { "avatar_128_access_token": channel_member_test_user.partner_id._get_avatar_128_access_token(), "id": channel_member_test_user.partner_id.id, "im_status": channel_member_test_user.partner_id.im_status, "im_status_access_token": channel_member_test_user.partner_id._get_im_status_access_token(), "mention_token": channel_member_test_user.partner_id._get_mention_token(), "name": channel_member_test_user.partner_id.name, "write_date": fields.Datetime.to_string( channel_member_test_user.partner_id.write_date ), }, ), }, }, { "type": "mail.record/insert", "payload": { "discuss.channel": [ { "id": channel.id, "rtc_session_ids": [("DELETE", [channel_member.rtc_session_ids.id])], }, ], }, }, ], ): channel_member._rtc_leave_call() @users('employee') @mute_logger('odoo.models.unlink') def test_30_add_members_while_in_call_should_invite_new_members_to_call(self): test_user = self.env['res.users'].sudo().create({'name': "Test User", 'login': 'test'}) test_guest = self.env['mail.guest'].sudo().create({'name': "Test Guest"}) self.env["mail.presence"]._update_presence(test_guest) channel = self.env['discuss.channel']._create_group(partners_to=self.user_employee.partner_id.ids) channel_member = channel.sudo().channel_member_ids.filtered(lambda member: member.partner_id == self.user_employee.partner_id) now = fields.Datetime.now() with patch.object(fields.Datetime, 'now', lambda: now + relativedelta(seconds=5)): channel_member._rtc_join_call() self._reset_bus() with self.mock_bus(): with patch.object(fields.Datetime, 'now', lambda: now + relativedelta(seconds=10)): channel._add_members(users=test_user, guests=test_guest, invite_to_rtc_call=True) channel_member_test_user = channel.sudo().channel_member_ids.filtered(lambda member: member.partner_id == test_user.partner_id) channel_member_test_guest = channel.sudo().channel_member_ids.filtered(lambda member: member.guest_id == test_guest) found_bus_notifs = self.assertBusNotifications( [ # mail.record/insert - discuss.channel (channel_name_member_ids) (self.cr.dbname, "discuss.channel", channel.id), # discuss.channel/joined (self.cr.dbname, "res.partner", test_user.partner_id.id), # mail.record/insert - discuss.channel.member (message_unread_counter, new_message_separator, …) (self.cr.dbname, "res.partner", self.user_employee.partner_id.id), # discuss.channel/new_message (self.cr.dbname, "discuss.channel", channel.id), # discuss.channel/joined (self.cr.dbname, "mail.guest", test_guest.id), # mail.record/insert - discuss.channel.member (message_unread_counter, new_message_separator, …) (self.cr.dbname, "res.partner", self.user_employee.partner_id.id), # discuss.channel/new_message (self.cr.dbname, "discuss.channel", channel.id), # mail.record/insert - discuss.channel (member_count), discuss.channel.member (self.cr.dbname, "discuss.channel", channel.id), # mail.record/insert - discuss.channel.member (rtc_inviting_session_id) (self.cr.dbname, "res.partner", test_user.partner_id.id), # mail.record/insert - discuss.channel.member (rtc_inviting_session_id) (self.cr.dbname, "mail.guest", test_guest.id), # mail.record/insert - discuss.channel (invited_member_ids), discuss.channel.member (self.cr.dbname, "discuss.channel", channel.id), ], message_items=[ { "type": "mail.record/insert", "payload": { "discuss.channel.member": [ { "channel_id": { "id": channel_member_test_user.channel_id.id, "model": "discuss.channel", }, "id": channel_member_test_user.id, "partner_id": channel_member_test_user.partner_id.id, "rtc_inviting_session_id": channel_member_test_user.rtc_inviting_session_id.id, }, { "id": channel_member.id, "partner_id": channel_member.partner_id.id, "channel_id": { "id": channel_member.channel_id.id, "model": "discuss.channel", }, }, ], "discuss.channel.rtc.session": [ { "channel_member_id": channel_member.id, "id": channel_member.rtc_session_ids.id, "is_camera_on": channel_member.rtc_session_ids.is_camera_on, "is_deaf": channel_member.rtc_session_ids.is_deaf, "is_muted": channel_member.rtc_session_ids.is_muted, "is_screen_sharing_on": channel_member.rtc_session_ids.is_screen_sharing_on, }, ], "res.partner": self._filter_partners_fields( { "avatar_128_access_token": channel_member.partner_id._get_avatar_128_access_token(), "id": channel_member.partner_id.id, "im_status": channel_member.partner_id.im_status, "im_status_access_token": channel_member.partner_id._get_im_status_access_token(), "mention_token": channel_member.partner_id._get_mention_token(), "name": channel_member.partner_id.name, "write_date": fields.Datetime.to_string( channel_member.partner_id.write_date ), }, ), }, }, { "type": "mail.record/insert", "payload": { "discuss.channel.member": [ { "channel_id": { "id": channel_member_test_guest.channel_id.id, "model": "discuss.channel", }, "guest_id": channel_member_test_guest.guest_id.id, "id": channel_member_test_guest.id, "rtc_inviting_session_id": channel_member_test_guest.rtc_inviting_session_id.id, }, { "id": channel_member.id, "partner_id": channel_member.partner_id.id, "channel_id": { "id": channel_member.channel_id.id, "model": "discuss.channel", }, }, ], "discuss.channel.rtc.session": [ { "channel_member_id": channel_member.id, "id": channel_member.rtc_session_ids.id, "is_camera_on": channel_member.rtc_session_ids.is_camera_on, "is_deaf": channel_member.rtc_session_ids.is_deaf, "is_muted": channel_member.rtc_session_ids.is_muted, "is_screen_sharing_on": channel_member.rtc_session_ids.is_screen_sharing_on, }, ], "res.partner": self._filter_partners_fields( { "avatar_128_access_token": channel_member.partner_id._get_avatar_128_access_token(), "id": channel_member.partner_id.id, "im_status": channel_member.partner_id.im_status, "im_status_access_token": channel_member.partner_id._get_im_status_access_token(), "mention_token": channel_member.partner_id._get_mention_token(), "name": channel_member.partner_id.name, "write_date": fields.Datetime.to_string( channel_member.partner_id.write_date ), }, ), }, }, { "type": "mail.record/insert", "payload": { "discuss.channel": [ { "id": channel.id, "invited_member_ids": [ ( "ADD", [channel_member_test_user.id, channel_member_test_guest.id], ) ], } ], "discuss.channel.member": [ { "id": channel_member_test_user.id, "partner_id": channel_member_test_user.partner_id.id, "channel_id": { "id": channel_member_test_user.channel_id.id, "model": "discuss.channel", }, }, { "id": channel_member_test_guest.id, "guest_id": channel_member_test_guest.guest_id.id, "channel_id": { "id": channel_member_test_guest.channel_id.id, "model": "discuss.channel", }, }, ], "mail.guest": [ { "avatar_128_access_token": channel_member_test_guest.guest_id._get_avatar_128_access_token(), "id": channel_member_test_guest.guest_id.id, "im_status": channel_member_test_guest.guest_id.im_status, "im_status_access_token": channel_member_test_guest.guest_id._get_im_status_access_token(), "name": channel_member_test_guest.guest_id.name, "write_date": fields.Datetime.to_string( channel_member_test_guest.guest_id.write_date ), }, ], "res.partner": self._filter_partners_fields( { "avatar_128_access_token": channel_member_test_user.partner_id._get_avatar_128_access_token(), "id": channel_member_test_user.partner_id.id, "im_status": channel_member_test_user.partner_id.im_status, "im_status_access_token": channel_member_test_user.partner_id._get_im_status_access_token(), "mention_token": channel_member_test_user.partner_id._get_mention_token(), "name": channel_member_test_user.partner_id.name, "write_date": fields.Datetime.to_string( channel_member_test_user.partner_id.write_date ), }, ), }, }, ], ) self.assertEqual(self._new_bus_notifs, found_bus_notifs) @users('employee') @mute_logger('odoo.models.unlink') def test_40_leave_call_should_remove_existing_sessions_of_user_in_channel_and_return_data(self): channel = self.env['discuss.channel']._create_group(partners_to=self.user_employee.partner_id.ids) channel_member = channel.sudo().channel_member_ids.filtered(lambda channel_member: channel_member.partner_id == self.user_employee.partner_id) channel_member._rtc_join_call() with self.assertBus( [ # update list of sessions (self.cr.dbname, "discuss.channel", channel.id), # end session (self.cr.dbname, "res.partner", self.user_employee.partner_id.id), # update call history (not asserted below) (self.cr.dbname, "discuss.channel", channel.id), ], [ { "type": "discuss.channel.rtc.session/ended", "payload": {"sessionId": channel_member.rtc_session_ids.id}, }, { "type": "mail.record/insert", "payload": { "discuss.channel": [ { "id": channel.id, "rtc_session_ids": [("DELETE", [channel_member.rtc_session_ids.id])], }, ], }, }, ], ): now = fields.Datetime.now() with patch.object(fields.Datetime, 'now', lambda: now + relativedelta(seconds=5)): channel_member._rtc_leave_call() @users('employee') @mute_logger('odoo.models.unlink') def test_50_garbage_collect_should_remove_old_sessions_and_notify_data(self): self.env["discuss.channel.rtc.session"].sudo().search([]).unlink() # clean up before test channel = self.env['discuss.channel']._create_group(partners_to=self.user_employee.partner_id.ids) channel_member = channel.sudo().channel_member_ids.filtered(lambda channel_member: channel_member.partner_id == self.user_employee.partner_id) channel_member._rtc_join_call() channel_member.rtc_session_ids.flush_model() channel_member.rtc_session_ids._write({'write_date': fields.Datetime.now() - relativedelta(days=2)}) with self.assertBus( [ # update list of sessions (self.cr.dbname, "discuss.channel", channel.id), # session ended (self.cr.dbname, "res.partner", self.user_employee.partner_id.id), # update call history duration (self.cr.dbname, "discuss.channel", channel.id), ], [ { "type": "discuss.channel.rtc.session/ended", "payload": {"sessionId": channel_member.rtc_session_ids.id}, }, { "type": "mail.record/insert", "payload": { "discuss.channel": [ { "id": channel.id, "rtc_session_ids": [("DELETE", [channel_member.rtc_session_ids.id])], }, ], }, }, ], ): self.env['discuss.channel.rtc.session'].sudo()._gc_inactive_sessions() self.assertFalse(channel_member.rtc_session_ids) @users('employee') @mute_logger('odoo.models.unlink') def test_51_action_disconnect_should_remove_selected_session_and_notify_data(self): channel = self.env['discuss.channel']._create_group(partners_to=self.user_employee.partner_id.ids) channel_member = channel.sudo().channel_member_ids.filtered(lambda channel_member: channel_member.partner_id == self.user_employee.partner_id) channel_member._rtc_join_call() with self.assertBus( [ # update list of sessions (self.cr.dbname, "discuss.channel", channel.id), # session ended (self.cr.dbname, "res.partner", self.user_employee.partner_id.id), # update call history duration (self.cr.dbname, "discuss.channel", channel.id), ], [ { "type": "discuss.channel.rtc.session/ended", "payload": {"sessionId": channel_member.rtc_session_ids.id}, }, { "type": "mail.record/insert", "payload": { "discuss.channel": [ { "id": channel.id, "rtc_session_ids": [("DELETE", [channel_member.rtc_session_ids.id])], }, ], }, }, ], ): channel_member.rtc_session_ids.action_disconnect() self.assertFalse(channel_member.rtc_session_ids) @users('employee') @mute_logger('odoo.models.unlink') def test_60_rtc_sync_sessions_should_gc_and_return_outdated_and_active_sessions(self): channel = self.env['discuss.channel']._create_group(partners_to=self.user_employee.partner_id.ids) channel_member = channel.sudo().channel_member_ids.filtered(lambda channel_member: channel_member.partner_id == self.user_employee.partner_id) store = Store() channel_member._rtc_join_call(store) join_call_values = store.get_result() test_guest = self.env['mail.guest'].sudo().create({'name': "Test Guest"}) test_channel_member = self.env['discuss.channel.member'].create({ 'guest_id': test_guest.id, 'channel_id': channel.id, }) test_session = self.env['discuss.channel.rtc.session'].sudo().create({'channel_member_id': test_channel_member.id}) test_session.flush_model() test_session._write({'write_date': fields.Datetime.now() - relativedelta(days=2)}) unused_ids = [9998, 9999] with self.assertBus( [ # update list of sessions (self.cr.dbname, "discuss.channel", channel.id), # session ended (self.cr.dbname, "mail.guest", test_guest.id), ], [ { "type": "discuss.channel.rtc.session/ended", "payload": {"sessionId": test_session.id}, }, { "type": "mail.record/insert", "payload": { "discuss.channel": [ {"id": channel.id, "rtc_session_ids": [("DELETE", [test_session.id])]}, ], }, }, ], ): current_rtc_sessions, outdated_rtc_sessions = channel_member._rtc_sync_sessions( check_rtc_session_ids=[join_call_values["Rtc"]["localSession"]] + unused_ids ) self.assertEqual(channel_member.rtc_session_ids, current_rtc_sessions) self.assertEqual(unused_ids, outdated_rtc_sessions.ids) self.assertFalse(outdated_rtc_sessions.exists()) def test_07_call_invitation_ui(self): bob = new_test_user(self.env, "bob", groups="base.group_user", email="bob@test.com") john = new_test_user(self.env, "john", groups="base.group_user", email="john@test.com") channel = self.env["discuss.channel"].with_user(bob)._create_group(partners_to=(bob | john).partner_id.ids) channel.with_user(bob).self_member_id.sudo()._rtc_join_call() self._reset_bus() self.start_tour("/odoo", "discuss_call_invitation.js", login="john")