diff --git a/addons/google_gmail/models/google_gmail_mixin.py b/addons/google_gmail/models/google_gmail_mixin.py index 289a0443fb81f..28ecef919764b 100644 --- a/addons/google_gmail/models/google_gmail_mixin.py +++ b/addons/google_gmail/models/google_gmail_mixin.py @@ -144,10 +144,14 @@ def _generate_oauth2_string(self, user, refresh_token): :return: The SASL argument for the OAuth2 mechanism. """ self.ensure_one() + # token life is around 1 hour, add 5s threshold to gives + # time to contact google servers and avoid clock synchronization + # problems + threshold_seconds = 5 now_timestamp = int(time.time()) if not self.google_gmail_access_token \ or not self.google_gmail_access_token_expiration \ - or self.google_gmail_access_token_expiration < now_timestamp: + or self.google_gmail_access_token_expiration - threshold_seconds < now_timestamp: access_token, expiration = self._fetch_gmail_access_token(self.google_gmail_refresh_token) diff --git a/addons/google_gmail/tests/__init__.py b/addons/google_gmail/tests/__init__.py new file mode 100644 index 0000000000000..7a800575a7fe4 --- /dev/null +++ b/addons/google_gmail/tests/__init__.py @@ -0,0 +1 @@ +from . import test_google_gmail diff --git a/addons/google_gmail/tests/test_google_gmail.py b/addons/google_gmail/tests/test_google_gmail.py new file mode 100644 index 0000000000000..9b8baf00abc2c --- /dev/null +++ b/addons/google_gmail/tests/test_google_gmail.py @@ -0,0 +1,50 @@ +from odoo.tests.common import SavepointCase +from unittest import mock +import time + +class TestIrMailServer(SavepointCase): + + def test_generate_oauth2_string_token_valid(self): + now_timestamp = int(time.time()) + mail_server = self.env["ir.mail_server"].new( + { + "google_gmail_access_token": "fake_access_token", + "google_gmail_access_token_expiration": now_timestamp + 10, + } + ) + oauth2_string = mail_server._generate_oauth2_string("user-account", "refresh-token") + + self.assertIn("fake_access_token", oauth2_string) + + + def test_generate_oauth2_string_token_expire_in_less_than_5s(self): + now_timestamp = int(time.time()) + mail_server = self.env["ir.mail_server"].new( + { + "google_gmail_access_token": "fake_access_token", + "google_gmail_access_token_expiration": now_timestamp + 2, + } + ) + with mock.patch( + "odoo.addons.google_gmail.models.google_gmail_mixin.GoogleGmailMixin._fetch_gmail_access_token", + return_value=("new-access-token", now_timestamp + 60*60) + ): + oauth2_string = mail_server._generate_oauth2_string("user-account", "refresh-token") + + self.assertIn("new-access-token", oauth2_string) + + def test_generate_oauth2_string_token_expired(self): + now_timestamp = int(time.time()) + mail_server = self.env["ir.mail_server"].new( + { + "google_gmail_access_token": "fake_access_token", + "google_gmail_access_token_expiration": now_timestamp - 2, + } + ) + with mock.patch( + "odoo.addons.google_gmail.models.google_gmail_mixin.GoogleGmailMixin._fetch_gmail_access_token", + return_value=("new-access-token", now_timestamp + 60*60) + ): + oauth2_string = mail_server._generate_oauth2_string("user-account", "refresh-token") + + self.assertIn("new-access-token", oauth2_string) \ No newline at end of file diff --git a/addons/mail/models/mail_mail.py b/addons/mail/models/mail_mail.py index 2e32ce22451dd..ec5368a16e701 100644 --- a/addons/mail/models/mail_mail.py +++ b/addons/mail/models/mail_mail.py @@ -278,7 +278,12 @@ def send(self, auto_commit=False, raise_exception=False): len(batch_ids), server_id) finally: if smtp_session: - smtp_session.quit() + try: + smtp_session.quit() + except smtplib.SMTPServerDisconnected: + _logger.info( + "Ignoring SMTPServerDisconnected while trying to quit non open session" + ) def _send(self, auto_commit=False, raise_exception=False, smtp_session=None): IrMailServer = self.env['ir.mail_server'] diff --git a/addons/mail/tests/__init__.py b/addons/mail/tests/__init__.py index 7287046d6ccac..9bdf6981482d8 100644 --- a/addons/mail/tests/__init__.py +++ b/addons/mail/tests/__init__.py @@ -5,3 +5,4 @@ from . import test_mail_render from . import test_res_partner from . import test_update_notification +from . import test_mail_mail \ No newline at end of file diff --git a/addons/mail/tests/test_mail_mail.py b/addons/mail/tests/test_mail_mail.py new file mode 100644 index 0000000000000..07569c623750f --- /dev/null +++ b/addons/mail/tests/test_mail_mail.py @@ -0,0 +1,17 @@ +from odoo.tests import SavepointCase +from unittest import mock +import smtplib + + +class MailCase(SavepointCase): + + def test_mail_send_missing_not_connected(self): + """This assume while calling self.env['ir.mail_server'].connect() it return + an disconnected smtp_session which is a non falsy object value""" + + not_connected = smtplib.SMTP(local_hostname="fake-hostname.com", port=9999, timeout=1) + mail = self.env["mail.mail"].new({}) + with mock.patch("odoo.addons.base.models.ir_mail_server.IrMailServer.connect", return_value=not_connected): + mail.send() + # if we get here SMTPServerDisconnected was not raised + self.assertEqual(mail.state, "outgoing") \ No newline at end of file