399 lines
20 KiB
Python
399 lines
20 KiB
Python
# Copyright The IETF Trust 2013-2020, All Rights Reserved
|
|
# -*- coding: utf-8 -*-
|
|
|
|
|
|
import datetime
|
|
|
|
from pyquery import PyQuery
|
|
|
|
import debug # pyflakes:ignore
|
|
|
|
from django.urls import reverse
|
|
from django.utils import timezone
|
|
|
|
from ietf.group.models import Group, GroupEvent
|
|
from ietf.meeting.factories import MeetingFactory
|
|
from ietf.meeting.models import Meeting, Room, TimeSlot, SchedTimeSessAssignment, Session, SchedulingEvent
|
|
from ietf.meeting.test_data import make_meeting_test_data
|
|
from ietf.name.models import ConstraintName, SessionStatusName
|
|
from ietf.person.models import Person
|
|
from ietf.secr.meetings.forms import get_times
|
|
from ietf.utils.mail import outbox
|
|
from ietf.utils.test_utils import TestCase
|
|
|
|
|
|
class SecrMeetingTestCase(TestCase):
|
|
settings_temp_path_overrides = TestCase.settings_temp_path_overrides + ['AGENDA_PATH']
|
|
|
|
def test_main(self):
|
|
"Main Test"
|
|
meeting = make_meeting_test_data()
|
|
url = reverse('ietf.secr.meetings.views.main')
|
|
self.client.login(username="secretary", password="secretary+password")
|
|
response = self.client.get(url)
|
|
self.assertEqual(response.status_code, 200)
|
|
response = self.client.post(url, {'meeting':meeting.number})
|
|
url = reverse('ietf.secr.meetings.views.view', kwargs={'meeting_id':meeting.number})
|
|
self.assertRedirects(response,url)
|
|
|
|
def test_view(self):
|
|
"View Test"
|
|
meeting = make_meeting_test_data()
|
|
url = reverse('ietf.secr.meetings.views.view', kwargs={'meeting_id':meeting.number})
|
|
self.client.login(username="secretary", password="secretary+password")
|
|
response = self.client.get(url)
|
|
self.assertEqual(response.status_code, 200)
|
|
q = PyQuery(response.content)
|
|
self.assertEqual({option.get('value') for option in q('#id_schedule_selector option:not([value=""])')}, {'base', 'test-schedule', 'test-unofficial-schedule'})
|
|
|
|
def test_add_meeting(self):
|
|
"Add Meeting"
|
|
meeting = make_meeting_test_data()
|
|
number = int(meeting.number) + 1
|
|
count = Meeting.objects.count()
|
|
url = reverse('ietf.secr.meetings.views.add')
|
|
post_data = dict(
|
|
number=number,city='Toronto',date='2014-07-20',country='CA',
|
|
time_zone='America/New_York',venue_name='Hilton',
|
|
days=6,
|
|
venue_addr='100 First Ave',
|
|
idsubmit_cutoff_day_offset_00=13,
|
|
idsubmit_cutoff_day_offset_01=20,
|
|
idsubmit_cutoff_time_utc =datetime.timedelta(hours=23, minutes=59, seconds=59),
|
|
idsubmit_cutoff_warning_days =datetime.timedelta(days=21),
|
|
submission_start_day_offset=90,
|
|
submission_cutoff_day_offset=26,
|
|
submission_correction_day_offset=50,
|
|
group_conflict_types=('conflict', 'conflic2', 'key_participant'),
|
|
)
|
|
|
|
self.client.login(username='secretary', password='secretary+password')
|
|
response = self.client.post(url, post_data, follow=True)
|
|
self.assertEqual(response.status_code, 200)
|
|
self.assertEqual(Meeting.objects.count(),count + 1)
|
|
new_meeting = Meeting.objects.get(number=number)
|
|
|
|
self.assertTrue(new_meeting.schedule)
|
|
self.assertEqual(new_meeting.schedule.name, 'sec-retary-1')
|
|
self.assertTrue(new_meeting.schedule.base)
|
|
self.assertEqual(new_meeting.schedule.base.name, 'base')
|
|
self.assertEqual(new_meeting.attendees, None)
|
|
self.assertCountEqual(
|
|
[cn.slug for cn in new_meeting.group_conflict_types.all()],
|
|
post_data['group_conflict_types'],
|
|
)
|
|
|
|
def test_add_meeting_default_conflict_types(self):
|
|
"""Add meeting should default to same conflict types as previous meeting"""
|
|
def _run_test(mtg):
|
|
url = reverse('ietf.secr.meetings.views.add')
|
|
r = self.client.get(url)
|
|
q = PyQuery(r.content)
|
|
selected_items = q('#id_group_conflict_types input[checked]')
|
|
selected_values = [si.value for si in selected_items]
|
|
expected_values = [cn.slug for cn in mtg.group_conflict_types.all()]
|
|
self.assertCountEqual(selected_values, expected_values)
|
|
|
|
self.client.login(username='secretary', password='secretary+password')
|
|
|
|
meeting = MeetingFactory(type_id='ietf', group_conflicts=[]) # start with no conflicts selected
|
|
_run_test(meeting)
|
|
|
|
# enable one
|
|
meeting.group_conflict_types.add(ConstraintName.objects.filter(is_group_conflict=True).first())
|
|
self.assertEqual(meeting.group_conflict_types.count(), 1)
|
|
_run_test(meeting)
|
|
|
|
# enable a few ([::2] selects every other)
|
|
meeting.group_conflict_types.clear()
|
|
for cn in ConstraintName.objects.filter(is_group_conflict=True)[::2]:
|
|
meeting.group_conflict_types.add(cn)
|
|
self.assertGreater(meeting.group_conflict_types.count(), 1)
|
|
_run_test(meeting)
|
|
|
|
def test_edit_meeting(self):
|
|
"Edit Meeting"
|
|
meeting = make_meeting_test_data()
|
|
url = reverse('ietf.secr.meetings.views.edit_meeting',kwargs={'meeting_id':meeting.number})
|
|
post_data = dict(number=meeting.number,
|
|
date='2014-07-20',
|
|
city='Toronto',
|
|
time_zone='America/Toronto',
|
|
days=7,
|
|
idsubmit_cutoff_day_offset_00=13,
|
|
idsubmit_cutoff_day_offset_01=20,
|
|
idsubmit_cutoff_time_utc =datetime.timedelta(hours=23, minutes=59, seconds=59),
|
|
idsubmit_cutoff_warning_days =datetime.timedelta(days=21),
|
|
submission_start_day_offset=90,
|
|
submission_cutoff_day_offset=26,
|
|
submission_correction_day_offset=50,
|
|
attendees=1234,
|
|
group_conflict_types=[
|
|
cn.slug for cn in ConstraintName.objects.filter(
|
|
is_group_conflict=True
|
|
).exclude(
|
|
meeting=meeting, # replace original set with those not assigned to the meeting
|
|
)
|
|
]
|
|
)
|
|
self.assertGreater(len(post_data['group_conflict_types']), 0) # test should include at least one
|
|
self.client.login(username="secretary", password="secretary+password")
|
|
response = self.client.post(url, post_data, follow=True)
|
|
self.assertEqual(response.status_code, 200)
|
|
meeting = Meeting.objects.get(number=meeting.number)
|
|
self.assertEqual(meeting.city,'Toronto')
|
|
self.assertEqual(meeting.attendees,1234)
|
|
self.assertCountEqual(
|
|
[cn.slug for cn in meeting.group_conflict_types.all()],
|
|
post_data['group_conflict_types'],
|
|
)
|
|
|
|
def test_notifications(self):
|
|
"Test Notifications"
|
|
meeting = make_meeting_test_data()
|
|
mars_group = Group.objects.get(acronym='mars')
|
|
ames_group = Group.objects.get(acronym='ames')
|
|
ames_stsa = meeting.schedule.assignments.get(session__group=ames_group)
|
|
assert SchedulingEvent.objects.filter(session=ames_stsa.session).order_by('-id')[0].status_id == 'schedw'
|
|
mars_stsa = meeting.schedule.assignments.get(session__group=mars_group)
|
|
SchedulingEvent.objects.create(session=mars_stsa.session, status=SessionStatusName.objects.get(slug='appr'), by=Person.objects.get(name="(System)"))
|
|
url = reverse('ietf.secr.meetings.views.notifications',kwargs={'meeting_id':72})
|
|
self.client.login(username="secretary", password="secretary+password")
|
|
response = self.client.get(url)
|
|
self.assertEqual(response.status_code, 200)
|
|
q = PyQuery(response.content)
|
|
self.assertEqual(q('#id_notification_list').html(),'ames, mars')
|
|
|
|
# test that only changes since last notification show up
|
|
now = timezone.now()
|
|
then = timezone.now()+datetime.timedelta(hours=1)
|
|
person = Person.objects.get(name="(System)")
|
|
GroupEvent.objects.create(group=mars_group,time=now,type='sent_notification',
|
|
by=person,desc='sent scheduled notification for %s' % meeting)
|
|
ss = meeting.schedule.assignments.get(session__group=ames_group)
|
|
ss.modified = then
|
|
ss.save()
|
|
self.client.login(username="secretary", password="secretary+password")
|
|
response = self.client.get(url)
|
|
self.assertEqual(response.status_code, 200)
|
|
q = PyQuery(response.content)
|
|
self.assertEqual(q('#id_notification_list').html(),'ames')
|
|
|
|
# test post: email goes out, status changed
|
|
mailbox_before = len(outbox)
|
|
self.client.login(username="secretary", password="secretary+password")
|
|
response = self.client.post(url)
|
|
self.assertEqual(response.status_code, 302)
|
|
self.assertEqual(len(outbox), mailbox_before + 1)
|
|
ames_stsa = meeting.schedule.assignments.get(session__group=ames_group)
|
|
self.assertEqual(SchedulingEvent.objects.filter(session=ames_stsa.session).order_by('-id')[0].status_id, 'sched')
|
|
mars_stsa = meeting.schedule.assignments.get(session__group=mars_group)
|
|
self.assertEqual(SchedulingEvent.objects.filter(session=mars_stsa.session).order_by('-id')[0].status_id, 'sched')
|
|
|
|
def test_meetings_rooms(self):
|
|
meeting = make_meeting_test_data()
|
|
url = reverse('ietf.secr.meetings.views.rooms',kwargs={'meeting_id':72,'schedule_name':'test-schedule'})
|
|
self.client.login(username="secretary", password="secretary+password")
|
|
response = self.client.get(url)
|
|
self.assertEqual(response.status_code, 200)
|
|
q = PyQuery(response.content)
|
|
self.assertEqual(len(q("#id_rooms_table tr input[type='checkbox']")),meeting.room_set.count()+1)
|
|
|
|
# test delete
|
|
# first unschedule sessions so we can delete
|
|
SchedTimeSessAssignment.objects.filter(schedule__in=[meeting.schedule, meeting.schedule.base, meeting.unofficial_schedule]).delete()
|
|
self.client.login(username="secretary", password="secretary+password")
|
|
post_dict = {
|
|
'room-TOTAL_FORMS': q('input[name="room-TOTAL_FORMS"]').val(),
|
|
'room-INITIAL_FORMS': q('input[name="room-INITIAL_FORMS"]').val(),
|
|
}
|
|
for i in range(meeting.room_set.count()):
|
|
for attr in ['meeting','id','name','capacity','DELETE']:
|
|
key = 'room-%d-%s' % (i,attr)
|
|
post_dict[key] = q('input[name="%s"]' % key).val()
|
|
response = self.client.post(url, post_dict)
|
|
self.assertEqual(response.status_code, 302)
|
|
self.assertEqual(Room.objects.filter(meeting=meeting).count(),0)
|
|
|
|
def test_meetings_times(self):
|
|
make_meeting_test_data()
|
|
url = reverse('ietf.secr.meetings.views.times',kwargs={'meeting_id':72,'schedule_name':'test-schedule'})
|
|
self.client.login(username="secretary", password="secretary+password")
|
|
response = self.client.get(url)
|
|
self.assertEqual(response.status_code, 200)
|
|
response = self.client.post(url, {
|
|
'day': 0,
|
|
'time':'08:00',
|
|
'duration':'01:00',
|
|
'name':'Test Morning Session'
|
|
}, follow=True)
|
|
self.assertRedirects(response, url)
|
|
self.assertContains(response, 'Test Morning Session')
|
|
|
|
def test_meetings_times_delete(self):
|
|
meeting = make_meeting_test_data()
|
|
qs = TimeSlot.objects.filter(meeting=meeting,type='regular')
|
|
before = qs.count()
|
|
expected_deletion_count = qs.filter(time=qs.first().time).count()
|
|
url = reverse('ietf.secr.meetings.views.times_delete',kwargs={
|
|
'meeting_id':meeting.number,
|
|
'schedule_name':meeting.schedule.name,
|
|
'time':qs.first().time.astimezone(meeting.tz()).strftime("%Y:%m:%d:%H:%M")
|
|
})
|
|
redirect_url = reverse('ietf.secr.meetings.views.times',kwargs={
|
|
'meeting_id':meeting.number,
|
|
'schedule_name':meeting.schedule.name
|
|
})
|
|
self.client.login(username="secretary", password="secretary+password")
|
|
response = self.client.get(url)
|
|
self.assertEqual(response.status_code, 200)
|
|
response = self.client.post(url, {'post':'yes'})
|
|
self.assertRedirects(response, redirect_url)
|
|
after = TimeSlot.objects.filter(meeting=meeting,type='regular').count()
|
|
self.assertEqual(after,before - expected_deletion_count)
|
|
|
|
def test_meetings_times_edit(self):
|
|
meeting = make_meeting_test_data()
|
|
timeslot = TimeSlot.objects.filter(meeting=meeting,type='regular').first()
|
|
url = reverse('ietf.secr.meetings.views.times_edit',kwargs={
|
|
'meeting_id':72,
|
|
'schedule_name':'test-schedule',
|
|
'time':timeslot.time.astimezone(meeting.tz()).strftime("%Y:%m:%d:%H:%M")
|
|
})
|
|
self.client.login(username="secretary", password="secretary+password")
|
|
response = self.client.post(url, {
|
|
'day':'1',
|
|
'time':'08:00',
|
|
'duration':'09:00',
|
|
'name':'Testing'
|
|
})
|
|
self.assertEqual(response.status_code, 302)
|
|
self.assertTrue(TimeSlot.objects.filter(meeting=meeting,name='Testing'))
|
|
|
|
def test_meetings_misc_sessions(self):
|
|
make_meeting_test_data()
|
|
url = reverse('ietf.secr.meetings.views.misc_sessions',kwargs={'meeting_id':72,'schedule_name':'test-schedule'})
|
|
self.client.login(username="secretary", password="secretary+password")
|
|
response = self.client.get(url)
|
|
self.assertEqual(response.status_code, 200)
|
|
|
|
def test_meetings_misc_session_add_valid(self):
|
|
meeting = make_meeting_test_data()
|
|
room = meeting.room_set.first()
|
|
group = Group.objects.get(acronym='secretariat')
|
|
url = reverse('ietf.secr.meetings.views.misc_sessions',kwargs={'meeting_id':72,'schedule_name':'test-schedule'})
|
|
self.client.login(username="secretary", password="secretary+password")
|
|
response = self.client.post(url, {
|
|
'day':'1',
|
|
'time':'08:00',
|
|
'duration':'02:00',
|
|
'name':'Testing',
|
|
'short':'test',
|
|
'purpose_0': 'admin', # purpose
|
|
'purpose_1':'reg', # type
|
|
'group':group.pk,
|
|
'location': room.pk,
|
|
'remote_instructions': 'http://webex.com/foobar',
|
|
})
|
|
self.assertRedirects(response, url)
|
|
session = Session.objects.filter(meeting=meeting, name='Testing').first()
|
|
self.assertTrue(session)
|
|
|
|
self.assertEqual(session.timeslotassignments.first().timeslot.location, room)
|
|
|
|
def test_meetings_misc_session_add_invalid(self):
|
|
make_meeting_test_data()
|
|
group = Group.objects.get(acronym='secretariat')
|
|
url = reverse('ietf.secr.meetings.views.misc_sessions',kwargs={'meeting_id':72,'schedule_name':'test-schedule'})
|
|
self.client.login(username="secretary", password="secretary+password")
|
|
response = self.client.post(url, {
|
|
'day':'1',
|
|
'time':'08:00',
|
|
'duration':'10',
|
|
'name':'Testing',
|
|
'short':'test',
|
|
'type':'reg',
|
|
'group':group.pk,
|
|
})
|
|
self.assertEqual(response.status_code, 200)
|
|
self.assertContains(response, 'invalid format')
|
|
|
|
def test_meetings_misc_session_edit(self):
|
|
meeting = make_meeting_test_data()
|
|
session = meeting.session_set.exclude(name='').first() # get first misc session
|
|
timeslot = session.official_timeslotassignment().timeslot
|
|
url = reverse('ietf.secr.meetings.views.misc_session_edit',kwargs={'meeting_id':72,'schedule_name':meeting.schedule.name,'slot_id':timeslot.pk})
|
|
redirect_url = reverse('ietf.secr.meetings.views.misc_sessions',kwargs={'meeting_id':72,'schedule_name':'test-schedule'})
|
|
new_time = (timeslot.time + datetime.timedelta(days=1)).astimezone(meeting.tz())
|
|
self.client.login(username="secretary", password="secretary+password")
|
|
response = self.client.get(url)
|
|
self.assertEqual(response.status_code, 200)
|
|
response = self.client.post(url, {
|
|
'name':'IETF Hackathon',
|
|
'short':'hackathon',
|
|
'location':timeslot.location.id,
|
|
'group':session.group.id,
|
|
'time':new_time.strftime('%H:%M'),
|
|
'duration':'01:00',
|
|
'day':'2',
|
|
'purpose_0': 'coding', # purpose
|
|
'purpose_1': 'other', # type
|
|
'remote_instructions': 'http://webex.com/foobar',
|
|
})
|
|
self.assertRedirects(response, redirect_url)
|
|
session = Session.objects.get(pk=session.pk) # get a clean instance to avoid cache problems
|
|
timeslot = session.official_timeslotassignment().timeslot
|
|
self.assertEqual(timeslot.time, new_time)
|
|
|
|
def test_meetings_misc_session_delete(self):
|
|
meeting = make_meeting_test_data()
|
|
schedule = meeting.schedule.base
|
|
slot = schedule.assignments.filter(timeslot__type='reg').first().timeslot
|
|
url = reverse('ietf.secr.meetings.views.misc_session_delete', kwargs={'meeting_id':meeting.number,'schedule_name':schedule.name,'slot_id':slot.id})
|
|
target = reverse('ietf.secr.meetings.views.misc_sessions', kwargs={'meeting_id':meeting.number,'schedule_name':schedule.name})
|
|
self.client.login(username="secretary", password="secretary+password")
|
|
response = self.client.get(url)
|
|
self.assertEqual(response.status_code, 200)
|
|
response = self.client.post(url, {'post':'yes'})
|
|
self.assertRedirects(response, target)
|
|
self.assertFalse(schedule.assignments.filter(timeslot=slot))
|
|
|
|
def test_meetings_misc_session_cancel(self):
|
|
meeting = make_meeting_test_data()
|
|
schedule = meeting.schedule.base
|
|
slot = schedule.assignments.filter(timeslot__type='reg').first().timeslot
|
|
url = reverse('ietf.secr.meetings.views.misc_session_cancel', kwargs={'meeting_id':meeting.number,'schedule_name':schedule.name,'slot_id':slot.id})
|
|
redirect_url = reverse('ietf.secr.meetings.views.misc_sessions', kwargs={'meeting_id':meeting.number,'schedule_name':schedule.name})
|
|
self.client.login(username="secretary", password="secretary+password")
|
|
response = self.client.get(url)
|
|
self.assertEqual(response.status_code, 200)
|
|
response = self.client.post(url, {'post':'yes'})
|
|
self.assertRedirects(response, redirect_url)
|
|
session = slot.sessionassignments.filter(schedule=schedule).first().session
|
|
self.assertEqual(SchedulingEvent.objects.filter(session=session).order_by('-id')[0].status_id, 'canceled')
|
|
|
|
def test_meetings_regular_session_edit(self):
|
|
meeting = make_meeting_test_data()
|
|
session = Session.objects.filter(meeting=meeting,group__acronym='mars').first()
|
|
url = reverse('ietf.secr.meetings.views.regular_session_edit', kwargs={'meeting_id':meeting.number,'schedule_name':meeting.schedule.name,'session_id':session.id})
|
|
redirect_url = reverse('ietf.secr.meetings.views.regular_sessions', kwargs={'meeting_id':meeting.number,'schedule_name':meeting.schedule.name})
|
|
self.client.login(username="secretary", password="secretary+password")
|
|
response = self.client.get(url)
|
|
self.assertEqual(response.status_code, 200)
|
|
response = self.client.post(url, {'agenda_note':'TEST'})
|
|
self.assertRedirects(response, redirect_url)
|
|
session = Session.objects.get(id=session.id)
|
|
self.assertEqual(session.agenda_note, 'TEST')
|
|
|
|
# ----------------------
|
|
# Unit Tests
|
|
# -----------------------
|
|
def test_get_times(self):
|
|
meeting = make_meeting_test_data()
|
|
timeslot = meeting.timeslot_set.filter(type='regular').first()
|
|
day = (timeslot.time.weekday() + 1) % 7 + 1 # fix up to match django __week_day filter
|
|
times = get_times(meeting,day)
|
|
values = [ x[0] for x in times ]
|
|
self.assertTrue(times)
|
|
self.assertTrue(timeslot.time.strftime('%H%M') in values)
|