Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1import logging 

2import os 

3from datetime import datetime, timedelta, date 

4from decimal import Decimal 

5from os.path import join 

6from urllib.parse import urlparse 

7from django.utils import timezone 

8from typing import List 

9from django.utils.timezone import now 

10from rest_framework.test import APIClient 

11 

12from jutil.drf_exceptions import transform_exception_to_drf 

13from jutil.modelfields import SafeCharField, SafeTextField 

14from jutil.middleware import logger as jutil_middleware_logger, ActivateUserProfileTimezoneMiddleware 

15import pytz 

16from django.conf import settings 

17from django.contrib.admin.models import LogEntry 

18from django.contrib.auth.models import User 

19from django.core.exceptions import ValidationError 

20from rest_framework.exceptions import ValidationError as DRFValidationError, ErrorDetail 

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

22from django.db import models 

23from django.http.response import HttpResponse 

24from django.test import TestCase 

25from django.test.client import RequestFactory, Client 

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

27from rest_framework.exceptions import NotAuthenticated 

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

29from jutil.auth import AuthUserMixin, get_auth_user, get_auth_user_or_none 

30from jutil.command import get_date_range_by_name, add_date_range_arguments, parse_date_range_arguments 

31from jutil.email import make_email_recipient_list 

32from jutil.middleware import EnsureOriginMiddleware, LogExceptionMiddleware, EnsureLanguageCookieMiddleware 

33from jutil.model import is_model_field_changed, clone_model, get_model_field_label_and_value, get_object_or_none, \ 

34 wait_object_or_none 

35from jutil.request import get_ip_info 

36from jutil.responses import CsvResponse 

37from jutil.testing import TestSetupMixin 

38from jutil.urls import modify_url 

39from jutil.xml import xml_to_dict, dict_to_element, _xml_filter_tag_name 

40from jutil.dates import add_month, per_delta, per_month, this_week, next_month, next_week, this_month, last_month, \ 

41 last_year, last_week, yesterday, end_of_month, this_year, get_time_steps, TIME_STEP_DAILY 

42from jutil.format import format_full_name, format_xml, format_xml_bytes, format_timedelta, dec1, dec2, dec3, dec4, dec5, \ 

43 dec6, format_table, ucfirst_lazy, strip_media_root, get_media_full_path, camel_case_to_underscore, \ 

44 underscore_to_camel_case, format_as_html_json, format_dict_as_html, choices_label, is_media_full_path 

45from jutil.parse import parse_datetime, parse_bool, parse_datetime_or_none 

46from jutil.validators import fi_payment_reference_number, se_ssn_validator, se_ssn_filter, fi_iban_validator, \ 

47 se_iban_validator, iban_filter_readable, email_filter, iban_validator, iban_bank_info, fi_company_org_id_validator, \ 

48 email_validator, fi_payment_reference_validator, iso_payment_reference_validator, fi_ssn_age, \ 

49 se_clearing_code_bank_info, ascii_filter, ee_iban_validator, be_iban_validator, dk_iban_validator, \ 

50 dk_iban_bank_info, dk_clearing_code_bank_name, country_code_sanitizer, phone_sanitizer, email_sanitizer, \ 

51 fi_company_org_id_generator, phone_validator, passport_filter, passport_validator, passport_sanitizer, \ 

52 country_code_validator, validate_country_iban, iban_bic, validate_country_company_org_id, fi_ssn_generator, \ 

53 fi_ssn_validator, bic_validator, iban_generator, bic_sanitizer, filter_country_company_org_id 

54from xml.etree.ElementTree import Element 

55from xml.etree import ElementTree as ET 

56from django.contrib import admin 

57 

58 

59MY_CHOICE_1 = '1' 

60MY_CHOICE_2 = '2' 

61MY_CHOICES = ( 

62 (MY_CHOICE_1, 'MY_CHOICE_1'), 

63 (MY_CHOICE_2, 'MY_CHOICE_2'), 

64) 

65 

66request_factory = RequestFactory() 

67 

68 

69class DummyLogHandler(logging.Handler): 

70 msgs: List[str] 

71 

72 def __init__(self): 

73 super().__init__() 

74 self.msgs = [] 

75 

76 def emit(self, record): 

77 msg = self.format(record) 

78 self.msgs.append(msg) 

79 

80 

81def dummy_time_zone_response(obj) -> HttpResponse: 

82 tz = timezone.get_current_timezone() 

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

84 

85 

86def dummy_admin_func_a(modeladmin, request, qs): 

87 print('dummy_admin_func_a') 

88 

89 

90def dummy_admin_func_b(modeladmin, request, qs): 

91 print('dummy_admin_func_b') 

92 

93 

94def dummy_middleware_get_response(obj) -> HttpResponse: 

95 return HttpResponse(b'hello content') 

96 

97 

98class DummyUserProfile: 

99 timezone = 'Europe/Helsinki' 

100 

101 

102class MyCustomAdmin(ModelAdminBase): 

103 max_history_length = 5 

104 actions = ( 

105 dummy_admin_func_b, 

106 dummy_admin_func_a, 

107 ) 

108 

109 def get_object(self, request, obj_id): 

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

111 

112 

113class Tests(TestCase, TestSetupMixin): 

114 def setUp(self): 

115 self.user = user = self.add_test_user('test@example.com', 'test1234') 

116 assert isinstance(user, User) 

117 user.is_superuser = True 

118 user.is_staff = True 

119 user.save() 

120 self.client = Client() 

121 

122 def test_api_client(self): 

123 api_client = self.create_api_client() 

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

125 

126 def test_payment_reference(self): 

127 self.assertEqual(fi_payment_reference_number('100'), '1009') 

128 invalids = [ 

129 '89182932u', 

130 '0000002', 

131 ] 

132 for invalid in invalids: 

133 try: 

134 fi_payment_reference_number(invalid) 

135 self.fail('invalid number but passed: {}'.format(invalid)) 

136 except ValidationError: 

137 pass 

138 

139 def test_format_full_name(self): 

140 samples = [ 

141 ('Short', 'Full Name', 'Short Full Name'), 

142 ('Short Middle Name Is Quite Long', 'Full Name', 'Short Full Name'), 

143 ('Short-Middle Name Is Quite Long', 'Full Name', 'Short Full Name'), 

144 ('Olga Demi', 'Serpuhovitinova-Miettinen', 'Olga Serpuhovitinova'), 

145 ('Olga-Anne Demi', 'Serpuhovitinovatsko', 'Olga S'), 

146 ] 

147 for v in samples: 

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

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

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

151 try: 

152 long_name = '19280309812083091829038190823081208301280381092830182038018203810283021' 

153 format_full_name(long_name, long_name) 

154 self.fail('format_full_name failed with long name') 

155 except Exception: 

156 pass 

157 

158 def test_parse_datetime(self): 

159 pairs = [ 

160 ('2016-06-12', '2016-06-12T00:00:00+00:00'), 

161 ('2016-06-12T01:00:00', '2016-06-12T01:00:00+00:00'), 

162 ('2016-06-12 01:00:00', '2016-06-12T01:00:00+00:00'), 

163 ('2016-06-12T01:00:00+00:00', '2016-06-12T01:00:00+00:00'), 

164 ('2016-06-12 01:00:00+00:00', '2016-06-12T01:00:00+00:00'), 

165 ] 

166 for t_str_input, t_ref_str in pairs: 

167 # parse_datetime 

168 t = parse_datetime(t_str_input) 

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

170 assert isinstance(t, datetime) 

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

172 # parse_datetime_or_none 

173 t = parse_datetime_or_none(t_str_input) 

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

175 assert isinstance(t, datetime) 

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

177 

178 invalids = [ 

179 '12312313ew', 

180 '2016-13-05', 

181 ] 

182 for t_str_input in invalids: 

183 # parse_datetime 

184 try: 

185 parse_datetime(t_str_input) 

186 self.fail('{} is not valid input for parse_datetime()'.format(t_str_input)) 

187 except Exception: 

188 pass 

189 # parse_datetime_or_none 

190 t = parse_datetime_or_none(t_str_input) 

191 assert t is None 

192 

193 def test_add_month(self): 

194 t = parse_datetime('2016-06-12T01:00:00') 

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

196 assert isinstance(t, datetime) 

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

198 t2 = add_month(t) 

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

200 t3 = add_month(t, 7) 

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

202 t4 = add_month(t, -1) 

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

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

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

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

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

208 

209 def test_se_ssn(self): 

210 se_ssn_validator('811228-9874') 

211 se_ssn_validator('670919-9530') 

212 with self.assertRaises(ValidationError): 

213 se_ssn_validator('811228-9873') 

214 

215 def test_phone_numbers(self): 

216 try: 

217 phone_validator('+358456343767') 

218 except Exception: 

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

220 with self.assertRaisesMessage(ValidationError, _('Invalid phone number')): 

221 phone_validator('214') 

222 self.assertEqual(phone_sanitizer('214'), '') 

223 

224 def test_passport(self): 

225 self.assertEqual(passport_filter('?ADsd-12312dsds'), 'ADSD-12312DSDS') 

226 passport_validator('21412312312') 

227 with self.assertRaisesMessage(ValidationError, _('Invalid passport number')): 

228 passport_validator('214') 

229 self.assertEqual(passport_sanitizer('214'), '') 

230 self.assertEqual(passport_sanitizer('21412312312'), '21412312312') 

231 

232 def test_country_code(self): 

233 for cc in ['FI', 'DK', 'ES', 'SE', 'VN']: 

234 country_code_validator(cc) 

235 with self.assertRaisesMessage(ValidationError, _('Invalid country code')): 

236 country_code_validator('Finland') 

237 

238 def test_bic(self): 

239 bic = iban_bic('FI21 1234 5600 0007 85') 

240 self.assertEqual(bic, 'NDEAFIHH') 

241 self.assertEqual(bic_sanitizer('NDEAFIHH'), 'NDEAFIHH') 

242 self.assertEqual(bic_sanitizer('NDEAFIH'), '') 

243 

244 def test_org_id(self): 

245 try: 

246 validate_country_company_org_id('FI', '2084069-9') 

247 except Exception: 

248 self.fail('2084069-9 is valid org id') 

249 with self.assertRaisesMessage(ValidationError, _('Invalid company organization ID')): 

250 validate_country_company_org_id('FI', '2084069-8') 

251 

252 def test_validate_country_iban(self): 

253 with self.assertRaisesMessage(ValidationError, _('Invalid IBAN account number')): 

254 validate_country_iban('FI15616515616156', 'SE') 

255 

256 def test_fi_ssn_generator(self): 

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

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

259 for n in range(10): 

260 ssn = fi_ssn_generator(min_year, max_year) 

261 try: 

262 fi_ssn_age(ssn) 

263 fi_ssn_validator(ssn) 

264 except Exception: 

265 self.fail('{} is valid SSN'.format(ssn)) 

266 fi_ssn_validator('110305+283X') 

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

268 with self.assertRaisesMessage(ValidationError, _('Invalid personal identification number')): 

269 fi_ssn_validator(ssn) 

270 with self.assertRaises(ValidationError): 

271 fi_ssn_generator(1700, 1799) 

272 with self.assertRaises(ValidationError): 

273 fi_ssn_generator(2100, 2200) 

274 

275 def test_iban(self): 

276 with self.assertRaisesMessage(ValidationError, _('Invalid IBAN account number')): 

277 iban_validator('') 

278 with self.assertRaisesMessage(ValidationError, _('Invalid country code')): 

279 iban_validator('XX') 

280 with self.assertRaisesMessage(ValidationError, _('Invalid IBAN account number')): 

281 iban_validator('FI2112345600000786') 

282 bic_validator('HELSFIHH') 

283 bic_validator('HELSFIHHXXX') 

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

285 bic_validator('HELSFIH') 

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

287 bic_validator('') 

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

289 bic_validator('XX123123123112') 

290 iban_validator('FI2112345600000785') 

291 iban_validator('SE4550000000058398257466') 

292 fi_iban_validator('FI2112345600000785') 

293 se_iban_validator('SE4550000000058398257466') 

294 ee_iban_validator('EE38 2200 2210 2014 5685') 

295 be_iban_validator('BE68 5390 0754 7034') 

296 with self.assertRaises(ValidationError): 

297 fi_iban_validator('FI2112345600000784') 

298 with self.assertRaises(ValidationError): 

299 se_iban_validator('SE4550000000058398257465') 

300 iban = 'FI8847304720017517' 

301 self.assertEqual(iban_filter_readable(iban), 'FI88 4730 4720 0175 17') 

302 self.assertEqual(iban_filter_readable(''), '') 

303 

304 def test_urls(self): 

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

306 self.assertEqual(modify_url('http://yle.fi/uutiset/3-8045550?a=123&b=456', {'a': '123', 'b': '456'}), 

307 'http://yle.fi/uutiset/3-8045550?a=123&b=456') 

308 

309 def test_email_filter_and_validation(self): 

310 emails = [ 

311 (' Asdsa@a-a.com ', 'asdsa@a-a.com', True), 

312 ('1asdsa@a-a2.com', '1asdsa@a-a2.com', True), 

313 (' Asdsa@a-a ', 'asdsa@a-a', False), 

314 (' @a-a2.com', '@a-a2.com', False), 

315 (' a-a2.com', 'a-a2.com', False), 

316 ('ää-a2@ää-a2.com', 'ää-a2@ää-a2.com', False), 

317 ('aaa.bbbbb@ccc-ddd.fi', 'aaa.bbbbb@ccc-ddd.fi', True), 

318 ] 

319 for i, o, is_valid in emails: 

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

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

322 if is_valid: 

323 email_validator(o) 

324 else: 

325 fail = False 

326 try: 

327 email_validator(o) 

328 except ValidationError: 

329 fail = True 

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

331 

332 def test_ip_info(self): 

333 ip, cc, host = get_ip_info('213.214.146.142') 

334 self.assertEqual(ip, '213.214.146.142') 

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

336 self.assertEqual(cc, 'FI') 

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

338 self.assertEqual(host, '213214146142.edelkey.net') 

339 

340 def test_parse_xml(self): 

341 # finvoice_201_example1.xml 

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

343 data = xml_to_dict(xml_bytes, value_key='value', attribute_prefix='_') 

344 # pprint(data) 

345 self.assertEqual(data['_Version'], '2.01') 

346 self.assertEqual(data['InvoiceRow'][0]['ArticleIdentifier'], '12345') 

347 self.assertEqual(data['InvoiceRow'][0]['DeliveredQuantity']['value'], '2') 

348 self.assertEqual(data['InvoiceRow'][0]['DeliveredQuantity']['_QuantityUnitCode'], 'kpl') 

349 self.assertEqual(data['InvoiceRow'][1]['ArticleIdentifier'], '123456') 

350 

351 # parse_xml1.xml 

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

353 data = xml_to_dict(xml_str.encode()) 

354 # pprint(data) 

355 ref_data = {'@version': '1.2', 

356 'A': [{'@class': 'x', 'B': {'@': 'hello', '@class': 'x2'}}, 

357 {'@class': 'y', 'B': {'@': 'world', '@class': 'y2'}}], 

358 'C': 'value node'} 

359 self.assertEqual(ref_data, data) 

360 

361 # parse_xml1.xml / no attributes 

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

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

364 # pprint(data) 

365 ref_data = {'A': [{'B': 'hello'}, {'B': 'world'}], 'C': 'value node'} 

366 self.assertEqual(ref_data, data) 

367 

368 # parse_xml2.xml / no attributes 

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

370 data = xml_to_dict(xml_str.encode(), ['VastausLoki', 'LuottoTietoMerkinnat'], parse_attributes=False) 

371 # pprint(data) 

372 ref_data = {'VastausLoki': {'KysyttyHenkiloTunnus': '020685-1234', 

373 'PaluuKoodi': 'Palveluvastaus onnistui', 

374 'SyyKoodi': '1'}} 

375 self.assertEqual(ref_data, data) 

376 

377 def test_dict_to_xml(self): 

378 data = { 

379 'Doc': { 

380 '@version': '1.2', 

381 'A': [{'@class': 'x', 'B': {'@': 'hello', '@class': 'x2'}}, 

382 {'@class': 'y', 'B': {'@': 'world', '@class': 'y2'}}], 

383 'C': 'value node', 

384 'D': 123, 

385 'E': ['abc'], 

386 } 

387 } 

388 el = dict_to_element(data) 

389 assert isinstance(el, Element) 

390 xml_str = ET.tostring(el, encoding='utf8', method='xml').decode() 

391 # print(xml_str) # <Doc version="1.2"><C>value node</C><A class="x"><B class="x2">hello</B></A><A class="y"><B class="y2">world</B></A></Doc> 

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

393 # print('') 

394 # pprint(data) 

395 # pprint(data2) 

396 self.assertEqual(data2, data) 

397 

398 def test_dict_to_xml2(self): 

399 self.assertEqual(_xml_filter_tag_name('TagName[0]'), 'TagName') 

400 self.assertEqual(_xml_filter_tag_name('TagName[1]'), 'TagName') 

401 self.assertEqual(_xml_filter_tag_name('TagName'), 'TagName') 

402 data = { 

403 'Doc': { 

404 '@version': '1.2', 

405 'A': [{'@class': 'x', 'B': {'@': 'hello', '@class': 'x2'}}, 

406 {'@class': 'y', 'B': {'@': 'world', '@class': 'y2'}}], 

407 'C': 'value node', 

408 'D': 123, 

409 'E': ['abc'], 

410 'F': ['line 1', 'line 2'] 

411 } 

412 } 

413 el = dict_to_element(data) 

414 assert isinstance(el, Element) 

415 xml_str = ET.tostring(el, encoding='utf8', method='xml').decode() 

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

417 self.assertEqual(data2, data) 

418 

419 def test_xml_to_dict(self): 

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

421<Document> 

422 <TxsSummry> 

423 <TtlNtries> 

424 <NbOfNtries>12</NbOfNtries> 

425 </TtlNtries> 

426 <TtlCdtNtries> 

427 <NbOfNtries>34</NbOfNtries> 

428 <Sum>1234.56</Sum> 

429 </TtlCdtNtries> 

430 <TtlDbtNtries> 

431 <NbOfNtries>0</NbOfNtries> 

432 <Sum>0</Sum> 

433 </TtlDbtNtries> 

434 </TxsSummry> 

435</Document>""" 

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

437 # print('') 

438 # pprint(data) 

439 self.assertEqual(data['Document']['TxsSummry']['TtlNtries']['NbOfNtries'], 12) 

440 self.assertEqual(data['Document']['TxsSummry']['TtlCdtNtries']['NbOfNtries'], 34) 

441 

442 def test_per_delta(self): 

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

444 end = begin + timedelta(days=4) 

445 ref = [(datetime(2017, 9, 17, 11, 42), datetime(2017, 9, 18, 11, 42)), 

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

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

448 (datetime(2017, 9, 20, 11, 42), datetime(2017, 9, 21, 11, 42))] 

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

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

451 

452 def test_per_month(self): 

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

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

455 ref = [(datetime(2017, 9, 1, 0, 0), datetime(2017, 10, 1, 0, 0)), 

456 (datetime(2017, 10, 1, 0, 0), datetime(2017, 11, 1, 0, 0))] 

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

458 

459 def test_dates(self): 

460 t = datetime(2018, 1, 30) 

461 b, e = this_week(t) 

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

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

464 b, e = this_month(t) 

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

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

467 b, e = next_week(t) 

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

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

470 

471 def test_named_date_ranges(self): 

472 t = datetime(2018, 5, 31) 

473 t_tz = pytz.utc.localize(t) 

474 named_ranges = [ 

475 ('last_year', last_year(t)), 

476 ('last_month', last_month(t)), 

477 ('last_week', last_week(t)), 

478 ('this_year', this_year(t)), 

479 ('this_month', this_month(t)), 

480 ('this_week', this_week(t)), 

481 ('yesterday', yesterday(t)), 

482 ('today', yesterday(t + timedelta(hours=24))), 

483 ] 

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

485 for days in day_ranges: 

486 named_ranges.append( 

487 ('plus_minus_{}d'.format(days), (t_tz - timedelta(days=days), t_tz + timedelta(days=days)))) 

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

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

490 for name, res in named_ranges: 

491 # print('testing', name) 

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

493 

494 def test_time_steps(self): 

495 t = parse_datetime('2020-08-24 23:28:36.503174') 

496 this_week_daily = [ 

497 '2020-08-24T00:00:00+00:00 2020-08-25T00:00:00+00:00', 

498 '2020-08-25T00:00:00+00:00 2020-08-26T00:00:00+00:00', 

499 '2020-08-26T00:00:00+00:00 2020-08-27T00:00:00+00:00', 

500 '2020-08-27T00:00:00+00:00 2020-08-28T00:00:00+00:00', 

501 '2020-08-28T00:00:00+00:00 2020-08-29T00:00:00+00:00', 

502 '2020-08-29T00:00:00+00:00 2020-08-30T00:00:00+00:00', 

503 '2020-08-30T00:00:00+00:00 2020-08-31T00:00:00+00:00', 

504 ] 

505 ix = 0 

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

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

508 ix += 1 

509 

510 def test_bank_info(self): 

511 ac = 'FI8847304720017517' 

512 inf = iban_bank_info(ac) 

513 self.assertEqual(inf[0], 'POPFFI22') 

514 self.assertEqual(inf[1], 'POP-Pankki') 

515 

516 ac = '' 

517 inf = iban_bank_info(ac) 

518 self.assertEqual(inf[0], '') 

519 self.assertEqual(inf[1], '') 

520 

521 ac = 'BE75270187592710' 

522 inf = iban_bank_info(ac) 

523 self.assertEqual(inf[0], 'GEBABEBB') 

524 self.assertEqual(inf[1], 'BNP Paribas Fortis') 

525 ac = 'BE58465045170210' 

526 inf = iban_bank_info(ac) 

527 self.assertEqual(inf[0], 'KREDBEBB') 

528 self.assertEqual(inf[1], 'KBC Bank') 

529 ac = 'BE11000123456748' 

530 inf = iban_bank_info(ac) 

531 self.assertEqual(inf[0], 'BPOTBEB1') 

532 self.assertEqual(inf[1], 'bpost bank') 

533 

534 def test_org_id_fi(self): 

535 valids = [ 

536 'FI01098230', 

537 'FI-01098230', 

538 '0109823-0', 

539 '2084069-9', 

540 ] 

541 invalids = [ 

542 '2084069-1', 

543 'SE2084069-1', 

544 ] 

545 co_filtered = [ 

546 ('FI', 'FI01098230', '0109823-0'), 

547 ('FI', 'FI-01098230', '0109823-0'), 

548 ('FI', '0109823-0', '0109823-0'), 

549 ('FI', '2084069-9', '2084069-9'), 

550 ('SE', '01098230', '01098230'), 

551 ('SE', '20840699', '20840699'), 

552 ] 

553 for cc, org, val in co_filtered: 

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

555 validate_country_company_org_id(cc, org) 

556 for valid in valids: 

557 fi_company_org_id_validator(valid) 

558 for invalid in invalids: 

559 try: 

560 fi_company_org_id_validator(invalid) 

561 self.fail('{} passed as valid FI-org'.format(invalid)) 

562 except ValidationError: 

563 # print('ok') 

564 pass 

565 for n in range(10): 

566 v0 = fi_company_org_id_generator() 

567 # print(v0) 

568 fi_company_org_id_validator(v0) 

569 

570 def test_reference_number_validators(self): 

571 valid_fi_refs = [ 

572 '302300', 

573 '202196', 

574 '302290', 

575 ] 

576 for ref_no in valid_fi_refs: 

577 fi_payment_reference_validator(ref_no) 

578 

579 invalid_fi_refs = [ 

580 '302301', 

581 '202195', 

582 '302291', 

583 ] 

584 for ref_no in invalid_fi_refs: 

585 try: 

586 fi_payment_reference_validator(ref_no) 

587 self.assertFalse(True, '{} should have failed validation'.format(ref_no)) 

588 except ValidationError: 

589 pass 

590 

591 valid_iso_refs = [ 

592 'RF92 1229', 

593 'RF11 1232', 

594 'RF48 1245', 

595 ] 

596 for ref_no in valid_iso_refs: 

597 iso_payment_reference_validator(ref_no) 

598 

599 invalid_iso_refs = [ 

600 'RF92 1229}', 

601 ] 

602 for ref_no in invalid_iso_refs: 

603 with self.assertRaisesMessage(ValidationError, 'Invalid payment reference'): 

604 iso_payment_reference_validator(ref_no) 

605 

606 def test_fi_ssn_age(self): 

607 samples = [ 

608 (date(2018, 12, 20), '231298-965X', 19), 

609 (date(2018, 12, 22), '231298-965X', 19), 

610 (date(2018, 12, 23), '231298-965X', 20), 

611 (date(2018, 12, 24), '231298-965X', 20), 

612 ] 

613 for date_now, ssn, age in samples: 

614 self.assertEqual(fi_ssn_age(ssn, date_now), age, 

615 msg='{} age is {} on {} but fi_ssn_age result was {}'.format(ssn, age, date_now, 

616 fi_ssn_age(ssn, date_now))) 

617 

618 def test_se_banks(self): 

619 self.assertEqual(se_clearing_code_bank_info('6789'), ('Handelsbanken', 9)) 

620 se_iban_validator('SE45 5000 0000 0583 9825 7466') 

621 with self.assertRaisesMessage(ValidationError, _('Invalid IBAN account number')): 

622 se_iban_validator('') 

623 with self.assertRaisesMessage(ValidationError, _('Invalid IBAN account number')): 

624 se_iban_validator('XX45 5000 0000 0583 9825 7466') 

625 with self.assertRaisesMessage(ValidationError, _('Invalid IBAN account number')): 

626 se_iban_validator('SE45 5000 0000 0583 9825') 

627 self.assertEqual(se_clearing_code_bank_info('9500'), ('Nordea AB', 10)) 

628 an = '957033025420' 

629 bank_name, acc_digits = se_clearing_code_bank_info(an) 

630 self.assertEqual(bank_name, 'Sparbanken Syd') 

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

632 

633 def test_dk_banks(self): 

634 an = 'DK50 0040 0440 1162 43' 

635 dk_iban_validator(an) 

636 bic, name = dk_iban_bank_info(an) 

637 self.assertEqual(name, 'Nordea') 

638 an = '8114 0008874093' 

639 name = dk_clearing_code_bank_name(an) 

640 self.assertEqual(name, 'Nykredit Bank') 

641 an = 'DK2520006893703029' 

642 name = dk_clearing_code_bank_name(an) 

643 self.assertEqual(name, 'Nordea') 

644 

645 def test_ascii_filter(self): 

646 pairs = [ 

647 ('Åke va Källe o Öring', 'Ake va Kalle o Oring'), 

648 ('Tôi đang đi mua sắm', 'Toi ang i mua sam'), 

649 ('HELÉN FRANZÉN', 'HELEN FRANZEN'), 

650 ] 

651 for a, b in pairs: 

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

653 

654 def test_l10n(self): 

655 with override('fi'): 

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

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

658 print(msg) 

659 self.assertTrue('muoto ei kelpaa' in msg) 

660 

661 try: 

662 parse_bool('hello') 

663 except DRFValidationError as e: 

664 self.assertEqual(str(e), 

665 "[ErrorDetail(string='hello ei ole yksi valittavissa olevista arvoista', code='invalid')]") 

666 

667 def test_sanitizers(self): 

668 self.assertEqual(country_code_sanitizer('kods'), '') 

669 self.assertEqual(country_code_sanitizer('fi'), 'FI') 

670 self.assertEqual(phone_sanitizer('+13146094459'), '+13146094459') 

671 self.assertEqual(phone_sanitizer('13146094459'), '13146094459') 

672 self.assertEqual(phone_sanitizer('+13146094459A'), '+13146094459') 

673 self.assertEqual(email_sanitizer('test@example.com'), 'test@example.com') 

674 self.assertEqual(email_sanitizer('testexample.com'), '') 

675 

676 def test_format_dict_as_html(self): 

677 a = {'b': 1, 'c': {'@testVariable': '123'}} 

678 res = '<pre>B: 1\nC:\n Test variable: 123\n\n</pre>' 

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

680 

681 def test_format_xml(self): 

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

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

684 dst_ref = '<?xml version="1.0"?>\n<ApplicationRequest>\n <CustomerId>1</CustomerId>\n <Command>DownloadFileList</Command>\n <Timestamp>2019-11-27T04:32:18.613452+02:00</Timestamp>\n <Environment>PRODUCTION</Environment>\n</ApplicationRequest>\n' 

685 dst = format_xml(src) 

686 self.assertEqual(dst, dst_ref) 

687 dst = format_xml_bytes(src.encode()) 

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

689 

690 def test_parse_sftp(self): 

691 test_cases = [ 

692 ('sftp://jani@kajala.com', ['jani', None, 'kajala.com', '']), 

693 ('sftp://jani.kajala:1231!@kajala.com', ['jani.kajala', '1231!', 'kajala.com', '']), 

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

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

696 ] 

697 for connection, ref_res in test_cases: 

698 res = urlparse(connection) 

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

700 self.assertListEqual(res_list, ref_res, 

701 'SFTP connection string "{}" parsed incorrectly'.format(connection)) 

702 

703 def test_admin(self): 

704 obj = self.user 

705 admin_log([obj], 'Hello, world') 

706 admin_log([obj], 'Hello, world', user=self.user, ip='127.0.0.1') 

707 admin_log(obj, 'Hello, world', user=self.user, ip='127.0.0.1') 

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

709 self.assertIsNotNone(e) 

710 assert isinstance(e, LogEntry) 

711 self.assertEqual(e.change_message, 'Hello, world') 

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

713 self.assertEqual(admin_obj_url(None, 'admin:auth_user_change'), '') 

714 self.assertEqual(admin_obj_link(None, 'admin:auth_user_change'), '') 

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

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

717 link = admin_obj_link(obj, 'User', 'admin:auth_user_change') 

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

719 

720 def test_cmd_parser(self): 

721 parser = CommandParser() 

722 add_date_range_arguments(parser) 

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

724 options = argv.__dict__ 

725 begin, end, steps = parse_date_range_arguments(options) 

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

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

728 

729 def test_format_timedelta(self): 

730 self.assertEqual(format_timedelta(timedelta(seconds=90)), '1min30s') 

731 self.assertEqual(format_timedelta(timedelta(seconds=3600 + 90)), '1h1min30s') 

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

733 self.assertEqual( 

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

735 '1h 1min 30s') 

736 self.assertEqual(format_timedelta(timedelta(seconds=90), seconds_label=''), '1min') 

737 self.assertEqual(format_timedelta(timedelta(seconds=0.090), seconds_label='s'), '0.090s') 

738 

739 def test_dec123456(self): 

740 self.assertEqual(dec1(Decimal('1.2345678')), Decimal('1.2')) 

741 self.assertEqual(dec2(Decimal('1.2345678')), Decimal('1.23')) 

742 self.assertEqual(dec3(Decimal('1.2345678')), Decimal('1.235')) 

743 self.assertEqual(dec4(Decimal('1.2345678')), Decimal('1.2346')) 

744 self.assertEqual(dec5(Decimal('1.2345678')), Decimal('1.23457')) 

745 self.assertEqual(dec6(Decimal('1.2345678')), Decimal('1.234568')) 

746 

747 def test_model_funcs(self): 

748 admin_log([self.user], 'test msg 1') 

749 obj = LogEntry.objects.all().order_by('-pk').last() 

750 assert isinstance(obj, LogEntry) 

751 self.assertFalse(is_model_field_changed(obj, 'change_message')) 

752 obj.change_message = 'hello world' 

753 self.assertTrue(is_model_field_changed(obj, 'change_message')) 

754 obj.save() 

755 self.assertFalse(is_model_field_changed(obj, 'change_message')) 

756 obj2 = clone_model(obj) 

757 assert isinstance(obj2, LogEntry) 

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

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

760 label, val = get_model_field_label_and_value(obj, 'action_time') 

761 self.assertEqual(label, _('action time')) 

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

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

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

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

766 self.assertIsNone(obj_b) 

767 

768 def test_format_table(self): 

769 a = [ 

770 ['date', 'description', 'count', 'unit price', 'total price'], 

771 [date(2019, 12, 15), 'oranges', 1000, dec2('0.99'), dec2('990.00')], 

772 [date(2020, 1, 3), 'apples', 4, dec2('1.10'), dec2('4.40')], 

773 [date(2020, 11, 3), 'apples', 5, dec2('10.10'), dec2('50.50')], 

774 ] 

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

776 out_ref = """ 

777--------------------------------------------------- 

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

779--------------------------------------------------- 

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

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

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

783--------------------------------------------------- 

784 """.strip() 

785 self.assertEqual(out, out_ref) 

786 

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

788 out_ref = """ 

789----------------------------------------------------- 

790| date|description|count|unit price|total price| 

791----------------------------------------------------- 

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

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

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

795----------------------------------------------------- 

796 """.strip() 

797 self.assertEqual(out, out_ref) 

798 

799 out = format_table(a, has_label_row=True, max_col=20, col_sep=' | ') 

800 out_ref = """ 

801------------------------------------------------------------- 

802| date | description | count | unit price | total price| 

803------------------------------------------------------------- 

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

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

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

807------------------------------------------------------------- 

808 """.strip() 

809 self.assertEqual(out, out_ref) 

810 

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

812 out_ref = """ 

813------------------------------------------------------------- 

814| date | description | count | unit price | total price| 

815------------------------------------------------------------- 

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

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

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

819------------------------------------------------------------- 

820 """.strip() 

821 self.assertEqual(out, out_ref) 

822 

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

824 out_ref = """ 

825------------------------------------------------- 

826| date | description | count | unit price|.. 

827------------------------------------------------- 

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

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

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

831------------------------------------------------- 

832 """.strip() 

833 self.assertEqual(out, out_ref) 

834 

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

836 out_ref = """ 

837----------------------------------------------------- 

838| date |description|count|unit price|total price| 

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

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

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

842----------------------------------------------------- 

843 """.strip() 

844 self.assertEqual(out, out_ref) 

845 

846 def test_ucfirst_lazy(self): 

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

848 s_ref = gettext_lazy("Missing value") 

849 s_en = "Missing value" 

850 s_fi = "Puuttuva arvo" 

851 with override('fi'): 

852 self.assertEqual(s, s_fi) 

853 self.assertEqual(s, s_ref) 

854 with override('en'): 

855 self.assertEqual(s, s_en) 

856 self.assertEqual(s, s_ref) 

857 

858 def test_media_paths(self): 

859 media_root1 = os.path.join(settings.MEDIA_ROOT, 'path1/path2') 

860 media_root2 = os.path.join(settings.MEDIA_ROOT, 'path3') + '/' 

861 test_paths = [ 

862 (True, os.path.join(media_root1, 'test1.file'), 'path1/path2/test1.file'), 

863 (False, os.path.join('/diff/path', 'test2.file'), '/diff/path/test2.file'), 

864 (True, os.path.join(media_root2, 'test3.file'), 'path3/test3.file'), 

865 ] 

866 for is_media, src, dst in test_paths: 

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

868 if is_media: 

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

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

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

872 

873 def test_end_of_month(self): 

874 helsinki = pytz.timezone('Europe/Helsinki') 

875 # 1 

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

877 eom = end_of_month(time_now, tz=helsinki) 

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

879 self.assertEqual(eom, eom_ref) 

880 # 2 

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

882 eom = end_of_month(time_now, tz=helsinki) 

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

884 self.assertEqual(eom, eom_ref) 

885 # 3 

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

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

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

889 self.assertEqual(eom, eom_ref) 

890 # 4 

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

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

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

894 self.assertEqual(eom, eom_ref) 

895 

896 def test_iban_generator_and_validator(self): 

897 test_ibans = [ 

898 'MD7289912714638112731113', 

899 'IS363252851674877586492113', 

900 'HR5125000099152386224', 

901 'CZ4750515755735423825528', 

902 'FI3253811381259333', 

903 'FR3212739000501869481882E94', 

904 ] 

905 for iban in test_ibans: 

906 iban_validator(iban) 

907 for cc in ['', 'FI', 'SE']: 

908 for n in range(100): 

909 acc = iban_generator(cc) 

910 try: 

911 iban_validator(acc) 

912 except Exception as e: 

913 print('iban_generator() returned', acc, 'but iban_validator() raised exception', e) 

914 self.fail('iban_validator(iban_generator()) should not raise Exception, account number was {}'.format(acc)) 

915 with self.assertRaisesMessage(ValidationError, _('Invalid country code')): 

916 iban_generator('XX') 

917 with self.assertRaisesMessage(ValidationError, _('IBAN checksum generation does not support >26 character IBANs')): 

918 iban_generator('AL') 

919 

920 def test_make_email_recipient(self): 

921 email_tests = [ 

922 { 

923 'list': [ 

924 ('Jani Kajala', 'kajala@example.com'), 

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

926 '<kajala@example.com>', 

927 'kajala@example.com', 

928 ], 

929 'result': [ 

930 ('Jani Kajala', 'kajala@example.com'), 

931 ('Jani Kajala', 'kajala@example.com'), 

932 ('kajala@example.com', 'kajala@example.com'), 

933 ('kajala@example.com', 'kajala@example.com'), 

934 ] 

935 } 

936 ] 

937 for et in email_tests: 

938 res = make_email_recipient_list(et['list']) 

939 self.assertListEqual(res, et['result']) 

940 

941 def test_choices(self): 

942 val = choices_label(MY_CHOICES, MY_CHOICE_1) 

943 self.assertEqual(val, 'MY_CHOICE_1') 

944 

945 def test_camel_case(self): 

946 pairs = [ 

947 ('camelCaseWord', 'camel_case_word'), 

948 ('camelCase', 'camel_case'), 

949 ('camel', 'camel'), 

950 ('camelCCase', 'camel_c_case'), 

951 ] 

952 for cc, us in pairs: 

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

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

955 

956 def create_dummy_request(self, path: str = '/admin/login/'): 

957 request = request_factory.get(path) 

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

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

960 return request 

961 

962 def test_model_admin_base(self): 

963 # test that actions sorting by name works 

964 request = self.create_dummy_request() 

965 user = self.user 

966 model_admin = MyCustomAdmin(LogEntry, admin.site) 

967 res = model_admin.get_actions(request) 

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

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

970 

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

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

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

974 for n in range(5): 

975 admin_log([user], 'InvisibleLogMessage') 

976 for n in range(5): 

977 admin_log([user], 'VisibleLogMessage') 

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

979 history_url = '/admin/auth/user/{}/history/'.format(user.id) 

980 c = self.client 

981 c.get(history_url, follow=True) 

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

983 res = c.get(history_url) 

984 content = res.content.decode() 

985 assert isinstance(content, str) 

986 self.assertEqual(content.count('VisibleLogMessage'), 5) 

987 self.assertEqual(content.count('InvisibleLogMessage'), 0) 

988 

989 def test_admin_log_entry_mixin(self): 

990 user = self.user 

991 AdminLogEntryMixin.fields_changed(user, ['username'], who=None) 

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

993 assert isinstance(e, LogEntry) 

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

995 

996 def test_auth(self): 

997 req = self.create_dummy_request() 

998 get_auth_user(req) # type: ignore 

999 req.user = None 

1000 self.assertIsNone(get_auth_user_or_none(req)) 

1001 try: 

1002 get_auth_user(req) # type: ignore 

1003 self.fail('get_auth_user fail') 

1004 except NotAuthenticated: 

1005 pass 

1006 try: 

1007 model_admin = AuthUserMixin() 

1008 model_admin.request = req 

1009 user = model_admin.auth_user 

1010 self.fail('get_auth_user fail') 

1011 except NotAuthenticated: 

1012 pass 

1013 

1014 def test_middleware(self): 

1015 # EnsureOriginMiddleware 

1016 request = self.create_dummy_request('/admin/login/') 

1017 EnsureOriginMiddleware(dummy_middleware_get_response)(request) 

1018 self.assertIn('HTTP_ORIGIN', request.META) 

1019 self.assertEqual(request.META['HTTP_ORIGIN'], request.get_host()) 

1020 

1021 # LogExceptionMiddleware 

1022 dummy_log = DummyLogHandler() 

1023 jutil_middleware_logger.addHandler(dummy_log) 

1024 try: 

1025 raise Exception('Dummy exception, ignore this') 

1026 except Exception as e: 

1027 mw = LogExceptionMiddleware(dummy_middleware_get_response) 

1028 mw.process_exception(request, e) 

1029 msg = dummy_log.msgs.pop() 

1030 self.assertIn('Exception: Dummy exception, ignore this', msg) 

1031 self.assertIn('user=test@example.com', msg) 

1032 jutil_middleware_logger.removeHandler(dummy_log) 

1033 

1034 # EnsureLanguageCookieMiddleware 

1035 lang_cookie_tests = [ 

1036 ('/admin/login/', settings.LANGUAGE_CODE), 

1037 ('/admin/login/?django_language=fi', 'fi'), 

1038 ] 

1039 for request_path, lang_code in lang_cookie_tests: 

1040 request = self.create_dummy_request(request_path) 

1041 mw = EnsureLanguageCookieMiddleware(dummy_middleware_get_response) 

1042 res = mw(request) 

1043 assert isinstance(res, HttpResponse) 

1044 self.assertEqual(res.status_code, 200) 

1045 self.assertIn('django_language', request.COOKIES) 

1046 self.assertIn(request.COOKIES['django_language'], lang_code) 

1047 self.assertIn('django_language', res.cookies) 

1048 self.assertEqual(str(res.cookies['django_language']), 'Set-Cookie: django_language={}; Path=/'.format(lang_code)) 

1049 

1050 # ActivateUserProfileTimezoneMiddleware 

1051 user = self.user 

1052 self.assertTrue(user.is_authenticated) 

1053 user.profile.timezone = 'Europe/Helsinki' 

1054 with timezone.override(pytz.timezone('America/Chicago')): 

1055 request = self.create_dummy_request('/admin/login/') 

1056 mw = ActivateUserProfileTimezoneMiddleware(dummy_time_zone_response) 

1057 res = mw(request) 

1058 content = res.content.decode() 

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

1060 

1061 def test_safe_fields(self): 

1062 class TestModel(models.Model): 

1063 cf = SafeCharField(max_length=256) 

1064 tf = SafeTextField() 

1065 

1066 obj = TestModel() 

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

1068 data_ref = 'hello world ' 

1069 for f in obj._meta.fields: 

1070 if f.name in ['cf', 'tf']: 

1071 f.save_form_data(obj, data) 

1072 self.assertEqual(obj.cf, data_ref) 

1073 self.assertEqual(obj.tf, data_ref) 

1074 

1075 def test_responses(self): 

1076 a = [ 

1077 ['date', 'description', 'count', 'unit price', 'total price'], 

1078 [date(2019, 12, 15), 'oranges', 1000, dec2('0.99'), dec2('990.00')], 

1079 [date(2020, 1, 3), 'apples', 4, dec2('1.10'), dec2('4.40')], 

1080 [date(2020, 11, 3), 'apples', 5, dec2('10.10'), dec2('50.50')], 

1081 ] 

1082 res = CsvResponse(a, 'test.csv') 

1083 content_ref = b'date,description,count,unit price,total price\r\n2019-12-15,oranges,1000,0.99,990.00\r\n2020-01-03,apples,4,1.10,4.40\r\n2020-11-03,apples,5,10.10,50.50\r\n' 

1084 self.assertEqual(content_ref, res.content) 

1085 print(res.content.decode()) 

1086 

1087 def test_wait_object_or_none(self): 

1088 admin_log([self.user], 'Hello, world') 

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

1090 assert isinstance(e, LogEntry) 

1091 e_id = e.id 

1092 obj = wait_object_or_none(LogEntry, id=e_id) 

1093 self.assertIsNotNone(obj) 

1094 self.assertEqual(obj.id, e_id) 

1095 t0 = now() 

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

1097 t1 = now() 

1098 self.assertIsNone(obj) 

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

1100 

1101 def test_format_as_html_json(self): 

1102 pairs = [ 

1103 (None, 'null'), 

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

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

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

1107 ] 

1108 for val, html in pairs: 

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

1110 

1111 def test_parse_bool(self): 

1112 refs_true = [ 

1113 True, 

1114 'True', 

1115 '1', 

1116 1, 

1117 'true', 

1118 'yes', 

1119 ] 

1120 refs_false = [ 

1121 False, 

1122 'False', 

1123 '0', 

1124 0, 

1125 'false', 

1126 'no', 

1127 ] 

1128 for ref in refs_true: 

1129 self.assertTrue(parse_bool(ref)) 

1130 for ref in refs_false: 

1131 self.assertFalse(parse_bool(ref)) 

1132 

1133 def test_exception_convert(self): 

1134 e = transform_exception_to_drf(ValidationError({'hello': 'world'})) 

1135 assert isinstance(e, DRFValidationError) 

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

1137 self.assertIn('hello', e.detail) 

1138 self.assertEqual(e.detail['hello'], [ErrorDetail(string='world', code='invalid')]) 

1139 

1140 

1141dummy_admin_func_a.short_description = 'A' # type: ignore 

1142dummy_admin_func_b.short_description = 'B' # type: ignore 

1143 

1144admin.site.unregister(User) 

1145admin.site.register(User, MyCustomAdmin) 

1146admin.site.register(LogEntry, MyCustomAdmin)