* fix: turn off automatic escaping in session request templates * test: ensure no unwanted escaping in session request emails * test: use PersonFactory to create test users * test: minor refactoring
1162 lines
56 KiB
Python
1162 lines
56 KiB
Python
# Copyright The IETF Trust 2013-2022, All Rights Reserved
|
|
# -*- coding: utf-8 -*-
|
|
|
|
|
|
import datetime
|
|
|
|
from django.urls import reverse
|
|
|
|
import debug # pyflakes:ignore
|
|
|
|
from ietf.utils.test_utils import TestCase
|
|
from ietf.group.factories import GroupFactory, RoleFactory
|
|
from ietf.meeting.models import Session, ResourceAssociation, SchedulingEvent, Constraint
|
|
from ietf.meeting.factories import MeetingFactory, SessionFactory
|
|
from ietf.name.models import ConstraintName, TimerangeName
|
|
from ietf.person.factories import PersonFactory
|
|
from ietf.person.models import Person
|
|
from ietf.secr.sreq.forms import SessionForm
|
|
from ietf.utils.mail import outbox, empty_outbox, get_payload_text, send_mail
|
|
from ietf.utils.timezone import date_today
|
|
|
|
|
|
from pyquery import PyQuery
|
|
|
|
SECR_USER='secretary'
|
|
|
|
class SreqUrlTests(TestCase):
|
|
def test_urls(self):
|
|
MeetingFactory(type_id='ietf',date=date_today())
|
|
|
|
self.client.login(username="secretary", password="secretary+password")
|
|
|
|
r = self.client.get("/secr/")
|
|
self.assertEqual(r.status_code, 200)
|
|
|
|
r = self.client.get("/secr/sreq/")
|
|
self.assertEqual(r.status_code, 200)
|
|
|
|
testgroup=GroupFactory()
|
|
r = self.client.get("/secr/sreq/%s/new/" % testgroup.acronym)
|
|
self.assertEqual(r.status_code, 200)
|
|
|
|
class SessionRequestTestCase(TestCase):
|
|
def test_main(self):
|
|
meeting = MeetingFactory(type_id='ietf', date=date_today())
|
|
SessionFactory.create_batch(2, meeting=meeting, status_id='sched')
|
|
SessionFactory.create_batch(2, meeting=meeting, status_id='disappr')
|
|
# Several unscheduled groups come from make_immutable_base_data
|
|
url = reverse('ietf.secr.sreq.views.main')
|
|
self.client.login(username="secretary", password="secretary+password")
|
|
r = self.client.get(url)
|
|
self.assertEqual(r.status_code, 200)
|
|
sched = r.context['scheduled_groups']
|
|
self.assertEqual(len(sched), 2)
|
|
unsched = r.context['unscheduled_groups']
|
|
self.assertEqual(len(unsched), 12)
|
|
|
|
def test_approve(self):
|
|
meeting = MeetingFactory(type_id='ietf', date=date_today())
|
|
ad = Person.objects.get(user__username='ad')
|
|
area = RoleFactory(name_id='ad', person=ad, group__type_id='area').group
|
|
mars = GroupFactory(parent=area, acronym='mars')
|
|
# create session waiting for approval
|
|
session = SessionFactory(meeting=meeting, group=mars, status_id='apprw')
|
|
url = reverse('ietf.secr.sreq.views.approve', kwargs={'acronym':'mars'})
|
|
self.client.login(username="ad", password="ad+password")
|
|
r = self.client.get(url)
|
|
self.assertRedirects(r,reverse('ietf.secr.sreq.views.view', kwargs={'acronym':'mars'}))
|
|
self.assertEqual(SchedulingEvent.objects.filter(session=session).order_by('-id')[0].status_id, 'appr')
|
|
|
|
def test_cancel(self):
|
|
meeting = MeetingFactory(type_id='ietf', date=date_today())
|
|
ad = Person.objects.get(user__username='ad')
|
|
area = RoleFactory(name_id='ad', person=ad, group__type_id='area').group
|
|
session = SessionFactory(meeting=meeting, group__parent=area, group__acronym='mars', status_id='sched')
|
|
url = reverse('ietf.secr.sreq.views.cancel', kwargs={'acronym':'mars'})
|
|
self.client.login(username="ad", password="ad+password")
|
|
r = self.client.get(url)
|
|
self.assertRedirects(r,reverse('ietf.secr.sreq.views.main'))
|
|
self.assertEqual(SchedulingEvent.objects.filter(session=session).order_by('-id')[0].status_id, 'deleted')
|
|
|
|
def test_cancel_notification_msg(self):
|
|
to = "<iesg-secretary@ietf.org>"
|
|
subject = "Dummy subject"
|
|
template = "sreq/session_cancel_notification.txt"
|
|
meeting = MeetingFactory(type_id="ietf", date=date_today())
|
|
requester = PersonFactory(name="James O'Rourke", user__username="jimorourke")
|
|
context = {"meeting": meeting, "requester": requester}
|
|
cc = "cc.a@example.com, cc.b@example.com"
|
|
bcc = "bcc@example.com"
|
|
|
|
msg = send_mail(
|
|
None,
|
|
to,
|
|
None,
|
|
subject,
|
|
template,
|
|
context,
|
|
cc=cc,
|
|
bcc=bcc,
|
|
)
|
|
self.assertEqual(requester.name, "James O'Rourke") # note ' (single quote) in the name
|
|
self.assertIn(
|
|
f"A request to cancel a meeting session has just been submitted by {requester.name}.",
|
|
get_payload_text(msg),
|
|
)
|
|
|
|
def test_edit(self):
|
|
meeting = MeetingFactory(type_id='ietf', date=date_today())
|
|
mars = RoleFactory(name_id='chair', person__user__username='marschairman', group__acronym='mars').group
|
|
group2 = GroupFactory()
|
|
group3 = GroupFactory()
|
|
group4 = GroupFactory()
|
|
iabprog = GroupFactory(type_id='program')
|
|
|
|
SessionFactory(meeting=meeting,group=mars,status_id='sched')
|
|
|
|
url = reverse('ietf.secr.sreq.views.edit', kwargs={'acronym':'mars'})
|
|
self.client.login(username="marschairman", password="marschairman+password")
|
|
r = self.client.get(url)
|
|
self.assertEqual(r.status_code, 200)
|
|
attendees = 10
|
|
comments = 'need lights'
|
|
mars_sessions = meeting.session_set.filter(group__acronym='mars')
|
|
empty_outbox()
|
|
post_data = {'num_session':'2',
|
|
'attendees': attendees,
|
|
'constraint_chair_conflict':iabprog.acronym,
|
|
'session_time_relation': 'subsequent-days',
|
|
'adjacent_with_wg': group2.acronym,
|
|
'joint_with_groups': group3.acronym + ' ' + group4.acronym,
|
|
'joint_for_session': '2',
|
|
'timeranges': ['thursday-afternoon-early', 'thursday-afternoon-late'],
|
|
'session_set-TOTAL_FORMS': '3', # matches what view actually sends, even with only 2 filled in
|
|
'session_set-INITIAL_FORMS': '1',
|
|
'session_set-MIN_NUM_FORMS': '1',
|
|
'session_set-MAX_NUM_FORMS': '3',
|
|
'session_set-0-id':mars_sessions[0].pk,
|
|
'session_set-0-name': mars_sessions[0].name,
|
|
'session_set-0-short': mars_sessions[0].short,
|
|
'session_set-0-purpose': mars_sessions[0].purpose_id,
|
|
'session_set-0-type': mars_sessions[0].type_id,
|
|
'session_set-0-requested_duration': '3600',
|
|
'session_set-0-on_agenda': mars_sessions[0].on_agenda,
|
|
'session_set-0-remote_instructions': mars_sessions[0].remote_instructions,
|
|
'session_set-0-attendees': attendees,
|
|
'session_set-0-comments': comments,
|
|
'session_set-0-DELETE': '',
|
|
# no session_set-1-id because it's a new request
|
|
'session_set-1-name': '',
|
|
'session_set-1-short': '',
|
|
'session_set-1-purpose': 'regular',
|
|
'session_set-1-type': 'regular',
|
|
'session_set-1-requested_duration': '3600',
|
|
'session_set-1-on_agenda': True,
|
|
'session_set-1-remote_instructions': mars_sessions[0].remote_instructions,
|
|
'session_set-1-attendees': attendees,
|
|
'session_set-1-comments': comments,
|
|
'session_set-1-DELETE': '',
|
|
'session_set-2-id': '',
|
|
'session_set-2-name': '',
|
|
'session_set-2-short': '',
|
|
'session_set-2-purpose': 'regular',
|
|
'session_set-2-type': 'regular',
|
|
'session_set-2-requested_duration': '',
|
|
'session_set-2-on_agenda': 'True',
|
|
'session_set-2-attendees': attendees,
|
|
'session_set-2-comments': '',
|
|
'session_set-2-DELETE': 'on',
|
|
'submit': 'Continue'}
|
|
r = self.client.post(url, post_data, HTTP_HOST='example.com')
|
|
redirect_url = reverse('ietf.secr.sreq.views.view', kwargs={'acronym': 'mars'})
|
|
self.assertRedirects(r, redirect_url)
|
|
|
|
# Check whether updates were stored in the database
|
|
sessions = Session.objects.filter(meeting=meeting, group=mars).order_by("id") # order to match edit() view
|
|
self.assertEqual(len(sessions), 2)
|
|
session = sessions[0]
|
|
|
|
self.assertEqual(session.constraints().get(name='chair_conflict').target.acronym, iabprog.acronym)
|
|
self.assertEqual(session.constraints().get(name='time_relation').time_relation, 'subsequent-days')
|
|
self.assertEqual(session.constraints().get(name='wg_adjacent').target.acronym, group2.acronym)
|
|
self.assertEqual(
|
|
list(session.constraints().get(name='timerange').timeranges.all().values('name')),
|
|
list(TimerangeName.objects.filter(name__in=['thursday-afternoon-early', 'thursday-afternoon-late']).values('name'))
|
|
)
|
|
self.assertFalse(sessions[0].joint_with_groups.count())
|
|
self.assertEqual(set(sessions[1].joint_with_groups.all()), {group3, group4})
|
|
|
|
# Check whether the updated data is visible on the view page
|
|
r = self.client.get(redirect_url)
|
|
self.assertContains(r, 'Schedule the sessions on subsequent days')
|
|
self.assertContains(r, 'Thursday early afternoon, Thursday late afternoon')
|
|
self.assertContains(r, group2.acronym)
|
|
# The sessions can be in any order in the HTML, deal with that
|
|
self.assertRegex(r.content.decode(), r'Second session with: ({} {}|{} {})'.format(group3.acronym, group4.acronym, group4.acronym, group3.acronym))
|
|
|
|
# check that a notification was sent
|
|
self.assertEqual(len(outbox), 1)
|
|
notification_payload = get_payload_text(outbox[0])
|
|
self.assertIn('1 Hour, 1 Hour', notification_payload)
|
|
self.assertNotIn('1 Hour, 1 Hour, 1 Hour', notification_payload)
|
|
|
|
# Edit again, changing the joint sessions and clearing some fields. The behaviour of
|
|
# edit is different depending on whether previous joint sessions were recorded.
|
|
empty_outbox()
|
|
post_data = {'num_session':'2',
|
|
'attendees':attendees,
|
|
'constraint_chair_conflict':'',
|
|
'comments':'need lights',
|
|
'joint_with_groups': group2.acronym,
|
|
'joint_for_session': '1',
|
|
'session_set-TOTAL_FORMS': '3', # matches what view actually sends, even with only 2 filled in
|
|
'session_set-INITIAL_FORMS': '2',
|
|
'session_set-MIN_NUM_FORMS': '1',
|
|
'session_set-MAX_NUM_FORMS': '3',
|
|
'session_set-0-id':sessions[0].pk,
|
|
'session_set-0-name': sessions[0].name,
|
|
'session_set-0-short': sessions[0].short,
|
|
'session_set-0-purpose': sessions[0].purpose_id,
|
|
'session_set-0-type': sessions[0].type_id,
|
|
'session_set-0-requested_duration': '3600',
|
|
'session_set-0-on_agenda': sessions[0].on_agenda,
|
|
'session_set-0-remote_instructions': sessions[0].remote_instructions,
|
|
'session_set-0-attendees': sessions[0].attendees,
|
|
'session_set-0-comments': sessions[1].comments,
|
|
'session_set-0-DELETE': '',
|
|
'session_set-1-id': sessions[1].pk,
|
|
'session_set-1-name': sessions[1].name,
|
|
'session_set-1-short': sessions[1].short,
|
|
'session_set-1-purpose': sessions[1].purpose_id,
|
|
'session_set-1-type': sessions[1].type_id,
|
|
'session_set-1-requested_duration': '3600',
|
|
'session_set-1-on_agenda': sessions[1].on_agenda,
|
|
'session_set-1-remote_instructions': sessions[1].remote_instructions,
|
|
'session_set-1-attendees': sessions[1].attendees,
|
|
'session_set-1-comments': sessions[1].comments,
|
|
'session_set-1-DELETE': '',
|
|
'session_set-2-id': '',
|
|
'session_set-2-name': '',
|
|
'session_set-2-short': '',
|
|
'session_set-2-purpose': 'regular',
|
|
'session_set-2-type': 'regular',
|
|
'session_set-2-requested_duration': '',
|
|
'session_set-2-on_agenda': 'True',
|
|
'session_set-2-attendees': attendees,
|
|
'session_set-2-comments': '',
|
|
'session_set-2-DELETE': 'on',
|
|
'submit': 'Continue'}
|
|
r = self.client.post(url, post_data, HTTP_HOST='example.com')
|
|
self.assertRedirects(r, redirect_url)
|
|
|
|
# Check whether updates were stored in the database
|
|
sessions = Session.objects.filter(meeting=meeting, group=mars)
|
|
self.assertEqual(len(sessions), 2)
|
|
session = sessions[0]
|
|
self.assertFalse(session.constraints().filter(name='time_relation'))
|
|
self.assertFalse(session.constraints().filter(name='wg_adjacent'))
|
|
self.assertFalse(session.constraints().filter(name='timerange'))
|
|
self.assertEqual(list(sessions[0].joint_with_groups.all()), [group2])
|
|
self.assertFalse(sessions[1].joint_with_groups.count())
|
|
|
|
# check that a notification was sent
|
|
self.assertEqual(len(outbox), 1)
|
|
notification_payload = get_payload_text(outbox[0])
|
|
self.assertIn('1 Hour, 1 Hour', notification_payload)
|
|
self.assertNotIn('1 Hour, 1 Hour, 1 Hour', notification_payload)
|
|
|
|
# Check whether the updated data is visible on the view page
|
|
r = self.client.get(redirect_url)
|
|
self.assertContains(r, 'First session with: {}'.format(group2.acronym))
|
|
|
|
|
|
def test_edit_constraint_bethere(self):
|
|
meeting = MeetingFactory(type_id='ietf', date=date_today())
|
|
mars = RoleFactory(name_id='chair', person__user__username='marschairman', group__acronym='mars').group
|
|
session = SessionFactory(meeting=meeting, group=mars, status_id='sched')
|
|
Constraint.objects.create(
|
|
meeting=meeting,
|
|
source=mars,
|
|
person=Person.objects.get(user__username='marschairman'),
|
|
name_id='bethere',
|
|
)
|
|
self.assertEqual(session.people_constraints.count(), 1)
|
|
url = reverse('ietf.secr.sreq.views.edit', kwargs=dict(acronym='mars'))
|
|
self.client.login(username='marschairman', password='marschairman+password')
|
|
attendees = '10'
|
|
ad = Person.objects.get(user__username='ad')
|
|
post_data = {
|
|
'num_session': '1',
|
|
'attendees': attendees,
|
|
'bethere': str(ad.pk),
|
|
'constraint_chair_conflict':'',
|
|
'comments':'',
|
|
'joint_with_groups': '',
|
|
'joint_for_session': '',
|
|
'delete_conflict': 'on',
|
|
'session_set-TOTAL_FORMS': '3', # matches what view actually sends, even with only 2 filled in
|
|
'session_set-INITIAL_FORMS': '1',
|
|
'session_set-MIN_NUM_FORMS': '1',
|
|
'session_set-MAX_NUM_FORMS': '3',
|
|
'session_set-0-id':session.pk,
|
|
'session_set-0-name': session.name,
|
|
'session_set-0-short': session.short,
|
|
'session_set-0-purpose': session.purpose_id,
|
|
'session_set-0-type': session.type_id,
|
|
'session_set-0-requested_duration': '3600',
|
|
'session_set-0-on_agenda': session.on_agenda,
|
|
'session_set-0-remote_instructions': session.remote_instructions,
|
|
'session_set-0-attendees': attendees,
|
|
'session_set-0-comments': '',
|
|
'session_set-0-DELETE': '',
|
|
'session_set-1-id': '',
|
|
'session_set-1-name': '',
|
|
'session_set-1-short': '',
|
|
'session_set-1-purpose':'regular',
|
|
'session_set-1-type':'regular',
|
|
'session_set-1-requested_duration': '',
|
|
'session_set-1-on_agenda': 'True',
|
|
'session_set-1-attendees': attendees,
|
|
'session_set-1-comments': '',
|
|
'session_set-1-DELETE': 'on',
|
|
'session_set-2-id': '',
|
|
'session_set-2-name': '',
|
|
'session_set-2-short': '',
|
|
'session_set-2-purpose': 'regular',
|
|
'session_set-2-type': 'regular',
|
|
'session_set-2-requested_duration': '',
|
|
'session_set-2-on_agenda': 'True',
|
|
'session_set-2-attendees': attendees,
|
|
'session_set-2-comments': '',
|
|
'session_set-2-DELETE': 'on',
|
|
'submit': 'Save',
|
|
}
|
|
r = self.client.post(url, post_data, HTTP_HOST='example.com')
|
|
redirect_url = reverse('ietf.secr.sreq.views.view', kwargs={'acronym': 'mars'})
|
|
self.assertRedirects(r, redirect_url)
|
|
self.assertEqual([pc.person for pc in session.people_constraints.all()], [ad])
|
|
|
|
def test_edit_inactive_conflicts(self):
|
|
"""Inactive conflicts should be displayed and removable"""
|
|
meeting = MeetingFactory(type_id='ietf', date=date_today(), group_conflicts=['chair_conflict'])
|
|
mars = RoleFactory(name_id='chair', person__user__username='marschairman', group__acronym='mars').group
|
|
session = SessionFactory(meeting=meeting, group=mars, status_id='sched')
|
|
other_group = GroupFactory()
|
|
Constraint.objects.create(
|
|
meeting=meeting,
|
|
name_id='conflict', # not in group_conflicts for the meeting
|
|
source=mars,
|
|
target=other_group,
|
|
)
|
|
|
|
url = reverse('ietf.secr.sreq.views.edit', kwargs=dict(acronym='mars'))
|
|
self.client.login(username='marschairman', password='marschairman+password')
|
|
r = self.client.get(url)
|
|
self.assertEqual(r.status_code, 200)
|
|
q = PyQuery(r.content)
|
|
|
|
# check that the inactive session is displayed
|
|
found = q('input#id_delete_conflict[type="checkbox"]')
|
|
self.assertEqual(len(found), 1)
|
|
delete_checkbox = found[0]
|
|
# check that the label on the checkbox is correct
|
|
self.assertIn('Delete this conflict', delete_checkbox.tail)
|
|
# check that the target is displayed correctly in the UI
|
|
self.assertIn(other_group.acronym, delete_checkbox.find('../input[@type="text"]').value)
|
|
|
|
attendees = '10'
|
|
post_data = {
|
|
'num_session': '1',
|
|
'attendees': attendees,
|
|
'constraint_chair_conflict':'',
|
|
'comments':'',
|
|
'joint_with_groups': '',
|
|
'joint_for_session': '',
|
|
'delete_conflict': 'on',
|
|
'session_set-TOTAL_FORMS': '1',
|
|
'session_set-INITIAL_FORMS': '1',
|
|
'session_set-MIN_NUM_FORMS': '1',
|
|
'session_set-MAX_NUM_FORMS': '3',
|
|
'session_set-0-id':session.pk,
|
|
'session_set-0-name': session.name,
|
|
'session_set-0-short': session.short,
|
|
'session_set-0-purpose': session.purpose_id,
|
|
'session_set-0-type': session.type_id,
|
|
'session_set-0-requested_duration': '3600',
|
|
'session_set-0-on_agenda': session.on_agenda,
|
|
'session_set-0-remote_instructions': session.remote_instructions,
|
|
'session_set-0-attendees': attendees,
|
|
'session_set-0-comments': '',
|
|
'session_set-0-DELETE': '',
|
|
'submit': 'Save',
|
|
}
|
|
r = self.client.post(url, post_data, HTTP_HOST='example.com')
|
|
redirect_url = reverse('ietf.secr.sreq.views.view', kwargs={'acronym': 'mars'})
|
|
self.assertRedirects(r, redirect_url)
|
|
self.assertEqual(len(mars.constraint_source_set.filter(name_id='conflict')), 0)
|
|
|
|
def test_tool_status(self):
|
|
MeetingFactory(type_id='ietf', date=date_today())
|
|
url = reverse('ietf.secr.sreq.views.tool_status')
|
|
self.client.login(username="secretary", password="secretary+password")
|
|
r = self.client.get(url)
|
|
self.assertEqual(r.status_code, 200)
|
|
r = self.client.post(url, {'message':'locked', 'submit':'Lock'})
|
|
self.assertRedirects(r,reverse('ietf.secr.sreq.views.main'))
|
|
|
|
def test_new_req_constraint_types(self):
|
|
"""Configurable constraint types should be handled correctly in a new request
|
|
|
|
Relies on SessionForm representing constraint values with element IDs
|
|
like id_constraint_<ConstraintName slug>
|
|
"""
|
|
meeting = MeetingFactory(type_id='ietf', date=date_today())
|
|
RoleFactory(name_id='chair', person__user__username='marschairman', group__acronym='mars')
|
|
url = reverse('ietf.secr.sreq.views.new', kwargs=dict(acronym='mars'))
|
|
self.client.login(username="marschairman", password="marschairman+password")
|
|
|
|
for expected in [
|
|
['conflict', 'conflic2', 'conflic3'],
|
|
['chair_conflict', 'tech_overlap', 'key_participant'],
|
|
]:
|
|
meeting.group_conflict_types.clear()
|
|
for slug in expected:
|
|
meeting.group_conflict_types.add(ConstraintName.objects.get(slug=slug))
|
|
|
|
r = self.client.get(url)
|
|
self.assertEqual(r.status_code, 200)
|
|
q = PyQuery(r.content)
|
|
self.assertCountEqual(
|
|
[elt.attr('id') for elt in q.items('*[id^=id_constraint_]')],
|
|
['id_constraint_{}'.format(conf_name) for conf_name in expected],
|
|
)
|
|
|
|
def test_edit_req_constraint_types(self):
|
|
"""Editing a request constraint should show the expected constraints"""
|
|
meeting = MeetingFactory(type_id='ietf', date=date_today())
|
|
SessionFactory(group__acronym='mars',
|
|
status_id='schedw',
|
|
meeting=meeting,
|
|
add_to_schedule=False)
|
|
RoleFactory(name_id='chair', person__user__username='marschairman', group__acronym='mars')
|
|
|
|
url = reverse('ietf.secr.sreq.views.edit', kwargs=dict(acronym='mars'))
|
|
self.client.login(username='marschairman', password='marschairman+password')
|
|
|
|
for expected in [
|
|
['conflict', 'conflic2', 'conflic3'],
|
|
['chair_conflict', 'tech_overlap', 'key_participant'],
|
|
]:
|
|
meeting.group_conflict_types.clear()
|
|
for slug in expected:
|
|
meeting.group_conflict_types.add(ConstraintName.objects.get(slug=slug))
|
|
|
|
r = self.client.get(url)
|
|
self.assertEqual(r.status_code, 200)
|
|
q = PyQuery(r.content)
|
|
self.assertCountEqual(
|
|
[elt.attr('id') for elt in q.items('*[id^=id_constraint_]')],
|
|
['id_constraint_{}'.format(conf_name) for conf_name in expected],
|
|
)
|
|
|
|
class SubmitRequestCase(TestCase):
|
|
def setUp(self):
|
|
super(SubmitRequestCase, self).setUp()
|
|
# Ensure meeting numbers are predictable. Temporarily needed while basing
|
|
# constraint types on meeting number, expected to go away when #2770 is resolved.
|
|
MeetingFactory.reset_sequence(0)
|
|
|
|
def test_submit_request(self):
|
|
meeting = MeetingFactory(type_id='ietf', date=date_today())
|
|
ad = Person.objects.get(user__username='ad')
|
|
area = RoleFactory(name_id='ad', person=ad, group__type_id='area').group
|
|
group = GroupFactory(parent=area)
|
|
group2 = GroupFactory(parent=area)
|
|
group3 = GroupFactory(parent=area)
|
|
group4 = GroupFactory(parent=area)
|
|
session_count_before = Session.objects.filter(meeting=meeting, group=group).count()
|
|
url = reverse('ietf.secr.sreq.views.new',kwargs={'acronym':group.acronym})
|
|
confirm_url = reverse('ietf.secr.sreq.views.confirm',kwargs={'acronym':group.acronym})
|
|
main_url = reverse('ietf.secr.sreq.views.main')
|
|
attendees = '10'
|
|
comments = 'need projector'
|
|
post_data = {'num_session':'1',
|
|
'attendees':attendees,
|
|
'constraint_chair_conflict':'',
|
|
'comments':comments,
|
|
'adjacent_with_wg': group2.acronym,
|
|
'timeranges': ['thursday-afternoon-early', 'thursday-afternoon-late'],
|
|
'joint_with_groups': group3.acronym + ' ' + group4.acronym,
|
|
'joint_for_session': '1',
|
|
'session_set-TOTAL_FORMS': '1',
|
|
'session_set-INITIAL_FORMS': '0',
|
|
'session_set-MIN_NUM_FORMS': '1',
|
|
'session_set-MAX_NUM_FORMS': '3',
|
|
# no 'session_set-0-id' to create a new session
|
|
'session_set-0-name': '',
|
|
'session_set-0-short': '',
|
|
'session_set-0-purpose': 'regular',
|
|
'session_set-0-type': 'regular',
|
|
'session_set-0-requested_duration': '3600',
|
|
'session_set-0-on_agenda': True,
|
|
'session_set-0-remote_instructions': '',
|
|
'session_set-0-attendees': attendees,
|
|
'session_set-0-comments': comments,
|
|
'session_set-0-DELETE': '',
|
|
'submit': 'Continue'}
|
|
self.client.login(username="secretary", password="secretary+password")
|
|
r = self.client.post(url,post_data)
|
|
self.assertEqual(r.status_code, 200)
|
|
|
|
# Verify the contents of the confirm view
|
|
self.assertContains(r, 'Thursday early afternoon, Thursday late afternoon')
|
|
self.assertContains(r, group2.acronym)
|
|
self.assertContains(r, 'First session with: {} {}'.format(group3.acronym, group4.acronym))
|
|
|
|
post_data['submit'] = 'Submit'
|
|
r = self.client.post(confirm_url,post_data)
|
|
self.assertRedirects(r, main_url)
|
|
session_count_after = Session.objects.filter(meeting=meeting, group=group, type='regular').count()
|
|
self.assertEqual(session_count_after, session_count_before + 1)
|
|
|
|
# test that second confirm does not add sessions
|
|
r = self.client.post(confirm_url,post_data)
|
|
self.assertRedirects(r, main_url)
|
|
session_count_after = Session.objects.filter(meeting=meeting, group=group, type='regular').count()
|
|
self.assertEqual(session_count_after, session_count_before + 1)
|
|
|
|
# Verify database content
|
|
session = Session.objects.get(meeting=meeting, group=group)
|
|
self.assertEqual(session.constraints().get(name='wg_adjacent').target.acronym, group2.acronym)
|
|
self.assertEqual(
|
|
list(session.constraints().get(name='timerange').timeranges.all().values('name')),
|
|
list(TimerangeName.objects.filter(name__in=['thursday-afternoon-early', 'thursday-afternoon-late']).values('name'))
|
|
)
|
|
self.assertEqual(set(list(session.joint_with_groups.all())), set([group3, group4]))
|
|
|
|
def test_submit_request_invalid(self):
|
|
MeetingFactory(type_id='ietf', date=date_today())
|
|
ad = Person.objects.get(user__username='ad')
|
|
area = RoleFactory(name_id='ad', person=ad, group__type_id='area').group
|
|
group = GroupFactory(parent=area)
|
|
url = reverse('ietf.secr.sreq.views.new',kwargs={'acronym':group.acronym})
|
|
attendees = '10'
|
|
comments = 'need projector'
|
|
post_data = {
|
|
'num_session':'2',
|
|
'attendees':attendees,
|
|
'constraint_chair_conflict':'',
|
|
'comments':comments,
|
|
'session_set-TOTAL_FORMS': '1',
|
|
'session_set-INITIAL_FORMS': '1',
|
|
'session_set-MIN_NUM_FORMS': '1',
|
|
'session_set-MAX_NUM_FORMS': '3',
|
|
# no 'session_set-0-id' to create a new session
|
|
'session_set-0-name': '',
|
|
'session_set-0-short': '',
|
|
'session_set-0-purpose': 'regular',
|
|
'session_set-0-type': 'regular',
|
|
'session_set-0-requested_duration': '3600',
|
|
'session_set-0-on_agenda': True,
|
|
'session_set-0-remote_instructions': '',
|
|
'session_set-0-attendees': attendees,
|
|
'session_set-0-comments': comments,
|
|
'session_set-0-DELETE': '',
|
|
}
|
|
self.client.login(username="secretary", password="secretary+password")
|
|
r = self.client.post(url,post_data)
|
|
self.assertEqual(r.status_code, 200)
|
|
q = PyQuery(r.content)
|
|
self.assertEqual(len(q('#session-request-form')),1)
|
|
self.assertContains(r, 'Must provide data for all sessions')
|
|
|
|
def test_submit_request_check_constraints(self):
|
|
m1 = MeetingFactory(type_id='ietf', date=date_today() - datetime.timedelta(days=100))
|
|
MeetingFactory(type_id='ietf', date=date_today(),
|
|
group_conflicts=['chair_conflict', 'conflic2', 'conflic3'])
|
|
ad = Person.objects.get(user__username='ad')
|
|
area = RoleFactory(name_id='ad', person=ad, group__type_id='area').group
|
|
group = GroupFactory(parent=area)
|
|
still_active_group = GroupFactory(parent=area)
|
|
Constraint.objects.create(
|
|
meeting=m1,
|
|
source=group,
|
|
target=still_active_group,
|
|
name_id='chair_conflict',
|
|
)
|
|
inactive_group = GroupFactory(parent=area, state_id='conclude')
|
|
inactive_group.save()
|
|
Constraint.objects.create(
|
|
meeting=m1,
|
|
source=group,
|
|
target=inactive_group,
|
|
name_id='chair_conflict',
|
|
)
|
|
session = SessionFactory(group=group, meeting=m1)
|
|
|
|
self.client.login(username="secretary", password="secretary+password")
|
|
|
|
url = reverse('ietf.secr.sreq.views.new',kwargs={'acronym':group.acronym})
|
|
r = self.client.get(url + '?previous')
|
|
self.assertEqual(r.status_code, 200)
|
|
q = PyQuery(r.content)
|
|
conflict1 = q('[name="constraint_chair_conflict"]').val()
|
|
self.assertIn(still_active_group.acronym, conflict1)
|
|
self.assertNotIn(inactive_group.acronym, conflict1)
|
|
|
|
attendees = '10'
|
|
comments = 'need projector'
|
|
post_data = {'num_session':'1',
|
|
'attendees':attendees,
|
|
'constraint_chair_conflict': group.acronym,
|
|
'comments':comments,
|
|
'session_set-TOTAL_FORMS': '1',
|
|
'session_set-INITIAL_FORMS': '1',
|
|
'session_set-MIN_NUM_FORMS': '1',
|
|
'session_set-MAX_NUM_FORMS': '3',
|
|
# no 'session_set-0-id' to create a new session
|
|
'session_set-0-name': '',
|
|
'session_set-0-short': '',
|
|
'session_set-0-purpose': session.purpose_id,
|
|
'session_set-0-type': session.type_id,
|
|
'session_set-0-requested_duration': '3600',
|
|
'session_set-0-on_agenda': session.on_agenda,
|
|
'session_set-0-remote_instructions': session.remote_instructions,
|
|
'session_set-0-attendees': attendees,
|
|
'session_set-0-comments': comments,
|
|
'session_set-0-DELETE': '',
|
|
'submit': 'Continue'}
|
|
r = self.client.post(url,post_data)
|
|
self.assertEqual(r.status_code, 200)
|
|
q = PyQuery(r.content)
|
|
self.assertEqual(len(q('#session-request-form')),1)
|
|
self.assertContains(r, "Cannot declare a conflict with the same group")
|
|
|
|
def test_request_notification(self):
|
|
meeting = MeetingFactory(type_id='ietf', date=date_today())
|
|
ad = Person.objects.get(user__username='ad')
|
|
area = GroupFactory(type_id='area')
|
|
RoleFactory(name_id='ad', person=ad, group=area)
|
|
group = GroupFactory(acronym='ames', parent=area)
|
|
group2 = GroupFactory(acronym='ames2', parent=area)
|
|
group3 = GroupFactory(acronym='ames2', parent=area)
|
|
group4 = GroupFactory(acronym='ames3', parent=area)
|
|
RoleFactory(name_id='chair', group=group, person__user__username='ameschairman')
|
|
resource = ResourceAssociation.objects.create(name_id='project')
|
|
# Bit of a test data hack - the fixture now has no used resources to pick from
|
|
resource.name.used=True
|
|
resource.name.save()
|
|
|
|
url = reverse('ietf.secr.sreq.views.new',kwargs={'acronym':group.acronym})
|
|
confirm_url = reverse('ietf.secr.sreq.views.confirm',kwargs={'acronym':group.acronym})
|
|
len_before = len(outbox)
|
|
attendees = '10'
|
|
post_data = {'num_session':'2',
|
|
'attendees':attendees,
|
|
'bethere':str(ad.pk),
|
|
'constraint_chair_conflict':group4.acronym,
|
|
'comments':'',
|
|
'resources': resource.pk,
|
|
'session_time_relation': 'subsequent-days',
|
|
'adjacent_with_wg': group2.acronym,
|
|
'joint_with_groups': group3.acronym,
|
|
'joint_for_session': '2',
|
|
'timeranges': ['thursday-afternoon-early', 'thursday-afternoon-late'],
|
|
'session_set-TOTAL_FORMS': '2',
|
|
'session_set-INITIAL_FORMS': '0',
|
|
'session_set-MIN_NUM_FORMS': '1',
|
|
'session_set-MAX_NUM_FORMS': '3',
|
|
# no 'session_set-0-id' for new session
|
|
'session_set-0-name': '',
|
|
'session_set-0-short': '',
|
|
'session_set-0-purpose': 'regular',
|
|
'session_set-0-type': 'regular',
|
|
'session_set-0-requested_duration': '3600',
|
|
'session_set-0-on_agenda': True,
|
|
'session_set-0-remote_instructions': '',
|
|
'session_set-0-attendees': attendees,
|
|
'session_set-0-comments': '',
|
|
'session_set-0-DELETE': '',
|
|
# no 'session_set-1-id' for new session
|
|
'session_set-1-name': '',
|
|
'session_set-1-short': '',
|
|
'session_set-1-purpose': 'regular',
|
|
'session_set-1-type': 'regular',
|
|
'session_set-1-requested_duration': '3600',
|
|
'session_set-1-on_agenda': True,
|
|
'session_set-1-remote_instructions': '',
|
|
'session_set-1-attendees': attendees,
|
|
'session_set-1-comments': '',
|
|
'session_set-1-DELETE': '',
|
|
'submit': 'Continue'}
|
|
self.client.login(username="ameschairman", password="ameschairman+password")
|
|
# submit
|
|
r = self.client.post(url,post_data)
|
|
self.assertEqual(r.status_code, 200)
|
|
q = PyQuery(r.content)
|
|
self.assertTrue('Confirm' in str(q("title")), r.context['form'].errors)
|
|
# confirm
|
|
post_data['submit'] = 'Submit'
|
|
r = self.client.post(confirm_url,post_data)
|
|
self.assertRedirects(r, reverse('ietf.secr.sreq.views.main'))
|
|
self.assertEqual(len(outbox),len_before+1)
|
|
notification = outbox[-1]
|
|
notification_payload = get_payload_text(notification)
|
|
sessions = Session.objects.filter(meeting=meeting,group=group)
|
|
self.assertEqual(len(sessions), 2)
|
|
session = sessions[0]
|
|
|
|
self.assertEqual(session.resources.count(),1)
|
|
self.assertEqual(session.people_constraints.count(),1)
|
|
self.assertEqual(session.constraints().get(name='time_relation').time_relation, 'subsequent-days')
|
|
self.assertEqual(session.constraints().get(name='wg_adjacent').target.acronym, group2.acronym)
|
|
self.assertEqual(
|
|
list(session.constraints().get(name='timerange').timeranges.all().values('name')),
|
|
list(TimerangeName.objects.filter(name__in=['thursday-afternoon-early', 'thursday-afternoon-late']).values('name'))
|
|
)
|
|
resource = session.resources.first()
|
|
self.assertTrue(resource.desc in notification_payload)
|
|
self.assertTrue('Schedule the sessions on subsequent days' in notification_payload)
|
|
self.assertTrue(group2.acronym in notification_payload)
|
|
self.assertTrue("Can't meet: Thursday early afternoon, Thursday late" in notification_payload)
|
|
self.assertTrue('Second session joint with: {}'.format(group3.acronym) in notification_payload)
|
|
self.assertTrue(ad.ascii_name() in notification_payload)
|
|
self.assertIn(ConstraintName.objects.get(slug='chair_conflict').name, notification_payload)
|
|
self.assertIn(group.acronym, notification_payload)
|
|
self.assertIn('1 Hour, 1 Hour', notification_payload)
|
|
self.assertNotIn('1 Hour, 1 Hour, 1 Hour', notification_payload)
|
|
self.assertNotIn('The third session requires your approval', notification_payload)
|
|
|
|
def test_request_notification_msg(self):
|
|
to = "<iesg-secretary@ietf.org>"
|
|
subject = "Dummy subject"
|
|
template = "sreq/session_request_notification.txt"
|
|
header = "A new"
|
|
meeting = MeetingFactory(type_id="ietf", date=date_today())
|
|
requester = PersonFactory(name="James O'Rourke", user__username="jimorourke")
|
|
context = {"header": header, "meeting": meeting, "requester": requester}
|
|
cc = "cc.a@example.com, cc.b@example.com"
|
|
bcc = "bcc@example.com"
|
|
|
|
msg = send_mail(
|
|
None,
|
|
to,
|
|
None,
|
|
subject,
|
|
template,
|
|
context,
|
|
cc=cc,
|
|
bcc=bcc,
|
|
)
|
|
self.assertEqual(requester.name, "James O'Rourke") # note ' (single quote) in the name
|
|
self.assertIn(
|
|
f"{header} meeting session request has just been submitted by {requester.name}.",
|
|
get_payload_text(msg),
|
|
)
|
|
|
|
def test_request_notification_third_session(self):
|
|
meeting = MeetingFactory(type_id='ietf', date=date_today())
|
|
ad = Person.objects.get(user__username='ad')
|
|
area = GroupFactory(type_id='area')
|
|
RoleFactory(name_id='ad', person=ad, group=area)
|
|
group = GroupFactory(acronym='ames', parent=area)
|
|
group2 = GroupFactory(acronym='ames2', parent=area)
|
|
group3 = GroupFactory(acronym='ames2', parent=area)
|
|
group4 = GroupFactory(acronym='ames3', parent=area)
|
|
RoleFactory(name_id='chair', group=group, person__user__username='ameschairman')
|
|
resource = ResourceAssociation.objects.create(name_id='project')
|
|
# Bit of a test data hack - the fixture now has no used resources to pick from
|
|
resource.name.used=True
|
|
resource.name.save()
|
|
|
|
url = reverse('ietf.secr.sreq.views.new',kwargs={'acronym':group.acronym})
|
|
confirm_url = reverse('ietf.secr.sreq.views.confirm',kwargs={'acronym':group.acronym})
|
|
len_before = len(outbox)
|
|
attendees = '10'
|
|
post_data = {'num_session':'2',
|
|
'third_session': 'true',
|
|
'attendees':attendees,
|
|
'bethere':str(ad.pk),
|
|
'constraint_chair_conflict':group4.acronym,
|
|
'comments':'',
|
|
'resources': resource.pk,
|
|
'session_time_relation': 'subsequent-days',
|
|
'adjacent_with_wg': group2.acronym,
|
|
'joint_with_groups': group3.acronym,
|
|
'joint_for_session': '2',
|
|
'timeranges': ['thursday-afternoon-early', 'thursday-afternoon-late'],
|
|
'session_set-TOTAL_FORMS': '3',
|
|
'session_set-INITIAL_FORMS': '0',
|
|
'session_set-MIN_NUM_FORMS': '1',
|
|
'session_set-MAX_NUM_FORMS': '3',
|
|
# no 'session_set-0-id' for new session
|
|
'session_set-0-name': '',
|
|
'session_set-0-short': '',
|
|
'session_set-0-purpose': 'regular',
|
|
'session_set-0-type': 'regular',
|
|
'session_set-0-requested_duration': '3600',
|
|
'session_set-0-on_agenda': True,
|
|
'session_set-0-remote_instructions': '',
|
|
'session_set-0-attendees': attendees,
|
|
'session_set-0-comments': '',
|
|
'session_set-0-DELETE': '',
|
|
# no 'session_set-1-id' for new session
|
|
'session_set-1-name': '',
|
|
'session_set-1-short': '',
|
|
'session_set-1-purpose': 'regular',
|
|
'session_set-1-type': 'regular',
|
|
'session_set-1-requested_duration': '3600',
|
|
'session_set-1-on_agenda': True,
|
|
'session_set-1-remote_instructions': '',
|
|
'session_set-1-attendees': attendees,
|
|
'session_set-1-comments': '',
|
|
'session_set-1-DELETE': '',
|
|
# no 'session_set-2-id' for new session
|
|
'session_set-2-name': '',
|
|
'session_set-2-short': '',
|
|
'session_set-2-purpose': 'regular',
|
|
'session_set-2-type': 'regular',
|
|
'session_set-2-requested_duration': '3600',
|
|
'session_set-2-on_agenda': True,
|
|
'session_set-2-remote_instructions': '',
|
|
'session_set-2-attendees': attendees,
|
|
'session_set-2-comments': '',
|
|
'session_set-2-DELETE': '',
|
|
'submit': 'Continue'}
|
|
self.client.login(username="ameschairman", password="ameschairman+password")
|
|
# submit
|
|
r = self.client.post(url,post_data)
|
|
self.assertEqual(r.status_code, 200)
|
|
q = PyQuery(r.content)
|
|
self.assertTrue('Confirm' in str(q("title")), r.context['form'].errors)
|
|
# confirm
|
|
post_data['submit'] = 'Submit'
|
|
r = self.client.post(confirm_url,post_data)
|
|
self.assertRedirects(r, reverse('ietf.secr.sreq.views.main'))
|
|
self.assertEqual(len(outbox),len_before+1)
|
|
notification = outbox[-1]
|
|
notification_payload = get_payload_text(notification)
|
|
sessions = Session.objects.filter(meeting=meeting,group=group)
|
|
self.assertEqual(len(sessions), 3)
|
|
session = sessions[0]
|
|
|
|
self.assertEqual(session.resources.count(),1)
|
|
self.assertEqual(session.people_constraints.count(),1)
|
|
self.assertEqual(session.constraints().get(name='time_relation').time_relation, 'subsequent-days')
|
|
self.assertEqual(session.constraints().get(name='wg_adjacent').target.acronym, group2.acronym)
|
|
self.assertEqual(
|
|
list(session.constraints().get(name='timerange').timeranges.all().values('name')),
|
|
list(TimerangeName.objects.filter(name__in=['thursday-afternoon-early', 'thursday-afternoon-late']).values('name'))
|
|
)
|
|
resource = session.resources.first()
|
|
self.assertTrue(resource.desc in notification_payload)
|
|
self.assertTrue('Schedule the sessions on subsequent days' in notification_payload)
|
|
self.assertTrue(group2.acronym in notification_payload)
|
|
self.assertTrue("Can't meet: Thursday early afternoon, Thursday late" in notification_payload)
|
|
self.assertTrue('Second session joint with: {}'.format(group3.acronym) in notification_payload)
|
|
self.assertTrue(ad.ascii_name() in notification_payload)
|
|
self.assertIn(ConstraintName.objects.get(slug='chair_conflict').name, notification_payload)
|
|
self.assertIn(group.acronym, notification_payload)
|
|
self.assertIn('1 Hour, 1 Hour, 1 Hour', notification_payload)
|
|
self.assertIn('The third session requires your approval', notification_payload)
|
|
|
|
class LockAppTestCase(TestCase):
|
|
def setUp(self):
|
|
super().setUp()
|
|
self.meeting = MeetingFactory(type_id='ietf', date=date_today(),session_request_lock_message='locked')
|
|
self.group = GroupFactory(acronym='mars')
|
|
RoleFactory(name_id='chair', group=self.group, person__user__username='marschairman')
|
|
SessionFactory(group=self.group,meeting=self.meeting)
|
|
|
|
def test_edit_request(self):
|
|
url = reverse('ietf.secr.sreq.views.edit',kwargs={'acronym':self.group.acronym})
|
|
self.client.login(username="secretary", password="secretary+password")
|
|
r = self.client.get(url)
|
|
self.assertEqual(r.status_code, 200)
|
|
q = PyQuery(r.content)
|
|
self.assertEqual(len(q(':disabled[name="submit"]')), 0)
|
|
chair = self.group.role_set.filter(name_id='chair').first().person.user.username
|
|
self.client.login(username=chair, password=f'{chair}+password')
|
|
r = self.client.get(url)
|
|
self.assertEqual(r.status_code, 200)
|
|
q = PyQuery(r.content)
|
|
self.assertEqual(len(q(':disabled[name="submit"]')), 1)
|
|
|
|
def test_view_request(self):
|
|
url = reverse('ietf.secr.sreq.views.view',kwargs={'acronym':self.group.acronym})
|
|
self.client.login(username="secretary", password="secretary+password")
|
|
r = self.client.get(url,follow=True)
|
|
self.assertEqual(r.status_code, 200)
|
|
q = PyQuery(r.content)
|
|
self.assertEqual(len(q(':enabled[name="edit"]')), 1) # secretary can edit
|
|
chair = self.group.role_set.filter(name_id='chair').first().person.user.username
|
|
self.client.login(username=chair, password=f'{chair}+password')
|
|
r = self.client.get(url,follow=True)
|
|
self.assertEqual(r.status_code, 200)
|
|
q = PyQuery(r.content)
|
|
self.assertEqual(len(q(':disabled[name="edit"]')), 1) # chair cannot edit
|
|
|
|
def test_new_request(self):
|
|
url = reverse('ietf.secr.sreq.views.new',kwargs={'acronym':self.group.acronym})
|
|
|
|
# try as WG Chair
|
|
self.client.login(username="marschairman", password="marschairman+password")
|
|
r = self.client.get(url, follow=True)
|
|
self.assertEqual(r.status_code, 200)
|
|
q = PyQuery(r.content)
|
|
self.assertEqual(len(q('#session-request-form')),0)
|
|
|
|
# try as Secretariat
|
|
self.client.login(username="secretary", password="secretary+password")
|
|
r = self.client.get(url,follow=True)
|
|
self.assertEqual(r.status_code, 200)
|
|
q = PyQuery(r.content)
|
|
self.assertEqual(len(q('#session-request-form')),1)
|
|
|
|
class NotMeetingCase(TestCase):
|
|
def test_not_meeting(self):
|
|
MeetingFactory(type_id='ietf',date=date_today())
|
|
group = GroupFactory(acronym='mars')
|
|
url = reverse('ietf.secr.sreq.views.no_session',kwargs={'acronym':group.acronym})
|
|
self.client.login(username="secretary", password="secretary+password")
|
|
|
|
empty_outbox()
|
|
|
|
r = self.client.get(url,follow=True)
|
|
# If the view invoked by that get throws an exception (such as an integrity error),
|
|
# the traceback from this test will talk about a TransactionManagementError and
|
|
# yell about executing queries before the end of an 'atomic' block
|
|
|
|
# This is a sign of a problem - a get shouldn't have a side-effect like this one does
|
|
self.assertEqual(r.status_code, 200)
|
|
self.assertContains(r, 'A message was sent to notify not having a session')
|
|
|
|
r = self.client.get(url,follow=True)
|
|
self.assertEqual(r.status_code, 200)
|
|
self.assertContains(r, 'is already marked as not meeting')
|
|
|
|
self.assertEqual(len(outbox),1)
|
|
self.assertTrue('Not having a session' in outbox[0]['Subject'])
|
|
self.assertTrue('session-request@' in outbox[0]['To'])
|
|
|
|
class RetrievePreviousCase(TestCase):
|
|
pass
|
|
|
|
# test error if already scheduled
|
|
# test get previous exists/doesn't exist
|
|
# test that groups scheduled and unscheduled add up to total groups
|
|
# test access by unauthorized
|
|
|
|
|
|
class SessionFormTest(TestCase):
|
|
def setUp(self):
|
|
super().setUp()
|
|
self.meeting = MeetingFactory(type_id='ietf')
|
|
self.group1 = GroupFactory()
|
|
self.group2 = GroupFactory()
|
|
self.group3 = GroupFactory()
|
|
self.group4 = GroupFactory()
|
|
self.group5 = GroupFactory()
|
|
self.group6 = GroupFactory()
|
|
|
|
attendees = '10'
|
|
comments = 'need lights'
|
|
self.valid_form_data = {
|
|
'num_session': '2',
|
|
'third_session': 'true',
|
|
'attendees': attendees,
|
|
'constraint_chair_conflict': self.group2.acronym,
|
|
'constraint_tech_overlap': self.group3.acronym,
|
|
'constraint_key_participant': self.group4.acronym,
|
|
'comments': comments,
|
|
'session_time_relation': 'subsequent-days',
|
|
'adjacent_with_wg': self.group5.acronym,
|
|
'joint_with_groups': self.group6.acronym,
|
|
'joint_for_session': '3',
|
|
'timeranges': ['thursday-afternoon-early', 'thursday-afternoon-late'],
|
|
'submit': 'Continue',
|
|
'session_set-TOTAL_FORMS': '3',
|
|
'session_set-INITIAL_FORMS': '0',
|
|
'session_set-MIN_NUM_FORMS': '1',
|
|
'session_set-MAX_NUM_FORMS': '3',
|
|
# no 'session_set-0-id' for new session
|
|
'session_set-0-name': '',
|
|
'session_set-0-short': '',
|
|
'session_set-0-purpose': 'regular',
|
|
'session_set-0-type': 'regular',
|
|
'session_set-0-requested_duration': '3600',
|
|
'session_set-0-on_agenda': True,
|
|
'session_set-0-remote_instructions': '',
|
|
'session_set-0-attendees': attendees,
|
|
'session_set-0-comments': '',
|
|
'session_set-0-DELETE': '',
|
|
# no 'session_set-1-id' for new session
|
|
'session_set-1-name': '',
|
|
'session_set-1-short': '',
|
|
'session_set-1-purpose': 'regular',
|
|
'session_set-1-type': 'regular',
|
|
'session_set-1-requested_duration': '3600',
|
|
'session_set-1-on_agenda': True,
|
|
'session_set-1-remote_instructions': '',
|
|
'session_set-1-attendees': attendees,
|
|
'session_set-1-comments': '',
|
|
'session_set-1-DELETE': '',
|
|
# no 'session_set-2-id' for new session
|
|
'session_set-2-name': '',
|
|
'session_set-2-short': '',
|
|
'session_set-2-purpose': 'regular',
|
|
'session_set-2-type': 'regular',
|
|
'session_set-2-requested_duration': '3600',
|
|
'session_set-2-on_agenda': True,
|
|
'session_set-2-remote_instructions': '',
|
|
'session_set-2-attendees': attendees,
|
|
'session_set-2-comments': '',
|
|
'session_set-2-DELETE': '',
|
|
}
|
|
|
|
def test_valid(self):
|
|
# Test with three sessions
|
|
form = SessionForm(data=self.valid_form_data, group=self.group1, meeting=self.meeting)
|
|
self.assertTrue(form.is_valid())
|
|
|
|
# Test with two sessions
|
|
self.valid_form_data.update({
|
|
'third_session': '',
|
|
'session_set-TOTAL_FORMS': '2',
|
|
'joint_for_session': '2'
|
|
})
|
|
form = SessionForm(data=self.valid_form_data, group=self.group1, meeting=self.meeting)
|
|
self.assertTrue(form.is_valid())
|
|
|
|
# Test with one session
|
|
self.valid_form_data.update({
|
|
'num_session': 1,
|
|
'session_set-TOTAL_FORMS': '1',
|
|
'joint_for_session': '1',
|
|
'session_time_relation': '',
|
|
})
|
|
form = SessionForm(data=self.valid_form_data, group=self.group1, meeting=self.meeting)
|
|
self.assertTrue(form.is_valid())
|
|
|
|
def test_invalid_groups(self):
|
|
new_form_data = {
|
|
'constraint_chair_conflict': 'doesnotexist',
|
|
'constraint_tech_overlap': 'doesnotexist',
|
|
'constraint_key_participant': 'doesnotexist',
|
|
'adjacent_with_wg': 'doesnotexist',
|
|
'joint_with_groups': 'doesnotexist',
|
|
}
|
|
form = self._invalid_test_helper(new_form_data)
|
|
self.assertEqual(set(form.errors.keys()), set(new_form_data.keys()))
|
|
|
|
def test_valid_group_appears_in_multiple_conflicts(self):
|
|
"""Some conflict types allow overlapping groups"""
|
|
new_form_data = {
|
|
'constraint_chair_conflict': self.group2.acronym,
|
|
'constraint_tech_overlap': self.group2.acronym,
|
|
}
|
|
self.valid_form_data.update(new_form_data)
|
|
form = SessionForm(data=self.valid_form_data, group=self.group1, meeting=self.meeting)
|
|
self.assertTrue(form.is_valid())
|
|
|
|
def test_invalid_group_appears_in_multiple_conflicts(self):
|
|
"""Some conflict types do not allow overlapping groups"""
|
|
self.meeting.group_conflict_types.clear()
|
|
self.meeting.group_conflict_types.add(ConstraintName.objects.get(slug='conflict'))
|
|
self.meeting.group_conflict_types.add(ConstraintName.objects.get(slug='conflic2'))
|
|
new_form_data = {
|
|
'constraint_conflict': self.group2.acronym,
|
|
'constraint_conflic2': self.group2.acronym,
|
|
}
|
|
form = self._invalid_test_helper(new_form_data)
|
|
self.assertEqual(form.non_field_errors(), ['%s appears in conflicts more than once' % self.group2.acronym])
|
|
|
|
def test_invalid_conflict_with_self(self):
|
|
new_form_data = {
|
|
'constraint_chair_conflict': self.group1.acronym,
|
|
}
|
|
self._invalid_test_helper(new_form_data)
|
|
|
|
def test_invalid_session_time_relation(self):
|
|
form = self._invalid_test_helper({
|
|
'third_session': '',
|
|
'session_set-TOTAL_FORMS': 1,
|
|
'num_session': 1,
|
|
'joint_for_session': '1',
|
|
})
|
|
self.assertEqual(form.errors,
|
|
{
|
|
'session_time_relation': ['Time between sessions can only be used when two '
|
|
'sessions are requested.']
|
|
})
|
|
|
|
def test_invalid_joint_for_session(self):
|
|
form = self._invalid_test_helper({
|
|
'third_session': '',
|
|
'session_set-TOTAL_FORMS': '2',
|
|
'num_session': 2,
|
|
'joint_for_session': '3',
|
|
})
|
|
self.assertEqual(form.errors,
|
|
{
|
|
'joint_for_session': [
|
|
'Session 3 can not be the joint session, the session has not been requested.']
|
|
})
|
|
|
|
form = self._invalid_test_helper({
|
|
'third_session': '',
|
|
'session_set-TOTAL_FORMS': '1',
|
|
'num_session': 1,
|
|
'joint_for_session': '2',
|
|
'session_time_relation': '',
|
|
})
|
|
self.assertEqual(form.errors,
|
|
{
|
|
'joint_for_session': [
|
|
'Session 2 can not be the joint session, the session has not been requested.']
|
|
})
|
|
|
|
def test_invalid_missing_session_length(self):
|
|
form = self._invalid_test_helper({
|
|
'session_set-TOTAL_FORMS': '2',
|
|
'session_set-1-requested_duration': '',
|
|
'third_session': 'false',
|
|
'joint_for_session': None,
|
|
})
|
|
self.assertEqual(form.session_forms.errors,
|
|
[
|
|
{},
|
|
{'requested_duration': ['This field is required.']},
|
|
])
|
|
|
|
form = self._invalid_test_helper({
|
|
'session_set-1-requested_duration': '',
|
|
'session_set-2-requested_duration': '',
|
|
'joint_for_session': None,
|
|
})
|
|
self.assertEqual(
|
|
form.session_forms.errors,
|
|
[
|
|
{},
|
|
{'requested_duration': ['This field is required.']},
|
|
{'requested_duration': ['This field is required.']},
|
|
])
|
|
|
|
form = self._invalid_test_helper({
|
|
'session_set-2-requested_duration': '',
|
|
'joint_for_session': None,
|
|
})
|
|
self.assertEqual(form.session_forms.errors,
|
|
[
|
|
{},
|
|
{},
|
|
{'requested_duration': ['This field is required.']},
|
|
])
|
|
|
|
def _invalid_test_helper(self, new_form_data):
|
|
form_data = dict(self.valid_form_data, **new_form_data)
|
|
form = SessionForm(data=form_data, group=self.group1, meeting=self.meeting)
|
|
self.assertFalse(form.is_valid())
|
|
return form
|