pytest (#209)
This commit is contained in:
parent
3f5d30e6fe
commit
23cccebb96
2
Makefile
2
Makefile
|
@ -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
|
||||
|
|
3
Pipfile
3
Pipfile
|
@ -59,5 +59,6 @@ docker = "*"
|
|||
pylint = "*"
|
||||
pylint-django = "*"
|
||||
selenium = "*"
|
||||
unittest-xml-reporting = "*"
|
||||
prospector = "*"
|
||||
pytest = "*"
|
||||
pytest-django = "*"
|
||||
|
|
|
@ -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": {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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/')]")
|
||||
|
|
|
@ -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"""
|
||||
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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)"""
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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()
|
||||
|
|
31
e2e/utils.py
31
e2e/utils.py
|
@ -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()
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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 (
|
||||
|
|
|
@ -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 (
|
||||
|
|
|
@ -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 (
|
||||
|
|
|
@ -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 (
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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
|
||||
|
||||
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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");
|
||||
});
|
||||
};
|
||||
|
||||
|
|
|
@ -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")},
|
||||
)
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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")
|
||||
|
||||
|
|
|
@ -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)
|
|
@ -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()
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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")},
|
||||
)
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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")},
|
||||
)
|
||||
|
||||
|
|
|
@ -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")},
|
||||
)
|
||||
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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")},
|
||||
)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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")},
|
||||
)
|
||||
|
|
|
@ -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")},
|
||||
)
|
||||
|
||||
|
|
|
@ -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")},
|
||||
)
|
||||
|
||||
|
|
|
@ -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")},
|
||||
)
|
||||
|
||||
|
|
|
@ -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")},
|
||||
)
|
||||
|
||||
|
|
|
@ -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")},
|
||||
)
|
||||
|
||||
|
|
|
@ -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
|
Reference in New Issue