# Copyright The IETF Trust 2009-2021, All Rights Reserved # -*- coding: utf-8 -*- import datetime import io import tarfile from pathlib import Path from pyquery import PyQuery from django.conf import settings from django.urls import reverse as urlreverse from django.utils.encoding import force_bytes from django.utils.html import escape import debug # pyflakes:ignore from ietf.doc.models import DocEvent, BallotPositionDocEvent, TelechatDocEvent from ietf.doc.models import Document, State, RelatedDocument from ietf.doc.factories import BallotDocEventFactory, BallotPositionDocEventFactory, TelechatDocEventFactory, WgDraftFactory, IndividualDraftFactory, ConflictReviewFactory, BaseDocumentFactory, CharterFactory, WgRfcFactory, IndividualRfcFactory from ietf.doc.utils import create_ballot_if_not_open from ietf.group.factories import RoleFactory, GroupFactory, DatedGroupMilestoneFactory, DatelessGroupMilestoneFactory from ietf.group.models import Group, GroupMilestone, Role from ietf.iesg.agenda import get_agenda_date, agenda_data, fill_in_agenda_administrivia, agenda_sections from ietf.iesg.models import TelechatDate, TelechatAgendaContent from ietf.name.models import StreamName, TelechatAgendaSectionName from ietf.person.models import Person from ietf.utils.test_utils import TestCase, login_testing_unauthorized, unicontent from ietf.iesg.factories import IESGMgmtItemFactory, TelechatAgendaContentFactory from ietf.utils.timezone import date_today, DEADLINE_TZINFO class IESGTests(TestCase): def test_feed(self): draft = WgDraftFactory(states=[('draft','active'),('draft-iesg','iesg-eva')],ad=Person.objects.get(user__username='ad')) ad = Person.objects.get(user__username="ad") ballot = create_ballot_if_not_open(None, draft, ad, 'approve') pos = BallotPositionDocEvent() pos.ballot = ballot pos.pos_id = "discuss" pos.type = "changed_ballot_position" pos.doc = draft pos.rev = draft.rev pos.balloter = pos.by = Person.objects.get(user__username="ad") pos.save() r = self.client.get(urlreverse("ietf.iesg.views.discusses")) self.assertEqual(r.status_code, 200) self.assertContains(r, draft.name) self.assertContains(r, escape(pos.balloter.plain_name())) # Mark draft as replaced draft.set_state(State.objects.get(type="draft", slug="repl")) r = self.client.get(urlreverse("ietf.iesg.views.discusses")) self.assertEqual(r.status_code, 200) self.assertNotContains(r, draft.name) self.assertNotContains(r, escape(pos.balloter.plain_name())) def test_milestones_needing_review(self): draft = WgDraftFactory() RoleFactory(name_id='ad',group=draft.group,person=Person.objects.get(user__username='ad')) m = GroupMilestone.objects.create(group=draft.group, state_id="review", desc="Test milestone", due=date_today(DEADLINE_TZINFO)) url = urlreverse("ietf.iesg.views.milestones_needing_review") login_testing_unauthorized(self, "ad", url) r = self.client.get(url) self.assertEqual(r.status_code, 200) self.assertContains(r, m.desc) draft.group.state_id = 'conclude' draft.group.save() r = self.client.get(url) self.assertEqual(r.status_code, 200) self.assertNotContains(r, m.desc) def test_milestones_needing_review_ordering(self): dated_group = GroupFactory(uses_milestone_dates=True) RoleFactory( name_id='ad', group=dated_group, person=Person.objects.get(user__username='ad'), ) dated_milestones = DatedGroupMilestoneFactory.create_batch( 2, group=dated_group, state_id="review" ) dated_milestones[0].due -= datetime.timedelta(days=1) # make this one earlier dated_milestones[0].save() dateless_group = GroupFactory(uses_milestone_dates=False) RoleFactory( name_id='ad', group=dateless_group, person=Person.objects.get(user__username='ad'), ) dateless_milestones = DatelessGroupMilestoneFactory.create_batch( 2, group=dateless_group, state_id="review" ) url = urlreverse("ietf.iesg.views.milestones_needing_review") self.client.login(username="ad", password="ad+password") r = self.client.get(url) self.assertEqual(r.status_code, 200) pq = PyQuery(r.content) # check order-by-date dated_tbody = pq(f'td:contains("{dated_milestones[0].desc}")').closest("tbody") next_td = dated_tbody.find('td:contains("Next")') self.assertEqual(next_td.siblings()[0].text.strip(), dated_milestones[0].desc) last_td = dated_tbody.find('td:contains("Last")') self.assertEqual(last_td.siblings()[0].text.strip(), dated_milestones[1].desc) # check order-by-order dateless_tbody = pq(f'td:contains("{dateless_milestones[0].desc}")').closest("tbody") next_td = dateless_tbody.find('td:contains("Next")') self.assertEqual(next_td.siblings()[0].text.strip(), dateless_milestones[0].desc) last_td = dateless_tbody.find('td:contains("Last")') self.assertEqual(last_td.siblings()[0].text.strip(), dateless_milestones[1].desc) def test_review_decisions(self): draft = WgDraftFactory() e = DocEvent(type="iesg_approved") e.doc = draft e.rev = draft.rev e.by = Person.objects.get(name="Areað Irector") e.save() url = urlreverse('ietf.iesg.views.review_decisions') r = self.client.get(url) self.assertEqual(r.status_code, 200) self.assertContains(r, draft.name) def test_photos(self): url = urlreverse("ietf.iesg.views.photos") r = self.client.get(url) self.assertEqual(r.status_code, 200) q = PyQuery(r.content) ads = Role.objects.filter(group__type='area', group__state='active', name_id='ad') self.assertEqual(len(q('.photo')), ads.count()) def test_ietf_activity(self): url = urlreverse("ietf.iesg.views.ietf_activity") r = self.client.get(url) self.assertEqual(r.status_code, 200) class IESGAgendaTests(TestCase): def setUp(self): super().setUp() mars = GroupFactory(acronym='mars',parent=Group.objects.get(acronym='farfut')) wgdraft = WgDraftFactory(name='draft-ietf-mars-test', group=mars, intended_std_level_id='ps') rfc = IndividualRfcFactory.create(stream_id='irtf', rfc_number=6666, std_level_id='inf', ) wgdraft.relateddocument_set.create(target=rfc, relationship_id='refnorm') ise_draft = IndividualDraftFactory(name='draft-imaginary-independent-submission') ise_draft.stream = StreamName.objects.get(slug="ise") ise_draft.save_with_history([DocEvent(doc=ise_draft, rev=ise_draft.rev, type="changed_stream", by=Person.objects.get(user__username="secretary"), desc="Test")]) ConflictReviewFactory(name='conflict-review-imaginary-irtf-submission', review_of=ise_draft) BaseDocumentFactory(type_id='statchg',name='status-change-imaginary-mid-review') WgRfcFactory(std_level_id='inf') WgRfcFactory(std_level_id='ps') CharterFactory(states=[('charter','iesgrev')]) self.telechat_docs = { "ietf_draft": Document.objects.get(name="draft-ietf-mars-test"), "ise_draft": ise_draft, "conflrev": Document.objects.get(name="conflict-review-imaginary-irtf-submission"), "statchg": Document.objects.get(name="status-change-imaginary-mid-review"), "charter": Document.objects.filter(type="charter")[0], } by = Person.objects.get(name="Areað Irector") date = get_agenda_date() for d in list(self.telechat_docs.values()): TelechatDocEvent.objects.create(type="scheduled_for_telechat", doc=d, rev=d.rev, by=by, telechat_date=date, returning_item=True) self.mgmt_items = [ ] for i in range(0, 10): self.mgmt_items.append(IESGMgmtItemFactory()) def test_fill_in_agenda_administrivia(self): roll_call = TelechatAgendaContentFactory(section_id='roll_call') minutes = TelechatAgendaContentFactory(section_id='minutes') action_items = TelechatAgendaContentFactory(section_id='action_items') sections = agenda_sections() fill_in_agenda_administrivia(None, sections) # n.b., date parameter is unused at present self.assertIn(roll_call.text, sections["1.1"]["text"]) self.assertIn(minutes.text, sections["1.3"]["text"]) self.assertIn(action_items.text, sections["1.4"]["text"]) def test_fill_in_agenda_docs(self): draft = self.telechat_docs["ietf_draft"] statchg = self.telechat_docs["statchg"] conflrev = self.telechat_docs["conflrev"] charter = self.telechat_docs["charter"] mgmtitem = self.mgmt_items # put on agenda date = date_today(settings.TIME_ZONE) + datetime.timedelta(days=50) TelechatDate.objects.create(date=date) telechat_event = TelechatDocEvent.objects.create( type="scheduled_for_telechat", doc=draft, rev=draft.rev, by=Person.objects.get(name="Areað Irector"), telechat_date=date, returning_item=False) date_str = date.isoformat() # 2.1 protocol WG submissions draft.intended_std_level_id = "ps" draft.group = GroupFactory(acronym="mars") draft.save_with_history([DocEvent.objects.create(doc=draft, rev=draft.rev, type="changed_group", by=Person.objects.get(user__username="secretary"), desc="Test")]) draft.set_state(State.objects.get(type="draft-iesg", slug="iesg-eva")) self.assertTrue(draft in agenda_data(date_str)["sections"]["2.1.1"]["docs"]) telechat_event.returning_item = True telechat_event.save() self.assertTrue(draft in agenda_data(date_str)["sections"]["2.1.2"]["docs"]) telechat_event.returning_item = False telechat_event.save() draft.set_state(State.objects.get(type="draft-iesg", slug="pub-req")) self.assertTrue(draft in agenda_data(date_str)["sections"]["2.1.3"]["docs"]) # 2.2 protocol individual submissions draft.group = Group.objects.get(type="individ") draft.save_with_history([DocEvent.objects.create(doc=draft, rev=draft.rev, type="changed_group", by=Person.objects.get(user__username="secretary"), desc="Test")]) draft.set_state(State.objects.get(type="draft-iesg", slug="iesg-eva")) self.assertTrue(draft in agenda_data(date_str)["sections"]["2.2.1"]["docs"]) telechat_event.returning_item = True telechat_event.save() self.assertTrue(draft in agenda_data(date_str)["sections"]["2.2.2"]["docs"]) telechat_event.returning_item = False telechat_event.save() draft.set_state(State.objects.get(type="draft-iesg", slug="pub-req")) self.assertTrue(draft in agenda_data(date_str)["sections"]["2.2.3"]["docs"]) # 3.1 document WG submissions draft.intended_std_level_id = "inf" draft.group = Group.objects.get(acronym="mars") draft.save_with_history([DocEvent.objects.create(doc=draft, rev=draft.rev, type="changed_group", by=Person.objects.get(user__username="secretary"), desc="Test")]) draft.set_state(State.objects.get(type="draft-iesg", slug="iesg-eva")) self.assertTrue(draft in agenda_data(date_str)["sections"]["3.1.1"]["docs"]) telechat_event.returning_item = True telechat_event.save() self.assertTrue(draft in agenda_data(date_str)["sections"]["3.1.2"]["docs"]) telechat_event.returning_item = False telechat_event.save() draft.set_state(State.objects.get(type="draft-iesg", slug="pub-req")) self.assertTrue(draft in agenda_data(date_str)["sections"]["3.1.3"]["docs"]) # 3.2 document individual submissions draft.group = Group.objects.get(type="individ") draft.save_with_history([DocEvent.objects.create(doc=draft, rev=draft.rev, type="changed_group", by=Person.objects.get(user__username="secretary"), desc="Test")]) draft.set_state(State.objects.get(type="draft-iesg", slug="iesg-eva")) self.assertTrue(draft in agenda_data(date_str)["sections"]["3.2.1"]["docs"]) telechat_event.returning_item = True telechat_event.save() self.assertTrue(draft in agenda_data(date_str)["sections"]["3.2.2"]["docs"]) telechat_event.returning_item = False telechat_event.save() draft.set_state(State.objects.get(type="draft-iesg", slug="pub-req")) self.assertTrue(draft in agenda_data(date_str)["sections"]["3.2.3"]["docs"]) # 2.3 protocol status changes telechat_event.doc = statchg telechat_event.save() relation = RelatedDocument.objects.create( source=statchg, target=Document.objects.filter(type_id="rfc", std_level="ps").first(), relationship_id="tohist") statchg.group = Group.objects.get(acronym="mars") statchg.save_with_history([DocEvent.objects.create(doc=statchg, rev=statchg.rev, type="changed_group", by=Person.objects.get(user__username="secretary"), desc="Test")]) statchg.set_state(State.objects.get(type="statchg", slug="iesgeval")) self.assertTrue(statchg in agenda_data(date_str)["sections"]["2.3.1"]["docs"]) telechat_event.returning_item = True telechat_event.save() self.assertTrue(statchg in agenda_data(date_str)["sections"]["2.3.2"]["docs"]) telechat_event.returning_item = False telechat_event.save() statchg.set_state(State.objects.get(type="statchg", slug="adrev")) self.assertTrue(statchg in agenda_data(date_str)["sections"]["2.3.3"]["docs"]) # 3.3 document status changes relation.target = Document.objects.filter(type_id="rfc", std_level="inf").first() relation.save() statchg.group = Group.objects.get(acronym="mars") statchg.save_with_history([DocEvent.objects.create(doc=statchg, rev=statchg.rev, type="changed_group", by=Person.objects.get(user__username="secretary"), desc="Test")]) statchg.set_state(State.objects.get(type="statchg", slug="iesgeval")) self.assertTrue(statchg in agenda_data(date_str)["sections"]["3.3.1"]["docs"]) telechat_event.returning_item = True telechat_event.save() self.assertTrue(statchg in agenda_data(date_str)["sections"]["3.3.2"]["docs"]) telechat_event.returning_item = False telechat_event.save() statchg.set_state(State.objects.get(type="statchg", slug="adrev")) self.assertTrue(statchg in agenda_data(date_str)["sections"]["3.3.3"]["docs"]) # 3.4 IRTF/ISE conflict reviews telechat_event.doc = conflrev telechat_event.save() conflrev.group = Group.objects.get(acronym="mars") conflrev.save_with_history([DocEvent.objects.create(doc=conflrev, rev=conflrev.rev, type="changed_group", by=Person.objects.get(user__username="secretary"), desc="Test")]) conflrev.set_state(State.objects.get(type="conflrev", slug="iesgeval")) self.assertTrue(conflrev in agenda_data(date_str)["sections"]["3.4.1"]["docs"]) telechat_event.returning_item = True telechat_event.save() self.assertTrue(conflrev in agenda_data(date_str)["sections"]["3.4.2"]["docs"]) telechat_event.returning_item = False telechat_event.save() conflrev.set_state(State.objects.get(type="conflrev", slug="needshep")) self.assertTrue(conflrev in agenda_data(date_str)["sections"]["3.4.3"]["docs"]) # 4 WGs telechat_event.doc = charter telechat_event.save() charter.group = Group.objects.get(acronym="mars") charter.save_with_history([DocEvent.objects.create(doc=charter, rev=charter.rev, type="changed_group", by=Person.objects.get(user__username="secretary"), desc="Test")]) charter.group.state_id = "bof" charter.group.save() charter.set_state(State.objects.get(type="charter", slug="infrev")) self.assertTrue(charter in agenda_data(date_str)["sections"]["4.1.1"]["docs"]) charter.set_state(State.objects.get(type="charter", slug="iesgrev")) self.assertTrue(charter in agenda_data(date_str)["sections"]["4.1.2"]["docs"]) charter.group.state_id = "active" charter.group.save() charter.set_state(State.objects.get(type="charter", slug="infrev")) self.assertTrue(charter in agenda_data(date_str)["sections"]["4.2.1"]["docs"]) charter.set_state(State.objects.get(type="charter", slug="iesgrev")) self.assertTrue(charter in agenda_data(date_str)["sections"]["4.2.2"]["docs"]) #for n, s in agenda_data(date_str)["sections"].iteritems(): # print n, s.get("docs") if "docs" in s else s["title"] # 10 Management Items for i, mi in enumerate(mgmtitem, start=1): s = "6." + str(i) self.assertEqual(mi.title, agenda_data(date_str)["sections"][s]['title']) def test_feed(self): r = self.client.get("/feed/iesg-agenda/") self.assertEqual(r.status_code, 200) for d in list(self.telechat_docs.values()): self.assertContains(r, d.name) self.assertContains(r, d.title) def test_agenda_json(self): r = self.client.get(urlreverse("ietf.iesg.views.agenda_json")) self.assertEqual(r.status_code, 200) for k, d in self.telechat_docs.items(): if d.type_id == "charter": self.assertContains(r, d.group.name, msg_prefix="%s '%s' not in response" % (k, d.group.name)) self.assertContains(r, d.group.acronym, msg_prefix="%s '%s' acronym not in response" % (k, d.group.acronym)) else: self.assertContains(r, d.name, msg_prefix="%s '%s' not in response" % (k, d.name)) self.assertContains(r, d.title, msg_prefix="%s '%s' title not in response" % (k, d.title)) self.assertTrue(r.json()) def test_agenda(self): action_items = TelechatAgendaContentFactory(section_id='action_items') r = self.client.get(urlreverse("ietf.iesg.views.agenda")) self.assertEqual(r.status_code, 200) self.assertContains(r, action_items.text) q = PyQuery(r.content) for k, d in self.telechat_docs.items(): if d.type_id == "charter": self.assertContains(r, d.group.name, msg_prefix="%s '%s' not in response" % (k, d.group.name)) self.assertContains(r, d.group.acronym, msg_prefix="%s '%s' acronym not in response" % (k, d.group.acronym)) else: self.assertContains(r, d.name, msg_prefix="%s '%s' not in response" % (k, d.name)) self.assertContains(r, d.title, msg_prefix="%s '%s' title not in response" % (k, d.title)) if d.type_id in ["charter", "draft"]: if d.group.parent is None: continue wg_url = urlreverse("ietf.group.views.active_groups", kwargs=dict(group_type="wg")) href = f"{wg_url}#{d.group.parent.acronym.upper()}" texts = [elem.text.strip() for elem in q(f'a[href="{href}"]')] self.assertGreater(len(texts), 0) if d.type_id == "charter": self.assertTrue(any(t == d.group.parent.acronym.upper() for t in texts)) elif d.type_id == "draft": self.assertTrue(any(t == f"({d.group.parent.acronym.upper()})" for t in texts)) for i, mi in enumerate(self.mgmt_items, start=1): s = "6." + str(i) self.assertContains(r, s, msg_prefix="Section '%s' not in response" % s) self.assertContains(r, mi.title, msg_prefix="Management item title '%s' not in response" % mi.title) # Make sure the sort places 6.9 before 6.10 self.assertLess(r.content.find(b"6.9"), r.content.find(b"6.10")) def test_agenda_restricted_sections(self): r = self.client.get(urlreverse("ietf.iesg.views.agenda")) # not logged in for section_id in ("roll_call", "minutes"): self.assertNotContains( r, urlreverse("ietf.iesg.views.telechat_agenda_content_view", kwargs={"section": section_id}) ) self.client.login(username="plain", password="plain+password") for section_id in ("roll_call", "minutes"): self.assertNotContains( r, urlreverse("ietf.iesg.views.telechat_agenda_content_view", kwargs={"section": section_id}) ) for username in ("ad", "secretary", "iab chair"): self.client.login(username=username, password=f"{username}+password") r = self.client.get(urlreverse("ietf.iesg.views.agenda")) self.assertEqual(r.status_code, 200) for section_id in ("roll_call", "minutes"): self.assertContains( r, urlreverse("ietf.iesg.views.telechat_agenda_content_view", kwargs={"section": section_id}) ) def test_agenda_txt(self): r = self.client.get(urlreverse("ietf.iesg.views.agenda_txt")) self.assertEqual(r.status_code, 200) for k, d in self.telechat_docs.items(): if d.type_id == "charter": self.assertContains(r, d.group.name, msg_prefix="%s '%s' not in response" % (k, d.group.name)) self.assertContains(r, d.group.acronym, msg_prefix="%s '%s' acronym not in response" % (k, d.group.acronym)) else: self.assertContains(r, d.name, msg_prefix="%s '%s' not in response" % (k, d.name)) self.assertContains(r, d.title, msg_prefix="%s '%s' title not in response" % (k, d.title)) for i, mi in enumerate(self.mgmt_items, start=1): s = "6." + str(i) self.assertContains(r, s, msg_prefix="Section '%s' not in response" % s) self.assertContains(r, mi.title, msg_prefix="Management item title '%s' not in response" % mi.title) # Make sure the sort places 6.9 before 6.10 self.assertLess(r.content.find(b"6.9"), r.content.find(b"6.10")) def test_agenda_moderator_package(self): url = urlreverse("ietf.iesg.views.agenda_moderator_package") login_testing_unauthorized(self, "secretary", url) r = self.client.get(url) self.assertEqual(r.status_code, 200) for k, d in self.telechat_docs.items(): if d.type_id == "charter": self.assertContains(r, d.group.name, msg_prefix="%s '%s' not in response" % (k, d.group.name)) self.assertContains(r, d.group.acronym, msg_prefix="%s '%s' acronym not in response" % (k, d.group.acronym)) else: if d.type_id == "draft" and d.name == "draft-ietf-mars-test": self.assertContains(r, d.name, msg_prefix="%s '%s' not in response" % (k, d.name)) self.assertContains(r, d.title, msg_prefix="%s '%s' title not in response" % (k, d.title)) self.assertContains(r, "Has downref: Yes", msg_prefix="%s downref not in response" % (k, )) self.assertContains(r, "Add rfc6666", msg_prefix="%s downref not in response" % (k, )) else: self.assertContains(r, d.name, msg_prefix="%s '%s' not in response" % (k, d.name)) self.assertContains(r, d.title, msg_prefix="%s '%s' title not in response" % (k, d.title)) def test_agenda_package(self): url = urlreverse("ietf.iesg.views.agenda_package") login_testing_unauthorized(self, "secretary", url) r = self.client.get(url) self.assertEqual(r.status_code, 200) for k, d in self.telechat_docs.items(): if d.type_id == "charter": self.assertContains(r, d.group.name, msg_prefix="%s '%s' not in response" % (k, d.group.name, )) self.assertContains(r, d.group.acronym, msg_prefix="%s '%s' acronym not in response" % (k, d.group.acronym, )) else: self.assertContains(r, d.name, msg_prefix="%s '%s' not in response" % (k, d.name, )) self.assertContains(r, d.title, msg_prefix="%s '%s' title not in response" % (k, d.title, )) def test_agenda_documents_txt(self): url = urlreverse("ietf.iesg.views.agenda_documents_txt") r = self.client.get(url) self.assertEqual(r.status_code, 200) for k, d in self.telechat_docs.items(): self.assertContains(r, d.name, msg_prefix="%s '%s' not in response" % (k, d.name, )) def test_agenda_documents(self): url = urlreverse("ietf.iesg.views.agenda_documents") r = self.client.get(url) self.assertEqual(r.status_code, 200) for k, d in self.telechat_docs.items(): self.assertContains(r, d.name, msg_prefix="%s '%s' not in response" % (k, d.name, )) self.assertContains(r, d.title, msg_prefix="%s '%s' not in response" % (k, d.title, )) def test_past_documents(self): url = urlreverse("ietf.iesg.views.past_documents") # We haven't put any documents on past telechats, so this should be empty r = self.client.get(url) self.assertEqual(r.status_code, 200) for k, d in self.telechat_docs.items(): self.assertNotIn(d.name, unicontent(r)) self.assertNotIn(d.title, unicontent(r)) # Add the documents to a past telechat by = Person.objects.get(name="Areað Irector") date = date_today(settings.TIME_ZONE) - datetime.timedelta(days=14) approved = State.objects.get(type='draft-iesg', slug='approved') iesg_eval = State.objects.get(type='draft-iesg', slug='iesg-eva') for d in list(self.telechat_docs.values()): if d.type_id in ['draft', 'charter']: create_ballot_if_not_open(None, d, by, 'approve') TelechatDocEvent.objects.create(type="scheduled_for_telechat", doc=d, rev=d.rev, by=by, telechat_date=date, returning_item=False) s = d.get_state('draft-iesg') d.states.clear() if s and s.slug == 'pub-req': d.states.add(iesg_eval) else: d.states.add(approved) # Now check that they are present on the past documents page r = self.client.get(url) self.assertEqual(r.status_code, 200) for k, d in self.telechat_docs.items(): if d.states.get(type='draft-iesg').slug in ['approved', 'iesg-eva', ]: self.assertIn(d.name, unicontent(r)) else: self.assertNotIn(d.name, unicontent(r)) def test_agenda_telechat_docs(self): d1 = self.telechat_docs["ietf_draft"] d2 = self.telechat_docs["ise_draft"] d1_filename = "%s-%s.txt" % (d1.name, d1.rev) d2_filename = "%s-%s.txt" % (d2.name, d2.rev) with (Path(settings.INTERNET_DRAFT_PATH) / d1_filename).open("w") as f: f.write("test content") url = urlreverse("ietf.iesg.views.telechat_docs_tarfile", kwargs=dict(date=get_agenda_date().isoformat())) r = self.client.get(url) self.assertEqual(r.status_code, 200) tar = tarfile.open(None, fileobj=io.BytesIO(r.content)) names = tar.getnames() self.assertIn(d1_filename, names) self.assertNotIn(d2_filename, names) self.assertIn("manifest.txt", names) f = tar.extractfile(d1_filename) self.assertEqual(f.read(), b"test content") f = tar.extractfile("manifest.txt") lines = list(f.readlines()) d1fn = force_bytes(d1_filename) d2fn = force_bytes(d2_filename) self.assertTrue(b"Included" in [l for l in lines if d1fn in l][0]) self.assertTrue(b"Not found" in [l for l in lines if d2fn in l][0]) def test_admin_change(self): draft = Document.objects.get(name="draft-ietf-mars-test") today = date_today(settings.TIME_ZONE) telechat_date = TelechatDate.objects.get(date=draft.telechat_date()) url = urlreverse('admin:iesg_telechatdate_change', args=(telechat_date.id,)) self.client.login(username="secretary", password="secretary+password") r = self.client.post(url, {'initial-date': telechat_date.date.strftime('%Y-%m-%d'), 'date':today.strftime('%Y-%m-%d')}) self.assertRedirects(r, urlreverse('admin:iesg_telechatdate_changelist')) draft = Document.objects.get(name="draft-ietf-mars-test") self.assertEqual(draft.telechat_date(),today) class IESGAgendaTelechatPagesTests(TestCase): def setUp(self): super().setUp() # make_immutable_test_data made a set of future telechats - only need one # We'll take the "next" one self.telechat_date = get_agenda_date() # make_immutable_test_data made and area with only one ad - give it another ad = Person.objects.get(user__username="ad") adrole = Role.objects.get(person=ad, name="ad") ad2 = RoleFactory(group=adrole.group, name_id="ad").person self.ads=[ad,ad2] # Make some drafts docs = [ WgDraftFactory(pages=2, states=[('draft-iesg','iesg-eva'),]), IndividualDraftFactory(pages=20, states=[('draft-iesg','iesg-eva'),]), WgDraftFactory(pages=200, states=[('draft-iesg','iesg-eva'),]), ] # Put them on the telechat for doc in docs: TelechatDocEventFactory(doc=doc, telechat_date=self.telechat_date) # Give them ballots ballots = [BallotDocEventFactory(doc=doc) for doc in docs] # Give the "ad" Area-Director a discuss on one BallotPositionDocEventFactory(balloter=ad, doc=docs[0], pos_id="discuss", ballot=ballots[0]) # and a "norecord" position on another BallotPositionDocEventFactory(balloter=ad, doc=docs[1], pos_id="norecord", ballot=ballots[1]) # Now "ad" should have 220 pages left to ballot on. # Every other ad should have 222 pages left to ballot on. def test_ad_pages_left_to_ballot_on(self): url = urlreverse("ietf.iesg.views.agenda_documents") # A non-AD user won't get "pages left" response = self.client.get(url) telechat = response.context["telechats"][0] self.assertEqual(telechat["date"], self.telechat_date) self.assertEqual(telechat["ad_pages_left_to_ballot_on"],0) self.assertNotContains(response,"pages left to ballot on") username=self.ads[0].user.username self.assertTrue(self.client.login(username=username, password=f"{username}+password")) response = self.client.get(url) telechat = response.context["telechats"][0] self.assertEqual(telechat["ad_pages_left_to_ballot_on"],220) self.assertContains(response,"220 pages left to ballot on") self.client.logout() username=self.ads[1].user.username self.assertTrue(self.client.login(username=username, password=f"{username}+password")) response = self.client.get(url) telechat = response.context["telechats"][0] self.assertEqual(telechat["ad_pages_left_to_ballot_on"],222) class RescheduleOnAgendaTests(TestCase): def test_reschedule(self): draft = WgDraftFactory() # add to schedule e = TelechatDocEvent(type="scheduled_for_telechat") e.doc = draft e.rev = draft.rev e.by = Person.objects.get(name="Areað Irector") e.telechat_date = TelechatDate.objects.active()[0].date e.returning_item = True e.save() form_id = draft.name url = urlreverse('ietf.iesg.views.agenda_documents') self.client.login(username="secretary", password="secretary+password") # normal get r = self.client.get(url) self.assertEqual(r.status_code, 200) q = PyQuery(r.content) self.assertEqual(len(q('form select[name=%s-telechat_date]' % form_id)), 1) self.assertEqual(len(q('form input[name=%s-clear_returning_item]' % form_id)), 1) # reschedule events_before = draft.docevent_set.count() d = TelechatDate.objects.active()[3].date r = self.client.post(url, { '%s-telechat_date' % form_id: d.isoformat(), '%s-clear_returning_item' % form_id: "1" }) self.assertEqual(r.status_code, 302) # check that it moved below the right header in the DOM on the # agenda docs page r = self.client.get(url) self.assertEqual(r.status_code, 200) content = unicontent(r) d_header_pos = content.find("IESG telechat %s" % d.isoformat()) draft_pos = content[d_header_pos:].find(draft.name) self.assertTrue(draft_pos>0) self.assertTrue(draft.latest_event(TelechatDocEvent, "scheduled_for_telechat")) self.assertEqual(draft.latest_event(TelechatDocEvent, "scheduled_for_telechat").telechat_date, d) self.assertTrue(not draft.latest_event(TelechatDocEvent, "scheduled_for_telechat").returning_item) self.assertEqual(draft.docevent_set.count(), events_before + 1) class TelechatAgendaContentTests(TestCase): def test_telechat_agenda_content_view(self): self.client.login(username="ad", password="ad+password") r = self.client.get(urlreverse("ietf.iesg.views.telechat_agenda_content_view", kwargs={"section": "fake"})) self.assertEqual(r.status_code, 404, "Nonexistent section should 404") for section in TelechatAgendaSectionName.objects.filter(used=True).values_list("slug", flat=True): r = self.client.get( urlreverse("ietf.iesg.views.telechat_agenda_content_view", kwargs={"section": section}) ) self.assertEqual(r.status_code, 404, "Section with no content should 404") for section in TelechatAgendaSectionName.objects.filter(used=True).values_list("slug", flat=True): content = TelechatAgendaContentFactory(section_id=section).text r = self.client.get( urlreverse("ietf.iesg.views.telechat_agenda_content_view", kwargs={"section": section}) ) self.assertContains(r, content, status_code=200) self.assertEqual(r.get("Content-Type", None), "text/plain") def test_telechat_agenda_content_view_permissions(self): for section in TelechatAgendaSectionName.objects.filter(used=True).values_list("slug", flat=True): TelechatAgendaContentFactory(section_id=section) url = urlreverse("ietf.iesg.views.telechat_agenda_content_view", kwargs={"section": section}) self.client.logout() login_testing_unauthorized(self, "plain", url) login_testing_unauthorized(self, "ad", url) self.assertEqual(self.client.get(url).status_code, 200) self.client.login(username="iab chair", password="iab chair+password") self.assertEqual(self.client.get(url).status_code, 200) self.client.login(username="secretary", password="secretary+password") self.assertEqual(self.client.get(url).status_code, 200) def test_telechat_agenda_content_edit(self): for section in TelechatAgendaSectionName.objects.filter(used=True): self.assertFalse(TelechatAgendaContent.objects.filter(section=section).exists()) url = urlreverse("ietf.iesg.views.telechat_agenda_content_edit", kwargs={"section": section.slug}) self.client.logout() login_testing_unauthorized(self, "plain", url, method="get") login_testing_unauthorized(self, "ad", url, method="get") login_testing_unauthorized(self, "iab chair", url, method="get") login_testing_unauthorized(self, "secretary", url, method="get") r = self.client.get(url) self.assertContains(r, str(section), status_code=200) self.client.logout() login_testing_unauthorized(self, "plain", url, method="post") login_testing_unauthorized(self, "ad", url, method="post") login_testing_unauthorized(self, "iab chair", url, method="post") login_testing_unauthorized(self, "secretary", url, method="post") r = self.client.post(url, {"text": "This is some content"}) self.assertRedirects(r, urlreverse("ietf.iesg.views.telechat_agenda_content_manage")) contents = TelechatAgendaContent.objects.filter(section=section) self.assertEqual(contents.count(), 1) self.assertEqual(contents.first().text, "This is some content") self.client.logout() login_testing_unauthorized(self, "plain", url, method="post") login_testing_unauthorized(self, "ad", url, method="post") login_testing_unauthorized(self, "iab chair", url, method="post") login_testing_unauthorized(self, "secretary", url, method="post") r = self.client.post(url, {"text": "This is some different content"}) self.assertRedirects(r, urlreverse("ietf.iesg.views.telechat_agenda_content_manage")) contents = TelechatAgendaContent.objects.filter(section=section) self.assertEqual(contents.count(), 1) self.assertEqual(contents.first().text, "This is some different content") def test_telechat_agenda_content_manage(self): url = urlreverse("ietf.iesg.views.telechat_agenda_content_manage") login_testing_unauthorized(self, "plain", url) login_testing_unauthorized(self, "ad", url) login_testing_unauthorized(self, "iab chair", url) login_testing_unauthorized(self, "secretary", url) self.assertEqual(TelechatAgendaContent.objects.count(), 0) r = self.client.get(url) self.assertEqual(r.status_code, 200) pq = PyQuery(r.content) for section in TelechatAgendaSectionName.objects.filter(used=True): # check that there's a tab even when section is empty nav_button = pq(f"button.nav-link#{section.slug}-tab") self.assertEqual(nav_button.text(), str(section)) edit_url = urlreverse("ietf.iesg.views.telechat_agenda_content_edit", kwargs={"section": section.pk}) edit_button = pq(f'div#{section.slug} a[href="{edit_url}"]') self.assertEqual(len(edit_button), 1) self.assertIn(f"No {section}", pq(f"div#{section.slug}").text()) # and create a section for the next test TelechatAgendaContentFactory(section_id=section.slug) r = self.client.get(url) self.assertEqual(r.status_code, 200) pq = PyQuery(r.content) for section in TelechatAgendaSectionName.objects.filter(used=True): # check that there's a tab with the content nav_button = pq(f"button.nav-link#{section.slug}-tab") self.assertEqual(nav_button.text(), str(section)) edit_url = urlreverse("ietf.iesg.views.telechat_agenda_content_edit", kwargs={"section": section.pk}) edit_button = pq(f'div#{section.slug} a[href="{edit_url}"]') self.assertEqual(len(edit_button), 1) self.assertIn( TelechatAgendaContent.objects.get(section=section).text, pq(f"div#{section.slug}").text() )