Coverage for jutil/tests.py: 97%

# Coverage for jutil/tests.py : 97%

## 739 statements

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

1 import logging

2 import os

3 from datetime import datetime, timedelta, date

4 from decimal import Decimal

5 from os.path import join

6 from urllib.parse import urlparse

7 from django.utils import timezone

8 from typing import List

9 from django.utils.timezone import now

10 from rest_framework.test import APIClient

11 from jutil.drf_exceptions import transform_exception_to_drf

12 from jutil.modelfields import SafeCharField, SafeTextField

13 from jutil.middleware import logger as jutil_middleware_logger, ActivateUserProfileTimezoneMiddleware

14 import pytz

15 from django.conf import settings

16 from django.contrib.admin.models import LogEntry

17 from django.contrib.auth.models import User

18 from django.core.exceptions import ValidationError

19 from rest_framework.exceptions import ValidationError as DRFValidationError, ErrorDetail

20 from django.core.management.base import CommandParser # type: ignore

21 from django.db import models

22 from django.http.response import HttpResponse

23 from django.test import TestCase

24 from django.test.client import RequestFactory, Client

25 from django.utils.translation import override, gettext as _, gettext_lazy

26 from rest_framework.exceptions import NotAuthenticated

27 from jutil.admin import admin_log, admin_obj_url, admin_obj_link, ModelAdminBase, AdminLogEntryMixin

28 from jutil.auth import AuthUserMixin, get_auth_user, get_auth_user_or_none

29 from jutil.command import get_date_range_by_name, add_date_range_arguments, parse_date_range_arguments

30 from jutil.email import make_email_recipient_list

31 from jutil.middleware import EnsureOriginMiddleware, LogExceptionMiddleware, EnsureLanguageCookieMiddleware

32 from jutil.model import (

33 is_model_field_changed,

34 clone_model,

35 get_model_field_label_and_value,

36 get_object_or_none,

37 wait_object_or_none,

38)

39 from jutil.request import get_ip_info

40 from jutil.responses import CsvResponse

41 from jutil.testing import TestSetupMixin

42 from jutil.urls import modify_url

43 from jutil.xml import xml_to_dict, dict_to_element, _xml_filter_tag_name

44 from jutil.dates import (

45 add_month,

46 per_delta,

47 per_month,

48 this_week,

49 next_month,

50 next_week,

51 this_month,

52 last_month,

53 last_year,

54 last_week,

55 yesterday,

56 end_of_month,

57 this_year,

58 get_time_steps,

59 TIME_STEP_DAILY,

60)

61 from jutil.format import (

62 format_full_name,

63 format_xml,

64 format_xml_bytes,

65 format_timedelta,

66 dec1,

67 dec2,

68 dec3,

69 dec4,

70 dec5,

71 dec6,

72 format_table,

73 ucfirst_lazy,

74 strip_media_root,

75 get_media_full_path,

76 camel_case_to_underscore,

77 underscore_to_camel_case,

78 format_as_html_json,

79 format_dict_as_html,

80 choices_label,

81 is_media_full_path,

82)

83 from jutil.parse import parse_datetime, parse_bool, parse_datetime_or_none

84 from jutil.validators import (

85 fi_payment_reference_number,

86 se_ssn_validator,

87 se_ssn_filter,

88 fi_iban_validator,

89 se_iban_validator,

90 iban_filter_readable,

91 email_filter,

92 iban_validator,

93 iban_bank_info,

94 fi_company_org_id_validator,

95 email_validator,

96 fi_payment_reference_validator,

97 iso_payment_reference_validator,

98 fi_ssn_age,

99 se_clearing_code_bank_info,

100 ascii_filter,

101 ee_iban_validator,

102 be_iban_validator,

103 dk_iban_validator,

104 dk_iban_bank_info,

105 dk_clearing_code_bank_name,

106 country_code_sanitizer,

107 phone_sanitizer,

108 email_sanitizer,

109 fi_company_org_id_generator,

110 phone_validator,

111 passport_filter,

112 passport_validator,

113 passport_sanitizer,

114 country_code_validator,

115 validate_country_iban,

116 iban_bic,

117 validate_country_company_org_id,

118 fi_ssn_generator,

119 fi_ssn_validator,

120 bic_validator,

121 iban_generator,

122 bic_sanitizer,

123 filter_country_company_org_id,

124)

125 from xml.etree.ElementTree import Element

126 from xml.etree import ElementTree as ET

127 from django.contrib import admin

128

129

130 MY_CHOICE_1 = "1"

131 MY_CHOICE_2 = "2"

132 MY_CHOICES = (

133 (MY_CHOICE_1, "MY_CHOICE_1"),

134 (MY_CHOICE_2, "MY_CHOICE_2"),

135)

136

137 request_factory = RequestFactory()

138

139

140 class DummyLogHandler(logging.Handler):

141 msgs: List[str]

142

143 def __init__(self):

144 super().__init__()

145 self.msgs = []

146

147 def emit(self, record):

148 msg = self.format(record)

149 self.msgs.append(msg)

150

151

152 def dummy_time_zone_response(obj) -> HttpResponse:

153 tz = timezone.get_current_timezone()

154 return HttpResponse(str(tz).encode())

155

156

157 def dummy_admin_func_a(modeladmin, request, qs):

158 print("dummy_admin_func_a")

159

160

161 def dummy_admin_func_b(modeladmin, request, qs):

162 print("dummy_admin_func_b")

163

164

165 def dummy_middleware_get_response(obj) -> HttpResponse:

166 return HttpResponse(b"hello content")

167

168

169 class DummyUserProfile:

170 timezone = "Europe/Helsinki"

171

172

173 class MyCustomAdmin(ModelAdminBase):

174 max_history_length = 5

175 actions = (

176 dummy_admin_func_b,

177 dummy_admin_func_a,

178 )

179

180 def get_object(self, request, obj_id):

181 return self.model.objects.get(id=obj_id)

182

183

184 class Tests(TestCase, TestSetupMixin):

185 def setUp(self):

186 self.user = user = self.add_test_user("test@example.com", "test1234")

187 assert isinstance(user, User)

188 user.is_superuser = True

189 user.is_staff = True

190 user.save()

191 self.client = Client()

192

193 def test_api_client(self):

194 api_client = self.create_api_client()

195 self.assertTrue(isinstance(api_client, APIClient))

196

197 def test_payment_reference(self):

198 self.assertEqual(fi_payment_reference_number("100"), "1009")

199 invalids = [

200 "89182932u",

201 "0000002",

202 ]

203 for invalid in invalids:

204 try:

205 fi_payment_reference_number(invalid)

206 self.fail("invalid number but passed: {}".format(invalid))

207 except ValidationError:

208 pass

209

210 def test_format_full_name(self):

211 samples = [

212 ("Short", "Full Name", "Short Full Name"),

213 ("Short Middle Name Is Quite Long", "Full Name", "Short Full Name"),

214 ("Short-Middle Name Is Quite Long", "Full Name", "Short Full Name"),

215 ("Olga Demi", "Serpuhovitinova-Miettinen", "Olga Serpuhovitinova"),

216 ("Olga-Anne Demi", "Serpuhovitinovatsko", "Olga S"),

217 ]

218 for v in samples:

219 limited = format_full_name(v[0], v[1], 20)

220 # print('{} {} -> {} (was: {})'.format(v[0], v[1], v[2], limited))

221 self.assertEqual(v[2], limited)

222 try:

223 long_name = "19280309812083091829038190823081208301280381092830182038018203810283021"

224 format_full_name(long_name, long_name)

225 self.fail("format_full_name failed with long name")

226 except Exception:

227 pass

228

229 def test_parse_datetime(self):

230 pairs = [

231 ("2016-06-12", "2016-06-12T00:00:00+00:00"),

232 ("2016-06-12T01:00:00", "2016-06-12T01:00:00+00:00"),

233 ("2016-06-12 01:00:00", "2016-06-12T01:00:00+00:00"),

234 ("2016-06-12T01:00:00+00:00", "2016-06-12T01:00:00+00:00"),

235 ("2016-06-12 01:00:00+00:00", "2016-06-12T01:00:00+00:00"),

236 ]

237 for t_str_input, t_ref_str in pairs:

238 # parse_datetime

239 t = parse_datetime(t_str_input)

240 self.assertTrue(isinstance(t, datetime))

241 assert isinstance(t, datetime)

242 self.assertEqual(t.isoformat(), t_ref_str)

243 # parse_datetime_or_none

244 t = parse_datetime_or_none(t_str_input)

245 self.assertTrue(isinstance(t, datetime))

246 assert isinstance(t, datetime)

247 self.assertEqual(t.isoformat(), t_ref_str)

248

249 invalids = [

250 "12312313ew",

251 "2016-13-05",

252 ]

253 for t_str_input in invalids:

254 # parse_datetime

255 try:

256 parse_datetime(t_str_input)

257 self.fail("{} is not valid input for parse_datetime()".format(t_str_input))

258 except Exception:

259 pass

260 # parse_datetime_or_none

261 t = parse_datetime_or_none(t_str_input)

262 assert t is None

263

264 def test_add_month(self):

265 t = parse_datetime("2016-06-12T01:00:00")

266 self.assertTrue(isinstance(t, datetime))

267 assert isinstance(t, datetime)

268 self.assertEqual(t.isoformat(), "2016-06-12T01:00:00+00:00")

269 t2 = add_month(t)

270 self.assertEqual(t2.isoformat(), "2016-07-12T01:00:00+00:00")

271 t3 = add_month(t, 7)

272 self.assertEqual(t3.isoformat(), "2017-01-12T01:00:00+00:00")

273 t4 = add_month(t, -1)

274 self.assertEqual(t4.isoformat(), "2016-05-12T01:00:00+00:00")

275 time_now = datetime(2020, 6, 30, 15, 47, 23, 818646)

276 self.assertEqual(add_month(time_now, -4).isoformat(), "2020-02-29T15:47:23.818646")

277 self.assertEqual(add_month(time_now, 8).isoformat(), "2021-02-28T15:47:23.818646")

278 self.assertEqual(add_month(time_now, 0).isoformat(), "2020-06-30T15:47:23.818646")

279

280 def test_se_ssn(self):

281 se_ssn_validator("811228-9874")

282 se_ssn_validator("670919-9530")

283 with self.assertRaises(ValidationError):

284 se_ssn_validator("811228-9873")

285

286 def test_phone_numbers(self):

287 try:

288 phone_validator("+358456343767")

289 except Exception:

290 self.fail('phone_validator("+358456343767") should not raise Exception')

291 with self.assertRaisesMessage(ValidationError, _("Invalid phone number")):

292 phone_validator("214")

293 self.assertEqual(phone_sanitizer("214"), "")

294

295 def test_passport(self):

296 self.assertEqual(passport_filter("?ADsd-12312dsds"), "ADSD-12312DSDS")

297 passport_validator("21412312312")

298 with self.assertRaisesMessage(ValidationError, _("Invalid passport number")):

299 passport_validator("214")

300 self.assertEqual(passport_sanitizer("214"), "")

301 self.assertEqual(passport_sanitizer("21412312312"), "21412312312")

302

303 def test_country_code(self):

304 for cc in ["FI", "DK", "ES", "SE", "VN"]:

305 country_code_validator(cc)

306 with self.assertRaisesMessage(ValidationError, _("Invalid country code")):

307 country_code_validator("Finland")

308

309 def test_bic(self):

310 bic = iban_bic("FI21 1234 5600 0007 85")

311 self.assertEqual(bic, "NDEAFIHH")

312 self.assertEqual(bic_sanitizer("NDEAFIHH"), "NDEAFIHH")

313 self.assertEqual(bic_sanitizer("NDEAFIH"), "")

314

315 def test_org_id(self):

316 try:

317 validate_country_company_org_id("FI", "2084069-9")

318 except Exception:

319 self.fail("2084069-9 is valid org id")

320 with self.assertRaisesMessage(ValidationError, _("Invalid company organization ID")):

321 validate_country_company_org_id("FI", "2084069-8")

322

323 def test_validate_country_iban(self):

324 with self.assertRaisesMessage(ValidationError, _("Invalid IBAN account number")):

325 validate_country_iban("FI15616515616156", "SE")

326

327 def test_fi_ssn_generator(self):

328 self.assertEqual(len(fi_ssn_generator()), 6 + 1 + 4)

329 for min_year, max_year in [(1800, 1900), (1900, 2000), (2000, 2050)]:

330 for n in range(10):

331 ssn = fi_ssn_generator(min_year, max_year)

332 try:

333 fi_ssn_age(ssn)

334 fi_ssn_validator(ssn)

335 except Exception:

336 self.fail("{} is valid SSN".format(ssn))

337 fi_ssn_validator("110305+283X")

338 for ssn in ["9999-123F", "271138-670X", "090228+256X"]:

339 with self.assertRaisesMessage(ValidationError, _("Invalid personal identification number")):

340 fi_ssn_validator(ssn)

341 with self.assertRaises(ValidationError):

342 fi_ssn_generator(1700, 1799)

343 with self.assertRaises(ValidationError):

344 fi_ssn_generator(2100, 2200)

345

346 def test_iban(self):

347 with self.assertRaisesMessage(ValidationError, _("Invalid IBAN account number")):

348 iban_validator("")

349 with self.assertRaisesMessage(ValidationError, _("Invalid country code")):

350 iban_validator("XX")

351 with self.assertRaisesMessage(ValidationError, _("Invalid IBAN account number")):

352 iban_validator("FI2112345600000786")

353 bic_validator("HELSFIHH")

354 bic_validator("HELSFIHHXXX")

355 with self.assertRaisesMessage(ValidationError, _("Invalid bank BIC/SWIFT code")):

356 bic_validator("HELSFIH")

357 with self.assertRaisesMessage(ValidationError, _("Invalid bank BIC/SWIFT code")):

358 bic_validator("")

359 with self.assertRaisesMessage(ValidationError, _("Invalid bank BIC/SWIFT code")):

360 bic_validator("XX123123123112")

361 iban_validator("FI2112345600000785")

362 iban_validator("SE4550000000058398257466")

363 fi_iban_validator("FI2112345600000785")

364 se_iban_validator("SE4550000000058398257466")

365 ee_iban_validator("EE38 2200 2210 2014 5685")

366 be_iban_validator("BE68 5390 0754 7034")

367 with self.assertRaises(ValidationError):

368 fi_iban_validator("FI2112345600000784")

369 with self.assertRaises(ValidationError):

370 se_iban_validator("SE4550000000058398257465")

371 iban = "FI8847304720017517"

372 self.assertEqual(iban_filter_readable(iban), "FI88 4730 4720 0175 17")

373 self.assertEqual(iban_filter_readable(""), "")

374

375 def test_urls(self):

376 url = "http://yle.fi/uutiset/3-8045550?a=123&b=456"

377 self.assertEqual(

378 modify_url("http://yle.fi/uutiset/3-8045550?a=123&b=456", {"a": "123", "b": "456"}),

379 "http://yle.fi/uutiset/3-8045550?a=123&b=456",

380 )

381

382 def test_email_filter_and_validation(self):

383 emails = [

384 (" Asdsa@a-a.com ", "asdsa@a-a.com", True),

385 ("1asdsa@a-a2.com", "1asdsa@a-a2.com", True),

386 (" Asdsa@a-a ", "asdsa@a-a", False),

387 (" @a-a2.com", "@a-a2.com", False),

388 (" a-a2.com", "a-a2.com", False),

389 ("ää-a2@ää-a2.com", "ää-a2@ää-a2.com", False),

390 ("aaa.bbbbb@ccc-ddd.fi", "aaa.bbbbb@ccc-ddd.fi", True),

391 ]

392 for i, o, is_valid in emails:

393 # print('email_filter({}) -> {}'.format(i, email_filter(i)))

394 self.assertEqual(email_filter(i), o)

395 if is_valid:

396 email_validator(o)

397 else:

398 fail = False

399 try:

400 email_validator(o)

401 except ValidationError:

402 fail = True

403 self.assertTrue(fail, "{} is not valid email but passed validation".format(o))

404

405 def test_ip_info(self):

406 ip, cc, host = get_ip_info("213.214.146.142")

407 self.assertEqual(ip, "213.214.146.142")

408 if cc: 408 ↛ 410line 408 didn't jump to line 410, because the condition on line 408 was never false

409 self.assertEqual(cc, "FI")

410 if host: 410 ↛ exitline 410 didn't return from function 'test_ip_info', because the condition on line 410 was never false

411 self.assertEqual(host, "213214146142.edelkey.net")

412

413 def test_parse_xml(self):

414 # finvoice_201_example1.xml

415 xml_bytes = open(join(settings.BASE_DIR, "data/fi/finvoice_201_example1.xml"), "rb").read()

416 data = xml_to_dict(xml_bytes, value_key="value", attribute_prefix="_")

417 # pprint(data)

418 self.assertEqual(data["_Version"], "2.01")

419 self.assertEqual(data["InvoiceRow"][0]["ArticleIdentifier"], "12345")

420 self.assertEqual(data["InvoiceRow"][0]["DeliveredQuantity"]["value"], "2")

421 self.assertEqual(data["InvoiceRow"][0]["DeliveredQuantity"]["_QuantityUnitCode"], "kpl")

422 self.assertEqual(data["InvoiceRow"][1]["ArticleIdentifier"], "123456")

423

424 # parse_xml1.xml

425 xml_str = open(join(settings.BASE_DIR, "data/parse_xml1.xml"), "rt").read()

426 data = xml_to_dict(xml_str.encode())

427 # pprint(data)

428 ref_data = {

429 "@version": "1.2",

430 "A": [

431 {"@class": "x", "B": {"@": "hello", "@class": "x2"}},

432 {"@class": "y", "B": {"@": "world", "@class": "y2"}},

433 ],

434 "C": "value node",

435 }

436 self.assertEqual(ref_data, data)

437

438 # parse_xml1.xml / no attributes

439 xml_str = open(join(settings.BASE_DIR, "data/parse_xml1.xml"), "rt").read()

440 data = xml_to_dict(xml_str.encode(), parse_attributes=False)

441 # pprint(data)

442 ref_data = {"A": [{"B": "hello"}, {"B": "world"}], "C": "value node"}

443 self.assertEqual(ref_data, data)

444

445 # parse_xml2.xml / no attributes

446 xml_str = open(join(settings.BASE_DIR, "data/parse_xml2.xml"), "rt").read()

447 data = xml_to_dict(xml_str.encode(), ["VastausLoki", "LuottoTietoMerkinnat"], parse_attributes=False)

448 # pprint(data)

449 ref_data = {

450 "VastausLoki": {

451 "KysyttyHenkiloTunnus": "020685-1234",

452 "PaluuKoodi": "Palveluvastaus onnistui",

453 "SyyKoodi": "1",

454 }

455 }

456 self.assertEqual(ref_data, data)

457

458 def test_dict_to_xml(self):

459 data = {

460 "Doc": {

461 "@version": "1.2",

462 "A": [

463 {"@class": "x", "B": {"@": "hello", "@class": "x2"}},

464 {"@class": "y", "B": {"@": "world", "@class": "y2"}},

465 ],

466 "C": "value node",

467 "D": 123,

468 "E": ["abc"],

469 }

470 }

471 el = dict_to_element(data)

472 assert isinstance(el, Element)

473 xml_str = ET.tostring(el, encoding="utf8", method="xml").decode()

474 # 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>

475 data2 = xml_to_dict(xml_str.encode(), document_tag=True, array_tags=["E"], int_tags=["D"])

476 # print('')

477 # pprint(data)

478 # pprint(data2)

479 self.assertEqual(data2, data)

480

481 def test_dict_to_xml2(self):

482 self.assertEqual(_xml_filter_tag_name("TagName[0]"), "TagName")

483 self.assertEqual(_xml_filter_tag_name("TagName[1]"), "TagName")

484 self.assertEqual(_xml_filter_tag_name("TagName"), "TagName")

485 data = {

486 "Doc": {

487 "@version": "1.2",

488 "A": [

489 {"@class": "x", "B": {"@": "hello", "@class": "x2"}},

490 {"@class": "y", "B": {"@": "world", "@class": "y2"}},

491 ],

492 "C": "value node",

493 "D": 123,

494 "E": ["abc"],

495 "F": ["line 1", "line 2"],

496 }

497 }

498 el = dict_to_element(data)

499 assert isinstance(el, Element)

500 xml_str = ET.tostring(el, encoding="utf8", method="xml").decode()

501 data2 = xml_to_dict(xml_str.encode(), document_tag=True, array_tags=["E", "F"], int_tags=["D"])

502 self.assertEqual(data2, data)

503

504 def test_xml_to_dict(self):

505 xml_str = """<?xml version="1.0" encoding="utf-8"?>

506<Document>

507 <TxsSummry>

508 <TtlNtries>

509 <NbOfNtries>12</NbOfNtries>

510 </TtlNtries>

511 <TtlCdtNtries>

512 <NbOfNtries>34</NbOfNtries>

513 <Sum>1234.56</Sum>

514 </TtlCdtNtries>

515 <TtlDbtNtries>

516 <NbOfNtries>0</NbOfNtries>

517 <Sum>0</Sum>

518 </TtlDbtNtries>

519 </TxsSummry>

520</Document>"""

521 data = xml_to_dict(xml_str.encode(), document_tag=True, array_tags=[], int_tags=["NbOfNtries"])

522 # print('')

523 # pprint(data)

524 self.assertEqual(data["Document"]["TxsSummry"]["TtlNtries"]["NbOfNtries"], 12)

525 self.assertEqual(data["Document"]["TxsSummry"]["TtlCdtNtries"]["NbOfNtries"], 34)

526

527 def test_per_delta(self):

528 begin = datetime(2017, 9, 17, 11, 42)

529 end = begin + timedelta(days=4)

530 ref = [

531 (datetime(2017, 9, 17, 11, 42), datetime(2017, 9, 18, 11, 42)),

532 (datetime(2017, 9, 18, 11, 42), datetime(2017, 9, 19, 11, 42)),

533 (datetime(2017, 9, 19, 11, 42), datetime(2017, 9, 20, 11, 42)),

534 (datetime(2017, 9, 20, 11, 42), datetime(2017, 9, 21, 11, 42)),

535 ]

536 res = per_delta(begin, end, timedelta(days=1))

537 self.assertEqual(list(res), ref)

538

539 def test_per_month(self):

540 begin = datetime(2017, 9, 1, 0, 0)

541 res = list(per_month(begin, begin + timedelta(days=32)))

542 ref = [

543 (datetime(2017, 9, 1, 0, 0), datetime(2017, 10, 1, 0, 0)),

544 (datetime(2017, 10, 1, 0, 0), datetime(2017, 11, 1, 0, 0)),

545 ]

546 self.assertEqual(list(res), ref)

547

548 def test_dates(self):

549 t = datetime(2018, 1, 30)

550 b, e = this_week(t)

551 self.assertEqual(b, pytz.utc.localize(datetime(2018, 1, 29)))

552 self.assertEqual(e, pytz.utc.localize(datetime(2018, 2, 5)))

553 b, e = this_month(t)

554 self.assertEqual(b, pytz.utc.localize(datetime(2018, 1, 1)))

555 self.assertEqual(e, pytz.utc.localize(datetime(2018, 2, 1)))

556 b, e = next_week(t)

557 self.assertEqual(b, pytz.utc.localize(datetime(2018, 2, 5)))

558 self.assertEqual(e, pytz.utc.localize(datetime(2018, 2, 12)))

559

560 def test_named_date_ranges(self):

561 t = datetime(2018, 5, 31)

562 t_tz = pytz.utc.localize(t)

563 named_ranges = [

564 ("last_year", last_year(t)),

565 ("last_month", last_month(t)),

566 ("last_week", last_week(t)),

567 ("this_year", this_year(t)),

568 ("this_month", this_month(t)),

569 ("this_week", this_week(t)),

570 ("yesterday", yesterday(t)),

571 ("today", yesterday(t + timedelta(hours=24))),

572 ]

573 day_ranges = [7, 15, 30, 60, 90]

574 for days in day_ranges:

575 named_ranges.append(

576 ("plus_minus_{}d".format(days), (t_tz - timedelta(days=days), t_tz + timedelta(days=days)))

577 )

578 named_ranges.append(("prev_{}d".format(days), (t_tz - timedelta(days=days), t_tz)))

579 named_ranges.append(("next_{}d".format(days), (t_tz, t_tz + timedelta(days=days))))

580 for name, res in named_ranges:

581 # print('testing', name)

582 self.assertEqual(get_date_range_by_name(name, t), res)

583

584 def test_time_steps(self):

585 t = parse_datetime("2020-08-24 23:28:36.503174")

586 this_week_daily = [

587 "2020-08-24T00:00:00+00:00 2020-08-25T00:00:00+00:00",

588 "2020-08-25T00:00:00+00:00 2020-08-26T00:00:00+00:00",

589 "2020-08-26T00:00:00+00:00 2020-08-27T00:00:00+00:00",

590 "2020-08-27T00:00:00+00:00 2020-08-28T00:00:00+00:00",

591 "2020-08-28T00:00:00+00:00 2020-08-29T00:00:00+00:00",

592 "2020-08-29T00:00:00+00:00 2020-08-30T00:00:00+00:00",

593 "2020-08-30T00:00:00+00:00 2020-08-31T00:00:00+00:00",

594 ]

595 ix = 0

596 for begin, end in get_time_steps(TIME_STEP_DAILY, *this_week(t)):

597 self.assertEqual("{} {}".format(begin.isoformat(), end.isoformat()), this_week_daily[ix])

598 ix += 1

599

600 def test_bank_info(self):

601 ac = "FI8847304720017517"

602 inf = iban_bank_info(ac)

603 self.assertEqual(inf[0], "POPFFI22")

604 self.assertEqual(inf[1], "POP-Pankki")

605

606 ac = ""

607 inf = iban_bank_info(ac)

608 self.assertEqual(inf[0], "")

609 self.assertEqual(inf[1], "")

610

611 ac = "BE75270187592710"

612 inf = iban_bank_info(ac)

613 self.assertEqual(inf[0], "GEBABEBB")

614 self.assertEqual(inf[1], "BNP Paribas Fortis")

615 ac = "BE58465045170210"

616 inf = iban_bank_info(ac)

617 self.assertEqual(inf[0], "KREDBEBB")

618 self.assertEqual(inf[1], "KBC Bank")

619 ac = "BE11000123456748"

620 inf = iban_bank_info(ac)

621 self.assertEqual(inf[0], "BPOTBEB1")

622 self.assertEqual(inf[1], "bpost bank")

623

624 def test_org_id_fi(self):

625 valids = [

626 "FI01098230",

627 "FI-01098230",

628 "0109823-0",

629 "2084069-9",

630 ]

631 invalids = [

632 "2084069-1",

633 "SE2084069-1",

634 ]

635 co_filtered = [

636 ("FI", "FI01098230", "0109823-0"),

637 ("FI", "FI-01098230", "0109823-0"),

638 ("FI", "0109823-0", "0109823-0"),

639 ("FI", "2084069-9", "2084069-9"),

640 ("SE", "01098230", "01098230"),

641 ("SE", "20840699", "20840699"),

642 ]

643 for cc, org, val in co_filtered:

644 self.assertEqual(filter_country_company_org_id(cc, org), val)

645 validate_country_company_org_id(cc, org)

646 for valid in valids:

647 fi_company_org_id_validator(valid)

648 for invalid in invalids:

649 try:

650 fi_company_org_id_validator(invalid)

651 self.fail("{} passed as valid FI-org".format(invalid))

652 except ValidationError:

653 # print('ok')

654 pass

655 for n in range(10):

656 v0 = fi_company_org_id_generator()

657 # print(v0)

658 fi_company_org_id_validator(v0)

659

660 def test_reference_number_validators(self):

661 valid_fi_refs = [

662 "302300",

663 "202196",

664 "302290",

665 ]

666 for ref_no in valid_fi_refs:

667 fi_payment_reference_validator(ref_no)

668

669 invalid_fi_refs = [

670 "302301",

671 "202195",

672 "302291",

673 ]

674 for ref_no in invalid_fi_refs:

675 try:

676 fi_payment_reference_validator(ref_no)

677 self.assertFalse(True, "{} should have failed validation".format(ref_no))

678 except ValidationError:

679 pass

680

681 valid_iso_refs = [

682 "RF92 1229",

683 "RF11 1232",

684 "RF48 1245",

685 ]

686 for ref_no in valid_iso_refs:

687 iso_payment_reference_validator(ref_no)

688

689 invalid_iso_refs = [

690 "RF92 1229}",

691 ]

692 for ref_no in invalid_iso_refs:

693 with self.assertRaisesMessage(ValidationError, "Invalid payment reference"):

694 iso_payment_reference_validator(ref_no)

695

696 def test_fi_ssn_age(self):

697 samples = [

698 (date(2018, 12, 20), "231298-965X", 19),

699 (date(2018, 12, 22), "231298-965X", 19),

700 (date(2018, 12, 23), "231298-965X", 20),

701 (date(2018, 12, 24), "231298-965X", 20),

702 ]

703 for date_now, ssn, age in samples:

704 self.assertEqual(

705 fi_ssn_age(ssn, date_now),

706 age,

707 msg="{} age is {} on {} but fi_ssn_age result was {}".format(

708 ssn, age, date_now, fi_ssn_age(ssn, date_now)

709 ),

710 )

711

712 def test_se_banks(self):

713 self.assertEqual(se_clearing_code_bank_info("6789"), ("Handelsbanken", 9))

714 se_iban_validator("SE45 5000 0000 0583 9825 7466")

715 with self.assertRaisesMessage(ValidationError, _("Invalid IBAN account number")):

716 se_iban_validator("")

717 with self.assertRaisesMessage(ValidationError, _("Invalid IBAN account number")):

718 se_iban_validator("XX45 5000 0000 0583 9825 7466")

719 with self.assertRaisesMessage(ValidationError, _("Invalid IBAN account number")):

720 se_iban_validator("SE45 5000 0000 0583 9825")

721 self.assertEqual(se_clearing_code_bank_info("9500"), ("Nordea AB", 10))

722 an = "957033025420"

723 bank_name, acc_digits = se_clearing_code_bank_info(an)

724 self.assertEqual(bank_name, "Sparbanken Syd")

725 self.assertGreaterEqual(len(an) - 4, acc_digits)

726

727 def test_dk_banks(self):

728 an = "DK50 0040 0440 1162 43"

729 dk_iban_validator(an)

730 bic, name = dk_iban_bank_info(an)

731 self.assertEqual(name, "Nordea")

732 an = "8114 0008874093"

733 name = dk_clearing_code_bank_name(an)

734 self.assertEqual(name, "Nykredit Bank")

735 an = "DK2520006893703029"

736 name = dk_clearing_code_bank_name(an)

737 self.assertEqual(name, "Nordea")

738

739 def test_ascii_filter(self):

740 pairs = [

741 ("Åke va Källe o Öring", "Ake va Kalle o Oring"),

742 ("Tôi đang đi mua sắm", "Toi ang i mua sam"),

743 ("HELÉN FRANZÉN", "HELEN FRANZEN"),

744 ]

745 for a, b in pairs:

746 self.assertEqual(ascii_filter(a), b, 'ascii_filter("{}") != "{}"'.format(b, ascii_filter(a)))

747

748 def test_l10n(self):

749 with override("fi"):

750 msg = _("“%(value)s” value has an invalid format. It must be in YYYY-MM-DD HH:MM[:ss[.uuuuuu]][TZ] format.")

751 if "muoto ei kelpaa" not in msg: 751 ↛ 752line 751 didn't jump to line 752, because the condition on line 751 was never true

752 print(msg)

753 self.assertTrue("muoto ei kelpaa" in msg)

754

755 try:

756 parse_bool("hello")

757 except ValidationError as e:

758 self.assertEqual(str(e), "['hello ei ole yksi valittavissa olevista arvoista']")

759

760 def test_sanitizers(self):

761 self.assertEqual(country_code_sanitizer("kods"), "")

762 self.assertEqual(country_code_sanitizer("fi"), "FI")

763 self.assertEqual(phone_sanitizer("+13146094459"), "+13146094459")

764 self.assertEqual(phone_sanitizer("13146094459"), "13146094459")

765 self.assertEqual(phone_sanitizer("+13146094459A"), "+13146094459")

766 self.assertEqual(email_sanitizer("test@example.com"), "test@example.com")

767 self.assertEqual(email_sanitizer("testexample.com"), "")

768

769 def test_format_dict_as_html(self):

770 a = {"b": 1, "c": {"@testVariable": "123"}}

771 res = "<pre>B: 1\nC:\n Test variable: 123\n\n</pre>"

772 self.assertEqual(format_dict_as_html(a), res)

773

774 def test_format_xml(self):

775 assert settings.XMLLINT_PATH, 'add e.g. XMLLINT_PATH = "/usr/bin/xmllint" to settings.py'

776 src = "<ApplicationRequest> <CustomerId>1</CustomerId> <Command>DownloadFileList</Command><Timestamp>2019-11-27T04:32:18.613452+02:00</Timestamp><Environment>PRODUCTION</Environment></ApplicationRequest>"

777 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'

778 dst = format_xml(src)

779 self.assertEqual(dst, dst_ref)

780 dst = format_xml_bytes(src.encode())

781 self.assertEqual(dst, dst_ref.encode())

782

783 def test_parse_sftp(self):

784 test_cases = [

785 ("sftp://jani@kajala.com", ["jani", None, "kajala.com", ""]),

786 ("sftp://jani.kajala:1231!@kajala.com", ["jani.kajala", "1231!", "kajala.com", ""]),

787 ("sftp://jani:1231!@kajala.com/my/dir", ["jani", "1231!", "kajala.com", "/my/dir"]),

788 ("sftp://jani.kajala:1231!@kajala.com/my/dir", ["jani.kajala", "1231!", "kajala.com", "/my/dir"]),

789 ]

790 for connection, ref_res in test_cases:

791 res = urlparse(connection)

792 res_list = [res.username, res.password, res.hostname, res.path]

793 self.assertListEqual(res_list, ref_res, 'SFTP connection string "{}" parsed incorrectly'.format(connection))

794

795 def test_admin(self):

796 obj = self.user

797 admin_log([obj], "Hello, world")

798 admin_log([obj], "Hello, world", user=self.user, ip="127.0.0.1")

799 admin_log(obj, "Hello, world", user=self.user, ip="127.0.0.1")

800 e = LogEntry.objects.all().filter(object_id=obj.id).last()

801 self.assertIsNotNone(e)

802 assert isinstance(e, LogEntry)

803 self.assertEqual(e.change_message, "Hello, world")

804 self.assertEqual(admin_obj_url(obj, "admin:auth_user_change"), "/admin/auth/user/{}/change/".format(obj.id))

805 self.assertEqual(admin_obj_url(None, "admin:auth_user_change"), "")

806 self.assertEqual(admin_obj_link(None, "admin:auth_user_change"), "")

807 self.assertEqual(admin_obj_url(obj), "/admin/auth/user/{}/change/".format(obj.id))

808 self.assertEqual(admin_obj_url(e), "/admin/admin/logentry/{}/change/".format(e.id))

809 link = admin_obj_link(obj, "User", "admin:auth_user_change")

810 self.assertEqual(link, "<a href='/admin/auth/user/{}/change/'>User</a>".format(obj.id))

811

812 def test_cmd_parser(self):

813 parser = CommandParser()

814 add_date_range_arguments(parser)

815 argv = parser.parse_args(["--begin", "2019-06-25", "--end", "2020-02-01"])

816 options = argv.__dict__

817 begin, end, steps = parse_date_range_arguments(options)

818 self.assertEqual(begin, pytz.utc.localize(datetime(2019, 6, 25)))

819 self.assertEqual(end, pytz.utc.localize(datetime(2020, 2, 1)))

820

821 def test_format_timedelta(self):

822 self.assertEqual(format_timedelta(timedelta(seconds=90)), "1min30s")

823 self.assertEqual(format_timedelta(timedelta(seconds=3600 + 90)), "1h1min30s")

824 self.assertEqual(format_timedelta(timedelta(seconds=90), minutes_label="min ", seconds_label="s "), "1min 30s")

825 self.assertEqual(

826 format_timedelta(timedelta(seconds=3600 + 90), hours_label="h ", minutes_label="min ", seconds_label="s "),

827 "1h 1min 30s",

828 )

829 self.assertEqual(format_timedelta(timedelta(seconds=90), seconds_label=""), "1min")

830 self.assertEqual(format_timedelta(timedelta(seconds=0.090), seconds_label="s"), "0.090s")

831

832 def test_dec123456(self):

833 self.assertEqual(dec1(Decimal("1.2345678")), Decimal("1.2"))

834 self.assertEqual(dec2(Decimal("1.2345678")), Decimal("1.23"))

835 self.assertEqual(dec3(Decimal("1.2345678")), Decimal("1.235"))

836 self.assertEqual(dec4(Decimal("1.2345678")), Decimal("1.2346"))

837 self.assertEqual(dec5(Decimal("1.2345678")), Decimal("1.23457"))

838 self.assertEqual(dec6(Decimal("1.2345678")), Decimal("1.234568"))

839

840 def test_model_funcs(self):

841 admin_log([self.user], "test msg 1")

842 obj = LogEntry.objects.all().order_by("-pk").last()

843 assert isinstance(obj, LogEntry)

844 self.assertFalse(is_model_field_changed(obj, "change_message"))

845 obj.change_message = "hello world"

846 self.assertTrue(is_model_field_changed(obj, "change_message"))

847 obj.save()

848 self.assertFalse(is_model_field_changed(obj, "change_message"))

849 obj2 = clone_model(obj)

850 assert isinstance(obj2, LogEntry)

851 self.assertEqual(obj.change_message, obj2.change_message)

852 self.assertGreater(obj2.pk, obj.pk)

853 label, val = get_model_field_label_and_value(obj, "action_time")

854 self.assertEqual(label, _("action time"))

855 self.assertEqual(str(obj.action_time), val)

856 obj_b = get_object_or_none(obj.__class__, id=obj.id)

857 self.assertEqual(obj_b.id, obj.id)

858 obj_b = get_object_or_none(obj.__class__, id=-1)

859 self.assertIsNone(obj_b)

860

861 def test_format_table(self):

862 a = [

863 ["date", "description", "count", "unit price", "total price"],

864 [date(2019, 12, 15), "oranges", 1000, dec2("0.99"), dec2("990.00")],

865 [date(2020, 1, 3), "apples", 4, dec2("1.10"), dec2("4.40")],

866 [date(2020, 11, 3), "apples", 5, dec2("10.10"), dec2("50.50")],

867 ]

868 out = format_table(a, has_label_row=True, max_col=10)

869 out_ref = """

870---------------------------------------------------

871| date|descript..|count|unit price|total pr..|

872---------------------------------------------------

873|2019-12-15| oranges| 1000| 0.99| 990.00|

874|2020-01-03| apples| 4| 1.10| 4.40|

875|2020-11-03| apples| 5| 10.10| 50.50|

876---------------------------------------------------

877 """.strip()

878 self.assertEqual(out, out_ref)

879

880 out = format_table(a, has_label_row=True, max_col=20)

881 out_ref = """

882-----------------------------------------------------

883| date|description|count|unit price|total price|

884-----------------------------------------------------

885|2019-12-15| oranges| 1000| 0.99| 990.00|

886|2020-01-03| apples| 4| 1.10| 4.40|

887|2020-11-03| apples| 5| 10.10| 50.50|

888-----------------------------------------------------

889 """.strip()

890 self.assertEqual(out, out_ref)

891

892 out = format_table(a, has_label_row=True, max_col=20, col_sep=" | ")

893 out_ref = """

894-------------------------------------------------------------

895| date | description | count | unit price | total price|

896-------------------------------------------------------------

897|2019-12-15 | oranges | 1000 | 0.99 | 990.00|

898|2020-01-03 | apples | 4 | 1.10 | 4.40|

899|2020-11-03 | apples | 5 | 10.10 | 50.50|

900-------------------------------------------------------------

901 """.strip()

902 self.assertEqual(out, out_ref)

903

904 out = format_table(a, has_label_row=True, max_col=20, col_sep=" | ", left_align=[1])

905 out_ref = """

906-------------------------------------------------------------

907| date | description | count | unit price | total price|

908-------------------------------------------------------------

909|2019-12-15 | oranges | 1000 | 0.99 | 990.00|

910|2020-01-03 | apples | 4 | 1.10 | 4.40|

911|2020-11-03 | apples | 5 | 10.10 | 50.50|

912-------------------------------------------------------------

913 """.strip()

914 self.assertEqual(out, out_ref)

915

916 out = format_table(a, has_label_row=True, max_col=20, col_sep=" | ", left_align=[1], max_line=50)

917 out_ref = """

918-------------------------------------------------

919| date | description | count | unit price|..

920-------------------------------------------------

921|2019-12-15 | oranges | 1000 | 0.99|..

922|2020-01-03 | apples | 4 | 1.10|..

923|2020-11-03 | apples | 5 | 10.10|..

924-------------------------------------------------

925 """.strip()

926 self.assertEqual(out, out_ref)

927

928 out = format_table(a, left_align=[1], center_align=[0, 2, 3, 4], max_col=50)

929 out_ref = """

930-----------------------------------------------------

931| date |description|count|unit price|total price|

932|2019-12-15|oranges |1000 | 0.99 | 990.00 |

933|2020-01-03|apples | 4 | 1.10 | 4.40 |

934|2020-11-03|apples | 5 | 10.10 | 50.50 |

935-----------------------------------------------------

936 """.strip()

937 self.assertEqual(out, out_ref)

938

939 def test_ucfirst_lazy(self):

940 s = gettext_lazy(ucfirst_lazy("missing value"))

941 s_ref = gettext_lazy("Missing value")

942 s_en = "Missing value"

943 s_fi = "Puuttuva arvo"

944 with override("fi"):

945 self.assertEqual(s, s_fi)

946 self.assertEqual(s, s_ref)

947 with override("en"):

948 self.assertEqual(s, s_en)

949 self.assertEqual(s, s_ref)

950

951 def test_media_paths(self):

952 media_root1 = os.path.join(settings.MEDIA_ROOT, "path1/path2")

953 media_root2 = os.path.join(settings.MEDIA_ROOT, "path3") + "/"

954 test_paths = [

955 (True, os.path.join(media_root1, "test1.file"), "path1/path2/test1.file"),

956 (False, os.path.join("/diff/path", "test2.file"), "/diff/path/test2.file"),

957 (True, os.path.join(media_root2, "test3.file"), "path3/test3.file"),

958 ]

959 for is_media, src, dst in test_paths:

960 self.assertEqual(is_media_full_path(src), is_media)

961 if is_media:

962 self.assertEqual(strip_media_root(src), dst)

963 self.assertEqual(get_media_full_path(dst), src)

964 self.assertEqual(get_media_full_path(dst), get_media_full_path(get_media_full_path(dst)))

965

966 def test_end_of_month(self):

967 helsinki = pytz.timezone("Europe/Helsinki")

968 # 1

969 time_now = datetime(2020, 6, 5, 15, 47, 23, 818646)

970 eom = end_of_month(time_now, tz=helsinki)

971 eom_ref = helsinki.localize(datetime(2020, 6, 30, 23, 59, 59, 999999))

972 self.assertEqual(eom, eom_ref)

973 # 2

974 time_now = datetime(2020, 7, 5, 15, 47, 23, 818646)

975 eom = end_of_month(time_now, tz=helsinki)

976 eom_ref = helsinki.localize(datetime(2020, 7, 31, 23, 59, 59, 999999))

977 self.assertEqual(eom, eom_ref)

978 # 3

979 time_now = datetime(2020, 6, 5, 15, 47, 23, 818646)

980 eom = end_of_month(time_now, n=1, tz=helsinki)

981 eom_ref = helsinki.localize(datetime(2020, 7, 31, 23, 59, 59, 999999))

982 self.assertEqual(eom, eom_ref)

983 # 4

984 time_now = datetime(2020, 7, 5, 15, 47, 23, 818646)

985 eom = end_of_month(time_now, n=-2, tz=helsinki)

986 eom_ref = helsinki.localize(datetime(2020, 5, 31, 23, 59, 59, 999999))

987 self.assertEqual(eom, eom_ref)

988

989 def test_iban_generator_and_validator(self):

990 test_ibans = [

991 "MD7289912714638112731113",

992 "IS363252851674877586492113",

993 "HR5125000099152386224",

994 "CZ4750515755735423825528",

995 "FI3253811381259333",

996 "FR3212739000501869481882E94",

997 ]

998 for iban in test_ibans:

999 iban_validator(iban)

1000 for cc in ["", "FI", "SE"]:

1001 for n in range(100):

1002 acc = iban_generator(cc)

1003 try:

1004 iban_validator(acc)

1005 except Exception as e:

1006 print("iban_generator() returned", acc, "but iban_validator() raised exception", e)

1007 self.fail(

1008 "iban_validator(iban_generator()) should not raise Exception, account number was {}".format(acc)

1009 )

1010 with self.assertRaisesMessage(ValidationError, _("Invalid country code")):

1011 iban_generator("XX")

1012 with self.assertRaisesMessage(

1013 ValidationError, _("IBAN checksum generation does not support >26 character IBANs")

1014 ):

1015 iban_generator("AL")

1016

1017 def test_make_email_recipient(self):

1018 email_tests = [

1019 {

1020 "list": [

1021 ("Jani Kajala", "kajala@example.com"),

1022 '"Jani Kajala" <kajala@example.com>',

1023 "<kajala@example.com>",

1024 "kajala@example.com",

1025 ],

1026 "result": [

1027 ("Jani Kajala", "kajala@example.com"),

1028 ("Jani Kajala", "kajala@example.com"),

1029 ("kajala@example.com", "kajala@example.com"),

1030 ("kajala@example.com", "kajala@example.com"),

1031 ],

1032 }

1033 ]

1034 for et in email_tests:

1035 res = make_email_recipient_list(et["list"])

1036 self.assertListEqual(res, et["result"])

1037

1038 def test_choices(self):

1039 val = choices_label(MY_CHOICES, MY_CHOICE_1)

1040 self.assertEqual(val, "MY_CHOICE_1")

1041

1042 def test_camel_case(self):

1043 pairs = [

1044 ("camelCaseWord", "camel_case_word"),

1045 ("camelCase", "camel_case"),

1046 ("camel", "camel"),

1047 ("camelCCase", "camel_c_case"),

1048 ]

1049 for cc, us in pairs:

1050 self.assertEqual(camel_case_to_underscore(cc), us)

1051 self.assertEqual(cc, underscore_to_camel_case(us))

1052

1053 def create_dummy_request(self, path: str = "/admin/login/"):

1054 request = request_factory.get(path)

1055 request.user = self.user # type: ignore

1056 request.user.profile = DummyUserProfile() # type: ignore

1057 return request

1058

1059 def test_model_admin_base(self):

1060 # test that actions sorting by name works

1061 request = self.create_dummy_request()

1062 user = self.user

1063 model_admin = MyCustomAdmin(LogEntry, admin.site)

1064 res = model_admin.get_actions(request)

1065 self.assertEqual(list(res.items())[0][0], "dummy_admin_func_a", "ModelAdminBase.get_actions sorting failed")

1066 self.assertEqual(list(res.items())[1][0], "dummy_admin_func_b", "ModelAdminBase.get_actions sorting failed")

1067

1068 # create 10 LogEntry for test user, 5 with text "VisibleLogMessage" and 5 "InvisibleLogMessage"

1069 # then check that "VisibleLogMessage" log entries are not visible since max_history_length = 5

1070 LogEntry.objects.filter(object_id=user.id).delete()

1071 for n in range(5):

1072 admin_log([user], "InvisibleLogMessage")

1073 for n in range(5):

1074 admin_log([user], "VisibleLogMessage")

1075 self.assertEqual(LogEntry.objects.filter(object_id=user.id).count(), 10)

1076 history_url = "/admin/auth/user/{}/history/".format(user.id)

1077 c = self.client

1078 c.get(history_url, follow=True)

1079 c.post("/admin/login/", {"username": "test@example.com", "password": "test1234"})

1080 res = c.get(history_url)

1081 content = res.content.decode()

1082 assert isinstance(content, str)

1083 self.assertEqual(content.count("VisibleLogMessage"), 5)

1084 self.assertEqual(content.count("InvisibleLogMessage"), 0)

1085

1086 def test_admin_log_entry_mixin(self):

1087 user = self.user

1088 AdminLogEntryMixin.fields_changed(user, ["username"], who=None)

1089 e = LogEntry.objects.filter(object_id=user.id).last()

1090 assert isinstance(e, LogEntry)

1091 self.assertEqual(e.change_message, 'username: "test@example.com"')

1092

1093 def test_auth(self):

1094 req = self.create_dummy_request()

1095 get_auth_user(req) # type: ignore

1096 req.user = None

1097 self.assertIsNone(get_auth_user_or_none(req))

1098 try:

1099 get_auth_user(req) # type: ignore

1100 self.fail("get_auth_user fail")

1101 except NotAuthenticated:

1102 pass

1103 try:

1104 model_admin = AuthUserMixin()

1105 model_admin.request = req

1106 user = model_admin.auth_user

1107 self.fail("get_auth_user fail")

1108 except NotAuthenticated:

1109 pass

1110

1111 def test_middleware(self):

1112 # EnsureOriginMiddleware

1113 request = self.create_dummy_request("/admin/login/")

1114 EnsureOriginMiddleware(dummy_middleware_get_response)(request)

1115 self.assertIn("HTTP_ORIGIN", request.META)

1116 self.assertEqual(request.META["HTTP_ORIGIN"], request.get_host())

1117

1118 # LogExceptionMiddleware

1119 dummy_log = DummyLogHandler()

1120 jutil_middleware_logger.addHandler(dummy_log)

1121 try:

1122 raise Exception("Dummy exception, ignore this")

1123 except Exception as e:

1124 mw = LogExceptionMiddleware(dummy_middleware_get_response)

1125 mw.process_exception(request, e)

1126 msg = dummy_log.msgs.pop()

1127 self.assertIn("Exception: Dummy exception, ignore this", msg)

1128 self.assertIn("user=test@example.com", msg)

1129 jutil_middleware_logger.removeHandler(dummy_log)

1130

1131 # EnsureLanguageCookieMiddleware

1132 lang_cookie_tests = [

1133 ("/admin/login/", settings.LANGUAGE_CODE),

1134 ("/admin/login/?django_language=fi", "fi"),

1135 ]

1136 for request_path, lang_code in lang_cookie_tests:

1137 request = self.create_dummy_request(request_path)

1138 mw = EnsureLanguageCookieMiddleware(dummy_middleware_get_response)

1139 res = mw(request)

1140 assert isinstance(res, HttpResponse)

1141 self.assertEqual(res.status_code, 200)

1142 self.assertIn("django_language", request.COOKIES)

1143 self.assertIn(request.COOKIES["django_language"], lang_code)

1144 self.assertIn("django_language", res.cookies)

1145 self.assertEqual(

1146 str(res.cookies["django_language"]), "Set-Cookie: django_language={}; Path=/".format(lang_code)

1147 )

1148

1149 # ActivateUserProfileTimezoneMiddleware

1150 user = self.user

1151 self.assertTrue(user.is_authenticated)

1152 user.profile.timezone = "Europe/Helsinki"

1153 with timezone.override(pytz.timezone("America/Chicago")):

1154 request = self.create_dummy_request("/admin/login/")

1155 mw = ActivateUserProfileTimezoneMiddleware(dummy_time_zone_response)

1156 res = mw(request)

1157 content = res.content.decode()

1158 self.assertEqual(content, user.profile.timezone)

1159

1160 def test_safe_fields(self):

1161 class TestModel(models.Model):

1162 cf = SafeCharField(max_length=256)

1163 tf = SafeTextField()

1164

1165 obj = TestModel()

1166 data = 'hello world <script>alert("popup")<script>'

1167 data_ref = "hello world "

1168 for f in obj._meta.fields:

1169 if f.name in ["cf", "tf"]:

1170 f.save_form_data(obj, data)

1171 self.assertEqual(obj.cf, data_ref)

1172 self.assertEqual(obj.tf, data_ref)

1173

1174 def test_responses(self):

1175 a = [

1176 ["date", "description", "count", "unit price", "total price"],

1177 [date(2019, 12, 15), "oranges", 1000, dec2("0.99"), dec2("990.00")],

1178 [date(2020, 1, 3), "apples", 4, dec2("1.10"), dec2("4.40")],

1179 [date(2020, 11, 3), "apples", 5, dec2("10.10"), dec2("50.50")],

1180 ]

1181 res = CsvResponse(a, "test.csv")

1182 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"

1183 self.assertEqual(content_ref, res.content)

1184 print(res.content.decode())

1185

1186 def test_wait_object_or_none(self):

1187 admin_log([self.user], "Hello, world")

1188 e = LogEntry.objects.all().filter(object_id=self.user.id).last()

1189 assert isinstance(e, LogEntry)

1190 e_id = e.id

1191 obj = wait_object_or_none(LogEntry, id=e_id)

1192 self.assertIsNotNone(obj)

1193 self.assertEqual(obj.id, e_id)

1194 t0 = now()

1195 obj = wait_object_or_none(LogEntry, timeout=1.0, sleep_interval=0.1, id=e_id + 1)

1196 t1 = now()

1197 self.assertIsNone(obj)

1198 self.assertGreater(t1 - t0, timedelta(seconds=0.99))

1199

1200 def test_format_as_html_json(self):

1201 pairs = [

1202 (None, "null"),

1203 ({"a": 1}, "{<br/>&nbsp;&nbsp;&nbsp;&nbsp;&quot;a&quot;:&nbsp;1<br/>}"),

1204 (

1205 {"script": "<script>window.alert()</script>"},

1206 "{<br/>&nbsp;&nbsp;&nbsp;&nbsp;&quot;script&quot;:&nbsp;&quot;&lt;script&gt;window.alert()&lt;/script&gt;&quot;<br/>}",

1207 ),

1208 ]

1209 for val, html in pairs:

1210 self.assertEqual(format_as_html_json(val), html)

1211

1212 def test_parse_bool(self):

1213 refs_true = [

1214 True,

1215 "True",

1216 "1",

1217 1,

1218 "true",

1219 "yes",

1220 ]

1221 refs_false = [

1222 False,

1223 "False",

1224 "0",

1225 0,

1226 "false",

1227 "no",

1228 ]

1229 for ref in refs_true:

1230 self.assertTrue(parse_bool(ref))

1231 for ref in refs_false:

1232 self.assertFalse(parse_bool(ref))

1233

1234 def test_exception_convert(self):

1235 e = transform_exception_to_drf(ValidationError({"hello": "world"}))

1236 assert isinstance(e, DRFValidationError)

1237 self.assertTrue(isinstance(e, DRFValidationError))

1238 self.assertIn("hello", e.detail) # type: ignore

1239 self.assertEqual(e.detail["hello"], [ErrorDetail(string="world", code="invalid")]) # type: ignore

1240

1241

1242 dummy_admin_func_a.short_description = "A" # type: ignore

1243 dummy_admin_func_b.short_description = "B" # type: ignore

1244

1245 admin.site.unregister(User)

1246 admin.site.register(User, MyCustomAdmin)

1247 admin.site.register(LogEntry, MyCustomAdmin)