Coverage for jutil/tests.py : 97%

Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1import logging
2import os
3from datetime import datetime, timedelta, date
4from decimal import Decimal
5from os.path import join
6from urllib.parse import urlparse
7from django.utils import timezone
8from typing import List
9from django.utils.timezone import now
10from rest_framework.test import APIClient
12from jutil.drf_exceptions import transform_exception_to_drf
13from jutil.modelfields import SafeCharField, SafeTextField
14from jutil.middleware import logger as jutil_middleware_logger, ActivateUserProfileTimezoneMiddleware
15import pytz
16from django.conf import settings
17from django.contrib.admin.models import LogEntry
18from django.contrib.auth.models import User
19from django.core.exceptions import ValidationError
20from rest_framework.exceptions import ValidationError as DRFValidationError, ErrorDetail
21from django.core.management.base import CommandParser # type: ignore
22from django.db import models
23from django.http.response import HttpResponse
24from django.test import TestCase
25from django.test.client import RequestFactory, Client
26from django.utils.translation import override, gettext as _, gettext_lazy
27from rest_framework.exceptions import NotAuthenticated
28from jutil.admin import admin_log, admin_obj_url, admin_obj_link, ModelAdminBase, AdminLogEntryMixin
29from jutil.auth import AuthUserMixin, get_auth_user, get_auth_user_or_none
30from jutil.command import get_date_range_by_name, add_date_range_arguments, parse_date_range_arguments
31from jutil.email import make_email_recipient_list
32from jutil.middleware import EnsureOriginMiddleware, LogExceptionMiddleware, EnsureLanguageCookieMiddleware
33from jutil.model import is_model_field_changed, clone_model, get_model_field_label_and_value, get_object_or_none, \
34 wait_object_or_none
35from jutil.request import get_ip_info
36from jutil.responses import CsvResponse
37from jutil.testing import TestSetupMixin
38from jutil.urls import modify_url
39from jutil.xml import xml_to_dict, dict_to_element, _xml_filter_tag_name
40from jutil.dates import add_month, per_delta, per_month, this_week, next_month, next_week, this_month, last_month, \
41 last_year, last_week, yesterday, end_of_month, this_year, get_time_steps, TIME_STEP_DAILY
42from jutil.format import format_full_name, format_xml, format_xml_bytes, format_timedelta, dec1, dec2, dec3, dec4, dec5, \
43 dec6, format_table, ucfirst_lazy, strip_media_root, get_media_full_path, camel_case_to_underscore, \
44 underscore_to_camel_case, format_as_html_json, format_dict_as_html, choices_label, is_media_full_path
45from jutil.parse import parse_datetime, parse_bool, parse_datetime_or_none
46from jutil.validators import fi_payment_reference_number, se_ssn_validator, se_ssn_filter, fi_iban_validator, \
47 se_iban_validator, iban_filter_readable, email_filter, iban_validator, iban_bank_info, fi_company_org_id_validator, \
48 email_validator, fi_payment_reference_validator, iso_payment_reference_validator, fi_ssn_age, \
49 se_clearing_code_bank_info, ascii_filter, ee_iban_validator, be_iban_validator, dk_iban_validator, \
50 dk_iban_bank_info, dk_clearing_code_bank_name, country_code_sanitizer, phone_sanitizer, email_sanitizer, \
51 fi_company_org_id_generator, phone_validator, passport_filter, passport_validator, passport_sanitizer, \
52 country_code_validator, validate_country_iban, iban_bic, validate_country_company_org_id, fi_ssn_generator, \
53 fi_ssn_validator, bic_validator, iban_generator, bic_sanitizer, filter_country_company_org_id
54from xml.etree.ElementTree import Element
55from xml.etree import ElementTree as ET
56from django.contrib import admin
59MY_CHOICE_1 = '1'
60MY_CHOICE_2 = '2'
61MY_CHOICES = (
62 (MY_CHOICE_1, 'MY_CHOICE_1'),
63 (MY_CHOICE_2, 'MY_CHOICE_2'),
64)
66request_factory = RequestFactory()
69class DummyLogHandler(logging.Handler):
70 msgs: List[str]
72 def __init__(self):
73 super().__init__()
74 self.msgs = []
76 def emit(self, record):
77 msg = self.format(record)
78 self.msgs.append(msg)
81def dummy_time_zone_response(obj) -> HttpResponse:
82 tz = timezone.get_current_timezone()
83 return HttpResponse(str(tz).encode())
86def dummy_admin_func_a(modeladmin, request, qs):
87 print('dummy_admin_func_a')
90def dummy_admin_func_b(modeladmin, request, qs):
91 print('dummy_admin_func_b')
94def dummy_middleware_get_response(obj) -> HttpResponse:
95 return HttpResponse(b'hello content')
98class DummyUserProfile:
99 timezone = 'Europe/Helsinki'
102class MyCustomAdmin(ModelAdminBase):
103 max_history_length = 5
104 actions = (
105 dummy_admin_func_b,
106 dummy_admin_func_a,
107 )
109 def get_object(self, request, obj_id):
110 return self.model.objects.get(id=obj_id)
113class Tests(TestCase, TestSetupMixin):
114 def setUp(self):
115 self.user = user = self.add_test_user('test@example.com', 'test1234')
116 assert isinstance(user, User)
117 user.is_superuser = True
118 user.is_staff = True
119 user.save()
120 self.client = Client()
122 def test_api_client(self):
123 api_client = self.create_api_client()
124 self.assertTrue(isinstance(api_client, APIClient))
126 def test_payment_reference(self):
127 self.assertEqual(fi_payment_reference_number('100'), '1009')
128 invalids = [
129 '89182932u',
130 '0000002',
131 ]
132 for invalid in invalids:
133 try:
134 fi_payment_reference_number(invalid)
135 self.fail('invalid number but passed: {}'.format(invalid))
136 except ValidationError:
137 pass
139 def test_format_full_name(self):
140 samples = [
141 ('Short', 'Full Name', 'Short Full Name'),
142 ('Short Middle Name Is Quite Long', 'Full Name', 'Short Full Name'),
143 ('Short-Middle Name Is Quite Long', 'Full Name', 'Short Full Name'),
144 ('Olga Demi', 'Serpuhovitinova-Miettinen', 'Olga Serpuhovitinova'),
145 ('Olga-Anne Demi', 'Serpuhovitinovatsko', 'Olga S'),
146 ]
147 for v in samples:
148 limited = format_full_name(v[0], v[1], 20)
149 # print('{} {} -> {} (was: {})'.format(v[0], v[1], v[2], limited))
150 self.assertEqual(v[2], limited)
151 try:
152 long_name = '19280309812083091829038190823081208301280381092830182038018203810283021'
153 format_full_name(long_name, long_name)
154 self.fail('format_full_name failed with long name')
155 except Exception:
156 pass
158 def test_parse_datetime(self):
159 pairs = [
160 ('2016-06-12', '2016-06-12T00:00:00+00:00'),
161 ('2016-06-12T01:00:00', '2016-06-12T01:00:00+00:00'),
162 ('2016-06-12 01:00:00', '2016-06-12T01:00:00+00:00'),
163 ('2016-06-12T01:00:00+00:00', '2016-06-12T01:00:00+00:00'),
164 ('2016-06-12 01:00:00+00:00', '2016-06-12T01:00:00+00:00'),
165 ]
166 for t_str_input, t_ref_str in pairs:
167 # parse_datetime
168 t = parse_datetime(t_str_input)
169 self.assertTrue(isinstance(t, datetime))
170 assert isinstance(t, datetime)
171 self.assertEqual(t.isoformat(), t_ref_str)
172 # parse_datetime_or_none
173 t = parse_datetime_or_none(t_str_input)
174 self.assertTrue(isinstance(t, datetime))
175 assert isinstance(t, datetime)
176 self.assertEqual(t.isoformat(), t_ref_str)
178 invalids = [
179 '12312313ew',
180 '2016-13-05',
181 ]
182 for t_str_input in invalids:
183 # parse_datetime
184 try:
185 parse_datetime(t_str_input)
186 self.fail('{} is not valid input for parse_datetime()'.format(t_str_input))
187 except Exception:
188 pass
189 # parse_datetime_or_none
190 t = parse_datetime_or_none(t_str_input)
191 assert t is None
193 def test_add_month(self):
194 t = parse_datetime('2016-06-12T01:00:00')
195 self.assertTrue(isinstance(t, datetime))
196 assert isinstance(t, datetime)
197 self.assertEqual(t.isoformat(), '2016-06-12T01:00:00+00:00')
198 t2 = add_month(t)
199 self.assertEqual(t2.isoformat(), '2016-07-12T01:00:00+00:00')
200 t3 = add_month(t, 7)
201 self.assertEqual(t3.isoformat(), '2017-01-12T01:00:00+00:00')
202 t4 = add_month(t, -1)
203 self.assertEqual(t4.isoformat(), '2016-05-12T01:00:00+00:00')
204 time_now = datetime(2020, 6, 30, 15, 47, 23, 818646)
205 self.assertEqual(add_month(time_now, -4).isoformat(), '2020-02-29T15:47:23.818646')
206 self.assertEqual(add_month(time_now, 8).isoformat(), '2021-02-28T15:47:23.818646')
207 self.assertEqual(add_month(time_now, 0).isoformat(), '2020-06-30T15:47:23.818646')
209 def test_se_ssn(self):
210 se_ssn_validator('811228-9874')
211 se_ssn_validator('670919-9530')
212 with self.assertRaises(ValidationError):
213 se_ssn_validator('811228-9873')
215 def test_phone_numbers(self):
216 try:
217 phone_validator('+358456343767')
218 except Exception:
219 self.fail('phone_validator("+358456343767") should not raise Exception')
220 with self.assertRaisesMessage(ValidationError, _('Invalid phone number')):
221 phone_validator('214')
222 self.assertEqual(phone_sanitizer('214'), '')
224 def test_passport(self):
225 self.assertEqual(passport_filter('?ADsd-12312dsds'), 'ADSD-12312DSDS')
226 passport_validator('21412312312')
227 with self.assertRaisesMessage(ValidationError, _('Invalid passport number')):
228 passport_validator('214')
229 self.assertEqual(passport_sanitizer('214'), '')
230 self.assertEqual(passport_sanitizer('21412312312'), '21412312312')
232 def test_country_code(self):
233 for cc in ['FI', 'DK', 'ES', 'SE', 'VN']:
234 country_code_validator(cc)
235 with self.assertRaisesMessage(ValidationError, _('Invalid country code')):
236 country_code_validator('Finland')
238 def test_bic(self):
239 bic = iban_bic('FI21 1234 5600 0007 85')
240 self.assertEqual(bic, 'NDEAFIHH')
241 self.assertEqual(bic_sanitizer('NDEAFIHH'), 'NDEAFIHH')
242 self.assertEqual(bic_sanitizer('NDEAFIH'), '')
244 def test_org_id(self):
245 try:
246 validate_country_company_org_id('FI', '2084069-9')
247 except Exception:
248 self.fail('2084069-9 is valid org id')
249 with self.assertRaisesMessage(ValidationError, _('Invalid company organization ID')):
250 validate_country_company_org_id('FI', '2084069-8')
252 def test_validate_country_iban(self):
253 with self.assertRaisesMessage(ValidationError, _('Invalid IBAN account number')):
254 validate_country_iban('FI15616515616156', 'SE')
256 def test_fi_ssn_generator(self):
257 self.assertEqual(len(fi_ssn_generator()), 6 + 1 + 4)
258 for min_year, max_year in [(1800, 1900), (1900, 2000), (2000, 2050)]:
259 for n in range(10):
260 ssn = fi_ssn_generator(min_year, max_year)
261 try:
262 fi_ssn_age(ssn)
263 fi_ssn_validator(ssn)
264 except Exception:
265 self.fail('{} is valid SSN'.format(ssn))
266 fi_ssn_validator('110305+283X')
267 for ssn in ['9999-123F', '271138-670X', '090228+256X']:
268 with self.assertRaisesMessage(ValidationError, _('Invalid personal identification number')):
269 fi_ssn_validator(ssn)
270 with self.assertRaises(ValidationError):
271 fi_ssn_generator(1700, 1799)
272 with self.assertRaises(ValidationError):
273 fi_ssn_generator(2100, 2200)
275 def test_iban(self):
276 with self.assertRaisesMessage(ValidationError, _('Invalid IBAN account number')):
277 iban_validator('')
278 with self.assertRaisesMessage(ValidationError, _('Invalid country code')):
279 iban_validator('XX')
280 with self.assertRaisesMessage(ValidationError, _('Invalid IBAN account number')):
281 iban_validator('FI2112345600000786')
282 bic_validator('HELSFIHH')
283 bic_validator('HELSFIHHXXX')
284 with self.assertRaisesMessage(ValidationError, _('Invalid bank BIC/SWIFT code')):
285 bic_validator('HELSFIH')
286 with self.assertRaisesMessage(ValidationError, _('Invalid bank BIC/SWIFT code')):
287 bic_validator('')
288 with self.assertRaisesMessage(ValidationError, _('Invalid bank BIC/SWIFT code')):
289 bic_validator('XX123123123112')
290 iban_validator('FI2112345600000785')
291 iban_validator('SE4550000000058398257466')
292 fi_iban_validator('FI2112345600000785')
293 se_iban_validator('SE4550000000058398257466')
294 ee_iban_validator('EE38 2200 2210 2014 5685')
295 be_iban_validator('BE68 5390 0754 7034')
296 with self.assertRaises(ValidationError):
297 fi_iban_validator('FI2112345600000784')
298 with self.assertRaises(ValidationError):
299 se_iban_validator('SE4550000000058398257465')
300 iban = 'FI8847304720017517'
301 self.assertEqual(iban_filter_readable(iban), 'FI88 4730 4720 0175 17')
302 self.assertEqual(iban_filter_readable(''), '')
304 def test_urls(self):
305 url = 'http://yle.fi/uutiset/3-8045550?a=123&b=456'
306 self.assertEqual(modify_url('http://yle.fi/uutiset/3-8045550?a=123&b=456', {'a': '123', 'b': '456'}),
307 'http://yle.fi/uutiset/3-8045550?a=123&b=456')
309 def test_email_filter_and_validation(self):
310 emails = [
311 (' Asdsa@a-a.com ', 'asdsa@a-a.com', True),
312 ('1asdsa@a-a2.com', '1asdsa@a-a2.com', True),
313 (' Asdsa@a-a ', 'asdsa@a-a', False),
314 (' @a-a2.com', '@a-a2.com', False),
315 (' a-a2.com', 'a-a2.com', False),
316 ('ää-a2@ää-a2.com', 'ää-a2@ää-a2.com', False),
317 ('aaa.bbbbb@ccc-ddd.fi', 'aaa.bbbbb@ccc-ddd.fi', True),
318 ]
319 for i, o, is_valid in emails:
320 # print('email_filter({}) -> {}'.format(i, email_filter(i)))
321 self.assertEqual(email_filter(i), o)
322 if is_valid:
323 email_validator(o)
324 else:
325 fail = False
326 try:
327 email_validator(o)
328 except ValidationError:
329 fail = True
330 self.assertTrue(fail, '{} is not valid email but passed validation'.format(o))
332 def test_ip_info(self):
333 ip, cc, host = get_ip_info('213.214.146.142')
334 self.assertEqual(ip, '213.214.146.142')
335 if cc: 335 ↛ 337line 335 didn't jump to line 337, because the condition on line 335 was never false
336 self.assertEqual(cc, 'FI')
337 if host: 337 ↛ exitline 337 didn't return from function 'test_ip_info', because the condition on line 337 was never false
338 self.assertEqual(host, '213214146142.edelkey.net')
340 def test_parse_xml(self):
341 # finvoice_201_example1.xml
342 xml_bytes = open(join(settings.BASE_DIR, 'data/fi/finvoice_201_example1.xml'), 'rb').read()
343 data = xml_to_dict(xml_bytes, value_key='value', attribute_prefix='_')
344 # pprint(data)
345 self.assertEqual(data['_Version'], '2.01')
346 self.assertEqual(data['InvoiceRow'][0]['ArticleIdentifier'], '12345')
347 self.assertEqual(data['InvoiceRow'][0]['DeliveredQuantity']['value'], '2')
348 self.assertEqual(data['InvoiceRow'][0]['DeliveredQuantity']['_QuantityUnitCode'], 'kpl')
349 self.assertEqual(data['InvoiceRow'][1]['ArticleIdentifier'], '123456')
351 # parse_xml1.xml
352 xml_str = open(join(settings.BASE_DIR, 'data/parse_xml1.xml'), 'rt').read()
353 data = xml_to_dict(xml_str.encode())
354 # pprint(data)
355 ref_data = {'@version': '1.2',
356 'A': [{'@class': 'x', 'B': {'@': 'hello', '@class': 'x2'}},
357 {'@class': 'y', 'B': {'@': 'world', '@class': 'y2'}}],
358 'C': 'value node'}
359 self.assertEqual(ref_data, data)
361 # parse_xml1.xml / no attributes
362 xml_str = open(join(settings.BASE_DIR, 'data/parse_xml1.xml'), 'rt').read()
363 data = xml_to_dict(xml_str.encode(), parse_attributes=False)
364 # pprint(data)
365 ref_data = {'A': [{'B': 'hello'}, {'B': 'world'}], 'C': 'value node'}
366 self.assertEqual(ref_data, data)
368 # parse_xml2.xml / no attributes
369 xml_str = open(join(settings.BASE_DIR, 'data/parse_xml2.xml'), 'rt').read()
370 data = xml_to_dict(xml_str.encode(), ['VastausLoki', 'LuottoTietoMerkinnat'], parse_attributes=False)
371 # pprint(data)
372 ref_data = {'VastausLoki': {'KysyttyHenkiloTunnus': '020685-1234',
373 'PaluuKoodi': 'Palveluvastaus onnistui',
374 'SyyKoodi': '1'}}
375 self.assertEqual(ref_data, data)
377 def test_dict_to_xml(self):
378 data = {
379 'Doc': {
380 '@version': '1.2',
381 'A': [{'@class': 'x', 'B': {'@': 'hello', '@class': 'x2'}},
382 {'@class': 'y', 'B': {'@': 'world', '@class': 'y2'}}],
383 'C': 'value node',
384 'D': 123,
385 'E': ['abc'],
386 }
387 }
388 el = dict_to_element(data)
389 assert isinstance(el, Element)
390 xml_str = ET.tostring(el, encoding='utf8', method='xml').decode()
391 # print(xml_str) # <Doc version="1.2"><C>value node</C><A class="x"><B class="x2">hello</B></A><A class="y"><B class="y2">world</B></A></Doc>
392 data2 = xml_to_dict(xml_str.encode(), document_tag=True, array_tags=['E'], int_tags=['D'])
393 # print('')
394 # pprint(data)
395 # pprint(data2)
396 self.assertEqual(data2, data)
398 def test_dict_to_xml2(self):
399 self.assertEqual(_xml_filter_tag_name('TagName[0]'), 'TagName')
400 self.assertEqual(_xml_filter_tag_name('TagName[1]'), 'TagName')
401 self.assertEqual(_xml_filter_tag_name('TagName'), 'TagName')
402 data = {
403 'Doc': {
404 '@version': '1.2',
405 'A': [{'@class': 'x', 'B': {'@': 'hello', '@class': 'x2'}},
406 {'@class': 'y', 'B': {'@': 'world', '@class': 'y2'}}],
407 'C': 'value node',
408 'D': 123,
409 'E': ['abc'],
410 'F': ['line 1', 'line 2']
411 }
412 }
413 el = dict_to_element(data)
414 assert isinstance(el, Element)
415 xml_str = ET.tostring(el, encoding='utf8', method='xml').decode()
416 data2 = xml_to_dict(xml_str.encode(), document_tag=True, array_tags=['E', 'F'], int_tags=['D'])
417 self.assertEqual(data2, data)
419 def test_xml_to_dict(self):
420 xml_str = """<?xml version="1.0" encoding="utf-8"?>
421<Document>
422 <TxsSummry>
423 <TtlNtries>
424 <NbOfNtries>12</NbOfNtries>
425 </TtlNtries>
426 <TtlCdtNtries>
427 <NbOfNtries>34</NbOfNtries>
428 <Sum>1234.56</Sum>
429 </TtlCdtNtries>
430 <TtlDbtNtries>
431 <NbOfNtries>0</NbOfNtries>
432 <Sum>0</Sum>
433 </TtlDbtNtries>
434 </TxsSummry>
435</Document>"""
436 data = xml_to_dict(xml_str.encode(), document_tag=True, array_tags=[], int_tags=['NbOfNtries'])
437 # print('')
438 # pprint(data)
439 self.assertEqual(data['Document']['TxsSummry']['TtlNtries']['NbOfNtries'], 12)
440 self.assertEqual(data['Document']['TxsSummry']['TtlCdtNtries']['NbOfNtries'], 34)
442 def test_per_delta(self):
443 begin = datetime(2017, 9, 17, 11, 42)
444 end = begin + timedelta(days=4)
445 ref = [(datetime(2017, 9, 17, 11, 42), datetime(2017, 9, 18, 11, 42)),
446 (datetime(2017, 9, 18, 11, 42), datetime(2017, 9, 19, 11, 42)),
447 (datetime(2017, 9, 19, 11, 42), datetime(2017, 9, 20, 11, 42)),
448 (datetime(2017, 9, 20, 11, 42), datetime(2017, 9, 21, 11, 42))]
449 res = per_delta(begin, end, timedelta(days=1))
450 self.assertEqual(list(res), ref)
452 def test_per_month(self):
453 begin = datetime(2017, 9, 1, 0, 0)
454 res = list(per_month(begin, begin + timedelta(days=32)))
455 ref = [(datetime(2017, 9, 1, 0, 0), datetime(2017, 10, 1, 0, 0)),
456 (datetime(2017, 10, 1, 0, 0), datetime(2017, 11, 1, 0, 0))]
457 self.assertEqual(list(res), ref)
459 def test_dates(self):
460 t = datetime(2018, 1, 30)
461 b, e = this_week(t)
462 self.assertEqual(b, pytz.utc.localize(datetime(2018, 1, 29)))
463 self.assertEqual(e, pytz.utc.localize(datetime(2018, 2, 5)))
464 b, e = this_month(t)
465 self.assertEqual(b, pytz.utc.localize(datetime(2018, 1, 1)))
466 self.assertEqual(e, pytz.utc.localize(datetime(2018, 2, 1)))
467 b, e = next_week(t)
468 self.assertEqual(b, pytz.utc.localize(datetime(2018, 2, 5)))
469 self.assertEqual(e, pytz.utc.localize(datetime(2018, 2, 12)))
471 def test_named_date_ranges(self):
472 t = datetime(2018, 5, 31)
473 t_tz = pytz.utc.localize(t)
474 named_ranges = [
475 ('last_year', last_year(t)),
476 ('last_month', last_month(t)),
477 ('last_week', last_week(t)),
478 ('this_year', this_year(t)),
479 ('this_month', this_month(t)),
480 ('this_week', this_week(t)),
481 ('yesterday', yesterday(t)),
482 ('today', yesterday(t + timedelta(hours=24))),
483 ]
484 day_ranges = [7, 15, 30, 60, 90]
485 for days in day_ranges:
486 named_ranges.append(
487 ('plus_minus_{}d'.format(days), (t_tz - timedelta(days=days), t_tz + timedelta(days=days))))
488 named_ranges.append(('prev_{}d'.format(days), (t_tz - timedelta(days=days), t_tz)))
489 named_ranges.append(('next_{}d'.format(days), (t_tz, t_tz + timedelta(days=days))))
490 for name, res in named_ranges:
491 # print('testing', name)
492 self.assertEqual(get_date_range_by_name(name, t), res)
494 def test_time_steps(self):
495 t = parse_datetime('2020-08-24 23:28:36.503174')
496 this_week_daily = [
497 '2020-08-24T00:00:00+00:00 2020-08-25T00:00:00+00:00',
498 '2020-08-25T00:00:00+00:00 2020-08-26T00:00:00+00:00',
499 '2020-08-26T00:00:00+00:00 2020-08-27T00:00:00+00:00',
500 '2020-08-27T00:00:00+00:00 2020-08-28T00:00:00+00:00',
501 '2020-08-28T00:00:00+00:00 2020-08-29T00:00:00+00:00',
502 '2020-08-29T00:00:00+00:00 2020-08-30T00:00:00+00:00',
503 '2020-08-30T00:00:00+00:00 2020-08-31T00:00:00+00:00',
504 ]
505 ix = 0
506 for begin, end in get_time_steps(TIME_STEP_DAILY, *this_week(t)):
507 self.assertEqual('{} {}'.format(begin.isoformat(), end.isoformat()), this_week_daily[ix])
508 ix += 1
510 def test_bank_info(self):
511 ac = 'FI8847304720017517'
512 inf = iban_bank_info(ac)
513 self.assertEqual(inf[0], 'POPFFI22')
514 self.assertEqual(inf[1], 'POP-Pankki')
516 ac = ''
517 inf = iban_bank_info(ac)
518 self.assertEqual(inf[0], '')
519 self.assertEqual(inf[1], '')
521 ac = 'BE75270187592710'
522 inf = iban_bank_info(ac)
523 self.assertEqual(inf[0], 'GEBABEBB')
524 self.assertEqual(inf[1], 'BNP Paribas Fortis')
525 ac = 'BE58465045170210'
526 inf = iban_bank_info(ac)
527 self.assertEqual(inf[0], 'KREDBEBB')
528 self.assertEqual(inf[1], 'KBC Bank')
529 ac = 'BE11000123456748'
530 inf = iban_bank_info(ac)
531 self.assertEqual(inf[0], 'BPOTBEB1')
532 self.assertEqual(inf[1], 'bpost bank')
534 def test_org_id_fi(self):
535 valids = [
536 'FI01098230',
537 'FI-01098230',
538 '0109823-0',
539 '2084069-9',
540 ]
541 invalids = [
542 '2084069-1',
543 'SE2084069-1',
544 ]
545 co_filtered = [
546 ('FI', 'FI01098230', '0109823-0'),
547 ('FI', 'FI-01098230', '0109823-0'),
548 ('FI', '0109823-0', '0109823-0'),
549 ('FI', '2084069-9', '2084069-9'),
550 ('SE', '01098230', '01098230'),
551 ('SE', '20840699', '20840699'),
552 ]
553 for cc, org, val in co_filtered:
554 self.assertEqual(filter_country_company_org_id(cc, org), val)
555 validate_country_company_org_id(cc, org)
556 for valid in valids:
557 fi_company_org_id_validator(valid)
558 for invalid in invalids:
559 try:
560 fi_company_org_id_validator(invalid)
561 self.fail('{} passed as valid FI-org'.format(invalid))
562 except ValidationError:
563 # print('ok')
564 pass
565 for n in range(10):
566 v0 = fi_company_org_id_generator()
567 # print(v0)
568 fi_company_org_id_validator(v0)
570 def test_reference_number_validators(self):
571 valid_fi_refs = [
572 '302300',
573 '202196',
574 '302290',
575 ]
576 for ref_no in valid_fi_refs:
577 fi_payment_reference_validator(ref_no)
579 invalid_fi_refs = [
580 '302301',
581 '202195',
582 '302291',
583 ]
584 for ref_no in invalid_fi_refs:
585 try:
586 fi_payment_reference_validator(ref_no)
587 self.assertFalse(True, '{} should have failed validation'.format(ref_no))
588 except ValidationError:
589 pass
591 valid_iso_refs = [
592 'RF92 1229',
593 'RF11 1232',
594 'RF48 1245',
595 ]
596 for ref_no in valid_iso_refs:
597 iso_payment_reference_validator(ref_no)
599 invalid_iso_refs = [
600 'RF92 1229}',
601 ]
602 for ref_no in invalid_iso_refs:
603 with self.assertRaisesMessage(ValidationError, 'Invalid payment reference'):
604 iso_payment_reference_validator(ref_no)
606 def test_fi_ssn_age(self):
607 samples = [
608 (date(2018, 12, 20), '231298-965X', 19),
609 (date(2018, 12, 22), '231298-965X', 19),
610 (date(2018, 12, 23), '231298-965X', 20),
611 (date(2018, 12, 24), '231298-965X', 20),
612 ]
613 for date_now, ssn, age in samples:
614 self.assertEqual(fi_ssn_age(ssn, date_now), age,
615 msg='{} age is {} on {} but fi_ssn_age result was {}'.format(ssn, age, date_now,
616 fi_ssn_age(ssn, date_now)))
618 def test_se_banks(self):
619 self.assertEqual(se_clearing_code_bank_info('6789'), ('Handelsbanken', 9))
620 se_iban_validator('SE45 5000 0000 0583 9825 7466')
621 with self.assertRaisesMessage(ValidationError, _('Invalid IBAN account number')):
622 se_iban_validator('')
623 with self.assertRaisesMessage(ValidationError, _('Invalid IBAN account number')):
624 se_iban_validator('XX45 5000 0000 0583 9825 7466')
625 with self.assertRaisesMessage(ValidationError, _('Invalid IBAN account number')):
626 se_iban_validator('SE45 5000 0000 0583 9825')
627 self.assertEqual(se_clearing_code_bank_info('9500'), ('Nordea AB', 10))
628 an = '957033025420'
629 bank_name, acc_digits = se_clearing_code_bank_info(an)
630 self.assertEqual(bank_name, 'Sparbanken Syd')
631 self.assertGreaterEqual(len(an) - 4, acc_digits)
633 def test_dk_banks(self):
634 an = 'DK50 0040 0440 1162 43'
635 dk_iban_validator(an)
636 bic, name = dk_iban_bank_info(an)
637 self.assertEqual(name, 'Nordea')
638 an = '8114 0008874093'
639 name = dk_clearing_code_bank_name(an)
640 self.assertEqual(name, 'Nykredit Bank')
641 an = 'DK2520006893703029'
642 name = dk_clearing_code_bank_name(an)
643 self.assertEqual(name, 'Nordea')
645 def test_ascii_filter(self):
646 pairs = [
647 ('Åke va Källe o Öring', 'Ake va Kalle o Oring'),
648 ('Tôi đang đi mua sắm', 'Toi ang i mua sam'),
649 ('HELÉN FRANZÉN', 'HELEN FRANZEN'),
650 ]
651 for a, b in pairs:
652 self.assertEqual(ascii_filter(a), b, 'ascii_filter("{}") != "{}"'.format(b, ascii_filter(a)))
654 def test_l10n(self):
655 with override('fi'):
656 msg = _("“%(value)s” value has an invalid format. It must be in YYYY-MM-DD HH:MM[:ss[.uuuuuu]][TZ] format.")
657 if 'muoto ei kelpaa' not in msg: 657 ↛ 658line 657 didn't jump to line 658, because the condition on line 657 was never true
658 print(msg)
659 self.assertTrue('muoto ei kelpaa' in msg)
661 try:
662 parse_bool('hello')
663 except DRFValidationError as e:
664 self.assertEqual(str(e),
665 "[ErrorDetail(string='hello ei ole yksi valittavissa olevista arvoista', code='invalid')]")
667 def test_sanitizers(self):
668 self.assertEqual(country_code_sanitizer('kods'), '')
669 self.assertEqual(country_code_sanitizer('fi'), 'FI')
670 self.assertEqual(phone_sanitizer('+13146094459'), '+13146094459')
671 self.assertEqual(phone_sanitizer('13146094459'), '13146094459')
672 self.assertEqual(phone_sanitizer('+13146094459A'), '+13146094459')
673 self.assertEqual(email_sanitizer('test@example.com'), 'test@example.com')
674 self.assertEqual(email_sanitizer('testexample.com'), '')
676 def test_format_dict_as_html(self):
677 a = {'b': 1, 'c': {'@testVariable': '123'}}
678 res = '<pre>B: 1\nC:\n Test variable: 123\n\n</pre>'
679 self.assertEqual(format_dict_as_html(a), res)
681 def test_format_xml(self):
682 assert settings.XMLLINT_PATH, 'add e.g. XMLLINT_PATH = "/usr/bin/xmllint" to settings.py'
683 src = '<ApplicationRequest> <CustomerId>1</CustomerId> <Command>DownloadFileList</Command><Timestamp>2019-11-27T04:32:18.613452+02:00</Timestamp><Environment>PRODUCTION</Environment></ApplicationRequest>'
684 dst_ref = '<?xml version="1.0"?>\n<ApplicationRequest>\n <CustomerId>1</CustomerId>\n <Command>DownloadFileList</Command>\n <Timestamp>2019-11-27T04:32:18.613452+02:00</Timestamp>\n <Environment>PRODUCTION</Environment>\n</ApplicationRequest>\n'
685 dst = format_xml(src)
686 self.assertEqual(dst, dst_ref)
687 dst = format_xml_bytes(src.encode())
688 self.assertEqual(dst, dst_ref.encode())
690 def test_parse_sftp(self):
691 test_cases = [
692 ('sftp://jani@kajala.com', ['jani', None, 'kajala.com', '']),
693 ('sftp://jani.kajala:1231!@kajala.com', ['jani.kajala', '1231!', 'kajala.com', '']),
694 ('sftp://jani:1231!@kajala.com/my/dir', ['jani', '1231!', 'kajala.com', '/my/dir']),
695 ('sftp://jani.kajala:1231!@kajala.com/my/dir', ['jani.kajala', '1231!', 'kajala.com', '/my/dir']),
696 ]
697 for connection, ref_res in test_cases:
698 res = urlparse(connection)
699 res_list = [res.username, res.password, res.hostname, res.path]
700 self.assertListEqual(res_list, ref_res,
701 'SFTP connection string "{}" parsed incorrectly'.format(connection))
703 def test_admin(self):
704 obj = self.user
705 admin_log([obj], 'Hello, world')
706 admin_log([obj], 'Hello, world', user=self.user, ip='127.0.0.1')
707 admin_log(obj, 'Hello, world', user=self.user, ip='127.0.0.1')
708 e = LogEntry.objects.all().filter(object_id=obj.id).last()
709 self.assertIsNotNone(e)
710 assert isinstance(e, LogEntry)
711 self.assertEqual(e.change_message, 'Hello, world')
712 self.assertEqual(admin_obj_url(obj, 'admin:auth_user_change'), '/admin/auth/user/{}/change/'.format(obj.id))
713 self.assertEqual(admin_obj_url(None, 'admin:auth_user_change'), '')
714 self.assertEqual(admin_obj_link(None, 'admin:auth_user_change'), '')
715 self.assertEqual(admin_obj_url(obj), '/admin/auth/user/{}/change/'.format(obj.id))
716 self.assertEqual(admin_obj_url(e), '/admin/admin/logentry/{}/change/'.format(e.id))
717 link = admin_obj_link(obj, 'User', 'admin:auth_user_change')
718 self.assertEqual(link, "<a href='/admin/auth/user/{}/change/'>User</a>".format(obj.id))
720 def test_cmd_parser(self):
721 parser = CommandParser()
722 add_date_range_arguments(parser)
723 argv = parser.parse_args(['--begin', '2019-06-25', '--end', '2020-02-01'])
724 options = argv.__dict__
725 begin, end, steps = parse_date_range_arguments(options)
726 self.assertEqual(begin, pytz.utc.localize(datetime(2019, 6, 25)))
727 self.assertEqual(end, pytz.utc.localize(datetime(2020, 2, 1)))
729 def test_format_timedelta(self):
730 self.assertEqual(format_timedelta(timedelta(seconds=90)), '1min30s')
731 self.assertEqual(format_timedelta(timedelta(seconds=3600 + 90)), '1h1min30s')
732 self.assertEqual(format_timedelta(timedelta(seconds=90), minutes_label='min ', seconds_label='s '), '1min 30s')
733 self.assertEqual(
734 format_timedelta(timedelta(seconds=3600 + 90), hours_label='h ', minutes_label='min ', seconds_label='s '),
735 '1h 1min 30s')
736 self.assertEqual(format_timedelta(timedelta(seconds=90), seconds_label=''), '1min')
737 self.assertEqual(format_timedelta(timedelta(seconds=0.090), seconds_label='s'), '0.090s')
739 def test_dec123456(self):
740 self.assertEqual(dec1(Decimal('1.2345678')), Decimal('1.2'))
741 self.assertEqual(dec2(Decimal('1.2345678')), Decimal('1.23'))
742 self.assertEqual(dec3(Decimal('1.2345678')), Decimal('1.235'))
743 self.assertEqual(dec4(Decimal('1.2345678')), Decimal('1.2346'))
744 self.assertEqual(dec5(Decimal('1.2345678')), Decimal('1.23457'))
745 self.assertEqual(dec6(Decimal('1.2345678')), Decimal('1.234568'))
747 def test_model_funcs(self):
748 admin_log([self.user], 'test msg 1')
749 obj = LogEntry.objects.all().order_by('-pk').last()
750 assert isinstance(obj, LogEntry)
751 self.assertFalse(is_model_field_changed(obj, 'change_message'))
752 obj.change_message = 'hello world'
753 self.assertTrue(is_model_field_changed(obj, 'change_message'))
754 obj.save()
755 self.assertFalse(is_model_field_changed(obj, 'change_message'))
756 obj2 = clone_model(obj)
757 assert isinstance(obj2, LogEntry)
758 self.assertEqual(obj.change_message, obj2.change_message)
759 self.assertGreater(obj2.pk, obj.pk)
760 label, val = get_model_field_label_and_value(obj, 'action_time')
761 self.assertEqual(label, _('action time'))
762 self.assertEqual(str(obj.action_time), val)
763 obj_b = get_object_or_none(obj.__class__, id=obj.id)
764 self.assertEqual(obj_b.id, obj.id)
765 obj_b = get_object_or_none(obj.__class__, id=-1)
766 self.assertIsNone(obj_b)
768 def test_format_table(self):
769 a = [
770 ['date', 'description', 'count', 'unit price', 'total price'],
771 [date(2019, 12, 15), 'oranges', 1000, dec2('0.99'), dec2('990.00')],
772 [date(2020, 1, 3), 'apples', 4, dec2('1.10'), dec2('4.40')],
773 [date(2020, 11, 3), 'apples', 5, dec2('10.10'), dec2('50.50')],
774 ]
775 out = format_table(a, has_label_row=True, max_col=10)
776 out_ref = """
777---------------------------------------------------
778| date|descript..|count|unit price|total pr..|
779---------------------------------------------------
780|2019-12-15| oranges| 1000| 0.99| 990.00|
781|2020-01-03| apples| 4| 1.10| 4.40|
782|2020-11-03| apples| 5| 10.10| 50.50|
783---------------------------------------------------
784 """.strip()
785 self.assertEqual(out, out_ref)
787 out = format_table(a, has_label_row=True, max_col=20)
788 out_ref = """
789-----------------------------------------------------
790| date|description|count|unit price|total price|
791-----------------------------------------------------
792|2019-12-15| oranges| 1000| 0.99| 990.00|
793|2020-01-03| apples| 4| 1.10| 4.40|
794|2020-11-03| apples| 5| 10.10| 50.50|
795-----------------------------------------------------
796 """.strip()
797 self.assertEqual(out, out_ref)
799 out = format_table(a, has_label_row=True, max_col=20, col_sep=' | ')
800 out_ref = """
801-------------------------------------------------------------
802| date | description | count | unit price | total price|
803-------------------------------------------------------------
804|2019-12-15 | oranges | 1000 | 0.99 | 990.00|
805|2020-01-03 | apples | 4 | 1.10 | 4.40|
806|2020-11-03 | apples | 5 | 10.10 | 50.50|
807-------------------------------------------------------------
808 """.strip()
809 self.assertEqual(out, out_ref)
811 out = format_table(a, has_label_row=True, max_col=20, col_sep=' | ', left_align=[1])
812 out_ref = """
813-------------------------------------------------------------
814| date | description | count | unit price | total price|
815-------------------------------------------------------------
816|2019-12-15 | oranges | 1000 | 0.99 | 990.00|
817|2020-01-03 | apples | 4 | 1.10 | 4.40|
818|2020-11-03 | apples | 5 | 10.10 | 50.50|
819-------------------------------------------------------------
820 """.strip()
821 self.assertEqual(out, out_ref)
823 out = format_table(a, has_label_row=True, max_col=20, col_sep=' | ', left_align=[1], max_line=50)
824 out_ref = """
825-------------------------------------------------
826| date | description | count | unit price|..
827-------------------------------------------------
828|2019-12-15 | oranges | 1000 | 0.99|..
829|2020-01-03 | apples | 4 | 1.10|..
830|2020-11-03 | apples | 5 | 10.10|..
831-------------------------------------------------
832 """.strip()
833 self.assertEqual(out, out_ref)
835 out = format_table(a, left_align=[1], center_align=[0, 2, 3, 4], max_col=50)
836 out_ref = """
837-----------------------------------------------------
838| date |description|count|unit price|total price|
839|2019-12-15|oranges |1000 | 0.99 | 990.00 |
840|2020-01-03|apples | 4 | 1.10 | 4.40 |
841|2020-11-03|apples | 5 | 10.10 | 50.50 |
842-----------------------------------------------------
843 """.strip()
844 self.assertEqual(out, out_ref)
846 def test_ucfirst_lazy(self):
847 s = gettext_lazy(ucfirst_lazy("missing value"))
848 s_ref = gettext_lazy("Missing value")
849 s_en = "Missing value"
850 s_fi = "Puuttuva arvo"
851 with override('fi'):
852 self.assertEqual(s, s_fi)
853 self.assertEqual(s, s_ref)
854 with override('en'):
855 self.assertEqual(s, s_en)
856 self.assertEqual(s, s_ref)
858 def test_media_paths(self):
859 media_root1 = os.path.join(settings.MEDIA_ROOT, 'path1/path2')
860 media_root2 = os.path.join(settings.MEDIA_ROOT, 'path3') + '/'
861 test_paths = [
862 (True, os.path.join(media_root1, 'test1.file'), 'path1/path2/test1.file'),
863 (False, os.path.join('/diff/path', 'test2.file'), '/diff/path/test2.file'),
864 (True, os.path.join(media_root2, 'test3.file'), 'path3/test3.file'),
865 ]
866 for is_media, src, dst in test_paths:
867 self.assertEqual(is_media_full_path(src), is_media)
868 if is_media:
869 self.assertEqual(strip_media_root(src), dst)
870 self.assertEqual(get_media_full_path(dst), src)
871 self.assertEqual(get_media_full_path(dst), get_media_full_path(get_media_full_path(dst)))
873 def test_end_of_month(self):
874 helsinki = pytz.timezone('Europe/Helsinki')
875 # 1
876 time_now = datetime(2020, 6, 5, 15, 47, 23, 818646)
877 eom = end_of_month(time_now, tz=helsinki)
878 eom_ref = helsinki.localize(datetime(2020, 6, 30, 23, 59, 59, 999999))
879 self.assertEqual(eom, eom_ref)
880 # 2
881 time_now = datetime(2020, 7, 5, 15, 47, 23, 818646)
882 eom = end_of_month(time_now, tz=helsinki)
883 eom_ref = helsinki.localize(datetime(2020, 7, 31, 23, 59, 59, 999999))
884 self.assertEqual(eom, eom_ref)
885 # 3
886 time_now = datetime(2020, 6, 5, 15, 47, 23, 818646)
887 eom = end_of_month(time_now, n=1, tz=helsinki)
888 eom_ref = helsinki.localize(datetime(2020, 7, 31, 23, 59, 59, 999999))
889 self.assertEqual(eom, eom_ref)
890 # 4
891 time_now = datetime(2020, 7, 5, 15, 47, 23, 818646)
892 eom = end_of_month(time_now, n=-2, tz=helsinki)
893 eom_ref = helsinki.localize(datetime(2020, 5, 31, 23, 59, 59, 999999))
894 self.assertEqual(eom, eom_ref)
896 def test_iban_generator_and_validator(self):
897 test_ibans = [
898 'MD7289912714638112731113',
899 'IS363252851674877586492113',
900 'HR5125000099152386224',
901 'CZ4750515755735423825528',
902 'FI3253811381259333',
903 'FR3212739000501869481882E94',
904 ]
905 for iban in test_ibans:
906 iban_validator(iban)
907 for cc in ['', 'FI', 'SE']:
908 for n in range(100):
909 acc = iban_generator(cc)
910 try:
911 iban_validator(acc)
912 except Exception as e:
913 print('iban_generator() returned', acc, 'but iban_validator() raised exception', e)
914 self.fail('iban_validator(iban_generator()) should not raise Exception, account number was {}'.format(acc))
915 with self.assertRaisesMessage(ValidationError, _('Invalid country code')):
916 iban_generator('XX')
917 with self.assertRaisesMessage(ValidationError, _('IBAN checksum generation does not support >26 character IBANs')):
918 iban_generator('AL')
920 def test_make_email_recipient(self):
921 email_tests = [
922 {
923 'list': [
924 ('Jani Kajala', 'kajala@example.com'),
925 '"Jani Kajala" <kajala@example.com>',
926 '<kajala@example.com>',
927 'kajala@example.com',
928 ],
929 'result': [
930 ('Jani Kajala', 'kajala@example.com'),
931 ('Jani Kajala', 'kajala@example.com'),
932 ('kajala@example.com', 'kajala@example.com'),
933 ('kajala@example.com', 'kajala@example.com'),
934 ]
935 }
936 ]
937 for et in email_tests:
938 res = make_email_recipient_list(et['list'])
939 self.assertListEqual(res, et['result'])
941 def test_choices(self):
942 val = choices_label(MY_CHOICES, MY_CHOICE_1)
943 self.assertEqual(val, 'MY_CHOICE_1')
945 def test_camel_case(self):
946 pairs = [
947 ('camelCaseWord', 'camel_case_word'),
948 ('camelCase', 'camel_case'),
949 ('camel', 'camel'),
950 ('camelCCase', 'camel_c_case'),
951 ]
952 for cc, us in pairs:
953 self.assertEqual(camel_case_to_underscore(cc), us)
954 self.assertEqual(cc, underscore_to_camel_case(us))
956 def create_dummy_request(self, path: str = '/admin/login/'):
957 request = request_factory.get(path)
958 request.user = self.user # type: ignore
959 request.user.profile = DummyUserProfile() # type: ignore
960 return request
962 def test_model_admin_base(self):
963 # test that actions sorting by name works
964 request = self.create_dummy_request()
965 user = self.user
966 model_admin = MyCustomAdmin(LogEntry, admin.site)
967 res = model_admin.get_actions(request)
968 self.assertEqual(list(res.items())[0][0], 'dummy_admin_func_a', 'ModelAdminBase.get_actions sorting failed')
969 self.assertEqual(list(res.items())[1][0], 'dummy_admin_func_b', 'ModelAdminBase.get_actions sorting failed')
971 # create 10 LogEntry for test user, 5 with text "VisibleLogMessage" and 5 "InvisibleLogMessage"
972 # then check that "VisibleLogMessage" log entries are not visible since max_history_length = 5
973 LogEntry.objects.filter(object_id=user.id).delete()
974 for n in range(5):
975 admin_log([user], 'InvisibleLogMessage')
976 for n in range(5):
977 admin_log([user], 'VisibleLogMessage')
978 self.assertEqual(LogEntry.objects.filter(object_id=user.id).count(), 10)
979 history_url = '/admin/auth/user/{}/history/'.format(user.id)
980 c = self.client
981 c.get(history_url, follow=True)
982 c.post('/admin/login/', {'username': 'test@example.com', 'password': 'test1234'})
983 res = c.get(history_url)
984 content = res.content.decode()
985 assert isinstance(content, str)
986 self.assertEqual(content.count('VisibleLogMessage'), 5)
987 self.assertEqual(content.count('InvisibleLogMessage'), 0)
989 def test_admin_log_entry_mixin(self):
990 user = self.user
991 AdminLogEntryMixin.fields_changed(user, ['username'], who=None)
992 e = LogEntry.objects.filter(object_id=user.id).last()
993 assert isinstance(e, LogEntry)
994 self.assertEqual(e.change_message, 'username: "test@example.com"')
996 def test_auth(self):
997 req = self.create_dummy_request()
998 get_auth_user(req) # type: ignore
999 req.user = None
1000 self.assertIsNone(get_auth_user_or_none(req))
1001 try:
1002 get_auth_user(req) # type: ignore
1003 self.fail('get_auth_user fail')
1004 except NotAuthenticated:
1005 pass
1006 try:
1007 model_admin = AuthUserMixin()
1008 model_admin.request = req
1009 user = model_admin.auth_user
1010 self.fail('get_auth_user fail')
1011 except NotAuthenticated:
1012 pass
1014 def test_middleware(self):
1015 # EnsureOriginMiddleware
1016 request = self.create_dummy_request('/admin/login/')
1017 EnsureOriginMiddleware(dummy_middleware_get_response)(request)
1018 self.assertIn('HTTP_ORIGIN', request.META)
1019 self.assertEqual(request.META['HTTP_ORIGIN'], request.get_host())
1021 # LogExceptionMiddleware
1022 dummy_log = DummyLogHandler()
1023 jutil_middleware_logger.addHandler(dummy_log)
1024 try:
1025 raise Exception('Dummy exception, ignore this')
1026 except Exception as e:
1027 mw = LogExceptionMiddleware(dummy_middleware_get_response)
1028 mw.process_exception(request, e)
1029 msg = dummy_log.msgs.pop()
1030 self.assertIn('Exception: Dummy exception, ignore this', msg)
1031 self.assertIn('user=test@example.com', msg)
1032 jutil_middleware_logger.removeHandler(dummy_log)
1034 # EnsureLanguageCookieMiddleware
1035 lang_cookie_tests = [
1036 ('/admin/login/', settings.LANGUAGE_CODE),
1037 ('/admin/login/?django_language=fi', 'fi'),
1038 ]
1039 for request_path, lang_code in lang_cookie_tests:
1040 request = self.create_dummy_request(request_path)
1041 mw = EnsureLanguageCookieMiddleware(dummy_middleware_get_response)
1042 res = mw(request)
1043 assert isinstance(res, HttpResponse)
1044 self.assertEqual(res.status_code, 200)
1045 self.assertIn('django_language', request.COOKIES)
1046 self.assertIn(request.COOKIES['django_language'], lang_code)
1047 self.assertIn('django_language', res.cookies)
1048 self.assertEqual(str(res.cookies['django_language']), 'Set-Cookie: django_language={}; Path=/'.format(lang_code))
1050 # ActivateUserProfileTimezoneMiddleware
1051 user = self.user
1052 self.assertTrue(user.is_authenticated)
1053 user.profile.timezone = 'Europe/Helsinki'
1054 with timezone.override(pytz.timezone('America/Chicago')):
1055 request = self.create_dummy_request('/admin/login/')
1056 mw = ActivateUserProfileTimezoneMiddleware(dummy_time_zone_response)
1057 res = mw(request)
1058 content = res.content.decode()
1059 self.assertEqual(content, user.profile.timezone)
1061 def test_safe_fields(self):
1062 class TestModel(models.Model):
1063 cf = SafeCharField(max_length=256)
1064 tf = SafeTextField()
1066 obj = TestModel()
1067 data = 'hello world <script>alert("popup")<script>'
1068 data_ref = 'hello world '
1069 for f in obj._meta.fields:
1070 if f.name in ['cf', 'tf']:
1071 f.save_form_data(obj, data)
1072 self.assertEqual(obj.cf, data_ref)
1073 self.assertEqual(obj.tf, data_ref)
1075 def test_responses(self):
1076 a = [
1077 ['date', 'description', 'count', 'unit price', 'total price'],
1078 [date(2019, 12, 15), 'oranges', 1000, dec2('0.99'), dec2('990.00')],
1079 [date(2020, 1, 3), 'apples', 4, dec2('1.10'), dec2('4.40')],
1080 [date(2020, 11, 3), 'apples', 5, dec2('10.10'), dec2('50.50')],
1081 ]
1082 res = CsvResponse(a, 'test.csv')
1083 content_ref = b'date,description,count,unit price,total price\r\n2019-12-15,oranges,1000,0.99,990.00\r\n2020-01-03,apples,4,1.10,4.40\r\n2020-11-03,apples,5,10.10,50.50\r\n'
1084 self.assertEqual(content_ref, res.content)
1085 print(res.content.decode())
1087 def test_wait_object_or_none(self):
1088 admin_log([self.user], 'Hello, world')
1089 e = LogEntry.objects.all().filter(object_id=self.user.id).last()
1090 assert isinstance(e, LogEntry)
1091 e_id = e.id
1092 obj = wait_object_or_none(LogEntry, id=e_id)
1093 self.assertIsNotNone(obj)
1094 self.assertEqual(obj.id, e_id)
1095 t0 = now()
1096 obj = wait_object_or_none(LogEntry, timeout=1.0, sleep_interval=0.1, id=e_id+1)
1097 t1 = now()
1098 self.assertIsNone(obj)
1099 self.assertGreater(t1-t0, timedelta(seconds=0.99))
1101 def test_format_as_html_json(self):
1102 pairs = [
1103 (None, 'null'),
1104 ({'a': 1}, '{<br/> "a": 1<br/>}'),
1105 ({"script": "<script>window.alert()</script>"},
1106 '{<br/> "script": "<script>window.alert()</script>"<br/>}'),
1107 ]
1108 for val, html in pairs:
1109 self.assertEqual(format_as_html_json(val), html)
1111 def test_parse_bool(self):
1112 refs_true = [
1113 True,
1114 'True',
1115 '1',
1116 1,
1117 'true',
1118 'yes',
1119 ]
1120 refs_false = [
1121 False,
1122 'False',
1123 '0',
1124 0,
1125 'false',
1126 'no',
1127 ]
1128 for ref in refs_true:
1129 self.assertTrue(parse_bool(ref))
1130 for ref in refs_false:
1131 self.assertFalse(parse_bool(ref))
1133 def test_exception_convert(self):
1134 e = transform_exception_to_drf(ValidationError({'hello': 'world'}))
1135 assert isinstance(e, DRFValidationError)
1136 self.assertTrue(isinstance(e, DRFValidationError))
1137 self.assertIn('hello', e.detail)
1138 self.assertEqual(e.detail['hello'], [ErrorDetail(string='world', code='invalid')])
1141dummy_admin_func_a.short_description = 'A' # type: ignore
1142dummy_admin_func_b.short_description = 'B' # type: ignore
1144admin.site.unregister(User)
1145admin.site.register(User, MyCustomAdmin)
1146admin.site.register(LogEntry, MyCustomAdmin)