From 861df96c8a881db28d1139ab480c600b72cf78da Mon Sep 17 00:00:00 2001 From: Kenneth Kehl <@kkehl@flexion.us> Date: Wed, 10 Sep 2025 10:18:04 -0700 Subject: [PATCH] fuzz --- poetry.lock | 38 ++++++++++++++++++++++++++-- pyproject.toml | 1 + tests/app/notifications/test_rest.py | 37 +++++++++++++++++++++++++++ 3 files changed, 74 insertions(+), 2 deletions(-) diff --git a/poetry.lock b/poetry.lock index fd97f650a..f99f19170 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 2.1.1 and should not be changed by hand. +# This file is automatically @generated by Poetry 2.1.3 and should not be changed by hand. [[package]] name = "aiohappyeyeballs" @@ -1969,6 +1969,40 @@ http2 = ["h2 (>=3,<5)"] socks = ["socksio (==1.*)"] zstd = ["zstandard (>=0.18.0)"] +[[package]] +name = "hypothesis" +version = "6.138.15" +description = "A library for property-based testing" +optional = false +python-versions = ">=3.9" +groups = ["dev"] +files = [ + {file = "hypothesis-6.138.15-py3-none-any.whl", hash = "sha256:b7cf743d461c319eb251a13c8e1dcf00f4ef7085e4ab5bf5abf102b2a5ffd694"}, + {file = "hypothesis-6.138.15.tar.gz", hash = "sha256:6b0e1aa182eacde87110995a3543530d69ef411f642162a656efcd46c2823ad1"}, +] + +[package.dependencies] +attrs = ">=22.2.0" +sortedcontainers = ">=2.1.0,<3.0.0" + +[package.extras] +all = ["black (>=20.8b0)", "click (>=7.0)", "crosshair-tool (>=0.0.95)", "django (>=4.2)", "dpcontracts (>=0.4)", "hypothesis-crosshair (>=0.0.25)", "lark (>=0.10.1)", "libcst (>=0.3.16)", "numpy (>=1.19.3)", "pandas (>=1.1)", "pytest (>=4.6)", "python-dateutil (>=1.4)", "pytz (>=2014.1)", "redis (>=3.0.0)", "rich (>=9.0.0)", "tzdata (>=2025.2) ; sys_platform == \"win32\" or sys_platform == \"emscripten\"", "watchdog (>=4.0.0)"] +cli = ["black (>=20.8b0)", "click (>=7.0)", "rich (>=9.0.0)"] +codemods = ["libcst (>=0.3.16)"] +crosshair = ["crosshair-tool (>=0.0.95)", "hypothesis-crosshair (>=0.0.25)"] +dateutil = ["python-dateutil (>=1.4)"] +django = ["django (>=4.2)"] +dpcontracts = ["dpcontracts (>=0.4)"] +ghostwriter = ["black (>=20.8b0)"] +lark = ["lark (>=0.10.1)"] +numpy = ["numpy (>=1.19.3)"] +pandas = ["pandas (>=1.1)"] +pytest = ["pytest (>=4.6)"] +pytz = ["pytz (>=2014.1)"] +redis = ["redis (>=3.0.0)"] +watchdog = ["watchdog (>=4.0.0)"] +zoneinfo = ["tzdata (>=2025.2) ; sys_platform == \"win32\" or sys_platform == \"emscripten\""] + [[package]] name = "identify" version = "2.6.13" @@ -5668,4 +5702,4 @@ cffi = ["cffi (>=1.17) ; python_version >= \"3.13\" and platform_python_implemen [metadata] lock-version = "2.1" python-versions = "^3.13.2" -content-hash = "2a1f323c9e8a7ce520a57570a833bedf9468f6f8ec78c850d1a815d38a810701" +content-hash = "f0e86f3ead4eb8f85fe4d07ac433674a3ed8f77709685561bcc32924f76c5d01" diff --git a/pyproject.toml b/pyproject.toml index 05b834b6e..d271a6a8a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -93,6 +93,7 @@ exceptiongroup = "==1.3.0" flake8 = "^7.3.0" flake8-bugbear = "^24.12.12" freezegun = "^1.5.5" +hypothesis = "^6.138.15" honcho = "*" isort = "^6.0.1" jinja2-cli = {version = "==0.8.2", extras = ["yaml"]} diff --git a/tests/app/notifications/test_rest.py b/tests/app/notifications/test_rest.py index b8beace53..7a8f55b75 100644 --- a/tests/app/notifications/test_rest.py +++ b/tests/app/notifications/test_rest.py @@ -3,6 +3,8 @@ import uuid import pytest from flask import current_app, json from freezegun import freeze_time +from hypothesis import given +from hypothesis import strategies as st from app.dao.api_key_dao import save_model_api_key from app.dao.notifications_dao import dao_update_notification @@ -14,6 +16,41 @@ from tests import create_service_authorization_header from tests.app.db import create_api_key, create_notification +@given( + st.emails(), + st.dictionaries( + keys=st.text(min_size=1, max_size=20), + values=st.text(min_size=0, max_size=100), + max_size=5, + ), + st.one_of(st.none(), st.text(min_size=0, max_size=50)), +) +def test_fuzz_send_email_notification( + client, + sample_service, + sample_template, + notify_db_session, + email_address, + personalisation, + reference, +): + template_id = str(sample_template.id) + auth_header = { + "Authorization": "ApiKey-v1 {}".format(sample_service.api_keys[0].secret) + } + payload = { + "template_id": template_id, + "email_address": email_address, + "personalisation": personalisation, + "reference": reference, + } + response = client.post("/v2/notifications/email", json=payload, headers=auth_header) + assert response.status_code in ( + 201, + 400, + ), f"Unexpected status: {response.status_code}, body: {response.json}" + + @pytest.mark.parametrize("type", (NotificationType.EMAIL, NotificationType.SMS)) def test_get_notification_by_id( client, sample_notification, sample_email_notification, type, mocker