diff --git a/.github/renovate.json b/.github/renovate.json new file mode 100644 index 0000000..6dfcc7d --- /dev/null +++ b/.github/renovate.json @@ -0,0 +1,18 @@ +{ + "lockFileMaintenance": { + "enabled": true, + "automerge": true + }, + "packageRules": [ + { + "matchPackageNames": [ + "ruff" + ], + "matchUpdateTypes": [ + "minor", + "patch" + ], + "automerge": true + } + ] +} \ No newline at end of file diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml new file mode 100644 index 0000000..2ce9b5f --- /dev/null +++ b/.github/workflows/tests.yaml @@ -0,0 +1,37 @@ +name: Run Tests + +on: + push: + branches: + - "main" + pull_request: +jobs: + pytest: + runs-on: ubuntu-latest + strategy: + matrix: + python-version: ["3.11"] + steps: + - uses: actions/checkout@v4 + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v5 + with: + python-version: ${{ matrix.python-version }} + - name: Test with pytest + run: | + make tests + + lint: + runs-on: ubuntu-latest + strategy: + matrix: + python-version: ["3.11"] + steps: + - uses: actions/checkout@v4 + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v5 + with: + python-version: ${{ matrix.python-version }} + - name: Lint with ruff + run: | + make lint diff --git a/poetry.lock b/poetry.lock index 1d74bda..524ee77 100644 --- a/poetry.lock +++ b/poetry.lock @@ -420,6 +420,20 @@ https = ["urllib3 (>=1.24.1)"] paramiko = ["paramiko"] pgp = ["gpg"] +[[package]] +name = "exceptiongroup" +version = "1.2.1" +description = "Backport of PEP 654 (exception groups)" +optional = false +python-versions = ">=3.7" +files = [ + {file = "exceptiongroup-1.2.1-py3-none-any.whl", hash = "sha256:5258b9ed329c5bbdd31a309f53cbfb0b155341807f6ff7606a1e801a891b29ad"}, + {file = "exceptiongroup-1.2.1.tar.gz", hash = "sha256:a4785e48b045528f5bfe627b6ad554ff32def154f42372786903b7abcfe1aa16"}, +] + +[package.extras] +test = ["pytest (>=6)"] + [[package]] name = "fastjsonschema" version = "2.20.0" @@ -480,6 +494,17 @@ doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linke perf = ["ipython"] test = ["flufl.flake8", "importlib-resources (>=1.3)", "jaraco.test (>=5.4)", "packaging", "pyfakefs", "pytest (>=6,!=8.1.*)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy", "pytest-perf (>=0.9.2)", "pytest-ruff (>=0.2.1)"] +[[package]] +name = "iniconfig" +version = "2.0.0" +description = "brain-dead simple config-ini parsing" +optional = false +python-versions = ">=3.7" +files = [ + {file = "iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374"}, + {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"}, +] + [[package]] name = "installer" version = "0.7.0" @@ -678,6 +703,21 @@ docs = ["furo (>=2023.9.10)", "proselint (>=0.13)", "sphinx (>=7.2.6)", "sphinx- test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.4.3)", "pytest-cov (>=4.1)", "pytest-mock (>=3.12)"] type = ["mypy (>=1.8)"] +[[package]] +name = "pluggy" +version = "1.5.0" +description = "plugin and hook calling mechanisms for python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669"}, + {file = "pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1"}, +] + +[package.extras] +dev = ["pre-commit", "tox"] +testing = ["pytest", "pytest-benchmark"] + [[package]] name = "poetry" version = "1.8.3" @@ -918,6 +958,42 @@ files = [ {file = "pyproject_hooks-1.1.0.tar.gz", hash = "sha256:4b37730834edbd6bd37f26ece6b44802fb1c1ee2ece0e54ddff8bfc06db86965"}, ] +[[package]] +name = "pytest" +version = "8.2.2" +description = "pytest: simple powerful testing with Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pytest-8.2.2-py3-none-any.whl", hash = "sha256:c434598117762e2bd304e526244f67bf66bbd7b5d6cf22138be51ff661980343"}, + {file = "pytest-8.2.2.tar.gz", hash = "sha256:de4bb8104e201939ccdc688b27a89a7be2079b22e2bd2b07f806b6ba71117977"}, +] + +[package.dependencies] +colorama = {version = "*", markers = "sys_platform == \"win32\""} +exceptiongroup = {version = ">=1.0.0rc8", markers = "python_version < \"3.11\""} +iniconfig = "*" +packaging = "*" +pluggy = ">=1.5,<2.0" +tomli = {version = ">=1", markers = "python_version < \"3.11\""} + +[package.extras] +dev = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"] + +[[package]] +name = "pytest-github-actions-annotate-failures" +version = "0.2.0" +description = "pytest plugin to annotate failed tests with a workflow command for GitHub Actions" +optional = false +python-versions = ">=3.7" +files = [ + {file = "pytest-github-actions-annotate-failures-0.2.0.tar.gz", hash = "sha256:844ab626d389496e44f960b42f0a72cce29ae06d363426d17ea9ae1b4bef2288"}, + {file = "pytest_github_actions_annotate_failures-0.2.0-py3-none-any.whl", hash = "sha256:8bcef65fed503faaa0524b59cfeccc8995130972dd7b008d64193cc41b9cde85"}, +] + +[package.dependencies] +pytest = ">=4.0.0" + [[package]] name = "pywin32-ctypes" version = "0.2.2" @@ -1294,4 +1370,4 @@ test = ["big-O", "importlib-resources", "jaraco.functools", "jaraco.itertools", [metadata] lock-version = "2.0" python-versions = "^3.10" -content-hash = "33f90eacb6df3f8d25482834381eaafd184c8b788620fafbf7f3131bbab68efd" +content-hash = "322f64aedf6d699526fd49e76d08a540c9ccfea3fedf2619f33d23b79cdd5751" diff --git a/pyproject.toml b/pyproject.toml index 6d9be46..5d3ebfe 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -12,8 +12,20 @@ packages = [{include = "pydantic_spaceapi"}] python = "^3.10" poetry = "^1.7.1" pydantic-extra-types = "^2.8.2" + +[tool.poetry.group.test] +optional = true + +[tool.poetry.group.test.dependencies] +pytest = "^8.0.0" ruff = "^0.5.1" +[tool.poetry.group.github] +optional = true + +[tool.poetry.group.github.dependencies] +pytest-github-actions-annotate-failures = "^0.2.0" + [build-system] requires = ["poetry-core"] build-backend = "poetry.core.masonry.api" diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/examples/valid_v13.json b/tests/examples/valid_v13.json new file mode 100644 index 0000000..0bb4dd2 --- /dev/null +++ b/tests/examples/valid_v13.json @@ -0,0 +1 @@ +{"api":"0.13","space":"Leigh Hackspace","logo":"https://raw.githubusercontent.com/leigh-hackspace/logos-graphics-assets/master/logo/rose_logo.svg","url":"https://leighhack.org/","location":{"address":"Leigh Hackspace, Unit 3.14, 3rd Floor, Leigh Spinners Mill, Park Lane, Leigh, WN7 2LB, United Kingdom","lat":53.493497,"lon":-2.493479,"timezone":"Europe/London","ext_osm_node":4300807520},"state":{"open":true,"lastchange":1720439671},"contact":{"twitter":"@leigh_hackspace","email":"info@leighhack.org","mastodon":"@leigh_hackspace@mastodon.social","ext_slack":"leighhack.slack.com","ext_instagram":"leighhackspace"},"issue_report_channels":["email"],"sensors":{"temperature":[{"value":22.91,"location":"Pi Room","unit":"°C"},{"value":23.6,"location":"Fabrication","unit":"°C"},{"value":24.0,"location":"Workshop","unit":"°C"},{"value":22.8,"location":"Main Space","unit":"°C"},{"value":22.8,"location":"Dark Room","unit":"°C"},{"value":23.7,"location":"Craft Space","unit":"°C"},{"value":22.4,"location":"Social Space","unit":"°C"},{"value":19.8,"location":"Outside","unit":"°C"}],"humidity":[{"value":54.92,"location":"Pi Room","unit":"%"},{"value":49.0,"location":"Fabrication","unit":"%"},{"value":51.0,"location":"Workshop","unit":"%"},{"value":50.0,"location":"Main Space","unit":"%"},{"value":50.0,"location":"Dark Room","unit":"%"},{"value":46.0,"location":"Craft Space","unit":"%"},{"value":61.0,"location":"Social Space","unit":"%"},{"value":60.0,"location":"Outside","unit":"%"}],"network_connections":[{"value":23.0,"location":"WiFi Clients","type":"wifi"}],"total_member_count":[{"value":24.0,"name":"Active Members"}],"ext_3d_printers":[{"name":"3D-1","state":"offline","lastchange":1720454454},{"name":"3D-2","state":"offline","lastchange":1719845534},{"name":"3D-3","state":"offline","lastchange":1720454466}]},"feeds":{"blog":{"type":"rss","url":"https://leighhack.org/blog/index.xml"},"calendar":{"type":"ical","url":"http://api.leighhack.org/events.ics"}},"api_compatibility":["13","14"],"links":[{"name":"Github","url":"https://github.com/leigh-hackspace"},{"name":"Slack","url":"https://join.slack.com/t/leighhack/shared_invite/enQtNDYzMjEyMDMxNDExLTE1MWY5N2IwMzdhMzQ0ZWFiNDkyNzJmMGM1ZmFkODcwMGM5ODFmYmI4MjhmM2JiMWEyY2E3NTRjMTQzMzljZWU"},{"name":"Discourse","url":"https://discourse.leighhack.org/"},{"name":"Join Leigh Hackspace","url":"https://leighhack.org/membership/"}],"membership_plans":[{"name":"Member","value":24,"currency":"GBP","billing_interval":"monthly","description":"Our standard membership that allows usage of the hackspace facilities.","ext_link":"https://pay.gocardless.com/AL00024VQTCKBK"},{"name":"Member+","value":30,"currency":"GBP","billing_interval":"monthly","description":"Standard membership with an additional donation.","ext_link":"https://pay.gocardless.com/AL00024VQW2FWQ"},{"name":"Concession","value":18,"currency":"GBP","billing_interval":"monthly","description":"A subsidised membership for pensioners, students, and low income earners.","ext_link":"https://pay.gocardless.com/AL0002BMN4S3AX"},{"name":"Family","value":40,"currency":"GBP","billing_interval":"monthly","description":"A discounted family membership for two adults and two children.","ext_link":"https://pay.gocardless.com/AL000637J3VZP7"},{"name":"Day Pass","value":5,"currency":"GBP","billing_interval":"daily","description":"Access to the hackspace's facilities for a day.","ext_link":"mailto:info@leighhack.org?subject=Day%20Pass"},{"name":"Patron","value":5,"currency":"GBP","billing_interval":"monthly","description":"Support the hackspace without being a member.","ext_link":"https://pay.gocardless.com/AL000FDGN4Q6AC"}],"ext_dabo":"Dabo!"} \ No newline at end of file diff --git a/tests/test_v13.py b/tests/test_v13.py new file mode 100644 index 0000000..2519391 --- /dev/null +++ b/tests/test_v13.py @@ -0,0 +1,17 @@ +import pytest +import json +import pathlib +from pydantic_spaceapi.v13 import SpaceAPIv13Model + +@pytest.fixture +def valid_v13(): + file = pathlib.Path("tests/examples/valid_v13.json") + with open(file) as f: + return json.loads(f.read()) + +def test_working(valid_v13): + """ + Check that we can parse a valid v13 definition + """ + obj = SpaceAPIv13Model(**valid_v13) + assert isinstance(obj, SpaceAPIv13Model) \ No newline at end of file