Files
mdpolish/tests/test_regex.py
T

109 lines
3.6 KiB
Python

from __future__ import annotations
import re
import pytest
from mdpolish import Modifier, ModifierContractError, Pipeline, RunStatus, regex_replace
def test_factory_returns_modifier_and_expands_capture_groups() -> None:
modifier = regex_replace(
modifier_id="example.swap-date",
version="1.0.0",
pattern=r"(\d{4})-(\d{2})-(\d{2})",
replacement=r"\3/\2/\1",
applicability="处理 ISO 形状的虚构日期;不验证真实日历日期。",
)
result = Pipeline([modifier]).transform("A 2026-08-26, B 2027-01-02")
assert isinstance(modifier, Modifier)
assert result.status is RunStatus.SUCCESS
assert result.output_markdown == "A 26/08/2026, B 02/01/2027"
assert len(result.changes) == 2
assert result.changes[0].before == "2026-08-26"
assert result.changes[0].after == "26/08/2026"
def test_flags_and_factory_parameters_are_recorded() -> None:
modifier = regex_replace(
modifier_id="example.case",
version="2.0.0",
pattern="token",
replacement="value",
flags=re.IGNORECASE | re.MULTILINE,
)
result = Pipeline([modifier]).transform("TOKEN")
assert result.status is RunStatus.SUCCESS
assert result.modifiers[0].modifier_id == modifier.modifier_id
assert result.modifiers[0].parameters == modifier.parameters
assert dict(modifier.parameters) == {
"flags": int(re.IGNORECASE | re.MULTILINE),
"pattern": "token",
"replacement": "value",
}
def test_zero_match_and_no_op_replacement_are_stable() -> None:
missing = Pipeline(
[regex_replace(modifier_id="example.missing", version="1.0.0", pattern="missing", replacement="new")]
).transform("text")
no_op = Pipeline(
[regex_replace(modifier_id="example.no-op", version="1.0.0", pattern="text", replacement="text")]
).transform("text")
assert missing.output_markdown == "text"
assert missing.changes == ()
assert no_op.status is RunStatus.SUCCESS
assert no_op.changes == ()
@pytest.mark.parametrize("pattern", [r"", r"^", r"a*", r"(?=a)"])
def test_zero_length_matches_are_rejected(pattern: str) -> None:
if pattern == r"(?=a)":
modifier = regex_replace(
modifier_id="example.zero",
version="1.0.0",
pattern=pattern,
replacement="x",
)
result = Pipeline([modifier]).transform("a")
assert result.status is RunStatus.FAILED
assert result.partial_markdown == "a"
assert result.changes == ()
return
with pytest.raises(ModifierContractError, match="zero-length"):
regex_replace(
modifier_id="example.zero",
version="1.0.0",
pattern=pattern,
replacement="x",
)
def test_invalid_pattern_flags_and_input_types_are_rejected() -> None:
with pytest.raises(ModifierContractError, match="compile"):
regex_replace(modifier_id="example.invalid", version="1.0.0", pattern="(", replacement="x")
with pytest.raises(ModifierContractError, match="flags"):
regex_replace(
modifier_id="example.flags",
version="1.0.0",
pattern="x",
replacement="y",
flags=True,
)
def test_successful_output_is_idempotent() -> None:
pipeline = Pipeline(
[regex_replace(modifier_id="example.space", version="1.0.0", pattern=r" {2,}", replacement=" ")]
)
first = pipeline.transform("a b")
assert first.output_markdown == "a b"
assert first.output_markdown is not None
assert pipeline.transform(first.output_markdown).changes == ()