Mercurial > p > roundup > code
comparison test/test_liveserver.py @ 6458:8f1b91756457
issue2551147 - Enable compression of http responses in roundup.
gzip, (brotli/zstd with optional packages) on the fly
compression/content-encoding enabled by default. Can serve
pre-compressed static assets as well if the client can accept it.
Docs updated.
Also added example nginx config to installation.txt. The config allows
nginx to compress data on the fly. If the config is used, dynamic
compression in roundup can be disabled.
Dedicating this checkin to my father Paul Hector Rouillard 1930-2021.
I did much of the development in this changeset while sitting with him
as he slept/transitioned. Without his encouragement and example, my
desire to learn would not be what it is and I wouldn't be half the
person I am.
| author | John Rouillard <rouilj@ieee.org> |
|---|---|
| date | Sat, 24 Jul 2021 16:31:36 -0400 |
| parents | 2a2da73e1e26 |
| children | 0e86ea84e59d |
comparison
equal
deleted
inserted
replaced
| 6457:dc59051807b6 | 6458:8f1b91756457 |
|---|---|
| 1 import shutil, errno, pytest | 1 import shutil, errno, pytest, json, gzip, os |
| 2 | 2 |
| 3 from roundup.anypy.strings import b2s | |
| 3 from roundup.cgi.wsgi_handler import RequestDispatcher | 4 from roundup.cgi.wsgi_handler import RequestDispatcher |
| 4 from .wsgi_liveserver import LiveServerTestCase | 5 from .wsgi_liveserver import LiveServerTestCase |
| 5 from . import db_test_base | 6 from . import db_test_base |
| 6 | 7 |
| 7 try: | 8 try: |
| 10 except ImportError: | 11 except ImportError: |
| 11 from .pytest_patcher import mark_class | 12 from .pytest_patcher import mark_class |
| 12 skip_requests = mark_class(pytest.mark.skip( | 13 skip_requests = mark_class(pytest.mark.skip( |
| 13 reason='Skipping liveserver tests: requests library not available')) | 14 reason='Skipping liveserver tests: requests library not available')) |
| 14 | 15 |
| 15 | 16 try: |
| 17 import brotli | |
| 18 skip_brotli = lambda func, *args, **kwargs: func | |
| 19 except ImportError: | |
| 20 from .pytest_patcher import mark_class | |
| 21 skip_brotli = mark_class(pytest.mark.skip( | |
| 22 reason='Skipping brotli tests: brotli library not available')) | |
| 23 brotli = None | |
| 24 | |
| 25 try: | |
| 26 import zstd | |
| 27 skip_zstd = lambda func, *args, **kwargs: func | |
| 28 except ImportError: | |
| 29 from .pytest_patcher import mark_class | |
| 30 skip_zstd = mark_class(pytest.mark.skip( | |
| 31 reason='Skipping zstd tests: zstd library not available')) | |
| 32 | |
| 16 @skip_requests | 33 @skip_requests |
| 17 class SimpleTest(LiveServerTestCase): | 34 class SimpleTest(LiveServerTestCase): |
| 18 # have chicken and egg issue here. Need to encode the base_url | 35 # have chicken and egg issue here. Need to encode the base_url |
| 19 # in the config file but we don't know it until after | 36 # in the config file but we don't know it until after |
| 20 # the server is started nd has read the config.ini. | 37 # the server is started nd has read the config.ini. |
| 38 # open the database | 55 # open the database |
| 39 cls.db = cls.instance.open('admin') | 56 cls.db = cls.instance.open('admin') |
| 40 | 57 |
| 41 # set the url the test instance will run at. | 58 # set the url the test instance will run at. |
| 42 cls.db.config['TRACKER_WEB'] = "http://localhost:9001/" | 59 cls.db.config['TRACKER_WEB'] = "http://localhost:9001/" |
| 60 # set up mailhost so errors get reported to debuging capture file | |
| 61 cls.db.config.MAILHOST = "localhost" | |
| 62 cls.db.config.MAIL_HOST = "localhost" | |
| 63 cls.db.config.MAIL_DEBUG = "../mail.log.t" | |
| 64 | |
| 65 # enable static precompressed files | |
| 66 cls.db.config.WEB_USE_PRECOMPRESSED_FILES = 1 | |
| 67 | |
| 43 cls.db.config.save() | 68 cls.db.config.save() |
| 44 | 69 |
| 45 cls.db.commit() | 70 cls.db.commit() |
| 46 cls.db.close() | 71 cls.db.close() |
| 47 | 72 |
| 204 print(f.status_code) | 229 print(f.status_code) |
| 205 print(f.headers) | 230 print(f.headers) |
| 206 | 231 |
| 207 self.assertEqual(f.status_code, 404) | 232 self.assertEqual(f.status_code, 404) |
| 208 | 233 |
| 209 | 234 def test_ims(self): |
| 235 ''' retreive the user_utils.js file with old and new | |
| 236 if-modified-since timestamps. | |
| 237 ''' | |
| 238 from datetime import datetime | |
| 239 | |
| 240 f = requests.get(self.url_base() + '/@@file/user_utils.js', | |
| 241 headers = { 'Accept-Encoding': 'gzip, foo', | |
| 242 'If-Modified-Since': 'Sun, 13 Jul 1986 01:20:00', | |
| 243 'Accept': '*/*'}) | |
| 244 print(f.status_code) | |
| 245 print(f.headers) | |
| 246 | |
| 247 self.assertEqual(f.status_code, 200) | |
| 248 expected = { 'Content-Type': 'application/javascript', | |
| 249 'Content-Encoding': 'gzip', | |
| 250 'Vary': 'Accept-Encoding', | |
| 251 } | |
| 252 | |
| 253 # use dict comprehension to remove fields like date, | |
| 254 # etag etc. from f.headers. | |
| 255 self.assertDictEqual({ key: value for (key, value) in f.headers.items() if key in expected }, expected) | |
| 256 | |
| 257 # now use today's date | |
| 258 a_few_seconds_ago = datetime.now().strftime('%a, %d %b %Y %H:%M:%S GMT') | |
| 259 f = requests.get(self.url_base() + '/@@file/user_utils.js', | |
| 260 headers = { 'Accept-Encoding': 'gzip, foo', | |
| 261 'If-Modified-Since': a_few_seconds_ago, | |
| 262 'Accept': '*/*'}) | |
| 263 print(f.status_code) | |
| 264 print(f.headers) | |
| 265 | |
| 266 self.assertEqual(f.status_code, 304) | |
| 267 expected = { 'Content-Type': 'application/javascript', | |
| 268 'Vary': 'Accept-Encoding', | |
| 269 'Content-Length': '0', | |
| 270 } | |
| 271 | |
| 272 # use dict comprehension to remove fields like date, etag | |
| 273 # etc. from f.headers. | |
| 274 self.assertDictEqual({ key: value for (key, value) in f.headers.items() if key in expected }, expected) | |
| 275 | |
| 276 | |
| 277 def test_compression_gzipfile(self): | |
| 278 '''Get the compressed dummy file''' | |
| 279 | |
| 280 # create a user_utils.js.gz file to test pre-compressed | |
| 281 # file serving code. Has custom contents to verify | |
| 282 # that I get the compressed one. | |
| 283 gzfile = "%s/html/user_utils.js.gzip"%self.dirname | |
| 284 test_text= b"Custom text for user_utils.js\n" | |
| 285 | |
| 286 with gzip.open(gzfile, 'wb') as f: | |
| 287 bytes_written = f.write(test_text) | |
| 288 | |
| 289 self.assertEqual(bytes_written, 30) | |
| 290 | |
| 291 # test file x-fer | |
| 292 f = requests.get(self.url_base() + '/@@file/user_utils.js', | |
| 293 headers = { 'Accept-Encoding': 'gzip, foo', | |
| 294 'Accept': '*/*'}) | |
| 295 print(f.status_code) | |
| 296 print(f.headers) | |
| 297 | |
| 298 self.assertEqual(f.status_code, 200) | |
| 299 expected = { 'Content-Type': 'application/javascript', | |
| 300 'Content-Encoding': 'gzip', | |
| 301 'Vary': 'Accept-Encoding', | |
| 302 'Content-Length': '69', | |
| 303 } | |
| 304 | |
| 305 # use dict comprehension to remove fields like date, | |
| 306 # content-length etc. from f.headers. | |
| 307 self.assertDictEqual({ key: value for (key, value) in | |
| 308 f.headers.items() if key in expected }, | |
| 309 expected) | |
| 310 | |
| 311 | |
| 312 # check content - verify it's the .gz file not the real file. | |
| 313 self.assertEqual(f.content, test_text) | |
| 314 | |
| 315 '''# verify that a different encoding request returns on the fly | |
| 316 | |
| 317 # test file x-fer using br, so we get runtime compression | |
| 318 f = requests.get(self.url_base() + '/@@file/user_utils.js', | |
| 319 headers = { 'Accept-Encoding': 'br, foo', | |
| 320 'Accept': '*/*'}) | |
| 321 print(f.status_code) | |
| 322 print(f.headers) | |
| 323 | |
| 324 self.assertEqual(f.status_code, 200) | |
| 325 expected = { 'Content-Type': 'application/javascript', | |
| 326 'Content-Encoding': 'br', | |
| 327 'Vary': 'Accept-Encoding', | |
| 328 'Content-Length': '960', | |
| 329 } | |
| 330 | |
| 331 # use dict comprehension to remove fields like date, | |
| 332 # content-length etc. from f.headers. | |
| 333 self.assertDictEqual({ key: value for (key, value) in | |
| 334 f.headers.items() if key in expected }, | |
| 335 expected) | |
| 336 | |
| 337 try: | |
| 338 from urllib3.response import BrotliDecoder | |
| 339 # requests has decoded br to text for me | |
| 340 data = f.content | |
| 341 except ImportError: | |
| 342 # I need to decode | |
| 343 data = brotli.decompress(f.content) | |
| 344 | |
| 345 self.assertEqual(b2s(data)[0:25], '// User Editing Utilities') | |
| 346 ''' | |
| 347 | |
| 348 # re-request file, but now make .gzip out of date. So we get the | |
| 349 # real file compressed on the fly, not our test file. | |
| 350 os.utime(gzfile, (0,0)) # use 1970/01/01 or os base time | |
| 351 | |
| 352 f = requests.get(self.url_base() + '/@@file/user_utils.js', | |
| 353 headers = { 'Accept-Encoding': 'gzip, foo', | |
| 354 'Accept': '*/*'}) | |
| 355 print(f.status_code) | |
| 356 print(f.headers) | |
| 357 | |
| 358 self.assertEqual(f.status_code, 200) | |
| 359 expected = { 'Content-Type': 'application/javascript', | |
| 360 'Content-Encoding': 'gzip', | |
| 361 'Vary': 'Accept-Encoding', | |
| 362 } | |
| 363 | |
| 364 # use dict comprehension to remove fields like date, | |
| 365 # content-length etc. from f.headers. | |
| 366 self.assertDictEqual({ key: value for (key, value) in | |
| 367 f.headers.items() if key in expected }, | |
| 368 expected) | |
| 369 | |
| 370 | |
| 371 # check content - verify it's the real file, not crafted .gz. | |
| 372 self.assertEqual(b2s(f.content)[0:25], '// User Editing Utilities') | |
| 373 | |
| 374 # cleanup | |
| 375 os.remove(gzfile) | |
| 376 | |
| 377 def test_compression_gzip(self): | |
| 378 # use basic auth for rest endpoint | |
| 379 f = requests.get(self.url_base() + '/rest/data/user/1/username', | |
| 380 auth=('admin', 'sekrit'), | |
| 381 headers = {'content-type': "", | |
| 382 'Accept-Encoding': 'gzip, foo', | |
| 383 'Accept': '*/*'}) | |
| 384 print(f.status_code) | |
| 385 print(f.headers) | |
| 386 | |
| 387 self.assertEqual(f.status_code, 200) | |
| 388 expected = { 'Content-Type': 'application/json', | |
| 389 'Access-Control-Allow-Origin': '*', | |
| 390 'Access-Control-Allow-Headers': 'Content-Type, Authorization, X-HTTP-Method-Override', | |
| 391 'Allow': 'OPTIONS, GET, POST, PUT, DELETE, PATCH', | |
| 392 'Access-Control-Allow-Methods': 'HEAD, OPTIONS, GET, PUT, DELETE, PATCH', | |
| 393 'Content-Encoding': 'gzip', | |
| 394 'Vary': 'Accept-Encoding', | |
| 395 } | |
| 396 | |
| 397 content_str = '''{ "data": { | |
| 398 "id": "1", | |
| 399 "link": "http://localhost:9001/rest/data/user/1/username", | |
| 400 "data": "admin" | |
| 401 } | |
| 402 }''' | |
| 403 content = json.loads(content_str) | |
| 404 | |
| 405 | |
| 406 if (type("") == type(f.content)): | |
| 407 json_dict = json.loads(f.content) | |
| 408 else: | |
| 409 json_dict = json.loads(b2s(f.content)) | |
| 410 | |
| 411 # etag wil not match, creation date different | |
| 412 del(json_dict['data']['@etag']) | |
| 413 | |
| 414 # type is "class 'str'" under py3, "type 'str'" py2 | |
| 415 # just skip comparing it. | |
| 416 del(json_dict['data']['type']) | |
| 417 | |
| 418 self.assertDictEqual(json_dict, content) | |
| 419 | |
| 420 # use dict comprehension to remove fields like date, | |
| 421 # content-length etc. from f.headers. | |
| 422 self.assertDictEqual({ key: value for (key, value) in f.headers.items() if key in expected }, expected) | |
| 423 | |
| 424 | |
| 425 | |
| 426 # use basic auth for rest endpoint, error case, bad attribute | |
| 427 f = requests.get(self.url_base() + '/rest/data/user/1/foo', | |
| 428 auth=('admin', 'sekrit'), | |
| 429 headers = {'content-type': "", | |
| 430 'Accept-Encoding': 'gzip, foo', | |
| 431 'Accept': '*/*'}) | |
| 432 print(f.status_code) | |
| 433 print(f.headers) | |
| 434 | |
| 435 # ERROR: attribute error turns into 405, not sure that's right. | |
| 436 # NOTE: not compressed payload too small | |
| 437 self.assertEqual(f.status_code, 405) | |
| 438 expected = { 'Content-Type': 'application/json', | |
| 439 'Access-Control-Allow-Origin': '*', | |
| 440 'Access-Control-Allow-Headers': 'Content-Type, Authorization, X-HTTP-Method-Override', | |
| 441 'Allow': 'OPTIONS, GET, POST, PUT, DELETE, PATCH', | |
| 442 'Access-Control-Allow-Methods': 'HEAD, OPTIONS, GET, PUT, DELETE, PATCH', | |
| 443 } | |
| 444 | |
| 445 content = { "error": | |
| 446 { | |
| 447 "status": 405, | |
| 448 "msg": "'foo'" | |
| 449 } | |
| 450 } | |
| 451 | |
| 452 json_dict = json.loads(b2s(f.content)) | |
| 453 self.assertDictEqual(json_dict, content) | |
| 454 | |
| 455 # use dict comprehension to remove fields like date, | |
| 456 # content-length etc. from f.headers. | |
| 457 self.assertDictEqual({ key: value for (key, value) in f.headers.items() if key in expected }, expected) | |
| 458 | |
| 459 # test file x-fer | |
| 460 f = requests.get(self.url_base() + '/@@file/user_utils.js', | |
| 461 headers = { 'Accept-Encoding': 'gzip, foo', | |
| 462 'Accept': '*/*'}) | |
| 463 print(f.status_code) | |
| 464 print(f.headers) | |
| 465 | |
| 466 self.assertEqual(f.status_code, 200) | |
| 467 expected = { 'Content-Type': 'application/javascript', | |
| 468 'Content-Encoding': 'gzip', | |
| 469 'Vary': 'Accept-Encoding', | |
| 470 } | |
| 471 | |
| 472 # check first few bytes. | |
| 473 self.assertEqual(b2s(f.content[0:25]), '// User Editing Utilities') | |
| 474 | |
| 475 # use dict comprehension to remove fields like date, | |
| 476 # content-length etc. from f.headers. | |
| 477 self.assertDictEqual({ key: value for (key, value) in | |
| 478 f.headers.items() if key in expected }, | |
| 479 expected) | |
| 480 | |
| 481 # test file x-fer | |
| 482 f = requests.get(self.url_base() + '/user1', | |
| 483 headers = { 'Accept-Encoding': 'gzip, foo', | |
| 484 'Accept': '*/*'}) | |
| 485 print(f.status_code) | |
| 486 print(f.headers) | |
| 487 | |
| 488 self.assertEqual(f.status_code, 200) | |
| 489 expected = { 'Content-Type': 'text/html; charset=utf-8', | |
| 490 'Content-Encoding': 'gzip', | |
| 491 'Vary': 'Accept-Encoding', | |
| 492 } | |
| 493 | |
| 494 # check first few bytes. | |
| 495 self.assertEqual(b2s(f.content[0:25]), '<!-- dollarId: user.item,') | |
| 496 | |
| 497 # use dict comprehension to remove fields like date, | |
| 498 # content-length etc. from f.headers. | |
| 499 self.assertDictEqual({ key: value for (key, value) in | |
| 500 f.headers.items() if key in expected }, | |
| 501 expected) | |
| 502 | |
| 503 @skip_brotli | |
| 504 def test_compression_br(self): | |
| 505 # use basic auth for rest endpoint | |
| 506 f = requests.get(self.url_base() + '/rest/data/user/1/username', | |
| 507 auth=('admin', 'sekrit'), | |
| 508 headers = {'content-type': "", | |
| 509 'Accept-Encoding': 'br, foo', | |
| 510 'Accept': '*/*'}) | |
| 511 print(f.status_code) | |
| 512 print(f.headers) | |
| 513 | |
| 514 self.assertEqual(f.status_code, 200) | |
| 515 expected = { 'Content-Type': 'application/json', | |
| 516 'Access-Control-Allow-Origin': '*', | |
| 517 'Access-Control-Allow-Headers': 'Content-Type, Authorization, X-HTTP-Method-Override', | |
| 518 'Allow': 'OPTIONS, GET, POST, PUT, DELETE, PATCH', | |
| 519 'Access-Control-Allow-Methods': 'HEAD, OPTIONS, GET, PUT, DELETE, PATCH', | |
| 520 'Content-Encoding': 'br', | |
| 521 'Vary': 'Accept-Encoding', | |
| 522 } | |
| 523 | |
| 524 content_str = '''{ "data": { | |
| 525 "id": "1", | |
| 526 "link": "http://localhost:9001/rest/data/user/1/username", | |
| 527 "data": "admin" | |
| 528 } | |
| 529 }''' | |
| 530 content = json.loads(content_str) | |
| 531 | |
| 532 | |
| 533 if (type("") == type(f.content)): | |
| 534 json_dict = json.loads(f.content) | |
| 535 else: | |
| 536 json_dict = json.loads(b2s(brotli.decompress(f.content))) | |
| 537 | |
| 538 # etag wil not match, creation date different | |
| 539 del(json_dict['data']['@etag']) | |
| 540 | |
| 541 # type is "class 'str'" under py3, "type 'str'" py2 | |
| 542 # just skip comparing it. | |
| 543 del(json_dict['data']['type']) | |
| 544 | |
| 545 self.assertDictEqual(json_dict, content) | |
| 546 | |
| 547 # use dict comprehension to remove fields like date, | |
| 548 # content-length etc. from f.headers. | |
| 549 self.assertDictEqual({ key: value for (key, value) in f.headers.items() if key in expected }, expected) | |
| 550 | |
| 551 | |
| 552 | |
| 553 # use basic auth for rest endpoint, error case, bad attribute | |
| 554 f = requests.get(self.url_base() + '/rest/data/user/1/foo', | |
| 555 auth=('admin', 'sekrit'), | |
| 556 headers = {'Accept-Encoding': 'br, foo', | |
| 557 'Accept': '*/*'}) | |
| 558 print(f.status_code) | |
| 559 print(f.headers) | |
| 560 # ERROR: attribute error turns into 405, not sure that's right. | |
| 561 # Note: not compressed payload too small | |
| 562 self.assertEqual(f.status_code, 405) | |
| 563 expected = { 'Content-Type': 'application/json', | |
| 564 'Access-Control-Allow-Origin': '*', | |
| 565 'Access-Control-Allow-Headers': 'Content-Type, Authorization, X-HTTP-Method-Override', | |
| 566 'Allow': 'OPTIONS, GET, POST, PUT, DELETE, PATCH', | |
| 567 'Access-Control-Allow-Methods': 'HEAD, OPTIONS, GET, PUT, DELETE, PATCH', | |
| 568 } | |
| 569 | |
| 570 content = { "error": | |
| 571 { | |
| 572 "status": 405, | |
| 573 "msg": "'foo'" | |
| 574 } | |
| 575 } | |
| 576 json_dict = json.loads(b2s(f.content)) | |
| 577 | |
| 578 self.assertDictEqual(json_dict, content) | |
| 579 | |
| 580 # use dict comprehension to remove fields like date, | |
| 581 # content-length etc. from f.headers. | |
| 582 self.assertDictEqual({ key: value for (key, value) in f.headers.items() if key in expected }, expected) | |
| 583 | |
| 584 # test file x-fer | |
| 585 f = requests.get(self.url_base() + '/@@file/user_utils.js', | |
| 586 headers = { 'Accept-Encoding': 'br, foo', | |
| 587 'Accept': '*/*'}) | |
| 588 print(f.status_code) | |
| 589 print(f.headers) | |
| 590 | |
| 591 self.assertEqual(f.status_code, 200) | |
| 592 expected = { 'Content-Type': 'application/javascript', | |
| 593 'Content-Encoding': 'br', | |
| 594 'Vary': 'Accept-Encoding', | |
| 595 } | |
| 596 | |
| 597 try: | |
| 598 from urllib3.response import BrotliDecoder | |
| 599 # requests has decoded br to text for me | |
| 600 data = f.content | |
| 601 except ImportError: | |
| 602 # I need to decode | |
| 603 data = brotli.decompress(f.content) | |
| 604 | |
| 605 # check first few bytes. | |
| 606 self.assertEqual(b2s(data)[0:25], '// User Editing Utilities') | |
| 607 | |
| 608 # use dict comprehension to remove fields like date, | |
| 609 # content-length etc. from f.headers. | |
| 610 self.assertDictEqual({ key: value for (key, value) in | |
| 611 f.headers.items() if key in expected }, | |
| 612 expected) | |
| 613 | |
| 614 # test file x-fer | |
| 615 f = requests.get(self.url_base() + '/user1', | |
| 616 headers = { 'Accept-Encoding': 'br, foo', | |
| 617 'Accept': '*/*'}) | |
| 618 print(f.status_code) | |
| 619 print(f.headers) | |
| 620 | |
| 621 self.assertEqual(f.status_code, 200) | |
| 622 expected = { 'Content-Type': 'text/html; charset=utf-8', | |
| 623 'Content-Encoding': 'br', | |
| 624 'Vary': 'Accept-Encoding', | |
| 625 } | |
| 626 | |
| 627 try: | |
| 628 from urllib3.response import BrotliDecoder | |
| 629 # requests has decoded br to text for me | |
| 630 data = f.content | |
| 631 except ImportError: | |
| 632 # I need to decode | |
| 633 data = brotli.decompress(f.content) | |
| 634 | |
| 635 # check first few bytes. | |
| 636 self.assertEqual(b2s(data)[0:25], | |
| 637 '<!-- dollarId: user.item,') | |
| 638 | |
| 639 # use dict comprehension to remove fields like date, | |
| 640 # content-length etc. from f.headers. | |
| 641 self.assertDictEqual({ key: value for (key, value) in | |
| 642 f.headers.items() if key in expected }, | |
| 643 expected) | |
| 644 | |
| 645 | |
| 646 @skip_zstd | |
| 647 def test_compression_zstd(self): | |
| 648 # use basic auth for rest endpoint | |
| 649 f = requests.get(self.url_base() + '/rest/data/user/1/username', | |
| 650 auth=('admin', 'sekrit'), | |
| 651 headers = {'content-type': "", | |
| 652 'Accept-Encoding': 'zstd, foo', | |
| 653 'Accept': '*/*'}) | |
| 654 print(f.status_code) | |
| 655 print(f.headers) | |
| 656 | |
| 657 self.assertEqual(f.status_code, 200) | |
| 658 expected = { 'Content-Type': 'application/json', | |
| 659 'Access-Control-Allow-Origin': '*', | |
| 660 'Access-Control-Allow-Headers': 'Content-Type, Authorization, X-HTTP-Method-Override', | |
| 661 'Allow': 'OPTIONS, GET, POST, PUT, DELETE, PATCH', | |
| 662 'Access-Control-Allow-Methods': 'HEAD, OPTIONS, GET, PUT, DELETE, PATCH', | |
| 663 'Content-Encoding': 'zstd', | |
| 664 'Vary': 'Accept-Encoding', | |
| 665 } | |
| 666 | |
| 667 content_str = '''{ "data": { | |
| 668 "id": "1", | |
| 669 "link": "http://localhost:9001/rest/data/user/1/username", | |
| 670 "data": "admin" | |
| 671 } | |
| 672 }''' | |
| 673 content = json.loads(content_str) | |
| 674 | |
| 675 | |
| 676 if (type("") == type(f.content)): | |
| 677 json_dict = json.loads(f.content) | |
| 678 else: | |
| 679 json_dict = json.loads(b2s(zstd.decompress(f.content))) | |
| 680 | |
| 681 # etag wil not match, creation date different | |
| 682 del(json_dict['data']['@etag']) | |
| 683 | |
| 684 # type is "class 'str'" under py3, "type 'str'" py2 | |
| 685 # just skip comparing it. | |
| 686 del(json_dict['data']['type']) | |
| 687 | |
| 688 self.assertDictEqual(json_dict, content) | |
| 689 | |
| 690 # use dict comprehension to remove fields like date, | |
| 691 # content-length etc. from f.headers. | |
| 692 self.assertDictEqual({ key: value for (key, value) in f.headers.items() if key in expected }, expected) | |
| 693 | |
| 694 | |
| 695 | |
| 696 # use basic auth for rest endpoint, error case, bad attribute | |
| 697 f = requests.get(self.url_base() + '/rest/data/user/1/foo', | |
| 698 auth=('admin', 'sekrit'), | |
| 699 headers = {'content-type': "", | |
| 700 'Accept-Encoding': 'zstd, foo', | |
| 701 'Accept': '*/*'}) | |
| 702 print(f.status_code) | |
| 703 print(f.headers) | |
| 704 | |
| 705 # ERROR: attribute error turns into 405, not sure that's right. | |
| 706 # Note: not compressed, payload too small | |
| 707 self.assertEqual(f.status_code, 405) | |
| 708 expected = { 'Content-Type': 'application/json', | |
| 709 'Access-Control-Allow-Origin': '*', | |
| 710 'Access-Control-Allow-Headers': 'Content-Type, Authorization, X-HTTP-Method-Override', | |
| 711 'Allow': 'OPTIONS, GET, POST, PUT, DELETE, PATCH', | |
| 712 'Access-Control-Allow-Methods': 'HEAD, OPTIONS, GET, PUT, DELETE, PATCH', | |
| 713 } | |
| 714 | |
| 715 content = { "error": | |
| 716 { | |
| 717 "status": 405, | |
| 718 "msg": "'foo'" | |
| 719 } | |
| 720 } | |
| 721 | |
| 722 json_dict = json.loads(b2s(f.content)) | |
| 723 self.assertDictEqual(json_dict, content) | |
| 724 | |
| 725 # use dict comprehension to remove fields like date, | |
| 726 # content-length etc. from f.headers. | |
| 727 self.assertDictEqual({ key: value for (key, value) in f.headers.items() if key in expected }, expected) | |
| 728 | |
| 729 # test file x-fer | |
| 730 f = requests.get(self.url_base() + '/@@file/user_utils.js', | |
| 731 headers = { 'Accept-Encoding': 'zstd, foo', | |
| 732 'Accept': '*/*'}) | |
| 733 print(f.status_code) | |
| 734 print(f.headers) | |
| 735 | |
| 736 self.assertEqual(f.status_code, 200) | |
| 737 expected = { 'Content-Type': 'application/javascript', | |
| 738 'Content-Encoding': 'zstd', | |
| 739 'Vary': 'Accept-Encoding', | |
| 740 } | |
| 741 | |
| 742 # check first few bytes. | |
| 743 self.assertEqual(b2s(zstd.decompress(f.content)[0:25]), '// User Editing Utilities') | |
| 744 | |
| 745 # use dict comprehension to remove fields like date, | |
| 746 # content-length etc. from f.headers. | |
| 747 self.assertDictEqual({ key: value for (key, value) in | |
| 748 f.headers.items() if key in expected }, | |
| 749 expected) | |
| 750 | |
| 751 # test file x-fer | |
| 752 f = requests.get(self.url_base() + '/user1', | |
| 753 headers = { 'Accept-Encoding': 'zstd, foo', | |
| 754 'Accept': '*/*'}) | |
| 755 print(f.status_code) | |
| 756 print(f.headers) | |
| 757 | |
| 758 self.assertEqual(f.status_code, 200) | |
| 759 expected = { 'Content-Type': 'text/html; charset=utf-8', | |
| 760 'Content-Encoding': 'zstd', | |
| 761 'Vary': 'Accept-Encoding', | |
| 762 } | |
| 763 | |
| 764 # check first few bytes. | |
| 765 self.assertEqual(b2s(zstd.decompress(f.content)[0:25]), | |
| 766 '<!-- dollarId: user.item,') | |
| 767 | |
| 768 # use dict comprehension to remove fields like date, | |
| 769 # content-length etc. from f.headers. | |
| 770 self.assertDictEqual({ key: value for (key, value) in | |
| 771 f.headers.items() if key in expected }, | |
| 772 expected) |
