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