Merge pull request from jennifer-richards/django42

chore: Upgrade to Django 4.2
This commit is contained in:
Jennifer Richards 2023-05-23 12:09:12 -03:00 committed by GitHub
commit 22e80ce4db
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
20 changed files with 174 additions and 96 deletions

View file

@ -691,7 +691,7 @@ class CustomApiTests(TestCase):
self.assertEqual(set(missing_fields), set(drop_fields))
def test_api_version(self):
DumpInfo.objects.create(date=timezone.datetime(2022,8,31,7,10,1,tzinfo=timezone.utc), host='testapi.example.com',tz='UTC')
DumpInfo.objects.create(date=timezone.datetime(2022,8,31,7,10,1,tzinfo=datetime.timezone.utc), host='testapi.example.com',tz='UTC')
url = urlreverse('ietf.api.views.version')
r = self.client.get(url)
data = r.json()

View file

@ -573,7 +573,7 @@ class ResurrectTests(DraftFileMixin, TestCase):
r = self.client.get(url)
self.assertEqual(r.status_code, 200)
q = PyQuery(r.content)
self.assertEqual(len(q('form [type=submit]')), 1)
self.assertEqual(len(q('#content form [type=submit]')), 1)
# request resurrect
@ -609,7 +609,7 @@ class ResurrectTests(DraftFileMixin, TestCase):
r = self.client.get(url)
self.assertEqual(r.status_code, 200)
q = PyQuery(r.content)
self.assertEqual(len(q('form [type=submit]')), 1)
self.assertEqual(len(q('#content form [type=submit]')), 1)
# complete resurrect
events_before = draft.docevent_set.count()

View file

@ -118,7 +118,7 @@ class EditAuthorsTests(IetfSeleniumTestCase):
# Must provide a "basis" (change reason)
self.driver.find_element(By.ID, 'id_basis').send_keys('change testing')
# Now click the 'submit' button and check that the update was accepted.
submit_button = self.driver.find_element(By.CSS_SELECTOR, 'button[type="submit"]')
submit_button = self.driver.find_element(By.CSS_SELECTOR, '#content button[type="submit"]')
self.driver.execute_script("arguments[0].click();", submit_button) # FIXME: no idea why this fails:
# self.scroll_to_element(submit_button)
# submit_button.click()
@ -132,4 +132,4 @@ class EditAuthorsTests(IetfSeleniumTestCase):
self.assertEqual(
list(draft.documentauthor_set.values_list('person', flat=True)),
[first_auth.person.pk] + [auth.pk for auth in authors]
)
)

View file

@ -765,7 +765,9 @@ def drafts_in_iesg_process(request):
if s.slug == "lc":
for d in docs:
e = d.latest_event(LastCallDocEvent, type="sent_last_call")
d.lc_expires = e.expires if e else datetime.datetime.min
# If we don't have an event, use an arbitrary date in the past (but not datetime.datetime.min,
# which causes problems with timezone conversions)
d.lc_expires = e.expires if e else datetime.datetime(1950, 1, 1)
docs = list(docs)
docs.sort(key=lambda d: d.lc_expires)

View file

@ -98,7 +98,7 @@ class IetfAuthTests(TestCase):
self.assertEqual(urlsplit(r["Location"])[2], urlreverse(ietf.ietfauth.views.profile))
# try logging out
r = self.client.get(urlreverse('django.contrib.auth.views.logout'))
r = self.client.post(urlreverse('django.contrib.auth.views.logout'), {})
self.assertEqual(r.status_code, 200)
self.assertNotContains(r, "accounts/logout")
@ -215,7 +215,7 @@ class IetfAuthTests(TestCase):
self.assertContains(r, "Allowlist entry creation successful")
# log out
r = self.client.get(urlreverse('django.contrib.auth.views.logout'))
r = self.client.post(urlreverse('django.contrib.auth.views.logout'), {})
self.assertEqual(r.status_code, 200)
# register and verify allowlisted email
@ -664,7 +664,7 @@ class IetfAuthTests(TestCase):
"new_password_confirmation": "foobar",
})
self.assertEqual(r.status_code, 200)
self.assertFormError(r, 'form', 'current_password', 'Invalid password')
self.assertFormError(r.context["form"], 'current_password', 'Invalid password')
# mismatching new passwords
r = self.client.post(chpw_url, {"current_password": "password",
@ -672,7 +672,7 @@ class IetfAuthTests(TestCase):
"new_password_confirmation": "barfoo",
})
self.assertEqual(r.status_code, 200)
self.assertFormError(r, 'form', None, "The password confirmation is different than the new password")
self.assertFormError(r.context["form"], None, "The password confirmation is different than the new password")
# correct password change
r = self.client.post(chpw_url, {"current_password": "password",
@ -711,7 +711,7 @@ class IetfAuthTests(TestCase):
"password": "password",
})
self.assertEqual(r.status_code, 200)
self.assertFormError(r, 'form', 'username',
self.assertFormError(r.context["form"], 'username',
"Select a valid choice. fiddlesticks is not one of the available choices.")
# wrong password
@ -719,7 +719,7 @@ class IetfAuthTests(TestCase):
"password": "foobar",
})
self.assertEqual(r.status_code, 200)
self.assertFormError(r, 'form', 'password', 'Invalid password')
self.assertFormError(r.context["form"], 'password', 'Invalid password')
# correct username change
r = self.client.post(chun_url, {"username": "othername@example.org",

View file

@ -3931,7 +3931,7 @@ class EditTests(TestCase):
'base': meeting.schedule.base_id,
})
self.assertEqual(r.status_code, 200)
self.assertFormError(r, 'form', 'name', 'Enter a valid value.')
self.assertFormError(r.context["form"], 'name', 'Enter a valid value.')
self.assertEqual(meeting.schedule_set.count(), orig_schedule_count, 'Schedule should not be created')
r = self.client.post(url, {
@ -3941,7 +3941,7 @@ class EditTests(TestCase):
'base': meeting.schedule.base_id,
})
self.assertEqual(r.status_code, 200)
self.assertFormError(r, 'form', 'name', 'Enter a valid value.')
self.assertFormError(r.context["form"], 'name', 'Enter a valid value.')
self.assertEqual(meeting.schedule_set.count(), orig_schedule_count, 'Schedule should not be created')
# Non-ASCII alphanumeric characters
@ -3952,7 +3952,7 @@ class EditTests(TestCase):
'base': meeting.schedule.base_id,
})
self.assertEqual(r.status_code, 200)
self.assertFormError(r, 'form', 'name', 'Enter a valid value.')
self.assertFormError(r.context["form"], 'name', 'Enter a valid value.')
self.assertEqual(meeting.schedule_set.count(), orig_schedule_count, 'Schedule should not be created')
def test_edit_session(self):
@ -4037,9 +4037,9 @@ class EditTests(TestCase):
self.assertIn(return_url_unofficial, r.content.decode())
r = self.client.post(url, {})
self.assertFormError(r, 'form', 'confirmed', 'This field is required.')
self.assertFormError(r.context["form"], 'confirmed', 'This field is required.')
r = self.client.post(url_unofficial, {})
self.assertFormError(r, 'form', 'confirmed', 'This field is required.')
self.assertFormError(r.context["form"], 'confirmed', 'This field is required.')
r = self.client.post(url, {'confirmed': 'on'})
self.assertRedirects(r, return_url)
@ -6518,8 +6518,8 @@ class ImportNotesTests(TestCase):
r = self.client.get(url) # try to import the same text
self.assertContains(r, "This document is identical", status_code=200)
q = PyQuery(r.content)
self.assertEqual(len(q('button:disabled[type="submit"]')), 1)
self.assertEqual(len(q('button:enabled[type="submit"]')), 0)
self.assertEqual(len(q('#content button:disabled[type="submit"]')), 1)
self.assertEqual(len(q('#content button:enabled[type="submit"]')), 0)
def test_allows_import_on_existing_bad_unicode(self):
"""Should not be able to import text identical to the current revision"""
@ -6543,8 +6543,8 @@ class ImportNotesTests(TestCase):
r = self.client.get(url) # try to import the same text
self.assertNotContains(r, "This document is identical", status_code=200)
q = PyQuery(r.content)
self.assertEqual(len(q('button:enabled[type="submit"]')), 1)
self.assertEqual(len(q('button:disabled[type="submit"]')), 0)
self.assertEqual(len(q('#content button:enabled[type="submit"]')), 1)
self.assertEqual(len(q('#content button:disabled[type="submit"]')), 0)
def test_handles_missing_previous_revision_file(self):
"""Should still allow import if the file for the previous revision is missing"""
@ -7973,7 +7973,7 @@ class ProceedingsTests(BaseMeetingTestCase):
invalid_file.seek(0) # read the file contents again
r = self.client.post(url, {'file': invalid_file, 'external_url': ''})
self.assertEqual(r.status_code, 200)
self.assertFormError(r, 'form', 'file', 'Found an unexpected extension: .png. Expected one of .pdf')
self.assertFormError(r.context["form"], 'file', 'Found an unexpected extension: .png. Expected one of .pdf')
def test_add_proceedings_material_doc_empty(self):
"""Upload proceedings materials document without specifying a file"""
@ -7986,7 +7986,7 @@ class ProceedingsTests(BaseMeetingTestCase):
)
r = self.client.post(url, {'external_url': ''})
self.assertEqual(r.status_code, 200)
self.assertFormError(r, 'form', 'file', 'This field is required')
self.assertFormError(r.context["form"], 'file', 'This field is required')
def test_add_proceedings_material_url(self):
"""Add a URL as proceedings material"""
@ -8010,7 +8010,7 @@ class ProceedingsTests(BaseMeetingTestCase):
)
r = self.client.post(url, {'use_url': 'on', 'external_url': "Ceci n'est pas une URL"})
self.assertEqual(r.status_code, 200)
self.assertFormError(r, 'form', 'external_url', 'Enter a valid URL.')
self.assertFormError(r.context["form"], 'external_url', 'Enter a valid URL.')
def test_add_proceedings_material_url_empty(self):
"""Add proceedings materials URL without specifying the URL"""
@ -8023,7 +8023,7 @@ class ProceedingsTests(BaseMeetingTestCase):
)
r = self.client.post(url, {'use_url': 'on', 'external_url': ''})
self.assertEqual(r.status_code, 200)
self.assertFormError(r, 'form', 'external_url', 'This field is required')
self.assertFormError(r.context["form"], 'external_url', 'This field is required')
@override_settings(MEETING_DOC_HREFS={'procmaterials': '{doc.name}:{doc.rev}'})
def test_replace_proceedings_material(self):

View file

@ -66,7 +66,7 @@ Tdb0MiLc+r/zvx8oXtgDjDUa
def provide_private_key_to_test_client(testcase):
session = testcase.client.session
session['NOMCOM_PRIVATE_KEY_%s'%testcase.nc.year()] = key
session['NOMCOM_PRIVATE_KEY_%s'%testcase.nc.year()] = key.decode("utf8")
session.save()
def nomcom_kwargs_for_year(year=None, *args, **kwargs):

View file

@ -188,8 +188,9 @@ class NomineePosition(models.Model):
def save(self, **kwargs):
if not self.pk and not self.state_id:
# Don't need to set update_fields because the self.pk test means this is a new instance
self.state = NomineePositionStateName.objects.get(slug='pending')
super(NomineePosition, self).save(**kwargs)
super().save(**kwargs)
def __str__(self):
return "%s - %s - %s" % (self.nominee, self.state, self.position)

View file

@ -55,8 +55,10 @@ def formatted_email(address):
@register.simple_tag
def decrypt(string, request, year, plain=False):
key = retrieve_nomcom_private_key(request, year)
try:
key = retrieve_nomcom_private_key(request, year)
except UnicodeError:
return f"-*- Encrypted text [Error retrieving private key, contact the secretariat ({settings.SECRETARIAT_SUPPORT_EMAIL})]"
if not key:
return '-*- Encrypted text [No private key provided] -*-'

View file

@ -4,6 +4,7 @@
import datetime
import io
import mock
import random
import shutil
@ -1577,6 +1578,16 @@ class NewActiveNomComTests(TestCase):
login_testing_unauthorized(self,self.chair.user.username,url)
response = self.client.get(url)
self.assertEqual(response.status_code,200)
# Check that we get an error if there's an encoding problem talking to openssl
# "\xc3\x28" is an invalid utf8 string
with mock.patch("ietf.nomcom.utils.pipe", return_value=(0, b"\xc3\x28", None)):
response = self.client.post(url, {'key': force_str(key)})
self.assertFormError(
response.context["form"],
None,
"An internal error occurred while adding your private key to your session."
f"Please contact the secretariat for assistance ({settings.SECRETARIAT_SUPPORT_EMAIL})",
)
response = self.client.post(url,{'key': force_str(key)})
self.assertEqual(response.status_code,302)
@ -1993,7 +2004,7 @@ class NoPublicKeyTests(TestCase):
text_bits = [x.xpath('.//text()') for x in q('.alert-warning')]
flat_text_bits = [item for sublist in text_bits for item in sublist]
self.assertTrue(any(['not yet' in y for y in flat_text_bits]))
self.assertEqual(bool(q('form:not(.navbar-form)')),expected_form)
self.assertEqual(bool(q('#content form:not(.navbar-form)')),expected_form)
self.client.logout()
def test_not_yet(self):

View file

@ -172,6 +172,12 @@ def command_line_safe_secret(secret):
return base64.encodebytes(secret).decode('utf-8').rstrip()
def retrieve_nomcom_private_key(request, year):
"""Retrieve decrypted nomcom private key from the session store
Retrieves encrypted, ascii-armored private key from the session store, encodes
as utf8 bytes, then decrypts. Raises UnicodeError if the value in the session
store cannot be encoded as utf8.
"""
private_key = request.session.get('NOMCOM_PRIVATE_KEY_%s' % year, None)
if not private_key:
@ -183,7 +189,8 @@ def retrieve_nomcom_private_key(request, year):
settings.OPENSSL_COMMAND,
command_line_safe_secret(settings.NOMCOM_APP_SECRET)
),
private_key
# The openssl command expects ascii-armored input, so utf8 encoding should be valid
private_key.encode("utf8")
)
if code != 0:
log("openssl error: %s:\n Error %s: %s" %(command, code, error))
@ -191,6 +198,12 @@ def retrieve_nomcom_private_key(request, year):
def store_nomcom_private_key(request, year, private_key):
"""Put encrypted nomcom private key in the session store
Encrypts the private key using openssl, then decodes the ascii-armored output
as utf8 and adds to the session store. Raises UnicodeError if the openssl's
output cannot be decoded as utf8.
"""
if not private_key:
request.session['NOMCOM_PRIVATE_KEY_%s' % year] = ''
else:
@ -205,8 +218,9 @@ def store_nomcom_private_key(request, year, private_key):
if code != 0:
log("openssl error: %s:\n Error %s: %s" %(command, code, error))
if error and error!=b"*** WARNING : deprecated key derivation used.\nUsing -iter or -pbkdf2 would be better.\n":
out = ''
request.session['NOMCOM_PRIVATE_KEY_%s' % year] = out
out = b''
# The openssl command output in 'out' is an ascii-armored value, so should be utf8-decodable
request.session['NOMCOM_PRIVATE_KEY_%s' % year] = out.decode("utf8")
def validate_private_key(key):

View file

@ -158,8 +158,16 @@ def private_key(request, year):
if request.method == 'POST':
form = PrivateKeyForm(data=request.POST)
if form.is_valid():
store_nomcom_private_key(request, year, force_bytes(form.cleaned_data.get('key', '')))
return HttpResponseRedirect(back_url)
try:
store_nomcom_private_key(request, year, force_bytes(form.cleaned_data.get('key', '')))
except UnicodeError:
form.add_error(
None,
"An internal error occurred while adding your private key to your session."
f"Please contact the secretariat for assistance ({settings.SECRETARIAT_SUPPORT_EMAIL})"
)
else:
return HttpResponseRedirect(back_url)
else:
form = PrivateKeyForm()

View file

@ -599,7 +599,9 @@ def suggested_review_requests_for_team(team):
res.sort(key=lambda r: (r.deadline, r.doc_id), reverse=True)
return res
def extract_revision_ordered_review_assignments_for_documents_and_replaced(review_assignment_queryset, names):
def extract_revision_ordered_review_assignments_for_documents_and_replaced(
review_assignment_queryset, names
):
"""Extracts all review assignments for document names (including replaced ancestors), return them neatly sorted."""
names = set(names)
@ -608,8 +610,13 @@ def extract_revision_ordered_review_assignments_for_documents_and_replaced(revie
assignments_for_each_doc = defaultdict(list)
replacement_name_set = set(e for l in replaces.values() for e in l) | names
for r in ( review_assignment_queryset.filter(review_request__doc__name__in=replacement_name_set)
.order_by("-reviewed_rev","-assigned_on", "-id").iterator()):
for r in (
review_assignment_queryset.filter(
review_request__doc__name__in=replacement_name_set
)
.order_by("-reviewed_rev", "-assigned_on", "-id")
.iterator(chunk_size=2000) # chunk_size not tested, using pre-Django 5 default value
):
assignments_for_each_doc[r.review_request.doc.name].append(r)
# now collect in breadth-first order to keep the revision order intact
@ -647,7 +654,10 @@ def extract_revision_ordered_review_assignments_for_documents_and_replaced(revie
return res
def extract_revision_ordered_review_requests_for_documents_and_replaced(review_request_queryset, names):
def extract_revision_ordered_review_requests_for_documents_and_replaced(
review_request_queryset, names
):
"""Extracts all review requests for document names (including replaced ancestors), return them neatly sorted."""
names = set(names)
@ -655,7 +665,13 @@ def extract_revision_ordered_review_requests_for_documents_and_replaced(review_r
replaces = extract_complete_replaces_ancestor_mapping_for_docs(names)
requests_for_each_doc = defaultdict(list)
for r in review_request_queryset.filter(doc__name__in=set(e for l in replaces.values() for e in l) | names).order_by("-time", "-id").iterator():
for r in (
review_request_queryset.filter(
doc__name__in=set(e for l in replaces.values() for e in l) | names
)
.order_by("-time", "-id")
.iterator(chunk_size=2000) # chunk_size not tested, using pre-Django 5 default value
):
requests_for_each_doc[r.doc.name].append(r)
# now collect in breadth-first order to keep the revision order intact

View file

@ -13,7 +13,13 @@ import warnings
from typing import Any, Dict, List, Tuple # pyflakes:ignore
warnings.simplefilter("always", DeprecationWarning)
warnings.filterwarnings("ignore", message="'oidc_provider' defines default_app_config") # hopefully only need until Django 4.1 or 4.2
warnings.filterwarnings("ignore", message="pkg_resources is deprecated as an API")
warnings.filterwarnings("ignore", "Log out via GET requests is deprecated") # happens in oidc_provider
warnings.filterwarnings("ignore", module="tastypie", message="The django.utils.datetime_safe module is deprecated.")
warnings.filterwarnings("ignore", module="oidc_provider", message="The django.utils.timezone.utc alias is deprecated.")
warnings.filterwarnings("ignore", message="The USE_DEPRECATED_PYTZ setting,") # https://github.com/ietf-tools/datatracker/issues/5635
warnings.filterwarnings("ignore", message="The USE_L10N setting is deprecated.") # https://github.com/ietf-tools/datatracker/issues/5648
warnings.filterwarnings("ignore", message="django.contrib.auth.hashers.CryptPasswordHasher is deprecated.") # https://github.com/ietf-tools/datatracker/issues/5663
warnings.filterwarnings("ignore", message="'urllib3\\[secure\\]' extra is deprecated")
warnings.filterwarnings("ignore", message="The logout\\(\\) view is superseded by")
warnings.filterwarnings("ignore", message="Report.file_reporters will no longer be available in Coverage.py 4.2", module="coverage.report")
@ -108,6 +114,11 @@ USE_L10N = False
USE_TZ = True
USE_DEPRECATED_PYTZ = True # supported until Django 5
# The DjangoDivFormRenderer is a transitional class that opts in to defaulting to the div.html
# template for formsets. This will become the default behavior in Django 5.0. This configuration
# can be removed at that point.
# See https://docs.djangoproject.com/en/4.2/releases/4.1/#forms
FORM_RENDERER = "django.forms.renderers.DjangoDivFormRenderer"
# Default primary key field type to use for models that dont have a field with primary_key=True.
# In the future (relative to 4.2), the default will become 'django.db.models.BigAutoField.'
@ -343,11 +354,7 @@ SESSION_COOKIE_SAMESITE = 'None'
SESSION_COOKIE_SECURE = True
SESSION_EXPIRE_AT_BROWSER_CLOSE = False
# We want to use the JSON serialisation, as it's safer -- but there is /secr/
# code which stashes objects in the session that can't be JSON serialized.
# Switch when that code is rewritten.
#SESSION_SERIALIZER = "django.contrib.sessions.serializers.JSONSerializer"
SESSION_SERIALIZER = 'django.contrib.sessions.serializers.PickleSerializer'
SESSION_SERIALIZER = "django.contrib.sessions.serializers.JSONSerializer"
SESSION_ENGINE = "django.contrib.sessions.backends.cache"
SESSION_SAVE_EVERY_REQUEST = True
SESSION_CACHE_ALIAS = 'sessions'
@ -1131,6 +1138,7 @@ ACCOUNT_REQUEST_EMAIL = 'account-request@ietf.org'
SILENCED_SYSTEM_CHECKS = [
"fields.W342", # Setting unique=True on a ForeignKey has the same effect as using a OneToOneField.
"fields.W905", # django.contrib.postgres.fields.CICharField is deprecated. (see https://github.com/ietf-tools/datatracker/issues/5660)
]
CHECKS_LIBRARY_PATCHES_TO_APPLY = [

View file

@ -24,7 +24,8 @@ class AffiliationAlias(models.Model):
def save(self, *args, **kwargs):
self.alias = self.alias.lower()
super(AffiliationAlias, self).save(*args, **kwargs)
update_fields = {"alias"}.union(kwargs.pop("update_fields", set()))
super(AffiliationAlias, self).save(update_fields=update_fields, *args, **kwargs)
class Meta:
verbose_name_plural = "affiliation aliases"

View file

@ -32,11 +32,13 @@
{% else %}
{% if user.is_authenticated %}
<li>
<a class="dropdown-item {% if flavor != 'top' %} text-wrap link-primary{% endif %}"
rel="nofollow"
href="{% url 'django.contrib.auth.views.logout' %}">
Sign out
</a>
<form id="logout-form" method="post" action="{% url 'django.contrib.auth.views.logout' %}">
{% csrf_token %}
<button class="dropdown-item {% if flavor != 'top' %} text-wrap link-primary{% endif %}"
type="submit">
Sign out
</button>
</form>
</li>
<li>
<a class="dropdown-item {% if flavor != 'top' %} text-wrap link-primary{% endif %}"

View file

@ -81,7 +81,7 @@ class IetfSeleniumTestCase(IetfLiveServerTestCase):
self.driver.get(url)
self.driver.find_element(By.NAME, 'username').send_keys(username)
self.driver.find_element(By.NAME, 'password').send_keys(password)
self.driver.find_element(By.XPATH, '//button[@type="submit"]').click()
self.driver.find_element(By.XPATH, '//*[@id="content"]//button[@type="submit"]').click()
def scroll_to_element(self, element):
"""Scroll an element into view"""
@ -108,4 +108,4 @@ class presence_of_element_child_by_css_selector:
def __call__(self, driver):
child = self.element.find_element(By.CSS_SELECTOR, self.child_selector)
return child if child is not None else False
return child if child is not None else False

View file

@ -742,6 +742,11 @@ class IetfTestRunner(DiscoverRunner):
"as the collection of test coverage data isn't currently threadsafe.")
sys.exit(1)
self.check_coverage = False
from ietf.doc.tests import TemplateTagTest # import here to prevent circular imports
# Ensure that the coverage tests come last. Specifically list TemplateTagTest before CoverageTest. If this list
# contains parent classes to later subclasses, the parent classes will determine the ordering, so use the most
# specific classes necessary to get the right ordering:
self.reorder_by = (PyFlakesTestCase, MyPyTest,) + self.reorder_by + (StaticLiveServerTestCase, TemplateTagTest, CoverageTest,)
def setup_test_environment(self, **kwargs):
global template_coverage_collection
@ -1061,20 +1066,57 @@ class IetfTestRunner(DiscoverRunner):
test_paths = [ os.path.join(*app.split('.')) for app in test_apps ]
return test_apps, test_paths
# Django 5 will drop the extra_tests mechanism for the test runner. Work around
# by adding a special label to the test suite, then injecting our extra tests
# in load_tests_for_label()
def build_suite(self, test_labels=None, extra_tests=None, **kwargs):
if test_labels is None:
# Base class sets test_labels to ["."] if it was None. The label we're
# adding will interfere with that, so replicate that behavior here.
test_labels = ["."]
test_labels = ("_ietf_extra_tests",) + tuple(test_labels)
return super().build_suite(test_labels, extra_tests, **kwargs)
def load_tests_for_label(self, label, discover_kwargs):
if label == "_ietf_extra_tests":
return self._extra_tests() or None
return super().load_tests_for_label(label, discover_kwargs)
def _extra_tests(self):
"""Get extra tests that should be added to the test suite"""
tests = []
if validation_settings["validate_html"]:
tests += [
TemplateValidationTests(
test_runner=self,
validate_html=self,
methodName='run_template_validation',
),
]
if self.check_coverage:
global template_coverage_collection, code_coverage_collection, url_coverage_collection
template_coverage_collection = True
code_coverage_collection = True
url_coverage_collection = True
tests += [
PyFlakesTestCase(test_runner=self, methodName='pyflakes_test'),
MyPyTest(test_runner=self, methodName='mypy_test'),
#CoverageTest(test_runner=self, methodName='interleaved_migrations_test'),
CoverageTest(test_runner=self, methodName='url_coverage_test'),
CoverageTest(test_runner=self, methodName='template_coverage_test'),
CoverageTest(test_runner=self, methodName='code_coverage_test'),
]
return tests
def run_tests(self, test_labels, extra_tests=None, **kwargs):
global old_destroy, old_create, test_database_name, template_coverage_collection, code_coverage_collection, url_coverage_collection
from django.db import connection
from ietf.doc.tests import TemplateTagTest
if extra_tests is None:
extra_tests=[]
# Tests that involve switching back and forth between the real
# database and the test database are way too dangerous to run
# against the production database
if socket.gethostname().split('.')[0] in ['core3', 'ietfa', 'ietfb', 'ietfc', ]:
raise EnvironmentError("Refusing to run tests on production server")
from django.db import connection
global old_destroy, old_create
old_create = connection.creation.__class__.create_test_db
connection.creation.__class__.create_test_db = safe_create_test_db
old_destroy = connection.creation.__class__.destroy_test_db
@ -1087,35 +1129,6 @@ class IetfTestRunner(DiscoverRunner):
self.test_apps, self.test_paths = self.get_test_paths(test_labels)
if validation_settings["validate_html"]:
extra_tests += [
TemplateValidationTests(
test_runner=self,
validate_html=self,
methodName='run_template_validation',
),
]
if self.check_coverage:
template_coverage_collection = True
code_coverage_collection = True
url_coverage_collection = True
extra_tests += [
PyFlakesTestCase(test_runner=self, methodName='pyflakes_test'),
MyPyTest(test_runner=self, methodName='mypy_test'),
#CoverageTest(test_runner=self, methodName='interleaved_migrations_test'),
CoverageTest(test_runner=self, methodName='url_coverage_test'),
CoverageTest(test_runner=self, methodName='template_coverage_test'),
CoverageTest(test_runner=self, methodName='code_coverage_test'),
]
# ensure that the coverage tests come last. Specifically list
# TemplateTagTest before CoverageTest. If this list contains
# parent classes to later subclasses, the parent classes will
# determine the ordering, so use the most specific classes
# necessary to get the right ordering:
self.reorder_by = (PyFlakesTestCase, MyPyTest, ) + self.reorder_by + (StaticLiveServerTestCase, TemplateTagTest, CoverageTest, )
failures = super(IetfTestRunner, self).run_tests(test_labels, extra_tests=extra_tests, **kwargs)
if self.check_coverage:
@ -1139,10 +1152,10 @@ class IetfTestRunner(DiscoverRunner):
if self.run_full_test_suite:
print((" %8s coverage: %6.2f%% (%s: %6.2f%%)" %
(test.capitalize(), test_coverage*100, latest_coverage_version, master_coverage*100, )))
(test.capitalize(), test_coverage*100, latest_coverage_version, master_coverage*100, )))
else:
print((" %8s coverage: %6.2f%%" %
(test.capitalize(), test_coverage*100, )))
(test.capitalize(), test_coverage*100, )))
print(("""
Per-file code and template coverage and per-url-pattern url coverage data

View file

@ -11,7 +11,7 @@
--- django/http/response.py.orig 2020-08-13 11:16:04.060627793 +0200
+++ django/http/response.py 2020-08-13 11:54:03.482476973 +0200
@@ -279,20 +279,28 @@
@@ -282,20 +282,28 @@
value = signing.get_cookie_signer(salt=key + salt).sign(value)
return self.set_cookie(key, value, **kwargs)

View file

@ -1,5 +1,5 @@
# -*- conf-mode -*-
setuptools>=51.1.0,<67.5.0 # Require this first, to prevent later errors
setuptools>=51.1.0 # Require this first, to prevent later errors
#
argon2-cffi>=21.3.0 # For the Argon2 password hasher option
beautifulsoup4>=4.11.1 # Only used in tests
@ -11,7 +11,7 @@ coverage>=4.5.4,<5.0 # Coverage 5.x moves from a json database to SQLite. Mo
decorator>=5.1.1
types-decorator>=5.1.1
defusedxml>=0.7.1 # for TastyPie when using xml; not a declared dependency
Django<4.2
Django>4.2,<5
django-analytical>=3.1.0
django-bootstrap5>=21.3
django-celery-beat>=2.3.0
@ -45,7 +45,7 @@ markdown>=3.3.6
types-markdown>=3.3.6
mock>=4.0.3 # Used only by tests, of course
types-mock>=4.0.3
mypy<1.3 # Version requirements determined by django-stubs.
mypy~=1.2.0 # Version requirements determined by django-stubs.
oic>=1.3 # Used only by tests
Pillow>=9.1.0
psycopg2>=2.9.6