1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
|
# 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 wtforms import Form, StringField, ValidationError
from webmentions_ssg.forms.validators import AllowedHostname, NotEqualTo, PublicURL
from webmentions_ssg.url_security import AddressResolutionError
class ComparisonForm(Form):
source = StringField("Source")
target = StringField("Target")
class URLForm(Form):
url = StringField("URL")
def test_not_equal_to_accepts_different_values() -> None:
form = ComparisonForm(MultiDict({"source": "source", "target": "target"}))
NotEqualTo("target")(form, form.source)
def test_not_equal_to_rejects_equal_values() -> None:
form = ComparisonForm(MultiDict({"source": "same", "target": "same"}))
with pytest.raises(ValidationError, match="Field must not be equal to target"):
NotEqualTo("target")(form, form.source)
def test_not_equal_to_uses_custom_message() -> None:
form = ComparisonForm(MultiDict({"source": "same", "target": "same"}))
with pytest.raises(ValidationError, match="Must differ from Target"):
NotEqualTo("target", "Must differ from %(other_label)s")(form, form.source)
def test_not_equal_to_rejects_unknown_field() -> None:
form = ComparisonForm(MultiDict({"source": "source", "target": "target"}))
with pytest.raises(ValidationError, match="Invalid field name 'missing'"):
NotEqualTo("missing")(form, form.source)
def test_allowed_hostname_accepts_configured_hostname(app: Flask) -> None:
app.config["WEBMENTIONS_SSG_ALLOWED_HOSTNAMES"] = ["dennisfink.me"]
form = URLForm(MultiDict({"url": "https://dennisfink.me/blog/example/"}))
with app.app_context():
AllowedHostname()(form, form.url)
def test_allowed_hostname_rejects_unconfigured_hostname(app: Flask) -> None:
app.config["WEBMENTIONS_SSG_ALLOWED_HOSTNAMES"] = ["dennisfink.me"]
form = URLForm(MultiDict({"url": "https://example.com/post"}))
with app.app_context(), pytest.raises(ValidationError, match="Invalid input"):
AllowedHostname()(form, form.url)
def test_allowed_hostname_uses_custom_message(app: Flask) -> None:
app.config["WEBMENTIONS_SSG_ALLOWED_HOSTNAMES"] = ["dennisfink.me"]
form = URLForm(MultiDict({"url": "https://example.com/post"}))
with (
app.app_context(),
pytest.raises(ValidationError, match="Hostname is not allowed"),
):
AllowedHostname("Hostname is not allowed")(form, form.url)
def test_public_url_accepts_public_url(monkeypatch: pytest.MonkeyPatch) -> None:
is_public_url = Mock(return_value=True)
monkeypatch.setattr("webmentions_ssg.forms.validators.is_public_url", is_public_url)
form = URLForm(MultiDict({"url": "https://example.com/post"}))
PublicURL()(form, form.url)
is_public_url.assert_called_once_with("https://example.com/post")
@pytest.mark.parametrize(
"outcome",
[
pytest.param(False, id="non-public-address"),
pytest.param(
AddressResolutionError("Could not resolve hostname"), id="resolution-error"
),
pytest.param(ValueError("No hostname was specified"), id="missing-hostname"),
],
)
def test_public_url_rejects_invalid_url(
monkeypatch: pytest.MonkeyPatch, outcome: bool | Exception
) -> None:
is_public_url = Mock()
if isinstance(outcome, Exception):
is_public_url.side_effect = outcome
else:
is_public_url.return_value = outcome
monkeypatch.setattr("webmentions_ssg.forms.validators.is_public_url", is_public_url)
form = URLForm(MultiDict({"url": "https://example.com/post"}))
with pytest.raises(ValidationError, match="URL must resolve to a public address"):
PublicURL()(form, form.url)
def test_public_url_uses_custom_message(monkeypatch: pytest.MonkeyPatch) -> None:
monkeypatch.setattr(
"webmentions_ssg.forms.validators.is_public_url", Mock(return_value=False)
)
form = URLForm(MultiDict({"url": "https://example.com/post"}))
with pytest.raises(ValidationError, match="Public URL required"):
PublicURL("Public URL required")(form, form.url)
|