From 530619650757a48cbc82903b91494ca664006b1c Mon Sep 17 00:00:00 2001 From: Manuel Holtgrewe Date: Tue, 2 Jan 2024 12:30:34 +0100 Subject: [PATCH] feat: more robust backend code and cleanup in tests (#354) (#356) --- backend/Pipfile | 4 +- backend/Pipfile.lock | 1927 ++++++++--------- backend/app/api/api_v1/api.py | 5 +- .../app/api/api_v1/endpoints/acmgseqvar.py | 28 +- backend/app/api/api_v1/endpoints/bookmarks.py | 20 +- backend/app/api/api_v1/endpoints/caseinfo.py | 14 +- .../app/api/api_v1/endpoints/clinvarsub.py | 34 +- backend/app/api/internal/endpoints/remote.py | 57 +- backend/app/core/config.py | 2 +- backend/app/db/init_db.py | 6 +- backend/app/etc/utils.py | 8 +- backend/app/main.py | 10 + backend/setup.cfg | 3 + backend/tests/api/api_v1/test_acmgseqvar.py | 348 +-- backend/tests/api/api_v1/test_adminmsgs.py | 2 + backend/tests/api/api_v1/test_bookmarks.py | 304 ++- backend/tests/api/api_v1/test_caseinfo.py | 403 ++-- backend/tests/api/api_v1/test_clinvarsub.py | 40 +- backend/tests/api/api_v1/test_utils.py | 21 +- backend/tests/api/internal/test_proxy.py | 26 +- backend/tests/api/internal/test_remote.py | 42 +- backend/tests/conftest.py | 46 +- backend/tests/crud/test_acmgseqvar.py | 8 + backend/tests/crud/test_adminmsg.py | 6 + backend/tests/crud/test_bookmark.py | 8 + backend/tests/crud/test_caseinfo.py | 8 + backend/tests/crud/test_clinvarsub.py | 18 + backend/tests/etc/test_utils.py | 13 +- backend/tests/test_main.py | 17 +- docs/dev_backend.rst | 15 + docs/dev_frontend.rst | 1 + 31 files changed, 1823 insertions(+), 1621 deletions(-) diff --git a/backend/Pipfile b/backend/Pipfile index 00de6ecb..b61eab7b 100644 --- a/backend/Pipfile +++ b/backend/Pipfile @@ -16,13 +16,10 @@ pydantic = {extras = ["email"], version = "*"} pydantic-settings = "*" python-dateutil = "*" python-dotenv = "*" -requests-mock = "*" sqlalchemy = "*" tenacity = "*" uvicorn = "*" alembic = "*" -requests = "*" -types-requests = "*" greenlet = "*" emails = "*" jinja2 = "*" @@ -61,6 +58,7 @@ watchdog = "*" celery-types = "*" jupyterlab = "*" pytest-mock = "*" +gevent = "*" [requires] python_version = "3.10" diff --git a/backend/Pipfile.lock b/backend/Pipfile.lock index c7e4b520..56b92399 100644 --- a/backend/Pipfile.lock +++ b/backend/Pipfile.lock @@ -1,7 +1,7 @@ { "_meta": { "hash": { - "sha256": "b2dbef0592f1a1315fbcdbbb1422114e4890976b27b73f2ff4cde07f3862e3e6" + "sha256": "9b08b1b60774b77d3d4f163974dfb5e3db142b43ef0d43bcc3ff7ace2083c82d" }, "pipfile-spec": 6, "requires": { @@ -18,12 +18,12 @@ "default": { "alembic": { "hashes": [ - "sha256:a23974ea301c3ee52705db809c7413cecd165290c6679b9998dd6c74342ca23a", - "sha256:ab4b3b94d2e1e5f81e34be8a9b7b7575fc9dd5398fccb0bef351ec9b14872623" + "sha256:2edcc97bed0bd3272611ce3a98d98279e9c209e7186e43e75bbb1b2bdfdbcc43", + "sha256:4932c8558bf68f2ee92b9bbcb8218671c627064d5b08939437af6d77dc05e595" ], "index": "pypi", "markers": "python_version >= '3.8'", - "version": "==1.13.0" + "version": "==1.13.1" }, "amqp": { "hashes": [ @@ -43,11 +43,11 @@ }, "anyio": { "hashes": [ - "sha256:44a3c9aba0f5defa43261a8b3efb97891f2bd7d804e0e1f56419befa1adfc780", - "sha256:91dee416e570e92c64041bd18b900d1d6fa78dff7048769ce5ac5ddad004fbb5" + "sha256:745843b39e829e108e518c489b31dc757de7d2131d53fac32bd8df268227bfee", + "sha256:e1875bb4b4e2de1669f4bc7869b6d3f54231cdced71605e6e64c9be77e3be50f" ], - "markers": "python_version >= '3.7'", - "version": "==3.7.1" + "markers": "python_version >= '3.8'", + "version": "==4.2.0" }, "asgiref": { "hashes": [ @@ -116,11 +116,11 @@ }, "attrs": { "hashes": [ - "sha256:1f28b4522cdc2fb4256ac1a020c78acf9cba2c6b461ccd2c126f3aa8e8335d04", - "sha256:6279836d581513a26f1bf235f9acd333bc9115683f14f7e8fae46c98fc50e015" + "sha256:935dc3b529c262f6cf76e50877d35a4bd3c1de194fd41f47a2b7ae8f19971f30", + "sha256:99b87a485a5820b23b879f04c2305b44b951b502fd64be915879d77a7e8fc6f1" ], "markers": "python_version >= '3.7'", - "version": "==23.1.0" + "version": "==23.2.0" }, "bcrypt": { "hashes": [ @@ -466,12 +466,12 @@ }, "fastapi": { "hashes": [ - "sha256:4d12838819aa52af244580675825e750ad67c9df4614f557a769606af902cf22", - "sha256:f19ebf6fdc82a3281d10f2cb4774bdfa90238e3b40af3525a0c09fd08ad1c480" + "sha256:5056e504ac6395bf68493d71fcfc5352fdbd5fda6f88c21f6420d80d81163296", + "sha256:8c7bc6d315da963ee4cdb605557827071a9a7f95aeb8fcdd3bde48cdc8764dd7" ], "index": "pypi", "markers": "python_version >= '3.8'", - "version": "==0.105.0" + "version": "==0.108.0" }, "fastapi-pagination": { "hashes": [ @@ -504,68 +504,68 @@ }, "greenlet": { "hashes": [ - "sha256:006c1028ac0cfcc4e772980cfe73f5476041c8c91d15d64f52482fc571149d46", - "sha256:0acadbc3f72cb0ee85070e8d36bd2a4673d2abd10731ee73c10222cf2dd4713c", - "sha256:0c0fdb8142742ee68e97c106eb81e7d3e883cc739d9c5f2b28bc38a7bafeb6d1", - "sha256:0df7eed98ea23b20e9db64d46eb05671ba33147df9405330695bcd81a73bb0c9", - "sha256:10d247260db20887ae8857c0cbc750b9170f0b067dd7d38fb68a3f2334393bd3", - "sha256:14b5d999aefe9ffd2049ad19079f733c3aaa426190ffecadb1d5feacef8fe397", - "sha256:18fe39d70d482b22f0014e84947c5aaa7211fb8e13dc4cc1c43ed2aa1db06d9a", - "sha256:1c1129bc47266d83444c85a8e990ae22688cf05fb20d7951fd2866007c2ba9bc", - "sha256:1dac09e3c0b78265d2e6d3cbac2d7c48bd1aa4b04a8ffeda3adde9f1688df2c3", - "sha256:2c93cd03acb1499ee4de675e1a4ed8eaaa7227f7949dc55b37182047b006a7aa", - "sha256:2e9c5423046eec21f6651268cb674dfba97280701e04ef23d312776377313206", - "sha256:2ee59c4627c8c4bb3e15949fbcd499abd6b7f4ad9e0bfcb62c65c5e2cabe0ec4", - "sha256:339c0272a62fac7e602e4e6ec32a64ff9abadc638b72f17f6713556ed011d493", - "sha256:38878744926cec29b5cc3654ef47f3003f14bfbba7230e3c8492393fe29cc28b", - "sha256:3e4bfa752b3688d74ab1186e2159779ff4867644d2b1ebf16db14281f0445377", - "sha256:520fcb53a39ef90f5021c77606952dbbc1da75d77114d69b8d7bded4a8e1a813", - "sha256:5f9ea7c2c9795549653b6f7569f6bc75d2c7d1f6b2854eb8ce0bc6ec3cb2dd88", - "sha256:654b84c9527182036747938b81938f1d03fb8321377510bc1854a9370418ab66", - "sha256:6d65bec56a7bc352bcf11b275b838df618651109074d455a772d3afe25390b7d", - "sha256:7363756cc439a503505b67983237d1cc19139b66488263eb19f5719a32597836", - "sha256:80d068e4b6e2499847d916ef64176811ead6bf210a610859220d537d935ec6fd", - "sha256:8756a94ed8f293450b0e91119eca2a36332deba69feb2f9ca410d35e74eae1e4", - "sha256:89a6f6ddcbef4000cda7e205c4c20d319488ff03db961d72d4e73519d2465309", - "sha256:8f34a765c5170c0673eb747213a0275ecc749ab3652bdbec324621ed5b2edaef", - "sha256:8f8d14a0a4e8c670fbce633d8b9a1ee175673a695475acd838e372966845f764", - "sha256:950e21562818f9c771989b5b65f990e76f4ac27af66e1bb34634ae67886ede2a", - "sha256:9560c580c896030ff9c311c603aaf2282234643c90d1dec738a1d93e3e53cd51", - "sha256:9acd8fd67c248b8537953cb3af8787c18a87c33d4dcf6830e410ee1f95a63fd4", - "sha256:a37ae53cca36823597fd5f65341b6f7bac2dd69ecd6ca01334bb795460ab150b", - "sha256:aecea0442975741e7d69daff9b13c83caff8c13eeb17485afa65f6360a045765", - "sha256:b1405614692ac986490d10d3e1a05e9734f473750d4bee3cf7d1286ef7af7da6", - "sha256:b1fd25dfc5879a82103b3d9e43fa952e3026c221996ff4d32a9c72052544835d", - "sha256:b2cedf279ca38ef3f4ed0d013a6a84a7fc3d9495a716b84a5fc5ff448965f251", - "sha256:b3f0497db77cfd034f829678b28267eeeeaf2fc21b3f5041600f7617139e6773", - "sha256:bfcecc984d60b20ffe30173b03bfe9ba6cb671b0be1e95c3e2056d4fe7006590", - "sha256:c1f647fe5b94b51488b314c82fdda10a8756d650cee8d3cd29f657c6031bdf73", - "sha256:c235131bf59d2546bb3ebaa8d436126267392f2e51b85ff45ac60f3a26549af0", - "sha256:c27b142a9080bdd5869a2fa7ebf407b3c0b24bd812db925de90e9afe3c417fd6", - "sha256:c42bb589e6e9f9d8bdd79f02f044dff020d30c1afa6e84c0b56d1ce8a324553c", - "sha256:cd5bc4fde0842ff2b9cf33382ad0b4db91c2582db836793d58d174c569637144", - "sha256:cecfdc950dd25f25d6582952e58521bca749cf3eeb7a9bad69237024308c8196", - "sha256:d1fceb5351ab1601903e714c3028b37f6ea722be6873f46e349a960156c05650", - "sha256:d4d0df07a38e41a10dfb62c6fc75ede196572b580f48ee49b9282c65639f3965", - "sha256:d5547b462b8099b84746461e882a3eb8a6e3f80be46cb6afb8524eeb191d1a30", - "sha256:d64643317e76b4b41fdba659e7eca29634e5739b8bc394eda3a9127f697ed4b0", - "sha256:db4233358d3438369051a2f290f1311a360d25c49f255a6c5d10b5bcb3aa2b49", - "sha256:e0e28f5233d64c693382f66d47c362b72089ebf8ac77df7e12ac705c9fa1163d", - "sha256:e79fb5a9fb2d0bd3b6573784f5e5adabc0b0566ad3180a028af99523ce8f6138", - "sha256:e84bef3cfb6b6bfe258c98c519811c240dbc5b33a523a14933a252e486797c90", - "sha256:ed1a8a08de7f68506a38f9a2ddb26bbd1480689e66d788fcd4b5f77e2d9ecfcc", - "sha256:ed9bf77b41798e8417657245b9f3649314218a4a17aefb02bb3992862df32495", - "sha256:edf7a1daba1f7c54326291a8cde58da86ab115b78c91d502be8744f0aa8e3ffa", - "sha256:f260e6c2337871a52161824058923df2bbddb38bc11a5cbe71f3474d877c5bd9", - "sha256:f27aa32466993c92d326df982c4acccd9530fe354e938d9e9deada563e71ce76", - "sha256:f4cf532bf3c58a862196b06947b1b5cc55503884f9b63bf18582a75228d9950e", - "sha256:fb5d60805057d8948065338be6320d35e26b0a72f45db392eb32b70dd6dc9227", - "sha256:fc14dd9554f88c9c1fe04771589ae24db76cd56c8f1104e4381b383d6b71aff8", - "sha256:fefd5eb2c0b1adffdf2802ff7df45bfe65988b15f6b972706a0e55d451bffaea" + "sha256:01bc7ea167cf943b4c802068e178bbf70ae2e8c080467070d01bfa02f337ee67", + "sha256:0448abc479fab28b00cb472d278828b3ccca164531daab4e970a0458786055d6", + "sha256:086152f8fbc5955df88382e8a75984e2bb1c892ad2e3c80a2508954e52295257", + "sha256:098d86f528c855ead3479afe84b49242e174ed262456c342d70fc7f972bc13c4", + "sha256:149e94a2dd82d19838fe4b2259f1b6b9957d5ba1b25640d2380bea9c5df37676", + "sha256:1551a8195c0d4a68fac7a4325efac0d541b48def35feb49d803674ac32582f61", + "sha256:15d79dd26056573940fcb8c7413d84118086f2ec1a8acdfa854631084393efcc", + "sha256:1996cb9306c8595335bb157d133daf5cf9f693ef413e7673cb07e3e5871379ca", + "sha256:1a7191e42732df52cb5f39d3527217e7ab73cae2cb3694d241e18f53d84ea9a7", + "sha256:1ea188d4f49089fc6fb283845ab18a2518d279c7cd9da1065d7a84e991748728", + "sha256:1f672519db1796ca0d8753f9e78ec02355e862d0998193038c7073045899f305", + "sha256:2516a9957eed41dd8f1ec0c604f1cdc86758b587d964668b5b196a9db5bfcde6", + "sha256:2797aa5aedac23af156bbb5a6aa2cd3427ada2972c828244eb7d1b9255846379", + "sha256:2dd6e660effd852586b6a8478a1d244b8dc90ab5b1321751d2ea15deb49ed414", + "sha256:3ddc0f794e6ad661e321caa8d2f0a55ce01213c74722587256fb6566049a8b04", + "sha256:3ed7fb269f15dc662787f4119ec300ad0702fa1b19d2135a37c2c4de6fadfd4a", + "sha256:419b386f84949bf0e7c73e6032e3457b82a787c1ab4a0e43732898a761cc9dbf", + "sha256:43374442353259554ce33599da8b692d5aa96f8976d567d4badf263371fbe491", + "sha256:52f59dd9c96ad2fc0d5724107444f76eb20aaccb675bf825df6435acb7703559", + "sha256:57e8974f23e47dac22b83436bdcf23080ade568ce77df33159e019d161ce1d1e", + "sha256:5b51e85cb5ceda94e79d019ed36b35386e8c37d22f07d6a751cb659b180d5274", + "sha256:649dde7de1a5eceb258f9cb00bdf50e978c9db1b996964cd80703614c86495eb", + "sha256:64d7675ad83578e3fc149b617a444fab8efdafc9385471f868eb5ff83e446b8b", + "sha256:68834da854554926fbedd38c76e60c4a2e3198c6fbed520b106a8986445caaf9", + "sha256:6b66c9c1e7ccabad3a7d037b2bcb740122a7b17a53734b7d72a344ce39882a1b", + "sha256:70fb482fdf2c707765ab5f0b6655e9cfcf3780d8d87355a063547b41177599be", + "sha256:7170375bcc99f1a2fbd9c306f5be8764eaf3ac6b5cb968862cad4c7057756506", + "sha256:73a411ef564e0e097dbe7e866bb2dda0f027e072b04da387282b02c308807405", + "sha256:77457465d89b8263bca14759d7c1684df840b6811b2499838cc5b040a8b5b113", + "sha256:7f362975f2d179f9e26928c5b517524e89dd48530a0202570d55ad6ca5d8a56f", + "sha256:81bb9c6d52e8321f09c3d165b2a78c680506d9af285bfccbad9fb7ad5a5da3e5", + "sha256:881b7db1ebff4ba09aaaeae6aa491daeb226c8150fc20e836ad00041bcb11230", + "sha256:894393ce10ceac937e56ec00bb71c4c2f8209ad516e96033e4b3b1de270e200d", + "sha256:99bf650dc5d69546e076f413a87481ee1d2d09aaaaaca058c9251b6d8c14783f", + "sha256:9da2bd29ed9e4f15955dd1595ad7bc9320308a3b766ef7f837e23ad4b4aac31a", + "sha256:afaff6cf5200befd5cec055b07d1c0a5a06c040fe5ad148abcd11ba6ab9b114e", + "sha256:b1b5667cced97081bf57b8fa1d6bfca67814b0afd38208d52538316e9422fc61", + "sha256:b37eef18ea55f2ffd8f00ff8fe7c8d3818abd3e25fb73fae2ca3b672e333a7a6", + "sha256:b542be2440edc2d48547b5923c408cbe0fc94afb9f18741faa6ae970dbcb9b6d", + "sha256:b7dcbe92cc99f08c8dd11f930de4d99ef756c3591a5377d1d9cd7dd5e896da71", + "sha256:b7f009caad047246ed379e1c4dbcb8b020f0a390667ea74d2387be2998f58a22", + "sha256:bba5387a6975598857d86de9eac14210a49d554a77eb8261cc68b7d082f78ce2", + "sha256:c5e1536de2aad7bf62e27baf79225d0d64360d4168cf2e6becb91baf1ed074f3", + "sha256:c5ee858cfe08f34712f548c3c363e807e7186f03ad7a5039ebadb29e8c6be067", + "sha256:c9db1c18f0eaad2f804728c67d6c610778456e3e1cc4ab4bbd5eeb8e6053c6fc", + "sha256:d353cadd6083fdb056bb46ed07e4340b0869c305c8ca54ef9da3421acbdf6881", + "sha256:d46677c85c5ba00a9cb6f7a00b2bfa6f812192d2c9f7d9c4f6a55b60216712f3", + "sha256:d4d1ac74f5c0c0524e4a24335350edad7e5f03b9532da7ea4d3c54d527784f2e", + "sha256:d73a9fe764d77f87f8ec26a0c85144d6a951a6c438dfe50487df5595c6373eac", + "sha256:da70d4d51c8b306bb7a031d5cff6cc25ad253affe89b70352af5f1cb68e74b53", + "sha256:daf3cb43b7cf2ba96d614252ce1684c1bccee6b2183a01328c98d36fcd7d5cb0", + "sha256:dca1e2f3ca00b84a396bc1bce13dd21f680f035314d2379c4160c98153b2059b", + "sha256:dd4f49ae60e10adbc94b45c0b5e6a179acc1736cf7a90160b404076ee283cf83", + "sha256:e1f145462f1fa6e4a4ae3c0f782e580ce44d57c8f2c7aae1b6fa88c0b2efdb41", + "sha256:e3391d1e16e2a5a1507d83e4a8b100f4ee626e8eca43cf2cadb543de69827c4c", + "sha256:fcd2469d6a2cf298f198f0487e0a5b1a47a42ca0fa4dfd1b6862c999f018ebbf", + "sha256:fd096eb7ffef17c456cfa587523c5f92321ae02427ff955bebe9e3c63bc9f0da", + "sha256:fe754d231288e1e64323cfad462fcee8f0288654c10bdf4f603a39ed923bef33" ], "index": "pypi", "markers": "python_version >= '3.7'", - "version": "==3.0.2" + "version": "==3.0.3" }, "h11": { "hashes": [ @@ -635,11 +635,11 @@ }, "jsonschema-specifications": { "hashes": [ - "sha256:9472fc4fea474cd74bea4a2b190daeccb5a9e4db2ea80efcf7a1b582fc9a81b8", - "sha256:e74ba7c0a65e8cb49dc26837d6cfe576557084a8b423ed16a420984228104f93" + "sha256:48a76787b3e70f5ed53f1160d2b81f586e4ca6d1548c5de7085d1682674764cc", + "sha256:87e4fdf3a94858b8a2ba2778d9ba57d8a9cafca7c7489c46ba0d30a8bc6a9c3c" ], "markers": "python_version >= '3.8'", - "version": "==2023.11.2" + "version": "==2023.12.1" }, "kombu": { "hashes": [ @@ -658,101 +658,97 @@ }, "lxml": { "hashes": [ - "sha256:05186a0f1346ae12553d66df1cfce6f251589fea3ad3da4f3ef4e34b2d58c6a3", - "sha256:075b731ddd9e7f68ad24c635374211376aa05a281673ede86cbe1d1b3455279d", - "sha256:081d32421db5df44c41b7f08a334a090a545c54ba977e47fd7cc2deece78809a", - "sha256:0a3d3487f07c1d7f150894c238299934a2a074ef590b583103a45002035be120", - "sha256:0bfd0767c5c1de2551a120673b72e5d4b628737cb05414f03c3277bf9bed3305", - "sha256:0c0850c8b02c298d3c7006b23e98249515ac57430e16a166873fc47a5d549287", - "sha256:0e2cb47860da1f7e9a5256254b74ae331687b9672dfa780eed355c4c9c3dbd23", - "sha256:120fa9349a24c7043854c53cae8cec227e1f79195a7493e09e0c12e29f918e52", - "sha256:1247694b26342a7bf47c02e513d32225ededd18045264d40758abeb3c838a51f", - "sha256:141f1d1a9b663c679dc524af3ea1773e618907e96075262726c7612c02b149a4", - "sha256:14e019fd83b831b2e61baed40cab76222139926b1fb5ed0e79225bc0cae14584", - "sha256:1509dd12b773c02acd154582088820893109f6ca27ef7291b003d0e81666109f", - "sha256:17a753023436a18e27dd7769e798ce302963c236bc4114ceee5b25c18c52c693", - "sha256:1e224d5755dba2f4a9498e150c43792392ac9b5380aa1b845f98a1618c94eeef", - "sha256:1f447ea5429b54f9582d4b955f5f1985f278ce5cf169f72eea8afd9502973dd5", - "sha256:23eed6d7b1a3336ad92d8e39d4bfe09073c31bfe502f20ca5116b2a334f8ec02", - "sha256:25f32acefac14ef7bd53e4218fe93b804ef6f6b92ffdb4322bb6d49d94cad2bc", - "sha256:2c74524e179f2ad6d2a4f7caf70e2d96639c0954c943ad601a9e146c76408ed7", - "sha256:303bf1edce6ced16bf67a18a1cf8339d0db79577eec5d9a6d4a80f0fb10aa2da", - "sha256:3331bece23c9ee066e0fb3f96c61322b9e0f54d775fccefff4c38ca488de283a", - "sha256:3e9bdd30efde2b9ccfa9cb5768ba04fe71b018a25ea093379c857c9dad262c40", - "sha256:411007c0d88188d9f621b11d252cce90c4a2d1a49db6c068e3c16422f306eab8", - "sha256:42871176e7896d5d45138f6d28751053c711ed4d48d8e30b498da155af39aebd", - "sha256:46f409a2d60f634fe550f7133ed30ad5321ae2e6630f13657fb9479506b00601", - "sha256:48628bd53a426c9eb9bc066a923acaa0878d1e86129fd5359aee99285f4eed9c", - "sha256:48d6ed886b343d11493129e019da91d4039826794a3e3027321c56d9e71505be", - "sha256:4930be26af26ac545c3dffb662521d4e6268352866956672231887d18f0eaab2", - "sha256:4aec80cde9197340bc353d2768e2a75f5f60bacda2bab72ab1dc499589b3878c", - "sha256:4c28a9144688aef80d6ea666c809b4b0e50010a2aca784c97f5e6bf143d9f129", - "sha256:4d2d1edbca80b510443f51afd8496be95529db04a509bc8faee49c7b0fb6d2cc", - "sha256:4dd9a263e845a72eacb60d12401e37c616438ea2e5442885f65082c276dfb2b2", - "sha256:4f1026bc732b6a7f96369f7bfe1a4f2290fb34dce00d8644bc3036fb351a4ca1", - "sha256:4fb960a632a49f2f089d522f70496640fdf1218f1243889da3822e0a9f5f3ba7", - "sha256:50670615eaf97227d5dc60de2dc99fb134a7130d310d783314e7724bf163f75d", - "sha256:50baa9c1c47efcaef189f31e3d00d697c6d4afda5c3cde0302d063492ff9b477", - "sha256:53ace1c1fd5a74ef662f844a0413446c0629d151055340e9893da958a374f70d", - "sha256:5515edd2a6d1a5a70bfcdee23b42ec33425e405c5b351478ab7dc9347228f96e", - "sha256:56dc1f1ebccc656d1b3ed288f11e27172a01503fc016bcabdcbc0978b19352b7", - "sha256:578695735c5a3f51569810dfebd05dd6f888147a34f0f98d4bb27e92b76e05c2", - "sha256:57aba1bbdf450b726d58b2aea5fe47c7875f5afb2c4a23784ed78f19a0462574", - "sha256:57d6ba0ca2b0c462f339640d22882acc711de224d769edf29962b09f77129cbf", - "sha256:5c245b783db29c4e4fbbbfc9c5a78be496c9fea25517f90606aa1f6b2b3d5f7b", - "sha256:5c31c7462abdf8f2ac0577d9f05279727e698f97ecbb02f17939ea99ae8daa98", - "sha256:64f479d719dc9f4c813ad9bb6b28f8390360660b73b2e4beb4cb0ae7104f1c12", - "sha256:65299ea57d82fb91c7f019300d24050c4ddeb7c5a190e076b5f48a2b43d19c42", - "sha256:6689a3d7fd13dc687e9102a27e98ef33730ac4fe37795d5036d18b4d527abd35", - "sha256:690dafd0b187ed38583a648076865d8c229661ed20e48f2335d68e2cf7dc829d", - "sha256:6fc3c450eaa0b56f815c7b62f2b7fba7266c4779adcf1cece9e6deb1de7305ce", - "sha256:704f61ba8c1283c71b16135caf697557f5ecf3e74d9e453233e4771d68a1f42d", - "sha256:71c52db65e4b56b8ddc5bb89fb2e66c558ed9d1a74a45ceb7dcb20c191c3df2f", - "sha256:71d66ee82e7417828af6ecd7db817913cb0cf9d4e61aa0ac1fde0583d84358db", - "sha256:7d298a1bd60c067ea75d9f684f5f3992c9d6766fadbc0bcedd39750bf344c2f4", - "sha256:8b77946fd508cbf0fccd8e400a7f71d4ac0e1595812e66025bac475a8e811694", - "sha256:8d7e43bd40f65f7d97ad8ef5c9b1778943d02f04febef12def25f7583d19baac", - "sha256:8df133a2ea5e74eef5e8fc6f19b9e085f758768a16e9877a60aec455ed2609b2", - "sha256:8ed74706b26ad100433da4b9d807eae371efaa266ffc3e9191ea436087a9d6a7", - "sha256:92af161ecbdb2883c4593d5ed4815ea71b31fafd7fd05789b23100d081ecac96", - "sha256:97047f0d25cd4bcae81f9ec9dc290ca3e15927c192df17331b53bebe0e3ff96d", - "sha256:9719fe17307a9e814580af1f5c6e05ca593b12fb7e44fe62450a5384dbf61b4b", - "sha256:9767e79108424fb6c3edf8f81e6730666a50feb01a328f4a016464a5893f835a", - "sha256:9a92d3faef50658dd2c5470af249985782bf754c4e18e15afb67d3ab06233f13", - "sha256:9bb6ad405121241e99a86efff22d3ef469024ce22875a7ae045896ad23ba2340", - "sha256:9e28c51fa0ce5674be9f560c6761c1b441631901993f76700b1b30ca6c8378d6", - "sha256:aca086dc5f9ef98c512bac8efea4483eb84abbf926eaeedf7b91479feb092458", - "sha256:ae8b9c6deb1e634ba4f1930eb67ef6e6bf6a44b6eb5ad605642b2d6d5ed9ce3c", - "sha256:b0a545b46b526d418eb91754565ba5b63b1c0b12f9bd2f808c852d9b4b2f9b5c", - "sha256:b4e4bc18382088514ebde9328da057775055940a1f2e18f6ad2d78aa0f3ec5b9", - "sha256:b6420a005548ad52154c8ceab4a1290ff78d757f9e5cbc68f8c77089acd3c432", - "sha256:b86164d2cff4d3aaa1f04a14685cbc072efd0b4f99ca5708b2ad1b9b5988a991", - "sha256:bb3bb49c7a6ad9d981d734ef7c7193bc349ac338776a0360cc671eaee89bcf69", - "sha256:bef4e656f7d98aaa3486d2627e7d2df1157d7e88e7efd43a65aa5dd4714916cf", - "sha256:c0781a98ff5e6586926293e59480b64ddd46282953203c76ae15dbbbf302e8bb", - "sha256:c2006f5c8d28dee289f7020f721354362fa304acbaaf9745751ac4006650254b", - "sha256:c41bfca0bd3532d53d16fd34d20806d5c2b1ace22a2f2e4c0008570bf2c58833", - "sha256:cd47b4a0d41d2afa3e58e5bf1f62069255aa2fd6ff5ee41604418ca925911d76", - "sha256:cdb650fc86227eba20de1a29d4b2c1bfe139dc75a0669270033cb2ea3d391b85", - "sha256:cef2502e7e8a96fe5ad686d60b49e1ab03e438bd9123987994528febd569868e", - "sha256:d27be7405547d1f958b60837dc4c1007da90b8b23f54ba1f8b728c78fdb19d50", - "sha256:d37017287a7adb6ab77e1c5bee9bcf9660f90ff445042b790402a654d2ad81d8", - "sha256:d3ff32724f98fbbbfa9f49d82852b159e9784d6094983d9a8b7f2ddaebb063d4", - "sha256:d73d8ecf8ecf10a3bd007f2192725a34bd62898e8da27eb9d32a58084f93962b", - "sha256:dd708cf4ee4408cf46a48b108fb9427bfa00b9b85812a9262b5c668af2533ea5", - "sha256:e3cd95e10c2610c360154afdc2f1480aea394f4a4f1ea0a5eacce49640c9b190", - "sha256:e4da8ca0c0c0aea88fd46be8e44bd49716772358d648cce45fe387f7b92374a7", - "sha256:eadfbbbfb41b44034a4c757fd5d70baccd43296fb894dba0295606a7cf3124aa", - "sha256:ed667f49b11360951e201453fc3967344d0d0263aa415e1619e85ae7fd17b4e0", - "sha256:f3df3db1d336b9356dd3112eae5f5c2b8b377f3bc826848567f10bfddfee77e9", - "sha256:f6bdac493b949141b733c5345b6ba8f87a226029cbabc7e9e121a413e49441e0", - "sha256:fbf521479bcac1e25a663df882c46a641a9bff6b56dc8b0fafaebd2f66fb231b", - "sha256:fc9b106a1bf918db68619fdcd6d5ad4f972fdd19c01d19bdb6bf63f3589a9ec5", - "sha256:fcdd00edfd0a3001e0181eab3e63bd5c74ad3e67152c84f93f13769a40e073a7", - "sha256:fe4bda6bd4340caa6e5cf95e73f8fea5c4bfc55763dd42f1b50a94c1b4a2fbd4" + "sha256:016de3b29a262655fc3d2075dc1b2611f84f4c3d97a71d579c883d45e201eee4", + "sha256:0326e9b8176ea77269fb39e7af4010906e73e9496a9f8eaf06d253b1b1231ceb", + "sha256:03290e2f714f2e7431c8430c08b48167f657da7bc689c6248e828ff3c66d5b1b", + "sha256:049fef98d02513c34f5babd07569fc1cf1ed14c0f2fbff18fe72597f977ef3c2", + "sha256:07a900735bad9af7be3085480bf384f68ed5580ba465b39a098e6a882c060d6b", + "sha256:0d277d4717756fe8816f0beeff229cb72f9dd02a43b70e1d3f07c8efadfb9fe1", + "sha256:173bcead3af5d87c7bca9a030675073ddaad8e0a9f0b04be07cd9390453e7226", + "sha256:1ef0793e1e2dd221fce7c142177008725680f7b9e4a184ab108d90d5d3ab69b7", + "sha256:21af2c3862db6f4f486cddf73ec1157b40d5828876c47cd880edcbad8240ea1b", + "sha256:2219cbf790e701acf9a21a31ead75f983e73daf0eceb9da6990212e4d20ebefe", + "sha256:2992591e2294bb07faf7f5f6d5cb60710c046404f4bfce09fb488b85d2a8f58f", + "sha256:3663542aee845129a981889c19b366beab0b1dadcf5ca164696aabfe1aa51667", + "sha256:3e6cbb68bf70081f036bfc018649cf4b46c4e7eaf7860a277cae92dee2a57f69", + "sha256:3f908afd0477cace17f941d1b9cfa10b769fe1464770abe4cfb3d9f35378d0f8", + "sha256:3ffa066db40b0347e48334bd4465de768e295a3525b9a59831228b5f4f93162d", + "sha256:405e3760f83a8ba3bdb6e622ec79595cdc20db916ce37377bbcb95b5711fa4ca", + "sha256:44fa9afd632210f1eeda51cf284ed8dbab0c7ec8b008dd39ba02818e0e114e69", + "sha256:4786b0af7511ea614fd86407a52a7bc161aa5772d311d97df2591ed2351de768", + "sha256:4a45a278518e4308865c1e9dbb2c42ce84fb154efb03adeb16fdae3c1687c7c9", + "sha256:4b9d5b01900a760eb3acf6cef50aead4ef2fa79e7ddb927084244e41dfe37b65", + "sha256:4e69c36c8618707a90ed3fb6f48a6cc9254ffcdbf7b259e439a5ae5fbf9c5206", + "sha256:52a9ab31853d3808e7cf0183b3a5f7e8ffd622ea4aee1deb5252dbeaefd5b40d", + "sha256:52c0acc2f29b0a204efc11a5ed911a74f50a25eb7d7d5069c2b1fd3b3346ce11", + "sha256:5382612ba2424cea5d2c89e2c29077023d8de88f8d60d5ceff5f76334516df9e", + "sha256:581a78f299a9f5448b2c3aea904bfcd17c59bf83016d221d7f93f83633bb2ab2", + "sha256:583c0e15ae06adc81035346ae2abb2e748f0b5197e7740d8af31222db41bbf7b", + "sha256:59cea9ba1c675fbd6867ca1078fc717a113e7f5b7644943b74137b7cc55abebf", + "sha256:5b39f63edbe7e018c2ac1cf0259ee0dd2355274e8a3003d404699b040782e55e", + "sha256:5eff173f0ff408bfa578cbdafd35a7e0ca94d1a9ffe09a8a48e0572d0904d486", + "sha256:5fb988e15378d6e905ca8f60813950a0c56da9469d0e8e5d8fe785b282684ec5", + "sha256:6507c58431dbd95b50654b3313c5ad54f90e54e5f2cdacf733de61eae478eec5", + "sha256:6a2de85deabf939b0af89e2e1ea46bfb1239545e2da6f8ac96522755a388025f", + "sha256:6a5501438dd521bb7e0dde5008c40c7bfcfaafaf86eccb3f9bd27509abb793da", + "sha256:6bba06d8982be0f0f6432d289a8d104417a0ab9ed04114446c4ceb6d4a40c65d", + "sha256:70ab4e02f7aa5fb4131c8b222a111ce7676f3767e36084fba3a4e7338dc82dcd", + "sha256:7188495c1bf71bfda87d78ed50601e72d252119ce11710d6e71ff36e35fea5a0", + "sha256:71a7cee869578bc17b18050532bb2f0bc682a7b97dda77041741a1bd2febe6c7", + "sha256:73bfab795d354aaf2f4eb7a5b0db513031734fd371047342d5803834ce19ec18", + "sha256:766868f729f3ab84125350f1a0ea2594d8b1628a608a574542a5aff7355b9941", + "sha256:77b73952534967a4497d9e4f26fbeebfba19950cbc66b7cc3a706214429d8106", + "sha256:78d6d8e5b54ed89dc0f0901eaaa579c384ad8d59fa43cc7fb06e9bb89115f8f4", + "sha256:793be9b4945c2dfd69828fb5948d7d9569b78e0599e4a2e88d92affeb0ff3aa3", + "sha256:7ba26a7dc929a1b3487d51bbcb0099afed2fc06e891b82845c8f37a2d7d7fbbd", + "sha256:7df433d08d4587dc3932f7fcfc3194519a6824824104854e76441fd3bc000d29", + "sha256:80209b31dd3908bc5b014f540fd192c97ea52ab179713a730456c5baf7ce80c1", + "sha256:8134d5441d1ed6a682e3de3d7a98717a328dce619ee9c4c8b3b91f0cb0eb3e28", + "sha256:81509dffd8aba3bdb43e90cbd218c9c068a1f4047d97bc9546b3ac9e3a4ae81d", + "sha256:88f559f8beb6b90e41a7faae4aca4c8173a4819874a9bf8e74c8d7c1d51f3162", + "sha256:894c5f71186b410679aaab5774543fcb9cbabe8893f0b31d11cf28a0740e80be", + "sha256:8cc0a951e5616ac626f7036309c41fb9774adcd4aa7db0886463da1ce5b65edb", + "sha256:8ce8b468ab50f9e944719d1134709ec11fe0d2840891a6cae369e22141b1094c", + "sha256:904d36165848b59c4e04ae5b969072e602bd987485076fca8ec42c6cd7a7aedc", + "sha256:96095bfc0c02072fc89afa67626013a253596ea5118b8a7f4daaae049dafa096", + "sha256:980ba47c8db4b9d870014c7040edb230825b79017a6a27aa54cdb6fcc02d8cc0", + "sha256:992029258ed719f130d5a9c443d142c32843046f1263f2c492862b2a853be570", + "sha256:99cad5c912f359e59e921689c04e54662cdd80835d80eeaa931e22612f515df7", + "sha256:9b59c429e1a2246da86ae237ffc3565efcdc71c281cd38ca8b44d5fb6a3b993a", + "sha256:9ca498f8554a09fbc3a2f8fc4b23261e07bc27bef99b3df98e2570688033f6fc", + "sha256:9cd3d6c2c67d4fdcd795e4945e2ba5434909c96640b4cc09453bd0dc7e8e1bac", + "sha256:a85136d0ee18a41c91cc3e2844c683be0e72e6dda4cb58da9e15fcaef3726af7", + "sha256:ac21aace6712472e77ea9dfc38329f53830c4259ece54c786107105ebb069053", + "sha256:aebd8fd378e074b22e79cad329dcccd243c40ff1cafaa512d19276c5bb9554e1", + "sha256:affdd833f82334fdb10fc9a1c7b35cdb5a86d0b672b4e14dd542e1fe7bcea894", + "sha256:b6d4e148edee59c2ad38af15810dbcb8b5d7b13e5de3509d8cf3edfe74c0adca", + "sha256:bb58e8f4b2cfe012cd312239b8d5139995fe8f5945c7c26d5fbbbb1ddb9acd47", + "sha256:bfdc4668ac56687a89ca3eca44231144a2e9d02ba3b877558db74ba20e2bd9fa", + "sha256:c1249aa4eaced30b59ecf8b8cae0b1ccede04583c74ca7d10b6f8bbead908b2c", + "sha256:c7cfb6af73602c8d288581df8a225989d7e9d5aab0a174be0e19fcfa800b6797", + "sha256:c7fe19abb3d3c55a9e65d289b12ad73b3a31a3f0bda3c539a890329ae9973bd6", + "sha256:c8954da15403db1acfc0544b3c3f963a6ef4e428283ab6555e3e298bbbff1cf6", + "sha256:c90c593aa8dd57d5dab0ef6d7d64af894008971d98e6a41b320fdd75258fbc6e", + "sha256:cb564bbe55ff0897d9cf1225041a44576d7ae87f06fd60163544c91de2623d3f", + "sha256:cfa8a4cdc3765574b7fd0c7cfa5fbd1e2108014c9dfd299c679e5152bea9a55e", + "sha256:d1bb64646480c36a4aa1b6a44a5b6e33d0fcbeab9f53f1b39072cd3bb2c6243a", + "sha256:dac2733fe4e159b0aae0439db6813b7b1d23ff96d0b34c0107b87faf79208c4e", + "sha256:db40e85cffd22f7d65dcce30e85af565a66401a6ed22fc0c56ed342cfa4ffc43", + "sha256:dd39ef87fd1f7bb5c4aa53454936e6135cbfe03fe3744e8218be193f9e4fef16", + "sha256:de1a8b54170024cf1c0c2718c82412bca42cd82e390556e3d8031af9541b416f", + "sha256:e675a4b95208e74c34ac0751cc4bab9170e7728b61601fb0f4746892c2bb7e0b", + "sha256:e6bb39d91bf932e7520cb5718ae3c2f498052aca53294d5d59fdd9068fe1a7f2", + "sha256:e8c63f5c7d87e7044880b01851ac4e863c3349e6f6b6ab456fe218d9346e816d", + "sha256:ea56825c1e23c9c8ea385a191dac75f9160477057285b88c88736d9305e6118f", + "sha256:ee60f33456ff34b2dd1d048a740a2572798356208e4c494301c931de3a0ab3a2", + "sha256:f15844a1b93dcaa09c2b22e22a73384f3ae4502347c3881cfdd674e14ac04e21", + "sha256:f298ac9149037d6a3d5c74991bded39ac46292520b9c7c182cb102486cc87677", + "sha256:f30e697b6215e759d0824768b2c5b0618d2dc19abe6c67eeed2b0460f52470d1", + "sha256:f92d73faa0b1a76d1932429d684b7ce95829e93c3eef3715ec9b98ab192c9d31", + "sha256:fef10f27d6318d2d7c88680e113511ddecf09ee4f9559b3623b73ee89fa8f6cc" ], "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'", - "version": "==4.9.3" + "version": "==5.0.0" }, "makefun": { "hashes": [ @@ -968,122 +964,122 @@ "email" ], "hashes": [ - "sha256:80c50fb8e3dcecfddae1adbcc00ec5822918490c99ab31f6cf6140ca1c1429f0", - "sha256:ff177ba64c6faf73d7afa2e8cad38fd456c0dbe01c9954e71038001cd15a6edd" + "sha256:b3ef57c62535b0941697cce638c08900d87fcb67e29cfa99e8a68f747f393f7a", + "sha256:d0caf5954bee831b6bfe7e338c32b9e30c85dfe080c843680783ac2b631673b4" ], "markers": "python_version >= '3.7'", - "version": "==2.5.2" + "version": "==2.5.3" }, "pydantic-core": { "hashes": [ - "sha256:038c9f763e650712b899f983076ce783175397c848da04985658e7628cbe873b", - "sha256:074f3d86f081ce61414d2dc44901f4f83617329c6f3ab49d2bc6c96948b2c26b", - "sha256:079206491c435b60778cf2b0ee5fd645e61ffd6e70c47806c9ed51fc75af078d", - "sha256:09b0e985fbaf13e6b06a56d21694d12ebca6ce5414b9211edf6f17738d82b0f8", - "sha256:0f6116a558fd06d1b7c2902d1c4cf64a5bd49d67c3540e61eccca93f41418124", - "sha256:103ef8d5b58596a731b690112819501ba1db7a36f4ee99f7892c40da02c3e189", - "sha256:16e29bad40bcf97aac682a58861249ca9dcc57c3f6be22f506501833ddb8939c", - "sha256:206ed23aecd67c71daf5c02c3cd19c0501b01ef3cbf7782db9e4e051426b3d0d", - "sha256:2248485b0322c75aee7565d95ad0e16f1c67403a470d02f94da7344184be770f", - "sha256:27548e16c79702f1e03f5628589c6057c9ae17c95b4c449de3c66b589ead0520", - "sha256:2d0ae0d8670164e10accbeb31d5ad45adb71292032d0fdb9079912907f0085f4", - "sha256:3128e0bbc8c091ec4375a1828d6118bc20404883169ac95ffa8d983b293611e6", - "sha256:3387277f1bf659caf1724e1afe8ee7dbc9952a82d90f858ebb931880216ea955", - "sha256:34708cc82c330e303f4ce87758828ef6e457681b58ce0e921b6e97937dd1e2a3", - "sha256:35613015f0ba7e14c29ac6c2483a657ec740e5ac5758d993fdd5870b07a61d8b", - "sha256:3ad873900297bb36e4b6b3f7029d88ff9829ecdc15d5cf20161775ce12306f8a", - "sha256:40180930807ce806aa71eda5a5a5447abb6b6a3c0b4b3b1b1962651906484d68", - "sha256:439c9afe34638ace43a49bf72d201e0ffc1a800295bed8420c2a9ca8d5e3dbb3", - "sha256:45e95333b8418ded64745f14574aa9bfc212cb4fbeed7a687b0c6e53b5e188cd", - "sha256:4641e8ad4efb697f38a9b64ca0523b557c7931c5f84e0fd377a9a3b05121f0de", - "sha256:49b08aae5013640a3bfa25a8eebbd95638ec3f4b2eaf6ed82cf0c7047133f03b", - "sha256:4bc536201426451f06f044dfbf341c09f540b4ebdb9fd8d2c6164d733de5e634", - "sha256:4ce601907e99ea5b4adb807ded3570ea62186b17f88e271569144e8cca4409c7", - "sha256:4e40f2bd0d57dac3feb3a3aed50f17d83436c9e6b09b16af271b6230a2915459", - "sha256:4e47a76848f92529879ecfc417ff88a2806438f57be4a6a8bf2961e8f9ca9ec7", - "sha256:513b07e99c0a267b1d954243845d8a833758a6726a3b5d8948306e3fe14675e3", - "sha256:531f4b4252fac6ca476fbe0e6f60f16f5b65d3e6b583bc4d87645e4e5ddde331", - "sha256:57d52fa717ff445cb0a5ab5237db502e6be50809b43a596fb569630c665abddf", - "sha256:59986de5710ad9613ff61dd9b02bdd2f615f1a7052304b79cc8fa2eb4e336d2d", - "sha256:5baab5455c7a538ac7e8bf1feec4278a66436197592a9bed538160a2e7d11e36", - "sha256:5c7d5b5005f177764e96bd584d7bf28d6e26e96f2a541fdddb934c486e36fd59", - "sha256:60b7607753ba62cf0739177913b858140f11b8af72f22860c28eabb2f0a61937", - "sha256:615a0a4bff11c45eb3c1996ceed5bdaa2f7b432425253a7c2eed33bb86d80abc", - "sha256:61ea96a78378e3bd5a0be99b0e5ed00057b71f66115f5404d0dae4819f495093", - "sha256:652c1988019752138b974c28f43751528116bcceadad85f33a258869e641d753", - "sha256:6637560562134b0e17de333d18e69e312e0458ee4455bdad12c37100b7cad706", - "sha256:678265f7b14e138d9a541ddabbe033012a2953315739f8cfa6d754cc8063e8ca", - "sha256:699156034181e2ce106c89ddb4b6504c30db8caa86e0c30de47b3e0654543260", - "sha256:6b9ff467ffbab9110e80e8c8de3bcfce8e8b0fd5661ac44a09ae5901668ba997", - "sha256:6c327e9cd849b564b234da821236e6bcbe4f359a42ee05050dc79d8ed2a91588", - "sha256:6d30226dfc816dd0fdf120cae611dd2215117e4f9b124af8c60ab9093b6e8e71", - "sha256:6e227c40c02fd873c2a73a98c1280c10315cbebe26734c196ef4514776120aeb", - "sha256:6e4d090e73e0725b2904fdbdd8d73b8802ddd691ef9254577b708d413bf3006e", - "sha256:70f4b4851dbb500129681d04cc955be2a90b2248d69273a787dda120d5cf1f69", - "sha256:70f947628e074bb2526ba1b151cee10e4c3b9670af4dbb4d73bc8a89445916b5", - "sha256:774de879d212db5ce02dfbf5b0da9a0ea386aeba12b0b95674a4ce0593df3d07", - "sha256:77fa384d8e118b3077cccfcaf91bf83c31fe4dc850b5e6ee3dc14dc3d61bdba1", - "sha256:79e0a2cdbdc7af3f4aee3210b1172ab53d7ddb6a2d8c24119b5706e622b346d0", - "sha256:7e88f5696153dc516ba6e79f82cc4747e87027205f0e02390c21f7cb3bd8abfd", - "sha256:7f8210297b04e53bc3da35db08b7302a6a1f4889c79173af69b72ec9754796b8", - "sha256:81982d78a45d1e5396819bbb4ece1fadfe5f079335dd28c4ab3427cd95389944", - "sha256:823fcc638f67035137a5cd3f1584a4542d35a951c3cc68c6ead1df7dac825c26", - "sha256:853a2295c00f1d4429db4c0fb9475958543ee80cfd310814b5c0ef502de24dda", - "sha256:88e74ab0cdd84ad0614e2750f903bb0d610cc8af2cc17f72c28163acfcf372a4", - "sha256:8aa1768c151cf562a9992462239dfc356b3d1037cc5a3ac829bb7f3bda7cc1f9", - "sha256:8c8a8812fe6f43a3a5b054af6ac2d7b8605c7bcab2804a8a7d68b53f3cd86e00", - "sha256:95b15e855ae44f0c6341ceb74df61b606e11f1087e87dcb7482377374aac6abe", - "sha256:96581cfefa9123accc465a5fd0cc833ac4d75d55cc30b633b402e00e7ced00a6", - "sha256:9bd18fee0923ca10f9a3ff67d4851c9d3e22b7bc63d1eddc12f439f436f2aada", - "sha256:a33324437018bf6ba1bb0f921788788641439e0ed654b233285b9c69704c27b4", - "sha256:a6a16f4a527aae4f49c875da3cdc9508ac7eef26e7977952608610104244e1b7", - "sha256:a717aef6971208f0851a2420b075338e33083111d92041157bbe0e2713b37325", - "sha256:a71891847f0a73b1b9eb86d089baee301477abef45f7eaf303495cd1473613e4", - "sha256:aae7ea3a1c5bb40c93cad361b3e869b180ac174656120c42b9fadebf685d121b", - "sha256:ab1cdb0f14dc161ebc268c09db04d2c9e6f70027f3b42446fa11c153521c0e88", - "sha256:ab4ea451082e684198636565224bbb179575efc1658c48281b2c866bfd4ddf04", - "sha256:abf058be9517dc877227ec3223f0300034bd0e9f53aebd63cf4456c8cb1e0863", - "sha256:af36f36538418f3806048f3b242a1777e2540ff9efaa667c27da63d2749dbce0", - "sha256:b53e9ad053cd064f7e473a5f29b37fc4cc9dc6d35f341e6afc0155ea257fc911", - "sha256:b7851992faf25eac90bfcb7bfd19e1f5ffa00afd57daec8a0042e63c74a4551b", - "sha256:b9b759b77f5337b4ea024f03abc6464c9f35d9718de01cfe6bae9f2e139c397e", - "sha256:ba39688799094c75ea8a16a6b544eb57b5b0f3328697084f3f2790892510d144", - "sha256:ba6b6b3846cfc10fdb4c971980a954e49d447cd215ed5a77ec8190bc93dd7bc5", - "sha256:bb4c2eda937a5e74c38a41b33d8c77220380a388d689bcdb9b187cf6224c9720", - "sha256:c0b97ec434041827935044bbbe52b03d6018c2897349670ff8fe11ed24d1d4ab", - "sha256:c1452a1acdf914d194159439eb21e56b89aa903f2e1c65c60b9d874f9b950e5d", - "sha256:c2027d05c8aebe61d898d4cffd774840a9cb82ed356ba47a90d99ad768f39789", - "sha256:c2adbe22ab4babbca99c75c5d07aaf74f43c3195384ec07ccbd2f9e3bddaecec", - "sha256:c2d97e906b4ff36eb464d52a3bc7d720bd6261f64bc4bcdbcd2c557c02081ed2", - "sha256:c339dabd8ee15f8259ee0f202679b6324926e5bc9e9a40bf981ce77c038553db", - "sha256:c6eae413494a1c3f89055da7a5515f32e05ebc1a234c27674a6956755fb2236f", - "sha256:c949f04ecad823f81b1ba94e7d189d9dfb81edbb94ed3f8acfce41e682e48cef", - "sha256:c97bee68898f3f4344eb02fec316db93d9700fb1e6a5b760ffa20d71d9a46ce3", - "sha256:ca61d858e4107ce5e1330a74724fe757fc7135190eb5ce5c9d0191729f033209", - "sha256:cb4679d4c2b089e5ef89756bc73e1926745e995d76e11925e3e96a76d5fa51fc", - "sha256:cb774298da62aea5c80a89bd58c40205ab4c2abf4834453b5de207d59d2e1651", - "sha256:ccd4d5702bb90b84df13bd491be8d900b92016c5a455b7e14630ad7449eb03f8", - "sha256:cf9d3fe53b1ee360e2421be95e62ca9b3296bf3f2fb2d3b83ca49ad3f925835e", - "sha256:d2ae91f50ccc5810b2f1b6b858257c9ad2e08da70bf890dee02de1775a387c66", - "sha256:d37f8ec982ead9ba0a22a996129594938138a1503237b87318392a48882d50b7", - "sha256:d81e6987b27bc7d101c8597e1cd2bcaa2fee5e8e0f356735c7ed34368c471550", - "sha256:dcf4e6d85614f7a4956c2de5a56531f44efb973d2fe4a444d7251df5d5c4dcfd", - "sha256:de790a3b5aa2124b8b78ae5faa033937a72da8efe74b9231698b5a1dd9be3405", - "sha256:e47e9a08bcc04d20975b6434cc50bf82665fbc751bcce739d04a3120428f3e27", - "sha256:e60f112ac88db9261ad3a52032ea46388378034f3279c643499edb982536a093", - "sha256:e87fc540c6cac7f29ede02e0f989d4233f88ad439c5cdee56f693cc9c1c78077", - "sha256:eac5c82fc632c599f4639a5886f96867ffced74458c7db61bc9a66ccb8ee3113", - "sha256:ebb4e035e28f49b6f1a7032920bb9a0c064aedbbabe52c543343d39341a5b2a3", - "sha256:ec1e72d6412f7126eb7b2e3bfca42b15e6e389e1bc88ea0069d0cc1742f477c6", - "sha256:ef98ca7d5995a82f43ec0ab39c4caf6a9b994cb0b53648ff61716370eadc43cf", - "sha256:f0cbc7fff06a90bbd875cc201f94ef0ee3929dfbd5c55a06674b60857b8b85ed", - "sha256:f4791cf0f8c3104ac668797d8c514afb3431bc3305f5638add0ba1a5a37e0d88", - "sha256:f5e412d717366e0677ef767eac93566582518fe8be923361a5c204c1a62eaafe", - "sha256:fb2ed8b3fe4bf4506d6dab3b93b83bbc22237e230cba03866d561c3577517d18", - "sha256:fe0a5a1025eb797752136ac8b4fa21aa891e3d74fd340f864ff982d649691867" + "sha256:00646784f6cd993b1e1c0e7b0fdcbccc375d539db95555477771c27555e3c556", + "sha256:00b1087dabcee0b0ffd104f9f53d7d3eaddfaa314cdd6726143af6bc713aa27e", + "sha256:0348b1dc6b76041516e8a854ff95b21c55f5a411c3297d2ca52f5528e49d8411", + "sha256:036137b5ad0cb0004c75b579445a1efccd072387a36c7f217bb8efd1afbe5245", + "sha256:095b707bb287bfd534044166ab767bec70a9bba3175dcdc3371782175c14e43c", + "sha256:0c08de15d50fa190d577e8591f0329a643eeaed696d7771760295998aca6bc66", + "sha256:1302a54f87b5cd8528e4d6d1bf2133b6aa7c6122ff8e9dc5220fbc1e07bffebd", + "sha256:172de779e2a153d36ee690dbc49c6db568d7b33b18dc56b69a7514aecbcf380d", + "sha256:1b027c86c66b8627eb90e57aee1f526df77dc6d8b354ec498be9a757d513b92b", + "sha256:1ce830e480f6774608dedfd4a90c42aac4a7af0a711f1b52f807130c2e434c06", + "sha256:1fd0c1d395372843fba13a51c28e3bb9d59bd7aebfeb17358ffaaa1e4dbbe948", + "sha256:23598acb8ccaa3d1d875ef3b35cb6376535095e9405d91a3d57a8c7db5d29341", + "sha256:24368e31be2c88bd69340fbfe741b405302993242ccb476c5c3ff48aeee1afe0", + "sha256:26a92ae76f75d1915806b77cf459811e772d8f71fd1e4339c99750f0e7f6324f", + "sha256:27e524624eace5c59af499cd97dc18bb201dc6a7a2da24bfc66ef151c69a5f2a", + "sha256:2b8719037e570639e6b665a4050add43134d80b687288ba3ade18b22bbb29dd2", + "sha256:2c5bcf3414367e29f83fd66f7de64509a8fd2368b1edf4351e862910727d3e51", + "sha256:2dbe357bc4ddda078f79d2a36fc1dd0494a7f2fad83a0a684465b6f24b46fe80", + "sha256:2f5fa187bde8524b1e37ba894db13aadd64faa884657473b03a019f625cee9a8", + "sha256:2f6ffc6701a0eb28648c845f4945a194dc7ab3c651f535b81793251e1185ac3d", + "sha256:314ccc4264ce7d854941231cf71b592e30d8d368a71e50197c905874feacc8a8", + "sha256:36026d8f99c58d7044413e1b819a67ca0e0b8ebe0f25e775e6c3d1fabb3c38fb", + "sha256:36099c69f6b14fc2c49d7996cbf4f87ec4f0e66d1c74aa05228583225a07b590", + "sha256:36fa402dcdc8ea7f1b0ddcf0df4254cc6b2e08f8cd80e7010d4c4ae6e86b2a87", + "sha256:370ffecb5316ed23b667d99ce4debe53ea664b99cc37bfa2af47bc769056d534", + "sha256:3860c62057acd95cc84044e758e47b18dcd8871a328ebc8ccdefd18b0d26a21b", + "sha256:399ac0891c284fa8eb998bcfa323f2234858f5d2efca3950ae58c8f88830f145", + "sha256:3a0b5db001b98e1c649dd55afa928e75aa4087e587b9524a4992316fa23c9fba", + "sha256:3dcf1978be02153c6a31692d4fbcc2a3f1db9da36039ead23173bc256ee3b91b", + "sha256:4241204e4b36ab5ae466ecec5c4c16527a054c69f99bba20f6f75232a6a534e2", + "sha256:438027a975cc213a47c5d70672e0d29776082155cfae540c4e225716586be75e", + "sha256:43e166ad47ba900f2542a80d83f9fc65fe99eb63ceec4debec160ae729824052", + "sha256:478e9e7b360dfec451daafe286998d4a1eeaecf6d69c427b834ae771cad4b622", + "sha256:4ce8299b481bcb68e5c82002b96e411796b844d72b3e92a3fbedfe8e19813eab", + "sha256:4f86f1f318e56f5cbb282fe61eb84767aee743ebe32c7c0834690ebea50c0a6b", + "sha256:55a23dcd98c858c0db44fc5c04fc7ed81c4b4d33c653a7c45ddaebf6563a2f66", + "sha256:599c87d79cab2a6a2a9df4aefe0455e61e7d2aeede2f8577c1b7c0aec643ee8e", + "sha256:5aa90562bc079c6c290f0512b21768967f9968e4cfea84ea4ff5af5d917016e4", + "sha256:64634ccf9d671c6be242a664a33c4acf12882670b09b3f163cd00a24cffbd74e", + "sha256:667aa2eac9cd0700af1ddb38b7b1ef246d8cf94c85637cbb03d7757ca4c3fdec", + "sha256:6a31d98c0d69776c2576dda4b77b8e0c69ad08e8b539c25c7d0ca0dc19a50d6c", + "sha256:6af4b3f52cc65f8a0bc8b1cd9676f8c21ef3e9132f21fed250f6958bd7223bed", + "sha256:6c8edaea3089bf908dd27da8f5d9e395c5b4dc092dbcce9b65e7156099b4b937", + "sha256:71d72ca5eaaa8d38c8df16b7deb1a2da4f650c41b58bb142f3fb75d5ad4a611f", + "sha256:72f9a942d739f09cd42fffe5dc759928217649f070056f03c70df14f5770acf9", + "sha256:747265448cb57a9f37572a488a57d873fd96bf51e5bb7edb52cfb37124516da4", + "sha256:75ec284328b60a4e91010c1acade0c30584f28a1f345bc8f72fe8b9e46ec6a96", + "sha256:78d0768ee59baa3de0f4adac9e3748b4b1fffc52143caebddfd5ea2961595277", + "sha256:78ee52ecc088c61cce32b2d30a826f929e1708f7b9247dc3b921aec367dc1b23", + "sha256:7be719e4d2ae6c314f72844ba9d69e38dff342bc360379f7c8537c48e23034b7", + "sha256:7e1f4744eea1501404b20b0ac059ff7e3f96a97d3e3f48ce27a139e053bb370b", + "sha256:7e90d6cc4aad2cc1f5e16ed56e46cebf4877c62403a311af20459c15da76fd91", + "sha256:7ebe3416785f65c28f4f9441e916bfc8a54179c8dea73c23023f7086fa601c5d", + "sha256:7f41533d7e3cf9520065f610b41ac1c76bc2161415955fbcead4981b22c7611e", + "sha256:7f5025db12fc6de7bc1104d826d5aee1d172f9ba6ca936bf6474c2148ac336c1", + "sha256:86c963186ca5e50d5c8287b1d1c9d3f8f024cbe343d048c5bd282aec2d8641f2", + "sha256:86ce5fcfc3accf3a07a729779d0b86c5d0309a4764c897d86c11089be61da160", + "sha256:8a14c192c1d724c3acbfb3f10a958c55a2638391319ce8078cb36c02283959b9", + "sha256:8b93785eadaef932e4fe9c6e12ba67beb1b3f1e5495631419c784ab87e975670", + "sha256:8ed1af8692bd8d2a29d702f1a2e6065416d76897d726e45a1775b1444f5928a7", + "sha256:92879bce89f91f4b2416eba4429c7b5ca22c45ef4a499c39f0c5c69257522c7c", + "sha256:94fc0e6621e07d1e91c44e016cc0b189b48db053061cc22d6298a611de8071bb", + "sha256:982487f8931067a32e72d40ab6b47b1628a9c5d344be7f1a4e668fb462d2da42", + "sha256:9862bf828112e19685b76ca499b379338fd4c5c269d897e218b2ae8fcb80139d", + "sha256:99b14dbea2fdb563d8b5a57c9badfcd72083f6006caf8e126b491519c7d64ca8", + "sha256:9c6a5c79b28003543db3ba67d1df336f253a87d3112dac3a51b94f7d48e4c0e1", + "sha256:a19b794f8fe6569472ff77602437ec4430f9b2b9ec7a1105cfd2232f9ba355e6", + "sha256:a306cdd2ad3a7d795d8e617a58c3a2ed0f76c8496fb7621b6cd514eb1532cae8", + "sha256:a3dde6cac75e0b0902778978d3b1646ca9f438654395a362cb21d9ad34b24acf", + "sha256:a874f21f87c485310944b2b2734cd6d318765bcbb7515eead33af9641816506e", + "sha256:a983cca5ed1dd9a35e9e42ebf9f278d344603bfcb174ff99a5815f953925140a", + "sha256:aca48506a9c20f68ee61c87f2008f81f8ee99f8d7f0104bff3c47e2d148f89d9", + "sha256:b2602177668f89b38b9f84b7b3435d0a72511ddef45dc14446811759b82235a1", + "sha256:b3e5fe4538001bb82e2295b8d2a39356a84694c97cb73a566dc36328b9f83b40", + "sha256:b6ca36c12a5120bad343eef193cc0122928c5c7466121da7c20f41160ba00ba2", + "sha256:b89f4477d915ea43b4ceea6756f63f0288941b6443a2b28c69004fe07fde0d0d", + "sha256:b9a9d92f10772d2a181b5ca339dee066ab7d1c9a34ae2421b2a52556e719756f", + "sha256:c99462ffc538717b3e60151dfaf91125f637e801f5ab008f81c402f1dff0cd0f", + "sha256:cb92f9061657287eded380d7dc455bbf115430b3aa4741bdc662d02977e7d0af", + "sha256:cdee837710ef6b56ebd20245b83799fce40b265b3b406e51e8ccc5b85b9099b7", + "sha256:cf10b7d58ae4a1f07fccbf4a0a956d705356fea05fb4c70608bb6fa81d103cda", + "sha256:d15687d7d7f40333bd8266f3814c591c2e2cd263fa2116e314f60d82086e353a", + "sha256:d5c28525c19f5bb1e09511669bb57353d22b94cf8b65f3a8d141c389a55dec95", + "sha256:d5f916acf8afbcab6bacbb376ba7dc61f845367901ecd5e328fc4d4aef2fcab0", + "sha256:dab03ed811ed1c71d700ed08bde8431cf429bbe59e423394f0f4055f1ca0ea60", + "sha256:db453f2da3f59a348f514cfbfeb042393b68720787bbef2b4c6068ea362c8149", + "sha256:de2a0645a923ba57c5527497daf8ec5df69c6eadf869e9cd46e86349146e5975", + "sha256:dea7fcd62915fb150cdc373212141a30037e11b761fbced340e9db3379b892d4", + "sha256:dfcbebdb3c4b6f739a91769aea5ed615023f3c88cb70df812849aef634c25fbe", + "sha256:dfcebb950aa7e667ec226a442722134539e77c575f6cfaa423f24371bb8d2e94", + "sha256:e0641b506486f0b4cd1500a2a65740243e8670a2549bb02bc4556a83af84ae03", + "sha256:e33b0834f1cf779aa839975f9d8755a7c2420510c0fa1e9fa0497de77cd35d2c", + "sha256:e4ace1e220b078c8e48e82c081e35002038657e4b37d403ce940fa679e57113b", + "sha256:e4cf2d5829f6963a5483ec01578ee76d329eb5caf330ecd05b3edd697e7d768a", + "sha256:e574de99d735b3fc8364cba9912c2bec2da78775eba95cbb225ef7dda6acea24", + "sha256:e646c0e282e960345314f42f2cea5e0b5f56938c093541ea6dbf11aec2862391", + "sha256:e8a5ac97ea521d7bde7621d86c30e86b798cdecd985723c4ed737a2aa9e77d0c", + "sha256:eedf97be7bc3dbc8addcef4142f4b4164066df0c6f36397ae4aaed3eb187d8ab", + "sha256:ef633add81832f4b56d3b4c9408b43d530dfca29e68fb1b797dcb861a2c734cd", + "sha256:f27207e8ca3e5e021e2402ba942e5b4c629718e665c81b8b306f3c8b1ddbb786", + "sha256:f85f3843bdb1fe80e8c206fe6eed7a1caeae897e496542cee499c374a85c6e08", + "sha256:f8e81e4b55930e5ffab4a68db1af431629cf2e4066dbdbfef65348b8ab804ea8", + "sha256:f96ae96a060a8072ceff4cfde89d261837b4294a4f28b84a28765470d502ccc6", + "sha256:fd9e98b408384989ea4ab60206b8e100d8687da18b5c813c11e92fd8212a98e0", + "sha256:ffff855100bc066ff2cd3aa4a60bc9534661816b110f0243e59503ec2df38421" ], "markers": "python_version >= '3.7'", - "version": "==2.14.5" + "version": "==2.14.6" }, "pydantic-settings": { "hashes": [ @@ -1151,122 +1147,113 @@ "sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f", "sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1" ], - "index": "pypi", "markers": "python_version >= '3.7'", "version": "==2.31.0" }, - "requests-mock": { - "hashes": [ - "sha256:ef10b572b489a5f28e09b708697208c4a3b2b89ef80a9f01584340ea357ec3c4", - "sha256:f7fae383f228633f6bececebdab236c478ace2284d6292c6e7e2867b9ab74d15" - ], - "index": "pypi", - "version": "==1.11.0" - }, "rpds-py": { "hashes": [ - "sha256:02744236ac1895d7be837878e707a5c35fb8edc5137602f253b63623d7ad5c8c", - "sha256:03f9c5875515820633bd7709a25c3e60c1ea9ad1c5d4030ce8a8c203309c36fd", - "sha256:044f6f46d62444800402851afa3c3ae50141f12013060c1a3a0677e013310d6d", - "sha256:07a2e1d78d382f7181789713cdf0c16edbad4fe14fe1d115526cb6f0eef0daa3", - "sha256:082e0e55d73690ffb4da4352d1b5bbe1b5c6034eb9dc8c91aa2a3ee15f70d3e2", - "sha256:13152dfe7d7c27c40df8b99ac6aab12b978b546716e99f67e8a67a1d441acbc3", - "sha256:13716e53627ad97babf72ac9e01cf9a7d4af2f75dd5ed7b323a7a9520e948282", - "sha256:13ff62d3561a23c17341b4afc78e8fcfd799ab67c0b1ca32091d71383a98ba4b", - "sha256:1607cda6129f815493a3c184492acb5ae4aa6ed61d3a1b3663aa9824ed26f7ac", - "sha256:164fcee32f15d04d61568c9cb0d919e37ff3195919cd604039ff3053ada0461b", - "sha256:1c24e30d720c0009b6fb2e1905b025da56103c70a8b31b99138e4ed1c2a6c5b0", - "sha256:1e6fcd0a0f62f2997107f758bb372397b8d5fd5f39cc6dcb86f7cb98a2172d6c", - "sha256:1fd0f0b1ccd7d537b858a56355a250108df692102e08aa2036e1a094fd78b2dc", - "sha256:2181e86d4e1cdf49a7320cb72a36c45efcb7670d0a88f09fd2d3a7967c0540fd", - "sha256:2974e6dff38afafd5ccf8f41cb8fc94600b3f4fd9b0a98f6ece6e2219e3158d5", - "sha256:2dccc623725d0b298f557d869a68496a2fd2a9e9c41107f234fa5f7a37d278ac", - "sha256:2df3d07a16a3bef0917b28cd564778fbb31f3ffa5b5e33584470e2d1b0f248f0", - "sha256:2e7e5633577b3bd56bf3af2ef6ae3778bbafb83743989d57f0e7edbf6c0980e4", - "sha256:2ee066a64f0d2ba45391cac15b3a70dcb549e968a117bd0500634754cfe0e5fc", - "sha256:2f1f295a5c28cfa74a7d48c95acc1c8a7acd49d7d9072040d4b694fe11cd7166", - "sha256:2faa97212b0dc465afeedf49045cdd077f97be1188285e646a9f689cb5dfff9e", - "sha256:30479a9f1fce47df56b07460b520f49fa2115ec2926d3b1303c85c81f8401ed1", - "sha256:337a8653fb11d2fbe7157c961cc78cb3c161d98cf44410ace9a3dc2db4fad882", - "sha256:3423007fc0661827e06f8a185a3792c73dda41f30f3421562f210cf0c9e49569", - "sha256:373b76eeb79e8c14f6d82cb1d4d5293f9e4059baec6c1b16dca7ad13b6131b39", - "sha256:3b79c63d29101cbaa53a517683557bb550462394fb91044cc5998dd2acff7340", - "sha256:3bbc89ce2a219662ea142f0abcf8d43f04a41d5b1880be17a794c39f0d609cb0", - "sha256:3c11bc5814554b018f6c5d6ae0969e43766f81e995000b53a5d8c8057055e886", - "sha256:3cd61e759c4075510052d1eca5cddbd297fe1164efec14ef1fce3f09b974dfe4", - "sha256:3d40fb3ca22e3d40f494d577441b263026a3bd8c97ae6ce89b2d3c4b39ac9581", - "sha256:3db0c998c92b909d7c90b66c965590d4f3cd86157176a6cf14aa1f867b77b889", - "sha256:422b0901878a31ef167435c5ad46560362891816a76cc0d150683f3868a6f0d1", - "sha256:46b4f3d47d1033db569173be62365fbf7808c2bd3fb742314d251f130d90d44c", - "sha256:485fbdd23becb822804ed05622907ee5c8e8a5f43f6f43894a45f463b2217045", - "sha256:53304cc14b1d94487d70086e1cb0cb4c29ec6da994d58ae84a4d7e78c6a6d04d", - "sha256:5595c80dd03d7e6c6afb73f3594bf3379a7d79fa57164b591d012d4b71d6ac4c", - "sha256:56b51ba29a18e5f5810224bcf00747ad931c0716e3c09a76b4a1edd3d4aba71f", - "sha256:580182fa5b269c2981e9ce9764367cb4edc81982ce289208d4607c203f44ffde", - "sha256:5e99d6510c8557510c220b865d966b105464740dcbebf9b79ecd4fbab30a13d9", - "sha256:5eb05b654a41e0f81ab27a7c3e88b6590425eb3e934e1d533ecec5dc88a6ffff", - "sha256:62b292fff4739c6be89e6a0240c02bda5a9066a339d90ab191cf66e9fdbdc193", - "sha256:6a5122b17a4faf5d7a6d91fa67b479736c0cacc7afe791ddebb7163a8550b799", - "sha256:6a8ff8e809da81363bffca2b965cb6e4bf6056b495fc3f078467d1f8266fe27f", - "sha256:6c43e1b89099279cc03eb1c725c5de12af6edcd2f78e2f8a022569efa639ada3", - "sha256:709dc11af2f74ba89c68b1592368c6edcbccdb0a06ba77eb28c8fe08bb6997da", - "sha256:7e072f5da38d6428ba1fc1115d3cc0dae895df671cb04c70c019985e8c7606be", - "sha256:813a65f95bfcb7c8f2a70dd6add9b51e9accc3bdb3e03d0ff7a9e6a2d3e174bf", - "sha256:86c01299942b0f4b5b5f28c8701689181ad2eab852e65417172dbdd6c5b3ccc8", - "sha256:893e38d0f4319dfa70c0f36381a37cc418985c87b11d9784365b1fff4fa6973b", - "sha256:8a5f574b92b3ee7d254e56d56e37ec0e1416acb1ae357c4956d76a1788dc58fb", - "sha256:8b9650f92251fdef843e74fc252cdfd6e3c700157ad686eeb0c6d7fdb2d11652", - "sha256:8ec464f20fe803ae00419bd1610934e3bda963aeba1e6181dfc9033dc7e8940c", - "sha256:8f333bfe782a2d05a67cfaa0cc9cd68b36b39ee6acfe099f980541ed973a7093", - "sha256:8ffdeb7dbd0160d4e391e1f857477e4762d00aa2199c294eb95dfb9451aa1d9f", - "sha256:911e600e798374c0d86235e7ef19109cf865d1336942d398ff313375a25a93ba", - "sha256:9235be95662559141934fced8197de6fee8c58870f36756b0584424b6d708393", - "sha256:938518a11780b39998179d07f31a4a468888123f9b00463842cd40f98191f4d3", - "sha256:93c18a1696a8e0388ed84b024fe1a188a26ba999b61d1d9a371318cb89885a8c", - "sha256:97532802f14d383f37d603a56e226909f825a83ff298dc1b6697de00d2243999", - "sha256:98ee201a52a7f65608e5494518932e1473fd43535f12cade0a1b4ab32737fe28", - "sha256:9d2ae79f31da5143e020a8d4fc74e1f0cbcb8011bdf97453c140aa616db51406", - "sha256:9d38494a8d21c246c535b41ecdb2d562c4b933cf3d68de03e8bc43a0d41be652", - "sha256:9d41ebb471a6f064c0d1c873c4f7dded733d16ca5db7d551fb04ff3805d87802", - "sha256:9e09d017e3f4d9bd7d17a30d3f59e4d6d9ba2d2ced280eec2425e84112cf623f", - "sha256:a6945c2d61c42bb7e818677f43638675b8c1c43e858b67a96df3eb2426a86c9d", - "sha256:a72e00826a2b032dda3eb25aa3e3579c6d6773d22d8446089a57a123481cc46c", - "sha256:aa1e626c524d2c7972c0f3a8a575d654a3a9c008370dc2a97e46abd0eaa749b9", - "sha256:ab095edf1d840a6a6a4307e1a5b907a299a94e7b90e75436ee770b8c35d22a25", - "sha256:ac2ac84a4950d627d84b61f082eba61314373cfab4b3c264b62efab02ababe83", - "sha256:ac7187bee72384b9cfedf09a29a3b2b6e8815cc64c095cdc8b5e6aec81e9fd5f", - "sha256:ae9d83a81b09ce3a817e2cbb23aabc07f86a3abc664c613cd283ce7a03541e95", - "sha256:afeabb382c1256a7477b739820bce7fe782bb807d82927102cee73e79b41b38b", - "sha256:b2a4cd924d0e2f4b1a68034abe4cadc73d69ad5f4cf02db6481c0d4d749f548f", - "sha256:b414ef79f1f06fb90b5165db8aef77512c1a5e3ed1b4807da8476b7e2c853283", - "sha256:b4ecbba7efd82bd2a4bb88aab7f984eb5470991c1347bdd1f35fb34ea28dba6e", - "sha256:b61d5096e75fd71018b25da50b82dd70ec39b5e15bb2134daf7eb7bbbc103644", - "sha256:b629db53fe17e6ce478a969d30bd1d0e8b53238c46e3a9c9db39e8b65a9ef973", - "sha256:b70b45a40ad0798b69748b34d508259ef2bdc84fb2aad4048bc7c9cafb68ddb3", - "sha256:b88c3ab98556bc351b36d6208a6089de8c8db14a7f6e1f57f82a334bd2c18f0b", - "sha256:baf744e5f9d5ee6531deea443be78b36ed1cd36c65a0b95ea4e8d69fa0102268", - "sha256:bbc7421cbd28b4316d1d017db338039a7943f945c6f2bb15e1439b14b5682d28", - "sha256:c31272c674f725dfe0f343d73b0abe8c878c646967ec1c6106122faae1efc15b", - "sha256:c51a899792ee2c696072791e56b2020caff58b275abecbc9ae0cb71af0645c95", - "sha256:c61e42b4ceb9759727045765e87d51c1bb9f89987aca1fcc8a040232138cad1c", - "sha256:c7cd0841a586b7105513a7c8c3d5c276f3adc762a072d81ef7fae80632afad1e", - "sha256:c827a931c6b57f50f1bb5de400dcfb00bad8117e3753e80b96adb72d9d811514", - "sha256:d2aa3ca9552f83b0b4fa6ca8c6ce08da6580f37e3e0ab7afac73a1cfdc230c0e", - "sha256:d46ee458452727a147d7897bb33886981ae1235775e05decae5d5d07f537695a", - "sha256:d64a657de7aae8db2da60dc0c9e4638a0c3893b4d60101fd564a3362b2bfeb34", - "sha256:d800a8e2ac62db1b9ea5d6d1724f1a93c53907ca061de4d05ed94e8dfa79050c", - "sha256:d9d7ebcd11ea76ba0feaae98485cd8e31467c3d7985210fab46983278214736b", - "sha256:dd7d3608589072f63078b4063a6c536af832e76b0b3885f1bfe9e892abe6c207", - "sha256:ec19e823b4ccd87bd69e990879acbce9e961fc7aebe150156b8f4418d4b27b7f", - "sha256:ee40206d1d6e95eaa2b7b919195e3689a5cf6ded730632de7f187f35a1b6052c", - "sha256:f138f550b83554f5b344d6be35d3ed59348510edc3cb96f75309db6e9bfe8210", - "sha256:f3e6e2e502c4043c52a99316d89dc49f416acda5b0c6886e0dd8ea7bb35859e8", - "sha256:fb10bb720348fe1647a94eb605accb9ef6a9b1875d8845f9e763d9d71a706387", - "sha256:fc066395e6332da1e7525d605b4c96055669f8336600bef8ac569d5226a7c76f", - "sha256:fc33267d58dfbb2361baed52668c5d8c15d24bc0372cecbb79fed77339b55e0d" - ], - "markers": "python_version >= '3.8'", - "version": "==0.15.2" + "sha256:0474df4ade9a3b4af96c3d36eb81856cb9462e4c6657d4caecfd840d2a13f3c9", + "sha256:071980663c273bf3d388fe5c794c547e6f35ba3335477072c713a3176bf14a60", + "sha256:07aab64e2808c3ebac2a44f67e9dc0543812b715126dfd6fe4264df527556cb6", + "sha256:088396c7c70e59872f67462fcac3ecbded5233385797021976a09ebd55961dfe", + "sha256:162d7cd9cd311c1b0ff1c55a024b8f38bd8aad1876b648821da08adc40e95734", + "sha256:19f00f57fdd38db4bb5ad09f9ead1b535332dbf624200e9029a45f1f35527ebb", + "sha256:1bdbc5fcb04a7309074de6b67fa9bc4b418ab3fc435fec1f2779a0eced688d04", + "sha256:1be2f033df1b8be8c3167ba3c29d5dca425592ee31e35eac52050623afba5772", + "sha256:24f7a2eb3866a9e91f4599851e0c8d39878a470044875c49bd528d2b9b88361c", + "sha256:290a81cfbe4673285cdf140ec5cd1658ffbf63ab359f2b352ebe172e7cfa5bf0", + "sha256:2946b120718eba9af2b4dd103affc1164a87b9e9ebff8c3e4c05d7b7a7e274e2", + "sha256:2bd82db36cd70b3628c0c57d81d2438e8dd4b7b32a6a9f25f24ab0e657cb6c4e", + "sha256:2ddef620e70eaffebed5932ce754d539c0930f676aae6212f8e16cd9743dd365", + "sha256:2e53b9b25cac9065328901713a7e9e3b12e4f57ef4280b370fbbf6fef2052eef", + "sha256:302bd4983bbd47063e452c38be66153760112f6d3635c7eeefc094299fa400a9", + "sha256:349cb40897fd529ca15317c22c0eab67f5ac5178b5bd2c6adc86172045210acc", + "sha256:358dafc89ce3894c7f486c615ba914609f38277ef67f566abc4c854d23b997fa", + "sha256:35953f4f2b3216421af86fd236b7c0c65935936a94ea83ddbd4904ba60757773", + "sha256:35ae5ece284cf36464eb160880018cf6088a9ac5ddc72292a6092b6ef3f4da53", + "sha256:3b811d182ad17ea294f2ec63c0621e7be92a1141e1012383461872cead87468f", + "sha256:3da5a4c56953bdbf6d04447c3410309616c54433146ccdb4a277b9cb499bc10e", + "sha256:3dc6a7620ba7639a3db6213da61312cb4aa9ac0ca6e00dc1cbbdc21c2aa6eb57", + "sha256:3f91df8e6dbb7360e176d1affd5fb0246d2b88d16aa5ebc7db94fd66b68b61da", + "sha256:4022b9dc620e14f30201a8a73898a873c8e910cb642bcd2f3411123bc527f6ac", + "sha256:413b9c17388bbd0d87a329d8e30c1a4c6e44e2bb25457f43725a8e6fe4161e9e", + "sha256:43d4dd5fb16eb3825742bad8339d454054261ab59fed2fbac84e1d84d5aae7ba", + "sha256:44627b6ca7308680a70766454db5249105fa6344853af6762eaad4158a2feebe", + "sha256:44a54e99a2b9693a37ebf245937fd6e9228b4cbd64b9cc961e1f3391ec6c7391", + "sha256:47713dc4fce213f5c74ca8a1f6a59b622fc1b90868deb8e8e4d993e421b4b39d", + "sha256:495a14b72bbe217f2695dcd9b5ab14d4f8066a00f5d209ed94f0aca307f85f6e", + "sha256:4c46ad6356e1561f2a54f08367d1d2e70a0a1bb2db2282d2c1972c1d38eafc3b", + "sha256:4d6a9f052e72d493efd92a77f861e45bab2f6be63e37fa8ecf0c6fd1a58fedb0", + "sha256:509b617ac787cd1149600e731db9274ebbef094503ca25158e6f23edaba1ca8f", + "sha256:5552f328eaef1a75ff129d4d0c437bf44e43f9436d3996e8eab623ea0f5fcf73", + "sha256:5a80e2f83391ad0808b4646732af2a7b67550b98f0cae056cb3b40622a83dbb3", + "sha256:5cf6af100ffb5c195beec11ffaa8cf8523057f123afa2944e6571d54da84cdc9", + "sha256:5e6caa3809e50690bd92fa490f5c38caa86082c8c3315aa438bce43786d5e90d", + "sha256:5ef00873303d678aaf8b0627e111fd434925ca01c657dbb2641410f1cdaef261", + "sha256:69ac7ea9897ec201ce68b48582f3eb34a3f9924488a5432a93f177bf76a82a7e", + "sha256:6a61226465bda9283686db8f17d02569a98e4b13c637be5a26d44aa1f1e361c2", + "sha256:6d904c5693e08bad240f16d79305edba78276be87061c872a4a15e2c301fa2c0", + "sha256:6dace7b26a13353e24613417ce2239491b40a6ad44e5776a18eaff7733488b44", + "sha256:6df15846ee3fb2e6397fe25d7ca6624af9f89587f3f259d177b556fed6bebe2c", + "sha256:703d95c75a72e902544fda08e965885525e297578317989fd15a6ce58414b41d", + "sha256:726ac36e8a3bb8daef2fd482534cabc5e17334052447008405daca7ca04a3108", + "sha256:781ef8bfc091b19960fc0142a23aedadafa826bc32b433fdfe6fd7f964d7ef44", + "sha256:80443fe2f7b3ea3934c5d75fb0e04a5dbb4a8e943e5ff2de0dec059202b70a8b", + "sha256:83640a5d7cd3bff694747d50436b8b541b5b9b9782b0c8c1688931d6ee1a1f2d", + "sha256:84c5a4d1f9dd7e2d2c44097fb09fffe728629bad31eb56caf97719e55575aa82", + "sha256:882ce6e25e585949c3d9f9abd29202367175e0aab3aba0c58c9abbb37d4982ff", + "sha256:888a97002e986eca10d8546e3c8b97da1d47ad8b69726dcfeb3e56348ebb28a3", + "sha256:8aad80645a011abae487d356e0ceb359f4938dfb6f7bcc410027ed7ae4f7bb8b", + "sha256:8cb6fe8ecdfffa0e711a75c931fb39f4ba382b4b3ccedeca43f18693864fe850", + "sha256:8d6b6937ae9eac6d6c0ca3c42774d89fa311f55adff3970fb364b34abde6ed3d", + "sha256:90123853fc8b1747f80b0d354be3d122b4365a93e50fc3aacc9fb4c2488845d6", + "sha256:96f957d6ab25a78b9e7fc9749d754b98eac825a112b4e666525ce89afcbd9ed5", + "sha256:981d135c7cdaf6cd8eadae1c950de43b976de8f09d8e800feed307140d3d6d00", + "sha256:9b32f742ce5b57201305f19c2ef7a184b52f6f9ba6871cc042c2a61f0d6b49b8", + "sha256:9f0350ef2fba5f34eb0c9000ea328e51b9572b403d2f7f3b19f24085f6f598e8", + "sha256:a297a4d08cc67c7466c873c78039d87840fb50d05473db0ec1b7b03d179bf322", + "sha256:a3d7e2ea25d3517c6d7e5a1cc3702cffa6bd18d9ef8d08d9af6717fc1c700eed", + "sha256:a4b682c5775d6a3d21e314c10124599976809455ee67020e8e72df1769b87bc3", + "sha256:a4ebb8b20bd09c5ce7884c8f0388801100f5e75e7f733b1b6613c713371feefc", + "sha256:a61f659665a39a4d17d699ab3593d7116d66e1e2e3f03ef3fb8f484e91908808", + "sha256:a9880b4656efe36ccad41edc66789e191e5ee19a1ea8811e0aed6f69851a82f4", + "sha256:ac08472f41ea77cd6a5dae36ae7d4ed3951d6602833af87532b556c1b4601d63", + "sha256:adc0c3d6fc6ae35fee3e4917628983f6ce630d513cbaad575b4517d47e81b4bb", + "sha256:af27423662f32d7501a00c5e7342f7dbd1e4a718aea7a239781357d15d437133", + "sha256:b2e75e17bd0bb66ee34a707da677e47c14ee51ccef78ed6a263a4cc965a072a1", + "sha256:b634c5ec0103c5cbebc24ebac4872b045cccb9456fc59efdcf6fe39775365bd2", + "sha256:b6f5549d6ed1da9bfe3631ca9483ae906f21410be2445b73443fa9f017601c6f", + "sha256:bd4b677d929cf1f6bac07ad76e0f2d5de367e6373351c01a9c0a39f6b21b4a8b", + "sha256:bf721ede3eb7b829e4a9b8142bd55db0bdc82902720548a703f7e601ee13bdc3", + "sha256:c647ca87fc0ebe808a41de912e9a1bfef9acb85257e5d63691364ac16b81c1f0", + "sha256:ca57468da2d9a660bcf8961637c85f2fbb2aa64d9bc3f9484e30c3f9f67b1dd7", + "sha256:cad0f59ee3dc35526039f4bc23642d52d5f6616b5f687d846bfc6d0d6d486db0", + "sha256:cc97f0640e91d7776530f06e6836c546c1c752a52de158720c4224c9e8053cad", + "sha256:ccd4e400309e1f34a5095bf9249d371f0fd60f8a3a5c4a791cad7b99ce1fd38d", + "sha256:cffa76b385dfe1e38527662a302b19ffb0e7f5cf7dd5e89186d2c94a22dd9d0c", + "sha256:d0dd7ed2f16df2e129496e7fbe59a34bc2d7fc8db443a606644d069eb69cbd45", + "sha256:d452817e0d9c749c431a1121d56a777bd7099b720b3d1c820f1725cb40928f58", + "sha256:d8dda2a806dfa4a9b795950c4f5cc56d6d6159f7d68080aedaff3bdc9b5032f5", + "sha256:dcbe1f8dd179e4d69b70b1f1d9bb6fd1e7e1bdc9c9aad345cdeb332e29d40748", + "sha256:e0441fb4fdd39a230477b2ca9be90868af64425bfe7b122b57e61e45737a653b", + "sha256:e04e56b4ca7a770593633556e8e9e46579d66ec2ada846b401252a2bdcf70a6d", + "sha256:e061de3b745fe611e23cd7318aec2c8b0e4153939c25c9202a5811ca911fd733", + "sha256:e93ec1b300acf89730cf27975ef574396bc04edecc358e9bd116fb387a123239", + "sha256:e9e557db6a177470316c82f023e5d571811c9a4422b5ea084c85da9aa3c035fc", + "sha256:eab36eae3f3e8e24b05748ec9acc66286662f5d25c52ad70cadab544e034536b", + "sha256:ec23fcad480e77ede06cf4127a25fc440f7489922e17fc058f426b5256ee0edb", + "sha256:ec2e1cf025b2c0f48ec17ff3e642661da7ee332d326f2e6619366ce8e221f018", + "sha256:ed99b4f7179d2111702020fd7d156e88acd533f5a7d3971353e568b6051d5c97", + "sha256:ee94cb58c0ba2c62ee108c2b7c9131b2c66a29e82746e8fa3aa1a1effbd3dcf1", + "sha256:f19afcfc0dd0dca35694df441e9b0f95bc231b512f51bded3c3d8ca32153ec19", + "sha256:f1b9d9260e06ea017feb7172976ab261e011c1dc2f8883c7c274f6b2aabfe01a", + "sha256:f28ac0e8e7242d140f99402a903a2c596ab71550272ae9247ad78f9a932b5698", + "sha256:f42e25c016927e2a6b1ce748112c3ab134261fc2ddc867e92d02006103e1b1b7", + "sha256:f4bd4578e44f26997e9e56c96dedc5f1af43cc9d16c4daa29c771a00b2a26851", + "sha256:f811771019f063bbd0aa7bb72c8a934bc13ebacb4672d712fc1639cfd314cccc" + ], + "markers": "python_version >= '3.8'", + "version": "==0.16.2" }, "sentry-sdk": { "extras": [ @@ -1308,67 +1295,67 @@ "asyncio" ], "hashes": [ - "sha256:0666031df46b9badba9bed00092a1ffa3aa063a5e68fa244acd9f08070e936d3", - "sha256:0a8c6aa506893e25a04233bc721c6b6cf844bafd7250535abb56cb6cc1368884", - "sha256:0e680527245895aba86afbd5bef6c316831c02aa988d1aad83c47ffe92655e74", - "sha256:14aebfe28b99f24f8a4c1346c48bc3d63705b1f919a24c27471136d2f219f02d", - "sha256:1e018aba8363adb0599e745af245306cb8c46b9ad0a6fc0a86745b6ff7d940fc", - "sha256:227135ef1e48165f37590b8bfc44ed7ff4c074bf04dc8d6f8e7f1c14a94aa6ca", - "sha256:31952bbc527d633b9479f5f81e8b9dfada00b91d6baba021a869095f1a97006d", - "sha256:3e983fa42164577d073778d06d2cc5d020322425a509a08119bdcee70ad856bf", - "sha256:42d0b0290a8fb0165ea2c2781ae66e95cca6e27a2fbe1016ff8db3112ac1e846", - "sha256:42ede90148b73fe4ab4a089f3126b2cfae8cfefc955c8174d697bb46210c8306", - "sha256:4895a63e2c271ffc7a81ea424b94060f7b3b03b4ea0cd58ab5bb676ed02f4221", - "sha256:4af79c06825e2836de21439cb2a6ce22b2ca129bad74f359bddd173f39582bf5", - "sha256:5f94aeb99f43729960638e7468d4688f6efccb837a858b34574e01143cf11f89", - "sha256:616fe7bcff0a05098f64b4478b78ec2dfa03225c23734d83d6c169eb41a93e55", - "sha256:62d9e964870ea5ade4bc870ac4004c456efe75fb50404c03c5fd61f8bc669a72", - "sha256:638c2c0b6b4661a4fd264f6fb804eccd392745c5887f9317feb64bb7cb03b3ea", - "sha256:63bfc3acc970776036f6d1d0e65faa7473be9f3135d37a463c5eba5efcdb24c8", - "sha256:6463aa765cf02b9247e38b35853923edbf2f6fd1963df88706bc1d02410a5577", - "sha256:64ac935a90bc479fee77f9463f298943b0e60005fe5de2aa654d9cdef46c54df", - "sha256:683ef58ca8eea4747737a1c35c11372ffeb84578d3aab8f3e10b1d13d66f2bc4", - "sha256:75eefe09e98043cff2fb8af9796e20747ae870c903dc61d41b0c2e55128f958d", - "sha256:787af80107fb691934a01889ca8f82a44adedbf5ef3d6ad7d0f0b9ac557e0c34", - "sha256:7c424983ab447dab126c39d3ce3be5bee95700783204a72549c3dceffe0fc8f4", - "sha256:7e0dc9031baa46ad0dd5a269cb7a92a73284d1309228be1d5935dac8fb3cae24", - "sha256:87a3d6b53c39cd173990de2f5f4b83431d534a74f0e2f88bd16eabb5667e65c6", - "sha256:89a01238fcb9a8af118eaad3ffcc5dedaacbd429dc6fdc43fe430d3a941ff965", - "sha256:9585b646ffb048c0250acc7dad92536591ffe35dba624bb8fd9b471e25212a35", - "sha256:964971b52daab357d2c0875825e36584d58f536e920f2968df8d581054eada4b", - "sha256:967c0b71156f793e6662dd839da54f884631755275ed71f1539c95bbada9aaab", - "sha256:9ca922f305d67605668e93991aaf2c12239c78207bca3b891cd51a4515c72e22", - "sha256:a86cb7063e2c9fb8e774f77fbf8475516d270a3e989da55fa05d08089d77f8c4", - "sha256:aeb397de65a0a62f14c257f36a726945a7f7bb60253462e8602d9b97b5cbe204", - "sha256:b41f5d65b54cdf4934ecede2f41b9c60c9f785620416e8e6c48349ab18643855", - "sha256:bd45a5b6c68357578263d74daab6ff9439517f87da63442d244f9f23df56138d", - "sha256:c14eba45983d2f48f7546bb32b47937ee2cafae353646295f0e99f35b14286ab", - "sha256:c1bda93cbbe4aa2aa0aa8655c5aeda505cd219ff3e8da91d1d329e143e4aff69", - "sha256:c4722f3bc3c1c2fcc3702dbe0016ba31148dd6efcd2a2fd33c1b4897c6a19693", - "sha256:c80c38bd2ea35b97cbf7c21aeb129dcbebbf344ee01a7141016ab7b851464f8e", - "sha256:cabafc7837b6cec61c0e1e5c6d14ef250b675fa9c3060ed8a7e38653bd732ff8", - "sha256:cc1d21576f958c42d9aec68eba5c1a7d715e5fc07825a629015fe8e3b0657fb0", - "sha256:d0f7fb0c7527c41fa6fcae2be537ac137f636a41b4c5a4c58914541e2f436b45", - "sha256:d4041ad05b35f1f4da481f6b811b4af2f29e83af253bf37c3c4582b2c68934ab", - "sha256:d5578e6863eeb998980c212a39106ea139bdc0b3f73291b96e27c929c90cd8e1", - "sha256:e3b5036aa326dc2df50cba3c958e29b291a80f604b1afa4c8ce73e78e1c9f01d", - "sha256:e599a51acf3cc4d31d1a0cf248d8f8d863b6386d2b6782c5074427ebb7803bda", - "sha256:f3420d00d2cb42432c1d0e44540ae83185ccbbc67a6054dcc8ab5387add6620b", - "sha256:f48ed89dd11c3c586f45e9eec1e437b355b3b6f6884ea4a4c3111a3358fd0c18", - "sha256:f508ba8f89e0a5ecdfd3761f82dda2a3d7b678a626967608f4273e0dba8f07ac", - "sha256:fd54601ef9cc455a0c61e5245f690c8a3ad67ddb03d3b91c361d076def0b4c60" + "sha256:00d76fe5d7cdb5d84d625ce002ce29fefba0bfd98e212ae66793fed30af73931", + "sha256:07cc423892f2ceda9ae1daa28c0355757f362ecc7505b1ab1a3d5d8dc1c44ac6", + "sha256:0bb7cedcddffca98c40bb0becd3423e293d1fef442b869da40843d751785beb3", + "sha256:1ca7903d5e7db791a355b579c690684fac6304478b68efdc7f2ebdcfe770d8d7", + "sha256:1d9b3fd5eca3c0b137a5e0e468e24ca544ed8ca4783e0e55341b7ed2807518ee", + "sha256:2587e108463cc2e5b45a896b2e7cc8659a517038026922a758bde009271aed11", + "sha256:29e51f848f843bbd75d74ae64ab1ab06302cb1dccd4549d1f5afe6b4a946edb2", + "sha256:2a479aa1ab199178ff1956b09ca8a0693e70f9c762875d69292d37049ffd0d8f", + "sha256:37e89d965b52e8b20571b5d44f26e2124b26ab63758bf1b7598a0e38fb2c4005", + "sha256:38732884eabc64982a09a846bacf085596ff2371e4e41d20c0734f7e50525d01", + "sha256:396f05c552f7fa30a129497c41bef5b4d1423f9af8fe4df0c3dcd38f3e3b9a14", + "sha256:4a1d4856861ba9e73bac05030cec5852eabfa9ef4af8e56c19d92de80d46fc34", + "sha256:56a0e90a959e18ac5f18c80d0cad9e90cb09322764f536e8a637426afb1cae2f", + "sha256:57ef6f2cb8b09a042d0dbeaa46a30f2df5dd1e1eb889ba258b0d5d7d6011b81c", + "sha256:5f801d85ba4753d4ed97181d003e5d3fa330ac7c4587d131f61d7f968f416862", + "sha256:6db686a1d9f183c639f7e06a2656af25d4ed438eda581de135d15569f16ace33", + "sha256:6db97656fd3fe3f7e5b077f12fa6adb5feb6e0b567a3e99f47ecf5f7ea0a09e3", + "sha256:6f5e75de91c754365c098ac08c13fdb267577ce954fa239dd49228b573ca88d7", + "sha256:7a6209e689d0ff206c40032b6418e3cfcfc5af044b3f66e381d7f1ae301544b4", + "sha256:7ae5d44517fe81079ce75cf10f96978284a6db2642c5932a69c82dbae09f009a", + "sha256:83fa6df0e035689df89ff77a46bf8738696785d3156c2c61494acdcddc75c69d", + "sha256:8f358f5cfce04417b6ff738748ca4806fe3d3ae8040fb4e6a0c9a6973ccf9b6e", + "sha256:9036ebfd934813990c5b9f71f297e77ed4963720db7d7ceec5a3fdb7cd2ef6ce", + "sha256:95bae3d38f8808d79072da25d5e5a6095f36fe1f9d6c614dd72c59ca8397c7c0", + "sha256:9aaaaa846b10dfbe1bda71079d0e31a7e2cebedda9409fa7dba3dfed1ae803e8", + "sha256:9b8d0e8578e7f853f45f4512b5c920f6a546cd4bed44137460b2a56534644205", + "sha256:9bafaa05b19dc07fa191c1966c5e852af516840b0d7b46b7c3303faf1a349bc9", + "sha256:9f29c7f0f4b42337ec5a779e166946a9f86d7d56d827e771b69ecbdf426124ac", + "sha256:9f992e0f916201731993eab8502912878f02287d9f765ef843677ff118d0e0b1", + "sha256:a04191a7c8d77e63f6fc1e8336d6c6e93176c0c010833e74410e647f0284f5a1", + "sha256:a0f611b431b84f55779cbb7157257d87b4a2876b067c77c4f36b15e44ced65e2", + "sha256:a3c2753bf4f48b7a6024e5e8a394af49b1b12c817d75d06942cae03d14ff87b3", + "sha256:a5cd7d30e47f87b21362beeb3e86f1b5886e7d9b0294b230dde3d3f4a1591375", + "sha256:acc58b7c2e40235712d857fdfc8f2bda9608f4a850d8d9ac0dd1fc80939ca6ac", + "sha256:adbd67dac4ebf54587198b63cd30c29fd7eafa8c0cab58893d9419414f8efe4b", + "sha256:b35c35e3923ade1e7ac44e150dec29f5863513246c8bf85e2d7d313e3832bcfb", + "sha256:c6910eb4ea90c0889f363965cd3c8c45a620ad27b526a7899f0054f6c1b9219e", + "sha256:cc889fda484d54d0b31feec409406267616536d048a450fc46943e152700bb79", + "sha256:ccfd336f96d4c9bbab0309f2a565bf15c468c2d8b2d277a32f89c5940f71fcf9", + "sha256:d8e7e8a150e7b548e7ecd6ebb9211c37265991bf2504297d9454e01b58530fc6", + "sha256:db09e424d7bb89b6215a184ca93b4f29d7f00ea261b787918a1af74143b98c06", + "sha256:e17e7e27af178d31b436dda6a596703b02a89ba74a15e2980c35ecd9909eea3a", + "sha256:e69290b921b7833c04206f233d6814c60bee1d135b09f5ae5d39229de9b46cd4", + "sha256:e8398593ccc4440ce6dffcc4f47d9b2d72b9fe7112ac12ea4a44e7d4de364db1", + "sha256:e9d036e343a604db3f5a6c33354018a84a1d3f6dcae3673358b404286204798c", + "sha256:ea490564435b5b204d8154f0e18387b499ea3cedc1e6af3b3a2ab18291d85aa7", + "sha256:f073321a79c81e1a009218a21089f61d87ee5fa3c9563f6be94f8b41ff181812", + "sha256:f0cc0b486a56dff72dddae6b6bfa7ff201b0eeac29d4bc6f0e9725dc3c360d71", + "sha256:fcf84fe93397a0f67733aa2a38ed4eab9fc6348189fc950e656e1ea198f45668" ], "index": "pypi", "markers": "python_version >= '3.7'", - "version": "==2.0.23" + "version": "==2.0.24" }, "starlette": { "hashes": [ - "sha256:6a6b0d042acb8d469a01eba54e9cda6cbd24ac602c4cd016723117d6a7e73b75", - "sha256:918416370e846586541235ccd38a474c08b80443ed31c578a418e2209b3eef91" + "sha256:cd0cb10ddb49313f609cedfac62c8c12e56c7314b66d89bb077ba228bada1b09", + "sha256:e54e2b7e2fb06dff9eac40133583f10dfa05913f5a85bf26f427c7a40a9a3d02" ], - "markers": "python_version >= '3.7'", - "version": "==0.27.0" + "markers": "python_version >= '3.8'", + "version": "==0.32.0.post1" }, "tabulate": { "hashes": [ @@ -1403,15 +1390,6 @@ "markers": "python_version >= '3.7'", "version": "==4.66.1" }, - "types-requests": { - "hashes": [ - "sha256:b32b9a86beffa876c0c3ac99a4cd3b8b51e973fb8e3bd4e0a6bb32c7efad80fc", - "sha256:dc5852a76f1eaf60eafa81a2e50aefa3d1f015c34cf0cba130930866b1b22a92" - ], - "index": "pypi", - "markers": "python_version >= '3.7'", - "version": "==2.31.0.10" - }, "typing-extensions": { "hashes": [ "sha256:23478f88c37f27d76ac8aee6c905017a143b0b1b886c3c9f66bc2fd94f9f5783", @@ -1422,11 +1400,11 @@ }, "tzdata": { "hashes": [ - "sha256:11ef1e08e54acb0d4f95bdb1be05da659673de4acbd21bf9c69e94cc5e907a3a", - "sha256:7e65763eef3120314099b6939b5546db7adce1e7d6f2e179e3df563c70511eda" + "sha256:aa3ace4329eeacda5b7beb7ea08ece826c28d761cda36e747cfbf97996d39bf3", + "sha256:dd54c94f294765522c77399649b4fefd95522479a664a0cec87f41bebc6148c9" ], "markers": "python_version >= '2'", - "version": "==2023.3" + "version": "==2023.4" }, "urllib3": { "hashes": [ @@ -1438,12 +1416,12 @@ }, "uvicorn": { "hashes": [ - "sha256:09c8e5a79dc466bdf28dead50093957db184de356fcdc48697bad3bde4c2588e", - "sha256:7c84fea70c619d4a710153482c0d230929af7bcf76c7bfa6de151f0a3a80121e" + "sha256:6dddbad1d7ee0f5140aba5ec138ddc9612c5109399903828b4874c9937f009c2", + "sha256:ce107f5d9bd02b4636001a77a4e74aab5e1e2b146868ebbad565237145af444c" ], "index": "pypi", "markers": "python_version >= '3.8'", - "version": "==0.24.0.post1" + "version": "==0.25.0" }, "vine": { "hashes": [ @@ -1470,95 +1448,6 @@ } }, "develop": { - "aiohttp": { - "hashes": [ - "sha256:02ab6006ec3c3463b528374c4cdce86434e7b89ad355e7bf29e2f16b46c7dd6f", - "sha256:04fa38875e53eb7e354ece1607b1d2fdee2d175ea4e4d745f6ec9f751fe20c7c", - "sha256:0b0a6a36ed7e164c6df1e18ee47afbd1990ce47cb428739d6c99aaabfaf1b3af", - "sha256:0d406b01a9f5a7e232d1b0d161b40c05275ffbcbd772dc18c1d5a570961a1ca4", - "sha256:0e49b08eafa4f5707ecfb321ab9592717a319e37938e301d462f79b4e860c32a", - "sha256:0e7ba7ff228c0d9a2cd66194e90f2bca6e0abca810b786901a569c0de082f489", - "sha256:11cb254e397a82efb1805d12561e80124928e04e9c4483587ce7390b3866d213", - "sha256:11ff168d752cb41e8492817e10fb4f85828f6a0142b9726a30c27c35a1835f01", - "sha256:176df045597e674fa950bf5ae536be85699e04cea68fa3a616cf75e413737eb5", - "sha256:219a16763dc0294842188ac8a12262b5671817042b35d45e44fd0a697d8c8361", - "sha256:22698f01ff5653fe66d16ffb7658f582a0ac084d7da1323e39fd9eab326a1f26", - "sha256:237533179d9747080bcaad4d02083ce295c0d2eab3e9e8ce103411a4312991a0", - "sha256:289ba9ae8e88d0ba16062ecf02dd730b34186ea3b1e7489046fc338bdc3361c4", - "sha256:2c59e0076ea31c08553e868cec02d22191c086f00b44610f8ab7363a11a5d9d8", - "sha256:2c9376e2b09895c8ca8b95362283365eb5c03bdc8428ade80a864160605715f1", - "sha256:3135713c5562731ee18f58d3ad1bf41e1d8883eb68b363f2ffde5b2ea4b84cc7", - "sha256:3b9c7426923bb7bd66d409da46c41e3fb40f5caf679da624439b9eba92043fa6", - "sha256:3c0266cd6f005e99f3f51e583012de2778e65af6b73860038b968a0a8888487a", - "sha256:41473de252e1797c2d2293804e389a6d6986ef37cbb4a25208de537ae32141dd", - "sha256:4831df72b053b1eed31eb00a2e1aff6896fb4485301d4ccb208cac264b648db4", - "sha256:49f0c1b3c2842556e5de35f122fc0f0b721334ceb6e78c3719693364d4af8499", - "sha256:4b4c452d0190c5a820d3f5c0f3cd8a28ace48c54053e24da9d6041bf81113183", - "sha256:4ee8caa925aebc1e64e98432d78ea8de67b2272252b0a931d2ac3bd876ad5544", - "sha256:500f1c59906cd142d452074f3811614be04819a38ae2b3239a48b82649c08821", - "sha256:5216b6082c624b55cfe79af5d538e499cd5f5b976820eac31951fb4325974501", - "sha256:54311eb54f3a0c45efb9ed0d0a8f43d1bc6060d773f6973efd90037a51cd0a3f", - "sha256:54631fb69a6e44b2ba522f7c22a6fb2667a02fd97d636048478db2fd8c4e98fe", - "sha256:565760d6812b8d78d416c3c7cfdf5362fbe0d0d25b82fed75d0d29e18d7fc30f", - "sha256:598db66eaf2e04aa0c8900a63b0101fdc5e6b8a7ddd805c56d86efb54eb66672", - "sha256:5c4fa235d534b3547184831c624c0b7c1e262cd1de847d95085ec94c16fddcd5", - "sha256:69985d50a2b6f709412d944ffb2e97d0be154ea90600b7a921f95a87d6f108a2", - "sha256:69da0f3ed3496808e8cbc5123a866c41c12c15baaaead96d256477edf168eb57", - "sha256:6c93b7c2e52061f0925c3382d5cb8980e40f91c989563d3d32ca280069fd6a87", - "sha256:70907533db712f7aa791effb38efa96f044ce3d4e850e2d7691abd759f4f0ae0", - "sha256:81b77f868814346662c96ab36b875d7814ebf82340d3284a31681085c051320f", - "sha256:82eefaf1a996060602f3cc1112d93ba8b201dbf5d8fd9611227de2003dddb3b7", - "sha256:85c3e3c9cb1d480e0b9a64c658cd66b3cfb8e721636ab8b0e746e2d79a7a9eed", - "sha256:8a22a34bc594d9d24621091d1b91511001a7eea91d6652ea495ce06e27381f70", - "sha256:8cef8710fb849d97c533f259103f09bac167a008d7131d7b2b0e3a33269185c0", - "sha256:8d44e7bf06b0c0a70a20f9100af9fcfd7f6d9d3913e37754c12d424179b4e48f", - "sha256:8d7f98fde213f74561be1d6d3fa353656197f75d4edfbb3d94c9eb9b0fc47f5d", - "sha256:8d8e4450e7fe24d86e86b23cc209e0023177b6d59502e33807b732d2deb6975f", - "sha256:8fc49a87ac269d4529da45871e2ffb6874e87779c3d0e2ccd813c0899221239d", - "sha256:90ec72d231169b4b8d6085be13023ece8fa9b1bb495e4398d847e25218e0f431", - "sha256:91c742ca59045dce7ba76cab6e223e41d2c70d79e82c284a96411f8645e2afff", - "sha256:9b05d33ff8e6b269e30a7957bd3244ffbce2a7a35a81b81c382629b80af1a8bf", - "sha256:9b05d5cbe9dafcdc733262c3a99ccf63d2f7ce02543620d2bd8db4d4f7a22f83", - "sha256:9c5857612c9813796960c00767645cb5da815af16dafb32d70c72a8390bbf690", - "sha256:a34086c5cc285be878622e0a6ab897a986a6e8bf5b67ecb377015f06ed316587", - "sha256:ab221850108a4a063c5b8a70f00dd7a1975e5a1713f87f4ab26a46e5feac5a0e", - "sha256:b796b44111f0cab6bbf66214186e44734b5baab949cb5fb56154142a92989aeb", - "sha256:b8c3a67eb87394386847d188996920f33b01b32155f0a94f36ca0e0c635bf3e3", - "sha256:bcb6532b9814ea7c5a6a3299747c49de30e84472fa72821b07f5a9818bce0f66", - "sha256:bcc0ea8d5b74a41b621ad4a13d96c36079c81628ccc0b30cfb1603e3dfa3a014", - "sha256:bea94403a21eb94c93386d559bce297381609153e418a3ffc7d6bf772f59cc35", - "sha256:bff7e2811814fa2271be95ab6e84c9436d027a0e59665de60edf44e529a42c1f", - "sha256:c72444d17777865734aa1a4d167794c34b63e5883abb90356a0364a28904e6c0", - "sha256:c7b5d5d64e2a14e35a9240b33b89389e0035e6de8dbb7ffa50d10d8b65c57449", - "sha256:c7e939f1ae428a86e4abbb9a7c4732bf4706048818dfd979e5e2839ce0159f23", - "sha256:c88a15f272a0ad3d7773cf3a37cc7b7d077cbfc8e331675cf1346e849d97a4e5", - "sha256:c9110c06eaaac7e1f5562caf481f18ccf8f6fdf4c3323feab28a93d34cc646bd", - "sha256:ca7ca5abfbfe8d39e653870fbe8d7710be7a857f8a8386fc9de1aae2e02ce7e4", - "sha256:cae4c0c2ca800c793cae07ef3d40794625471040a87e1ba392039639ad61ab5b", - "sha256:cdefe289681507187e375a5064c7599f52c40343a8701761c802c1853a504558", - "sha256:cf2a0ac0615842b849f40c4d7f304986a242f1e68286dbf3bd7a835e4f83acfd", - "sha256:cfeadf42840c1e870dc2042a232a8748e75a36b52d78968cda6736de55582766", - "sha256:d737e69d193dac7296365a6dcb73bbbf53bb760ab25a3727716bbd42022e8d7a", - "sha256:d7481f581251bb5558ba9f635db70908819caa221fc79ee52a7f58392778c636", - "sha256:df9cf74b9bc03d586fc53ba470828d7b77ce51b0582d1d0b5b2fb673c0baa32d", - "sha256:e1f80197f8b0b846a8d5cf7b7ec6084493950d0882cc5537fb7b96a69e3c8590", - "sha256:ecca113f19d5e74048c001934045a2b9368d77b0b17691d905af18bd1c21275e", - "sha256:ee2527134f95e106cc1653e9ac78846f3a2ec1004cf20ef4e02038035a74544d", - "sha256:f27fdaadce22f2ef950fc10dcdf8048407c3b42b73779e48a4e76b3c35bca26c", - "sha256:f694dc8a6a3112059258a725a4ebe9acac5fe62f11c77ac4dcf896edfa78ca28", - "sha256:f800164276eec54e0af5c99feb9494c295118fc10a11b997bbb1348ba1a52065", - "sha256:ffcd828e37dc219a72c9012ec44ad2e7e3066bec6ff3aaa19e7d435dbf4032ca" - ], - "version": "==3.9.1" - }, - "aiosignal": { - "hashes": [ - "sha256:54cd96e15e1649b75d6c87526a6ff0b6c1b0dd3459f43d9ca11d48c339b68cfc", - "sha256:f8376fb07dd1e86a584e4fcdec80b36b7f81aac666ebc724e2c090300dd83b17" - ], - "markers": "python_version >= '3.7'", - "version": "==1.3.1" - }, "aiosqlite": { "hashes": [ "sha256:95ee77b91c8d2808bd08a59fbebf66270e9090c3d92ffbf260dc0db0b979577d", @@ -1578,11 +1467,11 @@ }, "anyio": { "hashes": [ - "sha256:44a3c9aba0f5defa43261a8b3efb97891f2bd7d804e0e1f56419befa1adfc780", - "sha256:91dee416e570e92c64041bd18b900d1d6fa78dff7048769ce5ac5ddad004fbb5" + "sha256:745843b39e829e108e518c489b31dc757de7d2131d53fac32bd8df268227bfee", + "sha256:e1875bb4b4e2de1669f4bc7869b6d3f54231cdced71605e6e64c9be77e3be50f" ], - "markers": "python_version >= '3.7'", - "version": "==3.7.1" + "markers": "python_version >= '3.8'", + "version": "==4.2.0" }, "argon2-cffi": { "hashes": [ @@ -1642,21 +1531,13 @@ "markers": "python_version >= '3.8'", "version": "==2.0.4" }, - "async-timeout": { - "hashes": [ - "sha256:4640d96be84d82d02ed59ea2b7105a0f7b33abe8703703cd0ab0bf87c427522f", - "sha256:7405140ff1230c310e51dc27b3145b9092d659ce68ff733fb0cefe3ee42be028" - ], - "markers": "python_full_version < '3.12.0'", - "version": "==4.0.3" - }, "attrs": { "hashes": [ - "sha256:1f28b4522cdc2fb4256ac1a020c78acf9cba2c6b461ccd2c126f3aa8e8335d04", - "sha256:6279836d581513a26f1bf235f9acd333bc9115683f14f7e8fae46c98fc50e015" + "sha256:935dc3b529c262f6cf76e50877d35a4bd3c1de194fd41f47a2b7ae8f19971f30", + "sha256:99b87a485a5820b23b879f04c2305b44b951b502fd64be915879d77a7e8fc6f1" ], "markers": "python_version >= '3.7'", - "version": "==23.1.0" + "version": "==23.2.0" }, "babel": { "hashes": [ @@ -1676,32 +1557,32 @@ }, "black": { "hashes": [ - "sha256:12d5f10cce8dc27202e9a252acd1c9a426c83f95496c959406c96b785a92bb7d", - "sha256:193946e634e80bfb3aec41830f5d7431f8dd5b20d11d89be14b84a97c6b8bc75", - "sha256:330a327b422aca0634ecd115985c1c7fd7bdb5b5a2ef8aa9888a82e2ebe9437a", - "sha256:39dda060b9b395a6b7bf9c5db28ac87b3c3f48d4fdff470fa8a94ab8271da47e", - "sha256:593596f699ca2dcbbbdfa59fcda7d8ad6604370c10228223cd6cf6ce1ce7ed7e", - "sha256:67f19562d367468ab59bd6c36a72b2c84bc2f16b59788690e02bbcb140a77175", - "sha256:6a82a711d13e61840fb11a6dfecc7287f2424f1ca34765e70c909a35ffa7fb95", - "sha256:7231670266ca5191a76cb838185d9be59cfa4f5dd401b7c1c70b993c58f6b1b5", - "sha256:72db37a2266b16d256b3ea88b9affcdd5c41a74db551ec3dd4609a59c17d25bf", - "sha256:81a832b6e00eef2c13b3239d514ea3b7d5cc3eaa03d0474eedcbbda59441ba5d", - "sha256:97af22278043a6a1272daca10a6f4d36c04dfa77e61cbaaf4482e08f3640e9f0", - "sha256:996650a89fe5892714ea4ea87bc45e41a59a1e01675c42c433a35b490e5aa3f0", - "sha256:a7c07db8200b5315dc07e331dda4d889a56f6bf4db6a9c2a526fa3166a81614f", - "sha256:ace64c1a349c162d6da3cef91e3b0e78c4fc596ffde9413efa0525456148873d", - "sha256:ba09cae1657c4f8a8c9ff6cfd4a6baaf915bb4ef7d03acffe6a2f6585fa1bd01", - "sha256:bbd75d9f28a7283b7426160ca21c5bd640ca7cd8ef6630b4754b6df9e2da8462", - "sha256:bcf91b01ddd91a2fed9a8006d7baa94ccefe7e518556470cf40213bd3d44bbbc", - "sha256:bdbff34c487239a63d86db0c9385b27cdd68b1bfa4e706aa74bb94a435403672", - "sha256:c71048345bdbced456cddf1622832276d98a710196b842407840ae8055ade6ee", - "sha256:e73c5e3d37e5a3513d16b33305713237a234396ae56769b839d7c40759b8a41c", - "sha256:ead25c273adfad1095a8ad32afdb8304933efba56e3c1d31b0fee4143a1e424a", - "sha256:fdf6f23c83078a6c8da2442f4d4eeb19c28ac2a6416da7671b72f0295c4a697b" + "sha256:0808494f2b2df923ffc5723ed3c7b096bd76341f6213989759287611e9837d50", + "sha256:1fa88a0f74e50e4487477bc0bb900c6781dbddfdfa32691e780bf854c3b4a47f", + "sha256:25e57fd232a6d6ff3f4478a6fd0580838e47c93c83eaf1ccc92d4faf27112c4e", + "sha256:2d9e13db441c509a3763a7a3d9a49ccc1b4e974a47be4e08ade2a228876500ec", + "sha256:3e1b38b3135fd4c025c28c55ddfc236b05af657828a8a6abe5deec419a0b7055", + "sha256:3fa4be75ef2a6b96ea8d92b1587dd8cb3a35c7e3d51f0738ced0781c3aa3a5a3", + "sha256:4ce3ef14ebe8d9509188014d96af1c456a910d5b5cbf434a09fef7e024b3d0d5", + "sha256:4f0031eaa7b921db76decd73636ef3a12c942ed367d8c3841a0739412b260a54", + "sha256:602cfb1196dc692424c70b6507593a2b29aac0547c1be9a1d1365f0d964c353b", + "sha256:6d1bd9c210f8b109b1762ec9fd36592fdd528485aadb3f5849b2740ef17e674e", + "sha256:78baad24af0f033958cad29731e27363183e140962595def56423e626f4bee3e", + "sha256:8d4df77958a622f9b5a4c96edb4b8c0034f8434032ab11077ec6c56ae9f384ba", + "sha256:97e56155c6b737854e60a9ab1c598ff2533d57e7506d97af5481141671abf3ea", + "sha256:9c4352800f14be5b4864016882cdba10755bd50805c95f728011bcb47a4afd59", + "sha256:a4d6a9668e45ad99d2f8ec70d5c8c04ef4f32f648ef39048d010b0689832ec6d", + "sha256:a920b569dc6b3472513ba6ddea21f440d4b4c699494d2e972a1753cdc25df7b0", + "sha256:ae76c22bde5cbb6bfd211ec343ded2163bba7883c7bc77f6b756a1049436fbb9", + "sha256:b18fb2ae6c4bb63eebe5be6bd869ba2f14fd0259bda7d18a46b764d8fb86298a", + "sha256:c04b6d9d20e9c13f43eee8ea87d44156b8505ca8a3c878773f68b4e4812a421e", + "sha256:c88b3711d12905b74206227109272673edce0cb29f27e1385f33b0163c414bba", + "sha256:dd15245c8b68fe2b6bd0f32c1556509d11bb33aec9b5d0866dd8e2ed3dba09c2", + "sha256:e0aaf6041986767a5e0ce663c7a2f0e9eaf21e6ff87a5f95cbf3675bfd4c41d2" ], "index": "pypi", "markers": "python_version >= '3.8'", - "version": "==23.12.0" + "version": "==23.12.1" }, "bleach": { "hashes": [ @@ -1911,61 +1792,61 @@ "toml" ], "hashes": [ - "sha256:007a7e49831cfe387473e92e9ff07377f6121120669ddc39674e7244350a6a29", - "sha256:1191270b06ecd68b1d00897b2daddb98e1719f63750969614ceb3438228c088e", - "sha256:1367aa411afb4431ab58fd7ee102adb2665894d047c490649e86219327183134", - "sha256:1f0f8f0c497eb9c9f18f21de0750c8d8b4b9c7000b43996a094290b59d0e7523", - "sha256:222b038f08a7ebed1e4e78ccf3c09a1ca4ac3da16de983e66520973443b546bc", - "sha256:243576944f7c1a1205e5cd658533a50eba662c74f9be4c050d51c69bd4532936", - "sha256:2e9223a18f51d00d3ce239c39fc41410489ec7a248a84fab443fbb39c943616c", - "sha256:307aecb65bb77cbfebf2eb6e12009e9034d050c6c69d8a5f3f737b329f4f15fb", - "sha256:31c0b1b8b5a4aebf8fcd227237fc4263aa7fa0ddcd4d288d42f50eff18b0bac4", - "sha256:3b15e03b8ee6a908db48eccf4e4e42397f146ab1e91c6324da44197a45cb9132", - "sha256:3c854c1d2c7d3e47f7120b560d1a30c1ca221e207439608d27bc4d08fd4aeae8", - "sha256:475de8213ed95a6b6283056d180b2442eee38d5948d735cd3d3b52b86dd65b92", - "sha256:50c472c1916540f8b2deef10cdc736cd2b3d1464d3945e4da0333862270dcb15", - "sha256:593efa42160c15c59ee9b66c5f27a453ed3968718e6e58431cdfb2d50d5ad284", - "sha256:65d716b736f16e250435473c5ca01285d73c29f20097decdbb12571d5dfb2c94", - "sha256:733537a182b5d62184f2a72796eb6901299898231a8e4f84c858c68684b25a70", - "sha256:757453848c18d7ab5d5b5f1827293d580f156f1c2c8cef45bfc21f37d8681069", - "sha256:79c32f875fd7c0ed8d642b221cf81feba98183d2ff14d1f37a1bbce6b0347d9f", - "sha256:7f3bad1a9313401ff2964e411ab7d57fb700a2d5478b727e13f156c8f89774a0", - "sha256:7fbf3f5756e7955174a31fb579307d69ffca91ad163467ed123858ce0f3fd4aa", - "sha256:811ca7373da32f1ccee2927dc27dc523462fd30674a80102f86c6753d6681bc6", - "sha256:89400aa1752e09f666cc48708eaa171eef0ebe3d5f74044b614729231763ae69", - "sha256:8c944cf1775235c0857829c275c777a2c3e33032e544bcef614036f337ac37bb", - "sha256:9437a4074b43c177c92c96d051957592afd85ba00d3e92002c8ef45ee75df438", - "sha256:9e17d9cb06c13b4f2ef570355fa45797d10f19ca71395910b249e3f77942a837", - "sha256:9ede881c7618f9cf93e2df0421ee127afdfd267d1b5d0c59bcea771cf160ea4a", - "sha256:a1f76cfc122c9e0f62dbe0460ec9cc7696fc9a0293931a33b8870f78cf83a327", - "sha256:a2ac4245f18057dfec3b0074c4eb366953bca6787f1ec397c004c78176a23d56", - "sha256:a702e66483b1fe602717020a0e90506e759c84a71dbc1616dd55d29d86a9b91f", - "sha256:ad2453b852a1316c8a103c9c970db8fbc262f4f6b930aa6c606df9b2766eee06", - "sha256:af75cf83c2d57717a8493ed2246d34b1f3398cb8a92b10fd7a1858cad8e78f59", - "sha256:afdcc10c01d0db217fc0a64f58c7edd635b8f27787fea0a3054b856a6dff8717", - "sha256:c59a3e59fb95e6d72e71dc915e6d7fa568863fad0a80b33bc7b82d6e9f844973", - "sha256:cad9afc1644b979211989ec3ff7d82110b2ed52995c2f7263e7841c846a75348", - "sha256:d299d379b676812e142fb57662a8d0d810b859421412b4d7af996154c00c31bb", - "sha256:d31650d313bd90d027f4be7663dfa2241079edd780b56ac416b56eebe0a21aab", - "sha256:d874434e0cb7b90f7af2b6e3309b0733cde8ec1476eb47db148ed7deeb2a9494", - "sha256:db0338c4b0951d93d547e0ff8d8ea340fecf5885f5b00b23be5aa99549e14cfd", - "sha256:df04c64e58df96b4427db8d0559e95e2df3138c9916c96f9f6a4dd220db2fdb7", - "sha256:e995efb191f04b01ced307dbd7407ebf6e6dc209b528d75583277b10fd1800ee", - "sha256:eda7f6e92358ac9e1717ce1f0377ed2b9320cea070906ece4e5c11d172a45a39", - "sha256:ee453085279df1bac0996bc97004771a4a052b1f1e23f6101213e3796ff3cb85", - "sha256:ee6621dccce8af666b8c4651f9f43467bfbf409607c604b840b78f4ff3619aeb", - "sha256:eee5e741b43ea1b49d98ab6e40f7e299e97715af2488d1c77a90de4a663a86e2", - "sha256:f3bfd2c2f0e5384276e12b14882bf2c7621f97c35320c3e7132c156ce18436a1", - "sha256:f501e36ac428c1b334c41e196ff6bd550c0353c7314716e80055b1f0a32ba394", - "sha256:f9191be7af41f0b54324ded600e8ddbcabea23e1e8ba419d9a53b241dece821d", - "sha256:fbd8a5fe6c893de21a3c6835071ec116d79334fbdf641743332e442a3466f7ea", - "sha256:fc200cec654311ca2c3f5ab3ce2220521b3d4732f68e1b1e79bef8fcfc1f2b97", - "sha256:ff4800783d85bff132f2cc7d007426ec698cdce08c3062c8d501ad3f4ea3d16c", - "sha256:ffb0eacbadb705c0a6969b0adf468f126b064f3362411df95f6d4f31c40d31c1", - "sha256:fff0b2f249ac642fd735f009b8363c2b46cf406d3caec00e4deeb79b5ff39b40" - ], - "markers": "python_version >= '3.8'", - "version": "==7.3.3" + "sha256:04387a4a6ecb330c1878907ce0dc04078ea72a869263e53c72a1ba5bbdf380ca", + "sha256:0676cd0ba581e514b7f726495ea75aba3eb20899d824636c6f59b0ed2f88c471", + "sha256:0e8d06778e8fbffccfe96331a3946237f87b1e1d359d7fbe8b06b96c95a5407a", + "sha256:0eb3c2f32dabe3a4aaf6441dde94f35687224dfd7eb2a7f47f3fd9428e421058", + "sha256:109f5985182b6b81fe33323ab4707011875198c41964f014579cf82cebf2bb85", + "sha256:13eaf476ec3e883fe3e5fe3707caeb88268a06284484a3daf8250259ef1ba143", + "sha256:164fdcc3246c69a6526a59b744b62e303039a81e42cfbbdc171c91a8cc2f9446", + "sha256:26776ff6c711d9d835557ee453082025d871e30b3fd6c27fcef14733f67f0590", + "sha256:26f66da8695719ccf90e794ed567a1549bb2644a706b41e9f6eae6816b398c4a", + "sha256:29f3abe810930311c0b5d1a7140f6395369c3db1be68345638c33eec07535105", + "sha256:316543f71025a6565677d84bc4df2114e9b6a615aa39fb165d697dba06a54af9", + "sha256:36b0ea8ab20d6a7564e89cb6135920bc9188fb5f1f7152e94e8300b7b189441a", + "sha256:3cc9d4bc55de8003663ec94c2f215d12d42ceea128da8f0f4036235a119c88ac", + "sha256:485e9f897cf4856a65a57c7f6ea3dc0d4e6c076c87311d4bc003f82cfe199d25", + "sha256:5040148f4ec43644702e7b16ca864c5314ccb8ee0751ef617d49aa0e2d6bf4f2", + "sha256:51456e6fa099a8d9d91497202d9563a320513fcf59f33991b0661a4a6f2ad450", + "sha256:53d7d9158ee03956e0eadac38dfa1ec8068431ef8058fe6447043db1fb40d932", + "sha256:5a10a4920def78bbfff4eff8a05c51be03e42f1c3735be42d851f199144897ba", + "sha256:5b14b4f8760006bfdb6e08667af7bc2d8d9bfdb648351915315ea17645347137", + "sha256:5b2ccb7548a0b65974860a78c9ffe1173cfb5877460e5a229238d985565574ae", + "sha256:697d1317e5290a313ef0d369650cfee1a114abb6021fa239ca12b4849ebbd614", + "sha256:6ae8c9d301207e6856865867d762a4b6fd379c714fcc0607a84b92ee63feff70", + "sha256:707c0f58cb1712b8809ece32b68996ee1e609f71bd14615bd8f87a1293cb610e", + "sha256:74775198b702868ec2d058cb92720a3c5a9177296f75bd97317c787daf711505", + "sha256:756ded44f47f330666843b5781be126ab57bb57c22adbb07d83f6b519783b870", + "sha256:76f03940f9973bfaee8cfba70ac991825611b9aac047e5c80d499a44079ec0bc", + "sha256:79287fd95585ed36e83182794a57a46aeae0b64ca53929d1176db56aacc83451", + "sha256:799c8f873794a08cdf216aa5d0531c6a3747793b70c53f70e98259720a6fe2d7", + "sha256:7d360587e64d006402b7116623cebf9d48893329ef035278969fa3bbf75b697e", + "sha256:80b5ee39b7f0131ebec7968baa9b2309eddb35b8403d1869e08f024efd883566", + "sha256:815ac2d0f3398a14286dc2cea223a6f338109f9ecf39a71160cd1628786bc6f5", + "sha256:83c2dda2666fe32332f8e87481eed056c8b4d163fe18ecc690b02802d36a4d26", + "sha256:846f52f46e212affb5bcf131c952fb4075b55aae6b61adc9856222df89cbe3e2", + "sha256:936d38794044b26c99d3dd004d8af0035ac535b92090f7f2bb5aa9c8e2f5cd42", + "sha256:9864463c1c2f9cb3b5db2cf1ff475eed2f0b4285c2aaf4d357b69959941aa555", + "sha256:995ea5c48c4ebfd898eacb098164b3cc826ba273b3049e4a889658548e321b43", + "sha256:a1526d265743fb49363974b7aa8d5899ff64ee07df47dd8d3e37dcc0818f09ed", + "sha256:a56de34db7b7ff77056a37aedded01b2b98b508227d2d0979d373a9b5d353daa", + "sha256:a7c97726520f784239f6c62506bc70e48d01ae71e9da128259d61ca5e9788516", + "sha256:b8e99f06160602bc64da35158bb76c73522a4010f0649be44a4e167ff8555952", + "sha256:bb1de682da0b824411e00a0d4da5a784ec6496b6850fdf8c865c1d68c0e318dd", + "sha256:bf477c355274a72435ceb140dc42de0dc1e1e0bf6e97195be30487d8eaaf1a09", + "sha256:bf635a52fc1ea401baf88843ae8708591aa4adff875e5c23220de43b1ccf575c", + "sha256:bfd5db349d15c08311702611f3dccbef4b4e2ec148fcc636cf8739519b4a5c0f", + "sha256:c530833afc4707fe48524a44844493f36d8727f04dcce91fb978c414a8556cc6", + "sha256:cc6d65b21c219ec2072c1293c505cf36e4e913a3f936d80028993dd73c7906b1", + "sha256:cd3c1e4cb2ff0083758f09be0f77402e1bdf704adb7f89108007300a6da587d0", + "sha256:cfd2a8b6b0d8e66e944d47cdec2f47c48fef2ba2f2dff5a9a75757f64172857e", + "sha256:d0ca5c71a5a1765a0f8f88022c52b6b8be740e512980362f7fdbb03725a0d6b9", + "sha256:e7defbb9737274023e2d7af02cac77043c86ce88a907c58f42b580a97d5bcca9", + "sha256:e9d1bf53c4c8de58d22e0e956a79a5b37f754ed1ffdbf1a260d9dcfa2d8a325e", + "sha256:ea81d8f9691bb53f4fb4db603203029643caffc82bf998ab5b59ca05560f4c06" + ], + "markers": "python_version >= '3.8'", + "version": "==7.4.0" }, "debugpy": { "hashes": [ @@ -2041,19 +1922,19 @@ }, "faker": { "hashes": [ - "sha256:2d8a350e952225a145307d7461881c44a1c9320e90fbe8bd903d5947f133f3ec", - "sha256:ff61cca42547795bee8a11319792a8fee6d0f0cd191e831f7f3050c5851fcd8a" + "sha256:1d5dc0a75da7bc40741ee4c84d99dc087b97bd086d4222ad06ac4dd2219bcf3f", + "sha256:9c22c0a734ca01c6e4f2259eab5dab9081905a9d67b27272aea5c9feeb5a3789" ], "index": "pypi", "markers": "python_version >= '3.8'", - "version": "==21.0.0" + "version": "==22.0.0" }, "fastjsonschema": { "hashes": [ - "sha256:b9fd1a2dd6971dbc7fee280a95bd199ae0dd9ce22beb91cc75e9c1c528a5170e", - "sha256:e25df6647e1bc4a26070b700897b07b542ec898dd4f1f6ea013e7f6a88417225" + "sha256:3672b47bc94178c9f23dbb654bf47440155d4db9df5f7bc47643315f9c405cd0", + "sha256:e3126a94bdc4623d3de4485f8d468a12f02a67921315ddc87836d6e456dc789d" ], - "version": "==2.19.0" + "version": "==2.19.1" }, "flake8": { "hashes": [ @@ -2073,95 +1954,12 @@ }, "freezegun": { "hashes": [ - "sha256:065e77a12624d05531afa87ade12a0b9bdb53495c4573893252a055b545ce3ea", - "sha256:48984397b3b58ef5dfc645d6a304b0060f612bcecfdaaf45ce8aff0077a6cb6a" + "sha256:10939b0ba0ff5adaecf3b06a5c2f73071d9678e507c5eaedb23c761d56ac774b", + "sha256:55e0fc3c84ebf0a96a5aa23ff8b53d70246479e9a68863f1fcac5a3e52f19dd6" ], "index": "pypi", "markers": "python_version >= '3.7'", - "version": "==1.3.1" - }, - "frozenlist": { - "hashes": [ - "sha256:04ced3e6a46b4cfffe20f9ae482818e34eba9b5fb0ce4056e4cc9b6e212d09b7", - "sha256:0633c8d5337cb5c77acbccc6357ac49a1770b8c487e5b3505c57b949b4b82e98", - "sha256:068b63f23b17df8569b7fdca5517edef76171cf3897eb68beb01341131fbd2ad", - "sha256:0c250a29735d4f15321007fb02865f0e6b6a41a6b88f1f523ca1596ab5f50bd5", - "sha256:1979bc0aeb89b33b588c51c54ab0161791149f2461ea7c7c946d95d5f93b56ae", - "sha256:1a4471094e146b6790f61b98616ab8e44f72661879cc63fa1049d13ef711e71e", - "sha256:1b280e6507ea8a4fa0c0a7150b4e526a8d113989e28eaaef946cc77ffd7efc0a", - "sha256:1d0ce09d36d53bbbe566fe296965b23b961764c0bcf3ce2fa45f463745c04701", - "sha256:20b51fa3f588ff2fe658663db52a41a4f7aa6c04f6201449c6c7c476bd255c0d", - "sha256:23b2d7679b73fe0e5a4560b672a39f98dfc6f60df63823b0a9970525325b95f6", - "sha256:23b701e65c7b36e4bf15546a89279bd4d8675faabc287d06bbcfac7d3c33e1e6", - "sha256:2471c201b70d58a0f0c1f91261542a03d9a5e088ed3dc6c160d614c01649c106", - "sha256:27657df69e8801be6c3638054e202a135c7f299267f1a55ed3a598934f6c0d75", - "sha256:29acab3f66f0f24674b7dc4736477bcd4bc3ad4b896f5f45379a67bce8b96868", - "sha256:32453c1de775c889eb4e22f1197fe3bdfe457d16476ea407472b9442e6295f7a", - "sha256:3a670dc61eb0d0eb7080890c13de3066790f9049b47b0de04007090807c776b0", - "sha256:3e0153a805a98f5ada7e09826255ba99fb4f7524bb81bf6b47fb702666484ae1", - "sha256:410478a0c562d1a5bcc2f7ea448359fcb050ed48b3c6f6f4f18c313a9bdb1826", - "sha256:442acde1e068288a4ba7acfe05f5f343e19fac87bfc96d89eb886b0363e977ec", - "sha256:48f6a4533887e189dae092f1cf981f2e3885175f7a0f33c91fb5b7b682b6bab6", - "sha256:4f57dab5fe3407b6c0c1cc907ac98e8a189f9e418f3b6e54d65a718aaafe3950", - "sha256:4f9c515e7914626b2a2e1e311794b4c35720a0be87af52b79ff8e1429fc25f19", - "sha256:55fdc093b5a3cb41d420884cdaf37a1e74c3c37a31f46e66286d9145d2063bd0", - "sha256:5667ed53d68d91920defdf4035d1cdaa3c3121dc0b113255124bcfada1cfa1b8", - "sha256:590344787a90ae57d62511dd7c736ed56b428f04cd8c161fcc5e7232c130c69a", - "sha256:5a7d70357e7cee13f470c7883a063aae5fe209a493c57d86eb7f5a6f910fae09", - "sha256:5c3894db91f5a489fc8fa6a9991820f368f0b3cbdb9cd8849547ccfab3392d86", - "sha256:5c849d495bf5154cd8da18a9eb15db127d4dba2968d88831aff6f0331ea9bd4c", - "sha256:64536573d0a2cb6e625cf309984e2d873979709f2cf22839bf2d61790b448ad5", - "sha256:693945278a31f2086d9bf3df0fe8254bbeaef1fe71e1351c3bd730aa7d31c41b", - "sha256:6db4667b187a6742b33afbbaf05a7bc551ffcf1ced0000a571aedbb4aa42fc7b", - "sha256:6eb73fa5426ea69ee0e012fb59cdc76a15b1283d6e32e4f8dc4482ec67d1194d", - "sha256:722e1124aec435320ae01ee3ac7bec11a5d47f25d0ed6328f2273d287bc3abb0", - "sha256:7268252af60904bf52c26173cbadc3a071cece75f873705419c8681f24d3edea", - "sha256:74fb4bee6880b529a0c6560885fce4dc95936920f9f20f53d99a213f7bf66776", - "sha256:780d3a35680ced9ce682fbcf4cb9c2bad3136eeff760ab33707b71db84664e3a", - "sha256:82e8211d69a4f4bc360ea22cd6555f8e61a1bd211d1d5d39d3d228b48c83a897", - "sha256:89aa2c2eeb20957be2d950b85974b30a01a762f3308cd02bb15e1ad632e22dc7", - "sha256:8aefbba5f69d42246543407ed2461db31006b0f76c4e32dfd6f42215a2c41d09", - "sha256:96ec70beabbd3b10e8bfe52616a13561e58fe84c0101dd031dc78f250d5128b9", - "sha256:9750cc7fe1ae3b1611bb8cfc3f9ec11d532244235d75901fb6b8e42ce9229dfe", - "sha256:9acbb16f06fe7f52f441bb6f413ebae6c37baa6ef9edd49cdd567216da8600cd", - "sha256:9d3e0c25a2350080e9319724dede4f31f43a6c9779be48021a7f4ebde8b2d742", - "sha256:a06339f38e9ed3a64e4c4e43aec7f59084033647f908e4259d279a52d3757d09", - "sha256:a0cb6f11204443f27a1628b0e460f37fb30f624be6051d490fa7d7e26d4af3d0", - "sha256:a7496bfe1da7fb1a4e1cc23bb67c58fab69311cc7d32b5a99c2007b4b2a0e932", - "sha256:a828c57f00f729620a442881cc60e57cfcec6842ba38e1b19fd3e47ac0ff8dc1", - "sha256:a9b2de4cf0cdd5bd2dee4c4f63a653c61d2408055ab77b151c1957f221cabf2a", - "sha256:b46c8ae3a8f1f41a0d2ef350c0b6e65822d80772fe46b653ab6b6274f61d4a49", - "sha256:b7e3ed87d4138356775346e6845cccbe66cd9e207f3cd11d2f0b9fd13681359d", - "sha256:b7f2f9f912dca3934c1baec2e4585a674ef16fe00218d833856408c48d5beee7", - "sha256:ba60bb19387e13597fb059f32cd4d59445d7b18b69a745b8f8e5db0346f33480", - "sha256:beee944ae828747fd7cb216a70f120767fc9f4f00bacae8543c14a6831673f89", - "sha256:bfa4a17e17ce9abf47a74ae02f32d014c5e9404b6d9ac7f729e01562bbee601e", - "sha256:c037a86e8513059a2613aaba4d817bb90b9d9b6b69aace3ce9c877e8c8ed402b", - "sha256:c302220494f5c1ebeb0912ea782bcd5e2f8308037b3c7553fad0e48ebad6ad82", - "sha256:c6321c9efe29975232da3bd0af0ad216800a47e93d763ce64f291917a381b8eb", - "sha256:c757a9dd70d72b076d6f68efdbb9bc943665ae954dad2801b874c8c69e185068", - "sha256:c99169d4ff810155ca50b4da3b075cbde79752443117d89429595c2e8e37fed8", - "sha256:c9c92be9fd329ac801cc420e08452b70e7aeab94ea4233a4804f0915c14eba9b", - "sha256:cc7b01b3754ea68a62bd77ce6020afaffb44a590c2289089289363472d13aedb", - "sha256:db9e724bebd621d9beca794f2a4ff1d26eed5965b004a97f1f1685a173b869c2", - "sha256:dca69045298ce5c11fd539682cff879cc1e664c245d1c64da929813e54241d11", - "sha256:dd9b1baec094d91bf36ec729445f7769d0d0cf6b64d04d86e45baf89e2b9059b", - "sha256:e02a0e11cf6597299b9f3bbd3f93d79217cb90cfd1411aec33848b13f5c656cc", - "sha256:e6a20a581f9ce92d389a8c7d7c3dd47c81fd5d6e655c8dddf341e14aa48659d0", - "sha256:e7004be74cbb7d9f34553a5ce5fb08be14fb33bc86f332fb71cbe5216362a497", - "sha256:e774d53b1a477a67838a904131c4b0eef6b3d8a651f8b138b04f748fccfefe17", - "sha256:edb678da49d9f72c9f6c609fbe41a5dfb9a9282f9e6a2253d5a91e0fc382d7c0", - "sha256:f146e0911cb2f1da549fc58fc7bcd2b836a44b79ef871980d605ec392ff6b0d2", - "sha256:f56e2333dda1fe0f909e7cc59f021eba0d2307bc6f012a1ccf2beca6ba362439", - "sha256:f9a3ea26252bd92f570600098783d1371354d89d5f6b7dfd87359d669f2109b5", - "sha256:f9aa1878d1083b276b0196f2dfbe00c9b7e752475ed3b682025ff20c1c1f51ac", - "sha256:fb3c2db03683b5767dedb5769b8a40ebb47d6f7f45b1b3e3b4b51ec8ad9d9825", - "sha256:fbeb989b5cc29e8daf7f976b421c220f1b8c731cbf22b9130d8815418ea45887", - "sha256:fde5bd59ab5357e3853313127f4d3565fc7dad314a74d7b5d43c22c6a5ed2ced", - "sha256:fe1a06da377e3a1062ae5fe0926e12b84eceb8a50b350ddca72dc85015873f74" - ], - "markers": "python_version >= '3.8'", - "version": "==1.4.1" + "version": "==1.4.0" }, "furo": { "hashes": [ @@ -2172,6 +1970,118 @@ "markers": "python_version >= '3.8'", "version": "==2023.9.10" }, + "gevent": { + "hashes": [ + "sha256:272cffdf535978d59c38ed837916dfd2b5d193be1e9e5dcc60a5f4d5025dd98a", + "sha256:2c7b5c9912378e5f5ccf180d1fdb1e83f42b71823483066eddbe10ef1a2fcaa2", + "sha256:36a549d632c14684bcbbd3014a6ce2666c5f2a500f34d58d32df6c9ea38b6535", + "sha256:4368f341a5f51611411ec3fc62426f52ac3d6d42eaee9ed0f9eebe715c80184e", + "sha256:43daf68496c03a35287b8b617f9f91e0e7c0d042aebcc060cadc3f049aadd653", + "sha256:455e5ee8103f722b503fa45dedb04f3ffdec978c1524647f8ba72b4f08490af1", + "sha256:45792c45d60f6ce3d19651d7fde0bc13e01b56bb4db60d3f32ab7d9ec467374c", + "sha256:4e24c2af9638d6c989caffc691a039d7c7022a31c0363da367c0d32ceb4a0648", + "sha256:52b4abf28e837f1865a9bdeef58ff6afd07d1d888b70b6804557e7908032e599", + "sha256:52e9f12cd1cda96603ce6b113d934f1aafb873e2c13182cf8e86d2c5c41982ea", + "sha256:5f3c781c84794926d853d6fb58554dc0dcc800ba25c41d42f6959c344b4db5a6", + "sha256:62d121344f7465e3739989ad6b91f53a6ca9110518231553fe5846dbe1b4518f", + "sha256:65883ac026731ac112184680d1f0f1e39fa6f4389fd1fc0bf46cc1388e2599f9", + "sha256:707904027d7130ff3e59ea387dddceedb133cc742b00b3ffe696d567147a9c9e", + "sha256:72c002235390d46f94938a96920d8856d4ffd9ddf62a303a0d7c118894097e34", + "sha256:7532c17bc6c1cbac265e751b95000961715adef35a25d2b0b1813aa7263fb397", + "sha256:78eebaf5e73ff91d34df48f4e35581ab4c84e22dd5338ef32714264063c57507", + "sha256:7c1abc6f25f475adc33e5fc2dbcc26a732608ac5375d0d306228738a9ae14d3b", + "sha256:7c28e38dcde327c217fdafb9d5d17d3e772f636f35df15ffae2d933a5587addd", + "sha256:7ccf0fd378257cb77d91c116e15c99e533374a8153632c48a3ecae7f7f4f09fe", + "sha256:921dda1c0b84e3d3b1778efa362d61ed29e2b215b90f81d498eb4d8eafcd0b7a", + "sha256:a2898b7048771917d85a1d548fd378e8a7b2ca963db8e17c6d90c76b495e0e2b", + "sha256:a3c5e9b1f766a7a64833334a18539a362fb563f6c4682f9634dea72cbe24f771", + "sha256:ada07076b380918829250201df1d016bdafb3acf352f35e5693b59dceee8dd2e", + "sha256:b101086f109168b23fa3586fccd1133494bdb97f86920a24dc0b23984dc30b69", + "sha256:bf456bd6b992eb0e1e869e2fd0caf817f0253e55ca7977fd0e72d0336a8c1c6a", + "sha256:bf7af500da05363e66f122896012acb6e101a552682f2352b618e541c941a011", + "sha256:c3e5d2fa532e4d3450595244de8ccf51f5721a05088813c1abd93ad274fe15e7", + "sha256:c84d34256c243b0a53d4335ef0bc76c735873986d478c53073861a92566a8d71", + "sha256:d163d59f1be5a4c4efcdd13c2177baaf24aadf721fdf2e1af9ee54a998d160f5", + "sha256:d57737860bfc332b9b5aa438963986afe90f49645f6e053140cfa0fa1bdae1ae", + "sha256:dbb22a9bbd6a13e925815ce70b940d1578dbe5d4013f20d23e8a11eddf8d14a7", + "sha256:dcb8612787a7f4626aa881ff15ff25439561a429f5b303048f0fca8a1c781c39", + "sha256:dd6c32ab977ecf7c7b8c2611ed95fa4aaebd69b74bf08f4b4960ad516861517d", + "sha256:de350fde10efa87ea60d742901e1053eb2127ebd8b59a7d3b90597eb4e586599", + "sha256:e1ead6863e596a8cc2a03e26a7a0981f84b6b3e956101135ff6d02df4d9a6b07", + "sha256:ed7a048d3e526a5c1d55c44cb3bc06cfdc1947d06d45006cc4cf60dedc628904", + "sha256:f632487c87866094546a74eefbca2c74c1d03638b715b6feb12e80120960185a", + "sha256:fae8d5b5b8fa2a8f63b39f5447168b02db10c888a3e387ed7af2bd1b8612e543", + "sha256:fde6402c5432b835fbb7698f1c7f2809c8d6b2bd9d047ac1f5a7c1d5aa569303" + ], + "index": "pypi", + "markers": "python_version >= '3.8'", + "version": "==23.9.1" + }, + "greenlet": { + "hashes": [ + "sha256:01bc7ea167cf943b4c802068e178bbf70ae2e8c080467070d01bfa02f337ee67", + "sha256:0448abc479fab28b00cb472d278828b3ccca164531daab4e970a0458786055d6", + "sha256:086152f8fbc5955df88382e8a75984e2bb1c892ad2e3c80a2508954e52295257", + "sha256:098d86f528c855ead3479afe84b49242e174ed262456c342d70fc7f972bc13c4", + "sha256:149e94a2dd82d19838fe4b2259f1b6b9957d5ba1b25640d2380bea9c5df37676", + "sha256:1551a8195c0d4a68fac7a4325efac0d541b48def35feb49d803674ac32582f61", + "sha256:15d79dd26056573940fcb8c7413d84118086f2ec1a8acdfa854631084393efcc", + "sha256:1996cb9306c8595335bb157d133daf5cf9f693ef413e7673cb07e3e5871379ca", + "sha256:1a7191e42732df52cb5f39d3527217e7ab73cae2cb3694d241e18f53d84ea9a7", + "sha256:1ea188d4f49089fc6fb283845ab18a2518d279c7cd9da1065d7a84e991748728", + "sha256:1f672519db1796ca0d8753f9e78ec02355e862d0998193038c7073045899f305", + "sha256:2516a9957eed41dd8f1ec0c604f1cdc86758b587d964668b5b196a9db5bfcde6", + "sha256:2797aa5aedac23af156bbb5a6aa2cd3427ada2972c828244eb7d1b9255846379", + "sha256:2dd6e660effd852586b6a8478a1d244b8dc90ab5b1321751d2ea15deb49ed414", + "sha256:3ddc0f794e6ad661e321caa8d2f0a55ce01213c74722587256fb6566049a8b04", + "sha256:3ed7fb269f15dc662787f4119ec300ad0702fa1b19d2135a37c2c4de6fadfd4a", + "sha256:419b386f84949bf0e7c73e6032e3457b82a787c1ab4a0e43732898a761cc9dbf", + "sha256:43374442353259554ce33599da8b692d5aa96f8976d567d4badf263371fbe491", + "sha256:52f59dd9c96ad2fc0d5724107444f76eb20aaccb675bf825df6435acb7703559", + "sha256:57e8974f23e47dac22b83436bdcf23080ade568ce77df33159e019d161ce1d1e", + "sha256:5b51e85cb5ceda94e79d019ed36b35386e8c37d22f07d6a751cb659b180d5274", + "sha256:649dde7de1a5eceb258f9cb00bdf50e978c9db1b996964cd80703614c86495eb", + "sha256:64d7675ad83578e3fc149b617a444fab8efdafc9385471f868eb5ff83e446b8b", + "sha256:68834da854554926fbedd38c76e60c4a2e3198c6fbed520b106a8986445caaf9", + "sha256:6b66c9c1e7ccabad3a7d037b2bcb740122a7b17a53734b7d72a344ce39882a1b", + "sha256:70fb482fdf2c707765ab5f0b6655e9cfcf3780d8d87355a063547b41177599be", + "sha256:7170375bcc99f1a2fbd9c306f5be8764eaf3ac6b5cb968862cad4c7057756506", + "sha256:73a411ef564e0e097dbe7e866bb2dda0f027e072b04da387282b02c308807405", + "sha256:77457465d89b8263bca14759d7c1684df840b6811b2499838cc5b040a8b5b113", + "sha256:7f362975f2d179f9e26928c5b517524e89dd48530a0202570d55ad6ca5d8a56f", + "sha256:81bb9c6d52e8321f09c3d165b2a78c680506d9af285bfccbad9fb7ad5a5da3e5", + "sha256:881b7db1ebff4ba09aaaeae6aa491daeb226c8150fc20e836ad00041bcb11230", + "sha256:894393ce10ceac937e56ec00bb71c4c2f8209ad516e96033e4b3b1de270e200d", + "sha256:99bf650dc5d69546e076f413a87481ee1d2d09aaaaaca058c9251b6d8c14783f", + "sha256:9da2bd29ed9e4f15955dd1595ad7bc9320308a3b766ef7f837e23ad4b4aac31a", + "sha256:afaff6cf5200befd5cec055b07d1c0a5a06c040fe5ad148abcd11ba6ab9b114e", + "sha256:b1b5667cced97081bf57b8fa1d6bfca67814b0afd38208d52538316e9422fc61", + "sha256:b37eef18ea55f2ffd8f00ff8fe7c8d3818abd3e25fb73fae2ca3b672e333a7a6", + "sha256:b542be2440edc2d48547b5923c408cbe0fc94afb9f18741faa6ae970dbcb9b6d", + "sha256:b7dcbe92cc99f08c8dd11f930de4d99ef756c3591a5377d1d9cd7dd5e896da71", + "sha256:b7f009caad047246ed379e1c4dbcb8b020f0a390667ea74d2387be2998f58a22", + "sha256:bba5387a6975598857d86de9eac14210a49d554a77eb8261cc68b7d082f78ce2", + "sha256:c5e1536de2aad7bf62e27baf79225d0d64360d4168cf2e6becb91baf1ed074f3", + "sha256:c5ee858cfe08f34712f548c3c363e807e7186f03ad7a5039ebadb29e8c6be067", + "sha256:c9db1c18f0eaad2f804728c67d6c610778456e3e1cc4ab4bbd5eeb8e6053c6fc", + "sha256:d353cadd6083fdb056bb46ed07e4340b0869c305c8ca54ef9da3421acbdf6881", + "sha256:d46677c85c5ba00a9cb6f7a00b2bfa6f812192d2c9f7d9c4f6a55b60216712f3", + "sha256:d4d1ac74f5c0c0524e4a24335350edad7e5f03b9532da7ea4d3c54d527784f2e", + "sha256:d73a9fe764d77f87f8ec26a0c85144d6a951a6c438dfe50487df5595c6373eac", + "sha256:da70d4d51c8b306bb7a031d5cff6cc25ad253affe89b70352af5f1cb68e74b53", + "sha256:daf3cb43b7cf2ba96d614252ce1684c1bccee6b2183a01328c98d36fcd7d5cb0", + "sha256:dca1e2f3ca00b84a396bc1bce13dd21f680f035314d2379c4160c98153b2059b", + "sha256:dd4f49ae60e10adbc94b45c0b5e6a179acc1736cf7a90160b404076ee283cf83", + "sha256:e1f145462f1fa6e4a4ae3c0f782e580ce44d57c8f2c7aae1b6fa88c0b2efdb41", + "sha256:e3391d1e16e2a5a1507d83e4a8b100f4ee626e8eca43cf2cadb543de69827c4c", + "sha256:fcd2469d6a2cf298f198f0487e0a5b1a47a42ca0fa4dfd1b6862c999f018ebbf", + "sha256:fd096eb7ffef17c456cfa587523c5f92321ae02427ff955bebe9e3c63bc9f0da", + "sha256:fe754d231288e1e64323cfad462fcee8f0288654c10bdf4f603a39ed923bef33" + ], + "index": "pypi", + "markers": "python_version >= '3.7'", + "version": "==3.0.3" + }, "h11": { "hashes": [ "sha256:8f19fbbe99e72420ff35c00b27a34cb9937e902a8b810e2c88300c6f0a3b699d", @@ -2232,19 +2142,19 @@ }, "ipykernel": { "hashes": [ - "sha256:7d5d594b6690654b4d299edba5e872dc17bb7396a8d0609c97cb7b8a1c605de6", - "sha256:dab88b47f112f9f7df62236511023c9bdeef67abc73af7c652e4ce4441601686" + "sha256:69c11403d26de69df02225916f916b37ea4b9af417da0a8c827f84328d88e5f3", + "sha256:c6e9a9c63a7f4095c0a22a79f765f079f9ec7be4f2430a898ddea889e8665661" ], "markers": "python_version >= '3.8'", - "version": "==6.27.1" + "version": "==6.28.0" }, "ipython": { "hashes": [ - "sha256:ca6f079bb33457c66e233e4580ebfc4128855b4cf6370dddd73842a9563e8a27", - "sha256:e8267419d72d81955ec1177f8a29aaa90ac80ad647499201119e2f05e99aa397" + "sha256:2f55d59370f59d0d2b2212109fe0e6035cfea436b1c0e6150ad2244746272ec5", + "sha256:ac4da4ecf0042fb4e0ce57c60430c2db3c719fa8bdf92f8631d6bd8a5785d1f0" ], - "markers": "python_version >= '3.9'", - "version": "==8.18.1" + "markers": "python_version >= '3.10'", + "version": "==8.19.0" }, "isoduration": { "hashes": [ @@ -2303,11 +2213,11 @@ }, "jsonschema-specifications": { "hashes": [ - "sha256:9472fc4fea474cd74bea4a2b190daeccb5a9e4db2ea80efcf7a1b582fc9a81b8", - "sha256:e74ba7c0a65e8cb49dc26837d6cfe576557084a8b423ed16a420984228104f93" + "sha256:48a76787b3e70f5ed53f1160d2b81f586e4ca6d1548c5de7085d1682674764cc", + "sha256:87e4fdf3a94858b8a2ba2778d9ba57d8a9cafca7c7489c46ba0d30a8bc6a9c3c" ], "markers": "python_version >= '3.8'", - "version": "==2023.11.2" + "version": "==2023.12.1" }, "jupyter-client": { "hashes": [ @@ -2319,11 +2229,11 @@ }, "jupyter-core": { "hashes": [ - "sha256:1553311a97ccd12936037f36b9ab4d6ae8ceea6ad2d5c90d94a909e752178e40", - "sha256:220dfb00c45f0d780ce132bb7976b58263f81a3ada6e90a9b6823785a424f739" + "sha256:3d16aec2e1ec84b69f7794e49c32830c1d950ad149526aec954c100047c5f3a7", + "sha256:5139be639404f7f80f3db6f687f47b8a8ec97286b4fa063c984024720e7224dc" ], "markers": "python_version >= '3.8'", - "version": "==5.5.1" + "version": "==5.6.1" }, "jupyter-events": { "hashes": [ @@ -2351,20 +2261,20 @@ }, "jupyter-server-terminals": { "hashes": [ - "sha256:2fc0692c883bfd891f4fba0c4b4a684a37234b0ba472f2e97ed0a3888f46e1e4", - "sha256:ebcd68c9afbf98a480a533e6f3266354336e645536953b7abcc7bdeebc0154a3" + "sha256:16d3be9cf48be6a1f943f3a6c93c033be259cf4779184c66421709cf63dccfea", + "sha256:5e63e947ddd97bb2832db5ef837a258d9ccd4192cd608c1270850ad947ae5dd7" ], "markers": "python_version >= '3.8'", - "version": "==0.5.0" + "version": "==0.5.1" }, "jupyterlab": { "hashes": [ - "sha256:9ebada41d52651f623c0c9f069ddb8a21d6848e4c887d8e5ddc0613166ed5c0b", - "sha256:9f6f8e36d543fdbcc3df961a1d6a3f524b4a4001be0327a398f68fa4e534107c" + "sha256:46177eb8ede70dc73be922ac99f8ef943bdc2dfbc6a31b353c4bde848a35dee1", + "sha256:fe010ad9e37017488b468632ef2ead255fc7c671c5b64d9ca13e1f7b7e665c37" ], "index": "pypi", "markers": "python_version >= '3.8'", - "version": "==4.0.9" + "version": "==4.0.10" }, "jupyterlab-pygments": { "hashes": [ @@ -2487,119 +2397,39 @@ "markers": "python_version >= '3.7'", "version": "==3.0.2" }, - "multidict": { - "hashes": [ - "sha256:01a3a55bd90018c9c080fbb0b9f4891db37d148a0a18722b42f94694f8b6d4c9", - "sha256:0b1a97283e0c85772d613878028fec909f003993e1007eafa715b24b377cb9b8", - "sha256:0dfad7a5a1e39c53ed00d2dd0c2e36aed4650936dc18fd9a1826a5ae1cad6f03", - "sha256:11bdf3f5e1518b24530b8241529d2050014c884cf18b6fc69c0c2b30ca248710", - "sha256:1502e24330eb681bdaa3eb70d6358e818e8e8f908a22a1851dfd4e15bc2f8161", - "sha256:16ab77bbeb596e14212e7bab8429f24c1579234a3a462105cda4a66904998664", - "sha256:16d232d4e5396c2efbbf4f6d4df89bfa905eb0d4dc5b3549d872ab898451f569", - "sha256:21a12c4eb6ddc9952c415f24eef97e3e55ba3af61f67c7bc388dcdec1404a067", - "sha256:27c523fbfbdfd19c6867af7346332b62b586eed663887392cff78d614f9ec313", - "sha256:281af09f488903fde97923c7744bb001a9b23b039a909460d0f14edc7bf59706", - "sha256:33029f5734336aa0d4c0384525da0387ef89148dc7191aae00ca5fb23d7aafc2", - "sha256:3601a3cece3819534b11d4efc1eb76047488fddd0c85a3948099d5da4d504636", - "sha256:3666906492efb76453c0e7b97f2cf459b0682e7402c0489a95484965dbc1da49", - "sha256:36c63aaa167f6c6b04ef2c85704e93af16c11d20de1d133e39de6a0e84582a93", - "sha256:39ff62e7d0f26c248b15e364517a72932a611a9b75f35b45be078d81bdb86603", - "sha256:43644e38f42e3af682690876cff722d301ac585c5b9e1eacc013b7a3f7b696a0", - "sha256:4372381634485bec7e46718edc71528024fcdc6f835baefe517b34a33c731d60", - "sha256:458f37be2d9e4c95e2d8866a851663cbc76e865b78395090786f6cd9b3bbf4f4", - "sha256:45e1ecb0379bfaab5eef059f50115b54571acfbe422a14f668fc8c27ba410e7e", - "sha256:4b9d9e4e2b37daddb5c23ea33a3417901fa7c7b3dee2d855f63ee67a0b21e5b1", - "sha256:4ceef517eca3e03c1cceb22030a3e39cb399ac86bff4e426d4fc6ae49052cc60", - "sha256:4d1a3d7ef5e96b1c9e92f973e43aa5e5b96c659c9bc3124acbbd81b0b9c8a951", - "sha256:4dcbb0906e38440fa3e325df2359ac6cb043df8e58c965bb45f4e406ecb162cc", - "sha256:509eac6cf09c794aa27bcacfd4d62c885cce62bef7b2c3e8b2e49d365b5003fe", - "sha256:52509b5be062d9eafc8170e53026fbc54cf3b32759a23d07fd935fb04fc22d95", - "sha256:52f2dffc8acaba9a2f27174c41c9e57f60b907bb9f096b36b1a1f3be71c6284d", - "sha256:574b7eae1ab267e5f8285f0fe881f17efe4b98c39a40858247720935b893bba8", - "sha256:5979b5632c3e3534e42ca6ff856bb24b2e3071b37861c2c727ce220d80eee9ed", - "sha256:59d43b61c59d82f2effb39a93c48b845efe23a3852d201ed2d24ba830d0b4cf2", - "sha256:5a4dcf02b908c3b8b17a45fb0f15b695bf117a67b76b7ad18b73cf8e92608775", - "sha256:5cad9430ab3e2e4fa4a2ef4450f548768400a2ac635841bc2a56a2052cdbeb87", - "sha256:5fc1b16f586f049820c5c5b17bb4ee7583092fa0d1c4e28b5239181ff9532e0c", - "sha256:62501642008a8b9871ddfccbf83e4222cf8ac0d5aeedf73da36153ef2ec222d2", - "sha256:64bdf1086b6043bf519869678f5f2757f473dee970d7abf6da91ec00acb9cb98", - "sha256:64da238a09d6039e3bd39bb3aee9c21a5e34f28bfa5aa22518581f910ff94af3", - "sha256:666daae833559deb2d609afa4490b85830ab0dfca811a98b70a205621a6109fe", - "sha256:67040058f37a2a51ed8ea8f6b0e6ee5bd78ca67f169ce6122f3e2ec80dfe9b78", - "sha256:6748717bb10339c4760c1e63da040f5f29f5ed6e59d76daee30305894069a660", - "sha256:6b181d8c23da913d4ff585afd1155a0e1194c0b50c54fcfe286f70cdaf2b7176", - "sha256:6ed5f161328b7df384d71b07317f4d8656434e34591f20552c7bcef27b0ab88e", - "sha256:7582a1d1030e15422262de9f58711774e02fa80df0d1578995c76214f6954988", - "sha256:7d18748f2d30f94f498e852c67d61261c643b349b9d2a581131725595c45ec6c", - "sha256:7d6ae9d593ef8641544d6263c7fa6408cc90370c8cb2bbb65f8d43e5b0351d9c", - "sha256:81a4f0b34bd92df3da93315c6a59034df95866014ac08535fc819f043bfd51f0", - "sha256:8316a77808c501004802f9beebde51c9f857054a0c871bd6da8280e718444449", - "sha256:853888594621e6604c978ce2a0444a1e6e70c8d253ab65ba11657659dcc9100f", - "sha256:99b76c052e9f1bc0721f7541e5e8c05db3941eb9ebe7b8553c625ef88d6eefde", - "sha256:a2e4369eb3d47d2034032a26c7a80fcb21a2cb22e1173d761a162f11e562caa5", - "sha256:ab55edc2e84460694295f401215f4a58597f8f7c9466faec545093045476327d", - "sha256:af048912e045a2dc732847d33821a9d84ba553f5c5f028adbd364dd4765092ac", - "sha256:b1a2eeedcead3a41694130495593a559a668f382eee0727352b9a41e1c45759a", - "sha256:b1e8b901e607795ec06c9e42530788c45ac21ef3aaa11dbd0c69de543bfb79a9", - "sha256:b41156839806aecb3641f3208c0dafd3ac7775b9c4c422d82ee2a45c34ba81ca", - "sha256:b692f419760c0e65d060959df05f2a531945af31fda0c8a3b3195d4efd06de11", - "sha256:bc779e9e6f7fda81b3f9aa58e3a6091d49ad528b11ed19f6621408806204ad35", - "sha256:bf6774e60d67a9efe02b3616fee22441d86fab4c6d335f9d2051d19d90a40063", - "sha256:c048099e4c9e9d615545e2001d3d8a4380bd403e1a0578734e0d31703d1b0c0b", - "sha256:c5cb09abb18c1ea940fb99360ea0396f34d46566f157122c92dfa069d3e0e982", - "sha256:cc8e1d0c705233c5dd0c5e6460fbad7827d5d36f310a0fadfd45cc3029762258", - "sha256:d5e3fc56f88cc98ef8139255cf8cd63eb2c586531e43310ff859d6bb3a6b51f1", - "sha256:d6aa0418fcc838522256761b3415822626f866758ee0bc6632c9486b179d0b52", - "sha256:d6c254ba6e45d8e72739281ebc46ea5eb5f101234f3ce171f0e9f5cc86991480", - "sha256:d6d635d5209b82a3492508cf5b365f3446afb65ae7ebd755e70e18f287b0adf7", - "sha256:dcfe792765fab89c365123c81046ad4103fcabbc4f56d1c1997e6715e8015461", - "sha256:ddd3915998d93fbcd2566ddf9cf62cdb35c9e093075f862935573d265cf8f65d", - "sha256:ddff9c4e225a63a5afab9dd15590432c22e8057e1a9a13d28ed128ecf047bbdc", - "sha256:e41b7e2b59679edfa309e8db64fdf22399eec4b0b24694e1b2104fb789207779", - "sha256:e69924bfcdda39b722ef4d9aa762b2dd38e4632b3641b1d9a57ca9cd18f2f83a", - "sha256:ea20853c6dbbb53ed34cb4d080382169b6f4554d394015f1bef35e881bf83547", - "sha256:ee2a1ece51b9b9e7752e742cfb661d2a29e7bcdba2d27e66e28a99f1890e4fa0", - "sha256:eeb6dcc05e911516ae3d1f207d4b0520d07f54484c49dfc294d6e7d63b734171", - "sha256:f70b98cd94886b49d91170ef23ec5c0e8ebb6f242d734ed7ed677b24d50c82cf", - "sha256:fc35cb4676846ef752816d5be2193a1e8367b4c1397b74a565a9d0389c433a1d", - "sha256:ff959bee35038c4624250473988b24f846cbeb2c6639de3602c073f10410ceba" - ], - "markers": "python_version >= '3.7'", - "version": "==6.0.4" - }, "mypy": { "hashes": [ - "sha256:12cce78e329838d70a204293e7b29af9faa3ab14899aec397798a4b41be7f340", - "sha256:1484b8fa2c10adf4474f016e09d7a159602f3239075c7bf9f1627f5acf40ad49", - "sha256:204e0d6de5fd2317394a4eff62065614c4892d5a4d1a7ee55b765d7a3d9e3f82", - "sha256:2643d145af5292ee956aa0a83c2ce1038a3bdb26e033dadeb2f7066fb0c9abce", - "sha256:2c6e4464ed5f01dc44dc9821caf67b60a4e5c3b04278286a85c067010653a0eb", - "sha256:2f7f6985d05a4e3ce8255396df363046c28bea790e40617654e91ed580ca7c51", - "sha256:31902408f4bf54108bbfb2e35369877c01c95adc6192958684473658c322c8a5", - "sha256:40716d1f821b89838589e5b3106ebbc23636ffdef5abc31f7cd0266db936067e", - "sha256:4b901927f16224d0d143b925ce9a4e6b3a758010673eeded9b748f250cf4e8f7", - "sha256:4fc3d14ee80cd22367caaaf6e014494415bf440980a3045bf5045b525680ac33", - "sha256:5cf3f0c5ac72139797953bd50bc6c95ac13075e62dbfcc923571180bebb662e9", - "sha256:6dbdec441c60699288adf051f51a5d512b0d818526d1dcfff5a41f8cd8b4aaf1", - "sha256:72cf32ce7dd3562373f78bd751f73c96cfb441de147cc2448a92c1a308bd0ca6", - "sha256:75aa828610b67462ffe3057d4d8a4112105ed211596b750b53cbfe182f44777a", - "sha256:75c4d2a6effd015786c87774e04331b6da863fc3fc4e8adfc3b40aa55ab516fe", - "sha256:78e25b2fd6cbb55ddfb8058417df193f0129cad5f4ee75d1502248e588d9e0d7", - "sha256:84860e06ba363d9c0eeabd45ac0fde4b903ad7aa4f93cd8b648385a888e23200", - "sha256:8c5091ebd294f7628eb25ea554852a52058ac81472c921150e3a61cdd68f75a7", - "sha256:944bdc21ebd620eafefc090cdf83158393ec2b1391578359776c00de00e8907a", - "sha256:9c7ac372232c928fff0645d85f273a726970c014749b924ce5710d7d89763a28", - "sha256:d9b338c19fa2412f76e17525c1b4f2c687a55b156320acb588df79f2e6fa9fea", - "sha256:ee5d62d28b854eb61889cde4e1dbc10fbaa5560cb39780c3995f6737f7e82120", - "sha256:f2c2521a8e4d6d769e3234350ba7b65ff5d527137cdcde13ff4d99114b0c8e7d", - "sha256:f6efc9bd72258f89a3816e3a98c09d36f079c223aa345c659622f056b760ab42", - "sha256:f7c5d642db47376a0cc130f0de6d055056e010debdaf0707cd2b0fc7e7ef30ea", - "sha256:fcb6d9afb1b6208b4c712af0dafdc650f518836065df0d4fb1d800f5d6773db2", - "sha256:fcd2572dd4519e8a6642b733cd3a8cfc1ef94bafd0c1ceed9c94fe736cb65b6a" + "sha256:028cf9f2cae89e202d7b6593cd98db6759379f17a319b5faf4f9978d7084cdc6", + "sha256:2afecd6354bbfb6e0160f4e4ad9ba6e4e003b767dd80d85516e71f2e955ab50d", + "sha256:2b5b6c721bd4aabaadead3a5e6fa85c11c6c795e0c81a7215776ef8afc66de02", + "sha256:42419861b43e6962a649068a61f4a4839205a3ef525b858377a960b9e2de6e0d", + "sha256:42c6680d256ab35637ef88891c6bd02514ccb7e1122133ac96055ff458f93fc3", + "sha256:485a8942f671120f76afffff70f259e1cd0f0cfe08f81c05d8816d958d4577d3", + "sha256:4c886c6cce2d070bd7df4ec4a05a13ee20c0aa60cb587e8d1265b6c03cf91da3", + "sha256:4e6d97288757e1ddba10dd9549ac27982e3e74a49d8d0179fc14d4365c7add66", + "sha256:4ef4be7baf08a203170f29e89d79064463b7fc7a0908b9d0d5114e8009c3a259", + "sha256:51720c776d148bad2372ca21ca29256ed483aa9a4cdefefcef49006dff2a6835", + "sha256:52825b01f5c4c1c4eb0db253ec09c7aa17e1a7304d247c48b6f3599ef40db8bd", + "sha256:538fd81bb5e430cc1381a443971c0475582ff9f434c16cd46d2c66763ce85d9d", + "sha256:5c1538c38584029352878a0466f03a8ee7547d7bd9f641f57a0f3017a7c905b8", + "sha256:6ff8b244d7085a0b425b56d327b480c3b29cafbd2eff27316a004f9a7391ae07", + "sha256:7178def594014aa6c35a8ff411cf37d682f428b3b5617ca79029d8ae72f5402b", + "sha256:720a5ca70e136b675af3af63db533c1c8c9181314d207568bbe79051f122669e", + "sha256:7f1478736fcebb90f97e40aff11a5f253af890c845ee0c850fe80aa060a267c6", + "sha256:855fe27b80375e5c5878492f0729540db47b186509c98dae341254c8f45f42ae", + "sha256:8963b83d53ee733a6e4196954502b33567ad07dfd74851f32be18eb932fb1cb9", + "sha256:9261ed810972061388918c83c3f5cd46079d875026ba97380f3e3978a72f503d", + "sha256:99b00bc72855812a60d253420d8a2eae839b0afa4938f09f4d2aa9bb4654263a", + "sha256:ab3c84fa13c04aeeeabb2a7f67a25ef5d77ac9d6486ff33ded762ef353aa5592", + "sha256:afe3fe972c645b4632c563d3f3eff1cdca2fa058f730df2b93a35e3b0c538218", + "sha256:d19c413b3c07cbecf1f991e2221746b0d2a9410b59cb3f4fb9557f0365a1a817", + "sha256:df9824ac11deaf007443e7ed2a4a26bebff98d2bc43c6da21b2b64185da011c4", + "sha256:e46f44b54ebddbeedbd3d5b289a893219065ef805d95094d16a0af6630f5d410", + "sha256:f5ac9a4eeb1ec0f1ccdc6f326bcdb464de5f80eb07fb38b5ddd7b0de6bc61e55" ], "index": "pypi", "markers": "python_version >= '3.8'", - "version": "==1.7.1" + "version": "==1.8.0" }, "mypy-extensions": { "hashes": [ @@ -2619,11 +2449,11 @@ }, "nbconvert": { "hashes": [ - "sha256:22521cfcc10ba5755e44acb6a70d2bd8a891ce7aed6746481e10cd548b169e19", - "sha256:c6f61c86fca5b28bd17f4f9a308248e59fa2b54919e1589f6cc3575c5dfec2bd" + "sha256:483dde47facdaa4875903d651305ad53cd76e2255ae3c61efe412a95f2d22a24", + "sha256:92b9a44b63e5a7fb4f6fa0ef41261e35c16925046ccd1c04a5c8099bf100476e" ], "markers": "python_version >= '3.8'", - "version": "==7.13.0" + "version": "==7.14.0" }, "nbformat": { "hashes": [ @@ -2823,12 +2653,12 @@ }, "pytest": { "hashes": [ - "sha256:0d009c083ea859a71b76adf7c1d502e4bc170b80a8ef002da5806527b9591fac", - "sha256:d989d136982de4e3b29dabcc838ad581c64e8ed52c11fbe86ddebd9da0818cd5" + "sha256:2cf0005922c6ace4a3e2ec8b4080eb0d9753fdc93107415332f50ce9e7994280", + "sha256:b090cdf5ed60bf4c45261be03239c2c1c22df034fbffe691abe93cd80cea01d8" ], "index": "pypi", "markers": "python_version >= '3.7'", - "version": "==7.4.3" + "version": "==7.4.4" }, "pytest-anyio": { "hashes": [ @@ -2856,12 +2686,12 @@ }, "pytest-httpx": { "hashes": [ - "sha256:24f6f53d507ab483bea8f89b975a1a111fb613ccab4d86e570be8991776e8bcc", - "sha256:a33c4e8df415cc1232b3664869b6a8b8061c4c223335aca0b237cefbc01ba0eb" + "sha256:045774556a3633688742315a6981aab2806ce93bcbcc8444253ab87bca286800", + "sha256:a82505fdf59f19eaaf2853db3f3832b3dee35d3bc58000232db2b65c5fca0614" ], "index": "pypi", "markers": "python_version >= '3.9'", - "version": "==0.27.0" + "version": "==0.28.0" }, "pytest-mock": { "hashes": [ @@ -3077,7 +2907,6 @@ "sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f", "sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1" ], - "index": "pypi", "markers": "python_version >= '3.7'", "version": "==2.31.0" }, @@ -3099,108 +2928,108 @@ }, "rpds-py": { "hashes": [ - "sha256:02744236ac1895d7be837878e707a5c35fb8edc5137602f253b63623d7ad5c8c", - "sha256:03f9c5875515820633bd7709a25c3e60c1ea9ad1c5d4030ce8a8c203309c36fd", - "sha256:044f6f46d62444800402851afa3c3ae50141f12013060c1a3a0677e013310d6d", - "sha256:07a2e1d78d382f7181789713cdf0c16edbad4fe14fe1d115526cb6f0eef0daa3", - "sha256:082e0e55d73690ffb4da4352d1b5bbe1b5c6034eb9dc8c91aa2a3ee15f70d3e2", - "sha256:13152dfe7d7c27c40df8b99ac6aab12b978b546716e99f67e8a67a1d441acbc3", - "sha256:13716e53627ad97babf72ac9e01cf9a7d4af2f75dd5ed7b323a7a9520e948282", - "sha256:13ff62d3561a23c17341b4afc78e8fcfd799ab67c0b1ca32091d71383a98ba4b", - "sha256:1607cda6129f815493a3c184492acb5ae4aa6ed61d3a1b3663aa9824ed26f7ac", - "sha256:164fcee32f15d04d61568c9cb0d919e37ff3195919cd604039ff3053ada0461b", - "sha256:1c24e30d720c0009b6fb2e1905b025da56103c70a8b31b99138e4ed1c2a6c5b0", - "sha256:1e6fcd0a0f62f2997107f758bb372397b8d5fd5f39cc6dcb86f7cb98a2172d6c", - "sha256:1fd0f0b1ccd7d537b858a56355a250108df692102e08aa2036e1a094fd78b2dc", - "sha256:2181e86d4e1cdf49a7320cb72a36c45efcb7670d0a88f09fd2d3a7967c0540fd", - "sha256:2974e6dff38afafd5ccf8f41cb8fc94600b3f4fd9b0a98f6ece6e2219e3158d5", - "sha256:2dccc623725d0b298f557d869a68496a2fd2a9e9c41107f234fa5f7a37d278ac", - "sha256:2df3d07a16a3bef0917b28cd564778fbb31f3ffa5b5e33584470e2d1b0f248f0", - "sha256:2e7e5633577b3bd56bf3af2ef6ae3778bbafb83743989d57f0e7edbf6c0980e4", - "sha256:2ee066a64f0d2ba45391cac15b3a70dcb549e968a117bd0500634754cfe0e5fc", - "sha256:2f1f295a5c28cfa74a7d48c95acc1c8a7acd49d7d9072040d4b694fe11cd7166", - "sha256:2faa97212b0dc465afeedf49045cdd077f97be1188285e646a9f689cb5dfff9e", - "sha256:30479a9f1fce47df56b07460b520f49fa2115ec2926d3b1303c85c81f8401ed1", - "sha256:337a8653fb11d2fbe7157c961cc78cb3c161d98cf44410ace9a3dc2db4fad882", - "sha256:3423007fc0661827e06f8a185a3792c73dda41f30f3421562f210cf0c9e49569", - "sha256:373b76eeb79e8c14f6d82cb1d4d5293f9e4059baec6c1b16dca7ad13b6131b39", - "sha256:3b79c63d29101cbaa53a517683557bb550462394fb91044cc5998dd2acff7340", - "sha256:3bbc89ce2a219662ea142f0abcf8d43f04a41d5b1880be17a794c39f0d609cb0", - "sha256:3c11bc5814554b018f6c5d6ae0969e43766f81e995000b53a5d8c8057055e886", - "sha256:3cd61e759c4075510052d1eca5cddbd297fe1164efec14ef1fce3f09b974dfe4", - "sha256:3d40fb3ca22e3d40f494d577441b263026a3bd8c97ae6ce89b2d3c4b39ac9581", - "sha256:3db0c998c92b909d7c90b66c965590d4f3cd86157176a6cf14aa1f867b77b889", - "sha256:422b0901878a31ef167435c5ad46560362891816a76cc0d150683f3868a6f0d1", - "sha256:46b4f3d47d1033db569173be62365fbf7808c2bd3fb742314d251f130d90d44c", - "sha256:485fbdd23becb822804ed05622907ee5c8e8a5f43f6f43894a45f463b2217045", - "sha256:53304cc14b1d94487d70086e1cb0cb4c29ec6da994d58ae84a4d7e78c6a6d04d", - "sha256:5595c80dd03d7e6c6afb73f3594bf3379a7d79fa57164b591d012d4b71d6ac4c", - "sha256:56b51ba29a18e5f5810224bcf00747ad931c0716e3c09a76b4a1edd3d4aba71f", - "sha256:580182fa5b269c2981e9ce9764367cb4edc81982ce289208d4607c203f44ffde", - "sha256:5e99d6510c8557510c220b865d966b105464740dcbebf9b79ecd4fbab30a13d9", - "sha256:5eb05b654a41e0f81ab27a7c3e88b6590425eb3e934e1d533ecec5dc88a6ffff", - "sha256:62b292fff4739c6be89e6a0240c02bda5a9066a339d90ab191cf66e9fdbdc193", - "sha256:6a5122b17a4faf5d7a6d91fa67b479736c0cacc7afe791ddebb7163a8550b799", - "sha256:6a8ff8e809da81363bffca2b965cb6e4bf6056b495fc3f078467d1f8266fe27f", - "sha256:6c43e1b89099279cc03eb1c725c5de12af6edcd2f78e2f8a022569efa639ada3", - "sha256:709dc11af2f74ba89c68b1592368c6edcbccdb0a06ba77eb28c8fe08bb6997da", - "sha256:7e072f5da38d6428ba1fc1115d3cc0dae895df671cb04c70c019985e8c7606be", - "sha256:813a65f95bfcb7c8f2a70dd6add9b51e9accc3bdb3e03d0ff7a9e6a2d3e174bf", - "sha256:86c01299942b0f4b5b5f28c8701689181ad2eab852e65417172dbdd6c5b3ccc8", - "sha256:893e38d0f4319dfa70c0f36381a37cc418985c87b11d9784365b1fff4fa6973b", - "sha256:8a5f574b92b3ee7d254e56d56e37ec0e1416acb1ae357c4956d76a1788dc58fb", - "sha256:8b9650f92251fdef843e74fc252cdfd6e3c700157ad686eeb0c6d7fdb2d11652", - "sha256:8ec464f20fe803ae00419bd1610934e3bda963aeba1e6181dfc9033dc7e8940c", - "sha256:8f333bfe782a2d05a67cfaa0cc9cd68b36b39ee6acfe099f980541ed973a7093", - "sha256:8ffdeb7dbd0160d4e391e1f857477e4762d00aa2199c294eb95dfb9451aa1d9f", - "sha256:911e600e798374c0d86235e7ef19109cf865d1336942d398ff313375a25a93ba", - "sha256:9235be95662559141934fced8197de6fee8c58870f36756b0584424b6d708393", - "sha256:938518a11780b39998179d07f31a4a468888123f9b00463842cd40f98191f4d3", - "sha256:93c18a1696a8e0388ed84b024fe1a188a26ba999b61d1d9a371318cb89885a8c", - "sha256:97532802f14d383f37d603a56e226909f825a83ff298dc1b6697de00d2243999", - "sha256:98ee201a52a7f65608e5494518932e1473fd43535f12cade0a1b4ab32737fe28", - "sha256:9d2ae79f31da5143e020a8d4fc74e1f0cbcb8011bdf97453c140aa616db51406", - "sha256:9d38494a8d21c246c535b41ecdb2d562c4b933cf3d68de03e8bc43a0d41be652", - "sha256:9d41ebb471a6f064c0d1c873c4f7dded733d16ca5db7d551fb04ff3805d87802", - "sha256:9e09d017e3f4d9bd7d17a30d3f59e4d6d9ba2d2ced280eec2425e84112cf623f", - "sha256:a6945c2d61c42bb7e818677f43638675b8c1c43e858b67a96df3eb2426a86c9d", - "sha256:a72e00826a2b032dda3eb25aa3e3579c6d6773d22d8446089a57a123481cc46c", - "sha256:aa1e626c524d2c7972c0f3a8a575d654a3a9c008370dc2a97e46abd0eaa749b9", - "sha256:ab095edf1d840a6a6a4307e1a5b907a299a94e7b90e75436ee770b8c35d22a25", - "sha256:ac2ac84a4950d627d84b61f082eba61314373cfab4b3c264b62efab02ababe83", - "sha256:ac7187bee72384b9cfedf09a29a3b2b6e8815cc64c095cdc8b5e6aec81e9fd5f", - "sha256:ae9d83a81b09ce3a817e2cbb23aabc07f86a3abc664c613cd283ce7a03541e95", - "sha256:afeabb382c1256a7477b739820bce7fe782bb807d82927102cee73e79b41b38b", - "sha256:b2a4cd924d0e2f4b1a68034abe4cadc73d69ad5f4cf02db6481c0d4d749f548f", - "sha256:b414ef79f1f06fb90b5165db8aef77512c1a5e3ed1b4807da8476b7e2c853283", - "sha256:b4ecbba7efd82bd2a4bb88aab7f984eb5470991c1347bdd1f35fb34ea28dba6e", - "sha256:b61d5096e75fd71018b25da50b82dd70ec39b5e15bb2134daf7eb7bbbc103644", - "sha256:b629db53fe17e6ce478a969d30bd1d0e8b53238c46e3a9c9db39e8b65a9ef973", - "sha256:b70b45a40ad0798b69748b34d508259ef2bdc84fb2aad4048bc7c9cafb68ddb3", - "sha256:b88c3ab98556bc351b36d6208a6089de8c8db14a7f6e1f57f82a334bd2c18f0b", - "sha256:baf744e5f9d5ee6531deea443be78b36ed1cd36c65a0b95ea4e8d69fa0102268", - "sha256:bbc7421cbd28b4316d1d017db338039a7943f945c6f2bb15e1439b14b5682d28", - "sha256:c31272c674f725dfe0f343d73b0abe8c878c646967ec1c6106122faae1efc15b", - "sha256:c51a899792ee2c696072791e56b2020caff58b275abecbc9ae0cb71af0645c95", - "sha256:c61e42b4ceb9759727045765e87d51c1bb9f89987aca1fcc8a040232138cad1c", - "sha256:c7cd0841a586b7105513a7c8c3d5c276f3adc762a072d81ef7fae80632afad1e", - "sha256:c827a931c6b57f50f1bb5de400dcfb00bad8117e3753e80b96adb72d9d811514", - "sha256:d2aa3ca9552f83b0b4fa6ca8c6ce08da6580f37e3e0ab7afac73a1cfdc230c0e", - "sha256:d46ee458452727a147d7897bb33886981ae1235775e05decae5d5d07f537695a", - "sha256:d64a657de7aae8db2da60dc0c9e4638a0c3893b4d60101fd564a3362b2bfeb34", - "sha256:d800a8e2ac62db1b9ea5d6d1724f1a93c53907ca061de4d05ed94e8dfa79050c", - "sha256:d9d7ebcd11ea76ba0feaae98485cd8e31467c3d7985210fab46983278214736b", - "sha256:dd7d3608589072f63078b4063a6c536af832e76b0b3885f1bfe9e892abe6c207", - "sha256:ec19e823b4ccd87bd69e990879acbce9e961fc7aebe150156b8f4418d4b27b7f", - "sha256:ee40206d1d6e95eaa2b7b919195e3689a5cf6ded730632de7f187f35a1b6052c", - "sha256:f138f550b83554f5b344d6be35d3ed59348510edc3cb96f75309db6e9bfe8210", - "sha256:f3e6e2e502c4043c52a99316d89dc49f416acda5b0c6886e0dd8ea7bb35859e8", - "sha256:fb10bb720348fe1647a94eb605accb9ef6a9b1875d8845f9e763d9d71a706387", - "sha256:fc066395e6332da1e7525d605b4c96055669f8336600bef8ac569d5226a7c76f", - "sha256:fc33267d58dfbb2361baed52668c5d8c15d24bc0372cecbb79fed77339b55e0d" - ], - "markers": "python_version >= '3.8'", - "version": "==0.15.2" + "sha256:0474df4ade9a3b4af96c3d36eb81856cb9462e4c6657d4caecfd840d2a13f3c9", + "sha256:071980663c273bf3d388fe5c794c547e6f35ba3335477072c713a3176bf14a60", + "sha256:07aab64e2808c3ebac2a44f67e9dc0543812b715126dfd6fe4264df527556cb6", + "sha256:088396c7c70e59872f67462fcac3ecbded5233385797021976a09ebd55961dfe", + "sha256:162d7cd9cd311c1b0ff1c55a024b8f38bd8aad1876b648821da08adc40e95734", + "sha256:19f00f57fdd38db4bb5ad09f9ead1b535332dbf624200e9029a45f1f35527ebb", + "sha256:1bdbc5fcb04a7309074de6b67fa9bc4b418ab3fc435fec1f2779a0eced688d04", + "sha256:1be2f033df1b8be8c3167ba3c29d5dca425592ee31e35eac52050623afba5772", + "sha256:24f7a2eb3866a9e91f4599851e0c8d39878a470044875c49bd528d2b9b88361c", + "sha256:290a81cfbe4673285cdf140ec5cd1658ffbf63ab359f2b352ebe172e7cfa5bf0", + "sha256:2946b120718eba9af2b4dd103affc1164a87b9e9ebff8c3e4c05d7b7a7e274e2", + "sha256:2bd82db36cd70b3628c0c57d81d2438e8dd4b7b32a6a9f25f24ab0e657cb6c4e", + "sha256:2ddef620e70eaffebed5932ce754d539c0930f676aae6212f8e16cd9743dd365", + "sha256:2e53b9b25cac9065328901713a7e9e3b12e4f57ef4280b370fbbf6fef2052eef", + "sha256:302bd4983bbd47063e452c38be66153760112f6d3635c7eeefc094299fa400a9", + "sha256:349cb40897fd529ca15317c22c0eab67f5ac5178b5bd2c6adc86172045210acc", + "sha256:358dafc89ce3894c7f486c615ba914609f38277ef67f566abc4c854d23b997fa", + "sha256:35953f4f2b3216421af86fd236b7c0c65935936a94ea83ddbd4904ba60757773", + "sha256:35ae5ece284cf36464eb160880018cf6088a9ac5ddc72292a6092b6ef3f4da53", + "sha256:3b811d182ad17ea294f2ec63c0621e7be92a1141e1012383461872cead87468f", + "sha256:3da5a4c56953bdbf6d04447c3410309616c54433146ccdb4a277b9cb499bc10e", + "sha256:3dc6a7620ba7639a3db6213da61312cb4aa9ac0ca6e00dc1cbbdc21c2aa6eb57", + "sha256:3f91df8e6dbb7360e176d1affd5fb0246d2b88d16aa5ebc7db94fd66b68b61da", + "sha256:4022b9dc620e14f30201a8a73898a873c8e910cb642bcd2f3411123bc527f6ac", + "sha256:413b9c17388bbd0d87a329d8e30c1a4c6e44e2bb25457f43725a8e6fe4161e9e", + "sha256:43d4dd5fb16eb3825742bad8339d454054261ab59fed2fbac84e1d84d5aae7ba", + "sha256:44627b6ca7308680a70766454db5249105fa6344853af6762eaad4158a2feebe", + "sha256:44a54e99a2b9693a37ebf245937fd6e9228b4cbd64b9cc961e1f3391ec6c7391", + "sha256:47713dc4fce213f5c74ca8a1f6a59b622fc1b90868deb8e8e4d993e421b4b39d", + "sha256:495a14b72bbe217f2695dcd9b5ab14d4f8066a00f5d209ed94f0aca307f85f6e", + "sha256:4c46ad6356e1561f2a54f08367d1d2e70a0a1bb2db2282d2c1972c1d38eafc3b", + "sha256:4d6a9f052e72d493efd92a77f861e45bab2f6be63e37fa8ecf0c6fd1a58fedb0", + "sha256:509b617ac787cd1149600e731db9274ebbef094503ca25158e6f23edaba1ca8f", + "sha256:5552f328eaef1a75ff129d4d0c437bf44e43f9436d3996e8eab623ea0f5fcf73", + "sha256:5a80e2f83391ad0808b4646732af2a7b67550b98f0cae056cb3b40622a83dbb3", + "sha256:5cf6af100ffb5c195beec11ffaa8cf8523057f123afa2944e6571d54da84cdc9", + "sha256:5e6caa3809e50690bd92fa490f5c38caa86082c8c3315aa438bce43786d5e90d", + "sha256:5ef00873303d678aaf8b0627e111fd434925ca01c657dbb2641410f1cdaef261", + "sha256:69ac7ea9897ec201ce68b48582f3eb34a3f9924488a5432a93f177bf76a82a7e", + "sha256:6a61226465bda9283686db8f17d02569a98e4b13c637be5a26d44aa1f1e361c2", + "sha256:6d904c5693e08bad240f16d79305edba78276be87061c872a4a15e2c301fa2c0", + "sha256:6dace7b26a13353e24613417ce2239491b40a6ad44e5776a18eaff7733488b44", + "sha256:6df15846ee3fb2e6397fe25d7ca6624af9f89587f3f259d177b556fed6bebe2c", + "sha256:703d95c75a72e902544fda08e965885525e297578317989fd15a6ce58414b41d", + "sha256:726ac36e8a3bb8daef2fd482534cabc5e17334052447008405daca7ca04a3108", + "sha256:781ef8bfc091b19960fc0142a23aedadafa826bc32b433fdfe6fd7f964d7ef44", + "sha256:80443fe2f7b3ea3934c5d75fb0e04a5dbb4a8e943e5ff2de0dec059202b70a8b", + "sha256:83640a5d7cd3bff694747d50436b8b541b5b9b9782b0c8c1688931d6ee1a1f2d", + "sha256:84c5a4d1f9dd7e2d2c44097fb09fffe728629bad31eb56caf97719e55575aa82", + "sha256:882ce6e25e585949c3d9f9abd29202367175e0aab3aba0c58c9abbb37d4982ff", + "sha256:888a97002e986eca10d8546e3c8b97da1d47ad8b69726dcfeb3e56348ebb28a3", + "sha256:8aad80645a011abae487d356e0ceb359f4938dfb6f7bcc410027ed7ae4f7bb8b", + "sha256:8cb6fe8ecdfffa0e711a75c931fb39f4ba382b4b3ccedeca43f18693864fe850", + "sha256:8d6b6937ae9eac6d6c0ca3c42774d89fa311f55adff3970fb364b34abde6ed3d", + "sha256:90123853fc8b1747f80b0d354be3d122b4365a93e50fc3aacc9fb4c2488845d6", + "sha256:96f957d6ab25a78b9e7fc9749d754b98eac825a112b4e666525ce89afcbd9ed5", + "sha256:981d135c7cdaf6cd8eadae1c950de43b976de8f09d8e800feed307140d3d6d00", + "sha256:9b32f742ce5b57201305f19c2ef7a184b52f6f9ba6871cc042c2a61f0d6b49b8", + "sha256:9f0350ef2fba5f34eb0c9000ea328e51b9572b403d2f7f3b19f24085f6f598e8", + "sha256:a297a4d08cc67c7466c873c78039d87840fb50d05473db0ec1b7b03d179bf322", + "sha256:a3d7e2ea25d3517c6d7e5a1cc3702cffa6bd18d9ef8d08d9af6717fc1c700eed", + "sha256:a4b682c5775d6a3d21e314c10124599976809455ee67020e8e72df1769b87bc3", + "sha256:a4ebb8b20bd09c5ce7884c8f0388801100f5e75e7f733b1b6613c713371feefc", + "sha256:a61f659665a39a4d17d699ab3593d7116d66e1e2e3f03ef3fb8f484e91908808", + "sha256:a9880b4656efe36ccad41edc66789e191e5ee19a1ea8811e0aed6f69851a82f4", + "sha256:ac08472f41ea77cd6a5dae36ae7d4ed3951d6602833af87532b556c1b4601d63", + "sha256:adc0c3d6fc6ae35fee3e4917628983f6ce630d513cbaad575b4517d47e81b4bb", + "sha256:af27423662f32d7501a00c5e7342f7dbd1e4a718aea7a239781357d15d437133", + "sha256:b2e75e17bd0bb66ee34a707da677e47c14ee51ccef78ed6a263a4cc965a072a1", + "sha256:b634c5ec0103c5cbebc24ebac4872b045cccb9456fc59efdcf6fe39775365bd2", + "sha256:b6f5549d6ed1da9bfe3631ca9483ae906f21410be2445b73443fa9f017601c6f", + "sha256:bd4b677d929cf1f6bac07ad76e0f2d5de367e6373351c01a9c0a39f6b21b4a8b", + "sha256:bf721ede3eb7b829e4a9b8142bd55db0bdc82902720548a703f7e601ee13bdc3", + "sha256:c647ca87fc0ebe808a41de912e9a1bfef9acb85257e5d63691364ac16b81c1f0", + "sha256:ca57468da2d9a660bcf8961637c85f2fbb2aa64d9bc3f9484e30c3f9f67b1dd7", + "sha256:cad0f59ee3dc35526039f4bc23642d52d5f6616b5f687d846bfc6d0d6d486db0", + "sha256:cc97f0640e91d7776530f06e6836c546c1c752a52de158720c4224c9e8053cad", + "sha256:ccd4e400309e1f34a5095bf9249d371f0fd60f8a3a5c4a791cad7b99ce1fd38d", + "sha256:cffa76b385dfe1e38527662a302b19ffb0e7f5cf7dd5e89186d2c94a22dd9d0c", + "sha256:d0dd7ed2f16df2e129496e7fbe59a34bc2d7fc8db443a606644d069eb69cbd45", + "sha256:d452817e0d9c749c431a1121d56a777bd7099b720b3d1c820f1725cb40928f58", + "sha256:d8dda2a806dfa4a9b795950c4f5cc56d6d6159f7d68080aedaff3bdc9b5032f5", + "sha256:dcbe1f8dd179e4d69b70b1f1d9bb6fd1e7e1bdc9c9aad345cdeb332e29d40748", + "sha256:e0441fb4fdd39a230477b2ca9be90868af64425bfe7b122b57e61e45737a653b", + "sha256:e04e56b4ca7a770593633556e8e9e46579d66ec2ada846b401252a2bdcf70a6d", + "sha256:e061de3b745fe611e23cd7318aec2c8b0e4153939c25c9202a5811ca911fd733", + "sha256:e93ec1b300acf89730cf27975ef574396bc04edecc358e9bd116fb387a123239", + "sha256:e9e557db6a177470316c82f023e5d571811c9a4422b5ea084c85da9aa3c035fc", + "sha256:eab36eae3f3e8e24b05748ec9acc66286662f5d25c52ad70cadab544e034536b", + "sha256:ec23fcad480e77ede06cf4127a25fc440f7489922e17fc058f426b5256ee0edb", + "sha256:ec2e1cf025b2c0f48ec17ff3e642661da7ee332d326f2e6619366ce8e221f018", + "sha256:ed99b4f7179d2111702020fd7d156e88acd533f5a7d3971353e568b6051d5c97", + "sha256:ee94cb58c0ba2c62ee108c2b7c9131b2c66a29e82746e8fa3aa1a1effbd3dcf1", + "sha256:f19afcfc0dd0dca35694df441e9b0f95bc231b512f51bded3c3d8ca32153ec19", + "sha256:f1b9d9260e06ea017feb7172976ab261e011c1dc2f8883c7c274f6b2aabfe01a", + "sha256:f28ac0e8e7242d140f99402a903a2c596ab71550272ae9247ad78f9a932b5698", + "sha256:f42e25c016927e2a6b1ce748112c3ab134261fc2ddc867e92d02006103e1b1b7", + "sha256:f4bd4578e44f26997e9e56c96dedc5f1af43cc9d16c4daa29c771a00b2a26851", + "sha256:f811771019f063bbd0aa7bb72c8a934bc13ebacb4672d712fc1639cfd314cccc" + ], + "markers": "python_version >= '3.8'", + "version": "==0.16.2" }, "send2trash": { "hashes": [ @@ -3210,6 +3039,14 @@ "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'", "version": "==1.8.2" }, + "setuptools": { + "hashes": [ + "sha256:385eb4edd9c9d5c17540511303e39a147ce2fc04bc55289c322b9e5904fe2c05", + "sha256:be1af57fc409f93647f2e8e4573a142ed38724b8cdd389706a867bb4efcf1e78" + ], + "markers": "python_version >= '3.8'", + "version": "==69.0.3" + }, "six": { "hashes": [ "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926", @@ -3333,11 +3170,11 @@ }, "starlette": { "hashes": [ - "sha256:6a6b0d042acb8d469a01eba54e9cda6cbd24ac602c4cd016723117d6a7e73b75", - "sha256:918416370e846586541235ccd38a474c08b80443ed31c578a418e2209b3eef91" + "sha256:cd0cb10ddb49313f609cedfac62c8c12e56c7314b66d89bb077ba228bada1b09", + "sha256:e54e2b7e2fb06dff9eac40133583f10dfa05913f5a85bf26f427c7a40a9a3d02" ], - "markers": "python_version >= '3.7'", - "version": "==0.27.0" + "markers": "python_version >= '3.8'", + "version": "==0.32.0.post1" }, "syrupy": { "hashes": [ @@ -3498,101 +3335,55 @@ "markers": "python_version >= '3.8'", "version": "==1.7.0" }, - "yarl": { - "hashes": [ - "sha256:008d3e808d03ef28542372d01057fd09168419cdc8f848efe2804f894ae03e51", - "sha256:03caa9507d3d3c83bca08650678e25364e1843b484f19986a527630ca376ecce", - "sha256:07574b007ee20e5c375a8fe4a0789fad26db905f9813be0f9fef5a68080de559", - "sha256:09efe4615ada057ba2d30df871d2f668af661e971dfeedf0c159927d48bbeff0", - "sha256:0d2454f0aef65ea81037759be5ca9947539667eecebca092733b2eb43c965a81", - "sha256:0e9d124c191d5b881060a9e5060627694c3bdd1fe24c5eecc8d5d7d0eb6faabc", - "sha256:18580f672e44ce1238b82f7fb87d727c4a131f3a9d33a5e0e82b793362bf18b4", - "sha256:1f23e4fe1e8794f74b6027d7cf19dc25f8b63af1483d91d595d4a07eca1fb26c", - "sha256:206a55215e6d05dbc6c98ce598a59e6fbd0c493e2de4ea6cc2f4934d5a18d130", - "sha256:23d32a2594cb5d565d358a92e151315d1b2268bc10f4610d098f96b147370136", - "sha256:26a1dc6285e03f3cc9e839a2da83bcbf31dcb0d004c72d0730e755b33466c30e", - "sha256:29e0f83f37610f173eb7e7b5562dd71467993495e568e708d99e9d1944f561ec", - "sha256:2b134fd795e2322b7684155b7855cc99409d10b2e408056db2b93b51a52accc7", - "sha256:2d47552b6e52c3319fede1b60b3de120fe83bde9b7bddad11a69fb0af7db32f1", - "sha256:357495293086c5b6d34ca9616a43d329317feab7917518bc97a08f9e55648455", - "sha256:35a2b9396879ce32754bd457d31a51ff0a9d426fd9e0e3c33394bf4b9036b099", - "sha256:3777ce5536d17989c91696db1d459574e9a9bd37660ea7ee4d3344579bb6f129", - "sha256:3986b6f41ad22988e53d5778f91855dc0399b043fc8946d4f2e68af22ee9ff10", - "sha256:44d8ffbb9c06e5a7f529f38f53eda23e50d1ed33c6c869e01481d3fafa6b8142", - "sha256:49a180c2e0743d5d6e0b4d1a9e5f633c62eca3f8a86ba5dd3c471060e352ca98", - "sha256:4aa9741085f635934f3a2583e16fcf62ba835719a8b2b28fb2917bb0537c1dfa", - "sha256:4b21516d181cd77ebd06ce160ef8cc2a5e9ad35fb1c5930882baff5ac865eee7", - "sha256:4b3c1ffe10069f655ea2d731808e76e0f452fc6c749bea04781daf18e6039525", - "sha256:4c7d56b293cc071e82532f70adcbd8b61909eec973ae9d2d1f9b233f3d943f2c", - "sha256:4e9035df8d0880b2f1c7f5031f33f69e071dfe72ee9310cfc76f7b605958ceb9", - "sha256:54525ae423d7b7a8ee81ba189f131054defdb122cde31ff17477951464c1691c", - "sha256:549d19c84c55d11687ddbd47eeb348a89df9cb30e1993f1b128f4685cd0ebbf8", - "sha256:54beabb809ffcacbd9d28ac57b0db46e42a6e341a030293fb3185c409e626b8b", - "sha256:566db86717cf8080b99b58b083b773a908ae40f06681e87e589a976faf8246bf", - "sha256:5a2e2433eb9344a163aced6a5f6c9222c0786e5a9e9cac2c89f0b28433f56e23", - "sha256:5aef935237d60a51a62b86249839b51345f47564208c6ee615ed2a40878dccdd", - "sha256:604f31d97fa493083ea21bd9b92c419012531c4e17ea6da0f65cacdcf5d0bd27", - "sha256:63b20738b5aac74e239622d2fe30df4fca4942a86e31bf47a81a0e94c14df94f", - "sha256:686a0c2f85f83463272ddffd4deb5e591c98aac1897d65e92319f729c320eece", - "sha256:6a962e04b8f91f8c4e5917e518d17958e3bdee71fd1d8b88cdce74dd0ebbf434", - "sha256:6ad6d10ed9b67a382b45f29ea028f92d25bc0bc1daf6c5b801b90b5aa70fb9ec", - "sha256:6f5cb257bc2ec58f437da2b37a8cd48f666db96d47b8a3115c29f316313654ff", - "sha256:6fe79f998a4052d79e1c30eeb7d6c1c1056ad33300f682465e1b4e9b5a188b78", - "sha256:7855426dfbddac81896b6e533ebefc0af2f132d4a47340cee6d22cac7190022d", - "sha256:7d5aaac37d19b2904bb9dfe12cdb08c8443e7ba7d2852894ad448d4b8f442863", - "sha256:801e9264d19643548651b9db361ce3287176671fb0117f96b5ac0ee1c3530d53", - "sha256:81eb57278deb6098a5b62e88ad8281b2ba09f2f1147c4767522353eaa6260b31", - "sha256:824d6c50492add5da9374875ce72db7a0733b29c2394890aef23d533106e2b15", - "sha256:8397a3817d7dcdd14bb266283cd1d6fc7264a48c186b986f32e86d86d35fbac5", - "sha256:848cd2a1df56ddbffeb375535fb62c9d1645dde33ca4d51341378b3f5954429b", - "sha256:84fc30f71689d7fc9168b92788abc977dc8cefa806909565fc2951d02f6b7d57", - "sha256:8619d6915b3b0b34420cf9b2bb6d81ef59d984cb0fde7544e9ece32b4b3043c3", - "sha256:8a854227cf581330ffa2c4824d96e52ee621dd571078a252c25e3a3b3d94a1b1", - "sha256:8be9e837ea9113676e5754b43b940b50cce76d9ed7d2461df1af39a8ee674d9f", - "sha256:928cecb0ef9d5a7946eb6ff58417ad2fe9375762382f1bf5c55e61645f2c43ad", - "sha256:957b4774373cf6f709359e5c8c4a0af9f6d7875db657adb0feaf8d6cb3c3964c", - "sha256:992f18e0ea248ee03b5a6e8b3b4738850ae7dbb172cc41c966462801cbf62cf7", - "sha256:9fc5fc1eeb029757349ad26bbc5880557389a03fa6ada41703db5e068881e5f2", - "sha256:a00862fb23195b6b8322f7d781b0dc1d82cb3bcac346d1e38689370cc1cc398b", - "sha256:a3a6ed1d525bfb91b3fc9b690c5a21bb52de28c018530ad85093cc488bee2dd2", - "sha256:a6327976c7c2f4ee6816eff196e25385ccc02cb81427952414a64811037bbc8b", - "sha256:a7409f968456111140c1c95301cadf071bd30a81cbd7ab829169fb9e3d72eae9", - "sha256:a825ec844298c791fd28ed14ed1bffc56a98d15b8c58a20e0e08c1f5f2bea1be", - "sha256:a8c1df72eb746f4136fe9a2e72b0c9dc1da1cbd23b5372f94b5820ff8ae30e0e", - "sha256:a9bd00dc3bc395a662900f33f74feb3e757429e545d831eef5bb280252631984", - "sha256:aa102d6d280a5455ad6a0f9e6d769989638718e938a6a0a2ff3f4a7ff8c62cc4", - "sha256:aaaea1e536f98754a6e5c56091baa1b6ce2f2700cc4a00b0d49eca8dea471074", - "sha256:ad4d7a90a92e528aadf4965d685c17dacff3df282db1121136c382dc0b6014d2", - "sha256:b8477c1ee4bd47c57d49621a062121c3023609f7a13b8a46953eb6c9716ca392", - "sha256:ba6f52cbc7809cd8d74604cce9c14868306ae4aa0282016b641c661f981a6e91", - "sha256:bac8d525a8dbc2a1507ec731d2867025d11ceadcb4dd421423a5d42c56818541", - "sha256:bef596fdaa8f26e3d66af846bbe77057237cb6e8efff8cd7cc8dff9a62278bbf", - "sha256:c0ec0ed476f77db9fb29bca17f0a8fcc7bc97ad4c6c1d8959c507decb22e8572", - "sha256:c38c9ddb6103ceae4e4498f9c08fac9b590c5c71b0370f98714768e22ac6fa66", - "sha256:c7224cab95645c7ab53791022ae77a4509472613e839dab722a72abe5a684575", - "sha256:c74018551e31269d56fab81a728f683667e7c28c04e807ba08f8c9e3bba32f14", - "sha256:ca06675212f94e7a610e85ca36948bb8fc023e458dd6c63ef71abfd482481aa5", - "sha256:d1d2532b340b692880261c15aee4dc94dd22ca5d61b9db9a8a361953d36410b1", - "sha256:d25039a474c4c72a5ad4b52495056f843a7ff07b632c1b92ea9043a3d9950f6e", - "sha256:d5ff2c858f5f6a42c2a8e751100f237c5e869cbde669a724f2062d4c4ef93551", - "sha256:d7d7f7de27b8944f1fee2c26a88b4dabc2409d2fea7a9ed3df79b67277644e17", - "sha256:d7eeb6d22331e2fd42fce928a81c697c9ee2d51400bd1a28803965883e13cead", - "sha256:d8a1c6c0be645c745a081c192e747c5de06e944a0d21245f4cf7c05e457c36e0", - "sha256:d8b889777de69897406c9fb0b76cdf2fd0f31267861ae7501d93003d55f54fbe", - "sha256:d9e09c9d74f4566e905a0b8fa668c58109f7624db96a2171f21747abc7524234", - "sha256:db8e58b9d79200c76956cefd14d5c90af54416ff5353c5bfd7cbe58818e26ef0", - "sha256:ddb2a5c08a4eaaba605340fdee8fc08e406c56617566d9643ad8bf6852778fc7", - "sha256:e0381b4ce23ff92f8170080c97678040fc5b08da85e9e292292aba67fdac6c34", - "sha256:e23a6d84d9d1738dbc6e38167776107e63307dfc8ad108e580548d1f2c587f42", - "sha256:e516dc8baf7b380e6c1c26792610230f37147bb754d6426462ab115a02944385", - "sha256:ea65804b5dc88dacd4a40279af0cdadcfe74b3e5b4c897aa0d81cf86927fee78", - "sha256:ec61d826d80fc293ed46c9dd26995921e3a82146feacd952ef0757236fc137be", - "sha256:ee04010f26d5102399bd17f8df8bc38dc7ccd7701dc77f4a68c5b8d733406958", - "sha256:f3bc6af6e2b8f92eced34ef6a96ffb248e863af20ef4fde9448cc8c9b858b749", - "sha256:f7d6b36dd2e029b6bcb8a13cf19664c7b8e19ab3a58e0fefbb5b8461447ed5ec" + "zope.event": { + "hashes": [ + "sha256:2832e95014f4db26c47a13fdaef84cef2f4df37e66b59d8f1f4a8f319a632c26", + "sha256:bac440d8d9891b4068e2b5a2c5e2c9765a9df762944bda6955f96bb9b91e67cd" + ], + "markers": "python_version >= '3.7'", + "version": "==5.0" + }, + "zope.interface": { + "hashes": [ + "sha256:0c8cf55261e15590065039696607f6c9c1aeda700ceee40c70478552d323b3ff", + "sha256:13b7d0f2a67eb83c385880489dbb80145e9d344427b4262c49fbf2581677c11c", + "sha256:1f294a15f7723fc0d3b40701ca9b446133ec713eafc1cc6afa7b3d98666ee1ac", + "sha256:239a4a08525c080ff833560171d23b249f7f4d17fcbf9316ef4159f44997616f", + "sha256:2f8d89721834524a813f37fa174bac074ec3d179858e4ad1b7efd4401f8ac45d", + "sha256:2fdc7ccbd6eb6b7df5353012fbed6c3c5d04ceaca0038f75e601060e95345309", + "sha256:34c15ca9248f2e095ef2e93af2d633358c5f048c49fbfddf5fdfc47d5e263736", + "sha256:387545206c56b0315fbadb0431d5129c797f92dc59e276b3ce82db07ac1c6179", + "sha256:43b576c34ef0c1f5a4981163b551a8781896f2a37f71b8655fd20b5af0386abb", + "sha256:57d0a8ce40ce440f96a2c77824ee94bf0d0925e6089df7366c2272ccefcb7941", + "sha256:5a804abc126b33824a44a7aa94f06cd211a18bbf31898ba04bd0924fbe9d282d", + "sha256:67be3ca75012c6e9b109860820a8b6c9a84bfb036fbd1076246b98e56951ca92", + "sha256:6af47f10cfc54c2ba2d825220f180cc1e2d4914d783d6fc0cd93d43d7bc1c78b", + "sha256:6dc998f6de015723196a904045e5a2217f3590b62ea31990672e31fbc5370b41", + "sha256:70d2cef1bf529bff41559be2de9d44d47b002f65e17f43c73ddefc92f32bf00f", + "sha256:7ebc4d34e7620c4f0da7bf162c81978fce0ea820e4fa1e8fc40ee763839805f3", + "sha256:964a7af27379ff4357dad1256d9f215047e70e93009e532d36dcb8909036033d", + "sha256:97806e9ca3651588c1baaebb8d0c5ee3db95430b612db354c199b57378312ee8", + "sha256:9b9bc671626281f6045ad61d93a60f52fd5e8209b1610972cf0ef1bbe6d808e3", + "sha256:9ffdaa5290422ac0f1688cb8adb1b94ca56cee3ad11f29f2ae301df8aecba7d1", + "sha256:a0da79117952a9a41253696ed3e8b560a425197d4e41634a23b1507efe3273f1", + "sha256:a41f87bb93b8048fe866fa9e3d0c51e27fe55149035dcf5f43da4b56732c0a40", + "sha256:aa6fd016e9644406d0a61313e50348c706e911dca29736a3266fc9e28ec4ca6d", + "sha256:ad54ed57bdfa3254d23ae04a4b1ce405954969c1b0550cc2d1d2990e8b439de1", + "sha256:b012d023b4fb59183909b45d7f97fb493ef7a46d2838a5e716e3155081894605", + "sha256:b51b64432eed4c0744241e9ce5c70dcfecac866dff720e746d0a9c82f371dfa7", + "sha256:bbe81def9cf3e46f16ce01d9bfd8bea595e06505e51b7baf45115c77352675fd", + "sha256:c9559138690e1bd4ea6cd0954d22d1e9251e8025ce9ede5d0af0ceae4a401e43", + "sha256:e30506bcb03de8983f78884807e4fd95d8db6e65b69257eea05d13d519b83ac0", + "sha256:e33e86fd65f369f10608b08729c8f1c92ec7e0e485964670b4d2633a4812d36b", + "sha256:e441e8b7d587af0414d25e8d05e27040d78581388eed4c54c30c0c91aad3a379", + "sha256:e8bb9c990ca9027b4214fa543fd4025818dc95f8b7abce79d61dc8a2112b561a", + "sha256:ef43ee91c193f827e49599e824385ec7c7f3cd152d74cb1dfe02cb135f264d83", + "sha256:ef467d86d3cfde8b39ea1b35090208b0447caaabd38405420830f7fd85fbdd56", + "sha256:f89b28772fc2562ed9ad871c865f5320ef761a7fcc188a935e21fe8b31a38ca9", + "sha256:fddbab55a2473f1d3b8833ec6b7ac31e8211b0aa608df5ab09ce07f3727326de" ], "markers": "python_version >= '3.7'", - "version": "==1.9.4" + "version": "==6.1" } } } diff --git a/backend/app/api/api_v1/api.py b/backend/app/api/api_v1/api.py index 807a8dda..164b4e1a 100644 --- a/backend/app/api/api_v1/api.py +++ b/backend/app/api/api_v1/api.py @@ -90,7 +90,8 @@ def __init__( base_scopes=base_scopes, ) - async def get_id_email(self, token: str) -> Tuple[str, Optional[str]]: + # Note: It is OK to not epect coverage here as this integrates with upstream API. + async def get_id_email(self, token: str) -> Tuple[str, Optional[str]]: # pragma: no cover """Custom implementation that returns the user ID and email.""" async with self.get_httpx_client() as client: response_user = await client.get( @@ -103,7 +104,7 @@ async def get_id_email(self, token: str) -> Tuple[str, Optional[str]]: data_user: Dict[str, Any] = response_user.json() response_record = await client.get( - f"https://api.sandbox.orcid.org/v3.0/{data_user['sub']}/record", + f"https://api.orcid.org/v3.0/{data_user['sub']}/record", headers={**self.request_headers, "Authorization": f"Bearer {token}"}, ) if response_user.status_code >= 400: diff --git a/backend/app/api/api_v1/endpoints/acmgseqvar.py b/backend/app/api/api_v1/endpoints/acmgseqvar.py index c85e8d4d..221540f6 100644 --- a/backend/app/api/api_v1/endpoints/acmgseqvar.py +++ b/backend/app/api/api_v1/endpoints/acmgseqvar.py @@ -62,11 +62,11 @@ async def get_acmgseqvar(id: str, db: AsyncSession = Depends(deps.get_db)): :return: ACMG Sequence Variant :rtype: dict """ - response = await crud.acmgseqvar.get(db, id) - if not response: + result = await crud.acmgseqvar.get(db, id) + if not result: # pragma: no cover raise HTTPException(status_code=404, detail="ACMG Sequence Variant not found") else: - return response + return result @router.get("/list", response_model=list[schemas.AcmgSeqVarRead]) @@ -99,11 +99,11 @@ async def get_acmgseqvar_by_user( :return: ACMG Sequence Variant :rtype: dict """ - response = await crud.acmgseqvar.get_by_user(db, user_id=user.id, seqvar_name=seqvar) - if not response: + result = await crud.acmgseqvar.get_by_user(db, user_id=user.id, seqvar_name=seqvar) + if not result: # pragma: no cover raise HTTPException(status_code=404, detail="ACMG Sequence Variant not found") else: - return response + return result @router.put("/update", response_model=schemas.AcmgSeqVarRead) @@ -122,13 +122,13 @@ async def update_acmgseqvar( :rtype: dict """ acmgseqvar.user = user.id - response = await crud.acmgseqvar.get_by_user( + result = await crud.acmgseqvar.get_by_user( db, user_id=user.id, seqvar_name=acmgseqvar.seqvar_name ) - if not response: + if not result: # pragma: no cover raise HTTPException(status_code=404, detail="ACMG Sequence Variant not found") else: - return await crud.acmgseqvar.update(db, db_obj=response, obj_in=acmgseqvar) + return await crud.acmgseqvar.update(db, db_obj=result, obj_in=acmgseqvar) @router.delete( @@ -145,8 +145,8 @@ async def delete_acmgseqvar(id: str, db: AsyncSession = Depends(deps.get_db)): :return: ACMG Sequence Variant :rtype: dict """ - response = await crud.acmgseqvar.get(db, id) - if not response: + result = await crud.acmgseqvar.get(db, id) + if not result: # pragma: no cover raise HTTPException(status_code=404, detail="ACMG Sequence Variant not found") else: return await crud.acmgseqvar.remove(db, id=id) @@ -166,8 +166,8 @@ async def delete_acmgseqvar_by_user( :return: ACMG Sequence Variant :rtype: dict """ - response = await crud.acmgseqvar.get_by_user(db, user_id=user.id, seqvar_name=seqvar) - if not response: + result = await crud.acmgseqvar.get_by_user(db, user_id=user.id, seqvar_name=seqvar) + if not result: # pragma: no cover raise HTTPException(status_code=404, detail="ACMG Sequence Variant not found") else: - return await crud.acmgseqvar.remove(db, id=response.id) + return await crud.acmgseqvar.remove(db, id=result.id) diff --git a/backend/app/api/api_v1/endpoints/bookmarks.py b/backend/app/api/api_v1/endpoints/bookmarks.py index bce536a2..510d5e6b 100644 --- a/backend/app/api/api_v1/endpoints/bookmarks.py +++ b/backend/app/api/api_v1/endpoints/bookmarks.py @@ -60,11 +60,11 @@ async def get_bookmark(id: str, db: AsyncSession = Depends(deps.get_db)): :return: bookmark :rtype: dict """ - response = await crud.bookmark.get(db, id=id) - if not response: + result = await crud.bookmark.get(db, id=id) + if not result: # pragma: no cover raise HTTPException(status_code=404, detail="Bookmark not found") else: - return response + return result @router.delete( @@ -81,11 +81,11 @@ async def delete_bookmark(id: str, db: AsyncSession = Depends(deps.get_db)): :return: bookmark which was deleted :rtype: dict """ - response = await crud.bookmark.remove(db, id=id) - if not response: + result = await crud.bookmark.remove(db, id=id) + if not result: # pragma: no cover raise HTTPException(status_code=404, detail="Bookmark not found") else: - return response + return result @router.get("/list", response_model=list[schemas.BookmarkRead]) @@ -125,13 +125,13 @@ async def get_bookmark_for_user( :return: bookmark :rtype: dict """ - response = await crud.bookmark.get_by_user_and_obj( + result = await crud.bookmark.get_by_user_and_obj( db, user_id=user.id, obj_type=obj_type, obj_id=obj_id ) - if not response: + if not result: # pragma: no cover raise HTTPException(status_code=404, detail="Bookmark not found") else: - return response + return result @router.delete("/delete", response_model=schemas.BookmarkRead) @@ -157,5 +157,5 @@ async def delete_bookmark_for_user( ) if bookmark: return await crud.bookmark.remove(db, id=bookmark.id) - else: + else: # pragma: no cover raise HTTPException(status_code=404, detail="Bookmark not found") diff --git a/backend/app/api/api_v1/endpoints/caseinfo.py b/backend/app/api/api_v1/endpoints/caseinfo.py index c837a7c9..7cc4f2b0 100644 --- a/backend/app/api/api_v1/endpoints/caseinfo.py +++ b/backend/app/api/api_v1/endpoints/caseinfo.py @@ -60,11 +60,11 @@ async def get_caseinfo(id: str, db: AsyncSession = Depends(deps.get_db)): :return: Case Information :rtype: dict """ - response = await crud.caseinfo.get(db, id=id) - if not response: + caseinfo = await crud.caseinfo.get(db, id=id) + if not caseinfo: # pragma: no cover raise HTTPException(status_code=404, detail="Case Information not found") else: - return response + return caseinfo @router.get("/list", response_model=list[schemas.CaseInfoRead]) @@ -92,7 +92,7 @@ async def get_caseinfo_for_user( :rtype: dict """ caseinfo = await crud.caseinfo.get_by_user(db, user_id=user.id) - if not caseinfo: + if not caseinfo: # pragma: no cover raise HTTPException(status_code=404, detail="Case Information not found") return caseinfo @@ -114,7 +114,7 @@ async def update_caseinfo_for_user( """ caseinfoupdate.user = user.id caseinfo = await crud.caseinfo.get_by_user(db, user_id=user.id) - if not caseinfo: + if not caseinfo: # pragma: no cover # pragma: no cover raise HTTPException(status_code=404, detail="Case Information not found") return await crud.caseinfo.update(db, db_obj=caseinfo, obj_in=caseinfoupdate) @@ -134,7 +134,7 @@ async def delete_caseinfo(id: str, db: AsyncSession = Depends(deps.get_db)): :rtype: dict """ response = await crud.caseinfo.remove(db, id=id) - if not response: + if not response: # pragma: no cover raise HTTPException(status_code=404, detail="Case Information not found") else: return response @@ -151,6 +151,6 @@ async def delete_caseinfo_for_user( :rtype: dict """ caseinfo = await crud.caseinfo.get_by_user(db, user_id=user.id) - if not caseinfo: + if not caseinfo: # pragma: no cover raise HTTPException(status_code=404, detail="Case Information not found") return await crud.caseinfo.remove(db, id=caseinfo.id) diff --git a/backend/app/api/api_v1/endpoints/clinvarsub.py b/backend/app/api/api_v1/endpoints/clinvarsub.py index ccb2429a..d3b38bbd 100644 --- a/backend/app/api/api_v1/endpoints/clinvarsub.py +++ b/backend/app/api/api_v1/endpoints/clinvarsub.py @@ -87,7 +87,7 @@ async def get_submittingorg_by_id( :return: Submitting org data. """ response = await crud.submittingorg.get(db, id=submittingorg_id) - if not response: + if not response: # pragma: no cover raise HTTPException(status_code=404, detail="submitting org not found") elif response.owner != user.id: raise HTTPException(status_code=403, detail="user not owner of submitting org") @@ -111,7 +111,7 @@ async def update_submittingorg( :return: Paginated list of results. """ submittingorg_db = await crud.submittingorg.get(db, id=submittingorg_id) - if not submittingorg_db: + if not submittingorg_db: # pragma: no cover raise HTTPException(status_code=404, detail="submitting org not found") elif submittingorg_db.owner != user.id: raise HTTPException(status_code=403, detail="user not owner of submitting org") @@ -134,7 +134,7 @@ async def delete_submittingorg( :return: Paginated list of results. """ submittingorg_db = await crud.submittingorg.get(db, id=submittingorg_id) - if not submittingorg_db: + if not submittingorg_db: # pragma: no cover raise HTTPException(status_code=404, detail="submitting org not found") elif submittingorg_db.owner != user.id: raise HTTPException(status_code=403, detail="user not owner of submitting org") @@ -175,7 +175,7 @@ async def create_submissionthread( :return: Created submission thread. """ submittingorg = await crud.submittingorg.get(db, id=submissionthread.submittingorg_id) - if not submittingorg: + if not submittingorg: # pragma: no cover raise HTTPException(status_code=404, detail="submitting org not found") elif submittingorg.owner != user.id: raise HTTPException(status_code=403, detail="user not owner of submitting org") @@ -200,10 +200,10 @@ async def update_submissionthread( :return: Submission tread data. """ submissionthread_db = await crud.submissionthread.get(db, id=submissionthread_id) - if not submissionthread_db: + if not submissionthread_db: # pragma: no cover raise HTTPException(status_code=404, detail="submission thread not found") submittingorg = await crud.submittingorg.get(db, id=submissionthread_db.submittingorg_id) - if not submittingorg: + if not submittingorg: # pragma: no cover raise HTTPException(status_code=404, detail="submitting org not found") elif submittingorg.owner != user.id: raise HTTPException(status_code=403, detail="user not owner of submitting org") @@ -229,10 +229,10 @@ async def get_submissionthread_by_id( :return: Submission tread data. """ submissionthread_db = await crud.submissionthread.get(db, id=submissionthread_id) - if not submissionthread_db: + if not submissionthread_db: # pragma: no cover raise HTTPException(status_code=404, detail="submission thread not found") submittingorg = await crud.submittingorg.get(db, id=submissionthread_db.submittingorg_id) - if not submittingorg: + if not submittingorg: # pragma: no cover raise HTTPException(status_code=404, detail="submitting org not found") elif submittingorg.owner != user.id: raise HTTPException(status_code=403, detail="user not owner of submitting org") @@ -255,10 +255,10 @@ async def delete_submissionthread( :return: Paginated list of results. """ submissionthread_db = await crud.submissionthread.get(db, id=submissionthread_id) - if not submissionthread_db: + if not submissionthread_db: # pragma: no cover raise HTTPException(status_code=404, detail="submission thread not found") submittingorg = await crud.submittingorg.get(db, id=submissionthread_db.submittingorg_id) - if not submittingorg: + if not submittingorg: # pragma: no cover raise HTTPException(status_code=404, detail="submitting org not found") elif submittingorg.owner != user.id: raise HTTPException(status_code=403, detail="user not owner of submitting org") @@ -287,10 +287,10 @@ async def list_submissionactivities( :return: Paginated list of results. """ submissionthread_db = await crud.submissionthread.get(db, id=submissionthread_id) - if not submissionthread_db: + if not submissionthread_db: # pragma: no cover raise HTTPException(status_code=404, detail="submission thread not found") submittingorg = await crud.submittingorg.get(db, id=submissionthread_db.submittingorg_id) - if not submittingorg: + if not submittingorg: # pragma: no cover raise HTTPException(status_code=404, detail="submitting org not found") elif submittingorg.owner != user.id: raise HTTPException(status_code=403, detail="user not owner of submitting org") @@ -317,10 +317,10 @@ async def create_submissionactivity( :return: Created submission activity. """ submissionthread_db = await crud.submissionthread.get(db, id=submissionthread_id) - if not submissionthread_db: + if not submissionthread_db: # pragma: no cover raise HTTPException(status_code=404, detail="submission thread not found") submittingorg = await crud.submittingorg.get(db, id=submissionthread_db.submittingorg_id) - if not submittingorg: + if not submittingorg: # pragma: no cover raise HTTPException(status_code=404, detail="submitting org not found") elif submittingorg.owner != user.id: raise HTTPException(status_code=403, detail="user not owner of submitting org") @@ -345,15 +345,15 @@ async def update_submissionactivity( :return: Created submission activity. """ submissionactivity_db = await crud.submissionactivity.get(db, id=submissionactivity_id) - if not submissionactivity_db: + if not submissionactivity_db: # pragma: no cover raise HTTPException(status_code=404, detail="submission activity not found") submissionthread_db = await crud.submissionthread.get( db, id=submissionactivity_db.submissionthread_id ) - if not submissionthread_db: + if not submissionthread_db: # pragma: no cover raise HTTPException(status_code=404, detail="submission thread not found") submittingorg = await crud.submittingorg.get(db, id=submissionthread_db.submittingorg_id) - if not submittingorg: + if not submittingorg: # pragma: no cover raise HTTPException(status_code=404, detail="submitting org not found") elif submittingorg.owner != user.id: raise HTTPException(status_code=403, detail="user not owner of submitting org") diff --git a/backend/app/api/internal/endpoints/remote.py b/backend/app/api/internal/endpoints/remote.py index 309f495b..44c5c6c1 100644 --- a/backend/app/api/internal/endpoints/remote.py +++ b/backend/app/api/internal/endpoints/remote.py @@ -1,10 +1,6 @@ """Reverse proxies to external/remote services.""" -import ssl - import httpx -import requests -import urllib3 from fastapi import APIRouter, BackgroundTasks, Request, Response from fastapi.responses import JSONResponse, StreamingResponse from starlette.background import BackgroundTask @@ -46,28 +42,30 @@ def default_acmg_rating() -> dict[str, bool]: return {k: False for k in ACMG_RATING_KEYS} -class CustomHttpAdapter(requests.adapters.HTTPAdapter): - # "Transport adapter" that allows us to use custom ssl_context. +router = APIRouter() - def __init__(self, ssl_context=None, **kwargs): - self.ssl_context = ssl_context - super().__init__(**kwargs) - def init_poolmanager(self, connections, maxsize, block=False): - self.poolmanager = urllib3.poolmanager.PoolManager( - num_pools=connections, maxsize=maxsize, block=block, ssl_context=self.ssl_context - ) +class HTTPXClientWrapper: + """Wrapper around HTTPX AsyncClient to for graceful startup/shutdown within FastAPI.""" + transport: httpx.AsyncHTTPTransport | None = None + async_client: httpx.AsyncClient | None = None -def get_legacy_session(): - ctx = ssl.create_default_context(ssl.Purpose.SERVER_AUTH) - ctx.options |= 0x4 # OP_LEGACY_SERVER_CONNECT - session = requests.session() - session.mount("https://", CustomHttpAdapter(ctx)) - return session + def start(self): + self.transport = httpx.AsyncHTTPTransport(retries=1) + self.transport._pool._ssl_context.options |= 0x4 # OP_LEGACY_SERVER_CONNECT + self.async_client = httpx.AsyncClient(transport=self.transport) + async def stop(self): + await self.async_client.aclose() + self.async_client = None -router = APIRouter() + def __call__(self): + assert self.async_client is not None + return self.async_client + + +httpx_client_wrapper = HTTPXClientWrapper() @router.get("/variantvalidator/{path:path}") @@ -90,7 +88,7 @@ async def variantvalidator(request: Request, path: str): backend_url = "https://rest.variantvalidator.org/VariantValidator/variantvalidator/" + path backend_url = backend_url + (f"?{url.query}" if url.query else "") - client = httpx.AsyncClient() + client = httpx_client_wrapper() backend_req = client.build_request( method=request.method, url=backend_url, @@ -131,7 +129,7 @@ async def acmg(request: Request): f"queryType=position&chr={chromosome}&pos={position}" f"&ref={reference}&alt={alternative}&build={build}" ) - client = httpx.AsyncClient() + client = httpx_client_wrapper() backend_req = client.build_request(method="GET", url=url) backend_resp = await client.send(backend_req) if backend_resp.status_code != 200: @@ -148,7 +146,7 @@ async def acmg(request: Request): async def cnv_acmg(request: Request): """ Implement searching for ACMG classification for CNVs. - Proxy requests to the `WinterVar `_ backend. + Proxy requests to the `wAutoCNV `_ backend. :param request: request :type request: :class:`fastapi.Request` @@ -164,10 +162,16 @@ async def cnv_acmg(request: Request): if not chromosome or not start or not end or not func: return Response(status_code=400, content="Missing query parameters") - backend_resp = get_legacy_session().post( - "https://phoenix.bgi.com/api/acit/jobs/", + client = httpx_client_wrapper() + backend_req = client.build_request( + method="POST", + url="https://phoenix.bgi.com/api/acit/jobs/", data={"chromosome": chromosome, "start": start, "end": end, "func": func, "error": 0}, ) + backend_resp = await client.send(backend_req) + if backend_resp.status_code != 200: + return Response(status_code=backend_resp.status_code, content=backend_resp.content) + if backend_resp.status_code != 200: return Response(status_code=backend_resp.status_code, content=backend_resp.content) return JSONResponse(backend_resp.json()) @@ -188,7 +192,8 @@ async def pubtator3_api(request: Request, path: str): url = request.url backend_url = "https://www.ncbi.nlm.nih.gov/research/pubtator3-api/" + path backend_url = backend_url + (f"?{url.query}" if url.query else "") - client = httpx.AsyncClient() + + client = httpx_client_wrapper() backend_req = client.build_request( method=request.method, url=backend_url, diff --git a/backend/app/core/config.py b/backend/app/core/config.py index a1af1f27..e6ef99b0 100644 --- a/backend/app/core/config.py +++ b/backend/app/core/config.py @@ -171,7 +171,7 @@ def assemble_db_connection(cls, v: str | None, info: ValidationInfo) -> Any: return v else: password_file = info.data.get("POSTGRES_PASSWORD_FILE") - if password_file: + if password_file: # pragma: no cover logger.info(f"Reading password from {password_file}") with open(password_file, "rt") as inputf: password = inputf.read().strip() diff --git a/backend/app/db/init_db.py b/backend/app/db/init_db.py index 215d5490..078ee264 100644 --- a/backend/app/db/init_db.py +++ b/backend/app/db/init_db.py @@ -36,12 +36,12 @@ async def create_user( ) logger.info(f"User created {email}") return user - except UserAlreadyExists: + except UserAlreadyExists: # pragma: no cover logger.info(f"User {email} already exists") async def create_superuser(): - if settings.FIRST_SUPERUSER_EMAIL and settings.FIRST_SUPERUSER_PASSWORD: + if settings.FIRST_SUPERUSER_EMAIL and settings.FIRST_SUPERUSER_PASSWORD: # pragma: no cover await create_user( email=settings.FIRST_SUPERUSER_EMAIL, password=settings.FIRST_SUPERUSER_PASSWORD, @@ -49,5 +49,5 @@ async def create_superuser(): ) -async def init_db(): +async def init_db(): # pragma: no cover await create_superuser() diff --git a/backend/app/etc/utils.py b/backend/app/etc/utils.py index 2e42e446..8c35a71d 100644 --- a/backend/app/etc/utils.py +++ b/backend/app/etc/utils.py @@ -22,11 +22,11 @@ def send_email( mail_from=(settings.EMAILS_FROM_NAME, settings.EMAILS_FROM_EMAIL), ) smtp_options = {"host": settings.SMTP_HOST, "port": settings.SMTP_PORT} - if settings.SMTP_TLS: + if settings.SMTP_TLS: # pragma: no cover smtp_options["tls"] = True - if settings.SMTP_USER: + if settings.SMTP_USER: # pragma: no cover smtp_options["user"] = settings.SMTP_USER - if settings.SMTP_PASSWORD: + if settings.SMTP_PASSWORD: # pragma: no cover smtp_options["password"] = settings.SMTP_PASSWORD response = message.send(to=email_to, render=environment, smtp=smtp_options) logging.info(f"send email result: {response}") @@ -50,7 +50,7 @@ def send_user_verify_email(email_to: str, token: str, request: Request | None = """Send out user verification email""" if request: base_url = f"{request.url.components.scheme}://{request.url.components.netloc}" - else: + else: # pragma: no cover base_url = str(settings.SERVER_HOST) project_name = settings.PROJECT_NAME subject = f"{project_name} - Verify Your Email" diff --git a/backend/app/main.py b/backend/app/main.py index 34db5cf8..f0347440 100644 --- a/backend/app/main.py +++ b/backend/app/main.py @@ -12,6 +12,7 @@ from app.api.api_v1.api import api_router as api_v1_router from app.api.internal.api import api_router as internal_router +from app.api.internal.endpoints.remote import httpx_client_wrapper from app.core.config import settings from app.db.init_db import create_superuser @@ -33,10 +34,19 @@ traces_sample_rate=1.0, ) + +@asynccontextmanager +async def lifespan(app: FastAPI): + httpx_client_wrapper.start() + yield + await httpx_client_wrapper.stop() + + app = FastAPI( title="REEV", openapi_url=f"{settings.API_V1_STR}/openapi.json", docs_url=f"{settings.API_V1_STR}/docs", + lifespan=lifespan, debug=settings.DEBUG, ) diff --git a/backend/setup.cfg b/backend/setup.cfg index 0fd25704..fee367b9 100644 --- a/backend/setup.cfg +++ b/backend/setup.cfg @@ -16,6 +16,9 @@ ensure_newline_before_comments = true line_length = 100 [coverage:run] +concurrency = + thread + gevent omit = app/backend_pre_start.py app/init_db.py diff --git a/backend/tests/api/api_v1/test_acmgseqvar.py b/backend/tests/api/api_v1/test_acmgseqvar.py index 847882a8..db307c4f 100644 --- a/backend/tests/api/api_v1/test_acmgseqvar.py +++ b/backend/tests/api/api_v1/test_acmgseqvar.py @@ -7,6 +7,12 @@ from app.core.config import settings from app.models.user import User +from tests.conftest import UserChoice + +#: Shortcut for regular user. +REGUL = UserChoice.REGULAR +#: Shortcut for superuser. +SUPER = UserChoice.SUPERUSER # ------------------------------------------------------------------------------ # # /api/v1/acmgseqvar/create @@ -31,7 +37,7 @@ def acmgseqvar_post_data() -> dict[str, Any]: @pytest.mark.anyio -@pytest.mark.parametrize("test_user, client_user", [(False, False)], indirect=True) +@pytest.mark.parametrize("test_user, client_user", [(REGUL, REGUL)], indirect=True) async def test_create_acmgseqvar( db_session: AsyncSession, client_user: TestClient, @@ -40,10 +46,12 @@ async def test_create_acmgseqvar( ): """Test creating a acmgseqvar.""" _ = db_session + # act: response = client_user.post( f"{settings.API_V1_STR}/acmgseqvar/create", json=acmgseqvar_post_data, ) + # assert: content = response.json() assert response.status_code == 200 assert content["seqvar_name"] == acmgseqvar_post_data["seqvar_name"] @@ -52,7 +60,7 @@ async def test_create_acmgseqvar( @pytest.mark.anyio -@pytest.mark.parametrize("test_user, client_user", [(True, True)], indirect=True) +@pytest.mark.parametrize("test_user, client_user", [(SUPER, SUPER)], indirect=True) async def test_create_acmgseqvar_superuser( db_session: AsyncSession, client_user: TestClient, @@ -61,10 +69,12 @@ async def test_create_acmgseqvar_superuser( ): """Test creating a acmgseqvar as superuser.""" _ = db_session + # act: response = client_user.post( f"{settings.API_V1_STR}/acmgseqvar/create", json=acmgseqvar_post_data, ) + # assert: content = response.json() assert response.status_code == 200 assert content["seqvar_name"] == acmgseqvar_post_data["seqvar_name"] @@ -78,16 +88,18 @@ async def test_create_acmgseqvar_anon( ): """Test creating a acmgseqvar as anonymous user.""" _ = db_session + # act: response = client.post( f"{settings.API_V1_STR}/acmgseqvar/create", json=acmgseqvar_post_data, ) + # assert: assert response.status_code == 401 assert response.json() == {"detail": "Unauthorized"} @pytest.mark.anyio -@pytest.mark.parametrize("test_user, client_user", [(True, True)], indirect=True) +@pytest.mark.parametrize("test_user, client_user", [(SUPER, SUPER)], indirect=True) async def test_create_acmgseqvar_invalid_data( db_session: AsyncSession, client_user: TestClient, @@ -95,15 +107,18 @@ async def test_create_acmgseqvar_invalid_data( ): """Test creating a acmgseqvar with invalid data.""" _ = db_session + _ = test_user + # act: response = client_user.post( f"{settings.API_V1_STR}/acmgseqvar/create", json={"seqvar_name": "chr0:123:A:C", "acmg_rank": {"comment": "No comment"}}, ) + # assert: assert response.status_code == 422 @pytest.mark.anyio -@pytest.mark.parametrize("test_user, client_user", [(True, True)], indirect=True) +@pytest.mark.parametrize("test_user, client_user", [(SUPER, SUPER)], indirect=True) async def test_create_acmgseqvar_invalid_enums( db_session: AsyncSession, client_user: TestClient, @@ -112,12 +127,16 @@ async def test_create_acmgseqvar_invalid_enums( ): """Test creating a acmgseqvar with invalid enums.""" _ = db_session + _ = test_user + # arrange: post_data = acmgseqvar_post_data.copy() post_data["acmg_rank"]["criterias"][0]["criteria"] = "Pppm4" + # act: response = client_user.post( f"{settings.API_V1_STR}/acmgseqvar/create", json=post_data, ) + # assert: assert response.status_code == 422 @@ -127,7 +146,7 @@ async def test_create_acmgseqvar_invalid_enums( @pytest.mark.anyio -@pytest.mark.parametrize("test_user, client_user", [(False, False)], indirect=True) +@pytest.mark.parametrize("test_user, client_user", [(REGUL, REGUL)], indirect=True) async def test_list_all_acmgseqvars( db_session: AsyncSession, client_user: TestClient, @@ -136,23 +155,25 @@ async def test_list_all_acmgseqvars( ): """Test listing all acmgseqvars.""" _ = db_session + _ = test_user + # arrange: # Create acmgseqvar - response = client_user.post( + response_create = client_user.post( f"{settings.API_V1_STR}/acmgseqvar/create", json=acmgseqvar_post_data, ) - assert response.status_code == 200 - - response = client_user.get( + response_list_all = client_user.get( f"{settings.API_V1_STR}/acmgseqvar/list-all", ) - content = response.json() - assert response.status_code == 401 + # assert: + assert response_create.status_code == 200 + content = response_list_all.json() + assert response_list_all.status_code == 401 assert content == {"detail": "Unauthorized"} @pytest.mark.anyio -@pytest.mark.parametrize("test_user, client_user", [(True, True)], indirect=True) +@pytest.mark.parametrize("test_user, client_user", [(SUPER, SUPER)], indirect=True) async def test_list_all_acmgseqvars_superuser( db_session: AsyncSession, client_user: TestClient, @@ -161,18 +182,19 @@ async def test_list_all_acmgseqvars_superuser( ): """Test listing all acmgseqvars as superuser.""" _ = db_session + # act: # Create acmgseqvar - response = client_user.post( + response_create = client_user.post( f"{settings.API_V1_STR}/acmgseqvar/create", json=acmgseqvar_post_data, ) - assert response.status_code == 200 - - response = client_user.get( + response_list_all = client_user.get( f"{settings.API_V1_STR}/acmgseqvar/list-all", ) - content = response.json() - assert response.status_code == 200 + # assert: + assert response_create.status_code == 200 + content = response_list_all.json() + assert response_list_all.status_code == 200 assert content[0]["seqvar_name"] == acmgseqvar_post_data["seqvar_name"] assert content[0]["acmg_rank"] == acmgseqvar_post_data["acmg_rank"] assert content[0]["user"] == str(test_user.id) @@ -182,15 +204,17 @@ async def test_list_all_acmgseqvars_superuser( async def test_list_all_acmgseqvars_anon(db_session: AsyncSession, client: TestClient): """Test listing all acmgseqvars as anonymous user.""" _ = db_session + # act: response = client.get( f"{settings.API_V1_STR}/acmgseqvar/list-all", ) + # assert: assert response.status_code == 401 assert response.json() == {"detail": "Unauthorized"} @pytest.mark.anyio -@pytest.mark.parametrize("test_user, client_user", [(True, True)], indirect=True) +@pytest.mark.parametrize("test_user, client_user", [(SUPER, SUPER)], indirect=True) async def test_list_all_no_acmgseqvars( db_session: AsyncSession, client_user: TestClient, @@ -198,9 +222,12 @@ async def test_list_all_no_acmgseqvars( ): """Test listing all acmgseqvars as superuser.""" _ = db_session + _ = test_user + # act: response = client_user.get( f"{settings.API_V1_STR}/acmgseqvar/list-all", ) + # assert: content = response.json() assert response.status_code == 200 assert content == [] @@ -212,7 +239,7 @@ async def test_list_all_no_acmgseqvars( @pytest.mark.anyio -@pytest.mark.parametrize("test_user, client_user", [(False, False)], indirect=True) +@pytest.mark.parametrize("test_user, client_user", [(REGUL, REGUL)], indirect=True) async def test_get_by_id_acmgseqvar( db_session: AsyncSession, client_user: TestClient, @@ -221,26 +248,28 @@ async def test_get_by_id_acmgseqvar( ): """Test getting a acmgseqvar.""" _ = db_session + _ = test_user + # act: # Create acmgseqvar - response = client_user.post( + response_create = client_user.post( f"{settings.API_V1_STR}/acmgseqvar/create", json=acmgseqvar_post_data, ) - assert response.status_code == 200 # Get the acmgseqvar id - response = client_user.get(f"{settings.API_V1_STR}/acmgseqvar/list") - acmgseqvar_id = response.json()[0]["id"] - - response = client_user.get( + response_list = client_user.get(f"{settings.API_V1_STR}/acmgseqvar/list") + acmgseqvar_id = response_list.json()[0]["id"] + response_get_by_id = client_user.get( f"{settings.API_V1_STR}/acmgseqvar/get-by-id?id={acmgseqvar_id}", ) - content = response.json() - assert response.status_code == 401 + # assert: + assert response_create.status_code == 200 + content = response_get_by_id.json() + assert response_get_by_id.status_code == 401 assert content == {"detail": "Unauthorized"} @pytest.mark.anyio -@pytest.mark.parametrize("test_user, client_user", [(True, True)], indirect=True) +@pytest.mark.parametrize("test_user, client_user", [(SUPER, SUPER)], indirect=True) async def test_get_by_id_acmgseqvar_superuser( db_session: AsyncSession, client_user: TestClient, @@ -249,21 +278,22 @@ async def test_get_by_id_acmgseqvar_superuser( ): """Test getting a acmgseqvar as superuser.""" _ = db_session + # act: # Create acmgseqvar - response = client_user.post( + response_create = client_user.post( f"{settings.API_V1_STR}/acmgseqvar/create", json=acmgseqvar_post_data, ) - assert response.status_code == 200 # Get the acmgseqvar id - response = client_user.get(f"{settings.API_V1_STR}/acmgseqvar/list") - acmgseqvar_id = response.json()[0]["id"] - - response = client_user.get( + response_list = client_user.get(f"{settings.API_V1_STR}/acmgseqvar/list") + acmgseqvar_id = response_list.json()[0]["id"] + response_get_by_id = client_user.get( f"{settings.API_V1_STR}/acmgseqvar/get-by-id?id={acmgseqvar_id}", ) - content = response.json() - assert response.status_code == 200 + # assert: + assert response_create.status_code == 200 + content = response_get_by_id.json() + assert response_get_by_id.status_code == 200 assert content["seqvar_name"] == acmgseqvar_post_data["seqvar_name"] assert content["acmg_rank"] == acmgseqvar_post_data["acmg_rank"] assert content["user"] == str(test_user.id) @@ -273,25 +303,32 @@ async def test_get_by_id_acmgseqvar_superuser( async def test_get_by_id_acmgseqvar_anon(db_session: AsyncSession, client: TestClient): """Test getting a acmgseqvar as anonymous user.""" _ = db_session + # act: response = client.get( f"{settings.API_V1_STR}/acmgseqvar/get-by-id?id={uuid.uuid4()}", ) + # assert: assert response.status_code == 401 assert response.json() == {"detail": "Unauthorized"} @pytest.mark.anyio -@pytest.mark.parametrize("test_user, client_user", [(True, True)], indirect=True) +@pytest.mark.parametrize("test_user, client_user", [(SUPER, SUPER)], indirect=True) async def test_get_acmgseqvar_by_invalid_id( db_session: AsyncSession, client_user: TestClient, test_user: User, ): """Test getting a acmgseqvar with invalid id.""" + _ = db_session + _ = test_user + # arrange: caseinfo_id = uuid.uuid4() # Invalid id + # act: response = client_user.get( f"{settings.API_V1_STR}/acmgseqvar/get-by-id?id={caseinfo_id}", ) + # assert: assert response.status_code == 404 assert response.json() == {"detail": "ACMG Sequence Variant not found"} @@ -302,7 +339,7 @@ async def test_get_acmgseqvar_by_invalid_id( @pytest.mark.anyio -@pytest.mark.parametrize("test_user, client_user", [(False, False)], indirect=True) +@pytest.mark.parametrize("test_user, client_user", [(REGUL, REGUL)], indirect=True) async def test_list_acmgseqvars( db_session: AsyncSession, client_user: TestClient, @@ -311,25 +348,26 @@ async def test_list_acmgseqvars( ): """Test listing acmgseqvars.""" _ = db_session + # act: # Create acmgseqvar - response = client_user.post( + response_create = client_user.post( f"{settings.API_V1_STR}/acmgseqvar/create", json=acmgseqvar_post_data, ) - assert response.status_code == 200 - - response = client_user.get( + response_list = client_user.get( f"{settings.API_V1_STR}/acmgseqvar/list", ) - content = response.json() - assert response.status_code == 200 + # assert: + assert response_create.status_code == 200 + content = response_list.json() + assert response_list.status_code == 200 assert content[0]["seqvar_name"] == acmgseqvar_post_data["seqvar_name"] assert content[0]["acmg_rank"] == acmgseqvar_post_data["acmg_rank"] assert content[0]["user"] == str(test_user.id) @pytest.mark.anyio -@pytest.mark.parametrize("test_user, client_user", [(True, True)], indirect=True) +@pytest.mark.parametrize("test_user, client_user", [(SUPER, SUPER)], indirect=True) async def test_list_acmgseqvars_superuser( db_session: AsyncSession, client_user: TestClient, @@ -338,18 +376,19 @@ async def test_list_acmgseqvars_superuser( ): """Test listing acmgseqvars as superuser.""" _ = db_session + # act: # Create acmgseqvar - response = client_user.post( + response_create = client_user.post( f"{settings.API_V1_STR}/acmgseqvar/create", json=acmgseqvar_post_data, ) - assert response.status_code == 200 - - response = client_user.get( + response_list = client_user.get( f"{settings.API_V1_STR}/acmgseqvar/list", ) - content = response.json() - assert response.status_code == 200 + # assert: + assert response_create.status_code == 200 + content = response_list.json() + assert response_list.status_code == 200 assert content[0]["seqvar_name"] == acmgseqvar_post_data["seqvar_name"] assert content[0]["acmg_rank"] == acmgseqvar_post_data["acmg_rank"] assert content[0]["user"] == str(test_user.id) @@ -359,15 +398,17 @@ async def test_list_acmgseqvars_superuser( async def test_list_acmgseqvars_anon(db_session: AsyncSession, client: TestClient): """Test listing acmgseqvars as anonymous user.""" _ = db_session + # act: response = client.get( f"{settings.API_V1_STR}/acmgseqvar/list", ) + # assert: assert response.status_code == 401 assert response.json() == {"detail": "Unauthorized"} @pytest.mark.anyio -@pytest.mark.parametrize("test_user, client_user", [(True, True)], indirect=True) +@pytest.mark.parametrize("test_user, client_user", [(SUPER, SUPER)], indirect=True) async def test_list_no_acmgseqvars( db_session: AsyncSession, client_user: TestClient, @@ -375,9 +416,12 @@ async def test_list_no_acmgseqvars( ): """Test listing acmgseqvars with no records.""" _ = db_session + _ = test_user + # act: response = client_user.get( f"{settings.API_V1_STR}/acmgseqvar/list", ) + # assert: content = response.json() assert response.status_code == 200 assert content == [] @@ -389,7 +433,7 @@ async def test_list_no_acmgseqvars( @pytest.mark.anyio -@pytest.mark.parametrize("test_user, client_user", [(False, False)], indirect=True) +@pytest.mark.parametrize("test_user, client_user", [(REGUL, REGUL)], indirect=True) async def test_get_acmgseqvar( db_session: AsyncSession, client_user: TestClient, @@ -398,25 +442,26 @@ async def test_get_acmgseqvar( ): """Test getting a acmgseqvar.""" _ = db_session + # act: # Create acmgseqvar - response = client_user.post( + response_create = client_user.post( f"{settings.API_V1_STR}/acmgseqvar/create", json=acmgseqvar_post_data, ) - assert response.status_code == 200 - - response = client_user.get( + response_get = client_user.get( f"{settings.API_V1_STR}/acmgseqvar/get?seqvar={acmgseqvar_post_data['seqvar_name']}", ) - content = response.json() - assert response.status_code == 200 + # assert: + assert response_create.status_code == 200 + content = response_get.json() + assert response_get.status_code == 200 assert content["seqvar_name"] == acmgseqvar_post_data["seqvar_name"] assert content["acmg_rank"] == acmgseqvar_post_data["acmg_rank"] assert content["user"] == str(test_user.id) @pytest.mark.anyio -@pytest.mark.parametrize("test_user, client_user", [(True, True)], indirect=True) +@pytest.mark.parametrize("test_user, client_user", [(SUPER, SUPER)], indirect=True) async def test_get_acmgseqvar_superuser( db_session: AsyncSession, client_user: TestClient, @@ -425,18 +470,19 @@ async def test_get_acmgseqvar_superuser( ): """Test getting a acmgseqvar as superuser.""" _ = db_session + # act: # Create acmgseqvar - response = client_user.post( + response_create = client_user.post( f"{settings.API_V1_STR}/acmgseqvar/create", json=acmgseqvar_post_data, ) - assert response.status_code == 200 - - response = client_user.get( + response_get = client_user.get( f"{settings.API_V1_STR}/acmgseqvar/get?seqvar={acmgseqvar_post_data['seqvar_name']}", ) - content = response.json() - assert response.status_code == 200 + # assert: + assert response_create.status_code == 200 + content = response_get.json() + assert response_get.status_code == 200 assert content["seqvar_name"] == acmgseqvar_post_data["seqvar_name"] assert content["acmg_rank"] == acmgseqvar_post_data["acmg_rank"] assert content["user"] == str(test_user.id) @@ -446,15 +492,17 @@ async def test_get_acmgseqvar_superuser( async def test_get_acmgseqvar_anon(db_session: AsyncSession, client: TestClient): """Test getting a acmgseqvar as anonymous user.""" _ = db_session + # act: response = client.get( f"{settings.API_V1_STR}/acmgseqvar/get?seqvar={uuid.uuid4()}", ) + # assert: assert response.status_code == 401 assert response.json() == {"detail": "Unauthorized"} @pytest.mark.anyio -@pytest.mark.parametrize("test_user, client_user", [(True, True)], indirect=True) +@pytest.mark.parametrize("test_user, client_user", [(SUPER, SUPER)], indirect=True) async def test_get_no_acmgseqvar( db_session: AsyncSession, client_user: TestClient, @@ -462,9 +510,12 @@ async def test_get_no_acmgseqvar( ): """Test getting a acmgseqvar with no acmgseqvar.""" _ = db_session + _ = test_user + # act: response = client_user.get( f"{settings.API_V1_STR}/acmgseqvar/get?seqvar=invalid", ) + # assert: assert response.status_code == 404 assert response.json() == {"detail": "ACMG Sequence Variant not found"} @@ -492,7 +543,7 @@ def acmgseqvar_update_data() -> dict[str, Any]: @pytest.mark.anyio -@pytest.mark.parametrize("test_user, client_user", [(False, False)], indirect=True) +@pytest.mark.parametrize("test_user, client_user", [(REGUL, REGUL)], indirect=True) async def test_update_acmgseqvar( db_session: AsyncSession, client_user: TestClient, @@ -502,26 +553,28 @@ async def test_update_acmgseqvar( ): """Test updating a acmgseqvar.""" _ = db_session + # act: # Create acmgseqvar - response = client_user.post( + response_create = client_user.post( f"{settings.API_V1_STR}/acmgseqvar/create", json=acmgseqvar_post_data, ) - assert response.status_code == 200 # Update acmgseqvar - response = client_user.put( + response_update = client_user.put( f"{settings.API_V1_STR}/acmgseqvar/update", json=acmgseqvar_update_data, ) - content = response.json() - assert response.status_code == 200 + # assert: + assert response_create.status_code == 200 + content = response_update.json() + assert response_update.status_code == 200 assert content["seqvar_name"] == acmgseqvar_update_data["seqvar_name"] assert content["acmg_rank"] == acmgseqvar_update_data["acmg_rank"] assert content["user"] == str(test_user.id) @pytest.mark.anyio -@pytest.mark.parametrize("test_user, client_user", [(True, True)], indirect=True) +@pytest.mark.parametrize("test_user, client_user", [(SUPER, SUPER)], indirect=True) async def test_update_acmgseqvar_superuser( db_session: AsyncSession, client_user: TestClient, @@ -531,19 +584,21 @@ async def test_update_acmgseqvar_superuser( ): """Test updating a acmgseqvar as superuser.""" _ = db_session + # act: # Create acmgseqvar - response = client_user.post( + response_create = client_user.post( f"{settings.API_V1_STR}/acmgseqvar/create", json=acmgseqvar_post_data, ) - assert response.status_code == 200 # Update acmgseqvar - response = client_user.put( + response_update = client_user.put( f"{settings.API_V1_STR}/acmgseqvar/update", json=acmgseqvar_update_data, ) - content = response.json() - assert response.status_code == 200 + # assert: + assert response_create.status_code == 200 + content = response_update.json() + assert response_update.status_code == 200 assert content["seqvar_name"] == acmgseqvar_update_data["seqvar_name"] assert content["acmg_rank"] == acmgseqvar_update_data["acmg_rank"] assert content["user"] == str(test_user.id) @@ -555,16 +610,18 @@ async def test_update_acmgseqvar_anon( ): """Test updating a acmgseqvar as anonymous user.""" _ = db_session + # act: response = client.put( f"{settings.API_V1_STR}/acmgseqvar/update", json=acmgseqvar_update_data, ) + # assert: assert response.status_code == 401 assert response.json() == {"detail": "Unauthorized"} @pytest.mark.anyio -@pytest.mark.parametrize("test_user, client_user", [(True, True)], indirect=True) +@pytest.mark.parametrize("test_user, client_user", [(SUPER, SUPER)], indirect=True) async def test_update_acmgseqvar_patch( db_session: AsyncSession, client_user: TestClient, @@ -574,26 +631,28 @@ async def test_update_acmgseqvar_patch( ): """Test updating a acmgseqvar with invalid data.""" _ = db_session + # act: # Create acmgseqvar - response = client_user.post( + response_create = client_user.post( f"{settings.API_V1_STR}/acmgseqvar/create", json=acmgseqvar_post_data, ) - assert response.status_code == 200 # Update acmgseqvar - response = client_user.patch( + response_update = client_user.patch( f"{settings.API_V1_STR}/acmgseqvar/update", json=acmgseqvar_update_data, ) - content = response.json() - assert response.status_code == 200 + # assert: + assert response_create.status_code == 200 + content = response_update.json() + assert response_update.status_code == 200 assert content["seqvar_name"] == acmgseqvar_update_data["seqvar_name"] assert content["acmg_rank"] == acmgseqvar_update_data["acmg_rank"] assert content["user"] == str(test_user.id) @pytest.mark.anyio -@pytest.mark.parametrize("test_user, client_user", [(True, True)], indirect=True) +@pytest.mark.parametrize("test_user, client_user", [(SUPER, SUPER)], indirect=True) async def test_update_acmgseqvar_no_acmgseqvar( db_session: AsyncSession, client_user: TestClient, @@ -602,16 +661,19 @@ async def test_update_acmgseqvar_no_acmgseqvar( ): """Test updating a acmgseqvar with invalid data.""" _ = db_session + _ = test_user + # act: response = client_user.put( f"{settings.API_V1_STR}/acmgseqvar/update", json=acmgseqvar_update_data, ) + # assert: assert response.status_code == 404 assert response.json() == {"detail": "ACMG Sequence Variant not found"} @pytest.mark.anyio -@pytest.mark.parametrize("test_user, client_user", [(True, True)], indirect=True) +@pytest.mark.parametrize("test_user, client_user", [(SUPER, SUPER)], indirect=True) async def test_update_acmgseqvar_invalid_enum( db_session: AsyncSession, client_user: TestClient, @@ -621,20 +683,23 @@ async def test_update_acmgseqvar_invalid_enum( ): """Test updating a acmgseqvar with invalid enums.""" _ = db_session + _ = test_user + # act: # Create acmgseqvar - response = client_user.post( + response_create = client_user.post( f"{settings.API_V1_STR}/acmgseqvar/create", json=acmgseqvar_post_data, ) - assert response.status_code == 200 # Update acmgseqvar post_data = acmgseqvar_update_data.copy() post_data["acmg_rank"]["criterias"][0]["criteria"] = "Pppm4" - response = client_user.put( + response_update = client_user.put( f"{settings.API_V1_STR}/acmgseqvar/update", json=post_data, ) - assert response.status_code == 422 + # assert: + assert response_create.status_code == 200 + assert response_update.status_code == 422 # ------------------------------------------------------------------------------ @@ -643,7 +708,7 @@ async def test_update_acmgseqvar_invalid_enum( @pytest.mark.anyio -@pytest.mark.parametrize("test_user, client_user", [(False, False)], indirect=True) +@pytest.mark.parametrize("test_user, client_user", [(REGUL, REGUL)], indirect=True) async def test_delete_acmgseqvar_by_id( db_session: AsyncSession, client_user: TestClient, @@ -652,25 +717,26 @@ async def test_delete_acmgseqvar_by_id( ): """Test deleting a acmgseqvar by id.""" _ = db_session + _ = test_user + # act: # Create acmgseqvar - response = client_user.post( + _response_create = client_user.post( f"{settings.API_V1_STR}/acmgseqvar/create", json=acmgseqvar_post_data, ) - # Get the acmgseqvar id - response = client_user.get(f"{settings.API_V1_STR}/acmgseqvar/list") - acmgseqvar_id = response.json()[0]["id"] - - response = client_user.delete( + response_list = client_user.get(f"{settings.API_V1_STR}/acmgseqvar/list") + acmgseqvar_id = response_list.json()[0]["id"] + response_delete_by_id = client_user.delete( f"{settings.API_V1_STR}/acmgseqvar/delete-by-id?id={acmgseqvar_id}", ) - assert response.status_code == 401 - assert response.json() == {"detail": "Unauthorized"} + # assert: + assert response_delete_by_id.status_code == 401 + assert response_delete_by_id.json() == {"detail": "Unauthorized"} @pytest.mark.anyio -@pytest.mark.parametrize("test_user, client_user", [(True, True)], indirect=True) +@pytest.mark.parametrize("test_user, client_user", [(SUPER, SUPER)], indirect=True) async def test_delete_acmgseqvar_by_id_superuser( db_session: AsyncSession, client_user: TestClient, @@ -679,21 +745,21 @@ async def test_delete_acmgseqvar_by_id_superuser( ): """Test deleting a acmgseqvar by id as superuser.""" _ = db_session + # act: # Create acmgseqvar - response = client_user.post( + _response_create = client_user.post( f"{settings.API_V1_STR}/acmgseqvar/create", json=acmgseqvar_post_data, ) - # Get the acmgseqvar id - response = client_user.get(f"{settings.API_V1_STR}/acmgseqvar/list") - acmgseqvar_id = response.json()[0]["id"] - - response = client_user.delete( + response_list = client_user.get(f"{settings.API_V1_STR}/acmgseqvar/list") + acmgseqvar_id = response_list.json()[0]["id"] + response_delete_by_id = client_user.delete( f"{settings.API_V1_STR}/acmgseqvar/delete-by-id?id={acmgseqvar_id}", ) - assert response.status_code == 200 - content = response.json() + # assert: + assert response_delete_by_id.status_code == 200 + content = response_delete_by_id.json() assert content["seqvar_name"] == acmgseqvar_post_data["seqvar_name"] assert content["acmg_rank"] == acmgseqvar_post_data["acmg_rank"] assert content["user"] == str(test_user.id) @@ -703,15 +769,17 @@ async def test_delete_acmgseqvar_by_id_superuser( async def test_delete_acmgseqvar_by_id_anon(db_session: AsyncSession, client: TestClient): """Test deleting a acmgseqvar by id as anonymous user.""" _ = db_session + # act: response = client.delete( f"{settings.API_V1_STR}/acmgseqvar/delete-by-id?id={uuid.uuid4()}", ) + # assert: assert response.status_code == 401 assert response.json() == {"detail": "Unauthorized"} @pytest.mark.anyio -@pytest.mark.parametrize("test_user, client_user", [(True, True)], indirect=True) +@pytest.mark.parametrize("test_user, client_user", [(SUPER, SUPER)], indirect=True) async def test_delete_acmgseqvar_by_invalid_id( db_session: AsyncSession, client_user: TestClient, @@ -719,9 +787,12 @@ async def test_delete_acmgseqvar_by_invalid_id( ): """Test deleting a acmgseqvar by id with invalid id.""" _ = db_session + _ = test_user + # act: response = client_user.delete( f"{settings.API_V1_STR}/acmgseqvar/delete-by-id?id={uuid.uuid4()}", ) + # assert: assert response.status_code == 404 assert response.json() == {"detail": "ACMG Sequence Variant not found"} @@ -732,7 +803,7 @@ async def test_delete_acmgseqvar_by_invalid_id( @pytest.mark.anyio -@pytest.mark.parametrize("test_user, client_user", [(False, False)], indirect=True) +@pytest.mark.parametrize("test_user, client_user", [(REGUL, REGUL)], indirect=True) async def test_delete_acmgseqvar( db_session: AsyncSession, client_user: TestClient, @@ -741,30 +812,30 @@ async def test_delete_acmgseqvar( ): """Test deleting a acmgseqvar.""" _ = db_session + # act: # Create acmgseqvar - response = client_user.post( + _response_create = client_user.post( f"{settings.API_V1_STR}/acmgseqvar/create", json=acmgseqvar_post_data, ) - - response = client_user.delete( + response_delete = client_user.delete( f"{settings.API_V1_STR}/acmgseqvar/delete?seqvar={acmgseqvar_post_data['seqvar_name']}", ) - assert response.status_code == 200 - content = response.json() - assert content["seqvar_name"] == acmgseqvar_post_data["seqvar_name"] - assert content["acmg_rank"] == acmgseqvar_post_data["acmg_rank"] - assert content["user"] == str(test_user.id) - # Verify that the acmgseqvar is deleted - response = client_user.get( + responset_get = client_user.get( f"{settings.API_V1_STR}/acmgseqvar/get?seqvar={acmgseqvar_post_data['seqvar_name']}", ) - assert response.status_code == 404 + # assert: + assert response_delete.status_code == 200 + content = response_delete.json() + assert content["seqvar_name"] == acmgseqvar_post_data["seqvar_name"] + assert content["acmg_rank"] == acmgseqvar_post_data["acmg_rank"] + assert content["user"] == str(test_user.id) + assert responset_get.status_code == 404 @pytest.mark.anyio -@pytest.mark.parametrize("test_user, client_user", [(True, True)], indirect=True) +@pytest.mark.parametrize("test_user, client_user", [(SUPER, SUPER)], indirect=True) async def test_delete_acmgseqvar_superuser( db_session: AsyncSession, client_user: TestClient, @@ -773,41 +844,43 @@ async def test_delete_acmgseqvar_superuser( ): """Test deleting a acmgseqvar as superuser.""" _ = db_session + # act: # Create acmgseqvar - response = client_user.post( + _response_create = client_user.post( f"{settings.API_V1_STR}/acmgseqvar/create", json=acmgseqvar_post_data, ) - - response = client_user.delete( + response_delete = client_user.delete( f"{settings.API_V1_STR}/acmgseqvar/delete?seqvar={acmgseqvar_post_data['seqvar_name']}", ) - assert response.status_code == 200 - content = response.json() - assert content["seqvar_name"] == acmgseqvar_post_data["seqvar_name"] - assert content["acmg_rank"] == acmgseqvar_post_data["acmg_rank"] - assert content["user"] == str(test_user.id) - # Verify that the acmgseqvar is deleted - response = client_user.get( + response_get = client_user.get( f"{settings.API_V1_STR}/acmgseqvar/get?seqvar={acmgseqvar_post_data['seqvar_name']}", ) - assert response.status_code == 404 + # assert: + assert response_delete.status_code == 200 + content = response_delete.json() + assert content["seqvar_name"] == acmgseqvar_post_data["seqvar_name"] + assert content["acmg_rank"] == acmgseqvar_post_data["acmg_rank"] + assert content["user"] == str(test_user.id) + assert response_get.status_code == 404 @pytest.mark.anyio async def test_delete_acmgseqvar_anon(db_session: AsyncSession, client: TestClient): """Test deleting a acmgseqvar as anonymous user.""" _ = db_session + # act: response = client.delete( f"{settings.API_V1_STR}/acmgseqvar/delete?seqvar={uuid.uuid4()}", ) + # assert: assert response.status_code == 401 assert response.json() == {"detail": "Unauthorized"} @pytest.mark.anyio -@pytest.mark.parametrize("test_user, client_user", [(True, True)], indirect=True) +@pytest.mark.parametrize("test_user, client_user", [(SUPER, SUPER)], indirect=True) async def test_delete_acmgseqvar_no_acmgseqvar( db_session: AsyncSession, client_user: TestClient, @@ -815,8 +888,11 @@ async def test_delete_acmgseqvar_no_acmgseqvar( ): """Test deleting a acmgseqvar with invalid data.""" _ = db_session + _ = test_user + # act: response = client_user.delete( f"{settings.API_V1_STR}/acmgseqvar/delete?seqvar=invalid", ) + # assert: assert response.status_code == 404 assert response.json() == {"detail": "ACMG Sequence Variant not found"} diff --git a/backend/tests/api/api_v1/test_adminmsgs.py b/backend/tests/api/api_v1/test_adminmsgs.py index 1f0e9dde..52f27c11 100644 --- a/backend/tests/api/api_v1/test_adminmsgs.py +++ b/backend/tests/api/api_v1/test_adminmsgs.py @@ -9,6 +9,8 @@ async def test_adminmsgs_list(db_session: AsyncSession, client: TestClient): """Test proxying to annonars backend.""" _ = db_session # via ``get_db()`` dependency injection + # act: response = client.get(f"{settings.API_V1_STR}/adminmsgs/") + # assert: assert response.status_code == 200 assert response.json() == [] diff --git a/backend/tests/api/api_v1/test_bookmarks.py b/backend/tests/api/api_v1/test_bookmarks.py index f4d44a4a..e6a986e8 100644 --- a/backend/tests/api/api_v1/test_bookmarks.py +++ b/backend/tests/api/api_v1/test_bookmarks.py @@ -6,6 +6,12 @@ from app.core.config import settings from app.models.user import User +from tests.conftest import UserChoice + +#: Shortcut for regular user. +REGUL = UserChoice.REGULAR +#: Shortcut for superuser. +SUPER = UserChoice.SUPERUSER # ------------------------------------------------------------------------------ # /api/v1/bookmarks/create @@ -13,7 +19,7 @@ @pytest.mark.anyio -@pytest.mark.parametrize("test_user, client_user", [(False, False)], indirect=True) +@pytest.mark.parametrize("test_user, client_user", [(REGUL, REGUL)], indirect=True) async def test_create_bookmark( db_session: AsyncSession, client_user: TestClient, @@ -21,10 +27,12 @@ async def test_create_bookmark( ): """Test creating a bookmark as regular user.""" _ = db_session + # act: response = client_user.post( f"{settings.API_V1_STR}/bookmarks/create/", json={"obj_type": "gene", "obj_id": "exampleGene"}, ) + # assert: assert response.status_code == 200 assert response.json()["obj_type"] == "gene" assert response.json()["obj_id"] == "exampleGene" @@ -32,7 +40,7 @@ async def test_create_bookmark( @pytest.mark.anyio -@pytest.mark.parametrize("test_user, client_user", [(True, True)], indirect=True) +@pytest.mark.parametrize("test_user, client_user", [(SUPER, SUPER)], indirect=True) async def test_create_bookmark_superuser( db_session: AsyncSession, client_user: TestClient, @@ -40,10 +48,12 @@ async def test_create_bookmark_superuser( ): """Test creating a bookmark as superuser.""" _ = db_session + # act: response = client_user.post( f"{settings.API_V1_STR}/bookmarks/create/", json={"obj_type": "gene", "obj_id": "exampleGene"}, ) + # assert: assert response.status_code == 200 assert response.json()["obj_type"] == "gene" assert response.json()["obj_id"] == "exampleGene" @@ -54,16 +64,18 @@ async def test_create_bookmark_superuser( async def test_create_bookmark_anon(db_session: AsyncSession, client: TestClient): """Test creating a bookmark as anonymous user.""" _ = db_session + # act: response = client.post( f"{settings.API_V1_STR}/bookmarks/create/", json={"obj_type": "gene", "obj_id": "exampleGene"}, ) + # assert: assert response.status_code == 401 assert response.json() == {"detail": "Unauthorized"} @pytest.mark.anyio -@pytest.mark.parametrize("test_user, client_user", [(True, True)], indirect=True) +@pytest.mark.parametrize("test_user, client_user", [(SUPER, SUPER)], indirect=True) async def test_create_bookmark_invalid_data( db_session: AsyncSession, client_user: TestClient, @@ -71,10 +83,13 @@ async def test_create_bookmark_invalid_data( ): """Test creating a bookmark with invalid data.""" _ = db_session + _ = test_user + # act: response = client_user.post( f"{settings.API_V1_STR}/bookmarks/create/", json={"obj_type": "invalid", "obj_id": "invalid"}, ) + # assert: assert response.status_code == 422 @@ -84,7 +99,7 @@ async def test_create_bookmark_invalid_data( @pytest.mark.anyio -@pytest.mark.parametrize("test_user, client_user", [(False, False)], indirect=True) +@pytest.mark.parametrize("test_user, client_user", [(REGUL, REGUL)], indirect=True) async def test_list_all_bookmarks( db_session: AsyncSession, client_user: TestClient, @@ -92,20 +107,22 @@ async def test_list_all_bookmarks( ): """Test listing all bookmarks as regular user.""" _ = db_session + _ = test_user + # act: # Create a bookmark - response = client_user.post( + response_create = client_user.post( f"{settings.API_V1_STR}/bookmarks/create/", json={"obj_type": "gene", "obj_id": "exampleGene"}, ) - assert response.status_code == 200 - - response = client_user.get(f"{settings.API_V1_STR}/bookmarks/list-all/") - assert response.status_code == 401 - assert response.json() == {"detail": "Unauthorized"} + response_list_all = client_user.get(f"{settings.API_V1_STR}/bookmarks/list-all/") + # assert:s + assert response_create.status_code == 200 + assert response_list_all.status_code == 401 + assert response_list_all.json() == {"detail": "Unauthorized"} @pytest.mark.anyio -@pytest.mark.parametrize("test_user, client_user", [(True, True)], indirect=True) +@pytest.mark.parametrize("test_user, client_user", [(SUPER, SUPER)], indirect=True) async def test_list_all_bookmarks_superuser( db_session: AsyncSession, client_user: TestClient, @@ -113,31 +130,34 @@ async def test_list_all_bookmarks_superuser( ): """Test listing all bookmarks as superuser.""" _ = db_session + # act: # Create a bookmark - response = client_user.post( + response_create = client_user.post( f"{settings.API_V1_STR}/bookmarks/create/", json={"obj_type": "gene", "obj_id": "exampleGene"}, ) - assert response.status_code == 200 - - response = client_user.get(f"{settings.API_V1_STR}/bookmarks/list-all/") - assert response.status_code == 200 - assert response.json()[0]["obj_type"] == "gene" - assert response.json()[0]["obj_id"] == "exampleGene" - assert response.json()[0]["user"] == str(test_user.id) + response_list_all = client_user.get(f"{settings.API_V1_STR}/bookmarks/list-all/") + # assert: + assert response_create.status_code == 200 + assert response_list_all.status_code == 200 + assert response_list_all.json()[0]["obj_type"] == "gene" + assert response_list_all.json()[0]["obj_id"] == "exampleGene" + assert response_list_all.json()[0]["user"] == str(test_user.id) @pytest.mark.anyio async def test_list_all_bookmarks_anon(db_session: AsyncSession, client: TestClient): """Test listing all bookmarks as anonymous user.""" _ = db_session + # act: response = client.get(f"{settings.API_V1_STR}/bookmarks/list-all/") + # assert: assert response.status_code == 401 assert response.json() == {"detail": "Unauthorized"} @pytest.mark.anyio -@pytest.mark.parametrize("test_user, client_user", [(True, True)], indirect=True) +@pytest.mark.parametrize("test_user, client_user", [(SUPER, SUPER)], indirect=True) async def test_list_all_no_bookmarks( db_session: AsyncSession, client_user: TestClient, @@ -145,7 +165,10 @@ async def test_list_all_no_bookmarks( ): """Test listing all bookmarks as superuser when there are no bookmarks.""" _ = db_session + _ = test_user + # act: response = client_user.get(f"{settings.API_V1_STR}/bookmarks/list-all/") + # assert: assert response.status_code == 200 assert response.json() == [] @@ -156,61 +179,79 @@ async def test_list_all_no_bookmarks( @pytest.mark.anyio -@pytest.mark.parametrize("test_user, client_user", [(False, False)], indirect=True) +@pytest.mark.parametrize("test_user, client_user", [(REGUL, REGUL)], indirect=True) async def test_get_bookmark_by_id( db_session: AsyncSession, client_user: TestClient, test_user: User, ): """Test getting a bookmark by id as regular user.""" + _ = db_session + _ = test_user + # arrange: bookmark_id = uuid.uuid4() + # act: response = client_user.get(f"{settings.API_V1_STR}/bookmarks/get-by-id?id={bookmark_id}") + # assert: assert response.status_code == 401 # Forbidden access should be 403 @pytest.mark.anyio -@pytest.mark.parametrize("test_user, client_user", [(True, True)], indirect=True) +@pytest.mark.parametrize("test_user, client_user", [(SUPER, SUPER)], indirect=True) async def test_get_bookmark_by_id_superuser( db_session: AsyncSession, client_user: TestClient, test_user: User, ): """Test getting a bookmark by id as superuser.""" + _ = db_session + _ = test_user + # act: # Create a bookmark - response = client_user.post( + response_create = client_user.post( f"{settings.API_V1_STR}/bookmarks/create/", json={"obj_type": "gene", "obj_id": "exampleGene"}, ) - assert response.status_code == 200 # Get the bookmark id - response = client_user.get(f"{settings.API_V1_STR}/bookmarks/list/") - bookmark_id = response.json()[0]["id"] - - response = client_user.get(f"{settings.API_V1_STR}/bookmarks/get-by-id?id={bookmark_id}") - assert response.status_code == 200 - assert response.json()["id"] == bookmark_id + response_list = client_user.get(f"{settings.API_V1_STR}/bookmarks/list/") + bookmark_id = response_list.json()[0]["id"] + response_get_by_id = client_user.get( + f"{settings.API_V1_STR}/bookmarks/get-by-id?id={bookmark_id}" + ) + # assert: + assert response_create.status_code == 200 + assert response_get_by_id.status_code == 200 + assert response_get_by_id.json()["id"] == bookmark_id @pytest.mark.anyio async def test_get_bookmark_by_id_anon(db_session: AsyncSession, client: TestClient): """Test getting a bookmark by id as anonymous user.""" _ = db_session + # arrange: bookmark_id = uuid.uuid4() + # act: response = client.get(f"{settings.API_V1_STR}/bookmarks/get-by-id?id={bookmark_id}/") + # assert: assert response.status_code == 401 assert response.json() == {"detail": "Unauthorized"} @pytest.mark.anyio -@pytest.mark.parametrize("test_user, client_user", [(True, True)], indirect=True) +@pytest.mark.parametrize("test_user, client_user", [(SUPER, SUPER)], indirect=True) async def test_get_bookmark_by_invalid_id( db_session: AsyncSession, client_user: TestClient, test_user: User, ): """Test getting a bookmark by invalid id as superuser.""" + _ = db_session + _ = test_user + # arrange: bookmark_id = uuid.uuid4() # Invalid id + # act: response = client_user.get(f"{settings.API_V1_STR}/bookmarks/get-by-id?id={bookmark_id}") + # assert: assert response.status_code == 404 assert response.json() == {"detail": "Bookmark not found"} @@ -221,81 +262,98 @@ async def test_get_bookmark_by_invalid_id( @pytest.mark.anyio -@pytest.mark.parametrize("test_user, client_user", [(False, False)], indirect=True) +@pytest.mark.parametrize("test_user, client_user", [(REGUL, REGUL)], indirect=True) async def test_delete_bookmark_by_id( db_session: AsyncSession, client_user: TestClient, test_user: User, ): """Test deleting a bookmark by id as regular user.""" + _ = db_session + _ = test_user + # act: # Create a bookmark - response = client_user.post( + response_create = client_user.post( f"{settings.API_V1_STR}/bookmarks/create/", json={"obj_type": "gene", "obj_id": "exampleGene"}, ) - assert response.status_code == 200 # Get the bookmark id - response = client_user.get(f"{settings.API_V1_STR}/bookmarks/list/") - bookmark_id = response.json()[0]["id"] - - response = client_user.delete(f"{settings.API_V1_STR}/bookmarks/delete-by-id?id={bookmark_id}") - assert response.status_code == 401 - assert response.json() == {"detail": "Unauthorized"} - + response_list = client_user.get(f"{settings.API_V1_STR}/bookmarks/list/") + bookmark_id = response_list.json()[0]["id"] + response_delete_by_id = client_user.delete( + f"{settings.API_V1_STR}/bookmarks/delete-by-id?id={bookmark_id}" + ) # Verify that the bookmark was not deleted - response = client_user.get(f"{settings.API_V1_STR}/bookmarks/list/") - assert response.status_code == 200 - assert response.json()[0]["id"] == bookmark_id + response_list = client_user.get(f"{settings.API_V1_STR}/bookmarks/list/") + # assert: + assert response_create.status_code == 200 + assert response_delete_by_id.status_code == 401 + assert response_delete_by_id.json() == {"detail": "Unauthorized"} + assert response_list.status_code == 200 + assert response_list.json()[0]["id"] == bookmark_id @pytest.mark.anyio -@pytest.mark.parametrize("test_user, client_user", [(True, True)], indirect=True) +@pytest.mark.parametrize("test_user, client_user", [(SUPER, SUPER)], indirect=True) async def test_delete_bookmark_by_id_superuser( db_session: AsyncSession, client_user: TestClient, test_user: User, ): """Test deleting a bookmark by id as superuser.""" + _ = db_session + _ = test_user + # act: # Create a bookmark - response = client_user.post( + response_creat = client_user.post( f"{settings.API_V1_STR}/bookmarks/create/", json={"obj_type": "gene", "obj_id": "exampleGene"}, ) - assert response.status_code == 200 # Get the bookmark id - response = client_user.get(f"{settings.API_V1_STR}/bookmarks/list/") - bookmark_id = response.json()[0]["id"] - - response = client_user.delete(f"{settings.API_V1_STR}/bookmarks/delete-by-id?id={bookmark_id}") - assert response.status_code == 200 - assert response.json()["id"] == bookmark_id - + response_list = client_user.get(f"{settings.API_V1_STR}/bookmarks/list/") + bookmark_id = response_list.json()[0]["id"] + response_delete = client_user.delete( + f"{settings.API_V1_STR}/bookmarks/delete-by-id?id={bookmark_id}" + ) # Verify that the bookmark is indeed deleted - response = client_user.get(f"{settings.API_V1_STR}/bookmarks/get-by-id?id={bookmark_id}") - assert response.status_code == 404 # Not Found + response_get_by_id = client_user.get( + f"{settings.API_V1_STR}/bookmarks/get-by-id?id={bookmark_id}" + ) + # assert: + assert response_creat.status_code == 200 + assert response_delete.status_code == 200 + assert response_delete.json()["id"] == bookmark_id + assert response_get_by_id.status_code == 404 # Not Found @pytest.mark.anyio async def test_delete_bookmark_by_id_anon(db_session: AsyncSession, client: TestClient): """Test deleting a bookmark by id as anonymous user.""" _ = db_session + # arrange: bookmark_id = uuid.uuid4() + # act: response = client.delete(f"{settings.API_V1_STR}/bookmarks/delete-by-id?id={bookmark_id}/") + # assert: assert response.status_code == 401 assert response.json() == {"detail": "Unauthorized"} @pytest.mark.anyio -@pytest.mark.parametrize("test_user, client_user", [(True, True)], indirect=True) +@pytest.mark.parametrize("test_user, client_user", [(SUPER, SUPER)], indirect=True) async def test_delete_bookmark_by_invalid_id( db_session: AsyncSession, client_user: TestClient, test_user: User, ): """Test deleting a bookmark by invalid id as superuser.""" + _ = db_session + _ = test_user + # arrange: bookmark_id = uuid.uuid4() - + # act: response = client_user.delete(f"{settings.API_V1_STR}/bookmarks/delete-by-id?id={bookmark_id}") + # assert: assert response.status_code == 404 assert response.json() == {"detail": "Bookmark not found"} @@ -306,7 +364,7 @@ async def test_delete_bookmark_by_invalid_id( @pytest.mark.anyio -@pytest.mark.parametrize("test_user, client_user", [(False, False)], indirect=True) +@pytest.mark.parametrize("test_user, client_user", [(REGUL, REGUL)], indirect=True) async def test_list_bookmarks( db_session: AsyncSession, client_user: TestClient, @@ -314,22 +372,23 @@ async def test_list_bookmarks( ): """Test listing bookmarks as regular user.""" _ = db_session + # arrange: # Create a bookmark - response = client_user.post( + response_create = client_user.post( f"{settings.API_V1_STR}/bookmarks/create/", json={"obj_type": "gene", "obj_id": "exampleGene"}, ) - assert response.status_code == 200 - - response = client_user.get(f"{settings.API_V1_STR}/bookmarks/list/") - assert response.status_code == 200 - assert response.json()[0]["obj_type"] == "gene" - assert response.json()[0]["obj_id"] == "exampleGene" - assert response.json()[0]["user"] == str(test_user.id) + response_list = client_user.get(f"{settings.API_V1_STR}/bookmarks/list/") + # assert: + assert response_create.status_code == 200 + assert response_list.status_code == 200 + assert response_list.json()[0]["obj_type"] == "gene" + assert response_list.json()[0]["obj_id"] == "exampleGene" + assert response_list.json()[0]["user"] == str(test_user.id) @pytest.mark.anyio -@pytest.mark.parametrize("test_user, client_user", [(True, True)], indirect=True) +@pytest.mark.parametrize("test_user, client_user", [(SUPER, SUPER)], indirect=True) async def test_list_bookmarks_superuser( db_session: AsyncSession, client_user: TestClient, @@ -337,38 +396,43 @@ async def test_list_bookmarks_superuser( ): """Test listing bookmarks as superuser.""" _ = db_session + # act: # Create a bookmark - response = client_user.post( + response_create = client_user.post( f"{settings.API_V1_STR}/bookmarks/create/", json={"obj_type": "gene", "obj_id": "exampleGene"}, ) - assert response.status_code == 200 - - response = client_user.get(f"{settings.API_V1_STR}/bookmarks/list/") - assert response.status_code == 200 - assert response.json()[0]["obj_type"] == "gene" - assert response.json()[0]["obj_id"] == "exampleGene" - assert response.json()[0]["user"] == str(test_user.id) + response_list = client_user.get(f"{settings.API_V1_STR}/bookmarks/list/") + # assert: + assert response_create.status_code == 200 + assert response_list.status_code == 200 + assert response_list.json()[0]["obj_type"] == "gene" + assert response_list.json()[0]["obj_id"] == "exampleGene" + assert response_list.json()[0]["user"] == str(test_user.id) @pytest.mark.anyio async def test_list_bookmarks_anon(db_session: AsyncSession, client: TestClient): """Test listing bookmarks as anonymous user.""" _ = db_session + # act: response = client.get(f"{settings.API_V1_STR}/bookmarks/list/") + # assert: assert response.status_code == 401 assert response.json() == {"detail": "Unauthorized"} @pytest.mark.anyio -@pytest.mark.parametrize("test_user, client_user", [(True, True)], indirect=True) +@pytest.mark.parametrize("test_user, client_user", [(SUPER, SUPER)], indirect=True) async def test_list_no_bookmarks( db_session: AsyncSession, client_user: TestClient, ): """Test listing bookmarks as superuser when there are no bookmarks.""" _ = db_session + # act: response = client_user.get(f"{settings.API_V1_STR}/bookmarks/list/") + # assert: assert response.status_code == 200 assert response.json() == [] @@ -379,7 +443,7 @@ async def test_list_no_bookmarks( @pytest.mark.anyio -@pytest.mark.parametrize("test_user, client_user", [(False, False)], indirect=True) +@pytest.mark.parametrize("test_user, client_user", [(REGUL, REGUL)], indirect=True) async def test_get_bookmark( db_session: AsyncSession, client_user: TestClient, @@ -387,24 +451,25 @@ async def test_get_bookmark( ): """Test getting a bookmark as regular user.""" _ = db_session + # act: # Create a bookmark - response = client_user.post( + response_create = client_user.post( f"{settings.API_V1_STR}/bookmarks/create/", json={"obj_type": "gene", "obj_id": "exampleGene"}, ) - assert response.status_code == 200 - - response = client_user.get( + response_get = client_user.get( f"{settings.API_V1_STR}/bookmarks/get?obj_type=gene&obj_id=exampleGene" ) - assert response.status_code == 200 - assert response.json()["obj_type"] == "gene" - assert response.json()["obj_id"] == "exampleGene" - assert response.json()["user"] == str(test_user.id) + # assert: + assert response_create.status_code == 200 + assert response_get.status_code == 200 + assert response_get.json()["obj_type"] == "gene" + assert response_get.json()["obj_id"] == "exampleGene" + assert response_get.json()["user"] == str(test_user.id) @pytest.mark.anyio -@pytest.mark.parametrize("test_user, client_user", [(True, True)], indirect=True) +@pytest.mark.parametrize("test_user, client_user", [(SUPER, SUPER)], indirect=True) async def test_get_bookmark_superuser( db_session: AsyncSession, client_user: TestClient, @@ -412,42 +477,47 @@ async def test_get_bookmark_superuser( ): """Test getting a bookmark as superuser.""" _ = db_session + # act: # Create a bookmark - response = client_user.post( + response_create = client_user.post( f"{settings.API_V1_STR}/bookmarks/create/", json={"obj_type": "gene", "obj_id": "exampleGene"}, ) - assert response.status_code == 200 - - response = client_user.get( + response_get = client_user.get( f"{settings.API_V1_STR}/bookmarks/get?obj_type=gene&obj_id=exampleGene" ) - assert response.status_code == 200 - assert response.json()["obj_type"] == "gene" - assert response.json()["obj_id"] == "exampleGene" - assert response.json()["user"] == str(test_user.id) + # assert: + assert response_create.status_code == 200 + assert response_get.status_code == 200 + assert response_get.json()["obj_type"] == "gene" + assert response_get.json()["obj_id"] == "exampleGene" + assert response_get.json()["user"] == str(test_user.id) @pytest.mark.anyio async def test_get_bookmark_anon(db_session: AsyncSession, client: TestClient): """Test getting a bookmark as anonymous user.""" _ = db_session + # act: response = client.get(f"{settings.API_V1_STR}/bookmarks/get?obj_type=gene&obj_id=exampleGene") + # assert: assert response.status_code == 401 assert response.json() == {"detail": "Unauthorized"} @pytest.mark.anyio -@pytest.mark.parametrize("test_user, client_user", [(True, True)], indirect=True) +@pytest.mark.parametrize("test_user, client_user", [(SUPER, SUPER)], indirect=True) async def test_get_no_bookmarks( db_session: AsyncSession, client_user: TestClient, ): """Test getting a bookmark as superuser when there are no bookmarks.""" _ = db_session + # act: response = client_user.get( f"{settings.API_V1_STR}/bookmarks/get?obj_type=gene&obj_id=exampleGene" ) + # assert: assert response.status_code == 404 assert response.json() == {"detail": "Bookmark not found"} @@ -458,7 +528,7 @@ async def test_get_no_bookmarks( @pytest.mark.anyio -@pytest.mark.parametrize("test_user, client_user", [(False, False)], indirect=True) +@pytest.mark.parametrize("test_user, client_user", [(REGUL, REGUL)], indirect=True) async def test_delete_bookmark( db_session: AsyncSession, client_user: TestClient, @@ -466,27 +536,27 @@ async def test_delete_bookmark( ): """Test deleting a bookmark as regular user.""" _ = db_session + # act: # Create a bookmark - response = client_user.post( + response_create = client_user.post( f"{settings.API_V1_STR}/bookmarks/create/", json={"obj_type": "gene", "obj_id": "exampleGene"}, ) - - response = client_user.delete( + response_delete = client_user.delete( f"{settings.API_V1_STR}/bookmarks/delete?obj_type=gene&obj_id=exampleGene" ) - assert response.status_code == 200 - assert response.json()["obj_type"] == "gene" - # Verify that the bookmark is indeed deleted - response = client_user.get( + response_get = client_user.get( f"{settings.API_V1_STR}/bookmarks/get?obj_type=gene&obj_id=exampleGene" ) - assert response.status_code == 404 + # assert: + assert response_delete.status_code == 200 + assert response_delete.json()["obj_type"] == "gene" + assert response_get.status_code == 404 @pytest.mark.anyio -@pytest.mark.parametrize("test_user, client_user", [(True, True)], indirect=True) +@pytest.mark.parametrize("test_user, client_user", [(SUPER, SUPER)], indirect=True) async def test_delete_bookmark_superuser( db_session: AsyncSession, client_user: TestClient, @@ -494,46 +564,50 @@ async def test_delete_bookmark_superuser( ): """Test deleting a bookmark as superuser.""" _ = db_session + # act: # Create a bookmark - response = client_user.post( + response_create = client_user.post( f"{settings.API_V1_STR}/bookmarks/create/", json={"obj_type": "gene", "obj_id": "exampleGene"}, ) - - response = client_user.delete( + response_delete = client_user.delete( f"{settings.API_V1_STR}/bookmarks/delete?obj_type=gene&obj_id=exampleGene" ) - assert response.status_code == 200 - assert response.json()["obj_type"] == "gene" - # Verify that the bookmark is indeed deleted - response = client_user.get( + response_get = client_user.get( f"{settings.API_V1_STR}/bookmarks/get?obj_type=gene&obj_id=exampleGene" ) - assert response.status_code == 404 + # assert: + assert response_delete.status_code == 200 + assert response_delete.json()["obj_type"] == "gene" + assert response_get.status_code == 404 @pytest.mark.anyio async def test_delete_bookmark_anon(db_session: AsyncSession, client: TestClient): """Test deleting a bookmark as anonymous user.""" _ = db_session + # act: response = client.delete( f"{settings.API_V1_STR}/bookmarks/delete?obj_type=gene&obj_id=exampleGene" ) + # assert: assert response.status_code == 401 assert response.json() == {"detail": "Unauthorized"} @pytest.mark.anyio -@pytest.mark.parametrize("test_user, client_user", [(True, True)], indirect=True) +@pytest.mark.parametrize("test_user, client_user", [(SUPER, SUPER)], indirect=True) async def test_delete_no_bookmarks( db_session: AsyncSession, client_user: TestClient, ): """Test deleting a bookmark as superuser when there are no bookmarks.""" _ = db_session + # act: response = client_user.delete( f"{settings.API_V1_STR}/bookmarks/delete?obj_type=gene&obj_id=exampleGene" ) + # assert: assert response.status_code == 404 assert response.json() == {"detail": "Bookmark not found"} diff --git a/backend/tests/api/api_v1/test_caseinfo.py b/backend/tests/api/api_v1/test_caseinfo.py index bf939067..ae6b8dd5 100644 --- a/backend/tests/api/api_v1/test_caseinfo.py +++ b/backend/tests/api/api_v1/test_caseinfo.py @@ -6,6 +6,12 @@ from app.core.config import settings from app.models.user import User +from tests.conftest import UserChoice + +#: Shortcut for regular user. +REGUL = UserChoice.REGULAR +#: Shortcut for superuser. +SUPER = UserChoice.SUPERUSER # ------------------------------------------------------------------------------ # /api/v1/caseinfo/create @@ -13,7 +19,7 @@ @pytest.mark.anyio -@pytest.mark.parametrize("test_user, client_user", [(False, False)], indirect=True) +@pytest.mark.parametrize("test_user, client_user", [(REGUL, REGUL)], indirect=True) async def test_create_caseinfo( db_session: AsyncSession, client_user: TestClient, @@ -21,6 +27,7 @@ async def test_create_caseinfo( ): """Test creating a caseinfo as regular user.""" _ = db_session + # act: response = client_user.post( f"{settings.API_V1_STR}/caseinfo/create/", json={ @@ -36,6 +43,7 @@ async def test_create_caseinfo( "family_segregation": True, }, ) + # assert: assert response.status_code == 200 assert response.json()["user"] == str(test_user.id) assert response.json()["pseudonym"] == "test1" @@ -51,7 +59,7 @@ async def test_create_caseinfo( @pytest.mark.anyio -@pytest.mark.parametrize("test_user, client_user", [(True, True)], indirect=True) +@pytest.mark.parametrize("test_user, client_user", [(SUPER, SUPER)], indirect=True) async def test_create_caseinfo_superuser( db_session: AsyncSession, client_user: TestClient, @@ -59,10 +67,12 @@ async def test_create_caseinfo_superuser( ): """Test creating a caseinfo as superuser.""" _ = db_session + # act: response = client_user.post( f"{settings.API_V1_STR}/caseinfo/create/", json={"pseudonym": "type1", "age_of_onset_month": 20}, ) + # assert: assert response.status_code == 200 assert response.json()["pseudonym"] == "type1" assert response.json()["age_of_onset_month"] == 20 @@ -73,16 +83,18 @@ async def test_create_caseinfo_superuser( async def test_create_caseinfo_anon(db_session: AsyncSession, client: TestClient): """Test creating a caseinfo as anonymous user.""" _ = db_session + # act: response = client.post( f"{settings.API_V1_STR}/caseinfo/create/", json={"pseudonym": "type1", "age_of_onset_month": 20}, ) + # assert: assert response.status_code == 401 assert response.json() == {"detail": "Unauthorized"} @pytest.mark.anyio -@pytest.mark.parametrize("test_user, client_user", [(True, True)], indirect=True) +@pytest.mark.parametrize("test_user, client_user", [(SUPER, SUPER)], indirect=True) async def test_create_caseinfo_invalid_data( db_session: AsyncSession, client_user: TestClient, @@ -90,15 +102,17 @@ async def test_create_caseinfo_invalid_data( ): """Test creating a caseinfo with invalid data.""" _ = db_session + # act: response = client_user.post( f"{settings.API_V1_STR}/caseinfo/create/", json={"pseudonym": 33, "age_of_onset_month": [20]}, ) + # assert: assert response.status_code == 422 @pytest.mark.anyio -@pytest.mark.parametrize("test_user, client_user", [(True, True)], indirect=True) +@pytest.mark.parametrize("test_user, client_user", [(SUPER, SUPER)], indirect=True) async def test_create_caseinfo_invalid_enums( db_session: AsyncSession, client_user: TestClient, @@ -106,15 +120,17 @@ async def test_create_caseinfo_invalid_enums( ): """Test creating a caseinfo with invalid enums.""" _ = db_session + # act: response = client_user.post( f"{settings.API_V1_STR}/caseinfo/create/", json={"pseudonym": "test1", "inheritance": "invalid_inheritance"}, ) + # assert: assert response.status_code == 422 @pytest.mark.anyio -@pytest.mark.parametrize("test_user, client_user", [(True, True)], indirect=True) +@pytest.mark.parametrize("test_user, client_user", [(SUPER, SUPER)], indirect=True) async def test_create_caseinfo_invalid_terms( db_session: AsyncSession, client_user: TestClient, @@ -122,10 +138,12 @@ async def test_create_caseinfo_invalid_terms( ): """Test creating a caseinfo with invalid terms.""" _ = db_session + # act: response = client_user.post( f"{settings.API_V1_STR}/caseinfo/create/", json={"pseudonym": "test1", "diseases": [{"omim_id_invalid": "string"}]}, ) + # assert: assert response.status_code == 422 @@ -135,7 +153,7 @@ async def test_create_caseinfo_invalid_terms( @pytest.mark.anyio -@pytest.mark.parametrize("test_user, client_user", [(False, False)], indirect=True) +@pytest.mark.parametrize("test_user, client_user", [(REGUL, REGUL)], indirect=True) async def test_list_all_caseinfos( db_session: AsyncSession, client_user: TestClient, @@ -143,20 +161,20 @@ async def test_list_all_caseinfos( ): """Test listing all caseinfos as regular user.""" _ = db_session - # Create caseinfo - response = client_user.post( + # act: + response_create = client_user.post( f"{settings.API_V1_STR}/caseinfo/create/", json={"pseudonym": "test1", "age_of_onset_month": 20}, ) - assert response.status_code == 200 - - response = client_user.get(f"{settings.API_V1_STR}/caseinfo/list-all/") - assert response.status_code == 401 - assert response.json() == {"detail": "Unauthorized"} + response_list_all = client_user.get(f"{settings.API_V1_STR}/caseinfo/list-all/") + # assert: + assert response_create.status_code == 200 + assert response_list_all.status_code == 401 + assert response_list_all.json() == {"detail": "Unauthorized"} @pytest.mark.anyio -@pytest.mark.parametrize("test_user, client_user", [(True, True)], indirect=True) +@pytest.mark.parametrize("test_user, client_user", [(SUPER, SUPER)], indirect=True) async def test_list_all_caseinfos_superuser( db_session: AsyncSession, client_user: TestClient, @@ -164,31 +182,33 @@ async def test_list_all_caseinfos_superuser( ): """Test listing all caseinfos as superuser.""" _ = db_session - # Create caseifo - response = client_user.post( + # act: + response_create = client_user.post( f"{settings.API_V1_STR}/caseinfo/create/", json={"pseudonym": "test1", "age_of_onset_month": 20}, ) - assert response.status_code == 200 - - response = client_user.get(f"{settings.API_V1_STR}/caseinfo/list-all/") - assert response.status_code == 200 - assert response.json()[0]["pseudonym"] == "test1" - assert response.json()[0]["age_of_onset_month"] == 20 - assert response.json()[0]["user"] == str(test_user.id) + response_list_all = client_user.get(f"{settings.API_V1_STR}/caseinfo/list-all/") + # assert: + assert response_create.status_code == 200 + assert response_list_all.status_code == 200 + assert response_list_all.json()[0]["pseudonym"] == "test1" + assert response_list_all.json()[0]["age_of_onset_month"] == 20 + assert response_list_all.json()[0]["user"] == str(test_user.id) @pytest.mark.anyio async def test_list_all_caseinfos_anon(db_session: AsyncSession, client: TestClient): """Test listing all caseinfos as anonymous user.""" _ = db_session + # act: response = client.get(f"{settings.API_V1_STR}/caseinfo/list-all/") + # assert: assert response.status_code == 401 assert response.json() == {"detail": "Unauthorized"} @pytest.mark.anyio -@pytest.mark.parametrize("test_user, client_user", [(True, True)], indirect=True) +@pytest.mark.parametrize("test_user, client_user", [(SUPER, SUPER)], indirect=True) async def test_list_all_no_caseinfos( db_session: AsyncSession, client_user: TestClient, @@ -196,7 +216,10 @@ async def test_list_all_no_caseinfos( ): """Test listing all caseinfos with no caseinfos.""" _ = db_session + _ = test_user + # act: response = client_user.get(f"{settings.API_V1_STR}/caseinfo/list-all/") + # assert: assert response.status_code == 200 assert response.json() == [] @@ -207,20 +230,25 @@ async def test_list_all_no_caseinfos( @pytest.mark.anyio -@pytest.mark.parametrize("test_user, client_user", [(False, False)], indirect=True) +@pytest.mark.parametrize("test_user, client_user", [(REGUL, REGUL)], indirect=True) async def test_get_caseinfo_by_id( db_session: AsyncSession, client_user: TestClient, test_user: User, ): """Test getting a caseinfo by id as regular user.""" + _ = db_session + _ = test_user + # arrange: caseinfo_id = uuid.uuid4() + # act: response = client_user.get(f"{settings.API_V1_STR}/caseinfo/get-by-id?id={caseinfo_id}") + # assert: assert response.status_code == 401 # Forbidden access should be 403 @pytest.mark.anyio -@pytest.mark.parametrize("test_user, client_user", [(True, True)], indirect=True) +@pytest.mark.parametrize("test_user, client_user", [(SUPER, SUPER)], indirect=True) async def test_get_caseinfo_by_id_superuser( db_session: AsyncSession, client_user: TestClient, @@ -228,41 +256,49 @@ async def test_get_caseinfo_by_id_superuser( ): """Test getting a caseinfo by id as superuser.""" _ = db_session - # Create caseifo - response = client_user.post( + _ = test_user + # act: + response_create = client_user.post( f"{settings.API_V1_STR}/caseinfo/create/", json={"pseudonym": "test1", "age_of_onset_month": 20}, ) - assert response.status_code == 200 - # Get the caseinfo id - response = client_user.get(f"{settings.API_V1_STR}/caseinfo/list/") - caseinfo_id = response.json()[0]["id"] - - response = client_user.get(f"{settings.API_V1_STR}/caseinfo/get-by-id?id={caseinfo_id}") - assert response.status_code == 200 - assert response.json()["id"] == caseinfo_id + response_list = client_user.get(f"{settings.API_V1_STR}/caseinfo/list/") + caseinfo_id = response_list.json()[0]["id"] + response_get_by_id = client_user.get( + f"{settings.API_V1_STR}/caseinfo/get-by-id?id={caseinfo_id}" + ) + # assert: + assert response_create.status_code == 200 + assert response_get_by_id.status_code == 200 + assert response_get_by_id.json()["id"] == caseinfo_id @pytest.mark.anyio async def test_get_caseinfo_by_id_anon(db_session: AsyncSession, client: TestClient): """Test getting a caseinfo by id as anonymous user.""" _ = db_session + # arrange: caseinfo_id = uuid.uuid4() + # act: response = client.get(f"{settings.API_V1_STR}/caseinfo/get-by-id?id={caseinfo_id}/") + # assert: assert response.status_code == 401 assert response.json() == {"detail": "Unauthorized"} @pytest.mark.anyio -@pytest.mark.parametrize("test_user, client_user", [(True, True)], indirect=True) +@pytest.mark.parametrize("test_user, client_user", [(SUPER, SUPER)], indirect=True) async def test_get_caseinfo_by_invalid_id( db_session: AsyncSession, client_user: TestClient, test_user: User, ): """Test getting a caseinfo by invalid id.""" + # arrange: caseinfo_id = uuid.uuid4() # Invalid id + # act: response = client_user.get(f"{settings.API_V1_STR}/caseinfo/get-by-id?id={caseinfo_id}") + # assert: assert response.status_code == 404 assert response.json() == {"detail": "Case Information not found"} @@ -273,81 +309,96 @@ async def test_get_caseinfo_by_invalid_id( @pytest.mark.anyio -@pytest.mark.parametrize("test_user, client_user", [(False, False)], indirect=True) +@pytest.mark.parametrize("test_user, client_user", [(REGUL, REGUL)], indirect=True) async def test_delete_caseinfo_by_id( db_session: AsyncSession, client_user: TestClient, test_user: User, ): """Test deleting a caseinfo by id as regular user.""" + _ = db_session + _ = test_user + # act: # Create a caseinfo - response = client_user.post( + response_create = client_user.post( f"{settings.API_V1_STR}/caseinfo/create/", json={"pseudonym": "test1", "age_of_onset_month": 20}, ) - assert response.status_code == 200 # Get the caseinfo id - response = client_user.get(f"{settings.API_V1_STR}/caseinfo/list/") - caseinfo_id = response.json()[0]["id"] - - response = client_user.delete(f"{settings.API_V1_STR}/caseinfo/delete-by-id?id={caseinfo_id}") - assert response.status_code == 401 - assert response.json() == {"detail": "Unauthorized"} - + response_list = client_user.get(f"{settings.API_V1_STR}/caseinfo/list/") + caseinfo_id = response_list.json()[0]["id"] + response_delete = client_user.delete( + f"{settings.API_V1_STR}/caseinfo/delete-by-id?id={caseinfo_id}" + ) # Verify that the caseinfo was not deleted - response = client_user.get(f"{settings.API_V1_STR}/caseinfo/list/") - assert response.status_code == 200 - assert response.json()[0]["id"] == caseinfo_id + response_list2 = client_user.get(f"{settings.API_V1_STR}/caseinfo/list/") + # assert: + assert response_create.status_code == 200 + assert response_delete.status_code == 401 + assert response_delete.json() == {"detail": "Unauthorized"} + assert response_list2.status_code == 200 + assert response_list2.json()[0]["id"] == caseinfo_id @pytest.mark.anyio -@pytest.mark.parametrize("test_user, client_user", [(True, True)], indirect=True) +@pytest.mark.parametrize("test_user, client_user", [(SUPER, SUPER)], indirect=True) async def test_delete_caseinfo_by_id_superuser( db_session: AsyncSession, client_user: TestClient, test_user: User, ): """Test deleting a caseinfo by id as superuser.""" + _ = db_session + _ = test_user + # act: # Create a caseinfo - response = client_user.post( + response_create = client_user.post( f"{settings.API_V1_STR}/caseinfo/create/", json={"pseudonym": "test1", "age_of_onset_month": 20}, ) - assert response.status_code == 200 # Get the caseinfo id - response = client_user.get(f"{settings.API_V1_STR}/caseinfo/list/") - caseinfo_id = response.json()[0]["id"] - - response = client_user.delete(f"{settings.API_V1_STR}/caseinfo/delete-by-id?id={caseinfo_id}") - assert response.status_code == 200 - assert response.json()["id"] == caseinfo_id - + response_list = client_user.get(f"{settings.API_V1_STR}/caseinfo/list/") + caseinfo_id = response_list.json()[0]["id"] + response_delete = client_user.delete( + f"{settings.API_V1_STR}/caseinfo/delete-by-id?id={caseinfo_id}" + ) # Verify that the caseinfo is indeed deleted - response = client_user.get(f"{settings.API_V1_STR}/caseinfo/get-by-id?id={caseinfo_id}") - assert response.status_code == 404 # Not Found + response_get_by_id = client_user.get( + f"{settings.API_V1_STR}/caseinfo/get-by-id?id={caseinfo_id}" + ) + # act: + assert response_create.status_code == 200 + assert response_delete.status_code == 200 + assert response_delete.json()["id"] == caseinfo_id + assert response_get_by_id.status_code == 404 # Not Found @pytest.mark.anyio async def test_delete_caseinfo_by_id_anon(db_session: AsyncSession, client: TestClient): """Test deleting a caseinfo by id as anonymous user.""" _ = db_session + # arrange: caseinfo_id = uuid.uuid4() + # act: response = client.delete(f"{settings.API_V1_STR}/caseinfo/delete-by-id?id={caseinfo_id}/") + # assert: assert response.status_code == 401 assert response.json() == {"detail": "Unauthorized"} @pytest.mark.anyio -@pytest.mark.parametrize("test_user, client_user", [(True, True)], indirect=True) +@pytest.mark.parametrize("test_user, client_user", [(SUPER, SUPER)], indirect=True) async def test_delete_caseinfo_by_invalid_id( db_session: AsyncSession, client_user: TestClient, test_user: User, ): """Test deleting a caseinfo by invalid id.""" + # arrange: caseinfo_id = uuid.uuid4() - + # act: response = client_user.delete(f"{settings.API_V1_STR}/caseinfo/delete-by-id?id={caseinfo_id}") + # assert: assert response.status_code == 404 assert response.json() == {"detail": "Case Information not found"} @@ -358,7 +409,7 @@ async def test_delete_caseinfo_by_invalid_id( @pytest.mark.anyio -@pytest.mark.parametrize("test_user, client_user", [(False, False)], indirect=True) +@pytest.mark.parametrize("test_user, client_user", [(REGUL, REGUL)], indirect=True) async def test_list_caseinfo( db_session: AsyncSession, client_user: TestClient, @@ -366,22 +417,23 @@ async def test_list_caseinfo( ): """Test listing caseinfos as regular user.""" _ = db_session + # act: # Create a caseinfo - response = client_user.post( + response_create = client_user.post( f"{settings.API_V1_STR}/caseinfo/create/", json={"pseudonym": "test1", "age_of_onset_month": 20}, ) - assert response.status_code == 200 - - response = client_user.get(f"{settings.API_V1_STR}/caseinfo/list/") - assert response.status_code == 200 - assert response.json()[0]["pseudonym"] == "test1" - assert response.json()[0]["age_of_onset_month"] == 20 - assert response.json()[0]["user"] == str(test_user.id) + response_list = client_user.get(f"{settings.API_V1_STR}/caseinfo/list/") + # assert: + assert response_create.status_code == 200 + assert response_list.status_code == 200 + assert response_list.json()[0]["pseudonym"] == "test1" + assert response_list.json()[0]["age_of_onset_month"] == 20 + assert response_list.json()[0]["user"] == str(test_user.id) @pytest.mark.anyio -@pytest.mark.parametrize("test_user, client_user", [(True, True)], indirect=True) +@pytest.mark.parametrize("test_user, client_user", [(SUPER, SUPER)], indirect=True) async def test_list_caseinfo_superuser( db_session: AsyncSession, client_user: TestClient, @@ -389,38 +441,43 @@ async def test_list_caseinfo_superuser( ): """Test listing caseinfos as superuser.""" _ = db_session + # act: # Create a caseinfo - response = client_user.post( + response_crate = client_user.post( f"{settings.API_V1_STR}/caseinfo/create/", json={"pseudonym": "test1", "age_of_onset_month": 20}, ) - assert response.status_code == 200 - - response = client_user.get(f"{settings.API_V1_STR}/caseinfo/list/") - assert response.status_code == 200 - assert response.json()[0]["pseudonym"] == "test1" - assert response.json()[0]["age_of_onset_month"] == 20 - assert response.json()[0]["user"] == str(test_user.id) + response_list = client_user.get(f"{settings.API_V1_STR}/caseinfo/list/") + # assert: + assert response_crate.status_code == 200 + assert response_list.status_code == 200 + assert response_list.json()[0]["pseudonym"] == "test1" + assert response_list.json()[0]["age_of_onset_month"] == 20 + assert response_list.json()[0]["user"] == str(test_user.id) @pytest.mark.anyio async def test_list_caseinfo_anon(db_session: AsyncSession, client: TestClient): """Test listing caseinfos as anonymous user.""" _ = db_session + # act: response = client.get(f"{settings.API_V1_STR}/caseinfo/list/") + # assert: assert response.status_code == 401 assert response.json() == {"detail": "Unauthorized"} @pytest.mark.anyio -@pytest.mark.parametrize("test_user, client_user", [(True, True)], indirect=True) +@pytest.mark.parametrize("test_user, client_user", [(SUPER, SUPER)], indirect=True) async def test_list_no_caseinfo( db_session: AsyncSession, client_user: TestClient, ): """Test listing caseinfos with no caseinfos.""" _ = db_session + # act: response = client_user.get(f"{settings.API_V1_STR}/caseinfo/list/") + # assert: assert response.status_code == 200 assert response.json() == [] @@ -431,7 +488,7 @@ async def test_list_no_caseinfo( @pytest.mark.anyio -@pytest.mark.parametrize("test_user, client_user", [(False, False)], indirect=True) +@pytest.mark.parametrize("test_user, client_user", [(REGUL, REGUL)], indirect=True) async def test_get_caseinfo( db_session: AsyncSession, client_user: TestClient, @@ -439,22 +496,23 @@ async def test_get_caseinfo( ): """Test getting a caseinfo as regular user.""" _ = db_session + # act: # Create a caseinfo - response = client_user.post( + response_create = client_user.post( f"{settings.API_V1_STR}/caseinfo/create/", json={"pseudonym": "test1", "age_of_onset_month": 20}, ) - assert response.status_code == 200 - - response = client_user.get(f"{settings.API_V1_STR}/caseinfo/get") - assert response.status_code == 200 - assert response.json()["pseudonym"] == "test1" - assert response.json()["age_of_onset_month"] == 20 - assert response.json()["user"] == str(test_user.id) + response_get = client_user.get(f"{settings.API_V1_STR}/caseinfo/get") + # assert: + assert response_create.status_code == 200 + assert response_get.status_code == 200 + assert response_get.json()["pseudonym"] == "test1" + assert response_get.json()["age_of_onset_month"] == 20 + assert response_get.json()["user"] == str(test_user.id) @pytest.mark.anyio -@pytest.mark.parametrize("test_user, client_user", [(True, True)], indirect=True) +@pytest.mark.parametrize("test_user, client_user", [(SUPER, SUPER)], indirect=True) async def test_get_caseinfo_superuser( db_session: AsyncSession, client_user: TestClient, @@ -462,38 +520,43 @@ async def test_get_caseinfo_superuser( ): """Test getting a caseinfo as superuser.""" _ = db_session + # act: # Create a caseinfo - response = client_user.post( + response_create = client_user.post( f"{settings.API_V1_STR}/caseinfo/create/", json={"pseudonym": "test1", "age_of_onset_month": 20}, ) - assert response.status_code == 200 - - response = client_user.get(f"{settings.API_V1_STR}/caseinfo/get") - assert response.status_code == 200 - assert response.json()["pseudonym"] == "test1" - assert response.json()["age_of_onset_month"] == 20 - assert response.json()["user"] == str(test_user.id) + response_get = client_user.get(f"{settings.API_V1_STR}/caseinfo/get") + # assert: + assert response_create.status_code == 200 + assert response_get.status_code == 200 + assert response_get.json()["pseudonym"] == "test1" + assert response_get.json()["age_of_onset_month"] == 20 + assert response_get.json()["user"] == str(test_user.id) @pytest.mark.anyio async def test_get_caseinfo_anon(db_session: AsyncSession, client: TestClient): """Test getting a caseinfo as anonymous user.""" _ = db_session + # act: response = client.get(f"{settings.API_V1_STR}/caseinfo/get") + # assert: assert response.status_code == 401 assert response.json() == {"detail": "Unauthorized"} @pytest.mark.anyio -@pytest.mark.parametrize("test_user, client_user", [(True, True)], indirect=True) +@pytest.mark.parametrize("test_user, client_user", [(SUPER, SUPER)], indirect=True) async def test_get_no_caseinfo( db_session: AsyncSession, client_user: TestClient, ): """Test getting a caseinfo with no caseinfos.""" _ = db_session + # act: response = client_user.get(f"{settings.API_V1_STR}/caseinfo/get") + # assert: assert response.status_code == 404 assert response.json() == {"detail": "Case Information not found"} @@ -504,7 +567,7 @@ async def test_get_no_caseinfo( @pytest.mark.anyio -@pytest.mark.parametrize("test_user, client_user", [(False, False)], indirect=True) +@pytest.mark.parametrize("test_user, client_user", [(REGUL, REGUL)], indirect=True) async def test_update_caseinfo( db_session: AsyncSession, client_user: TestClient, @@ -513,24 +576,25 @@ async def test_update_caseinfo( """Test updating a caseinfo as regular user.""" _ = db_session # Create a caseinfo - response = client_user.post( + # act: + response_create = client_user.post( f"{settings.API_V1_STR}/caseinfo/create/", json={"pseudonym": "test1", "age_of_onset_month": 20}, ) - assert response.status_code == 200 - - response = client_user.put( + response_update = client_user.put( f"{settings.API_V1_STR}/caseinfo/update", json={"pseudonym": "test2", "age_of_onset_month": 21}, ) - assert response.status_code == 200 - assert response.json()["pseudonym"] == "test2" - assert response.json()["age_of_onset_month"] == 21 - assert response.json()["user"] == str(test_user.id) + # assert: + assert response_create.status_code == 200 + assert response_update.status_code == 200 + assert response_update.json()["pseudonym"] == "test2" + assert response_update.json()["age_of_onset_month"] == 21 + assert response_update.json()["user"] == str(test_user.id) @pytest.mark.anyio -@pytest.mark.parametrize("test_user, client_user", [(True, True)], indirect=True) +@pytest.mark.parametrize("test_user, client_user", [(SUPER, SUPER)], indirect=True) async def test_update_caseinfo_superuser( db_session: AsyncSession, client_user: TestClient, @@ -538,37 +602,40 @@ async def test_update_caseinfo_superuser( ): """Test updating a caseinfo as superuser.""" _ = db_session + # act: # Create a caseinfo - response = client_user.post( + response_create = client_user.post( f"{settings.API_V1_STR}/caseinfo/create/", json={"pseudonym": "test1", "age_of_onset_month": 20}, ) - assert response.status_code == 200 - - response = client_user.put( + response_update = client_user.put( f"{settings.API_V1_STR}/caseinfo/update", json={"pseudonym": "test2", "age_of_onset_month": 21}, ) - assert response.status_code == 200 - assert response.json()["pseudonym"] == "test2" - assert response.json()["age_of_onset_month"] == 21 - assert response.json()["user"] == str(test_user.id) + # assert: + assert response_create.status_code == 200 + assert response_update.status_code == 200 + assert response_update.json()["pseudonym"] == "test2" + assert response_update.json()["age_of_onset_month"] == 21 + assert response_update.json()["user"] == str(test_user.id) @pytest.mark.anyio async def test_update_caseinfo_anon(db_session: AsyncSession, client: TestClient): """Test updating a caseinfo as anonymous user.""" _ = db_session + # act: response = client.put( f"{settings.API_V1_STR}/caseinfo/update", json={"pseudonym": "test2", "age_of_onset_month": 21}, ) + # assert: assert response.status_code == 401 assert response.json() == {"detail": "Unauthorized"} @pytest.mark.anyio -@pytest.mark.parametrize("test_user, client_user", [(True, True)], indirect=True) +@pytest.mark.parametrize("test_user, client_user", [(SUPER, SUPER)], indirect=True) async def test_update_caseinfo_patch( db_session: AsyncSession, client_user: TestClient, @@ -576,41 +643,44 @@ async def test_update_caseinfo_patch( ): """Test updating a caseinfo with patch as superuser.""" _ = db_session + # act: # Create a caseinfo - response = client_user.post( + response_create = client_user.post( f"{settings.API_V1_STR}/caseinfo/create/", json={"pseudonym": "test1", "age_of_onset_month": 20}, ) - assert response.status_code == 200 - - response = client_user.patch( + response_update = client_user.patch( f"{settings.API_V1_STR}/caseinfo/update", json={"pseudonym": "test2", "age_of_onset_month": 21}, ) - assert response.status_code == 200 - assert response.json()["pseudonym"] == "test2" - assert response.json()["age_of_onset_month"] == 21 - assert response.json()["user"] == str(test_user.id) + # assert: + assert response_create.status_code == 200 + assert response_update.status_code == 200 + assert response_update.json()["pseudonym"] == "test2" + assert response_update.json()["age_of_onset_month"] == 21 + assert response_update.json()["user"] == str(test_user.id) @pytest.mark.anyio -@pytest.mark.parametrize("test_user, client_user", [(True, True)], indirect=True) +@pytest.mark.parametrize("test_user, client_user", [(SUPER, SUPER)], indirect=True) async def test_update_no_caseinfo( db_session: AsyncSession, client_user: TestClient, ): """Test updating a caseinfo with no caseinfos.""" _ = db_session + # act: response = client_user.put( f"{settings.API_V1_STR}/caseinfo/update", json={"pseudonym": "test2", "age_of_onset_month": 21}, ) + # assert: assert response.status_code == 404 assert response.json() == {"detail": "Case Information not found"} @pytest.mark.anyio -@pytest.mark.parametrize("test_user, client_user", [(True, True)], indirect=True) +@pytest.mark.parametrize("test_user, client_user", [(SUPER, SUPER)], indirect=True) async def test_update_caseinfo_invalid_enum( db_session: AsyncSession, client_user: TestClient, @@ -618,22 +688,24 @@ async def test_update_caseinfo_invalid_enum( ): """Test updating a caseinfo with invalid enums.""" _ = db_session + _ = test_user + # act: # Create a caseinfo - response = client_user.post( + response_create = client_user.post( f"{settings.API_V1_STR}/caseinfo/create/", json={"pseudonym": "test1", "age_of_onset_month": 20}, ) - assert response.status_code == 200 - response = client_user.put( f"{settings.API_V1_STR}/caseinfo/update", json={"pseudonym": "test2", "age_of_onset_month": [21], "sex": "invalid_sex"}, ) + # assert: + assert response_create.status_code == 200 assert response.status_code == 422 @pytest.mark.anyio -@pytest.mark.parametrize("test_user, client_user", [(True, True)], indirect=True) +@pytest.mark.parametrize("test_user, client_user", [(SUPER, SUPER)], indirect=True) async def test_update_caseinfo_invalid_terms( db_session: AsyncSession, client_user: TestClient, @@ -641,14 +713,14 @@ async def test_update_caseinfo_invalid_terms( ): """Test updating a caseinfo with invalid terms.""" _ = db_session + _ = test_user + # act: # Create a caseinfo - response = client_user.post( + response_create = client_user.post( f"{settings.API_V1_STR}/caseinfo/create/", json={"pseudonym": "test1", "age_of_onset_month": 20}, ) - assert response.status_code == 200 - - response = client_user.put( + response_update = client_user.put( f"{settings.API_V1_STR}/caseinfo/update/", json={ "pseudonym": "test1", @@ -656,7 +728,9 @@ async def test_update_caseinfo_invalid_terms( "diseases": [{"omim_id_invalid": "string"}], }, ) - assert response.status_code == 422 + # assert: + assert response_create.status_code == 200 + assert response_update.status_code == 422 # ------------------------------------------------------------------------------ @@ -665,7 +739,7 @@ async def test_update_caseinfo_invalid_terms( @pytest.mark.anyio -@pytest.mark.parametrize("test_user, client_user", [(False, False)], indirect=True) +@pytest.mark.parametrize("test_user, client_user", [(REGUL, REGUL)], indirect=True) async def test_delete_caseinfo( db_session: AsyncSession, client_user: TestClient, @@ -673,24 +747,25 @@ async def test_delete_caseinfo( ): """Test deleting a caseinfo as regular user.""" _ = db_session + _ = test_user + # act: # Create a caseinfo - response = client_user.post( + response_create = client_user.post( f"{settings.API_V1_STR}/caseinfo/create/", json={"pseudonym": "test1", "age_of_onset_month": 20}, ) - assert response.status_code == 200 - - response = client_user.delete(f"{settings.API_V1_STR}/caseinfo/delete") - assert response.status_code == 200 - assert response.json()["pseudonym"] == "test1" - + response_delete = client_user.delete(f"{settings.API_V1_STR}/caseinfo/delete") # Verify that the caseinfo is indeed deleted - response = client_user.get(f"{settings.API_V1_STR}/caseinfo/get") - assert response.status_code == 404 + response_get = client_user.get(f"{settings.API_V1_STR}/caseinfo/get") + # assert: + assert response_create.status_code == 200 + assert response_delete.status_code == 200 + assert response_delete.json()["pseudonym"] == "test1" + assert response_get.status_code == 404 @pytest.mark.anyio -@pytest.mark.parametrize("test_user, client_user", [(True, True)], indirect=True) +@pytest.mark.parametrize("test_user, client_user", [(SUPER, SUPER)], indirect=True) async def test_delete_caseinfo_superuser( db_session: AsyncSession, client_user: TestClient, @@ -698,39 +773,45 @@ async def test_delete_caseinfo_superuser( ): """Test deleting a caseinfo as superuser.""" _ = db_session + _ = test_user + # act: # Create a caseinfo - response = client_user.post( + response_create = client_user.post( f"{settings.API_V1_STR}/caseinfo/create/", json={"pseudonym": "test1", "age_of_onset_month": 20}, ) - assert response.status_code == 200 - - response = client_user.delete(f"{settings.API_V1_STR}/caseinfo/delete") - assert response.status_code == 200 - assert response.json()["pseudonym"] == "test1" - + response_delete = client_user.delete(f"{settings.API_V1_STR}/caseinfo/delete") # Verify that the caseinfo is indeed deleted - response = client_user.get(f"{settings.API_V1_STR}/caseinfo/get") - assert response.status_code == 404 + response_get = client_user.get(f"{settings.API_V1_STR}/caseinfo/get") + + # assert: + assert response_create.status_code == 200 + assert response_delete.status_code == 200 + assert response_delete.json()["pseudonym"] == "test1" + assert response_get.status_code == 404 @pytest.mark.anyio async def test_delete_caseinfo_anon(db_session: AsyncSession, client: TestClient): """Test deleting a caseinfo as anonymous user.""" _ = db_session + # act: response = client.delete(f"{settings.API_V1_STR}/caseinfo/delete") + # assert: assert response.status_code == 401 assert response.json() == {"detail": "Unauthorized"} @pytest.mark.anyio -@pytest.mark.parametrize("test_user, client_user", [(True, True)], indirect=True) +@pytest.mark.parametrize("test_user, client_user", [(SUPER, SUPER)], indirect=True) async def test_delete_no_caseinfo( db_session: AsyncSession, client_user: TestClient, ): """Test deleting a caseinfo with no caseinfos.""" _ = db_session + # act: response = client_user.delete(f"{settings.API_V1_STR}/caseinfo/delete") + # assert: assert response.status_code == 404 assert response.json() == {"detail": "Case Information not found"} diff --git a/backend/tests/api/api_v1/test_clinvarsub.py b/backend/tests/api/api_v1/test_clinvarsub.py index 588d8100..69a636ee 100644 --- a/backend/tests/api/api_v1/test_clinvarsub.py +++ b/backend/tests/api/api_v1/test_clinvarsub.py @@ -215,21 +215,22 @@ async def test_list_submissionthreads( :param is_owner: test case where ``client_user`` is owner or not :param good_query: unless ``None``, use query with or without entries """ + # arrange: if not is_owner: # make thread owned by different user await crud.submissionthread.update( db_session, db_obj=submissionthread, obj_in={"submittingorg_id": uuid.uuid4()} ) - - # run the test if good_query is None: query = "" else: val = submissionthread.primary_variant_desc if good_query else "BOGUS" query = f"?primary_variant_desc={val}" + # act: response = client_user.get( f"{settings.API_V1_STR}/clinvarsub/submissionthreads{query}", ) + # assert: assert response.status_code == 200 pages = response.json() if is_owner and good_query is not False: @@ -262,17 +263,18 @@ async def test_create_submissionthreads( submissionthread_create: SubmissionThreadCreate, is_owner: bool, ): + # arrange: if not is_owner: # make submission org owned by different user await crud.submittingorg.update( db_session, db_obj=submittingorg, obj_in={"owner": uuid.uuid4()} ) - - # run the test + # act: response = client_user.post( f"{settings.API_V1_STR}/clinvarsub/submissionthreads", json=submissionthread_create.model_dump(mode="json"), ) + # assert: if is_owner: assert response.status_code == 200 assert response.json() == { @@ -303,13 +305,13 @@ async def test_update_submissionthreads( submissionthread: SubmissionThread, is_owner: bool, ): + # arrange: if not is_owner: # make submittingorg owned by different user await crud.submittingorg.update( db_session, db_obj=submittingorg, obj_in={"owner": uuid.uuid4()} ) - - # run the tests + # act: response = client_user.put( f"{settings.API_V1_STR}/clinvarsub/submissionthreads/{submissionthread.id}", json={ @@ -318,6 +320,7 @@ async def test_update_submissionthreads( "status": SubmissionThreadStatus.WAITING.value, }, ) + # assert: if is_owner: assert response.status_code == 200 assert response.json() == { @@ -348,16 +351,17 @@ async def test_read_submissionthreads( submissionthread: SubmissionThread, is_owner: bool, ): + # arrange: if not is_owner: # make submittingorg owned by different user await crud.submittingorg.update( db_session, db_obj=submittingorg, obj_in={"owner": uuid.uuid4()} ) - - # run the tests + # act: response = client_user.get( f"{settings.API_V1_STR}/clinvarsub/submissionthreads/{submissionthread.id}", ) + # assert: if is_owner: assert response.status_code == 200 assert response.json() == { @@ -388,16 +392,17 @@ async def test_delete_submissionthreads( submissionthread: SubmissionThread, is_owner: bool, ): + # arrange: if not is_owner: # make submittingorg owned by different user await crud.submittingorg.update( db_session, db_obj=submittingorg, obj_in={"owner": uuid.uuid4()} ) - - # run the tests + # act: response = client_user.delete( f"{settings.API_V1_STR}/clinvarsub/submissionthreads/{submissionthread.id}", ) + # assert: if is_owner: assert response.status_code == 200 assert response.json() == { @@ -434,16 +439,17 @@ async def test_list_submissionactivities( """ :param is_owner: test case where ``client_user`` is owner or not """ + # arrange: if not is_owner: # make submitting org owned by different user await crud.submittingorg.update( db_session, db_obj=submittingorg, obj_in={"owner": uuid.uuid4()} ) - - # run the test + # act: response = client_user.get( f"{settings.API_V1_STR}/clinvarsub/submissionthreads/{submissionthread.id}/activities", ) + # assert: if is_owner: assert response.status_code == 200 assert response.json() == { @@ -487,17 +493,18 @@ async def test_create_submissionactivity( """ :param is_owner: test case where ``client_user`` is owner or not """ + # arrange: if not is_owner: # make submitting org owned by different user await crud.submittingorg.update( db_session, db_obj=submittingorg, obj_in={"owner": uuid.uuid4()} ) - - # run the test + # act: response = client_user.post( f"{settings.API_V1_STR}/clinvarsub/submissionthreads/{submissionthread.id}/activities", json=submissionactivity_create.model_dump(mode="json"), ) + # assert: if is_owner: assert response.status_code == 200 assert response.json() == { @@ -532,13 +539,13 @@ async def test_update_submissionactivity( """ :param is_owner: test case where ``client_user`` is owner or not """ + # arrange: if not is_owner: # make submitting org owned by different user await crud.submittingorg.update( db_session, db_obj=submittingorg, obj_in={"owner": uuid.uuid4()} ) - - # run the test + # act: response = client_user.put( f"{settings.API_V1_STR}/clinvarsub/submissionactivities/{submissionactivity.id}", json={ @@ -550,6 +557,7 @@ async def test_update_submissionactivity( "response_timestamp": submissionactivity.response_timestamp, }, ) + # assert: if is_owner: assert response.status_code == 200 assert response.json() == { diff --git a/backend/tests/api/api_v1/test_utils.py b/backend/tests/api/api_v1/test_utils.py index 604a4332..4ac80889 100644 --- a/backend/tests/api/api_v1/test_utils.py +++ b/backend/tests/api/api_v1/test_utils.py @@ -4,27 +4,36 @@ from app.core.config import settings from app.models.user import User +from tests.conftest import UserChoice + +#: Shortcut for regular user. +REGUL = UserChoice.REGULAR +#: Shortcut for superuser. +SUPER = UserChoice.SUPERUSER @pytest.mark.anyio -@pytest.mark.parametrize("test_user, client_user", [(False, False)], indirect=True) +@pytest.mark.parametrize("test_user, client_user", [(REGUL, REGUL)], indirect=True) async def test_test_email( db_session: AsyncSession, client_user: TestClient, test_user: User, ): """Test sending an email as regular user.""" + # arrange: _ = db_session email_to = "test1@example.com" + # act: response = client_user.post( f"{settings.API_V1_STR}/utils/test-email?email_to={email_to}", ) + # assert: assert response.status_code == 401 assert response.json() == {"detail": "Unauthorized"} @pytest.mark.anyio -@pytest.mark.parametrize("test_user, client_user", [(True, True)], indirect=True) +@pytest.mark.parametrize("test_user, client_user", [(SUPER, SUPER)], indirect=True) async def test_test_email_superuser( db_session: AsyncSession, client_user: TestClient, @@ -32,13 +41,16 @@ async def test_test_email_superuser( monkeypatch, ): """Test sending an email as superuser.""" - # Mock the send_test_email function + # arrange: + # Mock out the send_test_email function monkeypatch.setattr("app.api.api_v1.endpoints.utils.send_test_email", lambda email_to: None) _ = db_session email_to = "test1@example.com" + # act: response = client_user.post( f"{settings.API_V1_STR}/utils/test-email?email_to={email_to}", ) + # assert: assert response.status_code == 201 assert response.json() == {"msg": "Test email sent"} @@ -46,10 +58,13 @@ async def test_test_email_superuser( @pytest.mark.anyio async def test_test_email_anon(db_session: AsyncSession, client: TestClient): """Test sending an email as anonymous user.""" + # arrange: _ = db_session email_to = "test1@example.com" + # act: response = client.post( f"{settings.API_V1_STR}/utils/test-email?email_to={email_to}", ) + # assert: assert response.status_code == 401 assert response.json() == {"detail": "Unauthorized"} diff --git a/backend/tests/api/internal/test_proxy.py b/backend/tests/api/internal/test_proxy.py index 20dfe7c7..785705e8 100644 --- a/backend/tests/api/internal/test_proxy.py +++ b/backend/tests/api/internal/test_proxy.py @@ -15,14 +15,16 @@ @pytest.mark.anyio async def test_proxy_annonars(monkeypatch: MonkeyPatch, httpx_mock: HTTPXMock, client: TestClient): """Test proxying to annonars backend.""" + # arrange: monkeypatch.setattr(settings, "BACKEND_PREFIX_ANNONARS", f"http://{MOCKED_BACKEND_HOST}") httpx_mock.add_response( url=f"http://{MOCKED_BACKEND_HOST}/{MOCKED_URL_TOKEN}", method="GET", text="Mocked response", ) - + # act: response = client.get(f"/internal/proxy/annonars/{MOCKED_URL_TOKEN}") + # assert: assert response.status_code == 200 assert response.text == "Mocked response" @@ -30,14 +32,16 @@ async def test_proxy_annonars(monkeypatch: MonkeyPatch, httpx_mock: HTTPXMock, c @pytest.mark.anyio async def test_proxy_mehari(monkeypatch: MonkeyPatch, httpx_mock: HTTPXMock, client: TestClient): """Test proxying to mehari backend.""" + # arrange: monkeypatch.setattr(settings, "BACKEND_PREFIX_MEHARI", f"http://{MOCKED_BACKEND_HOST}") httpx_mock.add_response( url=f"http://{MOCKED_BACKEND_HOST}/{MOCKED_URL_TOKEN}", method="GET", text="Mocked response", ) - + # act: response = client.get(f"/internal/proxy/mehari/{MOCKED_URL_TOKEN}") + # assert: assert response.status_code == 200 assert response.text == "Mocked response" @@ -45,14 +49,16 @@ async def test_proxy_mehari(monkeypatch: MonkeyPatch, httpx_mock: HTTPXMock, cli @pytest.mark.anyio async def test_proxy_viguno(monkeypatch: MonkeyPatch, httpx_mock: HTTPXMock, client: TestClient): """Test proxying to viguno backend.""" + # arrange: monkeypatch.setattr(settings, "BACKEND_PREFIX_VIGUNO", f"http://{MOCKED_BACKEND_HOST}") httpx_mock.add_response( url=f"http://{MOCKED_BACKEND_HOST}/{MOCKED_URL_TOKEN}", method="GET", text="Mocked response", ) - + # act: response = client.get(f"/internal/proxy/viguno/{MOCKED_URL_TOKEN}") + # assert: assert response.status_code == 200 assert response.text == "Mocked response" @@ -60,14 +66,16 @@ async def test_proxy_viguno(monkeypatch: MonkeyPatch, httpx_mock: HTTPXMock, cli @pytest.mark.anyio async def test_proxy_nginx(monkeypatch: MonkeyPatch, httpx_mock: HTTPXMock, client: TestClient): """Test proxying to nginx backend.""" + # arrange: monkeypatch.setattr(settings, "BACKEND_PREFIX_NGINX", f"http://{MOCKED_BACKEND_HOST}") httpx_mock.add_response( url=f"http://{MOCKED_BACKEND_HOST}/{MOCKED_URL_TOKEN}", method="GET", text="Mocked response", ) - + # act: response = client.get(f"/internal/proxy/nginx/{MOCKED_URL_TOKEN}") + # assert: assert response.status_code == 200 assert response.text == "Mocked response" @@ -75,14 +83,16 @@ async def test_proxy_nginx(monkeypatch: MonkeyPatch, httpx_mock: HTTPXMock, clie @pytest.mark.anyio async def test_proxy_dotty(monkeypatch: MonkeyPatch, httpx_mock: HTTPXMock, client: TestClient): """Test proxying to dotty backend.""" + # arrange: monkeypatch.setattr(settings, "BACKEND_PREFIX_DOTTY", f"http://{MOCKED_BACKEND_HOST}") httpx_mock.add_response( url=f"http://{MOCKED_BACKEND_HOST}/{MOCKED_URL_TOKEN}", method="GET", text="Mocked response", ) - + # act: response = client.get(f"/internal/proxy/dotty/{MOCKED_URL_TOKEN}") + # assert: assert response.status_code == 200 assert response.text == "Mocked response" @@ -90,14 +100,16 @@ async def test_proxy_dotty(monkeypatch: MonkeyPatch, httpx_mock: HTTPXMock, clie @pytest.mark.anyio async def test_proxy_cada_prio(monkeypatch: MonkeyPatch, httpx_mock: HTTPXMock, client: TestClient): """Test proxying to cada-prio backend.""" + # arrange: monkeypatch.setattr(settings, "BACKEND_PREFIX_CADA_PRIO", f"http://{MOCKED_BACKEND_HOST}") httpx_mock.add_response( url=f"http://{MOCKED_BACKEND_HOST}/{MOCKED_URL_TOKEN}", method="GET", text="Mocked response", ) - + # act: response = client.get(f"/internal/proxy/cada-prio/{MOCKED_URL_TOKEN}") + # assert: assert response.status_code == 200 assert response.text == "Mocked response" @@ -105,6 +117,8 @@ async def test_proxy_cada_prio(monkeypatch: MonkeyPatch, httpx_mock: HTTPXMock, @pytest.mark.anyio async def test_invalid_proxy_route(client: TestClient): """Test invalid proxy route.""" + # act: response = client.get("/internal/proxy/some-other-path") + # assert: assert response.status_code == 404 assert response.text == "Reverse proxy route not found" diff --git a/backend/tests/api/internal/test_remote.py b/backend/tests/api/internal/test_remote.py index 83b260ea..e351d265 100644 --- a/backend/tests/api/internal/test_remote.py +++ b/backend/tests/api/internal/test_remote.py @@ -14,14 +14,16 @@ @pytest.mark.anyio async def test_variantvalidator(httpx_mock: HTTPXMock, client: TestClient): """Test variant validator endpoint.""" + # arrange: variantvalidator_url = "https://rest.variantvalidator.org/VariantValidator/variantvalidator" httpx_mock.add_response( url=f"{variantvalidator_url}/{MOCKED_URL_TOKEN}", method="GET", text="Mocked response", ) - + # act: response = client.get(f"/internal/remote/variantvalidator/{MOCKED_URL_TOKEN}") + # assert: assert response.status_code == 200 assert response.text == "Mocked response" @@ -29,6 +31,7 @@ async def test_variantvalidator(httpx_mock: HTTPXMock, client: TestClient): @pytest.mark.anyio async def test_acmg(httpx_mock: HTTPXMock, client: TestClient): """Test ACMG endpoint.""" + # arrange: acmg_url = "http://wintervar.wglab.org/api_new.php" acmg_qury_params = "?chromosome=1&position=123&reference=A&alternative=T&release=hg19" httpx_mock.add_response( @@ -36,8 +39,9 @@ async def test_acmg(httpx_mock: HTTPXMock, client: TestClient): method="GET", json={"acmg": "Mocked response"}, ) - + # act: response = client.get(f"/internal/remote/acmg/{acmg_qury_params}") + # assert: assert response.status_code == 200 assert response.json() == default_acmg_rating() @@ -45,6 +49,40 @@ async def test_acmg(httpx_mock: HTTPXMock, client: TestClient): @pytest.mark.anyio async def test_acmg_missing_query_params(client: TestClient): """Test ACMG endpoint with missing query parameters.""" + # act: response = client.get("/internal/remote/acmg") + # assert: assert response.status_code == 400 assert response.text == "Missing query parameters" + + +@pytest.mark.anyio +async def test_cnv_acmg(httpx_mock: HTTPXMock, client: TestClient): + """Test forwarding to PubTator 3 API.""" + # arrange: + httpx_mock.add_response( + url="https://phoenix.bgi.com/api/acit/jobs/", + method="POST", + json={"res": "Mocked response"}, + ) + # act: + response = client.get("/internal/remote/cnv/acmg/?chromosome=1&start=123&end=456&func=foo") + # assert: + assert response.status_code == 200 + assert response.json() == {"res": "Mocked response"} + + +@pytest.mark.anyio +async def test_pubtator3_api(httpx_mock: HTTPXMock, client: TestClient): + """Test forwarding to PubTator 3 API.""" + # arrange: + httpx_mock.add_response( + url="https://www.ncbi.nlm.nih.gov/research/pubtator3-api/foo", + method="GET", + json={"res": "Mocked response"}, + ) + # act: + response = client.get("/internal/remote/pubtator3-api/foo") + # assert: + assert response.status_code == 200 + assert response.json() == {"res": "Mocked response"} diff --git a/backend/tests/conftest.py b/backend/tests/conftest.py index 6a356771..cac999c0 100644 --- a/backend/tests/conftest.py +++ b/backend/tests/conftest.py @@ -1,4 +1,5 @@ import asyncio +import enum import logging import os from typing import AsyncGenerator, Iterator @@ -105,21 +106,29 @@ def non_mocked_hosts(client: TestClient) -> list[str]: return [client._base_url.host] +@enum.unique +class UserChoice(enum.Enum): + """Enum for test users.""" + + #: Anonymous user + NONE = "anonymous" + #: Regular user + REGULAR = "regular" + #: Superuser + SUPERUSER = "superuser" + + @pytest.fixture() -def test_user(db_session: AsyncSession, request) -> User | None: +def test_user(db_session: AsyncSession, request: pytest.FixtureRequest) -> User | None: """Create a test user and return it. - Special handling for ``request.param``: - If ``request.param`` is ``None``, return ``None``. - If ``request.param`` is ``True``, return a superuser. - If ``request.param`` is ``False``, return a regular user. + Special handling for ``request.param`` (type ``TestUser``). """ - if hasattr(request, "param") and request.param is None: - # Return None for anonymous user tests + # Get the user type from the request, defaulting to regular and early return for anonymous. + user_choice: UserChoice = getattr(request, "param", UserChoice.REGULAR) + if user_choice == UserChoice.NONE: return None - superuser: bool = request.param if hasattr(request, "param") else False - async def get_db_session() -> AsyncGenerator[AsyncSession, None]: yield db_session @@ -127,7 +136,7 @@ async def get_db_session() -> AsyncGenerator[AsyncSession, None]: init_db.create_user( email="test@example.com", password="password123", - is_superuser=superuser, + is_superuser=user_choice == UserChoice.SUPERUSER, get_async_session=get_db_session, ) ) @@ -135,32 +144,27 @@ async def get_db_session() -> AsyncGenerator[AsyncSession, None]: @pytest.fixture() -def client_user(test_user: User, request): +def client_user(test_user: User | None, request: pytest.FixtureRequest): """Create a test client with a test user. - Special handling for ``request.param``: - If ``request.param`` is ``None``, return a client with no user. - If ``request.param`` is ``True``, return a client with a superuser. - If ``request.param`` is ``False``, return a client with a regular user. + Special handling for ``request.param`` (type ``TestUser``). """ - superuser: bool = request.param if hasattr(request, "param") else False + # Get the user type from the request, defaulting to regular and early return for anonymous. + user: UserChoice = getattr(request, "param", UserChoice.REGULAR) - old_current_active_user = app.dependency_overrides.get(current_active_user) app.dependency_overrides[current_active_user] = lambda: test_user if test_user is not None: - old_current_active_user = app.dependency_overrides.get(current_active_user) app.dependency_overrides[current_active_user] = lambda: test_user - if superuser: - old_current_active_superuser = app.dependency_overrides.get(current_active_superuser) + if user == UserChoice.SUPERUSER: app.dependency_overrides[current_active_superuser] = lambda: test_user client = TestClient(app) yield client app.dependency_overrides.pop(current_active_user, None) - if superuser: + if user == UserChoice.SUPERUSER: app.dependency_overrides.pop(current_active_superuser, None) diff --git a/backend/tests/crud/test_acmgseqvar.py b/backend/tests/crud/test_acmgseqvar.py index f399fc0a..4e3d5f43 100644 --- a/backend/tests/crud/test_acmgseqvar.py +++ b/backend/tests/crud/test_acmgseqvar.py @@ -36,6 +36,7 @@ def acmgseqvar_create() -> AcmgSeqVarCreate: @pytest.mark.anyio async def test_create_get_acmgseqvar(db_session: AsyncSession, acmgseqvar_create: AcmgSeqVarCreate): """Test creating and retrieving a acmgseqvar.""" + # act: acmgseqvar_postcreate = await crud.acmgseqvar.create( session=db_session, obj_in=acmgseqvar_create ) @@ -43,6 +44,7 @@ async def test_create_get_acmgseqvar(db_session: AsyncSession, acmgseqvar_create session=db_session, id=acmgseqvar_postcreate.id, ) + # assert: assert stored_item assert acmgseqvar_postcreate.user == stored_item.user assert acmgseqvar_postcreate.seqvar_name == stored_item.seqvar_name @@ -52,15 +54,18 @@ async def test_create_get_acmgseqvar(db_session: AsyncSession, acmgseqvar_create @pytest.mark.anyio async def test_delete_acmgseqvar(db_session: AsyncSession, acmgseqvar_create: AcmgSeqVarCreate): """Test deleting a acmgseqvar.""" + # act: acmgseqvar_postcreate = await crud.acmgseqvar.create( session=db_session, obj_in=acmgseqvar_create ) + # assert: await crud.acmgseqvar.remove(session=db_session, id=acmgseqvar_postcreate.id) @pytest.mark.anyio async def test_get_multi_by_user(db_session: AsyncSession, acmgseqvar_create: AcmgSeqVarCreate): """Test get_multi_by_user.""" + # act: acmgseqvar_postcreate = await crud.acmgseqvar.create( session=db_session, obj_in=acmgseqvar_create ) @@ -68,6 +73,7 @@ async def test_get_multi_by_user(db_session: AsyncSession, acmgseqvar_create: Ac session=db_session, user_id=acmgseqvar_postcreate.user, ) + # assert: assert stored_item assert acmgseqvar_postcreate.user == stored_item[0].user assert acmgseqvar_postcreate.seqvar_name == stored_item[0].seqvar_name @@ -77,6 +83,7 @@ async def test_get_multi_by_user(db_session: AsyncSession, acmgseqvar_create: Ac @pytest.mark.anyio async def test_get_by_user(db_session: AsyncSession, acmgseqvar_create: AcmgSeqVarCreate): """Test get_by_user.""" + # act: acmgseqvar_postcreate = await crud.acmgseqvar.create( session=db_session, obj_in=acmgseqvar_create ) @@ -85,6 +92,7 @@ async def test_get_by_user(db_session: AsyncSession, acmgseqvar_create: AcmgSeqV user_id=acmgseqvar_postcreate.user, seqvar_name=acmgseqvar_postcreate.seqvar_name, ) + # assert: assert stored_item assert acmgseqvar_postcreate.user == stored_item.user assert acmgseqvar_postcreate.seqvar_name == stored_item.seqvar_name diff --git a/backend/tests/crud/test_adminmsg.py b/backend/tests/crud/test_adminmsg.py index 5b7f4d47..cc6f8a97 100644 --- a/backend/tests/crud/test_adminmsg.py +++ b/backend/tests/crud/test_adminmsg.py @@ -22,10 +22,12 @@ def adminmessage_create(faker: typing.Any) -> AdminMessageCreate: async def test_create_get_adminmessage( db_session: AsyncSession, adminmessage_create: AdminMessageCreate ): + # act: adminmessage_postcreate = await crud.adminmessage.create( session=db_session, obj_in=adminmessage_create ) stored_item = await crud.adminmessage.get(session=db_session, id=adminmessage_postcreate.id) + # assert: assert stored_item assert adminmessage_postcreate.id == stored_item.id assert adminmessage_postcreate.text == stored_item.text @@ -37,6 +39,7 @@ async def test_create_get_adminmessage( async def test_create_update_adminmessage( db_session: AsyncSession, faker: typing.Any, adminmessage_create: AdminMessageCreate ) -> None: + # act: adminmessage_update = AdminMessageUpdate( title=faker.sentence(), ) @@ -46,6 +49,7 @@ async def test_create_update_adminmessage( adminmessage_postupdate = await crud.adminmessage.update( session=db_session, db_obj=adminmessage_postcreate, obj_in=adminmessage_update ) + # assert: assert adminmessage_postupdate assert adminmessage_postupdate.title == adminmessage_update.title @@ -54,6 +58,7 @@ async def test_create_update_adminmessage( async def test_delete_adminmessage( db_session: AsyncSession, adminmessage_create: AdminMessageCreate ): + # act: adminmessage_postcreate = await crud.adminmessage.create( session=db_session, obj_in=adminmessage_create ) @@ -61,4 +66,5 @@ async def test_delete_adminmessage( adminmessage_postdelete = await crud.adminmessage.get( session=db_session, id=adminmessage_postcreate.id ) + # assert: assert adminmessage_postdelete is None diff --git a/backend/tests/crud/test_bookmark.py b/backend/tests/crud/test_bookmark.py index 579a9d89..48466581 100644 --- a/backend/tests/crud/test_bookmark.py +++ b/backend/tests/crud/test_bookmark.py @@ -20,8 +20,10 @@ def bookmark_create() -> BookmarkCreate: @pytest.mark.anyio async def test_create_get_bookmark(db_session: AsyncSession, bookmark_create: BookmarkCreate): """Test creating and retrieving a bookmark.""" + # act: bookmark_postcreate = await crud.bookmark.create(session=db_session, obj_in=bookmark_create) stored_item = await crud.bookmark.get(session=db_session, id=bookmark_postcreate.id) + # assert: assert stored_item assert bookmark_postcreate.id == stored_item.id assert bookmark_postcreate.obj_type == stored_item.obj_type @@ -31,17 +33,21 @@ async def test_create_get_bookmark(db_session: AsyncSession, bookmark_create: Bo @pytest.mark.anyio async def test_delete_bookmark(db_session: AsyncSession, bookmark_create: BookmarkCreate): """Test deleting a bookmark.""" + # act: bookmark_postcreate = await crud.bookmark.create(session=db_session, obj_in=bookmark_create) + # assert: await crud.bookmark.remove(session=db_session, id=bookmark_postcreate.id) @pytest.mark.anyio async def test_get_multi_by_user(db_session: AsyncSession, bookmark_create: BookmarkCreate): """Test retrieving multiple bookmarks by user.""" + # act: bookmark_postcreate = await crud.bookmark.create(session=db_session, obj_in=bookmark_create) stored_items = await crud.bookmark.get_multi_by_user( session=db_session, user_id=bookmark_postcreate.user ) + # assert: assert stored_items assert len(stored_items) == 1 assert bookmark_postcreate.id == stored_items[0].id @@ -52,6 +58,7 @@ async def test_get_multi_by_user(db_session: AsyncSession, bookmark_create: Book @pytest.mark.anyio async def test_get_by_user_and_obj(db_session: AsyncSession, bookmark_create: BookmarkCreate): """Test retrieving a bookmark by user and object.""" + # act: bookmark_postcreate = await crud.bookmark.create(session=db_session, obj_in=bookmark_create) stored_item = await crud.bookmark.get_by_user_and_obj( session=db_session, @@ -59,6 +66,7 @@ async def test_get_by_user_and_obj(db_session: AsyncSession, bookmark_create: Bo obj_type=bookmark_postcreate.obj_type, obj_id=bookmark_postcreate.obj_id, ) + # assert: assert stored_item assert bookmark_postcreate.id == stored_item.id assert bookmark_postcreate.obj_type == stored_item.obj_type diff --git a/backend/tests/crud/test_caseinfo.py b/backend/tests/crud/test_caseinfo.py index 2ab7921e..ea779fb4 100644 --- a/backend/tests/crud/test_caseinfo.py +++ b/backend/tests/crud/test_caseinfo.py @@ -35,8 +35,10 @@ def case_create() -> CaseInfoCreate: @pytest.mark.anyio async def test_create_get_caseinfo(db_session: AsyncSession, case_create: CaseInfoCreate): """Test creating and retrieving a caseinfo.""" + # act: caseinfo_postcreate = await crud.caseinfo.create(session=db_session, obj_in=case_create) stored_item = await crud.caseinfo.get(session=db_session, id=caseinfo_postcreate.id) + # assert: assert stored_item assert caseinfo_postcreate.id == stored_item.id assert caseinfo_postcreate.pseudonym == stored_item.pseudonym @@ -54,17 +56,21 @@ async def test_create_get_caseinfo(db_session: AsyncSession, case_create: CaseIn @pytest.mark.anyio async def test_delete_caseinfo(db_session: AsyncSession, case_create: CaseInfoCreate): """Test deleting a caseinfo.""" + # act: caseinfo_postcreate = await crud.caseinfo.create(session=db_session, obj_in=case_create) + # assert: await crud.caseinfo.remove(session=db_session, id=caseinfo_postcreate.id) @pytest.mark.anyio async def test_get_multi_by_user(db_session: AsyncSession, case_create: CaseInfoCreate): """Test retrieving multiple caseinfos by user.""" + # act: caseinfo_postcreate = await crud.caseinfo.create(session=db_session, obj_in=case_create) stored_items = await crud.caseinfo.get_multi_by_user( session=db_session, user_id=caseinfo_postcreate.user ) + # assert: assert stored_items assert len(stored_items) == 1 assert caseinfo_postcreate.id == stored_items[0].id @@ -83,11 +89,13 @@ async def test_get_multi_by_user(db_session: AsyncSession, case_create: CaseInfo @pytest.mark.anyio async def test_get_by_user(db_session: AsyncSession, case_create: CaseInfoCreate): """Test retrieving a caseinfo by user.""" + # act: caseinfo_postcreate = await crud.caseinfo.create(session=db_session, obj_in=case_create) stored_item = await crud.caseinfo.get_by_user( session=db_session, user_id=caseinfo_postcreate.user, ) + # assert: assert stored_item assert caseinfo_postcreate.id == stored_item.id assert caseinfo_postcreate.pseudonym == stored_item.pseudonym diff --git a/backend/tests/crud/test_clinvarsub.py b/backend/tests/crud/test_clinvarsub.py index 0c7e249f..84aac1c5 100644 --- a/backend/tests/crud/test_clinvarsub.py +++ b/backend/tests/crud/test_clinvarsub.py @@ -20,10 +20,12 @@ async def test_create_get_submittingorg( db_session: AsyncSession, submittingorg_create: SubmittingOrgCreate ): """Test creating and retrieving a SubmittingOrg.""" + # act: submittingorg_postcreate = await crud.submittingorg.create( session=db_session, obj_in=submittingorg_create ) stored_item = await crud.submittingorg.get(session=db_session, id=submittingorg_postcreate.id) + # assert: assert stored_item assert submittingorg_postcreate.id == stored_item.id assert submittingorg_postcreate.label == stored_item.label @@ -34,6 +36,7 @@ async def test_create_get_submittingorg( async def test_create_update_submittingorg( db_session: AsyncSession, faker: LoremProvider, submittingorg_create: SubmittingOrgCreate ) -> None: + # arrange: submittingorg_update = SubmittingOrgUpdate( label=faker.sentence(), clinvar_api_token=faker.word(), @@ -41,9 +44,11 @@ async def test_create_update_submittingorg( submittingorg_postcreate = await crud.submittingorg.create( session=db_session, obj_in=submittingorg_create ) + # act: submittingorg_postupdate = await crud.submittingorg.update( session=db_session, db_obj=submittingorg_postcreate, obj_in=submittingorg_update ) + # assert: assert submittingorg_postupdate assert submittingorg_postupdate.label == submittingorg_update.label @@ -53,10 +58,14 @@ async def test_delete_submittingorg( db_session: AsyncSession, submittingorg_create: SubmittingOrgCreate ): """Test deleting a submittingorg.""" + # arrange: submittingorg_postcreate = await crud.submittingorg.create( session=db_session, obj_in=submittingorg_create ) + # act: await crud.submittingorg.remove(session=db_session, id=submittingorg_postcreate.id) + # assert: + # In this case, you might want to assert that the item no longer exists in the database. @pytest.mark.anyio @@ -64,12 +73,14 @@ async def test_create_get_submissionthread( db_session: AsyncSession, submissionthread_create: SubmissionThreadCreate ): """Test creating and retrieving a SubmittingOrg.""" + # act: submissionthread_postcreate = await crud.submissionthread.create( session=db_session, obj_in=submissionthread_create ) stored_item = await crud.submissionthread.get( session=db_session, id=submissionthread_postcreate.id ) + # assert: assert stored_item assert submissionthread_postcreate.id == stored_item.id assert submissionthread_postcreate.desired_presence == stored_item.desired_presence @@ -82,18 +93,22 @@ async def test_create_get_submissionthread( async def test_create_update_submissionthread( db_session: AsyncSession, submissionthread_create: SubmissionThreadCreate ) -> None: + """Test creating and updating a submissionthread.""" + # arrange: submissionthread_update = SubmissionThreadUpdate( status=SubmissionThreadStatus.IN_PROGRESS, effective_scv="SCV000000001", desired_presence=VariantPresence.PRESENT, effective_presence=VariantPresence.PRESENT, ) + # act: submissionthread_postcreate = await crud.submissionthread.create( session=db_session, obj_in=submissionthread_create ) submissionthread_postupdate = await crud.submissionthread.update( session=db_session, db_obj=submissionthread_postcreate, obj_in=submissionthread_update ) + # assert: assert submissionthread_postupdate assert submissionthread_postcreate.status == submissionthread_update.status assert submissionthread_postcreate.desired_presence == submissionthread_update.desired_presence @@ -124,12 +139,15 @@ async def test_create_get_submissionactivity( db_session: AsyncSession, submissionactivity_create: SubmissionActivityCreate ): """Test creating and retrieving a SubmittingOrg.""" + # arrange: submissionactivity_postcreate = await crud.submissionactivity.create( session=db_session, obj_in=submissionactivity_create ) + # act: stored_item = await crud.submissionactivity.get( session=db_session, id=submissionactivity_postcreate.id ) + # assert: assert stored_item assert submissionactivity_postcreate.id == stored_item.id assert submissionactivity_postcreate.submissionthread_id == stored_item.submissionthread_id diff --git a/backend/tests/etc/test_utils.py b/backend/tests/etc/test_utils.py index 2d871624..7f790f0d 100644 --- a/backend/tests/etc/test_utils.py +++ b/backend/tests/etc/test_utils.py @@ -9,6 +9,7 @@ @pytest.fixture def mock_settings(monkeypatch): """Mock settings for sending emails.""" + # arrange: monkeypatch.setattr("app.core.config.settings.EMAILS_ENABLED", True) monkeypatch.setattr("app.core.config.settings.EMAILS_FROM_NAME", "Test From") monkeypatch.setattr("app.core.config.settings.EMAILS_FROM_EMAIL", "from@test.com") @@ -17,6 +18,7 @@ def mock_settings(monkeypatch): @pytest.fixture def mock_email_send(monkeypatch): """Mock the send method of emails.Message.""" + # arrange: mock_send = MagicMock() monkeypatch.setattr("emails.Message.send", mock_send) return mock_send @@ -25,6 +27,7 @@ def mock_email_send(monkeypatch): @pytest.fixture def mock_open_template(monkeypatch): """Mock the open method for reading email templates.""" + # arrange: m = mock_open(read_data="email content") monkeypatch.setattr("builtins.open", m) return m @@ -32,8 +35,9 @@ def mock_open_template(monkeypatch): def test_send_email(mock_settings, mock_email_send, mock_open_template): """Test sending an email.""" + # act: send_email("to@test.com", "Test Subject", "

Test Body

") - + # assert: mock_email_send.assert_called_once() args, kwargs = mock_email_send.call_args assert kwargs["to"] == "to@test.com" @@ -41,8 +45,9 @@ def test_send_email(mock_settings, mock_email_send, mock_open_template): def test_send_test_email(mock_settings, mock_email_send, mock_open_template): """Test sending a test email.""" + # act: send_test_email("to@test.com") - + # assert: mock_email_send.assert_called_once() args, kwargs = mock_email_send.call_args assert kwargs["to"] == "to@test.com" @@ -50,6 +55,7 @@ def test_send_test_email(mock_settings, mock_email_send, mock_open_template): def test_send_user_verify_email(mock_settings, mock_email_send, mock_open_template): """Test sending a user verification email.""" + # arrange: request_scope = { "type": "http", "scheme": "https", @@ -58,8 +64,9 @@ def test_send_user_verify_email(mock_settings, mock_email_send, mock_open_templa "headers": [(b"host", b"testserver")], } request = Request(scope=request_scope) + # act: send_user_verify_email("to@test.com", "token123", request) - + # assert: mock_email_send.assert_called_once() args, kwargs = mock_email_send.call_args assert kwargs["to"] == "to@test.com" diff --git a/backend/tests/test_main.py b/backend/tests/test_main.py index 1a142ed8..ca5e9fb8 100644 --- a/backend/tests/test_main.py +++ b/backend/tests/test_main.py @@ -8,8 +8,11 @@ @pytest.mark.anyio async def test_version(monkeypatch: MonkeyPatch, client: TestClient): """Test version endpoint.""" + # arrange: monkeypatch.setattr(settings, "REEV_VERSION", "1.2.3") + # act: response = client.get("/internal/version") + # assert: assert response.status_code == 200 assert response.text == "1.2.3" @@ -17,12 +20,12 @@ async def test_version(monkeypatch: MonkeyPatch, client: TestClient): @pytest.mark.anyio async def test_version_no_version(monkeypatch: MonkeyPatch, fp, client: TestClient): """Test version endpoint with no version.""" + # arrange: monkeypatch.setattr(settings, "REEV_VERSION", None) - # We mock the output of ``git describe`` as subprocesses will be triggered - # internally. fp.register(["git", "describe", "--tags", "--dirty"], stdout="v0.0.0-16-g7a4205d-dirty") + # act: response = client.get("/internal/version") - + # assert: assert response.status_code == 200 assert response.text == "v0.0.0-16-g7a4205d-dirty" @@ -30,7 +33,9 @@ async def test_version_no_version(monkeypatch: MonkeyPatch, fp, client: TestClie @pytest.mark.anyio async def test_favicon(client: TestClient): """Test favicon endpoint.""" + # act: response = client.get("/favicon.ico") + # assert: assert response.status_code == 200 assert response.headers["content-type"] == "image/vnd.microsoft.icon" @@ -38,9 +43,12 @@ async def test_favicon(client: TestClient): @pytest.mark.anyio async def test_frontend_settings(monkeypatch: MonkeyPatch, client: TestClient): """Test frontend settings endpoint.""" + # arrange: monkeypatch.setattr(settings, "MATOMO_HOST", "matomo.example.com") monkeypatch.setattr(settings, "MATOMO_SITE_ID", 42) + # act: response = client.get("/internal/frontend-settings") + # assert: assert response.status_code == 200 assert response.json() == {"matomo_host": "matomo.example.com", "matomo_site_id": 42} @@ -48,8 +56,11 @@ async def test_frontend_settings(monkeypatch: MonkeyPatch, client: TestClient): @pytest.mark.anyio async def test_frontend_settings_no_matomo(monkeypatch: MonkeyPatch, client: TestClient): """Test frontend settings endpoint with no matomo.""" + # arrange: monkeypatch.setattr(settings, "MATOMO_HOST", None) monkeypatch.setattr(settings, "MATOMO_SITE_ID", None) + # act: response = client.get("/internal/frontend-settings") + # assert: assert response.status_code == 200 assert response.json() == {"matomo_host": None, "matomo_site_id": None} diff --git a/docs/dev_backend.rst b/docs/dev_backend.rst index e119548c..efb2a550 100644 --- a/docs/dev_backend.rst +++ b/docs/dev_backend.rst @@ -5,3 +5,18 @@ Backend Development =================== This section describes the best practices to use for backend development. + +----- +Types +----- + +- prefer to use semantic-carrying types, e.g., ``enum.Enum`` over ``bool`` or ``int`` + +-------------- +Test Structure +-------------- + +- use assemble, act, assert structure e.g., `as described here `__ +- properly use fixtures, place them in ``conftest.py`` files +- use ``pytest.mark.parameterize`` to condense multiple test cases into one tests but don't overdo it + - see ``test_clinvarsub.py`` for an example (e.g., ``test_create_submissionthreads``) that is still good a trade-off but probably shows the highest degree of complexity (read: different test code paths) diff --git a/docs/dev_frontend.rst b/docs/dev_frontend.rst index d9c6ebd7..5e780081 100644 --- a/docs/dev_frontend.rst +++ b/docs/dev_frontend.rst @@ -28,6 +28,7 @@ Consider the following structure, an example is given below. - define fixture data - put larger fixture data into ``.json`` files within the ``__tests__`` folders (will go to LFS by our ``.gitattributes`` configuration) - define the tests +- use assemble, act, assert structure e.g., `as described here `__ - use ``describe.concurrent`` to describe the tests, usually one block per ``.spec.ts`` file - use ``it`` to define the tests - use ``async () => { ... }`` only when necessary, e.g., for ``await nextTick()``