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 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210
|
# SPDX-FileCopyrightText: 2022 Hynek Schlawack <hs@ox.cx>
#
# SPDX-License-Identifier: MIT
from __future__ import annotations
import secrets
from pathlib import Path
import pytest
from hatch_fancy_pypi_readme._fragments import FileFragment, TextFragment
from hatch_fancy_pypi_readme.exceptions import ConfigurationError
class TestTextFragment:
def test_ok(self):
"""
The text that is passed in is rendered without changes.
"""
text = secrets.token_urlsafe()
assert text == TextFragment.from_config({"text": text}).render()
@pytest.fixture(name="txt_path")
def _txt_path():
return Path("tests") / "example_text.md"
@pytest.fixture(name="txt")
def _txt(txt_path):
return txt_path.read_text()
class TestFileFragment:
def test_simple_ok(self, txt, txt_path):
"""
Loading a file works.
"""
assert (
txt == FileFragment.from_config({"path": str(txt_path)}).render()
)
def test_start_after_ok(self, txt_path):
"""
Specifying a `start-after` that exists in the file removes it along
with what comes before.
"""
assert """This is the *interesting* body!
<!-- but before this -->
Uninteresting Footer
""" == FileFragment.from_config(
{
"path": str(txt_path),
"start-after": "<!-- cut after this -->\n\n",
}
).render()
def test_start_at_ok(self, txt_path):
"""
Specifying a `start-at` that exists in the file removes everything
before the string, but not the string itself.
"""
assert """This is the *interesting* body!
<!-- but before this -->
Uninteresting Footer
""" == FileFragment.from_config(
{
"path": str(txt_path),
"start-at": "This is the *interesting* body!",
}
).render()
def test_end_before_ok(self, txt_path):
"""
Specifying an `end-before` that exists in the file cuts it off along
with everything that follows.
"""
assert """# Boring Header
<!-- cut after this -->
This is the *interesting* body!""" == FileFragment.from_config(
{
"path": str(txt_path),
"end-before": "\n\n<!-- but before this -->",
}
).render()
def test_start_end_ok(self, txt_path):
"""
Specifying existing `start-after` and `end-before` returns exactly
what's between them.
"""
assert (
"This is the *interesting* body!"
== FileFragment.from_config(
{
"path": str(txt_path),
"start-after": "<!-- cut after this -->\n\n",
"end-before": "\n\n<!-- but before this -->",
}
).render()
)
def test_start_after_end_before_not_found(self, txt_path):
"""
If `start-after` and/or `end-before` don't exist, a helpful error is
raised.
"""
with pytest.raises(ConfigurationError) as ei:
FileFragment.from_config(
{
"path": str(txt_path),
"start-after": "nope",
"end-before": "also nope",
}
)
assert [
"file fragment: 'start-after' 'nope' not found.",
"file fragment: 'end-before' 'also nope' not found.",
] == ei.value.errors
def test_start_at_end_before_not_found(self, txt_path):
"""
If `start-at` and/or `end-before` don't exist, a helpful error is
raised.
"""
with pytest.raises(ConfigurationError) as ei:
FileFragment.from_config(
{
"path": str(txt_path),
"start-at": "nope",
"end-before": "also nope",
}
)
assert [
"file fragment: 'start-at' 'nope' not found.",
"file fragment: 'end-before' 'also nope' not found.",
] == ei.value.errors
def test_start_after_at(self, txt_path):
"""
If both `start-after` and `start-at` are passed, abort with an error.
"""
with pytest.raises(ConfigurationError) as ei:
FileFragment.from_config(
{
"path": str(txt_path),
"start-after": "cut",
"start-at": "cut",
}
)
assert [
"file fragment: 'start-after' and 'start-at' are mutually "
"exclusive."
] == ei.value.errors
def test_pattern_no_match(self, txt_path):
"""
If the pattern doesn't match, a helpful error is raises.
"""
with pytest.raises(ConfigurationError) as ei:
FileFragment.from_config(
{
"path": str(txt_path),
"pattern": r"wtf",
}
)
assert ["file fragment: pattern 'wtf' not found."] == ei.value.errors
def test_pattern_no_group(self, txt_path):
"""
If the pattern matches but lacks a group, tell the user.
"""
with pytest.raises(ConfigurationError) as ei:
FileFragment.from_config(
{
"path": str(txt_path),
"pattern": r"Uninteresting",
}
)
assert [
"file fragment: pattern matches, but no group defined."
] == ei.value.errors
def test_pattern_ok(self, txt_path):
"""
If the pattern matches and has a group, return it.
"""
assert (
"*interesting*"
== FileFragment.from_config(
{
"path": str(txt_path),
"pattern": r"the (.*) body",
}
).render()
)
|