From 4dd3455c3439335e25f179d9050d4bb11756f023 Mon Sep 17 00:00:00 2001 From: Jordi Soucheiron Date: Thu, 28 Dec 2023 12:50:06 +0100 Subject: [PATCH 01/12] Add release drafter --- .github/workflows/pypi-release.yml | 2 +- .github/workflows/release-drafter.yml | 32 +++++++++++++++++++++++++++ pycfmodel/__version__.py | 3 +++ setup.py | 3 ++- 4 files changed, 38 insertions(+), 2 deletions(-) create mode 100644 .github/workflows/release-drafter.yml create mode 100644 pycfmodel/__version__.py diff --git a/.github/workflows/pypi-release.yml b/.github/workflows/pypi-release.yml index edb928e..45436b9 100644 --- a/.github/workflows/pypi-release.yml +++ b/.github/workflows/pypi-release.yml @@ -24,6 +24,6 @@ jobs: run: python setup.py sdist bdist_wheel - name: Publish distribution 📦 to PyPI - uses: pypa/gh-action-pypi-publish@master + uses: pypa/gh-action-pypi-publish@release/v1 with: password: ${{ secrets.pypi_password }} diff --git a/.github/workflows/release-drafter.yml b/.github/workflows/release-drafter.yml new file mode 100644 index 0000000..56eaca4 --- /dev/null +++ b/.github/workflows/release-drafter.yml @@ -0,0 +1,32 @@ +name: Release Drafter + +on: + push: + # branches to consider in the event; optional, defaults to all + branches: + - master + # pull_request event is required only for autolabeler + pull_request: + # Only following types are handled by the action, but one can default to all as well + types: [opened, reopened, synchronize] + # pull_request_target event is required for autolabeler to support PRs from forks + pull_request_target: + types: [opened, reopened, synchronize] + +permissions: + contents: read + +jobs: + update_release_draft: + permissions: + # write permission is required to create a github release + contents: write + # write permission is required for autolabeler + # otherwise, read permission is required at least + pull-requests: write + runs-on: ubuntu-latest + steps: + # Drafts your next Release notes as Pull Requests are merged into "master" + - uses: release-drafter/release-drafter@v5 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} \ No newline at end of file diff --git a/pycfmodel/__version__.py b/pycfmodel/__version__.py new file mode 100644 index 0000000..361bb3b --- /dev/null +++ b/pycfmodel/__version__.py @@ -0,0 +1,3 @@ +VERSION = (0, 21, 2) + +__version__ = ".".join(map(str, VERSION)) \ No newline at end of file diff --git a/setup.py b/setup.py index e510260..b63eca0 100644 --- a/setup.py +++ b/setup.py @@ -1,6 +1,7 @@ from pathlib import Path from setuptools import find_namespace_packages, setup +from pycfmodel.__version__ import __version__ readme = Path(__file__).parent / "README.md" long_description = readme.read_text() @@ -28,7 +29,7 @@ setup( name="pycfmodel", - version="0.21.1", + version=__version__, description="A python model for CloudFormation scripts", author="Skyscanner Product Security", author_email="security@skyscanner.net", From 58409334a6150f9b92c2e802bad49038b9472bf1 Mon Sep 17 00:00:00 2001 From: Jordi Soucheiron Date: Thu, 28 Dec 2023 12:59:01 +0100 Subject: [PATCH 02/12] Add release drafter config --- .github/release-drafter.yml | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 .github/release-drafter.yml diff --git a/.github/release-drafter.yml b/.github/release-drafter.yml new file mode 100644 index 0000000..fe1351d --- /dev/null +++ b/.github/release-drafter.yml @@ -0,0 +1,31 @@ +name-template: 'v$RESOLVED_VERSION 🌈' +tag-template: 'v$RESOLVED_VERSION' +categories: + - title: '🚀 Features' + labels: + - 'feature' + - 'enhancement' + - title: '🐛 Bug Fixes' + labels: + - 'fix' + - 'bugfix' + - 'bug' + - title: '🧰 Maintenance' + label: 'chore' +change-template: '- $TITLE @$AUTHOR (#$NUMBER)' +change-title-escapes: '\<*_&' # You can add # and @ to disable mentions, and add ` to disable code blocks. +version-resolver: + major: + labels: + - 'major' + minor: + labels: + - 'minor' + patch: + labels: + - 'patch' + default: patch +template: | + ## Changes + + $CHANGES \ No newline at end of file From dd5477ff1b795f6387fe8ed5417708f8a46b99ce Mon Sep 17 00:00:00 2001 From: Jordi Soucheiron Date: Thu, 28 Dec 2023 13:06:20 +0100 Subject: [PATCH 03/12] Remove release drafter action --- .github/release-drafter.yml | 31 ------------------------------- 1 file changed, 31 deletions(-) delete mode 100644 .github/release-drafter.yml diff --git a/.github/release-drafter.yml b/.github/release-drafter.yml deleted file mode 100644 index fe1351d..0000000 --- a/.github/release-drafter.yml +++ /dev/null @@ -1,31 +0,0 @@ -name-template: 'v$RESOLVED_VERSION 🌈' -tag-template: 'v$RESOLVED_VERSION' -categories: - - title: '🚀 Features' - labels: - - 'feature' - - 'enhancement' - - title: '🐛 Bug Fixes' - labels: - - 'fix' - - 'bugfix' - - 'bug' - - title: '🧰 Maintenance' - label: 'chore' -change-template: '- $TITLE @$AUTHOR (#$NUMBER)' -change-title-escapes: '\<*_&' # You can add # and @ to disable mentions, and add ` to disable code blocks. -version-resolver: - major: - labels: - - 'major' - minor: - labels: - - 'minor' - patch: - labels: - - 'patch' - default: patch -template: | - ## Changes - - $CHANGES \ No newline at end of file From de464ff3853e6ab6802a417da5f124b709eb17ef Mon Sep 17 00:00:00 2001 From: Jordi Soucheiron Date: Thu, 28 Dec 2023 13:22:46 +0100 Subject: [PATCH 04/12] New freeze system --- Makefile | 29 ++++++++++++++++++++++------- requirements-dev.txt | 42 ++++++++++++++++++++++++++++++++++++++++++ requirements-docs.txt | 33 +++++++++++++++++++++++++++++++++ requirements.txt | 4 ++-- setup.py | 3 +-- 5 files changed, 100 insertions(+), 11 deletions(-) create mode 100644 requirements-dev.txt create mode 100644 requirements-docs.txt diff --git a/Makefile b/Makefile index 45116e0..cc7240b 100644 --- a/Makefile +++ b/Makefile @@ -4,10 +4,10 @@ install: pip install -r requirements.txt install-dev: install - pip install -e ".[dev]" + pip install -r requirements-dev.txt install-docs: - pip install -e ".[dev,docs]" + pip install -r requirements-docs.txt format: isort-format black-format @@ -42,10 +42,25 @@ test: lint unit test-docs: mkdocs build --strict -freeze: - CUSTOM_COMPILE_COMMAND="make freeze" pip-compile --no-emit-index-url --no-annotate --output-file requirements.txt setup.py +FREEZE_OPTIONS = --no-emit-index-url --no-annotate -v +freeze-base: + CUSTOM_COMPILE_COMMAND="make freeze" pip-compile $(FREEZE_OPTIONS) setup.py --output-file requirements.txt +freeze-dev: + CUSTOM_COMPILE_COMMAND="make freeze" pip-compile $(FREEZE_OPTIONS) setup.py --extra dev --output-file requirements-dev.txt +freeze-docs: + CUSTOM_COMPILE_COMMAND="make freeze" pip-compile $(FREEZE_OPTIONS) setup.py --extra docs --output-file requirements-docs.txt -freeze-upgrade: - CUSTOM_COMPILE_COMMAND="make freeze" pip-compile --no-emit-index-url --upgrade --no-annotate --output-file requirements.txt setup.py +freeze: freeze-base freeze-dev freeze-docs -.PHONY: install install-dev install-docs format isort-format black-format lint isort-lint black-lint flake8-lint unit coverage test freeze freeze-upgrade +freeze-base-upgrade: + CUSTOM_COMPILE_COMMAND="make freeze" pip-compile $(FREEZE_OPTIONS) --upgrade setup.py --output-file requirements.txt +freeze-dev-upgrade: + CUSTOM_COMPILE_COMMAND="make freeze" pip-compile $(FREEZE_OPTIONS) --upgrade setup.py --extra dev --output-file requirements-dev.txt +freeze-docs-upgrade: + CUSTOM_COMPILE_COMMAND="make freeze" pip-compile $(FREEZE_OPTIONS) --upgrade setup.py --extra docs --output-file requirements-docs.txt + +freeze-upgrade: freeze-base-upgrade freeze-dev-upgrade freeze-docs-upgrade + +.PHONY: install install-dev install-docs format isort-format black-format lint isort-lint black-lint flake8-lint unit \ + coverage test freeze freeze-upgrade freeze-base freeze-dev freeze-docs freeze-base-upgrade freeze-dev-upgrade \ + freeze-docs-upgrade diff --git a/requirements-dev.txt b/requirements-dev.txt new file mode 100644 index 0000000..f9e04ba --- /dev/null +++ b/requirements-dev.txt @@ -0,0 +1,42 @@ +# +# This file is autogenerated by pip-compile with Python 3.10 +# by the following command: +# +# make freeze +# +anyio==4.2.0 +black==23.12.1 +build==1.0.3 +certifi==2023.11.17 +click==8.1.7 +coverage[toml]==7.4.0 +exceptiongroup==1.2.0 +flake8==6.1.0 +h11==0.14.0 +httpcore==1.0.2 +httpx==0.26.0 +idna==3.6 +iniconfig==2.0.0 +isort==5.13.2 +mccabe==0.7.0 +mypy-extensions==1.0.0 +packaging==23.2 +pathspec==0.12.1 +pip-tools==7.3.0 +platformdirs==4.1.0 +pluggy==1.3.0 +pycodestyle==2.11.1 +pydantic==1.10.13 +pyflakes==3.1.0 +pyproject-hooks==1.0.0 +pytest==7.4.3 +pytest-cov==4.1.0 +pytest-repeat==0.9.3 +sniffio==1.3.0 +tomli==2.0.1 +typing-extensions==4.9.0 +wheel==0.42.0 + +# The following packages are considered to be unsafe in a requirements file: +# pip +# setuptools diff --git a/requirements-docs.txt b/requirements-docs.txt new file mode 100644 index 0000000..031e150 --- /dev/null +++ b/requirements-docs.txt @@ -0,0 +1,33 @@ +# +# This file is autogenerated by pip-compile with Python 3.10 +# by the following command: +# +# make freeze +# +automacdoc==0.3 +beautifulsoup4==4.12.2 +click==8.1.7 +ghp-import==2.1.0 +importlib-metadata==7.0.1 +jinja2==3.1.2 +livereload==2.6.3 +markdown==3.5.1 +markupsafe==2.1.3 +mergedeep==1.3.4 +mkdocs==1.3.0 +mkdocs-material==4.6.3 +mkdocstrings==0.10.0 +packaging==23.2 +pydantic==1.10.13 +pygments==2.17.2 +pymdown-extensions==10.6 +python-dateutil==2.8.2 +pytkdocs==0.2.1 +pyyaml==6.0.1 +pyyaml-env-tag==0.1 +six==1.16.0 +soupsieve==2.5 +tornado==6.4 +typing-extensions==4.9.0 +watchdog==3.0.0 +zipp==3.17.0 diff --git a/requirements.txt b/requirements.txt index b124577..0287dbd 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,6 +1,6 @@ # -# This file is autogenerated by pip-compile with python 3.9 -# To update, run: +# This file is autogenerated by pip-compile with Python 3.10 +# by the following command: # # make freeze # diff --git a/setup.py b/setup.py index b63eca0..bc031b8 100644 --- a/setup.py +++ b/setup.py @@ -1,7 +1,6 @@ from pathlib import Path from setuptools import find_namespace_packages, setup -from pycfmodel.__version__ import __version__ readme = Path(__file__).parent / "README.md" long_description = readme.read_text() @@ -29,7 +28,7 @@ setup( name="pycfmodel", - version=__version__, + version="0.21.2", description="A python model for CloudFormation scripts", author="Skyscanner Product Security", author_email="security@skyscanner.net", From 8f5d8c731e1647ddd10533f71b28d3d16c952076 Mon Sep 17 00:00:00 2001 From: Jordi Soucheiron Date: Thu, 28 Dec 2023 13:24:54 +0100 Subject: [PATCH 05/12] Use older typing extensions to support 3.7 --- requirements-dev.txt | 2 +- requirements-docs.txt | 2 +- requirements.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/requirements-dev.txt b/requirements-dev.txt index f9e04ba..299cf11 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -34,7 +34,7 @@ pytest-cov==4.1.0 pytest-repeat==0.9.3 sniffio==1.3.0 tomli==2.0.1 -typing-extensions==4.9.0 +typing-extensions==3.7.1 wheel==0.42.0 # The following packages are considered to be unsafe in a requirements file: diff --git a/requirements-docs.txt b/requirements-docs.txt index 031e150..4dc48d7 100644 --- a/requirements-docs.txt +++ b/requirements-docs.txt @@ -28,6 +28,6 @@ pyyaml-env-tag==0.1 six==1.16.0 soupsieve==2.5 tornado==6.4 -typing-extensions==4.9.0 +typing-extensions==3.7.1 watchdog==3.0.0 zipp==3.17.0 diff --git a/requirements.txt b/requirements.txt index 0287dbd..5b9e175 100644 --- a/requirements.txt +++ b/requirements.txt @@ -5,4 +5,4 @@ # make freeze # pydantic==1.9.0 -typing-extensions==4.1.1 +typing-extensions==3.7.1 From 22c7bd8c995fc7d5ea448fdccd316296030f4b4e Mon Sep 17 00:00:00 2001 From: Jordi Soucheiron Date: Thu, 28 Dec 2023 13:27:39 +0100 Subject: [PATCH 06/12] Add release drafter config --- .github/release-drafter.yml | 17 ++++++++++++++ .github/workflows/release-drafter.yml | 32 --------------------------- requirements-dev.txt | 2 +- requirements-docs.txt | 2 +- requirements.txt | 2 +- 5 files changed, 20 insertions(+), 35 deletions(-) create mode 100644 .github/release-drafter.yml delete mode 100644 .github/workflows/release-drafter.yml diff --git a/.github/release-drafter.yml b/.github/release-drafter.yml new file mode 100644 index 0000000..4bd13a1 --- /dev/null +++ b/.github/release-drafter.yml @@ -0,0 +1,17 @@ +name-template: 'v$RESOLVED_VERSION' +tag-template: 'v$RESOLVED_VERSION' +change-template: '- $TITLE @$AUTHOR (#$NUMBER)' +change-title-escapes: '\<*_&' # You can add # and @ to disable mentions, and add ` to disable code blocks. +version-resolver: + major: + labels: + - 'major' + minor: + labels: + - 'minor' + patch: + labels: + - 'patch' + default: patch +template: | + ## Changes diff --git a/.github/workflows/release-drafter.yml b/.github/workflows/release-drafter.yml deleted file mode 100644 index 56eaca4..0000000 --- a/.github/workflows/release-drafter.yml +++ /dev/null @@ -1,32 +0,0 @@ -name: Release Drafter - -on: - push: - # branches to consider in the event; optional, defaults to all - branches: - - master - # pull_request event is required only for autolabeler - pull_request: - # Only following types are handled by the action, but one can default to all as well - types: [opened, reopened, synchronize] - # pull_request_target event is required for autolabeler to support PRs from forks - pull_request_target: - types: [opened, reopened, synchronize] - -permissions: - contents: read - -jobs: - update_release_draft: - permissions: - # write permission is required to create a github release - contents: write - # write permission is required for autolabeler - # otherwise, read permission is required at least - pull-requests: write - runs-on: ubuntu-latest - steps: - # Drafts your next Release notes as Pull Requests are merged into "master" - - uses: release-drafter/release-drafter@v5 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} \ No newline at end of file diff --git a/requirements-dev.txt b/requirements-dev.txt index 299cf11..78ae128 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -34,7 +34,7 @@ pytest-cov==4.1.0 pytest-repeat==0.9.3 sniffio==1.3.0 tomli==2.0.1 -typing-extensions==3.7.1 +typing-extensions==4.7.1 wheel==0.42.0 # The following packages are considered to be unsafe in a requirements file: diff --git a/requirements-docs.txt b/requirements-docs.txt index 4dc48d7..09faf22 100644 --- a/requirements-docs.txt +++ b/requirements-docs.txt @@ -28,6 +28,6 @@ pyyaml-env-tag==0.1 six==1.16.0 soupsieve==2.5 tornado==6.4 -typing-extensions==3.7.1 +typing-extensions==4.7.1 watchdog==3.0.0 zipp==3.17.0 diff --git a/requirements.txt b/requirements.txt index 5b9e175..06dace5 100644 --- a/requirements.txt +++ b/requirements.txt @@ -5,4 +5,4 @@ # make freeze # pydantic==1.9.0 -typing-extensions==3.7.1 +typing-extensions==4.7.1 From e38b9d48d8868225e26034de1a1a8eacecc6f907 Mon Sep 17 00:00:00 2001 From: Jordi Soucheiron Date: Thu, 28 Dec 2023 13:33:51 +0100 Subject: [PATCH 07/12] Upgrade requirements to work with 3.7 --- Makefile | 2 +- requirements-dev.txt | 35 +++++++++++++++++++---------------- requirements-docs.txt | 16 ++++++++-------- requirements.txt | 2 +- 4 files changed, 29 insertions(+), 26 deletions(-) diff --git a/Makefile b/Makefile index cc7240b..8c756d0 100644 --- a/Makefile +++ b/Makefile @@ -42,7 +42,7 @@ test: lint unit test-docs: mkdocs build --strict -FREEZE_OPTIONS = --no-emit-index-url --no-annotate -v +FREEZE_OPTIONS = --no-emit-index-url --no-annotate -v --resolver=backtracking freeze-base: CUSTOM_COMPILE_COMMAND="make freeze" pip-compile $(FREEZE_OPTIONS) setup.py --output-file requirements.txt freeze-dev: diff --git a/requirements-dev.txt b/requirements-dev.txt index 78ae128..03cf4a4 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -1,41 +1,44 @@ # -# This file is autogenerated by pip-compile with Python 3.10 +# This file is autogenerated by pip-compile with Python 3.7 # by the following command: # # make freeze # -anyio==4.2.0 -black==23.12.1 -build==1.0.3 +anyio==3.7.1 +black==23.3.0 +build==0.10.0 certifi==2023.11.17 click==8.1.7 -coverage[toml]==7.4.0 +coverage[toml]==7.2.7 exceptiongroup==1.2.0 -flake8==6.1.0 +flake8==5.0.4 h11==0.14.0 -httpcore==1.0.2 -httpx==0.26.0 +httpcore==0.17.3 +httpx==0.24.1 idna==3.6 +importlib-metadata==4.2.0 iniconfig==2.0.0 -isort==5.13.2 +isort==5.11.5 mccabe==0.7.0 mypy-extensions==1.0.0 packaging==23.2 -pathspec==0.12.1 -pip-tools==7.3.0 -platformdirs==4.1.0 -pluggy==1.3.0 -pycodestyle==2.11.1 -pydantic==1.10.13 -pyflakes==3.1.0 +pathspec==0.11.2 +pip-tools==6.14.0 +platformdirs==4.0.0 +pluggy==1.2.0 +pycodestyle==2.9.1 +pydantic==1.9.0 +pyflakes==2.5.0 pyproject-hooks==1.0.0 pytest==7.4.3 pytest-cov==4.1.0 pytest-repeat==0.9.3 sniffio==1.3.0 tomli==2.0.1 +typed-ast==1.5.5 typing-extensions==4.7.1 wheel==0.42.0 +zipp==3.15.0 # The following packages are considered to be unsafe in a requirements file: # pip diff --git a/requirements-docs.txt b/requirements-docs.txt index 09faf22..31be8bf 100644 --- a/requirements-docs.txt +++ b/requirements-docs.txt @@ -1,5 +1,5 @@ # -# This file is autogenerated by pip-compile with Python 3.10 +# This file is autogenerated by pip-compile with Python 3.7 # by the following command: # # make freeze @@ -8,26 +8,26 @@ automacdoc==0.3 beautifulsoup4==4.12.2 click==8.1.7 ghp-import==2.1.0 -importlib-metadata==7.0.1 +importlib-metadata==6.7.0 jinja2==3.1.2 livereload==2.6.3 -markdown==3.5.1 +markdown==3.4.4 markupsafe==2.1.3 mergedeep==1.3.4 mkdocs==1.3.0 mkdocs-material==4.6.3 mkdocstrings==0.10.0 packaging==23.2 -pydantic==1.10.13 +pydantic==1.9.0 pygments==2.17.2 -pymdown-extensions==10.6 +pymdown-extensions==10.2.1 python-dateutil==2.8.2 pytkdocs==0.2.1 pyyaml==6.0.1 pyyaml-env-tag==0.1 six==1.16.0 -soupsieve==2.5 -tornado==6.4 +soupsieve==2.4.1 +tornado==6.2 typing-extensions==4.7.1 watchdog==3.0.0 -zipp==3.17.0 +zipp==3.15.0 diff --git a/requirements.txt b/requirements.txt index 06dace5..fa1813f 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,5 +1,5 @@ # -# This file is autogenerated by pip-compile with Python 3.10 +# This file is autogenerated by pip-compile with Python 3.7 # by the following command: # # make freeze From 924ec1ab611f655dd6f228291e95bc7b97773ecb Mon Sep 17 00:00:00 2001 From: Jordi Soucheiron Date: Thu, 28 Dec 2023 13:34:56 +0100 Subject: [PATCH 08/12] Remove version file --- pycfmodel/__version__.py | 3 --- 1 file changed, 3 deletions(-) delete mode 100644 pycfmodel/__version__.py diff --git a/pycfmodel/__version__.py b/pycfmodel/__version__.py deleted file mode 100644 index 361bb3b..0000000 --- a/pycfmodel/__version__.py +++ /dev/null @@ -1,3 +0,0 @@ -VERSION = (0, 21, 2) - -__version__ = ".".join(map(str, VERSION)) \ No newline at end of file From a23381886af9c2c1028314888c3ad677be736cac Mon Sep 17 00:00:00 2001 From: Jordi Soucheiron Date: Thu, 28 Dec 2023 13:41:55 +0100 Subject: [PATCH 09/12] Replace flake8 by ruff --- Makefile | 6 +++--- requirements-dev.txt | 5 +---- setup.py | 2 +- 3 files changed, 5 insertions(+), 8 deletions(-) diff --git a/Makefile b/Makefile index 8c756d0..f9b0ae6 100644 --- a/Makefile +++ b/Makefile @@ -17,7 +17,7 @@ isort-format: black-format: black . -lint: isort-lint black-lint flake8-lint +lint: isort-lint black-lint ruff-lint isort-lint: isort --check-only . @@ -25,8 +25,8 @@ isort-lint: black-lint: black --check . -flake8-lint: - flake8 . +ruff-lint: + ruff . unit: pytest -svvv tests diff --git a/requirements-dev.txt b/requirements-dev.txt index 03cf4a4..13dcb97 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -11,7 +11,6 @@ certifi==2023.11.17 click==8.1.7 coverage[toml]==7.2.7 exceptiongroup==1.2.0 -flake8==5.0.4 h11==0.14.0 httpcore==0.17.3 httpx==0.24.1 @@ -19,20 +18,18 @@ idna==3.6 importlib-metadata==4.2.0 iniconfig==2.0.0 isort==5.11.5 -mccabe==0.7.0 mypy-extensions==1.0.0 packaging==23.2 pathspec==0.11.2 pip-tools==6.14.0 platformdirs==4.0.0 pluggy==1.2.0 -pycodestyle==2.9.1 pydantic==1.9.0 -pyflakes==2.5.0 pyproject-hooks==1.0.0 pytest==7.4.3 pytest-cov==4.1.0 pytest-repeat==0.9.3 +ruff==0.1.9 sniffio==1.3.0 tomli==2.0.1 typed-ast==1.5.5 diff --git a/setup.py b/setup.py index bc031b8..e87a0d3 100644 --- a/setup.py +++ b/setup.py @@ -9,13 +9,13 @@ dev_requires = [ "black>=22.1.0", - "flake8>=3.8.3", "httpx>=0.14.2", "isort>=5.4.2", "pip-tools>=2.0.2", "pytest>=6.0.1", "pytest-cov>=2.10.1", "pytest-repeat==0.9.3", + "ruff", ] docs_requires = [ From a6a80a00c94ea7f3740f199b74bdc8ddab963398 Mon Sep 17 00:00:00 2001 From: Jordi Soucheiron Date: Thu, 28 Dec 2023 13:47:47 +0100 Subject: [PATCH 10/12] Fix tests --- Makefile | 2 +- pycfmodel/model/cf_model.py | 11 +- .../model/resources/iam_managed_policy.py | 3 +- pycfmodel/model/resources/iam_policy.py | 3 +- pycfmodel/model/resources/iam_role.py | 4 +- .../properties/statement_condition.py | 15 +- pycfmodel/model/resources/properties/types.py | 8 +- pycfmodel/model/resources/resource.py | 9 +- pycfmodel/resolver.py | 42 ++- pyproject.toml | 63 ++++ .../properties/test_policy_document.py | 39 +- tests/resources/properties/test_principal.py | 7 +- tests/resources/properties/test_statement.py | 127 ++++++- .../properties/test_statement_condition.py | 355 +++++++++++++++--- .../resources/test_ec2_vpc_endpoint_policy.py | 13 +- tests/resources/test_es_domain.py | 13 +- tests/resources/test_iam_group.py | 11 +- tests/resources/test_iam_managed_policy.py | 9 +- tests/resources/test_iam_policy.py | 6 +- tests/resources/test_iam_role.py | 5 +- tests/resources/test_iam_user.py | 16 +- tests/resources/test_opensearch_domain.py | 28 +- tests/resources/test_s3_bucket.py | 17 +- tests/resources/test_security_group.py | 18 +- tests/resources/test_sns_topic_policy.py | 8 +- tests/test_action_expander.py | 26 +- tests/test_cf_model.py | 12 +- tests/test_generic.py | 10 +- tests/test_parameter.py | 18 +- tests/test_resolver.py | 189 ++++++++-- tests/test_resource.py | 159 ++++++-- tests/test_types.py | 62 ++- 32 files changed, 1119 insertions(+), 189 deletions(-) diff --git a/Makefile b/Makefile index f9b0ae6..1361422 100644 --- a/Makefile +++ b/Makefile @@ -26,7 +26,7 @@ black-lint: black --check . ruff-lint: - ruff . + ruff check . unit: pytest -svvv tests diff --git a/pycfmodel/model/cf_model.py b/pycfmodel/model/cf_model.py index 19c5951..39c0ed5 100644 --- a/pycfmodel/model/cf_model.py +++ b/pycfmodel/model/cf_model.py @@ -81,7 +81,16 @@ def resolve(self, extra_params=None) -> "CFModel": resolved_conditions = {} for key, value in conditions.items(): resolved_conditions.update( - {key: _extended_bool(resolve(value, extended_parameters, self.Mappings, resolved_conditions))} + { + key: _extended_bool( + resolve( + value, + extended_parameters, + self.Mappings, + resolved_conditions, + ) + ) + } ) resources = dict_value.pop("Resources") diff --git a/pycfmodel/model/resources/iam_managed_policy.py b/pycfmodel/model/resources/iam_managed_policy.py index b587636..7d9343b 100644 --- a/pycfmodel/model/resources/iam_managed_policy.py +++ b/pycfmodel/model/resources/iam_managed_policy.py @@ -48,6 +48,7 @@ class IAMManagedPolicy(Resource): def policy_documents(self) -> List[OptionallyNamedPolicyDocument]: return [ OptionallyNamedPolicyDocument( - name=self.Properties.ManagedPolicyName, policy_document=self.Properties.PolicyDocument + name=self.Properties.ManagedPolicyName, + policy_document=self.Properties.PolicyDocument, ) ] diff --git a/pycfmodel/model/resources/iam_policy.py b/pycfmodel/model/resources/iam_policy.py index d368ad0..52ebfc1 100644 --- a/pycfmodel/model/resources/iam_policy.py +++ b/pycfmodel/model/resources/iam_policy.py @@ -44,6 +44,7 @@ class IAMPolicy(Resource): def policy_documents(self) -> List[OptionallyNamedPolicyDocument]: return [ OptionallyNamedPolicyDocument( - name=self.Properties.PolicyName, policy_document=self.Properties.PolicyDocument + name=self.Properties.PolicyName, + policy_document=self.Properties.PolicyDocument, ) ] diff --git a/pycfmodel/model/resources/iam_role.py b/pycfmodel/model/resources/iam_role.py index 0fe9782..134bb41 100644 --- a/pycfmodel/model/resources/iam_role.py +++ b/pycfmodel/model/resources/iam_role.py @@ -57,7 +57,9 @@ def policy_documents(self) -> List[OptionallyNamedPolicyDocument]: return result @property - def assume_role_as_optionally_named_policy_document_list(self) -> List[OptionallyNamedPolicyDocument]: + def assume_role_as_optionally_named_policy_document_list( + self, + ) -> List[OptionallyNamedPolicyDocument]: return [OptionallyNamedPolicyDocument(name=None, policy_document=self.Properties.AssumeRolePolicyDocument)] @property diff --git a/pycfmodel/model/resources/properties/statement_condition.py b/pycfmodel/model/resources/properties/statement_condition.py index a67cc80..4f89bfa 100644 --- a/pycfmodel/model/resources/properties/statement_condition.py +++ b/pycfmodel/model/resources/properties/statement_condition.py @@ -45,9 +45,20 @@ def build_evaluator(function: str, arg_a: Any, arg_b: Any) -> Callable: elif function == "Null": return lambda kwargs: (kwargs.get(arg_a) is not None) is arg_b - elif function in ("StringEquals", "ArnEquals", "BinaryEquals", "NumericEquals", "DateEquals"): + elif function in ( + "StringEquals", + "ArnEquals", + "BinaryEquals", + "NumericEquals", + "DateEquals", + ): return lambda kwargs: kwargs[arg_a] == arg_b - elif function in ("StringNotEquals", "ArnNotEquals", "NumericNotEquals", "DateNotEquals"): + elif function in ( + "StringNotEquals", + "ArnNotEquals", + "NumericNotEquals", + "DateNotEquals", + ): return lambda kwargs: kwargs[arg_a] != arg_b elif function in ("NumericLessThan", "DateLessThan"): diff --git a/pycfmodel/model/resources/properties/types.py b/pycfmodel/model/resources/properties/types.py index ae95730..36cde64 100644 --- a/pycfmodel/model/resources/properties/types.py +++ b/pycfmodel/model/resources/properties/types.py @@ -9,5 +9,11 @@ from pycfmodel.model.resources.properties.tag import Tag Properties = Union[ - Policy, PolicyDocument, SecurityGroupEgressProp, SecurityGroupIngressProp, Statement, StatementCondition, Tag + Policy, + PolicyDocument, + SecurityGroupEgressProp, + SecurityGroupIngressProp, + Statement, + StatementCondition, + Tag, ] diff --git a/pycfmodel/model/resources/resource.py b/pycfmodel/model/resources/resource.py index eb69375..2cd916f 100644 --- a/pycfmodel/model/resources/resource.py +++ b/pycfmodel/model/resources/resource.py @@ -57,7 +57,8 @@ def policy_documents(self) -> List[OptionallyNamedPolicyDocument]: return policy_documents self.obtain_policy_documents( - policy_documents=policy_documents, properties=list(self.Properties.__dict__.values()) + policy_documents=policy_documents, + properties=list(self.Properties.__dict__.values()), ) return policy_documents @@ -71,7 +72,8 @@ def obtain_policy_documents(self, policy_documents: List, properties: List[Any]) elif isinstance(property_type, Policy): policy_documents.append( OptionallyNamedPolicyDocument( - name=property_type.PolicyName, policy_document=property_type.PolicyDocument + name=property_type.PolicyName, + policy_document=property_type.PolicyDocument, ) ) elif isinstance(property_type, OptionallyNamedPolicyDocument): @@ -80,7 +82,8 @@ def obtain_policy_documents(self, policy_documents: List, properties: List[Any]) self.obtain_policy_documents(policy_documents=policy_documents, properties=property_type) elif isinstance(property_type, Generic): self.obtain_policy_documents( - policy_documents=policy_documents, properties=list(property_type.__dict__.values()) + policy_documents=policy_documents, + properties=list(property_type.__dict__.values()), ) @property diff --git a/pycfmodel/resolver.py b/pycfmodel/resolver.py index 9aaad4d..e54cc42 100644 --- a/pycfmodel/resolver.py +++ b/pycfmodel/resolver.py @@ -22,7 +22,12 @@ class _BooleanModel(BaseModel): bool_value: bool -def resolve(function: ValidResolvers, params: Dict, mappings: Dict[str, Dict], conditions: Dict[str, bool]): +def resolve( + function: ValidResolvers, + params: Dict, + mappings: Dict[str, Dict], + conditions: Dict[str, bool], +): if function is None: return function @@ -147,24 +152,49 @@ def resolve_if(function_body, params: Dict, mappings: Dict[str, Dict], condition return resolve(false_section, params, mappings, conditions) -def resolve_and(function_body: List, params: Dict, mappings: Dict[str, Dict], conditions: Dict[str, bool]) -> bool: +def resolve_and( + function_body: List, + params: Dict, + mappings: Dict[str, Dict], + conditions: Dict[str, bool], +) -> bool: return all(_extended_bool(resolve(part, params, mappings, conditions)) for part in function_body) -def resolve_or(function_body: List, params: Dict, mappings: Dict[str, Dict], conditions: Dict[str, bool]) -> bool: +def resolve_or( + function_body: List, + params: Dict, + mappings: Dict[str, Dict], + conditions: Dict[str, bool], +) -> bool: return any(_extended_bool(resolve(part, params, mappings, conditions)) for part in function_body) -def resolve_not(function_body: List, params: Dict, mappings: Dict[str, Dict], conditions: Dict[str, bool]) -> bool: +def resolve_not( + function_body: List, + params: Dict, + mappings: Dict[str, Dict], + conditions: Dict[str, bool], +) -> bool: return not _extended_bool(resolve(function_body[0], params, mappings, conditions)) -def resolve_equals(function_body: List, params: Dict, mappings: Dict[str, Dict], conditions: Dict[str, bool]) -> bool: +def resolve_equals( + function_body: List, + params: Dict, + mappings: Dict[str, Dict], + conditions: Dict[str, bool], +) -> bool: part_1, part_2 = function_body return resolve(part_1, params, mappings, conditions) == resolve(part_2, params, mappings, conditions) -def resolve_base64(function_body: str, params: Dict, mappings: Dict[str, Dict], conditions: Dict[str, bool]) -> str: +def resolve_base64( + function_body: str, + params: Dict, + mappings: Dict[str, Dict], + conditions: Dict[str, bool], +) -> str: resolved_string = resolve(function_body, params, mappings, conditions) return str(b64encode(resolved_string.encode("utf-8")), "utf-8") diff --git a/pyproject.toml b/pyproject.toml index 7cc351d..2a6f27c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -8,3 +8,66 @@ include_trailing_comma = true force_grid_wrap = 0 use_parentheses = true line_length = 120 + +[tool.ruff] +# Exclude a variety of commonly ignored directories. +exclude = [ + ".bzr", + ".direnv", + ".eggs", + ".git", + ".git-rewrite", + ".hg", + ".ipynb_checkpoints", + ".mypy_cache", + ".nox", + ".pants.d", + ".pyenv", + ".pytest_cache", + ".pytype", + ".ruff_cache", + ".svn", + ".tox", + ".venv", + ".vscode", + "__pypackages__", + "_build", + "buck-out", + "build", + "dist", + "node_modules", + "site-packages", + "venv", +] + +# Same as Black. +line-length = 120 +indent-width = 4 + +# Assume Python 3.7 +target-version = "py37" + +[tool.ruff.lint] +# Enable Pyflakes (`F`) and a subset of the pycodestyle (`E`) codes by default. +select = ["E4", "E7", "E9", "F"] +ignore = [] + +# Allow fix for all enabled rules (when `--fix`) is provided. +fixable = ["ALL"] +unfixable = [] + +# Allow unused variables when underscore-prefixed. +dummy-variable-rgx = "^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$" + +[tool.ruff.format] +# Like Black, use double quotes for strings. +quote-style = "double" + +# Like Black, indent with spaces, rather than tabs. +indent-style = "space" + +# Like Black, respect magic trailing commas. +skip-magic-trailing-comma = false + +# Like Black, automatically detect the appropriate line ending. +line-ending = "auto" \ No newline at end of file diff --git a/tests/resources/properties/test_policy_document.py b/tests/resources/properties/test_policy_document.py index 38c244e..1472bbf 100644 --- a/tests/resources/properties/test_policy_document.py +++ b/tests/resources/properties/test_policy_document.py @@ -16,7 +16,10 @@ def policy_document_one_statement(): "Version": "2012-10-17", "Statement": { "Effect": "Allow", - "Principal": {"Service": ["ec2.amazonaws.com"], "AWS": "arn:aws:iam::324320755747:root"}, + "Principal": { + "Service": ["ec2.amazonaws.com"], + "AWS": "arn:aws:iam::324320755747:root", + }, "Action": ["sts:AssumeRole"], }, } @@ -31,12 +34,18 @@ def policy_document_multi_statement(): "Statement": [ { "Effect": "Allow", - "Principal": {"Service": ["ec2.amazonaws.com"], "AWS": "arn:aws:iam::324320755747:root"}, + "Principal": { + "Service": ["ec2.amazonaws.com"], + "AWS": "arn:aws:iam::324320755747:root", + }, "Action": ["sts:AssumeRole"], }, { "Effect": "Allow", - "Principal": {"Service": ["ec2.amazonaws.com"], "AWS": "arn:aws:iam::324320755747:root"}, + "Principal": { + "Service": ["ec2.amazonaws.com"], + "AWS": "arn:aws:iam::324320755747:root", + }, "Action": ["sts:AssumeRole"], }, ], @@ -47,7 +56,16 @@ def policy_document_multi_statement(): @fixture def policy_document_star_resource(): return PolicyDocument( - **{"Statement": [{"Action": ["*"], "Effect": "Allow", "Resource": "*", "Principal": {"AWS": ["156460612806"]}}]} + **{ + "Statement": [ + { + "Action": ["*"], + "Effect": "Allow", + "Resource": "*", + "Principal": {"AWS": ["156460612806"]}, + } + ] + } ) @@ -344,13 +362,20 @@ def test_policy_document_chan_check_if_the_statement_effect_is_allow_or_deny(sta assert PolicyDocument._is_statement_effect_allow(statement_effect=statement_effect) == is_allow -def test_policy_document_condition_with_source_ip(policy_document_condition_with_source_ip: PolicyDocument): +def test_policy_document_condition_with_source_ip( + policy_document_condition_with_source_ip: PolicyDocument, +): assert policy_document_condition_with_source_ip.Statement[0].Condition.IpAddress == { - "aws:SourceIp": [IPv4Network("116.202.65.160/32"), IPv4Network("116.202.68.32/27")] + "aws:SourceIp": [ + IPv4Network("116.202.65.160/32"), + IPv4Network("116.202.68.32/27"), + ] } -def test_policy_document_condition_with_source_vpce(policy_document_condition_with_source_vpce: PolicyDocument): +def test_policy_document_condition_with_source_vpce( + policy_document_condition_with_source_vpce: PolicyDocument, +): assert policy_document_condition_with_source_vpce.Statement[0].Condition.IpAddress == { "aws:SourceVpce": ["vpce-123456"] } diff --git a/tests/resources/properties/test_principal.py b/tests/resources/properties/test_principal.py index a53c473..90a576e 100644 --- a/tests/resources/properties/test_principal.py +++ b/tests/resources/properties/test_principal.py @@ -13,7 +13,12 @@ {"AWS": "arn:aws:iam::123456789012:role/role-name"}, {"AWS": "arn:aws:sts::123456789012:federated-user/user-name"}, {"AWS": ["123456789012", "555555555555"]}, - {"AWS": ["arn:aws:iam::123456789012:user/user-name-1", "arn:aws:iam::123456789012:user/user-name-2"]}, + { + "AWS": [ + "arn:aws:iam::123456789012:user/user-name-1", + "arn:aws:iam::123456789012:user/user-name-2", + ] + }, {"CanonicalUser": "79a59df900b949e55d96a1e698fbacedfd6e09d98eacf8f8d5218e7cd47ef2be"}, { "AWS": ["arn:aws:iam::123456789012:root", "999999999999"], diff --git a/tests/resources/properties/test_statement.py b/tests/resources/properties/test_statement.py index 1bee5b2..1cd99b6 100644 --- a/tests/resources/properties/test_statement.py +++ b/tests/resources/properties/test_statement.py @@ -6,19 +6,47 @@ def statement_1(): - return Statement(**{"Effect": "Allow", "Action": ["action1"], "NotAction": "action2", "Resource": ["arn"]}) + return Statement( + **{ + "Effect": "Allow", + "Action": ["action1"], + "NotAction": "action2", + "Resource": ["arn"], + } + ) def statement_2(): - return Statement(**{"Effect": "Allow", "Action": "action1", "NotAction": ["action2"], "Resource": ["arn"]}) + return Statement( + **{ + "Effect": "Allow", + "Action": "action1", + "NotAction": ["action2"], + "Resource": ["arn"], + } + ) def statement_3(): - return Statement(**{"Effect": "Allow", "Action": "action1", "Resource": ["arn1"], "NotResource": "arn2"}) + return Statement( + **{ + "Effect": "Allow", + "Action": "action1", + "Resource": ["arn1"], + "NotResource": "arn2", + } + ) def statement_4(): - return Statement(**{"Effect": "Allow", "Action": "action2", "Resource": "arn1", "NotResource": ["arn2"]}) + return Statement( + **{ + "Effect": "Allow", + "Action": "action2", + "Resource": "arn1", + "NotResource": ["arn2"], + } + ) def statement_principal_1(): @@ -30,14 +58,22 @@ def statement_principal_2(): **{ "Effect": "Allow", "Principal": { - "AWS": ["arn:aws:iam::AWS-account-ID:user/user-name-1", "arn:aws:iam::AWS-account-ID:user/UserName2"] + "AWS": [ + "arn:aws:iam::AWS-account-ID:user/user-name-1", + "arn:aws:iam::AWS-account-ID:user/UserName2", + ] }, } ) def statement_principal_3(): - return Statement(**{"Effect": "Allow", "Principal": {"Federated": "cognito-identity.amazonaws.com"}}) + return Statement( + **{ + "Effect": "Allow", + "Principal": {"Federated": "cognito-identity.amazonaws.com"}, + } + ) def statement_principal_4(): @@ -48,7 +84,10 @@ def statement_principal_5(): return Statement( **{ "Effect": "Allow", - "Principal": ["arn:aws:iam::AWS-account-ID:user/user-name-1", "arn:aws:iam::AWS-account-ID:user/UserName2"], + "Principal": [ + "arn:aws:iam::AWS-account-ID:user/user-name-1", + "arn:aws:iam::AWS-account-ID:user/UserName2", + ], } ) @@ -62,14 +101,22 @@ def statement_not_principal_2(): **{ "Effect": "Allow", "NotPrincipal": { - "AWS": ["arn:aws:iam::AWS-account-ID:user/user-name-1", "arn:aws:iam::AWS-account-ID:user/UserName2"] + "AWS": [ + "arn:aws:iam::AWS-account-ID:user/user-name-1", + "arn:aws:iam::AWS-account-ID:user/UserName2", + ] }, } ) def statement_not_principal_3(): - return Statement(**{"Effect": "Allow", "NotPrincipal": {"Federated": "cognito-identity.amazonaws.com"}}) + return Statement( + **{ + "Effect": "Allow", + "NotPrincipal": {"Federated": "cognito-identity.amazonaws.com"}, + } + ) def statement_not_principal_4(): @@ -89,7 +136,14 @@ def statement_not_principal_5(): def test_capitalize_effect(): - statement = Statement(**{"Effect": "allOw", "Action": ["action1"], "NotAction": "action2", "Resource": ["arn"]}) + statement = Statement( + **{ + "Effect": "allOw", + "Action": ["action1"], + "NotAction": "action2", + "Resource": ["arn"], + } + ) assert statement.Effect == "Allow" @@ -114,9 +168,18 @@ def test_get_action_list(statement, expected_output): @pytest.mark.parametrize( "statement, expected_output", [ - (Statement(**{"Effect": "Allow", "Action": "ec2:RunInstances", "Resource": ["arn"]}), ["ec2:RunInstances"]), - (Statement(**{"Effect": "Allow", "Action": "ec2:Run?nstances", "Resource": ["arn"]}), ["ec2:RunInstances"]), - (Statement(**{"Effect": "Allow", "Action": "ec?:RunInstances", "Resource": ["arn"]}), ["ec2:RunInstances"]), + ( + Statement(**{"Effect": "Allow", "Action": "ec2:RunInstances", "Resource": ["arn"]}), + ["ec2:RunInstances"], + ), + ( + Statement(**{"Effect": "Allow", "Action": "ec2:Run?nstances", "Resource": ["arn"]}), + ["ec2:RunInstances"], + ), + ( + Statement(**{"Effect": "Allow", "Action": "ec?:RunInstances", "Resource": ["arn"]}), + ["ec2:RunInstances"], + ), ( Statement(**{"Effect": "Allow", "Action": "ec2:Run*", "Resource": ["arn"]}), ["ec2:RunInstances", "ec2:RunScheduledInstances"], @@ -146,24 +209,36 @@ def test_get_resource_list(statement, expected_output): (statement_principal_1(), ["arn:aws:iam::123456789012:root"]), ( statement_principal_2(), - ["arn:aws:iam::AWS-account-ID:user/user-name-1", "arn:aws:iam::AWS-account-ID:user/UserName2"], + [ + "arn:aws:iam::AWS-account-ID:user/user-name-1", + "arn:aws:iam::AWS-account-ID:user/UserName2", + ], ), (statement_principal_3(), ["cognito-identity.amazonaws.com"]), (statement_principal_4(), ["arn:aws:iam::123456789012:root"]), ( statement_principal_5(), - ["arn:aws:iam::AWS-account-ID:user/user-name-1", "arn:aws:iam::AWS-account-ID:user/UserName2"], + [ + "arn:aws:iam::AWS-account-ID:user/user-name-1", + "arn:aws:iam::AWS-account-ID:user/UserName2", + ], ), (statement_not_principal_1(), ["arn:aws:iam::123456789012:root"]), ( statement_not_principal_2(), - ["arn:aws:iam::AWS-account-ID:user/user-name-1", "arn:aws:iam::AWS-account-ID:user/UserName2"], + [ + "arn:aws:iam::AWS-account-ID:user/user-name-1", + "arn:aws:iam::AWS-account-ID:user/UserName2", + ], ), (statement_not_principal_3(), ["cognito-identity.amazonaws.com"]), (statement_not_principal_4(), ["arn:aws:iam::123456789012:root"]), ( statement_not_principal_5(), - ["arn:aws:iam::AWS-account-ID:user/user-name-1", "arn:aws:iam::AWS-account-ID:user/UserName2"], + [ + "arn:aws:iam::AWS-account-ID:user/user-name-1", + "arn:aws:iam::AWS-account-ID:user/UserName2", + ], ), ], ) @@ -187,9 +262,21 @@ def test_actions_with(statement, pattern, expected_output): @pytest.mark.parametrize( "statement, pattern, expected_output", [ - (statement_principal_1(), re.compile(r"^.*123456789012.*$"), ["arn:aws:iam::123456789012:root"]), - (statement_principal_2(), re.compile(r"^.*user-name-1$"), ["arn:aws:iam::AWS-account-ID:user/user-name-1"]), - (statement_principal_3(), re.compile(r"^.*\.amazonaws\.com$"), ["cognito-identity.amazonaws.com"]), + ( + statement_principal_1(), + re.compile(r"^.*123456789012.*$"), + ["arn:aws:iam::123456789012:root"], + ), + ( + statement_principal_2(), + re.compile(r"^.*user-name-1$"), + ["arn:aws:iam::AWS-account-ID:user/user-name-1"], + ), + ( + statement_principal_3(), + re.compile(r"^.*\.amazonaws\.com$"), + ["cognito-identity.amazonaws.com"], + ), ], ) def test_principals_with(statement, pattern, expected_output): diff --git a/tests/resources/properties/test_statement_condition.py b/tests/resources/properties/test_statement_condition.py index 4bba591..d9d6af4 100644 --- a/tests/resources/properties/test_statement_condition.py +++ b/tests/resources/properties/test_statement_condition.py @@ -79,7 +79,8 @@ def test_statement_condition_remove_colon(): "ForAnyValue:ArnEquals": {"patata_2": ["test_2", "test_3"]}, } ) == StatementCondition( - ForAllValuesArnEqualsIfExists={"patata_1": "test_1"}, ForAnyValueArnEquals={"patata_2": ["test_2", "test_3"]} + ForAllValuesArnEqualsIfExists={"patata_1": "test_1"}, + ForAnyValueArnEquals={"patata_2": ["test_2", "test_3"]}, ) @@ -107,14 +108,62 @@ def test_build_evaluator_bool(function: str, arg_a: Any, arg_b: Any, params: Dic @pytest.mark.parametrize( "function, arg_a, arg_b, params, expected_output", [ - ("IpAddress", "patata", IPv4Network("203.0.113.0/24"), {"patata": IPv4Network("203.0.113.12")}, True), - ("IpAddress", "patata", IPv4Network("203.0.113.0/24"), {"patata": IPv4Network("10.1.1.1")}, False), - ("IpAddress", "patata", IPv4Network("203.0.113.10/32"), {"patata": IPv4Network("203.0.113.10")}, True), - ("IpAddress", "patata", IPv4Network("203.0.113.10/32"), {"patata": IPv4Network("10.1.1.1")}, False), - ("IpAddress", "patata", IPv4Network("172.16.0.0/12"), {"patata": IPv4Network("203.0.113.10")}, False), - ("IpAddress", "patata", IPv4Network("172.16.0.0/12"), {"patata": IPv4Network("172.16.0.13")}, True), - ("IpAddress", "patata", IPv4Network("172.16.0.0/12"), {"patata": IPv4Network("172.16.0.0/12")}, True), - ("IpAddress", "patata", IPv4Network("172.16.0.0/12"), {"patata": IPv4Network("203.0.113.10/32")}, False), + ( + "IpAddress", + "patata", + IPv4Network("203.0.113.0/24"), + {"patata": IPv4Network("203.0.113.12")}, + True, + ), + ( + "IpAddress", + "patata", + IPv4Network("203.0.113.0/24"), + {"patata": IPv4Network("10.1.1.1")}, + False, + ), + ( + "IpAddress", + "patata", + IPv4Network("203.0.113.10/32"), + {"patata": IPv4Network("203.0.113.10")}, + True, + ), + ( + "IpAddress", + "patata", + IPv4Network("203.0.113.10/32"), + {"patata": IPv4Network("10.1.1.1")}, + False, + ), + ( + "IpAddress", + "patata", + IPv4Network("172.16.0.0/12"), + {"patata": IPv4Network("203.0.113.10")}, + False, + ), + ( + "IpAddress", + "patata", + IPv4Network("172.16.0.0/12"), + {"patata": IPv4Network("172.16.0.13")}, + True, + ), + ( + "IpAddress", + "patata", + IPv4Network("172.16.0.0/12"), + {"patata": IPv4Network("172.16.0.0/12")}, + True, + ), + ( + "IpAddress", + "patata", + IPv4Network("172.16.0.0/12"), + {"patata": IPv4Network("203.0.113.10/32")}, + False, + ), ], ) def test_build_evaluator_ip_address(function: str, arg_a: Any, arg_b: Any, params: Dict, expected_output: bool): @@ -125,13 +174,55 @@ def test_build_evaluator_ip_address(function: str, arg_a: Any, arg_b: Any, param @pytest.mark.parametrize( "function, arg_a, arg_b, params, expected_output", [ - ("NotIpAddress", "patata", IPv4Network("203.0.113.0/24"), {"patata": IPv4Network("203.0.113.12")}, False), - ("NotIpAddress", "patata", IPv4Network("203.0.113.0/24"), {"patata": IPv4Network("10.1.1.1")}, True), - ("NotIpAddress", "patata", IPv4Network("203.0.113.10/32"), {"patata": IPv4Network("203.0.113.10")}, False), - ("NotIpAddress", "patata", IPv4Network("203.0.113.10/32"), {"patata": IPv4Network("10.1.1.1")}, True), - ("NotIpAddress", "patata", IPv4Network("172.16.0.0/12"), {"patata": IPv4Network("203.0.113.10")}, True), - ("NotIpAddress", "patata", IPv4Network("172.16.0.0/12"), {"patata": IPv4Network("172.16.0.13")}, False), - ("NotIpAddress", "patata", IPv4Network("172.0.0.0/8"), {"patata": IPv4Network("172.0.0.0/8")}, False), + ( + "NotIpAddress", + "patata", + IPv4Network("203.0.113.0/24"), + {"patata": IPv4Network("203.0.113.12")}, + False, + ), + ( + "NotIpAddress", + "patata", + IPv4Network("203.0.113.0/24"), + {"patata": IPv4Network("10.1.1.1")}, + True, + ), + ( + "NotIpAddress", + "patata", + IPv4Network("203.0.113.10/32"), + {"patata": IPv4Network("203.0.113.10")}, + False, + ), + ( + "NotIpAddress", + "patata", + IPv4Network("203.0.113.10/32"), + {"patata": IPv4Network("10.1.1.1")}, + True, + ), + ( + "NotIpAddress", + "patata", + IPv4Network("172.16.0.0/12"), + {"patata": IPv4Network("203.0.113.10")}, + True, + ), + ( + "NotIpAddress", + "patata", + IPv4Network("172.16.0.0/12"), + {"patata": IPv4Network("172.16.0.13")}, + False, + ), + ( + "NotIpAddress", + "patata", + IPv4Network("172.0.0.0/8"), + {"patata": IPv4Network("172.0.0.0/8")}, + False, + ), ], ) def test_build_evaluator_not_ip_address(function: str, arg_a: Any, arg_b: Any, params: Dict, expected_output: bool): @@ -155,7 +246,10 @@ def test_build_evaluator_null(function: str, arg_a: Any, arg_b: Any, params: Dic @pytest.mark.parametrize( "function, arg_a, arg_b, params, expected_output", - [("NumericEquals", "patata", 1, {"patata": 1}, True), ("NumericEquals", "patata", 1, {"patata": 2}, False)], + [ + ("NumericEquals", "patata", 1, {"patata": 1}, True), + ("NumericEquals", "patata", 1, {"patata": 2}, False), + ], ) def test_build_evaluator_numeric_equals(function: str, arg_a: Any, arg_b: Any, params: Dict, expected_output: bool): node = build_evaluator(function, arg_a, arg_b) @@ -164,7 +258,10 @@ def test_build_evaluator_numeric_equals(function: str, arg_a: Any, arg_b: Any, p @pytest.mark.parametrize( "function, arg_a, arg_b, params, expected_output", - [("NumericNotEquals", "patata", 1, {"patata": 1}, False), ("NumericNotEquals", "patata", 1, {"patata": 2}, True)], + [ + ("NumericNotEquals", "patata", 1, {"patata": 1}, False), + ("NumericNotEquals", "patata", 1, {"patata": 2}, True), + ], ) def test_build_evaluator_numeric_not_equals(function: str, arg_a: Any, arg_b: Any, params: Dict, expected_output: bool): node = build_evaluator(function, arg_a, arg_b) @@ -232,8 +329,20 @@ def test_build_evaluator_numeric_greater_than_equals( @pytest.mark.parametrize( "function, arg_a, arg_b, params, expected_output", [ - ("DateEquals", "patata", datetime_in_the_present(), {"patata": datetime_in_the_present()}, True), - ("DateEquals", "patata", datetime_in_the_present(), {"patata": datetime_in_the_past()}, False), + ( + "DateEquals", + "patata", + datetime_in_the_present(), + {"patata": datetime_in_the_present()}, + True, + ), + ( + "DateEquals", + "patata", + datetime_in_the_present(), + {"patata": datetime_in_the_past()}, + False, + ), ], ) def test_build_evaluator_date_equals(function: str, arg_a: Any, arg_b: Any, params: Dict, expected_output: bool): @@ -244,8 +353,20 @@ def test_build_evaluator_date_equals(function: str, arg_a: Any, arg_b: Any, para @pytest.mark.parametrize( "function, arg_a, arg_b, params, expected_output", [ - ("DateNotEquals", "patata", datetime_in_the_present(), {"patata": datetime_in_the_present()}, False), - ("DateNotEquals", "patata", datetime_in_the_present(), {"patata": datetime_in_the_past()}, True), + ( + "DateNotEquals", + "patata", + datetime_in_the_present(), + {"patata": datetime_in_the_present()}, + False, + ), + ( + "DateNotEquals", + "patata", + datetime_in_the_present(), + {"patata": datetime_in_the_past()}, + True, + ), ], ) def test_build_evaluator_date_not_equals(function: str, arg_a: Any, arg_b: Any, params: Dict, expected_output: bool): @@ -256,9 +377,27 @@ def test_build_evaluator_date_not_equals(function: str, arg_a: Any, arg_b: Any, @pytest.mark.parametrize( "function, arg_a, arg_b, params, expected_output", [ - ("DateLessThan", "patata", datetime_in_the_present(), {"patata": datetime_in_the_present()}, False), - ("DateLessThan", "patata", datetime_in_the_present(), {"patata": datetime_in_the_future()}, False), - ("DateLessThan", "patata", datetime_in_the_future(), {"patata": datetime_in_the_present()}, True), + ( + "DateLessThan", + "patata", + datetime_in_the_present(), + {"patata": datetime_in_the_present()}, + False, + ), + ( + "DateLessThan", + "patata", + datetime_in_the_present(), + {"patata": datetime_in_the_future()}, + False, + ), + ( + "DateLessThan", + "patata", + datetime_in_the_future(), + {"patata": datetime_in_the_present()}, + True, + ), ], ) def test_build_evaluator_date_less_than(function: str, arg_a: Any, arg_b: Any, params: Dict, expected_output: bool): @@ -269,9 +408,27 @@ def test_build_evaluator_date_less_than(function: str, arg_a: Any, arg_b: Any, p @pytest.mark.parametrize( "function, arg_a, arg_b, params, expected_output", [ - ("DateLessThanEquals", "patata", datetime_in_the_present(), {"patata": datetime_in_the_present()}, True), - ("DateLessThanEquals", "patata", datetime_in_the_present(), {"patata": datetime_in_the_future()}, False), - ("DateLessThanEquals", "patata", datetime_in_the_future(), {"patata": datetime_in_the_present()}, True), + ( + "DateLessThanEquals", + "patata", + datetime_in_the_present(), + {"patata": datetime_in_the_present()}, + True, + ), + ( + "DateLessThanEquals", + "patata", + datetime_in_the_present(), + {"patata": datetime_in_the_future()}, + False, + ), + ( + "DateLessThanEquals", + "patata", + datetime_in_the_future(), + {"patata": datetime_in_the_present()}, + True, + ), ], ) def test_build_evaluator_date_less_than_equals( @@ -284,9 +441,27 @@ def test_build_evaluator_date_less_than_equals( @pytest.mark.parametrize( "function, arg_a, arg_b, params, expected_output", [ - ("DateGreaterThan", "patata", datetime_in_the_present(), {"patata": datetime_in_the_present()}, False), - ("DateGreaterThan", "patata", datetime_in_the_future(), {"patata": datetime_in_the_present()}, False), - ("DateGreaterThan", "patata", datetime_in_the_present(), {"patata": datetime_in_the_future()}, True), + ( + "DateGreaterThan", + "patata", + datetime_in_the_present(), + {"patata": datetime_in_the_present()}, + False, + ), + ( + "DateGreaterThan", + "patata", + datetime_in_the_future(), + {"patata": datetime_in_the_present()}, + False, + ), + ( + "DateGreaterThan", + "patata", + datetime_in_the_present(), + {"patata": datetime_in_the_future()}, + True, + ), ], ) def test_build_evaluator_date_greater_than(function: str, arg_a: Any, arg_b: Any, params: Dict, expected_output: bool): @@ -297,9 +472,27 @@ def test_build_evaluator_date_greater_than(function: str, arg_a: Any, arg_b: Any @pytest.mark.parametrize( "function, arg_a, arg_b, params, expected_output", [ - ("DateGreaterThanEquals", "patata", datetime_in_the_present(), {"patata": datetime_in_the_present()}, True), - ("DateGreaterThanEquals", "patata", datetime_in_the_future(), {"patata": datetime_in_the_present()}, False), - ("DateGreaterThanEquals", "patata", datetime_in_the_present(), {"patata": datetime_in_the_future()}, True), + ( + "DateGreaterThanEquals", + "patata", + datetime_in_the_present(), + {"patata": datetime_in_the_present()}, + True, + ), + ( + "DateGreaterThanEquals", + "patata", + datetime_in_the_future(), + {"patata": datetime_in_the_present()}, + False, + ), + ( + "DateGreaterThanEquals", + "patata", + datetime_in_the_present(), + {"patata": datetime_in_the_future()}, + True, + ), ], ) def test_build_evaluator_date_greater_than_equals( @@ -504,8 +697,20 @@ def test_build_evaluator_arn_not_like(function: str, arg_a: Any, arg_b: Any, par @pytest.mark.parametrize( "function, arg_a, arg_b, params, expected_output", [ - ("BinaryEquals", "patata", bytearray("AaAaA", "utf-8"), {"patata": bytearray("AaAaA", "utf-8")}, True), - ("BinaryEquals", "patata", bytearray("AAAAA", "utf-8"), {"patata": bytearray("aaaaa", "utf-8")}, False), + ( + "BinaryEquals", + "patata", + bytearray("AaAaA", "utf-8"), + {"patata": bytearray("AaAaA", "utf-8")}, + True, + ), + ( + "BinaryEquals", + "patata", + bytearray("AAAAA", "utf-8"), + {"patata": bytearray("aaaaa", "utf-8")}, + False, + ), ], ) def test_build_evaluator_binary_equals(function: str, arg_a: Any, arg_b: Any, params: Dict, expected_output: bool): @@ -523,15 +728,46 @@ def test_build_evaluator_binary_equals(function: str, arg_a: Any, arg_b: Any, pa ("StringEquals", {"patata": "2"}, {"patata": ["2"]}, False), ("StringEquals", {"patata": "2"}, {"patata": ["2", "1"]}, False), ("StringEquals", {"patata": ["1", "3"]}, {"patata": ["4"]}, False), - ("StringLike", {"patata": ["sky*"]}, {"patata": ["skyx", "skyscanner", "nope"]}, True), - ("StringLike", {"patata": ["sky*"]}, {"patata": ["nopeskyx", "nopeskyscanner", "nope"]}, False), - ("IpAddress", {"patata": [IPv4Network("203.0.113.0/24")]}, {"patata": IPv4Network("203.0.113.0/24")}, True), - ("IpAddress", {"patata": [IPv4Network("203.0.113.0/24")]}, {"patata": [IPv4Network("203.0.113.0/24")]}, True), + ( + "StringLike", + {"patata": ["sky*"]}, + {"patata": ["skyx", "skyscanner", "nope"]}, + True, + ), + ( + "StringLike", + {"patata": ["sky*"]}, + {"patata": ["nopeskyx", "nopeskyscanner", "nope"]}, + False, + ), + ( + "IpAddress", + {"patata": [IPv4Network("203.0.113.0/24")]}, + {"patata": IPv4Network("203.0.113.0/24")}, + True, + ), + ( + "IpAddress", + {"patata": [IPv4Network("203.0.113.0/24")]}, + {"patata": [IPv4Network("203.0.113.0/24")]}, + True, + ), ("IpAddress", {"patata": ["vpce-123456"]}, {"patata": ["vpce-123456"]}, False), - ("NotIpAddress", {"patata": ["vpce-123456"]}, {"patata": ["vpce-654321"]}, False), + ( + "NotIpAddress", + {"patata": ["vpce-123456"]}, + {"patata": ["vpce-654321"]}, + False, + ), ( "IpAddress", - {"patata": [IPv4Network("203.0.113.0/24"), IPv4Network("103.0.113.0/24"), IPv4Network("85.0.113.0/24")]}, + { + "patata": [ + IPv4Network("203.0.113.0/24"), + IPv4Network("103.0.113.0/24"), + IPv4Network("85.0.113.0/24"), + ] + }, {"patata": [IPv4Network("10.0.0.0/8")]}, False, ), @@ -583,9 +819,19 @@ def test_build_root_evaluator_for_all_values( ("ForAllValuesNumericEqualsIfExists", {"patata": 1}, {}, True), ("ForAllValuesNumericEqualsIfExists", {"patata": [1, 2, 3]}, {}, True), ("ForAllValuesNumericEqualsIfExists", {"patata": 1}, {"patata": 2}, False), - ("ForAllValuesNumericEqualsIfExists", {"patata": [1, 2, 3]}, {"patata": 4}, False), + ( + "ForAllValuesNumericEqualsIfExists", + {"patata": [1, 2, 3]}, + {"patata": 4}, + False, + ), ("ForAllValuesNumericEqualsIfExists", {"patata": 1}, {"patata": [2, 3]}, False), - ("ForAllValuesNumericEqualsIfExists", {"patata": [1, 2, 3]}, {"patata": [1, 4]}, False), + ( + "ForAllValuesNumericEqualsIfExists", + {"patata": [1, 2, 3]}, + {"patata": [1, 4]}, + False, + ), ], ) def test_build_root_evaluator_for_all_values_if_exists( @@ -601,7 +847,12 @@ def test_build_root_evaluator_for_all_values_if_exists( ("ForAnyValueNumericEquals", {"patata": 1}, {"patata": 1}, True), ("ForAnyValueNumericEquals", {"patata": [1, 2, 3]}, {"patata": 1}, True), ("ForAnyValueNumericEquals", {"patata": 1}, {"patata": [1, 2]}, True), - ("ForAnyValueNumericEquals", {"patata": [1, 2, 3]}, {"patata": [1, 2, 5, 6]}, True), + ( + "ForAnyValueNumericEquals", + {"patata": [1, 2, 3]}, + {"patata": [1, 2, 5, 6]}, + True, + ), ("ForAnyValueNumericEquals", {"patata": 1}, {"patata": 2}, False), ("ForAnyValueNumericEquals", {"patata": [1, 2, 3]}, {"patata": 4}, False), ("ForAnyValueNumericEquals", {"patata": 1}, {"patata": [2, 3]}, False), @@ -621,9 +872,19 @@ def test_build_root_evaluator_for_any_value( ("ForAnyValueNumericEqualsIfExists", {"patata": 1}, {}, True), ("ForAnyValueNumericEqualsIfExists", {"patata": [1, 2, 3]}, {}, True), ("ForAnyValueNumericEqualsIfExists", {"patata": 1}, {"patata": 2}, False), - ("ForAnyValueNumericEqualsIfExists", {"patata": [1, 2, 3]}, {"patata": 4}, False), + ( + "ForAnyValueNumericEqualsIfExists", + {"patata": [1, 2, 3]}, + {"patata": 4}, + False, + ), ("ForAnyValueNumericEqualsIfExists", {"patata": 1}, {"patata": [2, 3]}, False), - ("ForAnyValueNumericEqualsIfExists", {"patata": [1, 2, 3]}, {"patata": [4, 5, 6]}, False), + ( + "ForAnyValueNumericEqualsIfExists", + {"patata": [1, 2, 3]}, + {"patata": [4, 5, 6]}, + False, + ), ], ) def test_build_root_evaluator_for_any_value_if_exists( diff --git a/tests/resources/test_ec2_vpc_endpoint_policy.py b/tests/resources/test_ec2_vpc_endpoint_policy.py index eabb994..5848b80 100644 --- a/tests/resources/test_ec2_vpc_endpoint_policy.py +++ b/tests/resources/test_ec2_vpc_endpoint_policy.py @@ -12,7 +12,14 @@ def ec2_vpc_endpoint_policy(): "Properties": { "PolicyDocument": { "Version": "2012-10-17", - "Statement": [{"Effect": "Allow", "Principal": "*", "Action": ["s3:GetObject"], "Resource": "*"}], + "Statement": [ + { + "Effect": "Allow", + "Principal": "*", + "Action": ["s3:GetObject"], + "Resource": "*", + } + ], }, "RouteTableIds": [{"Ref": "routetableA"}, {"Ref": "routetableB"}], "ServiceName": "com.amazonaws.eu-west-1.s3", @@ -57,5 +64,7 @@ def test_ec2_vpc_endpoint_policy_documents(ec2_vpc_endpoint_policy): ] -def test_ec2_vpc_endpoint_policy_documents_no_document(ec2_vpc_endpoint_policy_no_policy_document): +def test_ec2_vpc_endpoint_policy_documents_no_document( + ec2_vpc_endpoint_policy_no_policy_document, +): assert ec2_vpc_endpoint_policy_no_policy_document.policy_documents == [] diff --git a/tests/resources/test_es_domain.py b/tests/resources/test_es_domain.py index fca444d..6f2f97e 100644 --- a/tests/resources/test_es_domain.py +++ b/tests/resources/test_es_domain.py @@ -28,7 +28,12 @@ def valid_es_domain_from_aws_documentation_examples(): }, "AdvancedOptions": {"rest.action.multi.allow_explicit_index": True}, "DomainName": "test", - "EBSOptions": {"EBSEnabled": True, "Iops": "0", "VolumeSize": "20", "VolumeType": "gp2"}, + "EBSOptions": { + "EBSEnabled": True, + "Iops": "0", + "VolumeSize": "20", + "VolumeType": "gp2", + }, "ElasticsearchClusterConfig": { "DedicatedMasterEnabled": True, "InstanceCount": "2", @@ -166,7 +171,11 @@ def test_raise_error_if_invalid_fields_in_resource(): ESDomain(**{"Type": "AWS::Elasticsearch::Domain", "Properties": {"DomainName": []}}) assert exc_info.value.errors() == [ - {"loc": ("Properties", "DomainName"), "msg": "str type expected", "type": "type_error.str"}, + { + "loc": ("Properties", "DomainName"), + "msg": "str type expected", + "type": "type_error.str", + }, { "loc": ("Properties", "DomainName", "__root__"), "msg": "FunctionDict should only have 1 key and be a function", diff --git a/tests/resources/test_iam_group.py b/tests/resources/test_iam_group.py index ebcdd9f..c76b831 100644 --- a/tests/resources/test_iam_group.py +++ b/tests/resources/test_iam_group.py @@ -20,7 +20,11 @@ def iam_group(): "Statement": [ { "Effect": "Allow", - "Action": ["lambda:AddPermission", "ssm:SendCommand", "kms:Decrypt"], + "Action": [ + "lambda:AddPermission", + "ssm:SendCommand", + "kms:Decrypt", + ], "Resource": "*", } ], @@ -40,5 +44,8 @@ def test_policies(iam_group): def test_iamgroup_policy_documents(iam_group): assert iam_group.policy_documents == [ - OptionallyNamedPolicyDocument(name="BadPolicy", policy_document=iam_group.Properties.Policies[0].PolicyDocument) + OptionallyNamedPolicyDocument( + name="BadPolicy", + policy_document=iam_group.Properties.Policies[0].PolicyDocument, + ) ] diff --git a/tests/resources/test_iam_managed_policy.py b/tests/resources/test_iam_managed_policy.py index 9a9f8be..220a96b 100644 --- a/tests/resources/test_iam_managed_policy.py +++ b/tests/resources/test_iam_managed_policy.py @@ -17,7 +17,11 @@ def iam_managed_policy(): "Statement": [ { "Effect": "Allow", - "Action": ["lambda:AddPermission", "ssm:SendCommand", "kms:Decrypt"], + "Action": [ + "lambda:AddPermission", + "ssm:SendCommand", + "kms:Decrypt", + ], "Resource": "*", } ], @@ -34,6 +38,7 @@ def test_policies(iam_managed_policy): def test_iam_managedpolicy_policy_documents(iam_managed_policy): assert iam_managed_policy.policy_documents == [ OptionallyNamedPolicyDocument( - name="ManagedPolicy", policy_document=iam_managed_policy.Properties.PolicyDocument + name="ManagedPolicy", + policy_document=iam_managed_policy.Properties.PolicyDocument, ) ] diff --git a/tests/resources/test_iam_policy.py b/tests/resources/test_iam_policy.py index 256d0ec..0bd0959 100644 --- a/tests/resources/test_iam_policy.py +++ b/tests/resources/test_iam_policy.py @@ -16,7 +16,11 @@ def iam_policy(): "Statement": [ { "Effect": "Allow", - "Action": ["lambda:AddPermission", "ssm:SendCommand", "kms:Decrypt"], + "Action": [ + "lambda:AddPermission", + "ssm:SendCommand", + "kms:Decrypt", + ], "Resource": "*", } ], diff --git a/tests/resources/test_iam_role.py b/tests/resources/test_iam_role.py index c987ec9..e0fc348 100644 --- a/tests/resources/test_iam_role.py +++ b/tests/resources/test_iam_role.py @@ -15,7 +15,10 @@ def iam_role(): "Version": "2012-10-17", "Statement": { "Effect": "Allow", - "Principal": {"Service": ["ec2.amazonaws.com"], "AWS": "arn:aws:iam::111111111111:root"}, + "Principal": { + "Service": ["ec2.amazonaws.com"], + "AWS": "arn:aws:iam::111111111111:root", + }, "Action": ["sts:AssumeRole"], "Condition": { "StringLike": { diff --git a/tests/resources/test_iam_user.py b/tests/resources/test_iam_user.py index d3d2e70..9d465c5 100644 --- a/tests/resources/test_iam_user.py +++ b/tests/resources/test_iam_user.py @@ -12,7 +12,10 @@ def iam_user(): "Properties": { "Path": "/", "UserName": "TestUser", - "LoginProfile": {"Password": "ThisMyBestP@ssword", "PasswordResetRequired": False}, + "LoginProfile": { + "Password": "ThisMyBestP@ssword", + "PasswordResetRequired": False, + }, "Policies": [ { "PolicyName": "BadPolicy", @@ -21,7 +24,11 @@ def iam_user(): "Statement": [ { "Effect": "Allow", - "Action": ["lambda:AddPermission", "ssm:SendCommand", "kms:Decrypt"], + "Action": [ + "lambda:AddPermission", + "ssm:SendCommand", + "kms:Decrypt", + ], "Resource": "*", } ], @@ -40,7 +47,10 @@ def test_policies(iam_user): def test_iam_role_policy_documents(iam_user): assert iam_user.policy_documents == [ - OptionallyNamedPolicyDocument(name="BadPolicy", policy_document=iam_user.Properties.Policies[0].PolicyDocument) + OptionallyNamedPolicyDocument( + name="BadPolicy", + policy_document=iam_user.Properties.Policies[0].PolicyDocument, + ) ] diff --git a/tests/resources/test_opensearch_domain.py b/tests/resources/test_opensearch_domain.py index 6ec61d5..9c6a5c5 100644 --- a/tests/resources/test_opensearch_domain.py +++ b/tests/resources/test_opensearch_domain.py @@ -42,7 +42,12 @@ def valid_opensearch_domain_from_aws_documentation_examples(): "DedicatedMasterCount": "3", }, "DomainName": "test", - "EBSOptions": {"EBSEnabled": True, "Iops": "0", "VolumeSize": "20", "VolumeType": "gp2"}, + "EBSOptions": { + "EBSEnabled": True, + "Iops": "0", + "VolumeSize": "20", + "VolumeType": "gp2", + }, "EngineVersion": "OpenSearch_1.0", "LogPublishingOptions": { "ES_APPLICATION_LOGS": { @@ -84,7 +89,9 @@ def valid_opensearch_domain_with_access_policies(): ) -def test_valid_empty_opensearch_domain_resource_can_be_built(valid_empty_opensearch_domain): +def test_valid_empty_opensearch_domain_resource_can_be_built( + valid_empty_opensearch_domain, +): assert valid_empty_opensearch_domain.Type == "AWS::OpenSearchService::Domain" assert valid_empty_opensearch_domain.Properties.AccessPolicies is None assert valid_empty_opensearch_domain.Properties.AdvancedOptions is None @@ -196,10 +203,19 @@ def test_valid_opensearch_domain_from_aws_documentation_examples_resource_can_be def test_raise_error_if_invalid_fields_in_resource(): with pytest.raises(ValidationError) as exc_info: - OpenSearchDomain(**{"Type": "AWS::OpenSearchService::Domain", "Properties": {"DomainName": []}}) + OpenSearchDomain( + **{ + "Type": "AWS::OpenSearchService::Domain", + "Properties": {"DomainName": []}, + } + ) assert exc_info.value.errors() == [ - {"loc": ("Properties", "DomainName"), "msg": "str type expected", "type": "type_error.str"}, + { + "loc": ("Properties", "DomainName"), + "msg": "str type expected", + "type": "type_error.str", + }, { "loc": ("Properties", "DomainName", "__root__"), "msg": "FunctionDict should only have 1 key and be a function", @@ -213,7 +229,9 @@ def test_raise_error_if_invalid_fields_in_resource(): ] -def test_can_obtain_policy_documents_from_inherited_method(valid_opensearch_domain_with_access_policies): +def test_can_obtain_policy_documents_from_inherited_method( + valid_opensearch_domain_with_access_policies, +): assert len(valid_opensearch_domain_with_access_policies.policy_documents) == 1 assert valid_opensearch_domain_with_access_policies.policy_documents == [ OptionallyNamedPolicyDocument( diff --git a/tests/resources/test_s3_bucket.py b/tests/resources/test_s3_bucket.py index 38ae563..0f416cc 100644 --- a/tests/resources/test_s3_bucket.py +++ b/tests/resources/test_s3_bucket.py @@ -20,7 +20,10 @@ def valid_s3_bucket(): "Id": "MyRule1", "Status": "Enabled", "Prefix": "MyPrefix", - "Destination": {"Bucket": "arn:aws:s3:::my-replication-bucket", "StorageClass": "STANDARD"}, + "Destination": { + "Bucket": "arn:aws:s3:::my-replication-bucket", + "StorageClass": "STANDARD", + }, }, { "Status": "Enabled", @@ -46,7 +49,11 @@ def test_extra_fields_not_allowed_s3_bucket(): S3Bucket( **{ "Type": "AWS::S3::Bucket", - "Properties": {"AccelerateConfiguration": "None", "AnalyticsConfigurations": {"a": "b"}, "foo": "bar"}, + "Properties": { + "AccelerateConfiguration": "None", + "AnalyticsConfigurations": {"a": "b"}, + "foo": "bar", + }, } ) @@ -71,7 +78,11 @@ def test_extra_fields_not_allowed_s3_bucket(): "msg": "FunctionDict should only have 1 key and be a function", "type": "value_error", }, - {"loc": ("Properties", "foo"), "msg": "extra fields not permitted", "type": "value_error.extra"}, + { + "loc": ("Properties", "foo"), + "msg": "extra fields not permitted", + "type": "value_error.extra", + }, { "loc": ("Properties", "__root__"), "msg": "FunctionDict should only have 1 key and be a function", diff --git a/tests/resources/test_security_group.py b/tests/resources/test_security_group.py index f0adf1d..614defa 100644 --- a/tests/resources/test_security_group.py +++ b/tests/resources/test_security_group.py @@ -10,8 +10,22 @@ def security_group(): "Type": "AWS::EC2::SecurityGroup", "Properties": { "GroupDescription": "some_group_desc", - "SecurityGroupIngress": [{"CidrIp": "10.1.2.3/32", "FromPort": 34, "ToPort": 36, "IpProtocol": "tcp"}], - "SecurityGroupEgress": [{"CidrIp": "10.1.2.3/32", "FromPort": 34, "ToPort": 36, "IpProtocol": "tcp"}], + "SecurityGroupIngress": [ + { + "CidrIp": "10.1.2.3/32", + "FromPort": 34, + "ToPort": 36, + "IpProtocol": "tcp", + } + ], + "SecurityGroupEgress": [ + { + "CidrIp": "10.1.2.3/32", + "FromPort": 34, + "ToPort": 36, + "IpProtocol": "tcp", + } + ], "Tags": [{"Key": "a", "Value": "b"}], "VpcId": "vpc-9f8e9dfa", }, diff --git a/tests/resources/test_sns_topic_policy.py b/tests/resources/test_sns_topic_policy.py index a3372d5..4fbf146 100644 --- a/tests/resources/test_sns_topic_policy.py +++ b/tests/resources/test_sns_topic_policy.py @@ -15,7 +15,13 @@ def sns_topic_policy(): "Id": "MyTopicPolicy", "Version": "2012-10-17", "Statement": [ - {"Sid": "Stmt1", "Effect": "Allow", "Principal": "*", "Action": "sns:Publish", "Resource": "*"}, + { + "Sid": "Stmt1", + "Effect": "Allow", + "Principal": "*", + "Action": "sns:Publish", + "Resource": "*", + }, { "Sid": "Stmt2", "Effect": "Allow", diff --git a/tests/test_action_expander.py b/tests/test_action_expander.py index 70e1873..6313848 100644 --- a/tests/test_action_expander.py +++ b/tests/test_action_expander.py @@ -68,7 +68,13 @@ def test_expand_not_actions(action, reverse_expected_output): def test_expand_actions_scenario_1(): template = { "AWSTemplateFormatVersion": "2010-09-09", - "Parameters": {"StarParameter": {"Type": "String", "Default": "*", "Description": "Star Param"}}, + "Parameters": { + "StarParameter": { + "Type": "String", + "Default": "*", + "Description": "Star Param", + } + }, "Resources": { "rootRole": { "Type": "AWS::IAM::Role", @@ -114,7 +120,12 @@ def test_resolve_scenario_2(): template = { "AWSTemplateFormatVersion": "2010-09-09", "Description": "IAM role for Lambda", - "Parameters": {"LambdaFunctionName": {"Description": "Name of the lambda function", "Type": "String"}}, + "Parameters": { + "LambdaFunctionName": { + "Description": "Name of the lambda function", + "Type": "String", + } + }, "Resources": { "lambdaRole": { "Properties": { @@ -153,7 +164,10 @@ def test_resolve_scenario_2(): "PolicyDocument": { "Statement": [ { - "Action": ["xray:PutTraceSegments", "xray:PutTelemetryRecords"], + "Action": [ + "xray:PutTraceSegments", + "xray:PutTelemetryRecords", + ], "Effect": "Allow", "Resource": ["*"], } @@ -166,7 +180,11 @@ def test_resolve_scenario_2(): "PolicyDocument": { "Statement": [ { - "Action": ["logs:CreateLogGroup", "logs:CreateLogStream", "logs:PutLogEvents"], + "Action": [ + "logs:CreateLogGroup", + "logs:CreateLogStream", + "logs:PutLogEvents", + ], "Effect": "Allow", "Resource": ["*"], } diff --git a/tests/test_cf_model.py b/tests/test_cf_model.py index acccc85..4e05f6c 100644 --- a/tests/test_cf_model.py +++ b/tests/test_cf_model.py @@ -34,12 +34,20 @@ def test_resources_filtered_by_type(): model = CFModel(Resources={**generic_resource, **user}) assert model.resources_filtered_by_type(("Resource type",)) == generic_resource - assert model.resources_filtered_by_type(("Resource type", IAMUser)) == {**generic_resource, **user} + assert model.resources_filtered_by_type(("Resource type", IAMUser)) == { + **generic_resource, + **user, + } assert model.resources_filtered_by_type((IAMUser,)) == user def test_complex_metadata_schema(): - metadata = {"Foo": "a_string_value", "Bar": 1, "Fizz": ["a", "list"], "Buzz": {"another": "dict"}} + metadata = { + "Foo": "a_string_value", + "Bar": 1, + "Fizz": ["a", "list"], + "Buzz": {"another": "dict"}, + } model = CFModel(Metadata=metadata) assert model.Metadata == metadata diff --git a/tests/test_generic.py b/tests/test_generic.py index 70184ff..70242e7 100644 --- a/tests/test_generic.py +++ b/tests/test_generic.py @@ -48,8 +48,14 @@ def test_recursive(): ("2001:db00::0/120", IPv6Network("2001:db00::0/120")), (["2001:db00::0/120"], [IPv6Network("2001:db00::0/120")]), # ResolvableArnOrList - ("arn:aws:iam::123456789012:user/test-user", "arn:aws:iam::123456789012:user/test-user"), - (["arn:aws:iam::123456789012:user/test-user"], ["arn:aws:iam::123456789012:user/test-user"]), + ( + "arn:aws:iam::123456789012:user/test-user", + "arn:aws:iam::123456789012:user/test-user", + ), + ( + ["arn:aws:iam::123456789012:user/test-user"], + ["arn:aws:iam::123456789012:user/test-user"], + ), # ResolvableStrOrList ("potato", "potato"), (["potato"], ["potato"]), diff --git a/tests/test_parameter.py b/tests/test_parameter.py index 4ebfec3..3b0e19d 100644 --- a/tests/test_parameter.py +++ b/tests/test_parameter.py @@ -6,7 +6,11 @@ @pytest.mark.parametrize( "param, provided_value, expected", [ - ({"Type": "String", "NoEcho": True, "Default": "A"}, None, Parameter.NO_ECHO_WITH_DEFAULT), + ( + {"Type": "String", "NoEcho": True, "Default": "A"}, + None, + Parameter.NO_ECHO_WITH_DEFAULT, + ), ({"Type": "String", "NoEcho": True}, None, Parameter.NO_ECHO_NO_DEFAULT), ({"Type": "String", "NoEcho": False, "Default": "A"}, None, "A"), ({"Type": "String", "NoEcho": False}, None, None), @@ -16,8 +20,16 @@ ({"Type": "List", "Default": "1,2,3"}, None, ["1", "2", "3"]), ({"Type": "CommaDelimitedList", "Default": "a,b,c"}, None, ["a", "b", "c"]), # Tests with provided value - ({"Type": "String", "NoEcho": True, "Default": "A"}, "SuperSecret", Parameter.NO_ECHO_WITH_VALUE), - ({"Type": "String", "NoEcho": True}, "SuperSecret", Parameter.NO_ECHO_WITH_VALUE), + ( + {"Type": "String", "NoEcho": True, "Default": "A"}, + "SuperSecret", + Parameter.NO_ECHO_WITH_VALUE, + ), + ( + {"Type": "String", "NoEcho": True}, + "SuperSecret", + Parameter.NO_ECHO_WITH_VALUE, + ), ({"Type": "String", "NoEcho": False, "Default": "A"}, "B", "B"), ({"Type": "String", "NoEcho": False}, "B", "B"), ({"Type": "String", "Default": "abc"}, "B", "B"), diff --git a/tests/test_resolver.py b/tests/test_resolver.py index 34f4be2..ae081c4 100644 --- a/tests/test_resolver.py +++ b/tests/test_resolver.py @@ -10,7 +10,8 @@ @pytest.mark.parametrize( - "function, expected_output", [({"Ref": "abc"}, "ABC"), ({"Ref": "potato"}, "UNDEFINED_PARAM_potato")] + "function, expected_output", + [({"Ref": "abc"}, "ABC"), ({"Ref": "potato"}, "UNDEFINED_PARAM_potato")], ) def test_ref(function, expected_output): parameters = {"abc": "ABC"} @@ -22,7 +23,10 @@ def test_ref(function, expected_output): @pytest.mark.parametrize( "function, expected_output", - [({"Fn::ImportValue": "abc"}, "ABC"), ({"Fn::ImportValue": "potato"}, "UNDEFINED_PARAM_potato")], + [ + ({"Fn::ImportValue": "abc"}, "ABC"), + ({"Fn::ImportValue": "potato"}, "UNDEFINED_PARAM_potato"), + ], ) def test_import_value(function, expected_output): parameters = {"abc": "ABC"} @@ -38,7 +42,12 @@ def test_import_value(function, expected_output): ({"Fn::Join": ["", []]}, ""), ({"Fn::Join": ["", ["aws"]]}, "aws"), ( - {"Fn::Join": ["", ["arn:", "aws", ":s3:::elasticbeanstalk-*-", "1234567890"]]}, + { + "Fn::Join": [ + "", + ["arn:", "aws", ":s3:::elasticbeanstalk-*-", "1234567890"], + ] + }, "arn:aws:s3:::elasticbeanstalk-*-1234567890", ), ], @@ -54,9 +63,18 @@ def test_join(function, expected_output): @pytest.mark.parametrize( "function, expected_output", [ - ({"Fn::FindInMap": ["RegionMap", "eu-west-1", "HVM64"]}, "UNDEFINED_MAPPING_RegionMap_eu-west-1_HVM64"), - ({"Fn::FindInMap": ["RegionMap", "us-east-1", "HVM128"]}, "UNDEFINED_MAPPING_RegionMap_us-east-1_HVM128"), - ({"Fn::FindInMap": ["RegionMap", "us-east-1", "HVM64"]}, "ami-0ff8a91507f77f867"), + ( + {"Fn::FindInMap": ["RegionMap", "eu-west-1", "HVM64"]}, + "UNDEFINED_MAPPING_RegionMap_eu-west-1_HVM64", + ), + ( + {"Fn::FindInMap": ["RegionMap", "us-east-1", "HVM128"]}, + "UNDEFINED_MAPPING_RegionMap_us-east-1_HVM128", + ), + ( + {"Fn::FindInMap": ["RegionMap", "us-east-1", "HVM64"]}, + "ami-0ff8a91507f77f867", + ), ], ) def test_find_in_map(function, expected_output): @@ -71,7 +89,10 @@ def test_find_in_map(function, expected_output): "function, expected_output", [ ({"Fn::Sub": "www.skyscanner.net"}, "www.skyscanner.net"), - ({"Fn::Sub": ["www.${Domain}", {"Domain": "skyscanner.net"}]}, "www.skyscanner.net"), + ( + {"Fn::Sub": ["www.${Domain}", {"Domain": "skyscanner.net"}]}, + "www.skyscanner.net", + ), ({"Fn::Sub": "---${abc}---"}, "---ABC---"), ({"Fn::Sub": ["--${abc}-${def}--", {"def": "DEF"}]}, "--ABC-DEF--"), ], @@ -117,7 +138,8 @@ def test_split(function, expected_output): @pytest.mark.parametrize( - "function, expected_output", [({"Fn::If": ["A", "a", "b"]}, "a"), ({"Fn::If": ["B", "a", "b"]}, "b")] + "function, expected_output", + [({"Fn::If": ["A", "a", "b"]}, "a"), ({"Fn::If": ["B", "a", "b"]}, "b")], ) def test_if(function, expected_output): parameters = {} @@ -173,7 +195,10 @@ def test_or(function, expected_output): assert resolve(function, parameters, mappings, conditions) == expected_output -@pytest.mark.parametrize("function, expected_output", [({"Fn::Not": [True]}, False), ({"Fn::Not": [False]}, True)]) +@pytest.mark.parametrize( + "function, expected_output", + [({"Fn::Not": [True]}, False), ({"Fn::Not": [False]}, True)], +) def test_not(function, expected_output): parameters = {} mappings = {} @@ -222,7 +247,8 @@ def test_base64(function, expected_output): @pytest.mark.parametrize( - "function, expected_output", [({"Fn::GetAtt": ["logicalNameOfResource", "attributeName"]}, "GETATT")] + "function, expected_output", + [({"Fn::GetAtt": ["logicalNameOfResource", "attributeName"]}, "GETATT")], ) def test_get_attr(function, expected_output): parameters = {} @@ -260,8 +286,25 @@ def test_condition(function, expected_output): (1, ["HasAtLeast1Tags"]), (2, ["HasAtLeast1Tags", "HasAtLeast2Tags"]), (3, ["HasAtLeast1Tags", "HasAtLeast2Tags", "HasAtLeast3Tags"]), - (4, ["HasAtLeast1Tags", "HasAtLeast2Tags", "HasAtLeast3Tags", "HasAtLeast4Tags"]), - (5, ["HasAtLeast1Tags", "HasAtLeast2Tags", "HasAtLeast3Tags", "HasAtLeast4Tags", "HasAtLeast5Tags"]), + ( + 4, + [ + "HasAtLeast1Tags", + "HasAtLeast2Tags", + "HasAtLeast3Tags", + "HasAtLeast4Tags", + ], + ), + ( + 5, + [ + "HasAtLeast1Tags", + "HasAtLeast2Tags", + "HasAtLeast3Tags", + "HasAtLeast4Tags", + "HasAtLeast5Tags", + ], + ), ( 6, [ @@ -338,31 +381,58 @@ def test_resolve_recursive_conditions(num_custom_tags, expected): "Conditions": { "HasAtLeast10Tags": {"Fn::Equals": [{"Ref": "NumCustomTags"}, 10]}, # this is the condition stopper "HasAtLeast9Tags": { - "Fn::Or": [{"Fn::Equals": [{"Ref": "NumCustomTags"}, 9]}, {"Condition": "HasAtLeast10Tags"}] + "Fn::Or": [ + {"Fn::Equals": [{"Ref": "NumCustomTags"}, 9]}, + {"Condition": "HasAtLeast10Tags"}, + ] }, "HasAtLeast8Tags": { - "Fn::Or": [{"Fn::Equals": [{"Ref": "NumCustomTags"}, 8]}, {"Condition": "HasAtLeast9Tags"}] + "Fn::Or": [ + {"Fn::Equals": [{"Ref": "NumCustomTags"}, 8]}, + {"Condition": "HasAtLeast9Tags"}, + ] }, "HasAtLeast7Tags": { - "Fn::Or": [{"Fn::Equals": [{"Ref": "NumCustomTags"}, 7]}, {"Condition": "HasAtLeast8Tags"}] + "Fn::Or": [ + {"Fn::Equals": [{"Ref": "NumCustomTags"}, 7]}, + {"Condition": "HasAtLeast8Tags"}, + ] }, "HasAtLeast6Tags": { - "Fn::Or": [{"Fn::Equals": [{"Ref": "NumCustomTags"}, 6]}, {"Condition": "HasAtLeast7Tags"}] + "Fn::Or": [ + {"Fn::Equals": [{"Ref": "NumCustomTags"}, 6]}, + {"Condition": "HasAtLeast7Tags"}, + ] }, "HasAtLeast5Tags": { - "Fn::Or": [{"Fn::Equals": [{"Ref": "NumCustomTags"}, 5]}, {"Condition": "HasAtLeast6Tags"}] + "Fn::Or": [ + {"Fn::Equals": [{"Ref": "NumCustomTags"}, 5]}, + {"Condition": "HasAtLeast6Tags"}, + ] }, "HasAtLeast4Tags": { - "Fn::Or": [{"Fn::Equals": [{"Ref": "NumCustomTags"}, 4]}, {"Condition": "HasAtLeast5Tags"}] + "Fn::Or": [ + {"Fn::Equals": [{"Ref": "NumCustomTags"}, 4]}, + {"Condition": "HasAtLeast5Tags"}, + ] }, "HasAtLeast3Tags": { - "Fn::Or": [{"Fn::Equals": [{"Ref": "NumCustomTags"}, 3]}, {"Condition": "HasAtLeast4Tags"}] + "Fn::Or": [ + {"Fn::Equals": [{"Ref": "NumCustomTags"}, 3]}, + {"Condition": "HasAtLeast4Tags"}, + ] }, "HasAtLeast2Tags": { - "Fn::Or": [{"Fn::Equals": [{"Ref": "NumCustomTags"}, 2]}, {"Condition": "HasAtLeast3Tags"}] + "Fn::Or": [ + {"Fn::Equals": [{"Ref": "NumCustomTags"}, 2]}, + {"Condition": "HasAtLeast3Tags"}, + ] }, "HasAtLeast1Tags": { - "Fn::Or": [{"Fn::Equals": [{"Ref": "NumCustomTags"}, 1]}, {"Condition": "HasAtLeast2Tags"}] + "Fn::Or": [ + {"Fn::Equals": [{"Ref": "NumCustomTags"}, 1]}, + {"Condition": "HasAtLeast2Tags"}, + ] }, }, "Resources": {}, @@ -407,7 +477,15 @@ def test_join_and_ref(): mappings = {} conditions = {} function = { - "Fn::Join": ["", ["arn:", {"Ref": "Partition"}, ":s3:::elasticbeanstalk-*-", {"Ref": "AWS::AccountId"}]] + "Fn::Join": [ + "", + [ + "arn:", + {"Ref": "Partition"}, + ":s3:::elasticbeanstalk-*-", + {"Ref": "AWS::AccountId"}, + ], + ] } assert resolve(function, parameters, mappings, conditions) == "arn:patata:s3:::elasticbeanstalk-*-1234567890" @@ -475,7 +553,10 @@ def test_resolve_include_resource_when_condition_is_true_or_doesnt_exist(conditi "Type": "AWS::IAM::Role", "Condition": "testCondition", "Properties": { - "AssumeRolePolicyDocument": {"Version": "2012-10-17", "Statement": []}, + "AssumeRolePolicyDocument": { + "Version": "2012-10-17", + "Statement": [], + }, "Path": "/", "Policies": [], }, @@ -502,7 +583,10 @@ def test_resolve_include_resource_when_condition_is_not_present(): "test_resource_id": { "Type": "AWS::IAM::Role", "Properties": { - "AssumeRolePolicyDocument": {"Version": "2012-10-17", "Statement": []}, + "AssumeRolePolicyDocument": { + "Version": "2012-10-17", + "Statement": [], + }, "Path": "/", "Policies": [], }, @@ -516,7 +600,13 @@ def test_resolve_include_resource_when_condition_is_not_present(): def test_resolve_scenario_1(): template = { "AWSTemplateFormatVersion": "2010-09-09", - "Parameters": {"StarParameter": {"Type": "String", "Default": "*", "Description": "Star Param"}}, + "Parameters": { + "StarParameter": { + "Type": "String", + "Default": "*", + "Description": "Star Param", + } + }, "Resources": { "rootRole": { "Type": "AWS::IAM::Role", @@ -566,7 +656,12 @@ def test_resolve_scenario_2(): template = { "AWSTemplateFormatVersion": "2010-09-09", "Description": "IAM role for Lambda", - "Parameters": {"LambdaFunctionName": {"Description": "Name of the lambda function", "Type": "String"}}, + "Parameters": { + "LambdaFunctionName": { + "Description": "Name of the lambda function", + "Type": "String", + } + }, "Resources": { "lambdaRole": { "Properties": { @@ -605,7 +700,10 @@ def test_resolve_scenario_2(): "PolicyDocument": { "Statement": [ { - "Action": ["xray:PutTraceSegments", "xray:PutTelemetryRecords"], + "Action": [ + "xray:PutTraceSegments", + "xray:PutTelemetryRecords", + ], "Effect": "Allow", "Resource": ["*"], } @@ -618,7 +716,11 @@ def test_resolve_scenario_2(): "PolicyDocument": { "Statement": [ { - "Action": ["logs:CreateLogGroup", "logs:CreateLogStream", "logs:PutLogEvents"], + "Action": [ + "logs:CreateLogGroup", + "logs:CreateLogStream", + "logs:PutLogEvents", + ], "Effect": "Allow", "Resource": ["*"], } @@ -657,10 +759,20 @@ def test_resolve_scenario_3(): "GroupDescription": "Allow http to client host", "VpcId": "VPCID", "SecurityGroupIngress": [ - {"IpProtocol": "tcp", "FromPort": 80, "ToPort": 80, "CidrIp": {"Ref": "IPValueIngress"}} + { + "IpProtocol": "tcp", + "FromPort": 80, + "ToPort": 80, + "CidrIp": {"Ref": "IPValueIngress"}, + } ], "SecurityGroupEgress": [ - {"IpProtocol": "tcp", "FromPort": 80, "ToPort": 80, "CidrIp": {"Ref": "IPValueEgress"}} + { + "IpProtocol": "tcp", + "FromPort": 80, + "ToPort": 80, + "CidrIp": {"Ref": "IPValueEgress"}, + } ], }, } @@ -732,7 +844,11 @@ def test_resolve_booleans_on_conditions_for_modeled_resource(): "Id": "Key-Policy", "Statement": [ { - "Action": ["kms:CreateGrant", "kms:ListGrants", "kms:RevokeGrant"], + "Action": [ + "kms:CreateGrant", + "kms:ListGrants", + "kms:RevokeGrant", + ], "Effect": "Allow", "Sid": "Allow attachment of persistent resources", "Principal": {"AWS": "*"}, @@ -769,7 +885,11 @@ def test_resolve_booleans_different_properties_for_generic_resource(): "Version": "2012-10-17", "Statement": [ { - "Action": ["kms:CreateGrant", "kms:ListGrants", "kms:RevokeGrant"], + "Action": [ + "kms:CreateGrant", + "kms:ListGrants", + "kms:RevokeGrant", + ], "Effect": "Allow", "Principal": {"AWS": "*"}, "Resource": "*", @@ -793,7 +913,10 @@ def test_resolve_booleans_different_properties_for_generic_resource(): def test_resolve_template_with_a_valid_resource_without_properties(): - template = {"AWSTemplateFormatVersion": "2010-09-09", "Resources": {"MySNSTopic": {"Type": "AWS::SNS::Topic"}}} + template = { + "AWSTemplateFormatVersion": "2010-09-09", + "Resources": {"MySNSTopic": {"Type": "AWS::SNS::Topic"}}, + } model = parse(template).resolve() resource = model.Resources["MySNSTopic"] diff --git a/tests/test_resource.py b/tests/test_resource.py index b51c099..144abf2 100644 --- a/tests/test_resource.py +++ b/tests/test_resource.py @@ -13,7 +13,12 @@ { "AWSTemplateFormatVersion": "2010-09-09", "Description": "Test resolving a nonexistent resource to Resource class", - "Resources": {"NonexistentResource": {"Type": "AWS::Non::Existent", "Properties": {}}}, + "Resources": { + "NonexistentResource": { + "Type": "AWS::Non::Existent", + "Properties": {}, + } + }, }, [], 0, @@ -42,7 +47,13 @@ "Properties": { "PropertyRandom": "One", "PolicyDocument": { - "Statement": [{"Effect": "Allow", "Action": ["service:GetService"], "Resource": "*"}] + "Statement": [ + { + "Effect": "Allow", + "Action": ["service:GetService"], + "Resource": "*", + } + ] }, }, } @@ -51,7 +62,13 @@ [ OptionallyNamedPolicyDocument( policy_document=PolicyDocument( - Statement=[Statement(Effect="Allow", Action=["service:GetService"], Resource="*")] + Statement=[ + Statement( + Effect="Allow", + Action=["service:GetService"], + Resource="*", + ) + ] ), name=None, ) @@ -68,10 +85,22 @@ "Properties": { "PropertyRandom": "One", "PolicyDocumentOne": { - "Statement": [{"Effect": "Allow", "Action": ["service:GetService"], "Resource": "*"}] + "Statement": [ + { + "Effect": "Allow", + "Action": ["service:GetService"], + "Resource": "*", + } + ] }, "PolicyDocumentTwo": { - "Statement": [{"Effect": "Allow", "Action": ["service:GetService"], "Resource": "*"}] + "Statement": [ + { + "Effect": "Allow", + "Action": ["service:GetService"], + "Resource": "*", + } + ] }, }, } @@ -80,13 +109,25 @@ [ OptionallyNamedPolicyDocument( policy_document=PolicyDocument( - Statement=[Statement(Effect="Allow", Action=["service:GetService"], Resource="*")] + Statement=[ + Statement( + Effect="Allow", + Action=["service:GetService"], + Resource="*", + ) + ] ), name=None, ), OptionallyNamedPolicyDocument( policy_document=PolicyDocument( - Statement=[Statement(Effect="Allow", Action=["service:GetService"], Resource="*")] + Statement=[ + Statement( + Effect="Allow", + Action=["service:GetService"], + Resource="*", + ) + ] ), name=None, ), @@ -103,12 +144,22 @@ "Properties": { "PropertyRandom": "One", "PolicyDocumentOne": { - "Statement": [{"Effect": "Allow", "Action": ["service:GetService"], "Resource": "*"}] + "Statement": [ + { + "Effect": "Allow", + "Action": ["service:GetService"], + "Resource": "*", + } + ] }, "PropertyTwo": { "PolicyDocumentTwo": { "Statement": [ - {"Effect": "Allow", "Action": ["service:GetService"], "Resource": "*"} + { + "Effect": "Allow", + "Action": ["service:GetService"], + "Resource": "*", + } ] } }, @@ -119,13 +170,25 @@ [ OptionallyNamedPolicyDocument( policy_document=PolicyDocument( - Statement=[Statement(Effect="Allow", Action=["service:GetService"], Resource="*")] + Statement=[ + Statement( + Effect="Allow", + Action=["service:GetService"], + Resource="*", + ) + ] ), name=None, ), OptionallyNamedPolicyDocument( policy_document=PolicyDocument( - Statement=[Statement(Effect="Allow", Action=["service:GetService"], Resource="*")] + Statement=[ + Statement( + Effect="Allow", + Action=["service:GetService"], + Resource="*", + ) + ] ), name=None, ), @@ -144,14 +207,22 @@ "PropertyOne": { "PolicyDocumentOne": { "Statement": [ - {"Effect": "Allow", "Action": ["service:GetService"], "Resource": "*"} + { + "Effect": "Allow", + "Action": ["service:GetService"], + "Resource": "*", + } ] } }, "PropertyTwo": { "PolicyDocumentTwo": { "Statement": [ - {"Effect": "Allow", "Action": ["service:GetService"], "Resource": "*"} + { + "Effect": "Allow", + "Action": ["service:GetService"], + "Resource": "*", + } ] } }, @@ -162,13 +233,25 @@ [ OptionallyNamedPolicyDocument( policy_document=PolicyDocument( - Statement=[Statement(Effect="Allow", Action=["service:GetService"], Resource="*")] + Statement=[ + Statement( + Effect="Allow", + Action=["service:GetService"], + Resource="*", + ) + ] ), name=None, ), OptionallyNamedPolicyDocument( policy_document=PolicyDocument( - Statement=[Statement(Effect="Allow", Action=["service:GetService"], Resource="*")] + Statement=[ + Statement( + Effect="Allow", + Action=["service:GetService"], + Resource="*", + ) + ] ), name=None, ), @@ -184,10 +267,22 @@ "Type": "AWS::Non::Existent", "Properties": { "PolicyDocumentTwo": [ - {"Statement": [{"Effect": "Allow", "Action": ["service:GetService"], "Resource": "*"}]}, { "Statement": [ - {"Effect": "Allow", "Action": ["service:GetServiceAnother"], "Resource": "*"} + { + "Effect": "Allow", + "Action": ["service:GetService"], + "Resource": "*", + } + ] + }, + { + "Statement": [ + { + "Effect": "Allow", + "Action": ["service:GetServiceAnother"], + "Resource": "*", + } ] }, ] @@ -198,13 +293,25 @@ [ OptionallyNamedPolicyDocument( policy_document=PolicyDocument( - Statement=[Statement(Effect="Allow", Action=["service:GetService"], Resource="*")] + Statement=[ + Statement( + Effect="Allow", + Action=["service:GetService"], + Resource="*", + ) + ] ), name=None, ), OptionallyNamedPolicyDocument( policy_document=PolicyDocument( - Statement=[Statement(Effect="Allow", Action=["service:GetServiceAnother"], Resource="*")] + Statement=[ + Statement( + Effect="Allow", + Action=["service:GetServiceAnother"], + Resource="*", + ) + ] ), name=None, ), @@ -224,7 +331,11 @@ "PolicyName": "APolicyName", "PolicyDocument": { "Statement": [ - {"Effect": "Allow", "Action": ["service:GetService"], "Resource": "*"} + { + "Effect": "Allow", + "Action": ["service:GetService"], + "Resource": "*", + } ] }, } @@ -236,7 +347,13 @@ [ OptionallyNamedPolicyDocument( policy_document=PolicyDocument( - Statement=[Statement(Effect="Allow", Action=["service:GetService"], Resource="*")] + Statement=[ + Statement( + Effect="Allow", + Action=["service:GetService"], + Resource="*", + ) + ] ), name="APolicyName", ) diff --git a/tests/test_types.py b/tests/test_types.py index 4f07102..866dbd7 100644 --- a/tests/test_types.py +++ b/tests/test_types.py @@ -14,7 +14,13 @@ class Model(BaseModel): assert model_schema == { "title": "Model", "type": "object", - "properties": {"ip_network": {"title": "Ip Network", "type": "string", "format": "looseipv4network"}}, + "properties": { + "ip_network": { + "title": "Ip Network", + "type": "string", + "format": "looseipv4network", + } + }, "required": ["ip_network"], } @@ -27,7 +33,13 @@ class Model(BaseModel): assert model_schema == { "title": "Model", "type": "object", - "properties": {"ip_network": {"title": "Ip Network", "type": "string", "format": "looseipv6network"}}, + "properties": { + "ip_network": { + "title": "Ip Network", + "type": "string", + "format": "looseipv6network", + } + }, "required": ["ip_network"], } @@ -79,7 +91,11 @@ class Model(BaseModel): @pytest.mark.parametrize( "value", - ["2012::1234:abcd:ffff:c0a8:101/64", "2022::1234:abcd:ffff:c0a8:101/64", "2032::1234:abcd:ffff:c0a8:101/64"], + [ + "2012::1234:abcd:ffff:c0a8:101/64", + "2022::1234:abcd:ffff:c0a8:101/64", + "2032::1234:abcd:ffff:c0a8:101/64", + ], ) def test_loose_ip_v6_is_not_strict(value): class Model(BaseModel): @@ -95,11 +111,23 @@ class Model(BaseModel): [ ( "hello,world", - [{"loc": ("ip",), "msg": "Expected 4 octets in 'hello,world'", "type": "value_error.addressvalue"}], + [ + { + "loc": ("ip",), + "msg": "Expected 4 octets in 'hello,world'", + "type": "value_error.addressvalue", + } + ], ), ( "192.168.0.1.1.1/24", - [{"loc": ("ip",), "msg": "Expected 4 octets in '192.168.0.1.1.1'", "type": "value_error.addressvalue"}], + [ + { + "loc": ("ip",), + "msg": "Expected 4 octets in '192.168.0.1.1.1'", + "type": "value_error.addressvalue", + } + ], ), ( -1, @@ -123,7 +151,13 @@ class Model(BaseModel): ), ( "2001:db00::1/120", - [{"loc": ("ip",), "msg": "Expected 4 octets in '2001:db00::1'", "type": "value_error.addressvalue"}], + [ + { + "loc": ("ip",), + "msg": "Expected 4 octets in '2001:db00::1'", + "type": "value_error.addressvalue", + } + ], ), ], ) @@ -141,7 +175,13 @@ class Model(BaseModel): [ ( "hello,world", - [{"loc": ("ip",), "msg": "At least 3 parts expected in 'hello,world'", "type": "value_error.addressvalue"}], + [ + { + "loc": ("ip",), + "msg": "At least 3 parts expected in 'hello,world'", + "type": "value_error.addressvalue", + } + ], ), ( "192.168.0.1.1.1/24", @@ -175,7 +215,13 @@ class Model(BaseModel): ), ( "192.168.0.1/24", - [{"loc": ("ip",), "msg": "At least 3 parts expected in '192.168.0.1'", "type": "value_error.addressvalue"}], + [ + { + "loc": ("ip",), + "msg": "At least 3 parts expected in '192.168.0.1'", + "type": "value_error.addressvalue", + } + ], ), ], ) From ed340f7fc4b9e041914e001344a1f08394c65994 Mon Sep 17 00:00:00 2001 From: Jordi Soucheiron Date: Thu, 28 Dec 2023 13:52:44 +0100 Subject: [PATCH 11/12] Update makefile to be able to test docs --- Makefile | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Makefile b/Makefile index 1361422..7df9936 100644 --- a/Makefile +++ b/Makefile @@ -3,11 +3,11 @@ SOURCES = $(shell find . -name "*.py") install: pip install -r requirements.txt -install-dev: install - pip install -r requirements-dev.txt +install-dev: + pip install -r requirements.txt -r requirements-dev.txt . install-docs: - pip install -r requirements-docs.txt + pip install -r requirements.txt -r requirements-docs.txt . format: isort-format black-format From e11fc2e1b79a55bbb2ff9cbf364157a4c5f71d9d Mon Sep 17 00:00:00 2001 From: Jordi Soucheiron Date: Thu, 28 Dec 2023 14:13:33 +0100 Subject: [PATCH 12/12] Revert changes --- pycfmodel/model/cf_model.py | 11 +- .../model/resources/iam_managed_policy.py | 3 +- pycfmodel/model/resources/iam_policy.py | 3 +- pycfmodel/model/resources/iam_role.py | 4 +- .../properties/statement_condition.py | 15 +- pycfmodel/model/resources/properties/types.py | 8 +- pycfmodel/model/resources/resource.py | 9 +- pycfmodel/resolver.py | 42 +-- setup.py | 5 +- .../properties/test_policy_document.py | 39 +- tests/resources/properties/test_principal.py | 7 +- tests/resources/properties/test_statement.py | 127 +------ .../properties/test_statement_condition.py | 355 +++--------------- .../resources/test_ec2_vpc_endpoint_policy.py | 13 +- tests/resources/test_es_domain.py | 13 +- tests/resources/test_iam_group.py | 11 +- tests/resources/test_iam_managed_policy.py | 9 +- tests/resources/test_iam_policy.py | 6 +- tests/resources/test_iam_role.py | 5 +- tests/resources/test_iam_user.py | 16 +- tests/resources/test_opensearch_domain.py | 28 +- tests/resources/test_s3_bucket.py | 17 +- tests/resources/test_security_group.py | 18 +- tests/resources/test_sns_topic_policy.py | 8 +- tests/test_action_expander.py | 26 +- tests/test_cf_model.py | 12 +- tests/test_generic.py | 10 +- tests/test_parameter.py | 18 +- tests/test_resolver.py | 189 ++-------- tests/test_resource.py | 159 ++------ tests/test_types.py | 62 +-- 31 files changed, 192 insertions(+), 1056 deletions(-) diff --git a/pycfmodel/model/cf_model.py b/pycfmodel/model/cf_model.py index 39c0ed5..19c5951 100644 --- a/pycfmodel/model/cf_model.py +++ b/pycfmodel/model/cf_model.py @@ -81,16 +81,7 @@ def resolve(self, extra_params=None) -> "CFModel": resolved_conditions = {} for key, value in conditions.items(): resolved_conditions.update( - { - key: _extended_bool( - resolve( - value, - extended_parameters, - self.Mappings, - resolved_conditions, - ) - ) - } + {key: _extended_bool(resolve(value, extended_parameters, self.Mappings, resolved_conditions))} ) resources = dict_value.pop("Resources") diff --git a/pycfmodel/model/resources/iam_managed_policy.py b/pycfmodel/model/resources/iam_managed_policy.py index 7d9343b..b587636 100644 --- a/pycfmodel/model/resources/iam_managed_policy.py +++ b/pycfmodel/model/resources/iam_managed_policy.py @@ -48,7 +48,6 @@ class IAMManagedPolicy(Resource): def policy_documents(self) -> List[OptionallyNamedPolicyDocument]: return [ OptionallyNamedPolicyDocument( - name=self.Properties.ManagedPolicyName, - policy_document=self.Properties.PolicyDocument, + name=self.Properties.ManagedPolicyName, policy_document=self.Properties.PolicyDocument ) ] diff --git a/pycfmodel/model/resources/iam_policy.py b/pycfmodel/model/resources/iam_policy.py index 52ebfc1..d368ad0 100644 --- a/pycfmodel/model/resources/iam_policy.py +++ b/pycfmodel/model/resources/iam_policy.py @@ -44,7 +44,6 @@ class IAMPolicy(Resource): def policy_documents(self) -> List[OptionallyNamedPolicyDocument]: return [ OptionallyNamedPolicyDocument( - name=self.Properties.PolicyName, - policy_document=self.Properties.PolicyDocument, + name=self.Properties.PolicyName, policy_document=self.Properties.PolicyDocument ) ] diff --git a/pycfmodel/model/resources/iam_role.py b/pycfmodel/model/resources/iam_role.py index 134bb41..0fe9782 100644 --- a/pycfmodel/model/resources/iam_role.py +++ b/pycfmodel/model/resources/iam_role.py @@ -57,9 +57,7 @@ def policy_documents(self) -> List[OptionallyNamedPolicyDocument]: return result @property - def assume_role_as_optionally_named_policy_document_list( - self, - ) -> List[OptionallyNamedPolicyDocument]: + def assume_role_as_optionally_named_policy_document_list(self) -> List[OptionallyNamedPolicyDocument]: return [OptionallyNamedPolicyDocument(name=None, policy_document=self.Properties.AssumeRolePolicyDocument)] @property diff --git a/pycfmodel/model/resources/properties/statement_condition.py b/pycfmodel/model/resources/properties/statement_condition.py index 4f89bfa..a67cc80 100644 --- a/pycfmodel/model/resources/properties/statement_condition.py +++ b/pycfmodel/model/resources/properties/statement_condition.py @@ -45,20 +45,9 @@ def build_evaluator(function: str, arg_a: Any, arg_b: Any) -> Callable: elif function == "Null": return lambda kwargs: (kwargs.get(arg_a) is not None) is arg_b - elif function in ( - "StringEquals", - "ArnEquals", - "BinaryEquals", - "NumericEquals", - "DateEquals", - ): + elif function in ("StringEquals", "ArnEquals", "BinaryEquals", "NumericEquals", "DateEquals"): return lambda kwargs: kwargs[arg_a] == arg_b - elif function in ( - "StringNotEquals", - "ArnNotEquals", - "NumericNotEquals", - "DateNotEquals", - ): + elif function in ("StringNotEquals", "ArnNotEquals", "NumericNotEquals", "DateNotEquals"): return lambda kwargs: kwargs[arg_a] != arg_b elif function in ("NumericLessThan", "DateLessThan"): diff --git a/pycfmodel/model/resources/properties/types.py b/pycfmodel/model/resources/properties/types.py index 36cde64..ae95730 100644 --- a/pycfmodel/model/resources/properties/types.py +++ b/pycfmodel/model/resources/properties/types.py @@ -9,11 +9,5 @@ from pycfmodel.model.resources.properties.tag import Tag Properties = Union[ - Policy, - PolicyDocument, - SecurityGroupEgressProp, - SecurityGroupIngressProp, - Statement, - StatementCondition, - Tag, + Policy, PolicyDocument, SecurityGroupEgressProp, SecurityGroupIngressProp, Statement, StatementCondition, Tag ] diff --git a/pycfmodel/model/resources/resource.py b/pycfmodel/model/resources/resource.py index 2cd916f..eb69375 100644 --- a/pycfmodel/model/resources/resource.py +++ b/pycfmodel/model/resources/resource.py @@ -57,8 +57,7 @@ def policy_documents(self) -> List[OptionallyNamedPolicyDocument]: return policy_documents self.obtain_policy_documents( - policy_documents=policy_documents, - properties=list(self.Properties.__dict__.values()), + policy_documents=policy_documents, properties=list(self.Properties.__dict__.values()) ) return policy_documents @@ -72,8 +71,7 @@ def obtain_policy_documents(self, policy_documents: List, properties: List[Any]) elif isinstance(property_type, Policy): policy_documents.append( OptionallyNamedPolicyDocument( - name=property_type.PolicyName, - policy_document=property_type.PolicyDocument, + name=property_type.PolicyName, policy_document=property_type.PolicyDocument ) ) elif isinstance(property_type, OptionallyNamedPolicyDocument): @@ -82,8 +80,7 @@ def obtain_policy_documents(self, policy_documents: List, properties: List[Any]) self.obtain_policy_documents(policy_documents=policy_documents, properties=property_type) elif isinstance(property_type, Generic): self.obtain_policy_documents( - policy_documents=policy_documents, - properties=list(property_type.__dict__.values()), + policy_documents=policy_documents, properties=list(property_type.__dict__.values()) ) @property diff --git a/pycfmodel/resolver.py b/pycfmodel/resolver.py index e54cc42..9aaad4d 100644 --- a/pycfmodel/resolver.py +++ b/pycfmodel/resolver.py @@ -22,12 +22,7 @@ class _BooleanModel(BaseModel): bool_value: bool -def resolve( - function: ValidResolvers, - params: Dict, - mappings: Dict[str, Dict], - conditions: Dict[str, bool], -): +def resolve(function: ValidResolvers, params: Dict, mappings: Dict[str, Dict], conditions: Dict[str, bool]): if function is None: return function @@ -152,49 +147,24 @@ def resolve_if(function_body, params: Dict, mappings: Dict[str, Dict], condition return resolve(false_section, params, mappings, conditions) -def resolve_and( - function_body: List, - params: Dict, - mappings: Dict[str, Dict], - conditions: Dict[str, bool], -) -> bool: +def resolve_and(function_body: List, params: Dict, mappings: Dict[str, Dict], conditions: Dict[str, bool]) -> bool: return all(_extended_bool(resolve(part, params, mappings, conditions)) for part in function_body) -def resolve_or( - function_body: List, - params: Dict, - mappings: Dict[str, Dict], - conditions: Dict[str, bool], -) -> bool: +def resolve_or(function_body: List, params: Dict, mappings: Dict[str, Dict], conditions: Dict[str, bool]) -> bool: return any(_extended_bool(resolve(part, params, mappings, conditions)) for part in function_body) -def resolve_not( - function_body: List, - params: Dict, - mappings: Dict[str, Dict], - conditions: Dict[str, bool], -) -> bool: +def resolve_not(function_body: List, params: Dict, mappings: Dict[str, Dict], conditions: Dict[str, bool]) -> bool: return not _extended_bool(resolve(function_body[0], params, mappings, conditions)) -def resolve_equals( - function_body: List, - params: Dict, - mappings: Dict[str, Dict], - conditions: Dict[str, bool], -) -> bool: +def resolve_equals(function_body: List, params: Dict, mappings: Dict[str, Dict], conditions: Dict[str, bool]) -> bool: part_1, part_2 = function_body return resolve(part_1, params, mappings, conditions) == resolve(part_2, params, mappings, conditions) -def resolve_base64( - function_body: str, - params: Dict, - mappings: Dict[str, Dict], - conditions: Dict[str, bool], -) -> str: +def resolve_base64(function_body: str, params: Dict, mappings: Dict[str, Dict], conditions: Dict[str, bool]) -> str: resolved_string = resolve(function_body, params, mappings, conditions) return str(b64encode(resolved_string.encode("utf-8")), "utf-8") diff --git a/setup.py b/setup.py index e87a0d3..a65481d 100644 --- a/setup.py +++ b/setup.py @@ -39,5 +39,8 @@ python_requires=">=3.7", install_requires=install_requires, tests_require=dev_requires, - extras_require={"dev": dev_requires, "docs": docs_requires}, + extras_require={ + "dev": dev_requires, + "docs": docs_requires, + }, ) diff --git a/tests/resources/properties/test_policy_document.py b/tests/resources/properties/test_policy_document.py index 1472bbf..38c244e 100644 --- a/tests/resources/properties/test_policy_document.py +++ b/tests/resources/properties/test_policy_document.py @@ -16,10 +16,7 @@ def policy_document_one_statement(): "Version": "2012-10-17", "Statement": { "Effect": "Allow", - "Principal": { - "Service": ["ec2.amazonaws.com"], - "AWS": "arn:aws:iam::324320755747:root", - }, + "Principal": {"Service": ["ec2.amazonaws.com"], "AWS": "arn:aws:iam::324320755747:root"}, "Action": ["sts:AssumeRole"], }, } @@ -34,18 +31,12 @@ def policy_document_multi_statement(): "Statement": [ { "Effect": "Allow", - "Principal": { - "Service": ["ec2.amazonaws.com"], - "AWS": "arn:aws:iam::324320755747:root", - }, + "Principal": {"Service": ["ec2.amazonaws.com"], "AWS": "arn:aws:iam::324320755747:root"}, "Action": ["sts:AssumeRole"], }, { "Effect": "Allow", - "Principal": { - "Service": ["ec2.amazonaws.com"], - "AWS": "arn:aws:iam::324320755747:root", - }, + "Principal": {"Service": ["ec2.amazonaws.com"], "AWS": "arn:aws:iam::324320755747:root"}, "Action": ["sts:AssumeRole"], }, ], @@ -56,16 +47,7 @@ def policy_document_multi_statement(): @fixture def policy_document_star_resource(): return PolicyDocument( - **{ - "Statement": [ - { - "Action": ["*"], - "Effect": "Allow", - "Resource": "*", - "Principal": {"AWS": ["156460612806"]}, - } - ] - } + **{"Statement": [{"Action": ["*"], "Effect": "Allow", "Resource": "*", "Principal": {"AWS": ["156460612806"]}}]} ) @@ -362,20 +344,13 @@ def test_policy_document_chan_check_if_the_statement_effect_is_allow_or_deny(sta assert PolicyDocument._is_statement_effect_allow(statement_effect=statement_effect) == is_allow -def test_policy_document_condition_with_source_ip( - policy_document_condition_with_source_ip: PolicyDocument, -): +def test_policy_document_condition_with_source_ip(policy_document_condition_with_source_ip: PolicyDocument): assert policy_document_condition_with_source_ip.Statement[0].Condition.IpAddress == { - "aws:SourceIp": [ - IPv4Network("116.202.65.160/32"), - IPv4Network("116.202.68.32/27"), - ] + "aws:SourceIp": [IPv4Network("116.202.65.160/32"), IPv4Network("116.202.68.32/27")] } -def test_policy_document_condition_with_source_vpce( - policy_document_condition_with_source_vpce: PolicyDocument, -): +def test_policy_document_condition_with_source_vpce(policy_document_condition_with_source_vpce: PolicyDocument): assert policy_document_condition_with_source_vpce.Statement[0].Condition.IpAddress == { "aws:SourceVpce": ["vpce-123456"] } diff --git a/tests/resources/properties/test_principal.py b/tests/resources/properties/test_principal.py index 90a576e..a53c473 100644 --- a/tests/resources/properties/test_principal.py +++ b/tests/resources/properties/test_principal.py @@ -13,12 +13,7 @@ {"AWS": "arn:aws:iam::123456789012:role/role-name"}, {"AWS": "arn:aws:sts::123456789012:federated-user/user-name"}, {"AWS": ["123456789012", "555555555555"]}, - { - "AWS": [ - "arn:aws:iam::123456789012:user/user-name-1", - "arn:aws:iam::123456789012:user/user-name-2", - ] - }, + {"AWS": ["arn:aws:iam::123456789012:user/user-name-1", "arn:aws:iam::123456789012:user/user-name-2"]}, {"CanonicalUser": "79a59df900b949e55d96a1e698fbacedfd6e09d98eacf8f8d5218e7cd47ef2be"}, { "AWS": ["arn:aws:iam::123456789012:root", "999999999999"], diff --git a/tests/resources/properties/test_statement.py b/tests/resources/properties/test_statement.py index 1cd99b6..1bee5b2 100644 --- a/tests/resources/properties/test_statement.py +++ b/tests/resources/properties/test_statement.py @@ -6,47 +6,19 @@ def statement_1(): - return Statement( - **{ - "Effect": "Allow", - "Action": ["action1"], - "NotAction": "action2", - "Resource": ["arn"], - } - ) + return Statement(**{"Effect": "Allow", "Action": ["action1"], "NotAction": "action2", "Resource": ["arn"]}) def statement_2(): - return Statement( - **{ - "Effect": "Allow", - "Action": "action1", - "NotAction": ["action2"], - "Resource": ["arn"], - } - ) + return Statement(**{"Effect": "Allow", "Action": "action1", "NotAction": ["action2"], "Resource": ["arn"]}) def statement_3(): - return Statement( - **{ - "Effect": "Allow", - "Action": "action1", - "Resource": ["arn1"], - "NotResource": "arn2", - } - ) + return Statement(**{"Effect": "Allow", "Action": "action1", "Resource": ["arn1"], "NotResource": "arn2"}) def statement_4(): - return Statement( - **{ - "Effect": "Allow", - "Action": "action2", - "Resource": "arn1", - "NotResource": ["arn2"], - } - ) + return Statement(**{"Effect": "Allow", "Action": "action2", "Resource": "arn1", "NotResource": ["arn2"]}) def statement_principal_1(): @@ -58,22 +30,14 @@ def statement_principal_2(): **{ "Effect": "Allow", "Principal": { - "AWS": [ - "arn:aws:iam::AWS-account-ID:user/user-name-1", - "arn:aws:iam::AWS-account-ID:user/UserName2", - ] + "AWS": ["arn:aws:iam::AWS-account-ID:user/user-name-1", "arn:aws:iam::AWS-account-ID:user/UserName2"] }, } ) def statement_principal_3(): - return Statement( - **{ - "Effect": "Allow", - "Principal": {"Federated": "cognito-identity.amazonaws.com"}, - } - ) + return Statement(**{"Effect": "Allow", "Principal": {"Federated": "cognito-identity.amazonaws.com"}}) def statement_principal_4(): @@ -84,10 +48,7 @@ def statement_principal_5(): return Statement( **{ "Effect": "Allow", - "Principal": [ - "arn:aws:iam::AWS-account-ID:user/user-name-1", - "arn:aws:iam::AWS-account-ID:user/UserName2", - ], + "Principal": ["arn:aws:iam::AWS-account-ID:user/user-name-1", "arn:aws:iam::AWS-account-ID:user/UserName2"], } ) @@ -101,22 +62,14 @@ def statement_not_principal_2(): **{ "Effect": "Allow", "NotPrincipal": { - "AWS": [ - "arn:aws:iam::AWS-account-ID:user/user-name-1", - "arn:aws:iam::AWS-account-ID:user/UserName2", - ] + "AWS": ["arn:aws:iam::AWS-account-ID:user/user-name-1", "arn:aws:iam::AWS-account-ID:user/UserName2"] }, } ) def statement_not_principal_3(): - return Statement( - **{ - "Effect": "Allow", - "NotPrincipal": {"Federated": "cognito-identity.amazonaws.com"}, - } - ) + return Statement(**{"Effect": "Allow", "NotPrincipal": {"Federated": "cognito-identity.amazonaws.com"}}) def statement_not_principal_4(): @@ -136,14 +89,7 @@ def statement_not_principal_5(): def test_capitalize_effect(): - statement = Statement( - **{ - "Effect": "allOw", - "Action": ["action1"], - "NotAction": "action2", - "Resource": ["arn"], - } - ) + statement = Statement(**{"Effect": "allOw", "Action": ["action1"], "NotAction": "action2", "Resource": ["arn"]}) assert statement.Effect == "Allow" @@ -168,18 +114,9 @@ def test_get_action_list(statement, expected_output): @pytest.mark.parametrize( "statement, expected_output", [ - ( - Statement(**{"Effect": "Allow", "Action": "ec2:RunInstances", "Resource": ["arn"]}), - ["ec2:RunInstances"], - ), - ( - Statement(**{"Effect": "Allow", "Action": "ec2:Run?nstances", "Resource": ["arn"]}), - ["ec2:RunInstances"], - ), - ( - Statement(**{"Effect": "Allow", "Action": "ec?:RunInstances", "Resource": ["arn"]}), - ["ec2:RunInstances"], - ), + (Statement(**{"Effect": "Allow", "Action": "ec2:RunInstances", "Resource": ["arn"]}), ["ec2:RunInstances"]), + (Statement(**{"Effect": "Allow", "Action": "ec2:Run?nstances", "Resource": ["arn"]}), ["ec2:RunInstances"]), + (Statement(**{"Effect": "Allow", "Action": "ec?:RunInstances", "Resource": ["arn"]}), ["ec2:RunInstances"]), ( Statement(**{"Effect": "Allow", "Action": "ec2:Run*", "Resource": ["arn"]}), ["ec2:RunInstances", "ec2:RunScheduledInstances"], @@ -209,36 +146,24 @@ def test_get_resource_list(statement, expected_output): (statement_principal_1(), ["arn:aws:iam::123456789012:root"]), ( statement_principal_2(), - [ - "arn:aws:iam::AWS-account-ID:user/user-name-1", - "arn:aws:iam::AWS-account-ID:user/UserName2", - ], + ["arn:aws:iam::AWS-account-ID:user/user-name-1", "arn:aws:iam::AWS-account-ID:user/UserName2"], ), (statement_principal_3(), ["cognito-identity.amazonaws.com"]), (statement_principal_4(), ["arn:aws:iam::123456789012:root"]), ( statement_principal_5(), - [ - "arn:aws:iam::AWS-account-ID:user/user-name-1", - "arn:aws:iam::AWS-account-ID:user/UserName2", - ], + ["arn:aws:iam::AWS-account-ID:user/user-name-1", "arn:aws:iam::AWS-account-ID:user/UserName2"], ), (statement_not_principal_1(), ["arn:aws:iam::123456789012:root"]), ( statement_not_principal_2(), - [ - "arn:aws:iam::AWS-account-ID:user/user-name-1", - "arn:aws:iam::AWS-account-ID:user/UserName2", - ], + ["arn:aws:iam::AWS-account-ID:user/user-name-1", "arn:aws:iam::AWS-account-ID:user/UserName2"], ), (statement_not_principal_3(), ["cognito-identity.amazonaws.com"]), (statement_not_principal_4(), ["arn:aws:iam::123456789012:root"]), ( statement_not_principal_5(), - [ - "arn:aws:iam::AWS-account-ID:user/user-name-1", - "arn:aws:iam::AWS-account-ID:user/UserName2", - ], + ["arn:aws:iam::AWS-account-ID:user/user-name-1", "arn:aws:iam::AWS-account-ID:user/UserName2"], ), ], ) @@ -262,21 +187,9 @@ def test_actions_with(statement, pattern, expected_output): @pytest.mark.parametrize( "statement, pattern, expected_output", [ - ( - statement_principal_1(), - re.compile(r"^.*123456789012.*$"), - ["arn:aws:iam::123456789012:root"], - ), - ( - statement_principal_2(), - re.compile(r"^.*user-name-1$"), - ["arn:aws:iam::AWS-account-ID:user/user-name-1"], - ), - ( - statement_principal_3(), - re.compile(r"^.*\.amazonaws\.com$"), - ["cognito-identity.amazonaws.com"], - ), + (statement_principal_1(), re.compile(r"^.*123456789012.*$"), ["arn:aws:iam::123456789012:root"]), + (statement_principal_2(), re.compile(r"^.*user-name-1$"), ["arn:aws:iam::AWS-account-ID:user/user-name-1"]), + (statement_principal_3(), re.compile(r"^.*\.amazonaws\.com$"), ["cognito-identity.amazonaws.com"]), ], ) def test_principals_with(statement, pattern, expected_output): diff --git a/tests/resources/properties/test_statement_condition.py b/tests/resources/properties/test_statement_condition.py index d9d6af4..4bba591 100644 --- a/tests/resources/properties/test_statement_condition.py +++ b/tests/resources/properties/test_statement_condition.py @@ -79,8 +79,7 @@ def test_statement_condition_remove_colon(): "ForAnyValue:ArnEquals": {"patata_2": ["test_2", "test_3"]}, } ) == StatementCondition( - ForAllValuesArnEqualsIfExists={"patata_1": "test_1"}, - ForAnyValueArnEquals={"patata_2": ["test_2", "test_3"]}, + ForAllValuesArnEqualsIfExists={"patata_1": "test_1"}, ForAnyValueArnEquals={"patata_2": ["test_2", "test_3"]} ) @@ -108,62 +107,14 @@ def test_build_evaluator_bool(function: str, arg_a: Any, arg_b: Any, params: Dic @pytest.mark.parametrize( "function, arg_a, arg_b, params, expected_output", [ - ( - "IpAddress", - "patata", - IPv4Network("203.0.113.0/24"), - {"patata": IPv4Network("203.0.113.12")}, - True, - ), - ( - "IpAddress", - "patata", - IPv4Network("203.0.113.0/24"), - {"patata": IPv4Network("10.1.1.1")}, - False, - ), - ( - "IpAddress", - "patata", - IPv4Network("203.0.113.10/32"), - {"patata": IPv4Network("203.0.113.10")}, - True, - ), - ( - "IpAddress", - "patata", - IPv4Network("203.0.113.10/32"), - {"patata": IPv4Network("10.1.1.1")}, - False, - ), - ( - "IpAddress", - "patata", - IPv4Network("172.16.0.0/12"), - {"patata": IPv4Network("203.0.113.10")}, - False, - ), - ( - "IpAddress", - "patata", - IPv4Network("172.16.0.0/12"), - {"patata": IPv4Network("172.16.0.13")}, - True, - ), - ( - "IpAddress", - "patata", - IPv4Network("172.16.0.0/12"), - {"patata": IPv4Network("172.16.0.0/12")}, - True, - ), - ( - "IpAddress", - "patata", - IPv4Network("172.16.0.0/12"), - {"patata": IPv4Network("203.0.113.10/32")}, - False, - ), + ("IpAddress", "patata", IPv4Network("203.0.113.0/24"), {"patata": IPv4Network("203.0.113.12")}, True), + ("IpAddress", "patata", IPv4Network("203.0.113.0/24"), {"patata": IPv4Network("10.1.1.1")}, False), + ("IpAddress", "patata", IPv4Network("203.0.113.10/32"), {"patata": IPv4Network("203.0.113.10")}, True), + ("IpAddress", "patata", IPv4Network("203.0.113.10/32"), {"patata": IPv4Network("10.1.1.1")}, False), + ("IpAddress", "patata", IPv4Network("172.16.0.0/12"), {"patata": IPv4Network("203.0.113.10")}, False), + ("IpAddress", "patata", IPv4Network("172.16.0.0/12"), {"patata": IPv4Network("172.16.0.13")}, True), + ("IpAddress", "patata", IPv4Network("172.16.0.0/12"), {"patata": IPv4Network("172.16.0.0/12")}, True), + ("IpAddress", "patata", IPv4Network("172.16.0.0/12"), {"patata": IPv4Network("203.0.113.10/32")}, False), ], ) def test_build_evaluator_ip_address(function: str, arg_a: Any, arg_b: Any, params: Dict, expected_output: bool): @@ -174,55 +125,13 @@ def test_build_evaluator_ip_address(function: str, arg_a: Any, arg_b: Any, param @pytest.mark.parametrize( "function, arg_a, arg_b, params, expected_output", [ - ( - "NotIpAddress", - "patata", - IPv4Network("203.0.113.0/24"), - {"patata": IPv4Network("203.0.113.12")}, - False, - ), - ( - "NotIpAddress", - "patata", - IPv4Network("203.0.113.0/24"), - {"patata": IPv4Network("10.1.1.1")}, - True, - ), - ( - "NotIpAddress", - "patata", - IPv4Network("203.0.113.10/32"), - {"patata": IPv4Network("203.0.113.10")}, - False, - ), - ( - "NotIpAddress", - "patata", - IPv4Network("203.0.113.10/32"), - {"patata": IPv4Network("10.1.1.1")}, - True, - ), - ( - "NotIpAddress", - "patata", - IPv4Network("172.16.0.0/12"), - {"patata": IPv4Network("203.0.113.10")}, - True, - ), - ( - "NotIpAddress", - "patata", - IPv4Network("172.16.0.0/12"), - {"patata": IPv4Network("172.16.0.13")}, - False, - ), - ( - "NotIpAddress", - "patata", - IPv4Network("172.0.0.0/8"), - {"patata": IPv4Network("172.0.0.0/8")}, - False, - ), + ("NotIpAddress", "patata", IPv4Network("203.0.113.0/24"), {"patata": IPv4Network("203.0.113.12")}, False), + ("NotIpAddress", "patata", IPv4Network("203.0.113.0/24"), {"patata": IPv4Network("10.1.1.1")}, True), + ("NotIpAddress", "patata", IPv4Network("203.0.113.10/32"), {"patata": IPv4Network("203.0.113.10")}, False), + ("NotIpAddress", "patata", IPv4Network("203.0.113.10/32"), {"patata": IPv4Network("10.1.1.1")}, True), + ("NotIpAddress", "patata", IPv4Network("172.16.0.0/12"), {"patata": IPv4Network("203.0.113.10")}, True), + ("NotIpAddress", "patata", IPv4Network("172.16.0.0/12"), {"patata": IPv4Network("172.16.0.13")}, False), + ("NotIpAddress", "patata", IPv4Network("172.0.0.0/8"), {"patata": IPv4Network("172.0.0.0/8")}, False), ], ) def test_build_evaluator_not_ip_address(function: str, arg_a: Any, arg_b: Any, params: Dict, expected_output: bool): @@ -246,10 +155,7 @@ def test_build_evaluator_null(function: str, arg_a: Any, arg_b: Any, params: Dic @pytest.mark.parametrize( "function, arg_a, arg_b, params, expected_output", - [ - ("NumericEquals", "patata", 1, {"patata": 1}, True), - ("NumericEquals", "patata", 1, {"patata": 2}, False), - ], + [("NumericEquals", "patata", 1, {"patata": 1}, True), ("NumericEquals", "patata", 1, {"patata": 2}, False)], ) def test_build_evaluator_numeric_equals(function: str, arg_a: Any, arg_b: Any, params: Dict, expected_output: bool): node = build_evaluator(function, arg_a, arg_b) @@ -258,10 +164,7 @@ def test_build_evaluator_numeric_equals(function: str, arg_a: Any, arg_b: Any, p @pytest.mark.parametrize( "function, arg_a, arg_b, params, expected_output", - [ - ("NumericNotEquals", "patata", 1, {"patata": 1}, False), - ("NumericNotEquals", "patata", 1, {"patata": 2}, True), - ], + [("NumericNotEquals", "patata", 1, {"patata": 1}, False), ("NumericNotEquals", "patata", 1, {"patata": 2}, True)], ) def test_build_evaluator_numeric_not_equals(function: str, arg_a: Any, arg_b: Any, params: Dict, expected_output: bool): node = build_evaluator(function, arg_a, arg_b) @@ -329,20 +232,8 @@ def test_build_evaluator_numeric_greater_than_equals( @pytest.mark.parametrize( "function, arg_a, arg_b, params, expected_output", [ - ( - "DateEquals", - "patata", - datetime_in_the_present(), - {"patata": datetime_in_the_present()}, - True, - ), - ( - "DateEquals", - "patata", - datetime_in_the_present(), - {"patata": datetime_in_the_past()}, - False, - ), + ("DateEquals", "patata", datetime_in_the_present(), {"patata": datetime_in_the_present()}, True), + ("DateEquals", "patata", datetime_in_the_present(), {"patata": datetime_in_the_past()}, False), ], ) def test_build_evaluator_date_equals(function: str, arg_a: Any, arg_b: Any, params: Dict, expected_output: bool): @@ -353,20 +244,8 @@ def test_build_evaluator_date_equals(function: str, arg_a: Any, arg_b: Any, para @pytest.mark.parametrize( "function, arg_a, arg_b, params, expected_output", [ - ( - "DateNotEquals", - "patata", - datetime_in_the_present(), - {"patata": datetime_in_the_present()}, - False, - ), - ( - "DateNotEquals", - "patata", - datetime_in_the_present(), - {"patata": datetime_in_the_past()}, - True, - ), + ("DateNotEquals", "patata", datetime_in_the_present(), {"patata": datetime_in_the_present()}, False), + ("DateNotEquals", "patata", datetime_in_the_present(), {"patata": datetime_in_the_past()}, True), ], ) def test_build_evaluator_date_not_equals(function: str, arg_a: Any, arg_b: Any, params: Dict, expected_output: bool): @@ -377,27 +256,9 @@ def test_build_evaluator_date_not_equals(function: str, arg_a: Any, arg_b: Any, @pytest.mark.parametrize( "function, arg_a, arg_b, params, expected_output", [ - ( - "DateLessThan", - "patata", - datetime_in_the_present(), - {"patata": datetime_in_the_present()}, - False, - ), - ( - "DateLessThan", - "patata", - datetime_in_the_present(), - {"patata": datetime_in_the_future()}, - False, - ), - ( - "DateLessThan", - "patata", - datetime_in_the_future(), - {"patata": datetime_in_the_present()}, - True, - ), + ("DateLessThan", "patata", datetime_in_the_present(), {"patata": datetime_in_the_present()}, False), + ("DateLessThan", "patata", datetime_in_the_present(), {"patata": datetime_in_the_future()}, False), + ("DateLessThan", "patata", datetime_in_the_future(), {"patata": datetime_in_the_present()}, True), ], ) def test_build_evaluator_date_less_than(function: str, arg_a: Any, arg_b: Any, params: Dict, expected_output: bool): @@ -408,27 +269,9 @@ def test_build_evaluator_date_less_than(function: str, arg_a: Any, arg_b: Any, p @pytest.mark.parametrize( "function, arg_a, arg_b, params, expected_output", [ - ( - "DateLessThanEquals", - "patata", - datetime_in_the_present(), - {"patata": datetime_in_the_present()}, - True, - ), - ( - "DateLessThanEquals", - "patata", - datetime_in_the_present(), - {"patata": datetime_in_the_future()}, - False, - ), - ( - "DateLessThanEquals", - "patata", - datetime_in_the_future(), - {"patata": datetime_in_the_present()}, - True, - ), + ("DateLessThanEquals", "patata", datetime_in_the_present(), {"patata": datetime_in_the_present()}, True), + ("DateLessThanEquals", "patata", datetime_in_the_present(), {"patata": datetime_in_the_future()}, False), + ("DateLessThanEquals", "patata", datetime_in_the_future(), {"patata": datetime_in_the_present()}, True), ], ) def test_build_evaluator_date_less_than_equals( @@ -441,27 +284,9 @@ def test_build_evaluator_date_less_than_equals( @pytest.mark.parametrize( "function, arg_a, arg_b, params, expected_output", [ - ( - "DateGreaterThan", - "patata", - datetime_in_the_present(), - {"patata": datetime_in_the_present()}, - False, - ), - ( - "DateGreaterThan", - "patata", - datetime_in_the_future(), - {"patata": datetime_in_the_present()}, - False, - ), - ( - "DateGreaterThan", - "patata", - datetime_in_the_present(), - {"patata": datetime_in_the_future()}, - True, - ), + ("DateGreaterThan", "patata", datetime_in_the_present(), {"patata": datetime_in_the_present()}, False), + ("DateGreaterThan", "patata", datetime_in_the_future(), {"patata": datetime_in_the_present()}, False), + ("DateGreaterThan", "patata", datetime_in_the_present(), {"patata": datetime_in_the_future()}, True), ], ) def test_build_evaluator_date_greater_than(function: str, arg_a: Any, arg_b: Any, params: Dict, expected_output: bool): @@ -472,27 +297,9 @@ def test_build_evaluator_date_greater_than(function: str, arg_a: Any, arg_b: Any @pytest.mark.parametrize( "function, arg_a, arg_b, params, expected_output", [ - ( - "DateGreaterThanEquals", - "patata", - datetime_in_the_present(), - {"patata": datetime_in_the_present()}, - True, - ), - ( - "DateGreaterThanEquals", - "patata", - datetime_in_the_future(), - {"patata": datetime_in_the_present()}, - False, - ), - ( - "DateGreaterThanEquals", - "patata", - datetime_in_the_present(), - {"patata": datetime_in_the_future()}, - True, - ), + ("DateGreaterThanEquals", "patata", datetime_in_the_present(), {"patata": datetime_in_the_present()}, True), + ("DateGreaterThanEquals", "patata", datetime_in_the_future(), {"patata": datetime_in_the_present()}, False), + ("DateGreaterThanEquals", "patata", datetime_in_the_present(), {"patata": datetime_in_the_future()}, True), ], ) def test_build_evaluator_date_greater_than_equals( @@ -697,20 +504,8 @@ def test_build_evaluator_arn_not_like(function: str, arg_a: Any, arg_b: Any, par @pytest.mark.parametrize( "function, arg_a, arg_b, params, expected_output", [ - ( - "BinaryEquals", - "patata", - bytearray("AaAaA", "utf-8"), - {"patata": bytearray("AaAaA", "utf-8")}, - True, - ), - ( - "BinaryEquals", - "patata", - bytearray("AAAAA", "utf-8"), - {"patata": bytearray("aaaaa", "utf-8")}, - False, - ), + ("BinaryEquals", "patata", bytearray("AaAaA", "utf-8"), {"patata": bytearray("AaAaA", "utf-8")}, True), + ("BinaryEquals", "patata", bytearray("AAAAA", "utf-8"), {"patata": bytearray("aaaaa", "utf-8")}, False), ], ) def test_build_evaluator_binary_equals(function: str, arg_a: Any, arg_b: Any, params: Dict, expected_output: bool): @@ -728,46 +523,15 @@ def test_build_evaluator_binary_equals(function: str, arg_a: Any, arg_b: Any, pa ("StringEquals", {"patata": "2"}, {"patata": ["2"]}, False), ("StringEquals", {"patata": "2"}, {"patata": ["2", "1"]}, False), ("StringEquals", {"patata": ["1", "3"]}, {"patata": ["4"]}, False), - ( - "StringLike", - {"patata": ["sky*"]}, - {"patata": ["skyx", "skyscanner", "nope"]}, - True, - ), - ( - "StringLike", - {"patata": ["sky*"]}, - {"patata": ["nopeskyx", "nopeskyscanner", "nope"]}, - False, - ), - ( - "IpAddress", - {"patata": [IPv4Network("203.0.113.0/24")]}, - {"patata": IPv4Network("203.0.113.0/24")}, - True, - ), - ( - "IpAddress", - {"patata": [IPv4Network("203.0.113.0/24")]}, - {"patata": [IPv4Network("203.0.113.0/24")]}, - True, - ), + ("StringLike", {"patata": ["sky*"]}, {"patata": ["skyx", "skyscanner", "nope"]}, True), + ("StringLike", {"patata": ["sky*"]}, {"patata": ["nopeskyx", "nopeskyscanner", "nope"]}, False), + ("IpAddress", {"patata": [IPv4Network("203.0.113.0/24")]}, {"patata": IPv4Network("203.0.113.0/24")}, True), + ("IpAddress", {"patata": [IPv4Network("203.0.113.0/24")]}, {"patata": [IPv4Network("203.0.113.0/24")]}, True), ("IpAddress", {"patata": ["vpce-123456"]}, {"patata": ["vpce-123456"]}, False), - ( - "NotIpAddress", - {"patata": ["vpce-123456"]}, - {"patata": ["vpce-654321"]}, - False, - ), + ("NotIpAddress", {"patata": ["vpce-123456"]}, {"patata": ["vpce-654321"]}, False), ( "IpAddress", - { - "patata": [ - IPv4Network("203.0.113.0/24"), - IPv4Network("103.0.113.0/24"), - IPv4Network("85.0.113.0/24"), - ] - }, + {"patata": [IPv4Network("203.0.113.0/24"), IPv4Network("103.0.113.0/24"), IPv4Network("85.0.113.0/24")]}, {"patata": [IPv4Network("10.0.0.0/8")]}, False, ), @@ -819,19 +583,9 @@ def test_build_root_evaluator_for_all_values( ("ForAllValuesNumericEqualsIfExists", {"patata": 1}, {}, True), ("ForAllValuesNumericEqualsIfExists", {"patata": [1, 2, 3]}, {}, True), ("ForAllValuesNumericEqualsIfExists", {"patata": 1}, {"patata": 2}, False), - ( - "ForAllValuesNumericEqualsIfExists", - {"patata": [1, 2, 3]}, - {"patata": 4}, - False, - ), + ("ForAllValuesNumericEqualsIfExists", {"patata": [1, 2, 3]}, {"patata": 4}, False), ("ForAllValuesNumericEqualsIfExists", {"patata": 1}, {"patata": [2, 3]}, False), - ( - "ForAllValuesNumericEqualsIfExists", - {"patata": [1, 2, 3]}, - {"patata": [1, 4]}, - False, - ), + ("ForAllValuesNumericEqualsIfExists", {"patata": [1, 2, 3]}, {"patata": [1, 4]}, False), ], ) def test_build_root_evaluator_for_all_values_if_exists( @@ -847,12 +601,7 @@ def test_build_root_evaluator_for_all_values_if_exists( ("ForAnyValueNumericEquals", {"patata": 1}, {"patata": 1}, True), ("ForAnyValueNumericEquals", {"patata": [1, 2, 3]}, {"patata": 1}, True), ("ForAnyValueNumericEquals", {"patata": 1}, {"patata": [1, 2]}, True), - ( - "ForAnyValueNumericEquals", - {"patata": [1, 2, 3]}, - {"patata": [1, 2, 5, 6]}, - True, - ), + ("ForAnyValueNumericEquals", {"patata": [1, 2, 3]}, {"patata": [1, 2, 5, 6]}, True), ("ForAnyValueNumericEquals", {"patata": 1}, {"patata": 2}, False), ("ForAnyValueNumericEquals", {"patata": [1, 2, 3]}, {"patata": 4}, False), ("ForAnyValueNumericEquals", {"patata": 1}, {"patata": [2, 3]}, False), @@ -872,19 +621,9 @@ def test_build_root_evaluator_for_any_value( ("ForAnyValueNumericEqualsIfExists", {"patata": 1}, {}, True), ("ForAnyValueNumericEqualsIfExists", {"patata": [1, 2, 3]}, {}, True), ("ForAnyValueNumericEqualsIfExists", {"patata": 1}, {"patata": 2}, False), - ( - "ForAnyValueNumericEqualsIfExists", - {"patata": [1, 2, 3]}, - {"patata": 4}, - False, - ), + ("ForAnyValueNumericEqualsIfExists", {"patata": [1, 2, 3]}, {"patata": 4}, False), ("ForAnyValueNumericEqualsIfExists", {"patata": 1}, {"patata": [2, 3]}, False), - ( - "ForAnyValueNumericEqualsIfExists", - {"patata": [1, 2, 3]}, - {"patata": [4, 5, 6]}, - False, - ), + ("ForAnyValueNumericEqualsIfExists", {"patata": [1, 2, 3]}, {"patata": [4, 5, 6]}, False), ], ) def test_build_root_evaluator_for_any_value_if_exists( diff --git a/tests/resources/test_ec2_vpc_endpoint_policy.py b/tests/resources/test_ec2_vpc_endpoint_policy.py index 5848b80..eabb994 100644 --- a/tests/resources/test_ec2_vpc_endpoint_policy.py +++ b/tests/resources/test_ec2_vpc_endpoint_policy.py @@ -12,14 +12,7 @@ def ec2_vpc_endpoint_policy(): "Properties": { "PolicyDocument": { "Version": "2012-10-17", - "Statement": [ - { - "Effect": "Allow", - "Principal": "*", - "Action": ["s3:GetObject"], - "Resource": "*", - } - ], + "Statement": [{"Effect": "Allow", "Principal": "*", "Action": ["s3:GetObject"], "Resource": "*"}], }, "RouteTableIds": [{"Ref": "routetableA"}, {"Ref": "routetableB"}], "ServiceName": "com.amazonaws.eu-west-1.s3", @@ -64,7 +57,5 @@ def test_ec2_vpc_endpoint_policy_documents(ec2_vpc_endpoint_policy): ] -def test_ec2_vpc_endpoint_policy_documents_no_document( - ec2_vpc_endpoint_policy_no_policy_document, -): +def test_ec2_vpc_endpoint_policy_documents_no_document(ec2_vpc_endpoint_policy_no_policy_document): assert ec2_vpc_endpoint_policy_no_policy_document.policy_documents == [] diff --git a/tests/resources/test_es_domain.py b/tests/resources/test_es_domain.py index 6f2f97e..fca444d 100644 --- a/tests/resources/test_es_domain.py +++ b/tests/resources/test_es_domain.py @@ -28,12 +28,7 @@ def valid_es_domain_from_aws_documentation_examples(): }, "AdvancedOptions": {"rest.action.multi.allow_explicit_index": True}, "DomainName": "test", - "EBSOptions": { - "EBSEnabled": True, - "Iops": "0", - "VolumeSize": "20", - "VolumeType": "gp2", - }, + "EBSOptions": {"EBSEnabled": True, "Iops": "0", "VolumeSize": "20", "VolumeType": "gp2"}, "ElasticsearchClusterConfig": { "DedicatedMasterEnabled": True, "InstanceCount": "2", @@ -171,11 +166,7 @@ def test_raise_error_if_invalid_fields_in_resource(): ESDomain(**{"Type": "AWS::Elasticsearch::Domain", "Properties": {"DomainName": []}}) assert exc_info.value.errors() == [ - { - "loc": ("Properties", "DomainName"), - "msg": "str type expected", - "type": "type_error.str", - }, + {"loc": ("Properties", "DomainName"), "msg": "str type expected", "type": "type_error.str"}, { "loc": ("Properties", "DomainName", "__root__"), "msg": "FunctionDict should only have 1 key and be a function", diff --git a/tests/resources/test_iam_group.py b/tests/resources/test_iam_group.py index c76b831..ebcdd9f 100644 --- a/tests/resources/test_iam_group.py +++ b/tests/resources/test_iam_group.py @@ -20,11 +20,7 @@ def iam_group(): "Statement": [ { "Effect": "Allow", - "Action": [ - "lambda:AddPermission", - "ssm:SendCommand", - "kms:Decrypt", - ], + "Action": ["lambda:AddPermission", "ssm:SendCommand", "kms:Decrypt"], "Resource": "*", } ], @@ -44,8 +40,5 @@ def test_policies(iam_group): def test_iamgroup_policy_documents(iam_group): assert iam_group.policy_documents == [ - OptionallyNamedPolicyDocument( - name="BadPolicy", - policy_document=iam_group.Properties.Policies[0].PolicyDocument, - ) + OptionallyNamedPolicyDocument(name="BadPolicy", policy_document=iam_group.Properties.Policies[0].PolicyDocument) ] diff --git a/tests/resources/test_iam_managed_policy.py b/tests/resources/test_iam_managed_policy.py index 220a96b..9a9f8be 100644 --- a/tests/resources/test_iam_managed_policy.py +++ b/tests/resources/test_iam_managed_policy.py @@ -17,11 +17,7 @@ def iam_managed_policy(): "Statement": [ { "Effect": "Allow", - "Action": [ - "lambda:AddPermission", - "ssm:SendCommand", - "kms:Decrypt", - ], + "Action": ["lambda:AddPermission", "ssm:SendCommand", "kms:Decrypt"], "Resource": "*", } ], @@ -38,7 +34,6 @@ def test_policies(iam_managed_policy): def test_iam_managedpolicy_policy_documents(iam_managed_policy): assert iam_managed_policy.policy_documents == [ OptionallyNamedPolicyDocument( - name="ManagedPolicy", - policy_document=iam_managed_policy.Properties.PolicyDocument, + name="ManagedPolicy", policy_document=iam_managed_policy.Properties.PolicyDocument ) ] diff --git a/tests/resources/test_iam_policy.py b/tests/resources/test_iam_policy.py index 0bd0959..256d0ec 100644 --- a/tests/resources/test_iam_policy.py +++ b/tests/resources/test_iam_policy.py @@ -16,11 +16,7 @@ def iam_policy(): "Statement": [ { "Effect": "Allow", - "Action": [ - "lambda:AddPermission", - "ssm:SendCommand", - "kms:Decrypt", - ], + "Action": ["lambda:AddPermission", "ssm:SendCommand", "kms:Decrypt"], "Resource": "*", } ], diff --git a/tests/resources/test_iam_role.py b/tests/resources/test_iam_role.py index e0fc348..c987ec9 100644 --- a/tests/resources/test_iam_role.py +++ b/tests/resources/test_iam_role.py @@ -15,10 +15,7 @@ def iam_role(): "Version": "2012-10-17", "Statement": { "Effect": "Allow", - "Principal": { - "Service": ["ec2.amazonaws.com"], - "AWS": "arn:aws:iam::111111111111:root", - }, + "Principal": {"Service": ["ec2.amazonaws.com"], "AWS": "arn:aws:iam::111111111111:root"}, "Action": ["sts:AssumeRole"], "Condition": { "StringLike": { diff --git a/tests/resources/test_iam_user.py b/tests/resources/test_iam_user.py index 9d465c5..d3d2e70 100644 --- a/tests/resources/test_iam_user.py +++ b/tests/resources/test_iam_user.py @@ -12,10 +12,7 @@ def iam_user(): "Properties": { "Path": "/", "UserName": "TestUser", - "LoginProfile": { - "Password": "ThisMyBestP@ssword", - "PasswordResetRequired": False, - }, + "LoginProfile": {"Password": "ThisMyBestP@ssword", "PasswordResetRequired": False}, "Policies": [ { "PolicyName": "BadPolicy", @@ -24,11 +21,7 @@ def iam_user(): "Statement": [ { "Effect": "Allow", - "Action": [ - "lambda:AddPermission", - "ssm:SendCommand", - "kms:Decrypt", - ], + "Action": ["lambda:AddPermission", "ssm:SendCommand", "kms:Decrypt"], "Resource": "*", } ], @@ -47,10 +40,7 @@ def test_policies(iam_user): def test_iam_role_policy_documents(iam_user): assert iam_user.policy_documents == [ - OptionallyNamedPolicyDocument( - name="BadPolicy", - policy_document=iam_user.Properties.Policies[0].PolicyDocument, - ) + OptionallyNamedPolicyDocument(name="BadPolicy", policy_document=iam_user.Properties.Policies[0].PolicyDocument) ] diff --git a/tests/resources/test_opensearch_domain.py b/tests/resources/test_opensearch_domain.py index 9c6a5c5..6ec61d5 100644 --- a/tests/resources/test_opensearch_domain.py +++ b/tests/resources/test_opensearch_domain.py @@ -42,12 +42,7 @@ def valid_opensearch_domain_from_aws_documentation_examples(): "DedicatedMasterCount": "3", }, "DomainName": "test", - "EBSOptions": { - "EBSEnabled": True, - "Iops": "0", - "VolumeSize": "20", - "VolumeType": "gp2", - }, + "EBSOptions": {"EBSEnabled": True, "Iops": "0", "VolumeSize": "20", "VolumeType": "gp2"}, "EngineVersion": "OpenSearch_1.0", "LogPublishingOptions": { "ES_APPLICATION_LOGS": { @@ -89,9 +84,7 @@ def valid_opensearch_domain_with_access_policies(): ) -def test_valid_empty_opensearch_domain_resource_can_be_built( - valid_empty_opensearch_domain, -): +def test_valid_empty_opensearch_domain_resource_can_be_built(valid_empty_opensearch_domain): assert valid_empty_opensearch_domain.Type == "AWS::OpenSearchService::Domain" assert valid_empty_opensearch_domain.Properties.AccessPolicies is None assert valid_empty_opensearch_domain.Properties.AdvancedOptions is None @@ -203,19 +196,10 @@ def test_valid_opensearch_domain_from_aws_documentation_examples_resource_can_be def test_raise_error_if_invalid_fields_in_resource(): with pytest.raises(ValidationError) as exc_info: - OpenSearchDomain( - **{ - "Type": "AWS::OpenSearchService::Domain", - "Properties": {"DomainName": []}, - } - ) + OpenSearchDomain(**{"Type": "AWS::OpenSearchService::Domain", "Properties": {"DomainName": []}}) assert exc_info.value.errors() == [ - { - "loc": ("Properties", "DomainName"), - "msg": "str type expected", - "type": "type_error.str", - }, + {"loc": ("Properties", "DomainName"), "msg": "str type expected", "type": "type_error.str"}, { "loc": ("Properties", "DomainName", "__root__"), "msg": "FunctionDict should only have 1 key and be a function", @@ -229,9 +213,7 @@ def test_raise_error_if_invalid_fields_in_resource(): ] -def test_can_obtain_policy_documents_from_inherited_method( - valid_opensearch_domain_with_access_policies, -): +def test_can_obtain_policy_documents_from_inherited_method(valid_opensearch_domain_with_access_policies): assert len(valid_opensearch_domain_with_access_policies.policy_documents) == 1 assert valid_opensearch_domain_with_access_policies.policy_documents == [ OptionallyNamedPolicyDocument( diff --git a/tests/resources/test_s3_bucket.py b/tests/resources/test_s3_bucket.py index 0f416cc..38ae563 100644 --- a/tests/resources/test_s3_bucket.py +++ b/tests/resources/test_s3_bucket.py @@ -20,10 +20,7 @@ def valid_s3_bucket(): "Id": "MyRule1", "Status": "Enabled", "Prefix": "MyPrefix", - "Destination": { - "Bucket": "arn:aws:s3:::my-replication-bucket", - "StorageClass": "STANDARD", - }, + "Destination": {"Bucket": "arn:aws:s3:::my-replication-bucket", "StorageClass": "STANDARD"}, }, { "Status": "Enabled", @@ -49,11 +46,7 @@ def test_extra_fields_not_allowed_s3_bucket(): S3Bucket( **{ "Type": "AWS::S3::Bucket", - "Properties": { - "AccelerateConfiguration": "None", - "AnalyticsConfigurations": {"a": "b"}, - "foo": "bar", - }, + "Properties": {"AccelerateConfiguration": "None", "AnalyticsConfigurations": {"a": "b"}, "foo": "bar"}, } ) @@ -78,11 +71,7 @@ def test_extra_fields_not_allowed_s3_bucket(): "msg": "FunctionDict should only have 1 key and be a function", "type": "value_error", }, - { - "loc": ("Properties", "foo"), - "msg": "extra fields not permitted", - "type": "value_error.extra", - }, + {"loc": ("Properties", "foo"), "msg": "extra fields not permitted", "type": "value_error.extra"}, { "loc": ("Properties", "__root__"), "msg": "FunctionDict should only have 1 key and be a function", diff --git a/tests/resources/test_security_group.py b/tests/resources/test_security_group.py index 614defa..f0adf1d 100644 --- a/tests/resources/test_security_group.py +++ b/tests/resources/test_security_group.py @@ -10,22 +10,8 @@ def security_group(): "Type": "AWS::EC2::SecurityGroup", "Properties": { "GroupDescription": "some_group_desc", - "SecurityGroupIngress": [ - { - "CidrIp": "10.1.2.3/32", - "FromPort": 34, - "ToPort": 36, - "IpProtocol": "tcp", - } - ], - "SecurityGroupEgress": [ - { - "CidrIp": "10.1.2.3/32", - "FromPort": 34, - "ToPort": 36, - "IpProtocol": "tcp", - } - ], + "SecurityGroupIngress": [{"CidrIp": "10.1.2.3/32", "FromPort": 34, "ToPort": 36, "IpProtocol": "tcp"}], + "SecurityGroupEgress": [{"CidrIp": "10.1.2.3/32", "FromPort": 34, "ToPort": 36, "IpProtocol": "tcp"}], "Tags": [{"Key": "a", "Value": "b"}], "VpcId": "vpc-9f8e9dfa", }, diff --git a/tests/resources/test_sns_topic_policy.py b/tests/resources/test_sns_topic_policy.py index 4fbf146..a3372d5 100644 --- a/tests/resources/test_sns_topic_policy.py +++ b/tests/resources/test_sns_topic_policy.py @@ -15,13 +15,7 @@ def sns_topic_policy(): "Id": "MyTopicPolicy", "Version": "2012-10-17", "Statement": [ - { - "Sid": "Stmt1", - "Effect": "Allow", - "Principal": "*", - "Action": "sns:Publish", - "Resource": "*", - }, + {"Sid": "Stmt1", "Effect": "Allow", "Principal": "*", "Action": "sns:Publish", "Resource": "*"}, { "Sid": "Stmt2", "Effect": "Allow", diff --git a/tests/test_action_expander.py b/tests/test_action_expander.py index 6313848..70e1873 100644 --- a/tests/test_action_expander.py +++ b/tests/test_action_expander.py @@ -68,13 +68,7 @@ def test_expand_not_actions(action, reverse_expected_output): def test_expand_actions_scenario_1(): template = { "AWSTemplateFormatVersion": "2010-09-09", - "Parameters": { - "StarParameter": { - "Type": "String", - "Default": "*", - "Description": "Star Param", - } - }, + "Parameters": {"StarParameter": {"Type": "String", "Default": "*", "Description": "Star Param"}}, "Resources": { "rootRole": { "Type": "AWS::IAM::Role", @@ -120,12 +114,7 @@ def test_resolve_scenario_2(): template = { "AWSTemplateFormatVersion": "2010-09-09", "Description": "IAM role for Lambda", - "Parameters": { - "LambdaFunctionName": { - "Description": "Name of the lambda function", - "Type": "String", - } - }, + "Parameters": {"LambdaFunctionName": {"Description": "Name of the lambda function", "Type": "String"}}, "Resources": { "lambdaRole": { "Properties": { @@ -164,10 +153,7 @@ def test_resolve_scenario_2(): "PolicyDocument": { "Statement": [ { - "Action": [ - "xray:PutTraceSegments", - "xray:PutTelemetryRecords", - ], + "Action": ["xray:PutTraceSegments", "xray:PutTelemetryRecords"], "Effect": "Allow", "Resource": ["*"], } @@ -180,11 +166,7 @@ def test_resolve_scenario_2(): "PolicyDocument": { "Statement": [ { - "Action": [ - "logs:CreateLogGroup", - "logs:CreateLogStream", - "logs:PutLogEvents", - ], + "Action": ["logs:CreateLogGroup", "logs:CreateLogStream", "logs:PutLogEvents"], "Effect": "Allow", "Resource": ["*"], } diff --git a/tests/test_cf_model.py b/tests/test_cf_model.py index 4e05f6c..acccc85 100644 --- a/tests/test_cf_model.py +++ b/tests/test_cf_model.py @@ -34,20 +34,12 @@ def test_resources_filtered_by_type(): model = CFModel(Resources={**generic_resource, **user}) assert model.resources_filtered_by_type(("Resource type",)) == generic_resource - assert model.resources_filtered_by_type(("Resource type", IAMUser)) == { - **generic_resource, - **user, - } + assert model.resources_filtered_by_type(("Resource type", IAMUser)) == {**generic_resource, **user} assert model.resources_filtered_by_type((IAMUser,)) == user def test_complex_metadata_schema(): - metadata = { - "Foo": "a_string_value", - "Bar": 1, - "Fizz": ["a", "list"], - "Buzz": {"another": "dict"}, - } + metadata = {"Foo": "a_string_value", "Bar": 1, "Fizz": ["a", "list"], "Buzz": {"another": "dict"}} model = CFModel(Metadata=metadata) assert model.Metadata == metadata diff --git a/tests/test_generic.py b/tests/test_generic.py index 70242e7..70184ff 100644 --- a/tests/test_generic.py +++ b/tests/test_generic.py @@ -48,14 +48,8 @@ def test_recursive(): ("2001:db00::0/120", IPv6Network("2001:db00::0/120")), (["2001:db00::0/120"], [IPv6Network("2001:db00::0/120")]), # ResolvableArnOrList - ( - "arn:aws:iam::123456789012:user/test-user", - "arn:aws:iam::123456789012:user/test-user", - ), - ( - ["arn:aws:iam::123456789012:user/test-user"], - ["arn:aws:iam::123456789012:user/test-user"], - ), + ("arn:aws:iam::123456789012:user/test-user", "arn:aws:iam::123456789012:user/test-user"), + (["arn:aws:iam::123456789012:user/test-user"], ["arn:aws:iam::123456789012:user/test-user"]), # ResolvableStrOrList ("potato", "potato"), (["potato"], ["potato"]), diff --git a/tests/test_parameter.py b/tests/test_parameter.py index 3b0e19d..4ebfec3 100644 --- a/tests/test_parameter.py +++ b/tests/test_parameter.py @@ -6,11 +6,7 @@ @pytest.mark.parametrize( "param, provided_value, expected", [ - ( - {"Type": "String", "NoEcho": True, "Default": "A"}, - None, - Parameter.NO_ECHO_WITH_DEFAULT, - ), + ({"Type": "String", "NoEcho": True, "Default": "A"}, None, Parameter.NO_ECHO_WITH_DEFAULT), ({"Type": "String", "NoEcho": True}, None, Parameter.NO_ECHO_NO_DEFAULT), ({"Type": "String", "NoEcho": False, "Default": "A"}, None, "A"), ({"Type": "String", "NoEcho": False}, None, None), @@ -20,16 +16,8 @@ ({"Type": "List", "Default": "1,2,3"}, None, ["1", "2", "3"]), ({"Type": "CommaDelimitedList", "Default": "a,b,c"}, None, ["a", "b", "c"]), # Tests with provided value - ( - {"Type": "String", "NoEcho": True, "Default": "A"}, - "SuperSecret", - Parameter.NO_ECHO_WITH_VALUE, - ), - ( - {"Type": "String", "NoEcho": True}, - "SuperSecret", - Parameter.NO_ECHO_WITH_VALUE, - ), + ({"Type": "String", "NoEcho": True, "Default": "A"}, "SuperSecret", Parameter.NO_ECHO_WITH_VALUE), + ({"Type": "String", "NoEcho": True}, "SuperSecret", Parameter.NO_ECHO_WITH_VALUE), ({"Type": "String", "NoEcho": False, "Default": "A"}, "B", "B"), ({"Type": "String", "NoEcho": False}, "B", "B"), ({"Type": "String", "Default": "abc"}, "B", "B"), diff --git a/tests/test_resolver.py b/tests/test_resolver.py index ae081c4..34f4be2 100644 --- a/tests/test_resolver.py +++ b/tests/test_resolver.py @@ -10,8 +10,7 @@ @pytest.mark.parametrize( - "function, expected_output", - [({"Ref": "abc"}, "ABC"), ({"Ref": "potato"}, "UNDEFINED_PARAM_potato")], + "function, expected_output", [({"Ref": "abc"}, "ABC"), ({"Ref": "potato"}, "UNDEFINED_PARAM_potato")] ) def test_ref(function, expected_output): parameters = {"abc": "ABC"} @@ -23,10 +22,7 @@ def test_ref(function, expected_output): @pytest.mark.parametrize( "function, expected_output", - [ - ({"Fn::ImportValue": "abc"}, "ABC"), - ({"Fn::ImportValue": "potato"}, "UNDEFINED_PARAM_potato"), - ], + [({"Fn::ImportValue": "abc"}, "ABC"), ({"Fn::ImportValue": "potato"}, "UNDEFINED_PARAM_potato")], ) def test_import_value(function, expected_output): parameters = {"abc": "ABC"} @@ -42,12 +38,7 @@ def test_import_value(function, expected_output): ({"Fn::Join": ["", []]}, ""), ({"Fn::Join": ["", ["aws"]]}, "aws"), ( - { - "Fn::Join": [ - "", - ["arn:", "aws", ":s3:::elasticbeanstalk-*-", "1234567890"], - ] - }, + {"Fn::Join": ["", ["arn:", "aws", ":s3:::elasticbeanstalk-*-", "1234567890"]]}, "arn:aws:s3:::elasticbeanstalk-*-1234567890", ), ], @@ -63,18 +54,9 @@ def test_join(function, expected_output): @pytest.mark.parametrize( "function, expected_output", [ - ( - {"Fn::FindInMap": ["RegionMap", "eu-west-1", "HVM64"]}, - "UNDEFINED_MAPPING_RegionMap_eu-west-1_HVM64", - ), - ( - {"Fn::FindInMap": ["RegionMap", "us-east-1", "HVM128"]}, - "UNDEFINED_MAPPING_RegionMap_us-east-1_HVM128", - ), - ( - {"Fn::FindInMap": ["RegionMap", "us-east-1", "HVM64"]}, - "ami-0ff8a91507f77f867", - ), + ({"Fn::FindInMap": ["RegionMap", "eu-west-1", "HVM64"]}, "UNDEFINED_MAPPING_RegionMap_eu-west-1_HVM64"), + ({"Fn::FindInMap": ["RegionMap", "us-east-1", "HVM128"]}, "UNDEFINED_MAPPING_RegionMap_us-east-1_HVM128"), + ({"Fn::FindInMap": ["RegionMap", "us-east-1", "HVM64"]}, "ami-0ff8a91507f77f867"), ], ) def test_find_in_map(function, expected_output): @@ -89,10 +71,7 @@ def test_find_in_map(function, expected_output): "function, expected_output", [ ({"Fn::Sub": "www.skyscanner.net"}, "www.skyscanner.net"), - ( - {"Fn::Sub": ["www.${Domain}", {"Domain": "skyscanner.net"}]}, - "www.skyscanner.net", - ), + ({"Fn::Sub": ["www.${Domain}", {"Domain": "skyscanner.net"}]}, "www.skyscanner.net"), ({"Fn::Sub": "---${abc}---"}, "---ABC---"), ({"Fn::Sub": ["--${abc}-${def}--", {"def": "DEF"}]}, "--ABC-DEF--"), ], @@ -138,8 +117,7 @@ def test_split(function, expected_output): @pytest.mark.parametrize( - "function, expected_output", - [({"Fn::If": ["A", "a", "b"]}, "a"), ({"Fn::If": ["B", "a", "b"]}, "b")], + "function, expected_output", [({"Fn::If": ["A", "a", "b"]}, "a"), ({"Fn::If": ["B", "a", "b"]}, "b")] ) def test_if(function, expected_output): parameters = {} @@ -195,10 +173,7 @@ def test_or(function, expected_output): assert resolve(function, parameters, mappings, conditions) == expected_output -@pytest.mark.parametrize( - "function, expected_output", - [({"Fn::Not": [True]}, False), ({"Fn::Not": [False]}, True)], -) +@pytest.mark.parametrize("function, expected_output", [({"Fn::Not": [True]}, False), ({"Fn::Not": [False]}, True)]) def test_not(function, expected_output): parameters = {} mappings = {} @@ -247,8 +222,7 @@ def test_base64(function, expected_output): @pytest.mark.parametrize( - "function, expected_output", - [({"Fn::GetAtt": ["logicalNameOfResource", "attributeName"]}, "GETATT")], + "function, expected_output", [({"Fn::GetAtt": ["logicalNameOfResource", "attributeName"]}, "GETATT")] ) def test_get_attr(function, expected_output): parameters = {} @@ -286,25 +260,8 @@ def test_condition(function, expected_output): (1, ["HasAtLeast1Tags"]), (2, ["HasAtLeast1Tags", "HasAtLeast2Tags"]), (3, ["HasAtLeast1Tags", "HasAtLeast2Tags", "HasAtLeast3Tags"]), - ( - 4, - [ - "HasAtLeast1Tags", - "HasAtLeast2Tags", - "HasAtLeast3Tags", - "HasAtLeast4Tags", - ], - ), - ( - 5, - [ - "HasAtLeast1Tags", - "HasAtLeast2Tags", - "HasAtLeast3Tags", - "HasAtLeast4Tags", - "HasAtLeast5Tags", - ], - ), + (4, ["HasAtLeast1Tags", "HasAtLeast2Tags", "HasAtLeast3Tags", "HasAtLeast4Tags"]), + (5, ["HasAtLeast1Tags", "HasAtLeast2Tags", "HasAtLeast3Tags", "HasAtLeast4Tags", "HasAtLeast5Tags"]), ( 6, [ @@ -381,58 +338,31 @@ def test_resolve_recursive_conditions(num_custom_tags, expected): "Conditions": { "HasAtLeast10Tags": {"Fn::Equals": [{"Ref": "NumCustomTags"}, 10]}, # this is the condition stopper "HasAtLeast9Tags": { - "Fn::Or": [ - {"Fn::Equals": [{"Ref": "NumCustomTags"}, 9]}, - {"Condition": "HasAtLeast10Tags"}, - ] + "Fn::Or": [{"Fn::Equals": [{"Ref": "NumCustomTags"}, 9]}, {"Condition": "HasAtLeast10Tags"}] }, "HasAtLeast8Tags": { - "Fn::Or": [ - {"Fn::Equals": [{"Ref": "NumCustomTags"}, 8]}, - {"Condition": "HasAtLeast9Tags"}, - ] + "Fn::Or": [{"Fn::Equals": [{"Ref": "NumCustomTags"}, 8]}, {"Condition": "HasAtLeast9Tags"}] }, "HasAtLeast7Tags": { - "Fn::Or": [ - {"Fn::Equals": [{"Ref": "NumCustomTags"}, 7]}, - {"Condition": "HasAtLeast8Tags"}, - ] + "Fn::Or": [{"Fn::Equals": [{"Ref": "NumCustomTags"}, 7]}, {"Condition": "HasAtLeast8Tags"}] }, "HasAtLeast6Tags": { - "Fn::Or": [ - {"Fn::Equals": [{"Ref": "NumCustomTags"}, 6]}, - {"Condition": "HasAtLeast7Tags"}, - ] + "Fn::Or": [{"Fn::Equals": [{"Ref": "NumCustomTags"}, 6]}, {"Condition": "HasAtLeast7Tags"}] }, "HasAtLeast5Tags": { - "Fn::Or": [ - {"Fn::Equals": [{"Ref": "NumCustomTags"}, 5]}, - {"Condition": "HasAtLeast6Tags"}, - ] + "Fn::Or": [{"Fn::Equals": [{"Ref": "NumCustomTags"}, 5]}, {"Condition": "HasAtLeast6Tags"}] }, "HasAtLeast4Tags": { - "Fn::Or": [ - {"Fn::Equals": [{"Ref": "NumCustomTags"}, 4]}, - {"Condition": "HasAtLeast5Tags"}, - ] + "Fn::Or": [{"Fn::Equals": [{"Ref": "NumCustomTags"}, 4]}, {"Condition": "HasAtLeast5Tags"}] }, "HasAtLeast3Tags": { - "Fn::Or": [ - {"Fn::Equals": [{"Ref": "NumCustomTags"}, 3]}, - {"Condition": "HasAtLeast4Tags"}, - ] + "Fn::Or": [{"Fn::Equals": [{"Ref": "NumCustomTags"}, 3]}, {"Condition": "HasAtLeast4Tags"}] }, "HasAtLeast2Tags": { - "Fn::Or": [ - {"Fn::Equals": [{"Ref": "NumCustomTags"}, 2]}, - {"Condition": "HasAtLeast3Tags"}, - ] + "Fn::Or": [{"Fn::Equals": [{"Ref": "NumCustomTags"}, 2]}, {"Condition": "HasAtLeast3Tags"}] }, "HasAtLeast1Tags": { - "Fn::Or": [ - {"Fn::Equals": [{"Ref": "NumCustomTags"}, 1]}, - {"Condition": "HasAtLeast2Tags"}, - ] + "Fn::Or": [{"Fn::Equals": [{"Ref": "NumCustomTags"}, 1]}, {"Condition": "HasAtLeast2Tags"}] }, }, "Resources": {}, @@ -477,15 +407,7 @@ def test_join_and_ref(): mappings = {} conditions = {} function = { - "Fn::Join": [ - "", - [ - "arn:", - {"Ref": "Partition"}, - ":s3:::elasticbeanstalk-*-", - {"Ref": "AWS::AccountId"}, - ], - ] + "Fn::Join": ["", ["arn:", {"Ref": "Partition"}, ":s3:::elasticbeanstalk-*-", {"Ref": "AWS::AccountId"}]] } assert resolve(function, parameters, mappings, conditions) == "arn:patata:s3:::elasticbeanstalk-*-1234567890" @@ -553,10 +475,7 @@ def test_resolve_include_resource_when_condition_is_true_or_doesnt_exist(conditi "Type": "AWS::IAM::Role", "Condition": "testCondition", "Properties": { - "AssumeRolePolicyDocument": { - "Version": "2012-10-17", - "Statement": [], - }, + "AssumeRolePolicyDocument": {"Version": "2012-10-17", "Statement": []}, "Path": "/", "Policies": [], }, @@ -583,10 +502,7 @@ def test_resolve_include_resource_when_condition_is_not_present(): "test_resource_id": { "Type": "AWS::IAM::Role", "Properties": { - "AssumeRolePolicyDocument": { - "Version": "2012-10-17", - "Statement": [], - }, + "AssumeRolePolicyDocument": {"Version": "2012-10-17", "Statement": []}, "Path": "/", "Policies": [], }, @@ -600,13 +516,7 @@ def test_resolve_include_resource_when_condition_is_not_present(): def test_resolve_scenario_1(): template = { "AWSTemplateFormatVersion": "2010-09-09", - "Parameters": { - "StarParameter": { - "Type": "String", - "Default": "*", - "Description": "Star Param", - } - }, + "Parameters": {"StarParameter": {"Type": "String", "Default": "*", "Description": "Star Param"}}, "Resources": { "rootRole": { "Type": "AWS::IAM::Role", @@ -656,12 +566,7 @@ def test_resolve_scenario_2(): template = { "AWSTemplateFormatVersion": "2010-09-09", "Description": "IAM role for Lambda", - "Parameters": { - "LambdaFunctionName": { - "Description": "Name of the lambda function", - "Type": "String", - } - }, + "Parameters": {"LambdaFunctionName": {"Description": "Name of the lambda function", "Type": "String"}}, "Resources": { "lambdaRole": { "Properties": { @@ -700,10 +605,7 @@ def test_resolve_scenario_2(): "PolicyDocument": { "Statement": [ { - "Action": [ - "xray:PutTraceSegments", - "xray:PutTelemetryRecords", - ], + "Action": ["xray:PutTraceSegments", "xray:PutTelemetryRecords"], "Effect": "Allow", "Resource": ["*"], } @@ -716,11 +618,7 @@ def test_resolve_scenario_2(): "PolicyDocument": { "Statement": [ { - "Action": [ - "logs:CreateLogGroup", - "logs:CreateLogStream", - "logs:PutLogEvents", - ], + "Action": ["logs:CreateLogGroup", "logs:CreateLogStream", "logs:PutLogEvents"], "Effect": "Allow", "Resource": ["*"], } @@ -759,20 +657,10 @@ def test_resolve_scenario_3(): "GroupDescription": "Allow http to client host", "VpcId": "VPCID", "SecurityGroupIngress": [ - { - "IpProtocol": "tcp", - "FromPort": 80, - "ToPort": 80, - "CidrIp": {"Ref": "IPValueIngress"}, - } + {"IpProtocol": "tcp", "FromPort": 80, "ToPort": 80, "CidrIp": {"Ref": "IPValueIngress"}} ], "SecurityGroupEgress": [ - { - "IpProtocol": "tcp", - "FromPort": 80, - "ToPort": 80, - "CidrIp": {"Ref": "IPValueEgress"}, - } + {"IpProtocol": "tcp", "FromPort": 80, "ToPort": 80, "CidrIp": {"Ref": "IPValueEgress"}} ], }, } @@ -844,11 +732,7 @@ def test_resolve_booleans_on_conditions_for_modeled_resource(): "Id": "Key-Policy", "Statement": [ { - "Action": [ - "kms:CreateGrant", - "kms:ListGrants", - "kms:RevokeGrant", - ], + "Action": ["kms:CreateGrant", "kms:ListGrants", "kms:RevokeGrant"], "Effect": "Allow", "Sid": "Allow attachment of persistent resources", "Principal": {"AWS": "*"}, @@ -885,11 +769,7 @@ def test_resolve_booleans_different_properties_for_generic_resource(): "Version": "2012-10-17", "Statement": [ { - "Action": [ - "kms:CreateGrant", - "kms:ListGrants", - "kms:RevokeGrant", - ], + "Action": ["kms:CreateGrant", "kms:ListGrants", "kms:RevokeGrant"], "Effect": "Allow", "Principal": {"AWS": "*"}, "Resource": "*", @@ -913,10 +793,7 @@ def test_resolve_booleans_different_properties_for_generic_resource(): def test_resolve_template_with_a_valid_resource_without_properties(): - template = { - "AWSTemplateFormatVersion": "2010-09-09", - "Resources": {"MySNSTopic": {"Type": "AWS::SNS::Topic"}}, - } + template = {"AWSTemplateFormatVersion": "2010-09-09", "Resources": {"MySNSTopic": {"Type": "AWS::SNS::Topic"}}} model = parse(template).resolve() resource = model.Resources["MySNSTopic"] diff --git a/tests/test_resource.py b/tests/test_resource.py index 144abf2..b51c099 100644 --- a/tests/test_resource.py +++ b/tests/test_resource.py @@ -13,12 +13,7 @@ { "AWSTemplateFormatVersion": "2010-09-09", "Description": "Test resolving a nonexistent resource to Resource class", - "Resources": { - "NonexistentResource": { - "Type": "AWS::Non::Existent", - "Properties": {}, - } - }, + "Resources": {"NonexistentResource": {"Type": "AWS::Non::Existent", "Properties": {}}}, }, [], 0, @@ -47,13 +42,7 @@ "Properties": { "PropertyRandom": "One", "PolicyDocument": { - "Statement": [ - { - "Effect": "Allow", - "Action": ["service:GetService"], - "Resource": "*", - } - ] + "Statement": [{"Effect": "Allow", "Action": ["service:GetService"], "Resource": "*"}] }, }, } @@ -62,13 +51,7 @@ [ OptionallyNamedPolicyDocument( policy_document=PolicyDocument( - Statement=[ - Statement( - Effect="Allow", - Action=["service:GetService"], - Resource="*", - ) - ] + Statement=[Statement(Effect="Allow", Action=["service:GetService"], Resource="*")] ), name=None, ) @@ -85,22 +68,10 @@ "Properties": { "PropertyRandom": "One", "PolicyDocumentOne": { - "Statement": [ - { - "Effect": "Allow", - "Action": ["service:GetService"], - "Resource": "*", - } - ] + "Statement": [{"Effect": "Allow", "Action": ["service:GetService"], "Resource": "*"}] }, "PolicyDocumentTwo": { - "Statement": [ - { - "Effect": "Allow", - "Action": ["service:GetService"], - "Resource": "*", - } - ] + "Statement": [{"Effect": "Allow", "Action": ["service:GetService"], "Resource": "*"}] }, }, } @@ -109,25 +80,13 @@ [ OptionallyNamedPolicyDocument( policy_document=PolicyDocument( - Statement=[ - Statement( - Effect="Allow", - Action=["service:GetService"], - Resource="*", - ) - ] + Statement=[Statement(Effect="Allow", Action=["service:GetService"], Resource="*")] ), name=None, ), OptionallyNamedPolicyDocument( policy_document=PolicyDocument( - Statement=[ - Statement( - Effect="Allow", - Action=["service:GetService"], - Resource="*", - ) - ] + Statement=[Statement(Effect="Allow", Action=["service:GetService"], Resource="*")] ), name=None, ), @@ -144,22 +103,12 @@ "Properties": { "PropertyRandom": "One", "PolicyDocumentOne": { - "Statement": [ - { - "Effect": "Allow", - "Action": ["service:GetService"], - "Resource": "*", - } - ] + "Statement": [{"Effect": "Allow", "Action": ["service:GetService"], "Resource": "*"}] }, "PropertyTwo": { "PolicyDocumentTwo": { "Statement": [ - { - "Effect": "Allow", - "Action": ["service:GetService"], - "Resource": "*", - } + {"Effect": "Allow", "Action": ["service:GetService"], "Resource": "*"} ] } }, @@ -170,25 +119,13 @@ [ OptionallyNamedPolicyDocument( policy_document=PolicyDocument( - Statement=[ - Statement( - Effect="Allow", - Action=["service:GetService"], - Resource="*", - ) - ] + Statement=[Statement(Effect="Allow", Action=["service:GetService"], Resource="*")] ), name=None, ), OptionallyNamedPolicyDocument( policy_document=PolicyDocument( - Statement=[ - Statement( - Effect="Allow", - Action=["service:GetService"], - Resource="*", - ) - ] + Statement=[Statement(Effect="Allow", Action=["service:GetService"], Resource="*")] ), name=None, ), @@ -207,22 +144,14 @@ "PropertyOne": { "PolicyDocumentOne": { "Statement": [ - { - "Effect": "Allow", - "Action": ["service:GetService"], - "Resource": "*", - } + {"Effect": "Allow", "Action": ["service:GetService"], "Resource": "*"} ] } }, "PropertyTwo": { "PolicyDocumentTwo": { "Statement": [ - { - "Effect": "Allow", - "Action": ["service:GetService"], - "Resource": "*", - } + {"Effect": "Allow", "Action": ["service:GetService"], "Resource": "*"} ] } }, @@ -233,25 +162,13 @@ [ OptionallyNamedPolicyDocument( policy_document=PolicyDocument( - Statement=[ - Statement( - Effect="Allow", - Action=["service:GetService"], - Resource="*", - ) - ] + Statement=[Statement(Effect="Allow", Action=["service:GetService"], Resource="*")] ), name=None, ), OptionallyNamedPolicyDocument( policy_document=PolicyDocument( - Statement=[ - Statement( - Effect="Allow", - Action=["service:GetService"], - Resource="*", - ) - ] + Statement=[Statement(Effect="Allow", Action=["service:GetService"], Resource="*")] ), name=None, ), @@ -267,22 +184,10 @@ "Type": "AWS::Non::Existent", "Properties": { "PolicyDocumentTwo": [ + {"Statement": [{"Effect": "Allow", "Action": ["service:GetService"], "Resource": "*"}]}, { "Statement": [ - { - "Effect": "Allow", - "Action": ["service:GetService"], - "Resource": "*", - } - ] - }, - { - "Statement": [ - { - "Effect": "Allow", - "Action": ["service:GetServiceAnother"], - "Resource": "*", - } + {"Effect": "Allow", "Action": ["service:GetServiceAnother"], "Resource": "*"} ] }, ] @@ -293,25 +198,13 @@ [ OptionallyNamedPolicyDocument( policy_document=PolicyDocument( - Statement=[ - Statement( - Effect="Allow", - Action=["service:GetService"], - Resource="*", - ) - ] + Statement=[Statement(Effect="Allow", Action=["service:GetService"], Resource="*")] ), name=None, ), OptionallyNamedPolicyDocument( policy_document=PolicyDocument( - Statement=[ - Statement( - Effect="Allow", - Action=["service:GetServiceAnother"], - Resource="*", - ) - ] + Statement=[Statement(Effect="Allow", Action=["service:GetServiceAnother"], Resource="*")] ), name=None, ), @@ -331,11 +224,7 @@ "PolicyName": "APolicyName", "PolicyDocument": { "Statement": [ - { - "Effect": "Allow", - "Action": ["service:GetService"], - "Resource": "*", - } + {"Effect": "Allow", "Action": ["service:GetService"], "Resource": "*"} ] }, } @@ -347,13 +236,7 @@ [ OptionallyNamedPolicyDocument( policy_document=PolicyDocument( - Statement=[ - Statement( - Effect="Allow", - Action=["service:GetService"], - Resource="*", - ) - ] + Statement=[Statement(Effect="Allow", Action=["service:GetService"], Resource="*")] ), name="APolicyName", ) diff --git a/tests/test_types.py b/tests/test_types.py index 866dbd7..4f07102 100644 --- a/tests/test_types.py +++ b/tests/test_types.py @@ -14,13 +14,7 @@ class Model(BaseModel): assert model_schema == { "title": "Model", "type": "object", - "properties": { - "ip_network": { - "title": "Ip Network", - "type": "string", - "format": "looseipv4network", - } - }, + "properties": {"ip_network": {"title": "Ip Network", "type": "string", "format": "looseipv4network"}}, "required": ["ip_network"], } @@ -33,13 +27,7 @@ class Model(BaseModel): assert model_schema == { "title": "Model", "type": "object", - "properties": { - "ip_network": { - "title": "Ip Network", - "type": "string", - "format": "looseipv6network", - } - }, + "properties": {"ip_network": {"title": "Ip Network", "type": "string", "format": "looseipv6network"}}, "required": ["ip_network"], } @@ -91,11 +79,7 @@ class Model(BaseModel): @pytest.mark.parametrize( "value", - [ - "2012::1234:abcd:ffff:c0a8:101/64", - "2022::1234:abcd:ffff:c0a8:101/64", - "2032::1234:abcd:ffff:c0a8:101/64", - ], + ["2012::1234:abcd:ffff:c0a8:101/64", "2022::1234:abcd:ffff:c0a8:101/64", "2032::1234:abcd:ffff:c0a8:101/64"], ) def test_loose_ip_v6_is_not_strict(value): class Model(BaseModel): @@ -111,23 +95,11 @@ class Model(BaseModel): [ ( "hello,world", - [ - { - "loc": ("ip",), - "msg": "Expected 4 octets in 'hello,world'", - "type": "value_error.addressvalue", - } - ], + [{"loc": ("ip",), "msg": "Expected 4 octets in 'hello,world'", "type": "value_error.addressvalue"}], ), ( "192.168.0.1.1.1/24", - [ - { - "loc": ("ip",), - "msg": "Expected 4 octets in '192.168.0.1.1.1'", - "type": "value_error.addressvalue", - } - ], + [{"loc": ("ip",), "msg": "Expected 4 octets in '192.168.0.1.1.1'", "type": "value_error.addressvalue"}], ), ( -1, @@ -151,13 +123,7 @@ class Model(BaseModel): ), ( "2001:db00::1/120", - [ - { - "loc": ("ip",), - "msg": "Expected 4 octets in '2001:db00::1'", - "type": "value_error.addressvalue", - } - ], + [{"loc": ("ip",), "msg": "Expected 4 octets in '2001:db00::1'", "type": "value_error.addressvalue"}], ), ], ) @@ -175,13 +141,7 @@ class Model(BaseModel): [ ( "hello,world", - [ - { - "loc": ("ip",), - "msg": "At least 3 parts expected in 'hello,world'", - "type": "value_error.addressvalue", - } - ], + [{"loc": ("ip",), "msg": "At least 3 parts expected in 'hello,world'", "type": "value_error.addressvalue"}], ), ( "192.168.0.1.1.1/24", @@ -215,13 +175,7 @@ class Model(BaseModel): ), ( "192.168.0.1/24", - [ - { - "loc": ("ip",), - "msg": "At least 3 parts expected in '192.168.0.1'", - "type": "value_error.addressvalue", - } - ], + [{"loc": ("ip",), "msg": "At least 3 parts expected in '192.168.0.1'", "type": "value_error.addressvalue"}], ), ], )