✅ Testing!
This commit is contained in:
parent
bd7ace7a66
commit
6b44e34013
12
conftest.py
Normal file
12
conftest.py
Normal file
@ -0,0 +1,12 @@
|
||||
import os
|
||||
|
||||
from pytest import fixture
|
||||
|
||||
|
||||
@fixture(autouse=True, scope="session")
|
||||
def db():
|
||||
if os.path.exists("spiderweb-tests.db"):
|
||||
os.remove("spiderweb-tests.db")
|
||||
yield
|
||||
if os.path.exists("spiderweb-tests.db"):
|
||||
os.remove("spiderweb-tests.db")
|
16
example2.py
16
example2.py
@ -55,15 +55,15 @@ app = SpiderwebRouter(
|
||||
],
|
||||
staticfiles_dirs=["static_files"],
|
||||
routes=[
|
||||
["/", index],
|
||||
["/redirect", redirect],
|
||||
["/json", json],
|
||||
["/error", error],
|
||||
["/middleware", middleware],
|
||||
["/example/<int:id>", example],
|
||||
["/form", form, {"allowed_methods": ["GET", "POST"], "csrf_exempt": True}],
|
||||
("/", index),
|
||||
("/redirect", redirect),
|
||||
("/json", json),
|
||||
("/error", error),
|
||||
("/middleware", middleware),
|
||||
("/example/<int:id>", example),
|
||||
("/form", form, {"allowed_methods": ["GET", "POST"], "csrf_exempt": True}),
|
||||
],
|
||||
error_routes={"405": http405},
|
||||
error_routes={405: http405},
|
||||
)
|
||||
|
||||
|
||||
|
148
poetry.lock
generated
148
poetry.lock
generated
@ -11,6 +11,25 @@ files = [
|
||||
{file = "annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "attrs"
|
||||
version = "24.2.0"
|
||||
description = "Classes Without Boilerplate"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
{file = "attrs-24.2.0-py3-none-any.whl", hash = "sha256:81921eb96de3191c8258c199618104dd27ac608d9366f5e35d011eae1867ede2"},
|
||||
{file = "attrs-24.2.0.tar.gz", hash = "sha256:5cfb1b9148b5b086569baec03f20d7b6bf3bcacc9a42bebf87ffaaca362f6346"},
|
||||
]
|
||||
|
||||
[package.extras]
|
||||
benchmark = ["cloudpickle", "hypothesis", "mypy (>=1.11.1)", "pympler", "pytest (>=4.3.0)", "pytest-codspeed", "pytest-mypy-plugins", "pytest-xdist[psutil]"]
|
||||
cov = ["cloudpickle", "coverage[toml] (>=5.3)", "hypothesis", "mypy (>=1.11.1)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"]
|
||||
dev = ["cloudpickle", "hypothesis", "mypy (>=1.11.1)", "pre-commit", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"]
|
||||
docs = ["cogapp", "furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib-towncrier", "towncrier (<24.7)"]
|
||||
tests = ["cloudpickle", "hypothesis", "mypy (>=1.11.1)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"]
|
||||
tests-mypy = ["mypy (>=1.11.1)", "pytest-mypy-plugins"]
|
||||
|
||||
[[package]]
|
||||
name = "black"
|
||||
version = "24.8.0"
|
||||
@ -159,6 +178,90 @@ files = [
|
||||
{file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "coverage"
|
||||
version = "7.6.1"
|
||||
description = "Code coverage measurement for Python"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "coverage-7.6.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b06079abebbc0e89e6163b8e8f0e16270124c154dc6e4a47b413dd538859af16"},
|
||||
{file = "coverage-7.6.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:cf4b19715bccd7ee27b6b120e7e9dd56037b9c0681dcc1adc9ba9db3d417fa36"},
|
||||
{file = "coverage-7.6.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e61c0abb4c85b095a784ef23fdd4aede7a2628478e7baba7c5e3deba61070a02"},
|
||||
{file = "coverage-7.6.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fd21f6ae3f08b41004dfb433fa895d858f3f5979e7762d052b12aef444e29afc"},
|
||||
{file = "coverage-7.6.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f59d57baca39b32db42b83b2a7ba6f47ad9c394ec2076b084c3f029b7afca23"},
|
||||
{file = "coverage-7.6.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:a1ac0ae2b8bd743b88ed0502544847c3053d7171a3cff9228af618a068ed9c34"},
|
||||
{file = "coverage-7.6.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e6a08c0be454c3b3beb105c0596ebdc2371fab6bb90c0c0297f4e58fd7e1012c"},
|
||||
{file = "coverage-7.6.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:f5796e664fe802da4f57a168c85359a8fbf3eab5e55cd4e4569fbacecc903959"},
|
||||
{file = "coverage-7.6.1-cp310-cp310-win32.whl", hash = "sha256:7bb65125fcbef8d989fa1dd0e8a060999497629ca5b0efbca209588a73356232"},
|
||||
{file = "coverage-7.6.1-cp310-cp310-win_amd64.whl", hash = "sha256:3115a95daa9bdba70aea750db7b96b37259a81a709223c8448fa97727d546fe0"},
|
||||
{file = "coverage-7.6.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:7dea0889685db8550f839fa202744652e87c60015029ce3f60e006f8c4462c93"},
|
||||
{file = "coverage-7.6.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ed37bd3c3b063412f7620464a9ac1314d33100329f39799255fb8d3027da50d3"},
|
||||
{file = "coverage-7.6.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d85f5e9a5f8b73e2350097c3756ef7e785f55bd71205defa0bfdaf96c31616ff"},
|
||||
{file = "coverage-7.6.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9bc572be474cafb617672c43fe989d6e48d3c83af02ce8de73fff1c6bb3c198d"},
|
||||
{file = "coverage-7.6.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0c0420b573964c760df9e9e86d1a9a622d0d27f417e1a949a8a66dd7bcee7bc6"},
|
||||
{file = "coverage-7.6.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1f4aa8219db826ce6be7099d559f8ec311549bfc4046f7f9fe9b5cea5c581c56"},
|
||||
{file = "coverage-7.6.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:fc5a77d0c516700ebad189b587de289a20a78324bc54baee03dd486f0855d234"},
|
||||
{file = "coverage-7.6.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:b48f312cca9621272ae49008c7f613337c53fadca647d6384cc129d2996d1133"},
|
||||
{file = "coverage-7.6.1-cp311-cp311-win32.whl", hash = "sha256:1125ca0e5fd475cbbba3bb67ae20bd2c23a98fac4e32412883f9bcbaa81c314c"},
|
||||
{file = "coverage-7.6.1-cp311-cp311-win_amd64.whl", hash = "sha256:8ae539519c4c040c5ffd0632784e21b2f03fc1340752af711f33e5be83a9d6c6"},
|
||||
{file = "coverage-7.6.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:95cae0efeb032af8458fc27d191f85d1717b1d4e49f7cb226cf526ff28179778"},
|
||||
{file = "coverage-7.6.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:5621a9175cf9d0b0c84c2ef2b12e9f5f5071357c4d2ea6ca1cf01814f45d2391"},
|
||||
{file = "coverage-7.6.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:260933720fdcd75340e7dbe9060655aff3af1f0c5d20f46b57f262ab6c86a5e8"},
|
||||
{file = "coverage-7.6.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:07e2ca0ad381b91350c0ed49d52699b625aab2b44b65e1b4e02fa9df0e92ad2d"},
|
||||
{file = "coverage-7.6.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c44fee9975f04b33331cb8eb272827111efc8930cfd582e0320613263ca849ca"},
|
||||
{file = "coverage-7.6.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:877abb17e6339d96bf08e7a622d05095e72b71f8afd8a9fefc82cf30ed944163"},
|
||||
{file = "coverage-7.6.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:3e0cadcf6733c09154b461f1ca72d5416635e5e4ec4e536192180d34ec160f8a"},
|
||||
{file = "coverage-7.6.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:c3c02d12f837d9683e5ab2f3d9844dc57655b92c74e286c262e0fc54213c216d"},
|
||||
{file = "coverage-7.6.1-cp312-cp312-win32.whl", hash = "sha256:e05882b70b87a18d937ca6768ff33cc3f72847cbc4de4491c8e73880766718e5"},
|
||||
{file = "coverage-7.6.1-cp312-cp312-win_amd64.whl", hash = "sha256:b5d7b556859dd85f3a541db6a4e0167b86e7273e1cdc973e5b175166bb634fdb"},
|
||||
{file = "coverage-7.6.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:a4acd025ecc06185ba2b801f2de85546e0b8ac787cf9d3b06e7e2a69f925b106"},
|
||||
{file = "coverage-7.6.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a6d3adcf24b624a7b778533480e32434a39ad8fa30c315208f6d3e5542aeb6e9"},
|
||||
{file = "coverage-7.6.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d0c212c49b6c10e6951362f7c6df3329f04c2b1c28499563d4035d964ab8e08c"},
|
||||
{file = "coverage-7.6.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6e81d7a3e58882450ec4186ca59a3f20a5d4440f25b1cff6f0902ad890e6748a"},
|
||||
{file = "coverage-7.6.1-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:78b260de9790fd81e69401c2dc8b17da47c8038176a79092a89cb2b7d945d060"},
|
||||
{file = "coverage-7.6.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a78d169acd38300060b28d600344a803628c3fd585c912cacc9ea8790fe96862"},
|
||||
{file = "coverage-7.6.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:2c09f4ce52cb99dd7505cd0fc8e0e37c77b87f46bc9c1eb03fe3bc9991085388"},
|
||||
{file = "coverage-7.6.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6878ef48d4227aace338d88c48738a4258213cd7b74fd9a3d4d7582bb1d8a155"},
|
||||
{file = "coverage-7.6.1-cp313-cp313-win32.whl", hash = "sha256:44df346d5215a8c0e360307d46ffaabe0f5d3502c8a1cefd700b34baf31d411a"},
|
||||
{file = "coverage-7.6.1-cp313-cp313-win_amd64.whl", hash = "sha256:8284cf8c0dd272a247bc154eb6c95548722dce90d098c17a883ed36e67cdb129"},
|
||||
{file = "coverage-7.6.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:d3296782ca4eab572a1a4eca686d8bfb00226300dcefdf43faa25b5242ab8a3e"},
|
||||
{file = "coverage-7.6.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:502753043567491d3ff6d08629270127e0c31d4184c4c8d98f92c26f65019962"},
|
||||
{file = "coverage-7.6.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6a89ecca80709d4076b95f89f308544ec8f7b4727e8a547913a35f16717856cb"},
|
||||
{file = "coverage-7.6.1-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a318d68e92e80af8b00fa99609796fdbcdfef3629c77c6283566c6f02c6d6704"},
|
||||
{file = "coverage-7.6.1-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:13b0a73a0896988f053e4fbb7de6d93388e6dd292b0d87ee51d106f2c11b465b"},
|
||||
{file = "coverage-7.6.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:4421712dbfc5562150f7554f13dde997a2e932a6b5f352edcce948a815efee6f"},
|
||||
{file = "coverage-7.6.1-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:166811d20dfea725e2e4baa71fffd6c968a958577848d2131f39b60043400223"},
|
||||
{file = "coverage-7.6.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:225667980479a17db1048cb2bf8bfb39b8e5be8f164b8f6628b64f78a72cf9d3"},
|
||||
{file = "coverage-7.6.1-cp313-cp313t-win32.whl", hash = "sha256:170d444ab405852903b7d04ea9ae9b98f98ab6d7e63e1115e82620807519797f"},
|
||||
{file = "coverage-7.6.1-cp313-cp313t-win_amd64.whl", hash = "sha256:b9f222de8cded79c49bf184bdbc06630d4c58eec9459b939b4a690c82ed05657"},
|
||||
{file = "coverage-7.6.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:6db04803b6c7291985a761004e9060b2bca08da6d04f26a7f2294b8623a0c1a0"},
|
||||
{file = "coverage-7.6.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:f1adfc8ac319e1a348af294106bc6a8458a0f1633cc62a1446aebc30c5fa186a"},
|
||||
{file = "coverage-7.6.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a95324a9de9650a729239daea117df21f4b9868ce32e63f8b650ebe6cef5595b"},
|
||||
{file = "coverage-7.6.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b43c03669dc4618ec25270b06ecd3ee4fa94c7f9b3c14bae6571ca00ef98b0d3"},
|
||||
{file = "coverage-7.6.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8929543a7192c13d177b770008bc4e8119f2e1f881d563fc6b6305d2d0ebe9de"},
|
||||
{file = "coverage-7.6.1-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:a09ece4a69cf399510c8ab25e0950d9cf2b42f7b3cb0374f95d2e2ff594478a6"},
|
||||
{file = "coverage-7.6.1-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:9054a0754de38d9dbd01a46621636689124d666bad1936d76c0341f7d71bf569"},
|
||||
{file = "coverage-7.6.1-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:0dbde0f4aa9a16fa4d754356a8f2e36296ff4d83994b2c9d8398aa32f222f989"},
|
||||
{file = "coverage-7.6.1-cp38-cp38-win32.whl", hash = "sha256:da511e6ad4f7323ee5702e6633085fb76c2f893aaf8ce4c51a0ba4fc07580ea7"},
|
||||
{file = "coverage-7.6.1-cp38-cp38-win_amd64.whl", hash = "sha256:3f1156e3e8f2872197af3840d8ad307a9dd18e615dc64d9ee41696f287c57ad8"},
|
||||
{file = "coverage-7.6.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:abd5fd0db5f4dc9289408aaf34908072f805ff7792632250dcb36dc591d24255"},
|
||||
{file = "coverage-7.6.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:547f45fa1a93154bd82050a7f3cddbc1a7a4dd2a9bf5cb7d06f4ae29fe94eaf8"},
|
||||
{file = "coverage-7.6.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:645786266c8f18a931b65bfcefdbf6952dd0dea98feee39bd188607a9d307ed2"},
|
||||
{file = "coverage-7.6.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9e0b2df163b8ed01d515807af24f63de04bebcecbd6c3bfeff88385789fdf75a"},
|
||||
{file = "coverage-7.6.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:609b06f178fe8e9f89ef676532760ec0b4deea15e9969bf754b37f7c40326dbc"},
|
||||
{file = "coverage-7.6.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:702855feff378050ae4f741045e19a32d57d19f3e0676d589df0575008ea5004"},
|
||||
{file = "coverage-7.6.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:2bdb062ea438f22d99cba0d7829c2ef0af1d768d1e4a4f528087224c90b132cb"},
|
||||
{file = "coverage-7.6.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:9c56863d44bd1c4fe2abb8a4d6f5371d197f1ac0ebdee542f07f35895fc07f36"},
|
||||
{file = "coverage-7.6.1-cp39-cp39-win32.whl", hash = "sha256:6e2cd258d7d927d09493c8df1ce9174ad01b381d4729a9d8d4e38670ca24774c"},
|
||||
{file = "coverage-7.6.1-cp39-cp39-win_amd64.whl", hash = "sha256:06a737c882bd26d0d6ee7269b20b12f14a8704807a01056c80bb881a4b2ce6ca"},
|
||||
{file = "coverage-7.6.1-pp38.pp39.pp310-none-any.whl", hash = "sha256:e9a6e0eb86070e8ccaedfbd9d38fec54864f3125ab95419970575b42af7541df"},
|
||||
{file = "coverage-7.6.1.tar.gz", hash = "sha256:953510dfb7b12ab69d20135a0662397f077c59b1e6379a768e97c59d852ee51d"},
|
||||
]
|
||||
|
||||
[package.extras]
|
||||
toml = ["tomli"]
|
||||
|
||||
[[package]]
|
||||
name = "cryptography"
|
||||
version = "43.0.0"
|
||||
@ -264,6 +367,38 @@ setproctitle = ["setproctitle"]
|
||||
testing = ["coverage", "eventlet", "gevent", "pytest", "pytest-cov"]
|
||||
tornado = ["tornado (>=0.2)"]
|
||||
|
||||
[[package]]
|
||||
name = "hypothesis"
|
||||
version = "6.111.2"
|
||||
description = "A library for property-based testing"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "hypothesis-6.111.2-py3-none-any.whl", hash = "sha256:055e8228958e22178d6077e455fd86a72044d02dac130dbf9c8b31e161b9809c"},
|
||||
{file = "hypothesis-6.111.2.tar.gz", hash = "sha256:0496ad28c7240ee9ba89fcc7fb1dc74e89f3e40fbcbbb5f73c0091558dec8e6e"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
attrs = ">=22.2.0"
|
||||
sortedcontainers = ">=2.1.0,<3.0.0"
|
||||
|
||||
[package.extras]
|
||||
all = ["backports.zoneinfo (>=0.2.1)", "black (>=19.10b0)", "click (>=7.0)", "crosshair-tool (>=0.0.70)", "django (>=3.2)", "dpcontracts (>=0.4)", "hypothesis-crosshair (>=0.0.13)", "lark (>=0.10.1)", "libcst (>=0.3.16)", "numpy (>=1.17.3)", "pandas (>=1.1)", "pytest (>=4.6)", "python-dateutil (>=1.4)", "pytz (>=2014.1)", "redis (>=3.0.0)", "rich (>=9.0.0)", "tzdata (>=2024.1)"]
|
||||
cli = ["black (>=19.10b0)", "click (>=7.0)", "rich (>=9.0.0)"]
|
||||
codemods = ["libcst (>=0.3.16)"]
|
||||
crosshair = ["crosshair-tool (>=0.0.70)", "hypothesis-crosshair (>=0.0.13)"]
|
||||
dateutil = ["python-dateutil (>=1.4)"]
|
||||
django = ["django (>=3.2)"]
|
||||
dpcontracts = ["dpcontracts (>=0.4)"]
|
||||
ghostwriter = ["black (>=19.10b0)"]
|
||||
lark = ["lark (>=0.10.1)"]
|
||||
numpy = ["numpy (>=1.17.3)"]
|
||||
pandas = ["pandas (>=1.1)"]
|
||||
pytest = ["pytest (>=4.6)"]
|
||||
pytz = ["pytz (>=2014.1)"]
|
||||
redis = ["redis (>=3.0.0)"]
|
||||
zoneinfo = ["backports.zoneinfo (>=0.2.1)", "tzdata (>=2024.1)"]
|
||||
|
||||
[[package]]
|
||||
name = "idna"
|
||||
version = "3.7"
|
||||
@ -627,6 +762,17 @@ files = [
|
||||
{file = "ruff-0.5.7.tar.gz", hash = "sha256:8dfc0a458797f5d9fb622dd0efc52d796f23f0a1493a9527f4e49a550ae9a7e5"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sortedcontainers"
|
||||
version = "2.4.0"
|
||||
description = "Sorted Containers -- Sorted List, Sorted Dict, Sorted Set"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
files = [
|
||||
{file = "sortedcontainers-2.4.0-py2.py3-none-any.whl", hash = "sha256:a163dcaede0f1c021485e957a39245190e74249897e2ae4b2aa38595db237ee0"},
|
||||
{file = "sortedcontainers-2.4.0.tar.gz", hash = "sha256:25caa5a06cc30b6b83d11423433f65d1f9d76c4c6a0c90e3379eaa43b9bfdb88"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "typing-extensions"
|
||||
version = "4.12.2"
|
||||
@ -641,4 +787,4 @@ files = [
|
||||
[metadata]
|
||||
lock-version = "2.0"
|
||||
python-versions = "^3.11"
|
||||
content-hash = "84633fc94c48c2a05b5ec77367ad29f327be1dc249a6e4cb76b50ebbe14739b5"
|
||||
content-hash = "17f5dc4b157da57ad75a6f6aa3feb7adfa07500b805d5e79d1f09d640964949f"
|
||||
|
@ -41,6 +41,8 @@ ruff = "^0.5.5"
|
||||
pytest = "^8.3.2"
|
||||
black = "^24.8.0"
|
||||
gunicorn = "^23.0.0"
|
||||
hypothesis = "^6.111.2"
|
||||
coverage = "^7.6.1"
|
||||
|
||||
[build-system]
|
||||
requires = ["poetry-core"]
|
||||
@ -53,3 +55,32 @@ Homepage = "https://github.com/itsthejoker/spiderweb"
|
||||
Documentation = "https://github.com/itsthejoker/spiderweb"
|
||||
Repository = "https://git.joekaufeld.com/jkaufeld/spiderweb"
|
||||
"Bug Tracker" = "https://github.com/itsthejoker/spiderweb/issues"
|
||||
|
||||
[tool.pytest.ini_options]
|
||||
minversion = "6.0"
|
||||
addopts = ["--maxfail=2", "-rf"]
|
||||
|
||||
[tool.coverage.run]
|
||||
branch = true
|
||||
omit = ["conftest.py"]
|
||||
|
||||
[tool.coverage.report]
|
||||
# Regexes for lines to exclude from consideration
|
||||
exclude_also = [
|
||||
# Don't complain about missing debug-only code:
|
||||
"def __repr__",
|
||||
"if self\\.debug",
|
||||
|
||||
# Don't complain if tests don't hit defensive assertion code:
|
||||
"raise AssertionError",
|
||||
"raise NotImplementedError",
|
||||
|
||||
# Don't complain if non-runnable code isn't run:
|
||||
"if 0:",
|
||||
"if __name__ == .__main__.:",
|
||||
|
||||
# Don't complain about abstract methods, they aren't run:
|
||||
"@(abc\\.)?abstractmethod",
|
||||
]
|
||||
|
||||
ignore_errors = true
|
@ -1,7 +1,7 @@
|
||||
from peewee import DatabaseProxy
|
||||
|
||||
DEFAULT_ALLOWED_METHODS = ["GET"]
|
||||
DEFAULT_ENCODING = "ISO-8859-1"
|
||||
DEFAULT_ENCODING = "UTF-8"
|
||||
__version__ = "0.10.0"
|
||||
|
||||
# https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie
|
||||
|
@ -5,9 +5,6 @@ class IntConverter:
|
||||
def to_python(self, value):
|
||||
return int(value)
|
||||
|
||||
def to_url(self, value):
|
||||
return str(value)
|
||||
|
||||
|
||||
class StrConverter:
|
||||
regex = r"[^/]+"
|
||||
@ -16,9 +13,6 @@ class StrConverter:
|
||||
def to_python(self, value):
|
||||
return str(value)
|
||||
|
||||
def to_url(self, value):
|
||||
return str(value)
|
||||
|
||||
|
||||
class FloatConverter:
|
||||
regex = r"\d+\.\d+"
|
||||
@ -26,6 +20,3 @@ class FloatConverter:
|
||||
|
||||
def to_python(self, value):
|
||||
return float(value)
|
||||
|
||||
def to_url(self, value):
|
||||
return str(value)
|
||||
|
@ -11,7 +11,7 @@ def http403(request):
|
||||
|
||||
def http404(request):
|
||||
return JsonResponse(
|
||||
data={"error": f"Route {request.url} not found"}, status_code=404
|
||||
data={"error": f"Route `{request.path}` not found"}, status_code=404
|
||||
)
|
||||
|
||||
|
||||
|
@ -7,7 +7,7 @@ from threading import Thread
|
||||
from typing import Optional, Callable
|
||||
from wsgiref.simple_server import WSGIServer
|
||||
|
||||
from jinja2 import Environment, FileSystemLoader
|
||||
from jinja2 import BaseLoader, Environment, FileSystemLoader
|
||||
from peewee import Database, SqliteDatabase
|
||||
|
||||
from spiderweb.middleware import MiddlewareMixin
|
||||
@ -46,8 +46,8 @@ class SpiderwebRouter(LocalServerMixin, MiddlewareMixin, RoutesMixin, FernetMixi
|
||||
middleware: list[str] = None,
|
||||
append_slash: bool = False,
|
||||
staticfiles_dirs: list[str] = None,
|
||||
routes: list[list[str | Callable | dict]] = None,
|
||||
error_routes: dict[str, Callable] = None,
|
||||
routes: list[tuple[str, Callable] | tuple[str, Callable, dict]] = None,
|
||||
error_routes: dict[int, Callable] = None,
|
||||
secret_key: str = None,
|
||||
session_max_age=60 * 60 * 24 * 14, # 2 weeks
|
||||
session_cookie_name="swsession",
|
||||
@ -100,10 +100,14 @@ class SpiderwebRouter(LocalServerMixin, MiddlewareMixin, RoutesMixin, FernetMixi
|
||||
if self.routes:
|
||||
self.add_routes()
|
||||
|
||||
if self.error_routes:
|
||||
self.add_error_routes()
|
||||
|
||||
if self.templates_dirs:
|
||||
self.env = Environment(loader=FileSystemLoader(self.templates_dirs))
|
||||
self.template_loader = Environment(loader=FileSystemLoader(self.templates_dirs))
|
||||
else:
|
||||
self.env = None
|
||||
self.template_loader = None
|
||||
self.string_loader = Environment(loader=BaseLoader())
|
||||
|
||||
if self.staticfiles_dirs:
|
||||
for static_dir in self.staticfiles_dirs:
|
||||
@ -131,7 +135,6 @@ class SpiderwebRouter(LocalServerMixin, MiddlewareMixin, RoutesMixin, FernetMixi
|
||||
rendered_output = resp.render()
|
||||
if not isinstance(rendered_output, list):
|
||||
rendered_output = [rendered_output]
|
||||
|
||||
encoded_resp = [
|
||||
chunk.encode(DEFAULT_ENCODING) if isinstance(chunk, str) else chunk
|
||||
for chunk in rendered_output
|
||||
@ -183,12 +186,12 @@ class SpiderwebRouter(LocalServerMixin, MiddlewareMixin, RoutesMixin, FernetMixi
|
||||
def prepare_and_fire_response(self, start_response, request, resp) -> list[bytes]:
|
||||
try:
|
||||
if isinstance(resp, dict):
|
||||
self.fire_response(start_response, request, JsonResponse(data=resp))
|
||||
return self.fire_response(start_response, request, JsonResponse(data=resp))
|
||||
if isinstance(resp, TemplateResponse):
|
||||
resp.set_template_loader(self.env)
|
||||
resp.set_template_loader(self.template_loader)
|
||||
resp.set_string_loader(self.string_loader)
|
||||
|
||||
for middleware in self.middleware:
|
||||
middleware.process_response(request, resp)
|
||||
self.process_response_middleware(request, resp)
|
||||
|
||||
return self.fire_response(start_response, request, resp)
|
||||
|
||||
|
@ -21,7 +21,7 @@ class Session(SpiderwebModel):
|
||||
user_agent = TextField()
|
||||
|
||||
class Meta:
|
||||
table_name = 'spiderweb_sessions'
|
||||
table_name = "spiderweb_sessions"
|
||||
|
||||
|
||||
class SessionMiddleware(SpiderwebMiddleware):
|
||||
@ -68,7 +68,7 @@ class SessionMiddleware(SpiderwebMiddleware):
|
||||
}
|
||||
|
||||
# if a new session has been requested, ignore everything else and make that happen
|
||||
if request._session["new_session"]:
|
||||
if request._session["new_session"] is True:
|
||||
# we generated a new one earlier, so we can use it now
|
||||
session_key = request._session["id"]
|
||||
response.set_cookie(
|
||||
@ -76,6 +76,8 @@ class SessionMiddleware(SpiderwebMiddleware):
|
||||
session_key,
|
||||
**cookie_settings,
|
||||
)
|
||||
if not is_jsonable(request.SESSION):
|
||||
raise ValueError("Session data is not JSON serializable.")
|
||||
session = Session(
|
||||
session_key=session_key,
|
||||
session_data=json.dumps(request.SESSION),
|
||||
@ -97,18 +99,6 @@ class SessionMiddleware(SpiderwebMiddleware):
|
||||
)
|
||||
|
||||
session = request.META["SESSION"]
|
||||
if not session:
|
||||
if not is_jsonable(request.SESSION):
|
||||
raise ValueError("Session data is not JSON serializable.")
|
||||
session = Session(
|
||||
session_key=session_key,
|
||||
session_data=json.dumps(request.SESSION),
|
||||
created_at=datetime.now(),
|
||||
last_active=datetime.now(),
|
||||
ip_address=request.META.get("client_address"),
|
||||
user_agent=request.META.get("HTTP_USER_AGENT"),
|
||||
)
|
||||
else:
|
||||
session.session_data = json.dumps(request.SESSION)
|
||||
session.last_active = datetime.now()
|
||||
session.save()
|
||||
|
@ -10,7 +10,6 @@ from spiderweb.constants import REGEX_COOKIE_NAME
|
||||
from spiderweb.exceptions import GeneralException
|
||||
from spiderweb.request import Request
|
||||
|
||||
|
||||
mimetypes.init()
|
||||
|
||||
|
||||
@ -128,20 +127,39 @@ class RedirectResponse(HttpResponse):
|
||||
|
||||
|
||||
class TemplateResponse(HttpResponse):
|
||||
def __init__(self, request: Request, template=None, *args, **kwargs):
|
||||
def __init__(
|
||||
self,
|
||||
request: Request,
|
||||
template_path: str = None,
|
||||
template_string: str = None,
|
||||
*args,
|
||||
**kwargs,
|
||||
):
|
||||
super().__init__(*args, **kwargs)
|
||||
self.context["request"] = request
|
||||
self.template = template
|
||||
self.loader = None
|
||||
self.template_path = template_path
|
||||
self.template_string = template_string
|
||||
self.template_loader = None
|
||||
self.string_loader = None
|
||||
self._template = None
|
||||
if not template:
|
||||
if not template_path and not template_string:
|
||||
raise GeneralException("TemplateResponse requires a template.")
|
||||
|
||||
def render(self) -> str:
|
||||
if self.loader is None:
|
||||
raise GeneralException("TemplateResponse requires a template loader.")
|
||||
self._template = self.loader.get_template(self.template)
|
||||
if self.template_loader is None:
|
||||
if not self.template_string:
|
||||
raise GeneralException(
|
||||
"TemplateResponse has no loader. Did you set templates_dirs?"
|
||||
)
|
||||
else:
|
||||
self._template = self.string_loader.from_string(self.template_string)
|
||||
else:
|
||||
self._template = self.template_loader.get_template(self.template_path)
|
||||
|
||||
return self._template.render(**self.context)
|
||||
|
||||
def set_template_loader(self, env):
|
||||
self.loader = env
|
||||
def set_template_loader(self, loader):
|
||||
self.template_loader = loader
|
||||
|
||||
def set_string_loader(self, loader):
|
||||
self.string_loader = loader
|
||||
|
@ -1,5 +1,5 @@
|
||||
import re
|
||||
from typing import Callable, Any
|
||||
from typing import Callable, Any, Optional
|
||||
|
||||
from spiderweb.constants import DEFAULT_ALLOWED_METHODS
|
||||
from spiderweb.converters import * # noqa: F403
|
||||
@ -30,9 +30,9 @@ class RoutesMixin:
|
||||
# ones that start with underscores are the compiled versions, non-underscores
|
||||
# are the user-supplied versions
|
||||
_routes: dict
|
||||
routes: list[list[str | Callable | dict]]
|
||||
routes: list[tuple[str, Callable] | tuple[str, Callable, dict]] = None,
|
||||
_error_routes: dict
|
||||
error_routes: dict[str, Callable]
|
||||
error_routes: dict[int, Callable]
|
||||
append_slash: bool
|
||||
|
||||
def route(self, path, allowed_methods=None) -> Callable:
|
||||
|
1
spiderweb/tests/__init__.py
Normal file
1
spiderweb/tests/__init__.py
Normal file
@ -0,0 +1 @@
|
||||
from spiderweb.tests.middleware import ExplodingResponseMiddleware, ExplodingRequestMiddleware
|
11
spiderweb/tests/middleware.py
Normal file
11
spiderweb/tests/middleware.py
Normal file
@ -0,0 +1,11 @@
|
||||
from spiderweb import SpiderwebMiddleware, Request, HttpResponse, UnusedMiddleware
|
||||
|
||||
|
||||
class ExplodingRequestMiddleware(SpiderwebMiddleware):
|
||||
def process_request(self, request: Request) -> HttpResponse | None:
|
||||
raise UnusedMiddleware("Boom!")
|
||||
|
||||
|
||||
class ExplodingResponseMiddleware(SpiderwebMiddleware):
|
||||
def process_response(self, request: Request, response: HttpResponse) -> HttpResponse | None:
|
||||
raise UnusedMiddleware("Unfinished!")
|
88
spiderweb/tests/test_middleware.py
Normal file
88
spiderweb/tests/test_middleware.py
Normal file
@ -0,0 +1,88 @@
|
||||
from datetime import timedelta
|
||||
|
||||
from peewee import SqliteDatabase
|
||||
|
||||
from spiderweb import SpiderwebRouter, HttpResponse
|
||||
from spiderweb.constants import DEFAULT_ENCODING
|
||||
from spiderweb.middleware.sessions import Session
|
||||
from spiderweb.tests.helpers import setup
|
||||
|
||||
# app = SpiderwebRouter(
|
||||
# middleware=[
|
||||
# "spiderweb.middleware.sessions.SessionMiddleware",
|
||||
# "spiderweb.middleware.csrf.CSRFMiddleware",
|
||||
# "example_middleware.TestMiddleware",
|
||||
# "example_middleware.RedirectMiddleware",
|
||||
# "spiderweb.middleware.pydantic.PydanticMiddleware",
|
||||
# "example_middleware.ExplodingMiddleware",
|
||||
# ],
|
||||
# )
|
||||
|
||||
def index(request):
|
||||
if "value" in request.SESSION:
|
||||
request.SESSION['value'] += 1
|
||||
else:
|
||||
request.SESSION['value'] = 0
|
||||
return HttpResponse(body=str(request.SESSION['value']))
|
||||
|
||||
|
||||
def test_session_middleware():
|
||||
_, environ, start_response = setup()
|
||||
app = SpiderwebRouter(
|
||||
middleware=["spiderweb.middleware.sessions.SessionMiddleware"],
|
||||
db=SqliteDatabase("spiderweb-tests.db")
|
||||
)
|
||||
|
||||
app.add_route("/", index)
|
||||
|
||||
environ["HTTP_USER_AGENT"] = "hi"
|
||||
environ["REMOTE_ADDR"] = "1.1.1.1"
|
||||
|
||||
assert app(environ, start_response) == [bytes(str(0), DEFAULT_ENCODING)]
|
||||
|
||||
session_key = Session.select().first().session_key
|
||||
environ["HTTP_COOKIE"] = f"swsession={session_key}"
|
||||
|
||||
assert app(environ, start_response) == [bytes(str(1), DEFAULT_ENCODING)]
|
||||
assert app(environ, start_response) == [bytes(str(2), DEFAULT_ENCODING)]
|
||||
|
||||
def test_expired_session():
|
||||
_, environ, start_response = setup()
|
||||
app = SpiderwebRouter(
|
||||
middleware=["spiderweb.middleware.sessions.SessionMiddleware"],
|
||||
db=SqliteDatabase("spiderweb-tests.db")
|
||||
)
|
||||
|
||||
app.add_route("/", index)
|
||||
|
||||
environ["HTTP_USER_AGENT"] = "hi"
|
||||
environ["REMOTE_ADDR"] = "1.1.1.1"
|
||||
|
||||
assert app(environ, start_response) == [bytes(str(0), DEFAULT_ENCODING)]
|
||||
|
||||
session = Session.select().first()
|
||||
session.created_at = session.created_at - timedelta(seconds=app.session_max_age)
|
||||
session.save()
|
||||
|
||||
environ["HTTP_COOKIE"] = f"swsession={session.session_key}"
|
||||
|
||||
# it shouldn't increment because we get a new session
|
||||
assert app(environ, start_response) == [bytes(str(0), DEFAULT_ENCODING)]
|
||||
|
||||
session2 = list(Session.select())[-1]
|
||||
assert session2.session_key != session.session_key
|
||||
|
||||
|
||||
def test_exploding_middleware():
|
||||
_, environ, start_response = setup()
|
||||
app = SpiderwebRouter(
|
||||
middleware=[
|
||||
"spiderweb.tests.middleware.ExplodingRequestMiddleware",
|
||||
"spiderweb.tests.middleware.ExplodingResponseMiddleware",
|
||||
],
|
||||
db=SqliteDatabase("spiderweb-tests.db")
|
||||
)
|
||||
|
||||
app.add_route("/", index)
|
||||
|
||||
assert app(environ, start_response) == [bytes(str(0), DEFAULT_ENCODING)]
|
173
spiderweb/tests/test_responses.py
Normal file
173
spiderweb/tests/test_responses.py
Normal file
@ -0,0 +1,173 @@
|
||||
import pytest
|
||||
|
||||
from spiderweb import SpiderwebRouter, ConfigError
|
||||
from spiderweb.constants import DEFAULT_ENCODING
|
||||
from spiderweb.exceptions import NoResponseError, SpiderwebNetworkException
|
||||
from spiderweb.response import HttpResponse, JsonResponse, TemplateResponse, RedirectResponse
|
||||
from hypothesis import given, strategies as st
|
||||
|
||||
from spiderweb.tests.helpers import setup
|
||||
|
||||
|
||||
@given(st.text())
|
||||
def test_http_response(text):
|
||||
app, environ, start_response = setup()
|
||||
|
||||
@app.route("/")
|
||||
def index(request):
|
||||
return HttpResponse(text)
|
||||
|
||||
assert app(environ, start_response) == [bytes(text, DEFAULT_ENCODING)]
|
||||
|
||||
|
||||
def test_json_response():
|
||||
app, environ, start_response = setup()
|
||||
|
||||
@app.route("/")
|
||||
def index(request):
|
||||
return JsonResponse(data={"message": "text"})
|
||||
|
||||
assert app(environ, start_response) == [bytes('{"message": "text"}', DEFAULT_ENCODING)]
|
||||
|
||||
|
||||
def test_dict_response():
|
||||
app, environ, start_response = setup()
|
||||
|
||||
@app.route("/")
|
||||
def index(request):
|
||||
return {"message": "Hello, World!"}
|
||||
|
||||
assert app(environ, start_response) == [b'{"message": "Hello, World!"}']
|
||||
|
||||
|
||||
@given(st.text())
|
||||
def test_template_response(text):
|
||||
app, environ, start_response = setup()
|
||||
template = "MESSAGE: {{ message }}"
|
||||
|
||||
@app.route("/")
|
||||
def index(request):
|
||||
return TemplateResponse(
|
||||
request, template_string=template, context={"message": text}
|
||||
)
|
||||
|
||||
assert app(environ, start_response) == [b"MESSAGE: " + bytes(text, DEFAULT_ENCODING)]
|
||||
|
||||
|
||||
def test_redirect_response():
|
||||
app, environ, start_response = setup()
|
||||
|
||||
@app.route("/")
|
||||
def index(request):
|
||||
return RedirectResponse(location="/redirected")
|
||||
|
||||
assert app(environ, start_response) == [b'None']
|
||||
assert start_response.get_headers()["Location"] == "/redirected"
|
||||
|
||||
|
||||
def test_add_route_at_server_start():
|
||||
app, environ, start_response = setup()
|
||||
|
||||
def index(request):
|
||||
return RedirectResponse(location="/redirected")
|
||||
|
||||
def view2(request):
|
||||
return HttpResponse("View 2")
|
||||
|
||||
app = SpiderwebRouter(routes=[
|
||||
("/", index, {"allowed_methods": ["GET", "POST"], "csrf_exempt": True}),
|
||||
("/view2", view2),
|
||||
])
|
||||
|
||||
assert app(environ, start_response) == [b'None']
|
||||
assert start_response.get_headers()["Location"] == "/redirected"
|
||||
|
||||
|
||||
def test_redirect_on_append_slash():
|
||||
_, environ, start_response = setup()
|
||||
app = SpiderwebRouter(append_slash=True)
|
||||
|
||||
@app.route("/hello")
|
||||
def index(request):
|
||||
pass
|
||||
|
||||
environ["PATH_INFO"] = f"/hello"
|
||||
assert app(environ, start_response) == [b'None']
|
||||
assert start_response.get_headers()["Location"] == "/hello/"
|
||||
|
||||
|
||||
@given(st.text())
|
||||
def test_template_response_with_template(text):
|
||||
_, environ, start_response = setup()
|
||||
|
||||
app = SpiderwebRouter(templates_dirs=["spiderweb/tests"])
|
||||
|
||||
@app.route("/")
|
||||
def index(request):
|
||||
return TemplateResponse(
|
||||
request, "test.html", context={"message": text}
|
||||
)
|
||||
|
||||
assert app(environ, start_response) == [b"TEMPLATE! " + bytes(text, DEFAULT_ENCODING)]
|
||||
|
||||
|
||||
def test_view_returns_none():
|
||||
app, environ, start_response = setup()
|
||||
|
||||
@app.route("/")
|
||||
def index(request):
|
||||
pass
|
||||
|
||||
with pytest.raises(NoResponseError):
|
||||
assert app(environ, start_response) == [b'None']
|
||||
|
||||
|
||||
def test_exploding_view():
|
||||
app, environ, start_response = setup()
|
||||
|
||||
@app.route("/")
|
||||
def index(request):
|
||||
raise SpiderwebNetworkException("Boom!")
|
||||
|
||||
assert app(environ, start_response) == [
|
||||
b'Something went wrong.\n\nCode: Boom!\n\nMsg: None\n\nDesc: None'
|
||||
]
|
||||
|
||||
def test_missing_view():
|
||||
app, environ, start_response = setup()
|
||||
|
||||
assert app(environ, start_response) == [b'{"error": "Route `/` not found"}']
|
||||
|
||||
|
||||
def test_missing_view_with_custom_404():
|
||||
app, environ, start_response = setup()
|
||||
|
||||
@app.error(404)
|
||||
def custom_404(request):
|
||||
return HttpResponse("Custom 404")
|
||||
|
||||
assert app(environ, start_response) == [b'Custom 404']
|
||||
|
||||
|
||||
def test_duplicate_error_view():
|
||||
app, environ, start_response = setup()
|
||||
|
||||
@app.error(404)
|
||||
def custom_404(request):
|
||||
...
|
||||
|
||||
with pytest.raises(ConfigError):
|
||||
@app.error(404)
|
||||
def custom_404(request):
|
||||
...
|
||||
|
||||
|
||||
def test_missing_view_with_custom_404_alt():
|
||||
_, environ, start_response = setup()
|
||||
|
||||
def custom_404(request):
|
||||
return HttpResponse("Custom 404 2")
|
||||
|
||||
app = SpiderwebRouter(error_routes={404: custom_404})
|
||||
|
||||
assert app(environ, start_response) == [b'Custom 404 2']
|
102
spiderweb/tests/test_variable_urls.py
Normal file
102
spiderweb/tests/test_variable_urls.py
Normal file
@ -0,0 +1,102 @@
|
||||
import pytest
|
||||
|
||||
from spiderweb import SpiderwebRouter
|
||||
from spiderweb.constants import DEFAULT_ENCODING
|
||||
from spiderweb.exceptions import ParseError, ConfigError
|
||||
from spiderweb.response import HttpResponse, JsonResponse, TemplateResponse, RedirectResponse
|
||||
from hypothesis import given, strategies as st, assume
|
||||
|
||||
from peewee import SqliteDatabase
|
||||
|
||||
from spiderweb.tests.helpers import setup
|
||||
|
||||
|
||||
@given(st.text())
|
||||
def test_str_converter(text):
|
||||
assume(len(text) > 0)
|
||||
assume("/" not in text)
|
||||
app, environ, start_response = setup()
|
||||
|
||||
@app.route("/<str:test_input>")
|
||||
def index(request, test_input: str):
|
||||
return HttpResponse(test_input)
|
||||
|
||||
environ["PATH_INFO"] = f"/{text}"
|
||||
assert app(environ, start_response) == [bytes(text, DEFAULT_ENCODING)]
|
||||
|
||||
|
||||
@given(st.text())
|
||||
def test_default_str_converter(text):
|
||||
assume(len(text) > 0)
|
||||
assume("/" not in text)
|
||||
app, environ, start_response = setup()
|
||||
|
||||
@app.route("/<test_input>")
|
||||
def index(request, test_input: str):
|
||||
return HttpResponse(test_input)
|
||||
|
||||
environ["PATH_INFO"] = f"/{text}"
|
||||
assert app(environ, start_response) == [bytes(text, DEFAULT_ENCODING)]
|
||||
|
||||
|
||||
def test_unknown_converter():
|
||||
app, environ, start_response = setup()
|
||||
|
||||
with pytest.raises(ParseError):
|
||||
@app.route("/<asdf:test_input>")
|
||||
def index(request, test_input: str):
|
||||
return HttpResponse(test_input)
|
||||
|
||||
|
||||
def test_duplicate_route():
|
||||
app, environ, start_response = setup()
|
||||
|
||||
@app.route("/")
|
||||
def index(request):
|
||||
...
|
||||
|
||||
with pytest.raises(ConfigError):
|
||||
@app.route("/")
|
||||
def index(request):
|
||||
...
|
||||
|
||||
|
||||
def test_url_with_double_underscore():
|
||||
app, environ, start_response = setup()
|
||||
|
||||
with pytest.raises(ConfigError):
|
||||
@app.route("/<asdf:test__input>")
|
||||
def index(request, test_input: str):
|
||||
return HttpResponse(test_input)
|
||||
|
||||
|
||||
@given(st.integers())
|
||||
def test_int_converter(integer):
|
||||
assume(integer > 0)
|
||||
app, environ, start_response = setup()
|
||||
|
||||
@app.route("/<int:test_input>")
|
||||
def index(request, test_input: str):
|
||||
return HttpResponse(test_input)
|
||||
|
||||
environ["PATH_INFO"] = f"/{integer}"
|
||||
assert app(environ, start_response) == [bytes(str(integer), DEFAULT_ENCODING)]
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"number",
|
||||
[
|
||||
1.0000000000000002,
|
||||
294744.2324,
|
||||
0000.3,
|
||||
],
|
||||
)
|
||||
def test_float_converter(number):
|
||||
app, environ, start_response = setup()
|
||||
|
||||
@app.route("/<float:test_input>")
|
||||
def index(request, test_input: str):
|
||||
return HttpResponse(test_input)
|
||||
|
||||
environ["PATH_INFO"] = f"/{number}"
|
||||
assert app(environ, start_response) == [bytes(str(number), DEFAULT_ENCODING)]
|
Loading…
Reference in New Issue
Block a user