aboutsummaryrefslogtreecommitdiff
path: root/tests/forms/test_forms.py
diff options
context:
space:
mode:
Diffstat (limited to 'tests/forms/test_forms.py')
-rw-r--r--tests/forms/test_forms.py115
1 files changed, 115 insertions, 0 deletions
diff --git a/tests/forms/test_forms.py b/tests/forms/test_forms.py
new file mode 100644
index 0000000..6fe1849
--- /dev/null
+++ b/tests/forms/test_forms.py
@@ -0,0 +1,115 @@
+# SPDX-FileCopyrightText: 2026 Dennis Fink <me+coding@dennisfink.me>
+#
+# SPDX-License-Identifier: BSD-3-Clause
+
+from unittest.mock import Mock
+
+import pytest
+from flask import Flask
+from werkzeug.datastructures import MultiDict
+
+from webmentions_ssg.forms import AdminActionForm, EndpointForm, LoginForm
+
+VALID_SOURCE = "https://source.example/post"
+VALID_TARGET = "https://dennisfink.me/blog/example/"
+
+
+@pytest.fixture(autouse=True)
+def public_urls(monkeypatch: pytest.MonkeyPatch) -> None:
+ monkeypatch.setattr(
+ "webmentions_ssg.forms.validators.is_public_url", Mock(return_value=True)
+ )
+
+
+@pytest.mark.parametrize(
+ ("form_data", "invalid_field", "expected_error"),
+ [
+ pytest.param(
+ {"target": VALID_TARGET},
+ "source",
+ "This field is required.",
+ id="source-required",
+ ),
+ pytest.param(
+ {"source": "not a URL", "target": VALID_TARGET},
+ "source",
+ "Invalid URL.",
+ id="source-url",
+ ),
+ pytest.param(
+ {"source": "ftp://source.example/post", "target": VALID_TARGET},
+ "source",
+ "source must begin with http or https",
+ id="source-scheme",
+ ),
+ pytest.param(
+ {"source": VALID_SOURCE},
+ "target",
+ "This field is required.",
+ id="target-required",
+ ),
+ pytest.param(
+ {"source": VALID_SOURCE, "target": "not a URL"},
+ "target",
+ "Invalid URL.",
+ id="target-url",
+ ),
+ pytest.param(
+ {"source": VALID_SOURCE, "target": "ftp://dennisfink.me/blog/example/"},
+ "target",
+ "target must begin with http or https",
+ id="target-scheme",
+ ),
+ ],
+)
+def test_endpoint_form_rejects_invalid_field_syntax(
+ app: Flask, form_data: dict[str, str], invalid_field: str, expected_error: str
+) -> None:
+ with app.test_request_context("/endpoint", method="POST"):
+ form = EndpointForm(formdata=MultiDict(form_data), meta={"csrf": False})
+
+ assert not form.validate()
+ assert expected_error in form.errors[invalid_field]
+
+
+def test_endpoint_form_accepts_valid_data(app: Flask) -> None:
+ with app.test_request_context("/endpoint", method="POST"):
+ form = EndpointForm(
+ formdata=MultiDict({"source": VALID_SOURCE, "target": VALID_TARGET}),
+ meta={"csrf": False},
+ )
+
+ assert form.validate()
+ assert form.errors == {}
+
+
+@pytest.mark.parametrize(
+ ("form_data", "invalid_field"),
+ [
+ pytest.param({"password": "secret"}, "username", id="username-required"),
+ pytest.param({"username": "admin"}, "password", id="password-required"),
+ ],
+)
+def test_login_form_requires_credentials(
+ app: Flask, form_data: dict[str, str], invalid_field: str
+) -> None:
+ with app.test_request_context("/login", method="POST"):
+ form = LoginForm(formdata=MultiDict(form_data), meta={"csrf": False})
+
+ assert not form.validate()
+ assert "This field is required." in form.errors[invalid_field]
+
+
+def test_login_form_accepts_credentials(app: Flask) -> None:
+ with app.test_request_context("/login", method="POST"):
+ form = LoginForm(
+ formdata=MultiDict({"username": "admin", "password": "secret"}),
+ meta={"csrf": False},
+ )
+ assert form.validate()
+
+
+def test_admin_action_form_accepts_submission(app: Flask) -> None:
+ with app.test_request_context(method="POST"):
+ form = AdminActionForm(meta={"csrf": False})
+ assert form.validate()