diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml index 615c149..dc72327 100644 --- a/.github/workflows/tests.yaml +++ b/.github/workflows/tests.yaml @@ -10,7 +10,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python-version: ["3.8", "3.9", "3.10", "3.11"] + python-version: ["3.8", "3.9", "3.10", "3.11", "3.12"] steps: - name: Checkout 🔁 uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 #v4.1.1 diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 494d806..2df1d9b 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -34,6 +34,7 @@ repos: rev: v3.1.0 hooks: - id: prettier + exclude: "^openapi_to_fastapi/tests/__snapshots__/" - repo: https://github.com/pycqa/flake8 rev: 7.0.0 hooks: diff --git a/openapi_to_fastapi/tests/__snapshots__/test_router/test_custom_route_definitions.json b/openapi_to_fastapi/tests/__snapshots__/test_router/test_custom_route_definitions.json new file mode 100644 index 0000000..daef35a --- /dev/null +++ b/openapi_to_fastapi/tests/__snapshots__/test_router/test_custom_route_definitions.json @@ -0,0 +1,22 @@ +{ + "detail": [ + { + "input": null, + "loc": [ + "query", + "vendor" + ], + "msg": "Field required", + "type": "missing" + }, + { + "input": null, + "loc": [ + "header", + "auth-header" + ], + "msg": "Field required", + "type": "missing" + } + ] +} diff --git a/openapi_to_fastapi/tests/__snapshots__/test_router/test_weather_route_payload_errors.1.json b/openapi_to_fastapi/tests/__snapshots__/test_router/test_weather_route_payload_errors.1.json new file mode 100644 index 0000000..61a4a72 --- /dev/null +++ b/openapi_to_fastapi/tests/__snapshots__/test_router/test_weather_route_payload_errors.1.json @@ -0,0 +1,25 @@ +{ + "detail": [ + { + "input": "1,1.2", + "loc": [ + "body", + "lat" + ], + "msg": "Input should be a valid number, unable to parse string as a number", + "type": "float_parsing" + }, + { + "ctx": { + "le": 180.0 + }, + "input": "99999", + "loc": [ + "body", + "lon" + ], + "msg": "Input should be less than or equal to 180", + "type": "less_than_equal" + } + ] +} diff --git a/openapi_to_fastapi/tests/__snapshots__/test_router/test_weather_route_payload_errors.json b/openapi_to_fastapi/tests/__snapshots__/test_router/test_weather_route_payload_errors.json new file mode 100644 index 0000000..94b2625 --- /dev/null +++ b/openapi_to_fastapi/tests/__snapshots__/test_router/test_weather_route_payload_errors.json @@ -0,0 +1,22 @@ +{ + "detail": [ + { + "input": {}, + "loc": [ + "body", + "lat" + ], + "msg": "Field required", + "type": "missing" + }, + { + "input": {}, + "loc": [ + "body", + "lon" + ], + "msg": "Field required", + "type": "missing" + } + ] +} diff --git a/openapi_to_fastapi/tests/conftest.py b/openapi_to_fastapi/tests/conftest.py index 56d0d39..911c733 100644 --- a/openapi_to_fastapi/tests/conftest.py +++ b/openapi_to_fastapi/tests/conftest.py @@ -3,6 +3,7 @@ import pytest from fastapi import FastAPI from starlette.testclient import TestClient +from syrupy.extensions.json import JSONSnapshotExtension from openapi_to_fastapi.routes import SpecRouter @@ -28,3 +29,8 @@ def definitions_client(specs_root): spec_router = SpecRouter(specs_root / "definitions") app.include_router(spec_router.to_fastapi_router()) return TestClient(app) + + +@pytest.fixture +def json_snapshot(snapshot): + return snapshot.use_extension(JSONSnapshotExtension) diff --git a/openapi_to_fastapi/tests/snapshots/__init__.py b/openapi_to_fastapi/tests/snapshots/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/openapi_to_fastapi/tests/snapshots/snap_test_router.py b/openapi_to_fastapi/tests/snapshots/snap_test_router.py deleted file mode 100644 index 9d7d5c1..0000000 --- a/openapi_to_fastapi/tests/snapshots/snap_test_router.py +++ /dev/null @@ -1,59 +0,0 @@ -# -*- coding: utf-8 -*- -# snapshottest: v1 - https://goo.gl/zC4yUc -from __future__ import unicode_literals - -from snapshottest import Snapshot - -snapshots = Snapshot() - -snapshots["test_custom_route_definitions Custom route definition"] = { - "detail": [ - { - "input": None, - "loc": ["query", "vendor"], - "msg": "Field required", - "type": "missing", - }, - { - "input": None, - "loc": ["header", "auth-header"], - "msg": "Field required", - "type": "missing", - }, - ] -} - -snapshots["test_weather_route_payload_errors Incorrect payload type"] = { - "detail": [ - { - "input": "1,1.2", - "loc": ["body", "lat"], - "msg": "Input should be a valid number, unable to parse string as a number", - "type": "float_parsing", - }, - { - "ctx": {"le": 180.0}, - "input": "99999", - "loc": ["body", "lon"], - "msg": "Input should be less than or equal to 180", - "type": "less_than_equal", - }, - ] -} - -snapshots["test_weather_route_payload_errors Missing payload"] = { - "detail": [ - { - "input": {}, - "loc": ["body", "lat"], - "msg": "Field required", - "type": "missing", - }, - { - "input": {}, - "loc": ["body", "lon"], - "msg": "Field required", - "type": "missing", - }, - ] -} diff --git a/openapi_to_fastapi/tests/test_router.py b/openapi_to_fastapi/tests/test_router.py index 4f0e759..9c6f2e4 100644 --- a/openapi_to_fastapi/tests/test_router.py +++ b/openapi_to_fastapi/tests/test_router.py @@ -44,7 +44,7 @@ def test_pydantic_model_loading(specs_root): assert module.ValidationError(loc=[], msg="Crap", type="Error") -def test_weather_route_payload_errors(app, specs_root, client, snapshot): +def test_weather_route_payload_errors(app, specs_root, client, json_snapshot): spec_router = SpecRouter(specs_root / "definitions") @spec_router.post("/Weather/Current/Metric") @@ -55,14 +55,14 @@ def weather_metric(request): resp = client.post("/Weather/Current/Metric", json={}) assert resp.status_code == 422 - snapshot.assert_match(resp.json(), "Missing payload") + assert json_snapshot == resp.json(), "Missing payload" resp = client.post("/Weather/Current/Metric", json={"lat": "1,1.2", "lon": "99999"}) assert resp.status_code == 422 - snapshot.assert_match(resp.json(), "Incorrect payload type") + assert json_snapshot == resp.json(), "Incorrect payload type" -def test_company_custom_post_route(app, client, specs_root, snapshot): +def test_company_custom_post_route(app, client, specs_root): spec_router = SpecRouter(specs_root / "definitions") @spec_router.post("/Company/BasicInfo") @@ -76,7 +76,7 @@ def weather_metric(request): assert resp.json() == company_basic_info_resp -def test_default_post_handler(app, client, specs_root, snapshot): +def test_default_post_handler(app, client, specs_root): spec_router = SpecRouter(specs_root / "definitions") @spec_router.post() @@ -89,7 +89,7 @@ def company_info(request): assert resp.json() == company_basic_info_resp -def test_custom_route_definitions(app, client, specs_root, snapshot): +def test_custom_route_definitions(app, client, specs_root, json_snapshot): spec_router = SpecRouter(specs_root / "definitions") @spec_router.post("/Weather/Current/Metric") @@ -99,7 +99,7 @@ def weather_metric(request, vendor: str, auth_header: str = Header(...)): app.include_router(spec_router.to_fastapi_router()) resp = client.post("/Weather/Current/Metric", json={"lat": "30.5", "lon": 1.56}) assert resp.status_code == 422 - snapshot.assert_match(resp.json(), "Custom route definition") + assert json_snapshot == resp.json(), "Custom route definition" def test_response_model_is_parsed(app, client, specs_root): diff --git a/poetry.lock b/poetry.lock index 3ff7738..6ea06cd 100644 --- a/poetry.lock +++ b/poetry.lock @@ -276,21 +276,6 @@ typer = ">=0.12.3" [package.extras] standard = ["fastapi", "uvicorn[standard] (>=0.15.0)"] -[[package]] -name = "fastdiff" -version = "0.3.0" -description = "A fast native implementation of diff algorithm with a pure python fallback" -optional = false -python-versions = "*" -files = [ - {file = "fastdiff-0.3.0-py2.py3-none-any.whl", hash = "sha256:ca5f61f6ddf5a1564ddfd98132ad28e7abe4a88a638a8b014a2214f71e5918ec"}, - {file = "fastdiff-0.3.0.tar.gz", hash = "sha256:4dfa09c47832a8c040acda3f1f55fc0ab4d666f0e14e6951e6da78d59acd945a"}, -] - -[package.dependencies] -wasmer = ">=1.0.0" -wasmer-compiler-cranelift = ">=1.0.0" - [[package]] name = "flake8" version = "7.0.0" @@ -1112,38 +1097,6 @@ files = [ {file = "shellingham-1.5.4.tar.gz", hash = "sha256:8dbca0739d487e5bd35ab3ca4b36e11c4078f3a234bfce294b0a0291363404de"}, ] -[[package]] -name = "six" -version = "1.16.0" -description = "Python 2 and 3 compatibility utilities" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" -files = [ - {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, - {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, -] - -[[package]] -name = "snapshottest" -version = "0.6.0" -description = "Snapshot testing for pytest, unittest, Django, and Nose" -optional = false -python-versions = "*" -files = [ - {file = "snapshottest-0.6.0-py2.py3-none-any.whl", hash = "sha256:9b177cffe0870c589df8ddbee0a770149c5474b251955bdbde58b7f32a4ec429"}, - {file = "snapshottest-0.6.0.tar.gz", hash = "sha256:bbcaf81d92d8e330042e5c928e13d9f035e99e91b314fe55fda949c2f17b653c"}, -] - -[package.dependencies] -fastdiff = ">=0.1.4,<1" -six = ">=1.10.0" -termcolor = "*" - -[package.extras] -nose = ["nose"] -pytest = ["pytest"] -test = ["django (>=1.10.6)", "nose", "pytest (>=4.6)", "pytest-cov", "six"] - [[package]] name = "sniffio" version = "1.3.1" @@ -1174,18 +1127,18 @@ typing-extensions = {version = ">=3.10.0", markers = "python_version < \"3.10\"" full = ["httpx (>=0.22.0)", "itsdangerous", "jinja2", "python-multipart (>=0.0.7)", "pyyaml"] [[package]] -name = "termcolor" -version = "2.4.0" -description = "ANSI color formatting for output in terminal" +name = "syrupy" +version = "4.6.1" +description = "Pytest Snapshot Test Utility" optional = false -python-versions = ">=3.8" +python-versions = ">=3.8.1,<4" files = [ - {file = "termcolor-2.4.0-py3-none-any.whl", hash = "sha256:9297c0df9c99445c2412e832e882a7884038a25617c60cea2ad69488d4040d63"}, - {file = "termcolor-2.4.0.tar.gz", hash = "sha256:aab9e56047c8ac41ed798fa36d892a37aca6b3e9159f3e0c24bc64a9b3ac7b7a"}, + {file = "syrupy-4.6.1-py3-none-any.whl", hash = "sha256:203e52f9cb9fa749cf683f29bd68f02c16c3bc7e7e5fe8f2fc59bdfe488ce133"}, + {file = "syrupy-4.6.1.tar.gz", hash = "sha256:37a835c9ce7857eeef86d62145885e10b3cb9615bc6abeb4ce404b3f18e1bb36"}, ] -[package.extras] -tests = ["pytest", "pytest-cov"] +[package.dependencies] +pytest = ">=7.0.0,<9.0.0" [[package]] name = "toml" @@ -1394,52 +1347,6 @@ files = [ docs = ["Sphinx (>=4.1.2,<4.2.0)", "sphinx-rtd-theme (>=0.5.2,<0.6.0)", "sphinxcontrib-asyncio (>=0.3.0,<0.4.0)"] test = ["Cython (>=0.29.36,<0.30.0)", "aiohttp (==3.9.0b0)", "aiohttp (>=3.8.1)", "flake8 (>=5.0,<6.0)", "mypy (>=0.800)", "psutil", "pyOpenSSL (>=23.0.0,<23.1.0)", "pycodestyle (>=2.9.0,<2.10.0)"] -[[package]] -name = "wasmer" -version = "1.1.0" -description = "Python extension to run WebAssembly binaries" -optional = false -python-versions = "*" -files = [ - {file = "wasmer-1.1.0-cp310-cp310-macosx_10_7_x86_64.whl", hash = "sha256:c2af4b907ae2dabcac41e316e811d5937c93adf1f8b05c5d49427f8ce0f37630"}, - {file = "wasmer-1.1.0-cp310-cp310-manylinux_2_24_x86_64.whl", hash = "sha256:ab1ae980021e5ec0bf0c6cdd3b979b1d15a5f3eb2b8a32da8dcb1156e4a1e484"}, - {file = "wasmer-1.1.0-cp310-none-win_amd64.whl", hash = "sha256:d0d93aec6215893d33e803ef0a8d37bf948c585dd80ba0e23a83fafee820bc03"}, - {file = "wasmer-1.1.0-cp37-cp37m-macosx_10_7_x86_64.whl", hash = "sha256:1e63d16bd6e2e2272d8721647831de5c537e0bb08002ee6d7abf167ec02d5178"}, - {file = "wasmer-1.1.0-cp37-cp37m-manylinux_2_24_x86_64.whl", hash = "sha256:85e6a5bf44853e8e6a12e947ee3412da9e84f7ce49fc165ba5dbd293e9c5c405"}, - {file = "wasmer-1.1.0-cp37-none-win_amd64.whl", hash = "sha256:a182a6eca9b46d895b4985fc822fab8da3d2f84fab74ca27e55a7430a7fcf336"}, - {file = "wasmer-1.1.0-cp38-cp38-macosx_10_7_x86_64.whl", hash = "sha256:214d9a3cfb577ea9449eb2b5f13adceae34c55365e4c3d930066beb86a7f67bc"}, - {file = "wasmer-1.1.0-cp38-cp38-manylinux_2_24_x86_64.whl", hash = "sha256:b9e5605552bd7d2bc6337519b176defe83bc69b98abf3caaaefa4f7ec231d18a"}, - {file = "wasmer-1.1.0-cp38-none-win_amd64.whl", hash = "sha256:20b5190112e2e94a8947967f2bc683c9685855d0f34130d8434c87a55216a3bd"}, - {file = "wasmer-1.1.0-cp39-cp39-macosx_10_7_x86_64.whl", hash = "sha256:ee442f0970f40ec5e32011c92fd753fb2061da0faa13de13fafc730c31be34e3"}, - {file = "wasmer-1.1.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:aa112198b743cff2e391230436813fb4b244a24443e37866522b7197e3a034da"}, - {file = "wasmer-1.1.0-cp39-cp39-manylinux_2_24_x86_64.whl", hash = "sha256:c0b37117f6d3ff51ee96431c7d224d99799b08d174e30fcd0fcd7e2e3cb8140c"}, - {file = "wasmer-1.1.0-cp39-none-win_amd64.whl", hash = "sha256:a0a4730ec4907a4cb0d9d4a77ea2608c2c814f22a22b73fc80be0f110e014836"}, - {file = "wasmer-1.1.0-py3-none-any.whl", hash = "sha256:2caf8c67feae9cd4246421551036917811c446da4f27ad4c989521ef42751931"}, -] - -[[package]] -name = "wasmer-compiler-cranelift" -version = "1.1.0" -description = "The Cranelift compiler for the `wasmer` package (to compile WebAssembly module)" -optional = false -python-versions = "*" -files = [ - {file = "wasmer_compiler_cranelift-1.1.0-cp310-cp310-macosx_10_7_x86_64.whl", hash = "sha256:9869910179f39696a020edc5689f7759257ac1cce569a7a0fcf340c59788baad"}, - {file = "wasmer_compiler_cranelift-1.1.0-cp310-cp310-manylinux_2_24_x86_64.whl", hash = "sha256:405546ee864ac158a4107f374dfbb1c8d6cfb189829bdcd13050143a4bd98f28"}, - {file = "wasmer_compiler_cranelift-1.1.0-cp310-none-win_amd64.whl", hash = "sha256:bdf75af9ef082e6aeb752550f694273340ece970b65099e0746db0f972760d11"}, - {file = "wasmer_compiler_cranelift-1.1.0-cp37-cp37m-macosx_10_7_x86_64.whl", hash = "sha256:7d9c782b7721789b16e303b7e70c59df370896dd62b77e2779e3a44b4e1aa20c"}, - {file = "wasmer_compiler_cranelift-1.1.0-cp37-cp37m-manylinux_2_24_x86_64.whl", hash = "sha256:ff7dd5bd69030b63521c24583bf0f5457cd2580237340b91ce35370f72a4a1cc"}, - {file = "wasmer_compiler_cranelift-1.1.0-cp37-none-win_amd64.whl", hash = "sha256:447285402e366a34667a674db70458c491acd6940b797c175c0b0027f48e64bb"}, - {file = "wasmer_compiler_cranelift-1.1.0-cp38-cp38-macosx_10_7_x86_64.whl", hash = "sha256:55a524985179f6b7b88ac973e8fac5a2574d3b125a966fba75fedd5a2525e484"}, - {file = "wasmer_compiler_cranelift-1.1.0-cp38-cp38-manylinux_2_24_x86_64.whl", hash = "sha256:bd03db5a916ead51b442c66acad38847dfe127cf90b2019b1680f1920c4f8d06"}, - {file = "wasmer_compiler_cranelift-1.1.0-cp38-none-win_amd64.whl", hash = "sha256:157d87cbd1d04adbad55b50cb4bedc28e444caf74797fd96df17390667e58699"}, - {file = "wasmer_compiler_cranelift-1.1.0-cp39-cp39-macosx_10_7_x86_64.whl", hash = "sha256:ff25fc99ebafa04a6c271d08a90d17b927930e3019a2b333c7cfb48ba32c6f71"}, - {file = "wasmer_compiler_cranelift-1.1.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:9697ae082317a56776df8ff7df8c922eac38488ef38d3478fe5f0ca144c185ab"}, - {file = "wasmer_compiler_cranelift-1.1.0-cp39-cp39-manylinux_2_24_x86_64.whl", hash = "sha256:2a4349b1ddd727bd46bc5ede741839dcfc5153c52f064a83998c4150d5d4a85c"}, - {file = "wasmer_compiler_cranelift-1.1.0-cp39-none-win_amd64.whl", hash = "sha256:32fe38614fccc933da77ee4372904a5fa9c12b859209a2e4048a8284c4c371f2"}, - {file = "wasmer_compiler_cranelift-1.1.0-py3-none-any.whl", hash = "sha256:200fea80609cfb088457327acf66d5aa61f4c4f66b5a71133ada960b534c7355"}, -] - [[package]] name = "watchfiles" version = "0.22.0" @@ -1611,4 +1518,4 @@ files = [ [metadata] lock-version = "2.0" python-versions = ">=3.8.1,<4.0.0" -content-hash = "8d11071a5cfffd11d2db09767020a37324af2a6fe1f0d0504755dc7b96962066" +content-hash = "a3ca4b3f91130a1cbe77b2b34ee46b30045ac12d2ce884de4e6dbf57af6bd5d2" diff --git a/pyproject.toml b/pyproject.toml index daea28c..e780e01 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -25,11 +25,11 @@ pydantic = "^2.7.1" isort = "^5.13.2" pytest = "^8.2.1" pytest-asyncio = "^0.23.7" -snapshottest = "^0.6.0" flake8 = "^7.0.0" mypy = "^1.10.0" invoke = "^2.2.0" httpx = "^0.27.0" +syrupy = "^4.6.1" [tool.skjold] report_only = false