This commit is contained in:
Jens L 2020-09-11 23:21:11 +02:00 committed by GitHub
parent 3f5d30e6fe
commit 23cccebb96
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
59 changed files with 403 additions and 254 deletions

View File

@ -1,7 +1,7 @@
all: lint-fix lint coverage gen
coverage:
coverage run --concurrency=multiprocessing manage.py test passbook --failfast
coverage run --concurrency=multiprocessing manage.py test --failfast
coverage combine
coverage html
coverage report

View File

@ -59,5 +59,6 @@ docker = "*"
pylint = "*"
pylint-django = "*"
selenium = "*"
unittest-xml-reporting = "*"
prospector = "*"
pytest = "*"
pytest-django = "*"

167
Pipfile.lock generated
View File

@ -1,7 +1,7 @@
{
"_meta": {
"hash": {
"sha256": "a798bbd0b97857cac136c1743b8d6ad8bf8c3d95e2760c71d324bb2a7f47f678"
"sha256": "80570636236962f4b934a884817292de9f7bb48520aa964afc2959b0f795fb57"
},
"pipfile-spec": 6,
"requires": {
@ -28,6 +28,7 @@
"sha256:70cdb10628468ff14e57ec2f751c7aa9e48e7e3651cfd62d431213c0c4e58f21",
"sha256:aa7f313fb887c91f15474c1229907a04dac0b8135822d6603437803424c0aa59"
],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'",
"version": "==2.6.1"
},
"asgiref": {
@ -35,6 +36,7 @@
"sha256:7e51911ee147dd685c3c8b805c0ad0cb58d360987b56953878f8c06d2d1c6f1a",
"sha256:9fc6fb5d39b8af147ba40765234fa822b39818b12cc80b35ad9b0cef3a476aed"
],
"markers": "python_version >= '3.5'",
"version": "==3.2.10"
},
"async-timeout": {
@ -42,6 +44,7 @@
"sha256:0c3c816a028d47f659d6ff5c745cb2acf1f966da1fe5c19c77a70282b25f4c5f",
"sha256:4291ca197d287d274d0b6cb5d6f8f8f82d434ed288f962539ff18cc9012f9ea3"
],
"markers": "python_full_version >= '3.5.3'",
"version": "==3.0.1"
},
"attrs": {
@ -49,6 +52,7 @@
"sha256:26b54ddbbb9ee1d34d5d3668dd37d6cf74990ab23c828c2888dccdceee395594",
"sha256:fce7fc47dfc976152e82d53ff92fa0407700c21acd20886a13777a0d20e655dc"
],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==20.2.0"
},
"autobahn": {
@ -56,6 +60,7 @@
"sha256:24ce276d313e84d68241c3aef30d484f352b90a40168981b3640312c821df77b",
"sha256:86bbce30cdd407137c57670993a8f9bfdfe3f8e994b889181d85e844d5aa8dfb"
],
"markers": "python_version >= '3.5'",
"version": "==20.7.1"
},
"automat": {
@ -92,6 +97,7 @@
"sha256:513d4ff98dd27f85743a8dc0e92f55ddb1b49e060c2d5961512855cda2c01a98",
"sha256:bbaa39c3dede00175df2dc2b03d0cf18dd2d32a7de7beb68072d13043c9edb20"
],
"markers": "python_version ~= '3.5'",
"version": "==4.1.1"
},
"celery": {
@ -170,6 +176,7 @@
"sha256:d2b5255c7c6349bc1bd1e59e08cd12acbbd63ce649f2588755783aa94dfb6b1a",
"sha256:dacca89f4bfadd5de3d7489b7c8a566eee0d3676333fbb50030263894c38c0dc"
],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'",
"version": "==7.1.2"
},
"constantly": {
@ -338,6 +345,7 @@
"sha256:6dd02d5a4bd2516fb93f80360673bf540c3b6641fec8766b1da2870a5aa00b32",
"sha256:8b1ac62c581dbc5799b03e535854b92fc4053ecfe74bad3f9c05782063d4196b"
],
"markers": "python_version >= '3.5'",
"version": "==3.11.1"
},
"djangorestframework-guardian": {
@ -354,6 +362,7 @@
"sha256:9e4d7ecfc600058e07ba661411a2b7de2fd0fafa17d1a7f7361cd47b1175c827",
"sha256:a2aeea129088da402665e92e0b25b04b073c04b2dce4ab65caaa38b7ce2e1a99"
],
"markers": "python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==0.15.2"
},
"drf-yasg": {
@ -383,6 +392,7 @@
"hashes": [
"sha256:b1bead90b70cf6ec3f0710ae53a525360fa360d306a86583adc6bf83a4db537d"
],
"markers": "python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==0.18.2"
},
"google-auth": {
@ -390,6 +400,7 @@
"sha256:bcbd9f970e7144fe933908aa286d7a12c44b7deb6d78a76871f0377a29d09789",
"sha256:f4d5093f13b1b1c0a434ab1dc851cd26a983f86a4d75c95239974e33ed406a87"
],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==1.21.1"
},
"gunicorn": {
@ -456,6 +467,7 @@
"sha256:e64be68255234bb489a574c4f2f8df7029c98c81ec4d160d6cd836e7f0679390",
"sha256:e82d6b930e02e80e5109b678c663a9ed210680ded81c1abaf54635d88d1da298"
],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==1.1.0"
},
"httptools": {
@ -502,6 +514,7 @@
"sha256:1a29730d366e996aaacffb2f1f1cb9593dc38e2ddd30c91250c6dde09ea9b417",
"sha256:f38b2b640938a4f35ade69ac3d053042959b62a0f1076a5bbaa1b9526605a8a2"
],
"markers": "python_version >= '3.5'",
"version": "==0.5.1"
},
"itypes": {
@ -516,6 +529,7 @@
"sha256:89aab215427ef59c34ad58735269eb58b1a5808103067f7bb9d5836c651b3bb0",
"sha256:f0a4641d3cf955324a89c04f3d94663aa4d638abe8f733ecd3582848e1c37035"
],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'",
"version": "==2.11.2"
},
"jmespath": {
@ -523,6 +537,7 @@
"sha256:b85d0567b8666149a93172712e68920734333c0ce7e89b78b3e987f71e5ed4f9",
"sha256:cdf6525904cc597730141d61b36f2e4b8ecc257c420fa2f4549bac2c2d0cb72f"
],
"markers": "python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==0.10.0"
},
"jsonschema": {
@ -537,6 +552,7 @@
"sha256:be48cdffb54a2194d93ad6533d73f69408486483d189fe9f5990ee24255b0e0a",
"sha256:ca1b45faac8c0b18493d02a8571792f3c40291cf2bcf1f55afed3d8f3aa7ba74"
],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'",
"version": "==4.6.11"
},
"kubernetes": {
@ -549,8 +565,11 @@
},
"ldap3": {
"hashes": [
"sha256:10bdd23b612e942ce90ea4dbc744dfd88735949833e46c5467a2dcf68e60f469",
"sha256:37d633e20fa360c302b1263c96fe932d40622d0119f1bddcb829b03462eeeeb7",
"sha256:7c3738570766f5e5e74a56fade15470f339d5c436d821cf476ef27da0a4de8b0"
"sha256:7c3738570766f5e5e74a56fade15470f339d5c436d821cf476ef27da0a4de8b0",
"sha256:8f59a7b5399555b22db06f153daa76c77ded2dd84bc0f0ffe5b0b33901b6eac4",
"sha256:bed71c6ce2f70a00a330eed0c8370664c065239d45bcbe1b82517b6f6eed7f25"
],
"index": "pypi",
"version": "==2.8.1"
@ -628,6 +647,7 @@
"sha256:e249096428b3ae81b08327a63a485ad0878de3fb939049038579ac0ef61e17e7",
"sha256:e8313f01ba26fbbe36c7be1966a7b7424942f670f38e666995b88d012765b9be"
],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==1.1.1"
},
"msgpack": {
@ -658,6 +678,7 @@
"sha256:bee41cc35fcca6e988463cacc3bcb8a96224f470ca547e697b604cc697b2f889",
"sha256:df884cd6cbe20e32633f1db1072e9356f53638e4361bef4e8b03c9127c9328ea"
],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==3.1.0"
},
"packaging": {
@ -715,15 +736,37 @@
},
"pyasn1": {
"hashes": [
"sha256:014c0e9976956a08139dc0712ae195324a75e142284d5f87f1a87ee1b068a359",
"sha256:03840c999ba71680a131cfaee6fab142e1ed9bbd9c693e285cc6aca0d555e576",
"sha256:0458773cfe65b153891ac249bcf1b5f8f320b7c2ce462151f8fa74de8934becf",
"sha256:08c3c53b75eaa48d71cf8c710312316392ed40899cb34710d092e96745a358b7",
"sha256:39c7e2ec30515947ff4e87fb6f456dfc6e84857d34be479c9d4a4ba4bf46aa5d",
"sha256:aef77c9fb94a3ac588e87841208bdec464471d9871bd5050a287cc9a475cd0ba"
"sha256:5c9414dcfede6e441f7e8f81b43b34e834731003427e5b09e4e00e3172a10f00",
"sha256:6e7545f1a61025a4e58bb336952c5061697da694db1cae97b116e9c46abcf7c8",
"sha256:78fa6da68ed2727915c4767bb386ab32cdba863caa7dbe473eaae45f9959da86",
"sha256:7ab8a544af125fb704feadb008c99a88805126fb525280b2270bb25cc1d78a12",
"sha256:99fcc3c8d804d1bc6d9a099921e39d827026409a58f2a720dcdb89374ea0c776",
"sha256:aef77c9fb94a3ac588e87841208bdec464471d9871bd5050a287cc9a475cd0ba",
"sha256:e89bf84b5437b532b0803ba5c9a5e054d21fec423a89952a74f87fa2c9b7bce2",
"sha256:fec3e9d8e36808a28efb59b489e4528c10ad0f480e57dcc32b4de5c9d8c9fdf3"
],
"version": "==0.4.8"
},
"pyasn1-modules": {
"hashes": [
"sha256:0845a5582f6a02bb3e1bde9ecfc4bfcae6ec3210dd270522fee602365430c3f8",
"sha256:0fe1b68d1e486a1ed5473f1302bd991c1611d319bba158e98b106ff86e1d7199",
"sha256:15b7c67fabc7fc240d87fb9aabf999cf82311a6d6fb2c70d00d3d0604878c811",
"sha256:426edb7a5e8879f1ec54a1864f16b882c2837bfd06eee62f2c982315ee2473ed",
"sha256:65cebbaffc913f4fe9e4808735c95ea22d7a7775646ab690518c056784bc21b4",
"sha256:905f84c712230b2c592c19470d3ca8d552de726050d1d1716282a1f6146be65e",
"sha256:a50b808ffeb97cb3601dd25981f6b016cbb3d31fbf57a8b8a87428e6158d0c74"
"sha256:a50b808ffeb97cb3601dd25981f6b016cbb3d31fbf57a8b8a87428e6158d0c74",
"sha256:a99324196732f53093a84c4369c996713eb8c89d360a496b599fb1a9c47fc3eb",
"sha256:b80486a6c77252ea3a3e9b1e360bc9cf28eaac41263d173c032581ad2f20fe45",
"sha256:c29a5e5cc7a3f05926aff34e097e84f8589cd790ce0ed41b67aed6857b26aafd",
"sha256:cbac4bc38d117f2a49aeedec4407d23e8866ea4ac27ff2cf7fb3e5b570df19e0",
"sha256:f39edd8c4ecaa4556e989147ebf219227e2cd2e8a43c7e7fcb1f1c18c5fd6a3d",
"sha256:fe0644d9ab041506b62782e92b06b8c68cca799e1a9636ec398675459e031405"
],
"version": "==0.2.8"
},
@ -732,6 +775,7 @@
"sha256:2d475327684562c3a96cc71adf7dc8c4f0565175cf86b6d7a404ff4c771f15f0",
"sha256:7582ad22678f0fcd81102833f60ef8d0e57288b6b5fb00323d101be910e35705"
],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==2.20"
},
"pycryptodome": {
@ -803,6 +847,7 @@
"sha256:ea4d4b58f9bc34e224ef4b4604a6be03d72ef1f8c486391f970205f6733dbc46",
"sha256:f60b3484ce4be04f5da3777c51c5140d3fe21cdd6674f2b6568f41c8130bcdeb"
],
"markers": "python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==3.9.8"
},
"pyhamcrest": {
@ -810,6 +855,7 @@
"sha256:412e00137858f04bde0729913874a48485665f2d36fe9ee449f26be864af9316",
"sha256:7ead136e03655af85069b6f47b23eb7c3e5c221aa9f022a4fbb499f5b7308f29"
],
"markers": "python_version >= '3.5'",
"version": "==2.0.2"
},
"pyjwkest": {
@ -831,19 +877,21 @@
"sha256:c203ec8783bf771a155b207279b9bccb8dea02d8f0c9e5f8ead507bc3246ecc1",
"sha256:ef9d7589ef3c200abe66653d3f1ab1033c3c419ae9b9bdb1240a85b024efc88b"
],
"markers": "python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==2.4.7"
},
"pyrsistent": {
"hashes": [
"sha256:27515d2d5db0629c7dadf6fbe76973eb56f098c1b01d36de42eb69220d2c19e4"
"sha256:28669905fe725965daa16184933676547c5bb40a5153055a8dee2a4bd7933ad3"
],
"version": "==0.17.2"
"version": "==0.16.0"
},
"python-dateutil": {
"hashes": [
"sha256:73ebfe9dbf22e832286dafa60473e4cd239f8592f699aa5adaf10050e6e1823c",
"sha256:75bb3f31ea686f1197762692a9ee6a7550b59fc6ca3a1f4b5d7e32fb98e2da2a"
],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==2.8.1"
},
"pytz": {
@ -883,6 +931,7 @@
"sha256:0e7e0cfca8660dea8b7d5cd8c4f6c5e29e11f31158c0b0ae91a397f00e5a05a2",
"sha256:432b788c4530cfe16d8d943a09d40ca6c16149727e4afe8c2c9d5580c59d9f24"
],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'",
"version": "==3.5.3"
},
"requests": {
@ -890,12 +939,14 @@
"sha256:b3559a131db72c33ee969480840fff4bb6dd111de7dd27c8ee1f820f4f00231b",
"sha256:fe75cc94a9443b9246fc7049224f75604b113c36acb93f87b80ed42c44cbb898"
],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'",
"version": "==2.24.0"
},
"requests-oauthlib": {
"hashes": [
"sha256:7f71572defaecd16372f9006f33c2ec8c077c3cfa6f5911a9a90202beb513f3d",
"sha256:b4261601a71fd721a8bd6d7aa1cc1d6a8a93b4a9f5e96626f8e4d91e8beeaa6a"
"sha256:b4261601a71fd721a8bd6d7aa1cc1d6a8a93b4a9f5e96626f8e4d91e8beeaa6a",
"sha256:fa6c47b933f01060936d87ae9327fead68768b69c6c9ea2109c48be30f2d4dbc"
],
"index": "pypi",
"version": "==1.3.0"
@ -940,7 +991,7 @@
"sha256:e9f7d1d8c26a6a12c23421061f9022bb62704e38211fe375c645485f38df34a2",
"sha256:f6061a31880c1ed6b6ce341215336e2f3d0c1deccd84957b6fa8ca474b41e89f"
],
"markers": "platform_python_implementation == 'CPython' and python_version < '3.9'",
"markers": "python_version < '3.9' and platform_python_implementation == 'CPython'",
"version": "==0.2.2"
},
"s3transfer": {
@ -979,6 +1030,7 @@
"sha256:30639c035cdb23534cd4aa2dd52c3bf48f06e5f4a941509c8bafd8ce11080259",
"sha256:8b74bedcbbbaca38ff6d7491d76f2b06b3592611af620f8426e82dddb04a5ced"
],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==1.15.0"
},
"sqlparse": {
@ -986,6 +1038,7 @@
"sha256:022fb9c87b524d1f7862b3037e541f68597a730a8843245c349fc93e1643dc4e",
"sha256:e162203737712307dfe78860cc56c8da8a852ab2ee33750e33aeadf38d12c548"
],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==0.3.1"
},
"structlog": {
@ -1033,6 +1086,7 @@
"sha256:f058bd0168271de4dcdc39845b52dd0a4a2fecf5f1246335f13f5e96eaebb467",
"sha256:f3c19e5bd42bbe4bf345704ad7c326c74d3fd7a1b3844987853bef180be638d4"
],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'",
"version": "==20.3.0"
},
"txaio": {
@ -1040,6 +1094,7 @@
"sha256:17938f2bca4a9cabce61346758e482ca4e600160cbc28e861493eac74a19539d",
"sha256:38a469daf93c37e5527cb062653d6393ae11663147c42fab7ddc3f6d00d434ae"
],
"markers": "python_version >= '3.5'",
"version": "==20.4.1"
},
"uritemplate": {
@ -1047,6 +1102,7 @@
"sha256:07620c3f3f8eed1f12600845892b0e036a2420acf513c53f7de0abd911a5894f",
"sha256:5af8ad10cec94f215e3f48112de2022e1d5a37ed427fbd88652fa908f2ab7cae"
],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==3.0.1"
},
"urllib3": {
@ -1058,7 +1114,6 @@
"sha256:e7983572181f5e1522d9c98453462384ee92a0be7fac5f1413a1e35c56cc0461"
],
"index": "pypi",
"markers": null,
"version": "==1.25.10"
},
"uvicorn": {
@ -1089,6 +1144,7 @@
"sha256:133ee6d7a9016f177ddeaf191c1f58421a1dcc6ee9a42c58b34bed40e1d2cd87",
"sha256:ea4947cc56d1fd6f2095c8d543ee25dad966f78692528e68b4fada11ba3f98af"
],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==1.3.0"
},
"websocket-client": {
@ -1123,6 +1179,7 @@
"sha256:e898a0863421650f0bebac8ba40840fc02258ef4714cb7e1fd76b6a6354bda36",
"sha256:f8a7bff6e8664afc4e6c28b983845c5bc14965030e3fb98789734d416af77c4b"
],
"markers": "python_full_version >= '3.6.1'",
"version": "==8.1"
},
"zope.interface": {
@ -1168,6 +1225,7 @@
"sha256:f68bf937f113b88c866d090fea0bc52a098695173fc613b055a17ff0cf9683b6",
"sha256:fb55c182a3f7b84c1a2d6de5fa7b1a05d4660d866b91dbf8d74549c57a1499e8"
],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'",
"version": "==5.1.0"
}
},
@ -1184,6 +1242,7 @@
"sha256:7e51911ee147dd685c3c8b805c0ad0cb58d360987b56953878f8c06d2d1c6f1a",
"sha256:9fc6fb5d39b8af147ba40765234fa822b39818b12cc80b35ad9b0cef3a476aed"
],
"markers": "python_version >= '3.5'",
"version": "==3.2.10"
},
"astroid": {
@ -1191,6 +1250,7 @@
"sha256:4c17cea3e592c21b6e222f673868961bad77e1f985cb1694ed077475a89229c1",
"sha256:d8506842a3faf734b81599c8b98dcc423de863adcc1999248480b18bd31a0f38"
],
"markers": "python_version >= '3.5'",
"version": "==2.4.1"
},
"attrs": {
@ -1198,6 +1258,7 @@
"sha256:26b54ddbbb9ee1d34d5d3668dd37d6cf74990ab23c828c2888dccdceee395594",
"sha256:fce7fc47dfc976152e82d53ff92fa0407700c21acd20886a13777a0d20e655dc"
],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==20.2.0"
},
"autopep8": {
@ -1228,6 +1289,7 @@
"sha256:477f0e18a0d58e50bb3dbc9af7fcda464fd0ebfc7a6151d8888602d7153171a0",
"sha256:cd4f3a231305e405ed8944d8ff35bd742d9bc740ad62f483bd0ca21ce7131984"
],
"markers": "python_version >= '3.5'",
"version": "==1.0.0"
},
"bumpversion": {
@ -1257,6 +1319,7 @@
"sha256:d2b5255c7c6349bc1bd1e59e08cd12acbbd63ce649f2588755783aa94dfb6b1a",
"sha256:dacca89f4bfadd5de3d7489b7c8a566eee0d3676333fbb50030263894c38c0dc"
],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'",
"version": "==7.1.2"
},
"colorama": {
@ -1343,6 +1406,7 @@
"sha256:15e351d19611c887e482fb960eae4d44845013cc142d42896e9862f775d8cf5c",
"sha256:f04b9fcbac03b0a3e58c0ab3a0ecc462e023a9faf046d57794184028123aa208"
],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==3.8.3"
},
"flake8-polyfill": {
@ -1357,6 +1421,7 @@
"sha256:91f36bfb1ab7949b3b40e23736db18231bf7593edada2ba5c3a174a7b23657ac",
"sha256:c9e1f2d0db7ddb9a704c2a0217be31214e91a4fe1dea1efad19ae42ba0c285c9"
],
"markers": "python_version >= '3.4'",
"version": "==4.0.5"
},
"gitpython": {
@ -1364,6 +1429,7 @@
"sha256:080bf8e2cf1a2b907634761c2eaefbe83b69930c94c66ad11b65a8252959f912",
"sha256:1858f4fd089abe92ae465f01d5aaaf55e937eca565fb2c1fce35a51b5f85c910"
],
"markers": "python_version >= '3.4'",
"version": "==3.1.8"
},
"idna": {
@ -1373,11 +1439,19 @@
],
"version": "==2.10"
},
"iniconfig": {
"hashes": [
"sha256:80cf40c597eb564e86346103f609d74efce0f6b4d4f30ec8ce9e2c26411ba437",
"sha256:e5f92f89355a67de0595932a6c6c02ab4afddc6fcdc0bfc5becd0d60884d3f69"
],
"version": "==1.0.1"
},
"isort": {
"hashes": [
"sha256:54da7e92468955c4fceacd0c86bd0ec997b0e1ee80d97f67c35a78b719dccab1",
"sha256:6e811fcb295968434526407adb8796944f1988c5b65e8139058f2014cbe100fd"
],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==4.3.21"
},
"lazy-object-proxy": {
@ -1404,6 +1478,7 @@
"sha256:efa1909120ce98bbb3777e8b6f92237f5d5c8ea6758efea36a473e1d38f7d3e4",
"sha256:f3900e8a5de27447acbf900b4750b0ddfd7ec1ea7fbaf11dfa911141bc522af0"
],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==1.4.3"
},
"mccabe": {
@ -1413,6 +1488,22 @@
],
"version": "==0.6.1"
},
"more-itertools": {
"hashes": [
"sha256:6f83822ae94818eae2612063a5101a7311e68ae8002005b5e05f03fd74a86a20",
"sha256:9b30f12df9393f0d28af9210ff8efe48d10c94f73e5daf886f10c4b0b0b4f03c"
],
"markers": "python_version >= '3.5'",
"version": "==8.5.0"
},
"packaging": {
"hashes": [
"sha256:4357f74f47b9c12db93624a82154e9b120fa8293699949152b22065d556079f8",
"sha256:998416ba6962ae7fbd6596850b80e17859a5753ba17c32284f67bfff33784181"
],
"index": "pypi",
"version": "==20.4"
},
"pathspec": {
"hashes": [
"sha256:7d91249d21749788d07a2d0f94147accd8f845507400749ea19c1ec9054a12b0",
@ -1425,6 +1516,7 @@
"sha256:14bfd98f51c78a3dd22a1ef45cf194ad79eee4a19e8e1a0d5c7f8e81ffe182ea",
"sha256:5adc0f9fc64319d8df5ca1e4e06eea674c26b80e6f00c530b18ce6a6592ead15"
],
"markers": "python_version >= '2.6'",
"version": "==5.5.0"
},
"pep8-naming": {
@ -1434,6 +1526,14 @@
],
"version": "==0.10.0"
},
"pluggy": {
"hashes": [
"sha256:15b2acde666561e1298d71b523007ed7364de07029219b604cf808bfa1c765b0",
"sha256:966c145cd83c96502c3c3868f50408687b38434af77734af1e9ca461a4081d2d"
],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==0.13.1"
},
"prospector": {
"hashes": [
"sha256:43e5e187c027336b0e4c4aa6a82d66d3b923b5ec5b51968126132e32f9d14a2f"
@ -1441,11 +1541,20 @@
"index": "pypi",
"version": "==1.3.0"
},
"py": {
"hashes": [
"sha256:366389d1db726cd2fcfc79732e75410e5fe4d31db13692115529d34069a043c2",
"sha256:9ca6883ce56b4e8da7e79ac18787889fa5206c79dcc67fb065376cd2fe03f342"
],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==1.9.0"
},
"pycodestyle": {
"hashes": [
"sha256:2295e7b2f6b5bd100585ebcb1f616591b652db8a741695b3d8f5d28bdc934367",
"sha256:c58a7d2815e0e8d7972bf1803331fb0152f867bd89adf8a01dfd55085434192e"
],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==2.6.0"
},
"pydocstyle": {
@ -1453,6 +1562,7 @@
"sha256:19b86fa8617ed916776a11cd8bc0197e5b9856d5433b777f51a3defe13075325",
"sha256:aca749e190a01726a4fb472dd4ef23b5c9da7b9205c0a7857c06533de13fd678"
],
"markers": "python_version >= '3.5'",
"version": "==5.1.1"
},
"pyflakes": {
@ -1460,6 +1570,7 @@
"sha256:0d94e0e05a19e57a99444b6ddcf9a6eb2e5c68d3ca1e98e90707af8152c90a92",
"sha256:35b2d75ee967ea93b55750aa9edbbf72813e06a66ba54438df2cfac9e3c27fc8"
],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==2.2.0"
},
"pylint": {
@ -1497,6 +1608,30 @@
],
"version": "==0.6"
},
"pyparsing": {
"hashes": [
"sha256:c203ec8783bf771a155b207279b9bccb8dea02d8f0c9e5f8ead507bc3246ecc1",
"sha256:ef9d7589ef3c200abe66653d3f1ab1033c3c419ae9b9bdb1240a85b024efc88b"
],
"markers": "python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==2.4.7"
},
"pytest": {
"hashes": [
"sha256:85228d75db9f45e06e57ef9bf4429267f81ac7c0d742cc9ed63d09886a9fe6f4",
"sha256:8b6007800c53fdacd5a5c192203f4e531eb2a1540ad9c752e052ec0f7143dbad"
],
"index": "pypi",
"version": "==6.0.1"
},
"pytest-django": {
"hashes": [
"sha256:64f99d565dd9497af412fcab2989fe40982c1282d4118ff422b407f3f7275ca5",
"sha256:664e5f42242e5e182519388f01b9f25d824a9feb7cd17d8f863c8d776f38baf9"
],
"index": "pypi",
"version": "==3.9.0"
},
"pytz": {
"hashes": [
"sha256:a494d53b6d39c3c6e44c3bec237336e14305e4f29bbf800b599253057fbb79ed",
@ -1552,6 +1687,7 @@
"sha256:b3559a131db72c33ee969480840fff4bb6dd111de7dd27c8ee1f820f4f00231b",
"sha256:fe75cc94a9443b9246fc7049224f75604b113c36acb93f87b80ed42c44cbb898"
],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'",
"version": "==2.24.0"
},
"requirements-detector": {
@ -1579,6 +1715,7 @@
"sha256:30639c035cdb23534cd4aa2dd52c3bf48f06e5f4a941509c8bafd8ce11080259",
"sha256:8b74bedcbbbaca38ff6d7491d76f2b06b3592611af620f8426e82dddb04a5ced"
],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==1.15.0"
},
"smmap": {
@ -1586,6 +1723,7 @@
"sha256:54c44c197c819d5ef1991799a7e30b662d1e520f2ac75c9efbeb54a742214cf4",
"sha256:9c98bbd1f9786d22f14b3d4126894d56befb835ec90cef151af566c7e19b5d24"
],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==3.0.4"
},
"snowballstemmer": {
@ -1600,6 +1738,7 @@
"sha256:022fb9c87b524d1f7862b3037e541f68597a730a8843245c349fc93e1643dc4e",
"sha256:e162203737712307dfe78860cc56c8da8a852ab2ee33750e33aeadf38d12c548"
],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==0.3.1"
},
"stevedore": {
@ -1607,6 +1746,7 @@
"sha256:a34086819e2c7a7f86d5635363632829dab8014e5fd7be2454c7cba84ac7514e",
"sha256:ddc09a744dc224c84ec8e8efcb70595042d21c97c76df60daee64c9ad53bc7ee"
],
"markers": "python_version >= '3.6'",
"version": "==3.2.1"
},
"toml": {
@ -1642,14 +1782,6 @@
],
"version": "==1.4.1"
},
"unittest-xml-reporting": {
"hashes": [
"sha256:7bf515ea8cb244255a25100cd29db611a73f8d3d0aaf672ed3266307e14cc1ca",
"sha256:984cebba69e889401bfe3adb9088ca376b3a1f923f0590d005126c1bffd1a695"
],
"index": "pypi",
"version": "==3.0.4"
},
"urllib3": {
"extras": [
"secure"
@ -1659,7 +1791,6 @@
"sha256:e7983572181f5e1522d9c98453462384ee92a0be7fac5f1413a1e35c56cc0461"
],
"index": "pypi",
"markers": null,
"version": "==1.25.10"
},
"websocket-client": {

View File

@ -2,7 +2,7 @@ version: '3.7'
services:
chrome:
image: selenium/standalone-chrome-debug:3.141.59-20200525
image: selenium/standalone-chrome-debug:3.141.59-20200719
volumes:
- /dev/shm:/dev/shm
network_mode: host

View File

@ -1,13 +1,12 @@
"""Test Enroll flow"""
from time import sleep
from sys import platform
from typing import Any, Dict, Optional
from unittest.case import skipUnless
from django.test import override_settings
from docker import DockerClient, from_env
from docker.models.containers import Container
from docker.types import Healthcheck
from selenium.webdriver.common.by import By
from selenium.webdriver.support import expected_conditions as ec
from structlog import get_logger
from e2e.utils import USER, SeleniumTestCase
from passbook.flows.models import Flow, FlowDesignation, FlowStageBinding
@ -18,41 +17,23 @@ from passbook.stages.prompt.models import FieldTypes, Prompt, PromptStage
from passbook.stages.user_login.models import UserLoginStage
from passbook.stages.user_write.models import UserWriteStage
LOGGER = get_logger()
@skipUnless(platform.startswith("linux"), "requires local docker")
class TestFlowsEnroll(SeleniumTestCase):
"""Test Enroll flow"""
def setUp(self):
self.container = self.setup_client()
super().setUp()
def setup_client(self) -> Container:
"""Setup test IdP container"""
client: DockerClient = from_env()
container = client.containers.run(
image="mailhog/mailhog:v1.0.1",
detach=True,
network_mode="host",
auto_remove=True,
healthcheck=Healthcheck(
def get_container_specs(self) -> Optional[Dict[str, Any]]:
return {
"image": "mailhog/mailhog:v1.0.1",
"detach": True,
"network_mode": "host",
"auto_remove": True,
"healthcheck": Healthcheck(
test=["CMD", "wget", "--spider", "http://localhost:8025"],
interval=5 * 100 * 1000000,
start_period=1 * 100 * 1000000,
),
)
while True:
container.reload()
status = container.attrs.get("State", {}).get("Health", {}).get("Status")
if status == "healthy":
return container
LOGGER.info("Container failed healthcheck")
sleep(1)
def tearDown(self):
self.container.kill()
super().tearDown()
}
def test_enroll_2_step(self):
"""Test 2-step enroll flow"""
@ -220,21 +201,25 @@ class TestFlowsEnroll(SeleniumTestCase):
self.driver.find_element(By.ID, "id_name").send_keys("some name")
self.driver.find_element(By.ID, "id_email").send_keys("foo@bar.baz")
self.driver.find_element(By.CSS_SELECTOR, ".pf-c-button").click()
sleep(3)
# Wait for the success message so we know the email is sent
self.wait.until(
ec.presence_of_element_located((By.CSS_SELECTOR, ".pf-c-form > p"))
)
# Open Mailhog
self.driver.get("http://localhost:8025")
# Click on first message
self.wait.until(
ec.presence_of_element_located((By.CLASS_NAME, "msglist-message"))
)
self.driver.find_element(By.CLASS_NAME, "msglist-message").click()
sleep(3)
self.driver.switch_to.frame(self.driver.find_element(By.CLASS_NAME, "tab-pane"))
self.driver.find_element(By.ID, "confirm").click()
self.driver.close()
self.driver.switch_to.window(self.driver.window_handles[0])
# We're now logged in
sleep(3)
self.wait.until(
ec.presence_of_element_located(
(By.XPATH, "//a[contains(@href, '/-/user/')]")

View File

@ -1,10 +1,14 @@
"""test default login flow"""
from sys import platform
from unittest.case import skipUnless
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
from e2e.utils import USER, SeleniumTestCase
@skipUnless(platform.startswith("linux"), "requires local docker")
class TestFlowsLogin(SeleniumTestCase):
"""test default login flow"""

View File

@ -1,7 +1,6 @@
"""test stage setup flows (password change)"""
import string
from random import SystemRandom
from time import sleep
from sys import platform
from unittest.case import skipUnless
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
@ -9,9 +8,11 @@ from selenium.webdriver.common.keys import Keys
from e2e.utils import USER, SeleniumTestCase
from passbook.core.models import User
from passbook.flows.models import Flow, FlowDesignation
from passbook.providers.oauth2.generators import generate_client_secret
from passbook.stages.password.models import PasswordStage
@skipUnless(platform.startswith("linux"), "requires local docker")
class TestFlowsStageSetup(SeleniumTestCase):
"""test stage setup flows"""
@ -27,10 +28,7 @@ class TestFlowsStageSetup(SeleniumTestCase):
stage.change_flow = flow
stage.save()
new_password = "".join(
SystemRandom().choice(string.ascii_uppercase + string.digits)
for _ in range(8)
)
new_password = generate_client_secret()
self.driver.get(
f"{self.live_server_url}/flows/default-authentication-flow/?next=%2F"
@ -48,7 +46,7 @@ class TestFlowsStageSetup(SeleniumTestCase):
self.driver.find_element(By.ID, "id_password_repeat").send_keys(new_password)
self.driver.find_element(By.CSS_SELECTOR, ".pf-c-button").click()
sleep(2)
self.wait_for_url(self.url("passbook_core:user-settings"))
# Because USER() is cached, we need to get the user manually here
user = User.objects.get(username=USER().username)
self.assertTrue(user.check_password(new_password))

View File

@ -1,12 +1,11 @@
"""test OAuth Provider flow"""
from time import sleep
from sys import platform
from typing import Any, Dict, Optional
from unittest.case import skipUnless
from docker import DockerClient, from_env
from docker.models.containers import Container
from docker.types import Healthcheck
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
from structlog import get_logger
from e2e.utils import USER, SeleniumTestCase
from passbook.core.models import Application
@ -19,32 +18,29 @@ from passbook.providers.oauth2.generators import (
)
from passbook.providers.oauth2.models import ClientTypes, OAuth2Provider, ResponseTypes
LOGGER = get_logger()
@skipUnless(platform.startswith("linux"), "requires local docker")
class TestProviderOAuth2Github(SeleniumTestCase):
"""test OAuth Provider flow"""
def setUp(self):
self.client_id = generate_client_id()
self.client_secret = generate_client_secret()
self.container = self.setup_client()
super().setUp()
def setup_client(self) -> Container:
def get_container_specs(self) -> Optional[Dict[str, Any]]:
"""Setup client grafana container which we test OAuth against"""
client: DockerClient = from_env()
container = client.containers.run(
image="grafana/grafana:7.1.0",
detach=True,
network_mode="host",
auto_remove=True,
healthcheck=Healthcheck(
return {
"image": "grafana/grafana:7.1.0",
"detach": True,
"network_mode": "host",
"auto_remove": True,
"healthcheck": Healthcheck(
test=["CMD", "wget", "--spider", "http://localhost:3000"],
interval=5 * 100 * 1000000,
start_period=1 * 100 * 1000000,
),
environment={
"environment": {
"GF_AUTH_GITHUB_ENABLED": "true",
"GF_AUTH_GITHUB_ALLOW_SIGN_UP": "true",
"GF_AUTH_GITHUB_CLIENT_ID": self.client_id,
@ -61,22 +57,10 @@ class TestProviderOAuth2Github(SeleniumTestCase):
),
"GF_LOG_LEVEL": "debug",
},
)
while True:
container.reload()
status = container.attrs.get("State", {}).get("Health", {}).get("Status")
if status == "healthy":
return container
LOGGER.info("Container failed healthcheck")
sleep(1)
def tearDown(self):
self.container.kill()
super().tearDown()
}
def test_authorization_consent_implied(self):
"""test OAuth Provider flow (default authorization flow with implied consent)"""
sleep(1)
# Bootstrap all needed objects
authorization_flow = Flow.objects.get(
slug="default-provider-authorization-implicit-consent"
@ -129,7 +113,6 @@ class TestProviderOAuth2Github(SeleniumTestCase):
def test_authorization_consent_explicit(self):
"""test OAuth Provider flow (default authorization flow with explicit consent)"""
sleep(1)
# Bootstrap all needed objects
authorization_flow = Flow.objects.get(
slug="default-provider-authorization-explicit-consent"
@ -167,8 +150,13 @@ class TestProviderOAuth2Github(SeleniumTestCase):
By.XPATH, "/html/body/div[2]/div/main/div/form/div[2]/ul/li[1]"
).text,
)
sleep(1)
self.driver.find_element(By.CSS_SELECTOR, "[type=submit]").click()
self.driver.find_element(
By.CSS_SELECTOR,
(
"form[action='/flows/b/default-provider-authorization-explicit-consent/'] "
"[type=submit]"
),
).click()
self.wait_for_url("http://localhost:3000/?orgId=1")
self.driver.find_element(By.XPATH, "//a[contains(@href, '/profile')]").click()
@ -197,7 +185,6 @@ class TestProviderOAuth2Github(SeleniumTestCase):
def test_denied(self):
"""test OAuth Provider flow (default authorization flow, denied)"""
sleep(1)
# Bootstrap all needed objects
authorization_flow = Flow.objects.get(
slug="default-provider-authorization-explicit-consent"

View File

@ -1,8 +1,9 @@
"""test OAuth2 OpenID Provider flow"""
from sys import platform
from time import sleep
from typing import Any, Dict, Optional
from unittest.case import skipUnless
from docker import DockerClient, from_env
from docker.models.containers import Container
from docker.types import Healthcheck
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
@ -34,29 +35,27 @@ from passbook.providers.oauth2.models import (
LOGGER = get_logger()
@skipUnless(platform.startswith("linux"), "requires local docker")
class TestProviderOAuth2OIDC(SeleniumTestCase):
"""test OAuth with OpenID Provider flow"""
def setUp(self):
self.client_id = generate_client_id()
self.client_secret = generate_client_secret()
self.container = self.setup_client()
super().setUp()
def setup_client(self) -> Container:
"""Setup client grafana container which we test OIDC against"""
client: DockerClient = from_env()
container = client.containers.run(
image="grafana/grafana:7.1.0",
detach=True,
network_mode="host",
auto_remove=True,
healthcheck=Healthcheck(
def get_container_specs(self) -> Optional[Dict[str, Any]]:
return {
"image": "grafana/grafana:7.1.0",
"detach": True,
"network_mode": "host",
"auto_remove": True,
"healthcheck": Healthcheck(
test=["CMD", "wget", "--spider", "http://localhost:3000"],
interval=5 * 100 * 1000000,
start_period=1 * 100 * 1000000,
),
environment={
"environment": {
"GF_AUTH_GENERIC_OAUTH_ENABLED": "true",
"GF_AUTH_GENERIC_OAUTH_CLIENT_ID": self.client_id,
"GF_AUTH_GENERIC_OAUTH_CLIENT_SECRET": self.client_secret,
@ -72,18 +71,7 @@ class TestProviderOAuth2OIDC(SeleniumTestCase):
),
"GF_LOG_LEVEL": "debug",
},
)
while True:
container.reload()
status = container.attrs.get("State", {}).get("Health", {}).get("Status")
if status == "healthy":
return container
LOGGER.info("Container failed healthcheck")
sleep(1)
def tearDown(self):
self.container.kill()
super().tearDown()
}
def test_redirect_uri_error(self):
"""test OpenID Provider flow (invalid redirect URI, check error message)"""

View File

@ -1,5 +1,7 @@
"""test SAML Provider flow"""
from sys import platform
from time import sleep
from unittest.case import skipUnless
from docker import DockerClient, from_env
from docker.models.containers import Container
@ -23,6 +25,7 @@ from passbook.providers.saml.models import (
LOGGER = get_logger()
@skipUnless(platform.startswith("linux"), "requires local docker")
class TestProviderSAML(SeleniumTestCase):
"""test SAML Provider flow"""
@ -60,10 +63,6 @@ class TestProviderSAML(SeleniumTestCase):
LOGGER.info("Container failed healthcheck")
sleep(1)
def tearDown(self):
self.container.kill()
super().tearDown()
def test_sp_initiated_implicit(self):
"""test SAML Provider flow SP-initiated flow (implicit consent)"""
# Bootstrap all needed objects

View File

@ -1,5 +1,7 @@
"""test SAML Source"""
from sys import platform
from time import sleep
from unittest.case import skipUnless
from docker import DockerClient, from_env
from docker.models.containers import Container
@ -68,6 +70,7 @@ Sm75WXsflOxuTn08LbgGc4s=
-----END PRIVATE KEY-----"""
@skipUnless(platform.startswith("linux"), "requires local docker")
class TestSourceSAML(SeleniumTestCase):
"""test SAML Source flow"""
@ -103,10 +106,6 @@ class TestSourceSAML(SeleniumTestCase):
LOGGER.info("Container failed healthcheck")
sleep(1)
def tearDown(self):
self.container.kill()
super().tearDown()
def test_idp_redirect(self):
"""test SAML Source With redirect binding"""
sleep(1)

View File

@ -1,8 +1,9 @@
"""test OAuth Source"""
from os.path import abspath
from time import sleep
from sys import platform
from typing import Any, Dict, Optional
from unittest.case import skipUnless
from docker import DockerClient, from_env
from docker.models.containers import Container
from docker.types import Healthcheck
from selenium.webdriver.common.by import By
@ -21,6 +22,7 @@ CONFIG_PATH = "/tmp/dex.yml"
LOGGER = get_logger()
@skipUnless(platform.startswith("linux"), "requires local docker")
class TestSourceOAuth(SeleniumTestCase):
"""test OAuth Source flow"""
@ -28,7 +30,7 @@ class TestSourceOAuth(SeleniumTestCase):
def setUp(self):
self.client_secret = generate_client_secret()
self.container = self.setup_client()
self.prepare_dex_config()
super().setUp()
def prepare_dex_config(self):
@ -66,34 +68,23 @@ class TestSourceOAuth(SeleniumTestCase):
with open(CONFIG_PATH, "w+") as _file:
safe_dump(config, _file)
def setup_client(self) -> Container:
"""Setup test Dex container"""
self.prepare_dex_config()
client: DockerClient = from_env()
container = client.containers.run(
image="quay.io/dexidp/dex:v2.24.0",
detach=True,
network_mode="host",
auto_remove=True,
command="serve /config.yml",
healthcheck=Healthcheck(
def get_container_specs(self) -> Optional[Dict[str, Any]]:
return {
"image": "quay.io/dexidp/dex:v2.24.0",
"detach": True,
"network_mode": "host",
"auto_remove": True,
"command": "serve /config.yml",
"healthcheck": Healthcheck(
test=["CMD", "wget", "--spider", "http://localhost:5556/dex/healthz"],
interval=5 * 100 * 1000000,
start_period=1 * 100 * 1000000,
),
volumes={abspath(CONFIG_PATH): {"bind": "/config.yml", "mode": "ro"}},
)
while True:
container.reload()
status = container.attrs.get("State", {}).get("Health", {}).get("Status")
if status == "healthy":
return container
LOGGER.info("Container failed healthcheck")
sleep(1)
"volumes": {abspath(CONFIG_PATH): {"bind": "/config.yml", "mode": "ro"}},
}
def create_objects(self):
"""Create required objects"""
sleep(1)
# Bootstrap all needed objects
authentication_flow = Flow.objects.get(slug="default-source-authentication")
enrollment_flow = Flow.objects.get(slug="default-source-enrollment")
@ -111,10 +102,6 @@ class TestSourceOAuth(SeleniumTestCase):
consumer_secret=self.client_secret,
)
def tearDown(self):
self.container.kill()
super().tearDown()
def test_oauth_enroll(self):
"""test OAuth Source With With OIDC"""
self.create_objects()
@ -141,6 +128,7 @@ class TestSourceOAuth(SeleniumTestCase):
)
self.driver.find_element(By.CSS_SELECTOR, "button[type=submit]").click()
self.wait.until(ec.presence_of_element_located((By.NAME, "username")))
# At this point we've been redirected back
# and we're asked for the username
self.driver.find_element(By.NAME, "username").click()

View File

@ -4,13 +4,16 @@ from glob import glob
from importlib.util import module_from_spec, spec_from_file_location
from inspect import getmembers, isfunction
from os import environ, makedirs
from time import time
from time import sleep, time
from typing import Any, Dict, Optional
from django.apps import apps
from django.contrib.staticfiles.testing import StaticLiveServerTestCase
from django.db import connection, transaction
from django.db.utils import IntegrityError
from django.shortcuts import reverse
from docker import DockerClient, from_env
from docker.models.containers import Container
from selenium import webdriver
from selenium.webdriver.common.desired_capabilities import DesiredCapabilities
from selenium.webdriver.remote.webdriver import WebDriver
@ -30,15 +33,35 @@ def USER() -> User: # noqa
class SeleniumTestCase(StaticLiveServerTestCase):
"""StaticLiveServerTestCase which automatically creates a Webdriver instance"""
container: Optional[Container] = None
def setUp(self):
super().setUp()
makedirs("selenium_screenshots/", exist_ok=True)
self.driver = self._get_driver()
self.driver.maximize_window()
self.driver.implicitly_wait(30)
self.wait = WebDriverWait(self.driver, 50)
self.driver.implicitly_wait(10)
self.wait = WebDriverWait(self.driver, 30)
self.apply_default_data()
self.logger = get_logger()
if specs := self.get_container_specs():
self.container = self._start_container(specs)
def _start_container(self, specs: Dict[str, Any]) -> Container:
client: DockerClient = from_env()
container = client.containers.run(**specs)
while True:
container.reload()
status = container.attrs.get("State", {}).get("Health", {}).get("Status")
if status == "healthy":
return container
self.logger.info("Container failed healthcheck")
sleep(1)
def get_container_specs(self) -> Optional[Dict[str, Any]]:
"""Optionally get container specs which will launched on setup, wait for the container to
be healthy, and deleted again on tearDown"""
return None
def _get_driver(self) -> WebDriver:
return webdriver.Remote(
@ -57,6 +80,8 @@ class SeleniumTestCase(StaticLiveServerTestCase):
self.logger.warning(
line["message"], source=line["source"], level=line["level"]
)
if self.container:
self.container.kill()
self.driver.quit()
super().tearDown()

View File

@ -1,9 +1,10 @@
"""Gunicorn config"""
from multiprocessing import cpu_count
from pathlib import Path
import structlog
bind = "0.0.0.0:8000"
workers = 2
threads = 4
user = "passbook"
group = "passbook"
@ -40,3 +41,11 @@ logconfig_dict = {
"gunicorn": {"handlers": ["console"], "level": "INFO", "propagate": False},
},
}
# if we're running in kubernetes, use fixed workers because we can scale with more pods
# otherwise (assume docker-compose), use as much as we can
if Path("/var/run/secrets/kubernetes.io").exists():
workers = 2
else:
worker = cpu_count()
threads = 4

View File

@ -5,7 +5,7 @@ from django.contrib.auth.mixins import (
)
from django.contrib.messages.views import SuccessMessageMixin
from django.urls import reverse_lazy
from django.utils.translation import ugettext as _
from django.utils.translation import gettext as _
from django.views.generic import ListView, UpdateView
from guardian.mixins import PermissionListMixin, PermissionRequiredMixin

View File

@ -5,7 +5,7 @@ from django.contrib.auth.mixins import (
)
from django.contrib.messages.views import SuccessMessageMixin
from django.urls import reverse_lazy
from django.utils.translation import ugettext as _
from django.utils.translation import gettext as _
from django.views.generic import ListView, UpdateView
from guardian.mixins import PermissionListMixin, PermissionRequiredMixin

View File

@ -7,7 +7,7 @@ from django.contrib.auth.mixins import (
from django.contrib.messages.views import SuccessMessageMixin
from django.http import HttpRequest, HttpResponse, JsonResponse
from django.urls import reverse_lazy
from django.utils.translation import ugettext as _
from django.utils.translation import gettext as _
from django.views.generic import DetailView, FormView, ListView, UpdateView
from guardian.mixins import PermissionListMixin, PermissionRequiredMixin

View File

@ -5,7 +5,7 @@ from django.contrib.auth.mixins import (
)
from django.contrib.messages.views import SuccessMessageMixin
from django.urls import reverse_lazy
from django.utils.translation import ugettext as _
from django.utils.translation import gettext as _
from django.views.generic import ListView, UpdateView
from guardian.mixins import PermissionListMixin, PermissionRequiredMixin

View File

@ -5,7 +5,7 @@ from django.contrib.auth.mixins import (
)
from django.contrib.messages.views import SuccessMessageMixin
from django.urls import reverse_lazy
from django.utils.translation import ugettext as _
from django.utils.translation import gettext as _
from django.views.generic import ListView, UpdateView
from guardian.mixins import PermissionListMixin, PermissionRequiredMixin

View File

@ -10,7 +10,7 @@ from django.contrib.messages.views import SuccessMessageMixin
from django.db.models import QuerySet
from django.http import HttpResponse
from django.urls import reverse_lazy
from django.utils.translation import ugettext as _
from django.utils.translation import gettext as _
from django.views.generic import FormView
from django.views.generic.detail import DetailView
from guardian.mixins import PermissionListMixin, PermissionRequiredMixin

View File

@ -6,7 +6,7 @@ from django.contrib.auth.mixins import (
from django.contrib.messages.views import SuccessMessageMixin
from django.db.models import QuerySet
from django.urls import reverse_lazy
from django.utils.translation import ugettext as _
from django.utils.translation import gettext as _
from django.views.generic import ListView, UpdateView
from guardian.mixins import PermissionListMixin, PermissionRequiredMixin

View File

@ -5,7 +5,7 @@ from django.contrib.auth.mixins import (
)
from django.contrib.messages.views import SuccessMessageMixin
from django.urls import reverse_lazy
from django.utils.translation import ugettext as _
from django.utils.translation import gettext as _
from guardian.mixins import PermissionListMixin, PermissionRequiredMixin
from passbook.admin.views.utils import (

View File

@ -5,7 +5,7 @@ from django.contrib.auth.mixins import (
)
from django.contrib.messages.views import SuccessMessageMixin
from django.urls import reverse_lazy
from django.utils.translation import ugettext as _
from django.utils.translation import gettext as _
from guardian.mixins import PermissionListMixin, PermissionRequiredMixin
from passbook.admin.views.utils import (

View File

@ -5,7 +5,7 @@ from django.contrib.auth.mixins import (
)
from django.contrib.messages.views import SuccessMessageMixin
from django.urls import reverse_lazy
from django.utils.translation import ugettext as _
from django.utils.translation import gettext as _
from guardian.mixins import PermissionListMixin, PermissionRequiredMixin
from passbook.admin.views.utils import (

View File

@ -5,7 +5,7 @@ from django.contrib.auth.mixins import (
)
from django.contrib.messages.views import SuccessMessageMixin
from django.urls import reverse_lazy
from django.utils.translation import ugettext as _
from django.utils.translation import gettext as _
from guardian.mixins import PermissionListMixin, PermissionRequiredMixin
from passbook.admin.views.utils import (

View File

@ -5,7 +5,7 @@ from django.contrib.auth.mixins import (
)
from django.contrib.messages.views import SuccessMessageMixin
from django.urls import reverse_lazy
from django.utils.translation import ugettext as _
from django.utils.translation import gettext as _
from django.views.generic import ListView, UpdateView
from guardian.mixins import PermissionListMixin, PermissionRequiredMixin

View File

@ -6,7 +6,7 @@ from django.contrib.auth.mixins import (
from django.contrib.messages.views import SuccessMessageMixin
from django.http import HttpResponseRedirect
from django.urls import reverse_lazy
from django.utils.translation import ugettext as _
from django.utils.translation import gettext as _
from django.views.generic import ListView
from guardian.mixins import PermissionListMixin, PermissionRequiredMixin

View File

@ -5,7 +5,7 @@ from django.contrib.auth.mixins import (
)
from django.contrib.messages.views import SuccessMessageMixin
from django.urls import reverse_lazy
from django.utils.translation import ugettext as _
from django.utils.translation import gettext as _
from django.views.generic import ListView, UpdateView
from guardian.mixins import PermissionListMixin, PermissionRequiredMixin

View File

@ -1,7 +1,7 @@
"""passbook Token administration"""
from django.contrib.auth.mixins import LoginRequiredMixin
from django.urls import reverse_lazy
from django.utils.translation import ugettext as _
from django.utils.translation import gettext as _
from django.views.generic import ListView
from guardian.mixins import PermissionListMixin, PermissionRequiredMixin

View File

@ -9,7 +9,7 @@ from django.http import HttpRequest, HttpResponse
from django.shortcuts import redirect
from django.urls import reverse, reverse_lazy
from django.utils.http import urlencode
from django.utils.translation import ugettext as _
from django.utils.translation import gettext as _
from django.views.generic import DetailView, ListView, UpdateView
from guardian.mixins import (
PermissionListMixin,

View File

@ -1,6 +1,5 @@
"""api v2 urls"""
from django.conf.urls import url
from django.urls import path
from django.urls import path, re_path
from drf_yasg import openapi
from drf_yasg.views import get_schema_view
from rest_framework import routers
@ -119,7 +118,7 @@ SchemaView = get_schema_view(
)
urlpatterns = [
url(
re_path(
r"^swagger(?P<format>\.json|\.yaml)$",
SchemaView.without_ui(cache_timeout=0),
name="schema-json",

View File

@ -1,5 +1,5 @@
"""passbook core utils view"""
from django.utils.translation import ugettext as _
from django.utils.translation import gettext as _
from django.views.generic import TemplateView

View File

@ -52,7 +52,7 @@ def create_default_source_enrollment_flow(
# PromptStage to ask user for their username
prompt_stage, _ = PromptStage.objects.using(db_alias).update_or_create(
name="default-source-enrollment-username-prompt",
name="Welcome to passbook! Please select a username.",
)
prompt, _ = Prompt.objects.using(db_alias).update_or_create(
field_key="username",

View File

@ -115,11 +115,12 @@ const updateFormAction = (form) => {
for (let index = 0; index < form.elements.length; index++) {
const element = form.elements[index];
if (element.value === form.action) {
console.log("Found Form action URL in form elements, not changing form action.");
console.log("pb-flow: Found Form action URL in form elements, not changing form action.");
return false;
}
}
form.action = flowBodyUrl;
console.log(`pb-flow: updated form.action ${flowBodyUrl}`);
return true;
};
const checkAutosubmit = (form) => {
@ -129,11 +130,11 @@ const checkAutosubmit = (form) => {
};
const setFormSubmitHandlers = () => {
document.querySelectorAll("#flow-body form").forEach(form => {
console.log(`Checking for autosubmit attribute ${form}`);
console.log(`pb-flow: Checking for autosubmit attribute ${form}`);
checkAutosubmit(form);
console.log(`Setting action for form ${form}`);
console.log(`pb-flow: Setting action for form ${form}`);
updateFormAction(form);
console.log(`Adding handler for form ${form}`);
console.log(`pb-flow: Adding handler for form ${form}`);
form.addEventListener('submit', (e) => {
e.preventDefault();
let formData = new FormData(form);
@ -145,6 +146,7 @@ const setFormSubmitHandlers = () => {
updateCard(data);
});
});
form.classList.add("pb-flow-wrapped");
});
};

View File

@ -3,7 +3,7 @@ from unittest.mock import MagicMock, PropertyMock, patch
from django.shortcuts import reverse
from django.test import Client, TestCase
from django.utils.encoding import force_text
from django.utils.encoding import force_str
from passbook.flows.exceptions import EmptyFlowException, FlowNonApplicableException
from passbook.flows.markers import ReevaluateMarker, StageMarker
@ -247,7 +247,7 @@ class TestFlowExecutor(TestCase):
response = self.client.post(exec_url)
self.assertEqual(response.status_code, 200)
self.assertJSONEqual(
force_text(response.content),
force_str(response.content),
{"type": "redirect", "to": reverse("passbook_core:overview")},
)
@ -293,7 +293,7 @@ class TestFlowExecutor(TestCase):
# First request, run the planner
response = self.client.get(exec_url)
self.assertEqual(response.status_code, 200)
self.assertIn("dummy1", force_text(response.content))
self.assertIn("dummy1", force_str(response.content))
plan: FlowPlan = self.client.session[SESSION_KEY_PLAN]
@ -316,13 +316,13 @@ class TestFlowExecutor(TestCase):
# but it won't save it, hence we cant' check the plan
response = self.client.get(exec_url)
self.assertEqual(response.status_code, 200)
self.assertIn("dummy4", force_text(response.content))
self.assertIn("dummy4", force_str(response.content))
# fourth request, this confirms the last stage (dummy4)
# We do this request without the patch, so the policy results in false
response = self.client.post(exec_url)
self.assertEqual(response.status_code, 200)
self.assertJSONEqual(
force_text(response.content),
force_str(response.content),
{"type": "redirect", "to": reverse("passbook_core:overview")},
)

View File

@ -14,7 +14,7 @@ from django.forms import ModelForm
from django.http import HttpRequest
from django.shortcuts import reverse
from django.utils import dateformat, timezone
from django.utils.translation import ugettext_lazy as _
from django.utils.translation import gettext_lazy as _
from jwkest.jwk import Key, RSAKey, SYMKey, import_rsa_key
from jwkest.jws import JWS

View File

@ -82,7 +82,7 @@ def extract_client_auth(request: HttpRequest) -> Tuple[str, str]:
b64_user_pass = auth_header.split()[1]
try:
user_pass = b64decode(b64_user_pass).decode("utf-8").split(":")
client_id, client_secret = tuple(user_pass)
client_id, client_secret = user_pass
except (ValueError, Error):
client_id = client_secret = ""
else:

View File

@ -2,7 +2,7 @@
from typing import Any, Dict, List
from django.http import HttpRequest, HttpResponse
from django.utils.translation import ugettext_lazy as _
from django.utils.translation import gettext_lazy as _
from django.views import View
from structlog import get_logger

View File

@ -5,7 +5,7 @@ from django.db import models
from django.forms import ModelForm
from django.http import HttpRequest
from django.shortcuts import reverse
from django.utils.translation import ugettext_lazy as _
from django.utils.translation import gettext_lazy as _
from structlog import get_logger
from passbook.core.models import PropertyMapping, Provider

View File

@ -12,7 +12,6 @@ https://docs.djangoproject.com/en/2.1/ref/settings/
import importlib
import os
import sys
from json import dumps
import structlog
@ -156,6 +155,7 @@ DJANGO_REDIS_IGNORE_EXCEPTIONS = True
DJANGO_REDIS_LOG_IGNORED_EXCEPTIONS = True
SESSION_ENGINE = "django.contrib.sessions.backends.cache"
SESSION_CACHE_ALIAS = "default"
SESSION_COOKIE_SAMESITE = "lax"
MIDDLEWARE = [
"django_prometheus.middleware.PrometheusBeforeMiddleware",
@ -372,15 +372,9 @@ LOGGING = {
}
TEST = False
TEST_RUNNER = "xmlrunner.extra.djangotestrunner.XMLTestRunner"
TEST_RUNNER = "passbook.root.test_runner.PytestTestRunner"
LOG_LEVEL = CONFIG.y("log_level").upper()
TEST_OUTPUT_FILE_NAME = "unittest.xml"
if len(sys.argv) >= 2 and sys.argv[1] == "test":
LOG_LEVEL = "DEBUG"
TEST = True
CELERY_TASK_ALWAYS_EAGER = True
_LOGGING_HANDLER_MAP = {
"": LOG_LEVEL,
@ -431,7 +425,6 @@ for _app in INSTALLED_APPS:
pass
if DEBUG:
SESSION_COOKIE_SAMESITE = None
INSTALLED_APPS.append("debug_toolbar")
MIDDLEWARE.append("debug_toolbar.middleware.DebugToolbarMiddleware")

View File

@ -0,0 +1,35 @@
"""Integrate ./manage.py test with pytest"""
from django.conf import settings
class PytestTestRunner:
"""Runs pytest to discover and run tests."""
def __init__(self, verbosity=1, failfast=False, keepdb=False, **_):
self.verbosity = verbosity
self.failfast = failfast
self.keepdb = keepdb
settings.TEST = True
settings.CELERY_TASK_ALWAYS_EAGER = True
def run_tests(self, test_labels):
"""Run pytest and return the exitcode.
It translates some of Django's test command option to pytest's.
"""
import pytest
argv = []
if self.verbosity == 0:
argv.append("--quiet")
if self.verbosity == 2:
argv.append("--verbose")
if self.verbosity == 3:
argv.append("-vv")
if self.failfast:
argv.append("--exitfirst")
if self.keepdb:
argv.append("--reuse-db")
argv.extend(test_labels)
return pytest.main(argv)

View File

@ -15,7 +15,7 @@ admin.site.login = RedirectView.as_view(
pattern_name="passbook_flows:default-authentication"
)
admin.site.logout = RedirectView.as_view(
pattern_name="passbook_flows:default-invalidate"
pattern_name="passbook_flows:default-invalidation"
)
handler400 = error.BadRequestView.as_view()

View File

@ -5,7 +5,7 @@ from urllib.parse import parse_qs, urlencode
from django.http import HttpRequest
from django.utils.crypto import constant_time_compare, get_random_string
from django.utils.encoding import force_text
from django.utils.encoding import force_str
from requests import Session
from requests.exceptions import RequestException
from requests_oauthlib import OAuth1
@ -111,7 +111,7 @@ class OAuthClient(BaseOAuthClient):
def get_request_token(self, request, callback):
"Fetch the OAuth request token. Only required for OAuth 1.0."
callback = force_text(request.build_absolute_uri(callback))
callback = force_str(request.build_absolute_uri(callback))
try:
response = self.session.request(
"post",
@ -128,7 +128,7 @@ class OAuthClient(BaseOAuthClient):
def get_redirect_args(self, request, callback):
"Get request parameters for redirect url."
callback = force_text(request.build_absolute_uri(callback))
callback = force_str(request.build_absolute_uri(callback))
raw_token = self.get_request_token(request, callback)
token, secret = self.parse_raw_token(raw_token)
if token is not None and secret is not None:

View File

@ -6,7 +6,7 @@ from django.contrib import messages
from django.http import Http404, HttpRequest, HttpResponse
from django.shortcuts import redirect
from django.urls import reverse
from django.utils.translation import ugettext as _
from django.utils.translation import gettext as _
from django.views.generic import View
from structlog import get_logger

View File

@ -6,7 +6,7 @@ from django.contrib.auth.mixins import LoginRequiredMixin
from django.http import HttpRequest, HttpResponse
from django.shortcuts import get_object_or_404, redirect, render
from django.urls import reverse
from django.utils.translation import ugettext as _
from django.utils.translation import gettext as _
from django.views.generic import TemplateView, View
from passbook.sources.oauth.models import OAuthSource, UserOAuthSourceConnection

View File

@ -2,7 +2,7 @@
from django.conf import settings
from django.shortcuts import reverse
from django.test import Client, TestCase
from django.utils.encoding import force_text
from django.utils.encoding import force_str
from passbook.core.models import User
from passbook.flows.markers import StageMarker
@ -50,6 +50,6 @@ class TestCaptchaStage(TestCase):
)
self.assertEqual(response.status_code, 200)
self.assertJSONEqual(
force_text(response.content),
force_str(response.content),
{"type": "redirect", "to": reverse("passbook_core:overview")},
)

View File

@ -3,7 +3,7 @@ from time import sleep
from django.shortcuts import reverse
from django.test import Client, TestCase
from django.utils.encoding import force_text
from django.utils.encoding import force_str
from passbook.core.models import Application, User
from passbook.core.tasks import clean_expired_models
@ -49,7 +49,7 @@ class TestConsentStage(TestCase):
)
self.assertEqual(response.status_code, 200)
self.assertJSONEqual(
force_text(response.content),
force_str(response.content),
{"type": "redirect", "to": reverse("passbook_core:overview")},
)
self.assertFalse(UserConsent.objects.filter(user=self.user).exists())
@ -80,7 +80,7 @@ class TestConsentStage(TestCase):
)
self.assertEqual(response.status_code, 200)
self.assertJSONEqual(
force_text(response.content),
force_str(response.content),
{"type": "redirect", "to": reverse("passbook_core:overview")},
)
self.assertTrue(
@ -117,7 +117,7 @@ class TestConsentStage(TestCase):
)
self.assertEqual(response.status_code, 200)
self.assertJSONEqual(
force_text(response.content),
force_str(response.content),
{"type": "redirect", "to": reverse("passbook_core:overview")},
)
self.assertTrue(

View File

@ -1,7 +1,7 @@
"""dummy tests"""
from django.shortcuts import reverse
from django.test import Client, TestCase
from django.utils.encoding import force_text
from django.utils.encoding import force_str
from passbook.core.models import User
from passbook.flows.models import Flow, FlowDesignation, FlowStageBinding
@ -44,7 +44,7 @@ class TestDummyStage(TestCase):
response = self.client.post(url, {})
self.assertEqual(response.status_code, 200)
self.assertJSONEqual(
force_text(response.content),
force_str(response.content),
{"type": "redirect", "to": reverse("passbook_core:overview")},
)

View File

@ -4,7 +4,7 @@ from unittest.mock import MagicMock, patch
from django.core import mail
from django.shortcuts import reverse
from django.test import Client, TestCase
from django.utils.encoding import force_text
from django.utils.encoding import force_str
from passbook.core.models import Token, User
from passbook.flows.markers import StageMarker
@ -114,7 +114,7 @@ class TestEmailStage(TestCase):
self.assertEqual(response.status_code, 200)
self.assertJSONEqual(
force_text(response.content),
force_str(response.content),
{"type": "redirect", "to": reverse("passbook_core:overview")},
)

View File

@ -1,7 +1,7 @@
"""identification tests"""
from django.shortcuts import reverse
from django.test import Client, TestCase
from django.utils.encoding import force_text
from django.utils.encoding import force_str
from passbook.core.models import User
from passbook.flows.models import Flow, FlowDesignation, FlowStageBinding
@ -56,7 +56,7 @@ class TestIdentificationStage(TestCase):
response = self.client.post(url, form_data)
self.assertEqual(response.status_code, 200)
self.assertJSONEqual(
force_text(response.content),
force_str(response.content),
{"type": "redirect", "to": reverse("passbook_core:overview")},
)
@ -101,7 +101,7 @@ class TestIdentificationStage(TestCase):
),
)
self.assertEqual(response.status_code, 200)
self.assertIn(flow.slug, force_text(response.content))
self.assertIn(flow.slug, force_str(response.content))
def test_recovery_flow(self):
"""Test that recovery flow is linked correctly"""
@ -122,4 +122,4 @@ class TestIdentificationStage(TestCase):
),
)
self.assertEqual(response.status_code, 200)
self.assertIn(flow.slug, force_text(response.content))
self.assertIn(flow.slug, force_str(response.content))

View File

@ -3,7 +3,7 @@ from unittest.mock import MagicMock, patch
from django.shortcuts import reverse
from django.test import Client, TestCase
from django.utils.encoding import force_text
from django.utils.encoding import force_str
from guardian.shortcuts import get_anonymous_user
from passbook.core.models import User
@ -59,7 +59,7 @@ class TestUserLoginStage(TestCase):
self.assertEqual(response.status_code, 200)
self.assertJSONEqual(
force_text(response.content),
force_str(response.content),
{"type": "redirect", "to": reverse("passbook_flows:denied")},
)
@ -86,7 +86,7 @@ class TestUserLoginStage(TestCase):
self.assertEqual(response.status_code, 200)
self.assertJSONEqual(
force_text(response.content),
force_str(response.content),
{"type": "redirect", "to": reverse("passbook_core:overview")},
)
@ -125,6 +125,6 @@ class TestUserLoginStage(TestCase):
self.assertEqual(response.status_code, 200)
self.assertJSONEqual(
force_text(response.content),
force_str(response.content),
{"type": "redirect", "to": reverse("passbook_core:overview")},
)

View File

@ -2,7 +2,7 @@
from typing import Any, Dict
from django.http import HttpRequest, HttpResponse
from django.utils.encoding import force_text
from django.utils.encoding import force_str
from django.views.generic import FormView
from django_otp.plugins.otp_totp.models import TOTPDevice
from lxml.etree import tostring # nosec
@ -35,7 +35,7 @@ class OTPTimeStageView(FormView, StageView):
"""Get QR Code SVG as string based on `device`"""
qr_code = QRCode(image_factory=SvgFillImage)
qr_code.add_data(device.config_url)
return force_text(tostring(qr_code.make_image().get_image()))
return force_str(tostring(qr_code.make_image().get_image()))
def get(self, request: HttpRequest, *args, **kwargs) -> HttpResponse:
user = self.executor.plan.context.get(PLAN_CONTEXT_PENDING_USER)

View File

@ -6,7 +6,7 @@ from unittest.mock import MagicMock, patch
from django.core.exceptions import PermissionDenied
from django.shortcuts import reverse
from django.test import Client, TestCase
from django.utils.encoding import force_text
from django.utils.encoding import force_str
from passbook.core.models import User
from passbook.flows.markers import StageMarker
@ -61,7 +61,7 @@ class TestPasswordStage(TestCase):
self.assertEqual(response.status_code, 200)
self.assertJSONEqual(
force_text(response.content),
force_str(response.content),
{"type": "redirect", "to": reverse("passbook_flows:denied")},
)
@ -84,7 +84,7 @@ class TestPasswordStage(TestCase):
),
)
self.assertEqual(response.status_code, 200)
self.assertIn(flow.slug, force_text(response.content))
self.assertIn(flow.slug, force_str(response.content))
def test_valid_password(self):
"""Test with a valid pending user and valid password"""
@ -106,7 +106,7 @@ class TestPasswordStage(TestCase):
self.assertEqual(response.status_code, 200)
self.assertJSONEqual(
force_text(response.content),
force_str(response.content),
{"type": "redirect", "to": reverse("passbook_core:overview")},
)
@ -154,6 +154,6 @@ class TestPasswordStage(TestCase):
self.assertEqual(response.status_code, 200)
self.assertJSONEqual(
force_text(response.content),
force_str(response.content),
{"type": "redirect", "to": reverse("passbook_flows:denied")},
)

View File

@ -3,7 +3,7 @@ from unittest.mock import MagicMock, patch
from django.shortcuts import reverse
from django.test import Client, TestCase
from django.utils.encoding import force_text
from django.utils.encoding import force_str
from passbook.core.models import User
from passbook.flows.markers import StageMarker
@ -110,9 +110,9 @@ class TestPromptStage(TestCase):
)
self.assertEqual(response.status_code, 200)
for prompt in self.stage.fields.all():
self.assertIn(prompt.field_key, force_text(response.content))
self.assertIn(prompt.label, force_text(response.content))
self.assertIn(prompt.placeholder, force_text(response.content))
self.assertIn(prompt.field_key, force_str(response.content))
self.assertIn(prompt.label, force_str(response.content))
self.assertIn(prompt.placeholder, force_str(response.content))
def test_valid_form_with_policy(self) -> PromptForm:
"""Test form validation"""
@ -164,7 +164,7 @@ class TestPromptStage(TestCase):
)
self.assertEqual(response.status_code, 200)
self.assertJSONEqual(
force_text(response.content),
force_str(response.content),
{"type": "redirect", "to": reverse("passbook_core:overview")},
)

View File

@ -1,7 +1,7 @@
"""delete tests"""
from django.shortcuts import reverse
from django.test import Client, TestCase
from django.utils.encoding import force_text
from django.utils.encoding import force_str
from passbook.core.models import User
from passbook.flows.markers import StageMarker
@ -44,7 +44,7 @@ class TestUserDeleteStage(TestCase):
)
self.assertEqual(response.status_code, 200)
self.assertJSONEqual(
force_text(response.content),
force_str(response.content),
{"type": "redirect", "to": reverse("passbook_flows:denied")},
)
@ -83,7 +83,7 @@ class TestUserDeleteStage(TestCase):
)
self.assertEqual(response.status_code, 200)
self.assertJSONEqual(
force_text(response.content),
force_str(response.content),
{"type": "redirect", "to": reverse("passbook_core:overview")},
)

View File

@ -1,7 +1,7 @@
"""login tests"""
from django.shortcuts import reverse
from django.test import Client, TestCase
from django.utils.encoding import force_text
from django.utils.encoding import force_str
from passbook.core.models import User
from passbook.flows.markers import StageMarker
@ -50,7 +50,7 @@ class TestUserLoginStage(TestCase):
self.assertEqual(response.status_code, 200)
self.assertJSONEqual(
force_text(response.content),
force_str(response.content),
{"type": "redirect", "to": reverse("passbook_core:overview")},
)
@ -71,7 +71,7 @@ class TestUserLoginStage(TestCase):
self.assertEqual(response.status_code, 200)
self.assertJSONEqual(
force_text(response.content),
force_str(response.content),
{"type": "redirect", "to": reverse("passbook_flows:denied")},
)
@ -93,7 +93,7 @@ class TestUserLoginStage(TestCase):
self.assertEqual(response.status_code, 200)
self.assertJSONEqual(
force_text(response.content),
force_str(response.content),
{"type": "redirect", "to": reverse("passbook_flows:denied")},
)

View File

@ -1,7 +1,7 @@
"""logout tests"""
from django.shortcuts import reverse
from django.test import Client, TestCase
from django.utils.encoding import force_text
from django.utils.encoding import force_str
from passbook.core.models import User
from passbook.flows.markers import StageMarker
@ -50,7 +50,7 @@ class TestUserLogoutStage(TestCase):
self.assertEqual(response.status_code, 200)
self.assertJSONEqual(
force_text(response.content),
force_str(response.content),
{"type": "redirect", "to": reverse("passbook_core:overview")},
)

View File

@ -4,7 +4,7 @@ from random import SystemRandom
from django.shortcuts import reverse
from django.test import Client, TestCase
from django.utils.encoding import force_text
from django.utils.encoding import force_str
from passbook.core.models import User
from passbook.flows.markers import StageMarker
@ -59,7 +59,7 @@ class TestUserWriteStage(TestCase):
self.assertEqual(response.status_code, 200)
self.assertJSONEqual(
force_text(response.content),
force_str(response.content),
{"type": "redirect", "to": reverse("passbook_core:overview")},
)
user_qs = User.objects.filter(
@ -97,7 +97,7 @@ class TestUserWriteStage(TestCase):
self.assertEqual(response.status_code, 200)
self.assertJSONEqual(
force_text(response.content),
force_str(response.content),
{"type": "redirect", "to": reverse("passbook_core:overview")},
)
user_qs = User.objects.filter(
@ -124,7 +124,7 @@ class TestUserWriteStage(TestCase):
self.assertEqual(response.status_code, 200)
self.assertJSONEqual(
force_text(response.content),
force_str(response.content),
{"type": "redirect", "to": reverse("passbook_flows:denied")},
)

6
pytest.ini Normal file
View File

@ -0,0 +1,6 @@
[pytest]
DJANGO_SETTINGS_MODULE = passbook.root.settings
# -- recommended but optional:
python_files = tests.py test_*.py *_tests.py
junit_family = xunit2
addopts = -p no:celery --junitxml=unittest.xml