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.modelfields import SafeCharField, SafeTextField
13from jutil.middleware import logger as jutil_middleware_logger, ActivateUserProfileTimezoneMiddleware
14import pytz
15from django.conf import settings
16from django.contrib.admin.models import LogEntry
17from django.contrib.auth.models import User
18from django.core.exceptions import ValidationError
19from django.core.management.base import CommandParser # type: ignore
20from django.db import models
21from django.http.response import HttpResponse
22from django.test import TestCase
23from django.test.client import RequestFactory, Client
24from django.utils.translation import override, gettext as _, gettext_lazy
25from rest_framework.exceptions import NotAuthenticated
26from jutil.admin import admin_log, admin_obj_url, admin_obj_link, ModelAdminBase, AdminLogEntryMixin
27from jutil.auth import AuthUserMixin, get_auth_user, get_auth_user_or_none
28from jutil.command import get_date_range_by_name, add_date_range_arguments, parse_date_range_arguments
29from jutil.email import make_email_recipient_list
30from jutil.middleware import EnsureOriginMiddleware, LogExceptionMiddleware, EnsureLanguageCookieMiddleware
31from jutil.model import is_model_field_changed, clone_model, get_model_field_label_and_value, get_object_or_none, \
32 wait_object_or_none
33from jutil.request import get_ip_info
34from jutil.responses import CsvResponse
35from jutil.testing import TestSetupMixin
36from jutil.urls import modify_url
37from jutil.xml import xml_to_dict, dict_to_element, _xml_filter_tag_name
38from jutil.dates import add_month, per_delta, per_month, this_week, next_month, next_week, this_month, last_month, \
39 last_year, last_week, yesterday, end_of_month, this_year, get_time_steps, TIME_STEP_DAILY
40from jutil.format import format_full_name, format_xml, format_xml_bytes, format_timedelta, dec1, dec2, dec3, dec4, dec5, \
41 dec6, format_table, ucfirst_lazy, strip_media_root, get_media_full_path, camel_case_to_underscore, \
42 underscore_to_camel_case, format_as_html_json, format_dict_as_html, choices_label, is_media_full_path
43from jutil.parse import parse_datetime, parse_bool, parse_datetime_or_none
44from jutil.validators import fi_payment_reference_number, se_ssn_validator, se_ssn_filter, fi_iban_validator, \
45 se_iban_validator, iban_filter_readable, email_filter, iban_validator, iban_bank_info, fi_company_org_id_validator, \
46 email_validator, fi_payment_reference_validator, iso_payment_reference_validator, fi_ssn_age, \
47 se_clearing_code_bank_info, ascii_filter, ee_iban_validator, be_iban_validator, dk_iban_validator, \
48 dk_iban_bank_info, dk_clearing_code_bank_name, country_code_sanitizer, phone_sanitizer, email_sanitizer, \
49 fi_company_org_id_generator, phone_validator, passport_filter, passport_validator, passport_sanitizer, \
50 country_code_validator, validate_country_iban, iban_bic, validate_country_company_org_id, fi_ssn_generator, \
51 fi_ssn_validator, bic_validator, iban_generator, bic_sanitizer, filter_country_company_org_id
52from xml.etree.ElementTree import Element
53from xml.etree import ElementTree as ET
54from django.contrib import admin
57MY_CHOICE_1 = '1'
58MY_CHOICE_2 = '2'
59MY_CHOICES = (
60 (MY_CHOICE_1, 'MY_CHOICE_1'),
61 (MY_CHOICE_2, 'MY_CHOICE_2'),
62)
64request_factory = RequestFactory()
67class DummyLogHandler(logging.Handler):
68 msgs: List[str]
70 def __init__(self):
71 super().__init__()
72 self.msgs = []
74 def emit(self, record):
75 msg = self.format(record)
76 self.msgs.append(msg)
79def dummy_time_zone_response(obj) -> HttpResponse:
80 tz = timezone.get_current_timezone()
81 return HttpResponse(str(tz).encode())
84def dummy_admin_func_a(modeladmin, request, qs):
85 print('dummy_admin_func_a')
88def dummy_admin_func_b(modeladmin, request, qs):
89 print('dummy_admin_func_b')
92def dummy_middleware_get_response(obj) -> HttpResponse:
93 return HttpResponse(b'hello content')
96class DummyUserProfile:
97 timezone = 'Europe/Helsinki'
100class MyCustomAdmin(ModelAdminBase):
101 max_history_length = 5
102 actions = (
103 dummy_admin_func_b,
104 dummy_admin_func_a,
105 )
107 def get_object(self, request, obj_id):
108 return self.model.objects.get(id=obj_id)
111class Tests(TestCase, TestSetupMixin):
112 def setUp(self):
113 self.user = user = self.add_test_user('test@example.com', 'test1234')
114 assert isinstance(user, User)
115 user.is_superuser = True
116 user.is_staff = True
117 user.save()
118 self.client = Client()
120 def test_api_client(self):
121 api_client = self.create_api_client()
122 self.assertTrue(isinstance(api_client, APIClient))
124 def test_payment_reference(self):
125 self.assertEqual(fi_payment_reference_number('100'), '1009')
126 invalids = [
127 '89182932u',
128 '0000002',
129 ]
130 for invalid in invalids:
131 try:
132 fi_payment_reference_number(invalid)
133 self.fail('invalid number but passed: {}'.format(invalid))
134 except ValidationError:
135 pass
137 def test_format_full_name(self):
138 samples = [
139 ('Short', 'Full Name', 'Short Full Name'),
140 ('Short Middle Name Is Quite Long', 'Full Name', 'Short Full Name'),
141 ('Short-Middle Name Is Quite Long', 'Full Name', 'Short Full Name'),
142 ('Olga Demi', 'Serpuhovitinova-Miettinen', 'Olga Serpuhovitinova'),
143 ('Olga-Anne Demi', 'Serpuhovitinovatsko', 'Olga S'),
144 ]
145 for v in samples:
146 limited = format_full_name(v[0], v[1], 20)
147 # print('{} {} -> {} (was: {})'.format(v[0], v[1], v[2], limited))
148 self.assertEqual(v[2], limited)
149 try:
150 long_name = '19280309812083091829038190823081208301280381092830182038018203810283021'
151 format_full_name(long_name, long_name)
152 self.fail('format_full_name failed with long name')
153 except Exception:
154 pass
156 def test_parse_datetime(self):
157 pairs = [
158 ('2016-06-12', '2016-06-12T00:00:00+00:00'),
159 ('2016-06-12T01:00:00', '2016-06-12T01:00:00+00:00'),
160 ('2016-06-12 01:00:00', '2016-06-12T01:00:00+00:00'),
161 ('2016-06-12T01:00:00+00:00', '2016-06-12T01:00:00+00:00'),
162 ('2016-06-12 01:00:00+00:00', '2016-06-12T01:00:00+00:00'),
163 ]
164 for t_str_input, t_ref_str in pairs:
165 # parse_datetime
166 t = parse_datetime(t_str_input)
167 self.assertTrue(isinstance(t, datetime))
168 assert isinstance(t, datetime)
169 self.assertEqual(t.isoformat(), t_ref_str)
170 # parse_datetime_or_none
171 t = parse_datetime_or_none(t_str_input)
172 self.assertTrue(isinstance(t, datetime))
173 assert isinstance(t, datetime)
174 self.assertEqual(t.isoformat(), t_ref_str)
176 invalids = [
177 '12312313ew',
178 '2016-13-05',
179 ]
180 for t_str_input in invalids:
181 # parse_datetime
182 try:
183 parse_datetime(t_str_input)
184 self.fail('{} is not valid input for parse_datetime()'.format(t_str_input))
185 except Exception:
186 pass
187 # parse_datetime_or_none
188 t = parse_datetime_or_none(t_str_input)
189 assert t is None
191 def test_add_month(self):
192 t = parse_datetime('2016-06-12T01:00:00')
193 self.assertTrue(isinstance(t, datetime))
194 assert isinstance(t, datetime)
195 self.assertEqual(t.isoformat(), '2016-06-12T01:00:00+00:00')
196 t2 = add_month(t)
197 self.assertEqual(t2.isoformat(), '2016-07-12T01:00:00+00:00')
198 t3 = add_month(t, 7)
199 self.assertEqual(t3.isoformat(), '2017-01-12T01:00:00+00:00')
200 t4 = add_month(t, -1)
201 self.assertEqual(t4.isoformat(), '2016-05-12T01:00:00+00:00')
202 time_now = datetime(2020, 6, 30, 15, 47, 23, 818646)
203 self.assertEqual(add_month(time_now, -4).isoformat(), '2020-02-29T15:47:23.818646')
204 self.assertEqual(add_month(time_now, 8).isoformat(), '2021-02-28T15:47:23.818646')
205 self.assertEqual(add_month(time_now, 0).isoformat(), '2020-06-30T15:47:23.818646')
207 def test_se_ssn(self):
208 se_ssn_validator('811228-9874')
209 se_ssn_validator('670919-9530')
210 with self.assertRaises(ValidationError):
211 se_ssn_validator('811228-9873')
213 def test_phone_numbers(self):
214 try:
215 phone_validator('+358456343767')
216 except Exception:
217 self.fail('phone_validator("+358456343767") should not raise Exception')
218 with self.assertRaisesMessage(ValidationError, _('Invalid phone number')):
219 phone_validator('214')
220 self.assertEqual(phone_sanitizer('214'), '')
222 def test_passport(self):
223 self.assertEqual(passport_filter('?ADsd-12312dsds'), 'ADSD-12312DSDS')
224 passport_validator('21412312312')
225 with self.assertRaisesMessage(ValidationError, _('Invalid passport number')):
226 passport_validator('214')
227 self.assertEqual(passport_sanitizer('214'), '')
228 self.assertEqual(passport_sanitizer('21412312312'), '21412312312')
230 def test_country_code(self):
231 for cc in ['FI', 'DK', 'ES', 'SE', 'VN']:
232 country_code_validator(cc)
233 with self.assertRaisesMessage(ValidationError, _('Invalid country code')):
234 country_code_validator('Finland')
236 def test_bic(self):
237 bic = iban_bic('FI21 1234 5600 0007 85')
238 self.assertEqual(bic, 'NDEAFIHH')
239 self.assertEqual(bic_sanitizer('NDEAFIHH'), 'NDEAFIHH')
240 self.assertEqual(bic_sanitizer('NDEAFIH'), '')
242 def test_org_id(self):
243 try:
244 validate_country_company_org_id('FI', '2084069-9')
245 except Exception:
246 self.fail('2084069-9 is valid org id')
247 with self.assertRaisesMessage(ValidationError, _('Invalid company organization ID')):
248 validate_country_company_org_id('FI', '2084069-8')
250 def test_validate_country_iban(self):
251 with self.assertRaisesMessage(ValidationError, _('Invalid IBAN account number')):
252 validate_country_iban('FI15616515616156', 'SE')
254 def test_fi_ssn_generator(self):
255 self.assertEqual(len(fi_ssn_generator()), 6 + 1 + 4)
256 for min_year, max_year in [(1800, 1900), (1900, 2000), (2000, 2050)]:
257 for n in range(10):
258 ssn = fi_ssn_generator(min_year, max_year)
259 try:
260 fi_ssn_age(ssn)
261 fi_ssn_validator(ssn)
262 except Exception:
263 self.fail('{} is valid SSN'.format(ssn))
264 fi_ssn_validator('110305+283X')
265 for ssn in ['9999-123F', '271138-670X', '090228+256X']:
266 with self.assertRaisesMessage(ValidationError, _('Invalid personal identification number')):
267 fi_ssn_validator(ssn)
268 with self.assertRaises(ValidationError):
269 fi_ssn_generator(1700, 1799)
270 with self.assertRaises(ValidationError):
271 fi_ssn_generator(2100, 2200)
273 def test_iban(self):
274 with self.assertRaisesMessage(ValidationError, _('Invalid IBAN account number')):
275 iban_validator('')
276 with self.assertRaisesMessage(ValidationError, _('Invalid country code')):
277 iban_validator('XX')
278 with self.assertRaisesMessage(ValidationError, _('Invalid IBAN account number')):
279 iban_validator('FI2112345600000786')
280 bic_validator('HELSFIHH')
281 bic_validator('HELSFIHHXXX')
282 with self.assertRaisesMessage(ValidationError, _('Invalid bank BIC/SWIFT code')):
283 bic_validator('HELSFIH')
284 with self.assertRaisesMessage(ValidationError, _('Invalid bank BIC/SWIFT code')):
285 bic_validator('')
286 with self.assertRaisesMessage(ValidationError, _('Invalid bank BIC/SWIFT code')):
287 bic_validator('XX123123123112')
288 iban_validator('FI2112345600000785')
289 iban_validator('SE4550000000058398257466')
290 fi_iban_validator('FI2112345600000785')
291 se_iban_validator('SE4550000000058398257466')
292 ee_iban_validator('EE38 2200 2210 2014 5685')
293 be_iban_validator('BE68 5390 0754 7034')
294 with self.assertRaises(ValidationError):
295 fi_iban_validator('FI2112345600000784')
296 with self.assertRaises(ValidationError):
297 se_iban_validator('SE4550000000058398257465')
298 iban = 'FI8847304720017517'
299 self.assertEqual(iban_filter_readable(iban), 'FI88 4730 4720 0175 17')
300 self.assertEqual(iban_filter_readable(''), '')
302 def test_urls(self):
303 url = 'http://yle.fi/uutiset/3-8045550?a=123&b=456'
304 self.assertEqual(modify_url('http://yle.fi/uutiset/3-8045550?a=123&b=456', {'a': '123', 'b': '456'}),
305 'http://yle.fi/uutiset/3-8045550?a=123&b=456')
307 def test_email_filter_and_validation(self):
308 emails = [
309 (' Asdsa@a-a.com ', 'asdsa@a-a.com', True),
310 ('1asdsa@a-a2.com', '1asdsa@a-a2.com', True),
311 (' Asdsa@a-a ', 'asdsa@a-a', False),
312 (' @a-a2.com', '@a-a2.com', False),
313 (' a-a2.com', 'a-a2.com', False),
314 ('ää-a2@ää-a2.com', 'ää-a2@ää-a2.com', False),
315 ('aaa.bbbbb@ccc-ddd.fi', 'aaa.bbbbb@ccc-ddd.fi', True),
316 ]
317 for i, o, is_valid in emails:
318 # print('email_filter({}) -> {}'.format(i, email_filter(i)))
319 self.assertEqual(email_filter(i), o)
320 if is_valid:
321 email_validator(o)
322 else:
323 fail = False
324 try:
325 email_validator(o)
326 except ValidationError:
327 fail = True
328 self.assertTrue(fail, '{} is not valid email but passed validation'.format(o))
330 def test_ip_info(self):
331 ip, cc, host = get_ip_info('213.214.146.142')
332 self.assertEqual(ip, '213.214.146.142')
333 if cc: 333 ↛ 335line 333 didn't jump to line 335, because the condition on line 333 was never false
334 self.assertEqual(cc, 'FI')
335 if host: 335 ↛ exitline 335 didn't return from function 'test_ip_info', because the condition on line 335 was never false
336 self.assertEqual(host, '213214146142.edelkey.net')
338 def test_parse_xml(self):
339 # finvoice_201_example1.xml
340 xml_bytes = open(join(settings.BASE_DIR, 'data/fi/finvoice_201_example1.xml'), 'rb').read()
341 data = xml_to_dict(xml_bytes, value_key='value', attribute_prefix='_')
342 # pprint(data)
343 self.assertEqual(data['_Version'], '2.01')
344 self.assertEqual(data['InvoiceRow'][0]['ArticleIdentifier'], '12345')
345 self.assertEqual(data['InvoiceRow'][0]['DeliveredQuantity']['value'], '2')
346 self.assertEqual(data['InvoiceRow'][0]['DeliveredQuantity']['_QuantityUnitCode'], 'kpl')
347 self.assertEqual(data['InvoiceRow'][1]['ArticleIdentifier'], '123456')
349 # parse_xml1.xml
350 xml_str = open(join(settings.BASE_DIR, 'data/parse_xml1.xml'), 'rt').read()
351 data = xml_to_dict(xml_str.encode())
352 # pprint(data)
353 ref_data = {'@version': '1.2',
354 'A': [{'@class': 'x', 'B': {'@': 'hello', '@class': 'x2'}},
355 {'@class': 'y', 'B': {'@': 'world', '@class': 'y2'}}],
356 'C': 'value node'}
357 self.assertEqual(ref_data, data)
359 # parse_xml1.xml / no attributes
360 xml_str = open(join(settings.BASE_DIR, 'data/parse_xml1.xml'), 'rt').read()
361 data = xml_to_dict(xml_str.encode(), parse_attributes=False)
362 # pprint(data)
363 ref_data = {'A': [{'B': 'hello'}, {'B': 'world'}], 'C': 'value node'}
364 self.assertEqual(ref_data, data)
366 # parse_xml2.xml / no attributes
367 xml_str = open(join(settings.BASE_DIR, 'data/parse_xml2.xml'), 'rt').read()
368 data = xml_to_dict(xml_str.encode(), ['VastausLoki', 'LuottoTietoMerkinnat'], parse_attributes=False)
369 # pprint(data)
370 ref_data = {'VastausLoki': {'KysyttyHenkiloTunnus': '020685-1234',
371 'PaluuKoodi': 'Palveluvastaus onnistui',
372 'SyyKoodi': '1'}}
373 self.assertEqual(ref_data, data)
375 def test_dict_to_xml(self):
376 data = {
377 'Doc': {
378 '@version': '1.2',
379 'A': [{'@class': 'x', 'B': {'@': 'hello', '@class': 'x2'}},
380 {'@class': 'y', 'B': {'@': 'world', '@class': 'y2'}}],
381 'C': 'value node',
382 'D': 123,
383 'E': ['abc'],
384 }
385 }
386 el = dict_to_element(data)
387 assert isinstance(el, Element)
388 xml_str = ET.tostring(el, encoding='utf8', method='xml').decode()
389 # 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>
390 data2 = xml_to_dict(xml_str.encode(), document_tag=True, array_tags=['E'], int_tags=['D'])
391 # print('')
392 # pprint(data)
393 # pprint(data2)
394 self.assertEqual(data2, data)
396 def test_dict_to_xml2(self):
397 self.assertEqual(_xml_filter_tag_name('TagName[0]'), 'TagName')
398 self.assertEqual(_xml_filter_tag_name('TagName[1]'), 'TagName')
399 self.assertEqual(_xml_filter_tag_name('TagName'), 'TagName')
400 data = {
401 'Doc': {
402 '@version': '1.2',
403 'A': [{'@class': 'x', 'B': {'@': 'hello', '@class': 'x2'}},
404 {'@class': 'y', 'B': {'@': 'world', '@class': 'y2'}}],
405 'C': 'value node',
406 'D': 123,
407 'E': ['abc'],
408 'F': ['line 1', 'line 2']
409 }
410 }
411 el = dict_to_element(data)
412 assert isinstance(el, Element)
413 xml_str = ET.tostring(el, encoding='utf8', method='xml').decode()
414 data2 = xml_to_dict(xml_str.encode(), document_tag=True, array_tags=['E', 'F'], int_tags=['D'])
415 self.assertEqual(data2, data)
417 def test_xml_to_dict(self):
418 xml_str = """<?xml version="1.0" encoding="utf-8"?>
419<Document>
420 <TxsSummry>
421 <TtlNtries>
422 <NbOfNtries>12</NbOfNtries>
423 </TtlNtries>
424 <TtlCdtNtries>
425 <NbOfNtries>34</NbOfNtries>
426 <Sum>1234.56</Sum>
427 </TtlCdtNtries>
428 <TtlDbtNtries>
429 <NbOfNtries>0</NbOfNtries>
430 <Sum>0</Sum>
431 </TtlDbtNtries>
432 </TxsSummry>
433</Document>"""
434 data = xml_to_dict(xml_str.encode(), document_tag=True, array_tags=[], int_tags=['NbOfNtries'])
435 # print('')
436 # pprint(data)
437 self.assertEqual(data['Document']['TxsSummry']['TtlNtries']['NbOfNtries'], 12)
438 self.assertEqual(data['Document']['TxsSummry']['TtlCdtNtries']['NbOfNtries'], 34)
440 def test_per_delta(self):
441 begin = datetime(2017, 9, 17, 11, 42)
442 end = begin + timedelta(days=4)
443 ref = [(datetime(2017, 9, 17, 11, 42), datetime(2017, 9, 18, 11, 42)),
444 (datetime(2017, 9, 18, 11, 42), datetime(2017, 9, 19, 11, 42)),
445 (datetime(2017, 9, 19, 11, 42), datetime(2017, 9, 20, 11, 42)),
446 (datetime(2017, 9, 20, 11, 42), datetime(2017, 9, 21, 11, 42))]
447 res = per_delta(begin, end, timedelta(days=1))
448 self.assertEqual(list(res), ref)
450 def test_per_month(self):
451 begin = datetime(2017, 9, 1, 0, 0)
452 res = list(per_month(begin, begin + timedelta(days=32)))
453 ref = [(datetime(2017, 9, 1, 0, 0), datetime(2017, 10, 1, 0, 0)),
454 (datetime(2017, 10, 1, 0, 0), datetime(2017, 11, 1, 0, 0))]
455 self.assertEqual(list(res), ref)
457 def test_dates(self):
458 t = datetime(2018, 1, 30)
459 b, e = this_week(t)
460 self.assertEqual(b, pytz.utc.localize(datetime(2018, 1, 29)))
461 self.assertEqual(e, pytz.utc.localize(datetime(2018, 2, 5)))
462 b, e = this_month(t)
463 self.assertEqual(b, pytz.utc.localize(datetime(2018, 1, 1)))
464 self.assertEqual(e, pytz.utc.localize(datetime(2018, 2, 1)))
465 b, e = next_week(t)
466 self.assertEqual(b, pytz.utc.localize(datetime(2018, 2, 5)))
467 self.assertEqual(e, pytz.utc.localize(datetime(2018, 2, 12)))
469 def test_named_date_ranges(self):
470 t = datetime(2018, 5, 31)
471 t_tz = pytz.utc.localize(t)
472 named_ranges = [
473 ('last_year', last_year(t)),
474 ('last_month', last_month(t)),
475 ('last_week', last_week(t)),
476 ('this_year', this_year(t)),
477 ('this_month', this_month(t)),
478 ('this_week', this_week(t)),
479 ('yesterday', yesterday(t)),
480 ('today', yesterday(t + timedelta(hours=24))),
481 ]
482 day_ranges = [7, 15, 30, 60, 90]
483 for days in day_ranges:
484 named_ranges.append(
485 ('plus_minus_{}d'.format(days), (t_tz - timedelta(days=days), t_tz + timedelta(days=days))))
486 named_ranges.append(('prev_{}d'.format(days), (t_tz - timedelta(days=days), t_tz)))
487 named_ranges.append(('next_{}d'.format(days), (t_tz, t_tz + timedelta(days=days))))
488 for name, res in named_ranges:
489 # print('testing', name)
490 self.assertEqual(get_date_range_by_name(name, t), res)
492 def test_time_steps(self):
493 t = parse_datetime('2020-08-24 23:28:36.503174')
494 this_week_daily = [
495 '2020-08-24T00:00:00+00:00 2020-08-25T00:00:00+00:00',
496 '2020-08-25T00:00:00+00:00 2020-08-26T00:00:00+00:00',
497 '2020-08-26T00:00:00+00:00 2020-08-27T00:00:00+00:00',
498 '2020-08-27T00:00:00+00:00 2020-08-28T00:00:00+00:00',
499 '2020-08-28T00:00:00+00:00 2020-08-29T00:00:00+00:00',
500 '2020-08-29T00:00:00+00:00 2020-08-30T00:00:00+00:00',
501 '2020-08-30T00:00:00+00:00 2020-08-31T00:00:00+00:00',
502 ]
503 ix = 0
504 for begin, end in get_time_steps(TIME_STEP_DAILY, *this_week(t)):
505 self.assertEqual('{} {}'.format(begin.isoformat(), end.isoformat()), this_week_daily[ix])
506 ix += 1
508 def test_bank_info(self):
509 ac = 'FI8847304720017517'
510 inf = iban_bank_info(ac)
511 self.assertEqual(inf[0], 'POPFFI22')
512 self.assertEqual(inf[1], 'POP-Pankki')
514 ac = ''
515 inf = iban_bank_info(ac)
516 self.assertEqual(inf[0], '')
517 self.assertEqual(inf[1], '')
519 ac = 'BE75270187592710'
520 inf = iban_bank_info(ac)
521 self.assertEqual(inf[0], 'GEBABEBB')
522 self.assertEqual(inf[1], 'BNP Paribas Fortis')
523 ac = 'BE58465045170210'
524 inf = iban_bank_info(ac)
525 self.assertEqual(inf[0], 'KREDBEBB')
526 self.assertEqual(inf[1], 'KBC Bank')
527 ac = 'BE11000123456748'
528 inf = iban_bank_info(ac)
529 self.assertEqual(inf[0], 'BPOTBEB1')
530 self.assertEqual(inf[1], 'bpost bank')
532 def test_org_id_fi(self):
533 valids = [
534 'FI01098230',
535 'FI-01098230',
536 '0109823-0',
537 '2084069-9',
538 ]
539 invalids = [
540 '2084069-1',
541 'SE2084069-1',
542 ]
543 co_filtered = [
544 ('FI', 'FI01098230', '0109823-0'),
545 ('FI', 'FI-01098230', '0109823-0'),
546 ('FI', '0109823-0', '0109823-0'),
547 ('FI', '2084069-9', '2084069-9'),
548 ('SE', '01098230', '01098230'),
549 ('SE', '20840699', '20840699'),
550 ]
551 for cc, org, val in co_filtered:
552 self.assertEqual(filter_country_company_org_id(cc, org), val)
553 validate_country_company_org_id(cc, org)
554 for valid in valids:
555 fi_company_org_id_validator(valid)
556 for invalid in invalids:
557 try:
558 fi_company_org_id_validator(invalid)
559 self.fail('{} passed as valid FI-org'.format(invalid))
560 except ValidationError:
561 # print('ok')
562 pass
563 for n in range(10):
564 v0 = fi_company_org_id_generator()
565 # print(v0)
566 fi_company_org_id_validator(v0)
568 def test_reference_number_validators(self):
569 valid_fi_refs = [
570 '302300',
571 '202196',
572 '302290',
573 ]
574 for ref_no in valid_fi_refs:
575 fi_payment_reference_validator(ref_no)
577 invalid_fi_refs = [
578 '302301',
579 '202195',
580 '302291',
581 ]
582 for ref_no in invalid_fi_refs:
583 try:
584 fi_payment_reference_validator(ref_no)
585 self.assertFalse(True, '{} should have failed validation'.format(ref_no))
586 except ValidationError:
587 pass
589 valid_iso_refs = [
590 'RF92 1229',
591 'RF11 1232',
592 'RF48 1245',
593 ]
594 for ref_no in valid_iso_refs:
595 iso_payment_reference_validator(ref_no)
597 invalid_iso_refs = [
598 'RF92 1229}',
599 ]
600 for ref_no in invalid_iso_refs:
601 with self.assertRaisesMessage(ValidationError, 'Invalid payment reference'):
602 iso_payment_reference_validator(ref_no)
604 def test_fi_ssn_age(self):
605 samples = [
606 (date(2018, 12, 20), '231298-965X', 19),
607 (date(2018, 12, 22), '231298-965X', 19),
608 (date(2018, 12, 23), '231298-965X', 20),
609 (date(2018, 12, 24), '231298-965X', 20),
610 ]
611 for date_now, ssn, age in samples:
612 self.assertEqual(fi_ssn_age(ssn, date_now), age,
613 msg='{} age is {} on {} but fi_ssn_age result was {}'.format(ssn, age, date_now,
614 fi_ssn_age(ssn, date_now)))
616 def test_se_banks(self):
617 self.assertEqual(se_clearing_code_bank_info('6789'), ('Handelsbanken', 9))
618 se_iban_validator('SE45 5000 0000 0583 9825 7466')
619 with self.assertRaisesMessage(ValidationError, _('Invalid IBAN account number')):
620 se_iban_validator('')
621 with self.assertRaisesMessage(ValidationError, _('Invalid IBAN account number')):
622 se_iban_validator('XX45 5000 0000 0583 9825 7466')
623 with self.assertRaisesMessage(ValidationError, _('Invalid IBAN account number')):
624 se_iban_validator('SE45 5000 0000 0583 9825')
625 self.assertEqual(se_clearing_code_bank_info('9500'), ('Nordea AB', 10))
626 an = '957033025420'
627 bank_name, acc_digits = se_clearing_code_bank_info(an)
628 self.assertEqual(bank_name, 'Sparbanken Syd')
629 self.assertGreaterEqual(len(an) - 4, acc_digits)
631 def test_dk_banks(self):
632 an = 'DK50 0040 0440 1162 43'
633 dk_iban_validator(an)
634 bic, name = dk_iban_bank_info(an)
635 self.assertEqual(name, 'Nordea')
636 an = '8114 0008874093'
637 name = dk_clearing_code_bank_name(an)
638 self.assertEqual(name, 'Nykredit Bank')
639 an = 'DK2520006893703029'
640 name = dk_clearing_code_bank_name(an)
641 self.assertEqual(name, 'Nordea')
643 def test_ascii_filter(self):
644 pairs = [
645 ('Åke va Källe o Öring', 'Ake va Kalle o Oring'),
646 ('Tôi đang đi mua sắm', 'Toi ang i mua sam'),
647 ('HELÉN FRANZÉN', 'HELEN FRANZEN'),
648 ]
649 for a, b in pairs:
650 self.assertEqual(ascii_filter(a), b, 'ascii_filter("{}") != "{}"'.format(b, ascii_filter(a)))
652 def test_l10n(self):
653 from rest_framework.exceptions import ValidationError
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 ValidationError 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, 'User id={}: username=test@example.com'.format(user.id))
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))
1134dummy_admin_func_a.short_description = 'A' # type: ignore
1135dummy_admin_func_b.short_description = 'B' # type: ignore
1137admin.site.unregister(User)
1138admin.site.register(User, MyCustomAdmin)
1139admin.site.register(LogEntry, MyCustomAdmin)