Merged in [19969] from jennifer@painless-security.com:

Use correct UTC time when creating Meetecho conferences. Fixes #3565.
 - Legacy-Id: 19970
Note: SVN reference [19969] has been migrated to Git commit 82ea659ed4
This commit is contained in:
Robert Sparks 2022-02-23 21:04:25 +00:00
commit 9fbc98a16d
4 changed files with 54 additions and 41 deletions

View file

@ -1099,7 +1099,7 @@ def create_interim_session_conferences(sessions):
confs = meetecho_manager.create(
group=session.group,
description=str(session),
start_time=ts.time,
start_time=ts.utc_start_time(),
duration=ts.duration,
)
except Exception as err:

View file

@ -456,8 +456,8 @@ class InterimTests(TestCase):
def test_create_interim_session_conferences(self, mock):
mock_conf_mgr = mock.return_value # "instance" seen by the internals
sessions = [
SessionFactory(meeting__type_id='interim', remote_instructions='junk'),
SessionFactory(meeting__type_id='interim', remote_instructions=''),
SessionFactory(meeting__type_id='interim', meeting__time_zone='america/halifax', remote_instructions='junk'),
SessionFactory(meeting__type_id='interim', meeting__time_zone='asia/kuala_lumpur', remote_instructions=''),
]
timeslots = [
session.official_timeslotassignment().timeslot for session in sessions
@ -482,18 +482,18 @@ class InterimTests(TestCase):
mock_conf_mgr.create.return_value = [
Conference(
manager=mock_conf_mgr, id=1, public_id='some-uuid', description='desc',
start_time=timeslots[0].time, duration=timeslots[0].duration, url='fake-meetecho-url',
start_time=timeslots[0].utc_start_time(), duration=timeslots[0].duration, url='fake-meetecho-url',
deletion_token='please-delete-me',
),
]
create_interim_session_conferences([sessions[0]])
self.assertTrue(mock_conf_mgr.create.called)
self.assertCountEqual(
self.assertEqual(
mock_conf_mgr.create.call_args[1],
{
'group': sessions[0].group,
'description': str(sessions[0]),
'start_time': timeslots[0].time,
'start_time': timeslots[0].utc_start_time(),
'duration': timeslots[0].duration,
}
)
@ -507,30 +507,30 @@ class InterimTests(TestCase):
mock_conf_mgr.create.side_effect = [
[Conference(
manager=mock_conf_mgr, id=1, public_id='some-uuid', description='desc',
start_time=timeslots[0].time, duration=timeslots[0].duration, url='different-fake-meetecho-url',
start_time=timeslots[0].utc_start_time(), duration=timeslots[0].duration, url='different-fake-meetecho-url',
deletion_token='please-delete-me',
)],
[Conference(
manager=mock_conf_mgr, id=2, public_id='another-uuid', description='desc',
start_time=timeslots[1].time, duration=timeslots[1].duration, url='another-fake-meetecho-url',
start_time=timeslots[1].utc_start_time(), duration=timeslots[1].duration, url='another-fake-meetecho-url',
deletion_token='please-delete-me-too',
)],
]
create_interim_session_conferences([sessions[0], sessions[1]])
self.assertTrue(mock_conf_mgr.create.called)
self.assertCountEqual(
self.assertEqual(
mock_conf_mgr.create.call_args_list,
[
({
'group': sessions[0].group,
'description': str(sessions[0]),
'start_time': timeslots[0].time,
'start_time': timeslots[0].utc_start_time(),
'duration': timeslots[0].duration,
},),
({
'group': sessions[1].group,
'description': str(sessions[1]),
'start_time': timeslots[1].time,
'start_time': timeslots[1].utc_start_time(),
'duration': timeslots[1].duration,
},),
]

View file

@ -15,11 +15,14 @@ import debug # pyflakes: ignore
from datetime import datetime, timedelta
from json import JSONDecodeError
from pytz import utc
from typing import Dict, Sequence, Union
from urllib.parse import urljoin
class MeetechoAPI:
timezone = utc
def __init__(self, api_base: str, client_id: str, client_secret: str, request_timeout=3.01):
self.client_id = client_id
self.client_secret = client_secret
@ -57,10 +60,10 @@ class MeetechoAPI:
return None
def _deserialize_time(self, s: str) -> datetime:
return datetime.strptime(s, '%Y-%m-%d %H:%M:%S')
return self.timezone.localize(datetime.strptime(s, '%Y-%m-%d %H:%M:%S'))
def _serialize_time(self, dt: datetime) -> str:
return dt.strftime('%Y-%m-%d %H:%M:%S')
return dt.astimezone(self.timezone).strftime('%Y-%m-%d %H:%M:%S')
def _deserialize_duration(self, minutes: int) -> timedelta:
return timedelta(minutes=minutes)

View file

@ -4,6 +4,7 @@ import datetime
import requests
import requests_mock
from pytz import timezone, utc
from unittest.mock import patch
from urllib.parse import urljoin
@ -91,7 +92,7 @@ class APITests(TestCase):
api = MeetechoAPI(API_BASE, CLIENT_ID, CLIENT_SECRET)
api_response = api.schedule_meeting(
wg_token='my-token',
start_time=datetime.datetime(2021, 9, 14, 10, 0, 0),
start_time=utc.localize(datetime.datetime(2021, 9, 14, 10, 0, 0)),
duration=datetime.timedelta(minutes=130),
description='interim-2021-wgname-01',
extrainfo='message for staff',
@ -117,23 +118,32 @@ class APITests(TestCase):
},
'Incorrect request content'
)
self.assertEqual(
api_response,
{
'rooms': {
'3d55bce0-535e-4ba8-bb8e-734911cf3c32': {
'room': {
'id': 18,
'start_time': datetime.datetime(2021, 9, 14, 10, 0, 0),
'duration': datetime.timedelta(minutes=130),
'description': 'interim-2021-wgname-01',
# same time in different time zones
for start_time in [
utc.localize(datetime.datetime(2021, 9, 14, 10, 0, 0)),
timezone('america/halifax').localize(datetime.datetime(2021, 9, 14, 7, 0, 0)),
timezone('europe/kiev').localize(datetime.datetime(2021, 9, 14, 13, 0, 0)),
timezone('pacific/easter').localize(datetime.datetime(2021, 9, 14, 5, 0, 0)),
timezone('africa/porto-novo').localize(datetime.datetime(2021, 9, 14, 11, 0, 0)),
]:
self.assertEqual(
api_response,
{
'rooms': {
'3d55bce0-535e-4ba8-bb8e-734911cf3c32': {
'room': {
'id': 18,
'start_time': start_time,
'duration': datetime.timedelta(minutes=130),
'description': 'interim-2021-wgname-01',
},
'url': 'https://meetings.conf.meetecho.com/interim/?short=3d55bce0-535e-4ba8-bb8e-734911cf3c32',
'deletion_token': 'session-deletion-token',
},
'url': 'https://meetings.conf.meetecho.com/interim/?short=3d55bce0-535e-4ba8-bb8e-734911cf3c32',
'deletion_token': 'session-deletion-token',
},
}
},
)
}
},
f'Incorrect time conversion for {start_time.tzinfo.zone}',
)
def test_fetch_meetings(self):
self.maxDiff = 2048
@ -181,7 +191,7 @@ class APITests(TestCase):
'3d55bce0-535e-4ba8-bb8e-734911cf3c32': {
'room': {
'id': 18,
'start_time': datetime.datetime(2021, 9, 14, 10, 0, 0),
'start_time': utc.localize(datetime.datetime(2021, 9, 14, 10, 0, 0)),
'duration': datetime.timedelta(minutes=130),
'description': 'interim-2021-wgname-01',
},
@ -191,7 +201,7 @@ class APITests(TestCase):
'e68e96d4-d38f-475b-9073-ecab46ca96a5': {
'room': {
'id': 23,
'start_time': datetime.datetime(2021, 9, 15, 14, 30, 0),
'start_time': utc.localize(datetime.datetime(2021, 9, 15, 14, 30, 0)),
'duration': datetime.timedelta(minutes=30),
'description': 'interim-2021-wgname-02',
},
@ -239,7 +249,7 @@ class APITests(TestCase):
def test_time_serialization(self):
"""Time de/serialization should be consistent"""
time = datetime.datetime.now().replace(microsecond=0) # cut off to 0 microseconds
time = datetime.datetime.now(utc).replace(microsecond=0) # cut off to 0 microseconds
api = MeetechoAPI(API_BASE, CLIENT_ID, CLIENT_SECRET)
self.assertEqual(api._deserialize_time(api._serialize_time(time)), time)
@ -253,7 +263,7 @@ class ConferenceManagerTests(TestCase):
'session-1-uuid': {
'room': {
'id': 1,
'start_time': datetime.datetime(2022,2,4,1,2,3),
'start_time': utc.localize(datetime.datetime(2022,2,4,1,2,3)),
'duration': datetime.timedelta(minutes=45),
'description': 'some-description',
},
@ -263,7 +273,7 @@ class ConferenceManagerTests(TestCase):
'session-2-uuid': {
'room': {
'id': 2,
'start_time': datetime.datetime(2022,2,5,4,5,6),
'start_time': utc.localize(datetime.datetime(2022,2,5,4,5,6)),
'duration': datetime.timedelta(minutes=90),
'description': 'another-description',
},
@ -280,7 +290,7 @@ class ConferenceManagerTests(TestCase):
id=1,
public_id='session-1-uuid',
description='some-description',
start_time=datetime.datetime(2022,2,4,1,2,3),
start_time=utc.localize(datetime.datetime(2022, 2, 4, 1, 2, 3)),
duration=datetime.timedelta(minutes=45),
url='https://example.com/some/url',
deletion_token='delete-me',
@ -290,7 +300,7 @@ class ConferenceManagerTests(TestCase):
id=2,
public_id='session-2-uuid',
description='another-description',
start_time=datetime.datetime(2022,2,5,4,5,6),
start_time=utc.localize(datetime.datetime(2022, 2, 5, 4, 5, 6)),
duration=datetime.timedelta(minutes=90),
url='https://example.com/another/url',
deletion_token='delete-me-too',
@ -306,7 +316,7 @@ class ConferenceManagerTests(TestCase):
'session-1-uuid': {
'room': {
'id': 1,
'start_time': datetime.datetime(2022,2,4,1,2,3),
'start_time': utc.localize(datetime.datetime(2022,2,4,1,2,3)),
'duration': datetime.timedelta(minutes=45),
'description': 'some-description',
},
@ -325,7 +335,7 @@ class ConferenceManagerTests(TestCase):
id=1,
public_id='session-1-uuid',
description='some-description',
start_time=datetime.datetime(2022,2,4,1,2,3),
start_time=utc.localize(datetime.datetime(2022,2,4,1,2,3)),
duration=datetime.timedelta(minutes=45),
url='https://example.com/some/url',
deletion_token='delete-me',
@ -341,7 +351,7 @@ class ConferenceManagerTests(TestCase):
'session-1-uuid': {
'room': {
'id': 1,
'start_time': datetime.datetime(2022,2,4,1,2,3),
'start_time': utc.localize(datetime.datetime(2022,2,4,1,2,3)),
'duration': datetime.timedelta(minutes=45),
'description': 'some-description',
},
@ -359,7 +369,7 @@ class ConferenceManagerTests(TestCase):
id=1,
public_id='session-1-uuid',
description='some-description',
start_time=datetime.datetime(2022,2,4,1,2,3),
start_time=utc.localize(datetime.datetime(2022,2,4,1,2,3)),
duration=datetime.timedelta(minutes=45),
url='https://example.com/some/url',
deletion_token='delete-me',