重构为函数式通用 Markdown 修改库

This commit is contained in:
2026-08-26 15:33:37 +08:00
parent 1abf72ccb1
commit bb0507db30
89 changed files with 1589 additions and 14850 deletions
+114
View File
@@ -0,0 +1,114 @@
from __future__ import annotations
from dataclasses import FrozenInstanceError
from typing import cast
import pytest
from mdpolish import DocumentSnapshot, Modifier, ModifierContractError, ProposedChange
def no_changes(snapshot: DocumentSnapshot) -> tuple[ProposedChange, ...]:
return ()
def test_plain_function_and_mapping_parameters_create_immutable_modifier() -> None:
source_parameters: dict[str, object] = {
"enabled": True,
"nested": {"count": 2},
"values": ["a", "b"],
}
modifier = Modifier(
modifier_id="example.rule",
version="1.2.3",
parameters=source_parameters,
applicability="处理虚构测试输入;排除其他文本。",
propose=no_changes,
)
source_parameters["enabled"] = False
assert modifier.propose is no_changes
assert modifier.parameters == (
("enabled", True),
("nested", (("count", 2),)),
("values", ("a", "b")),
)
with pytest.raises(FrozenInstanceError):
modifier.version = "2.0.0" # type: ignore[misc]
def test_normalized_parameters_are_sorted_and_validated() -> None:
modifier = Modifier(
modifier_id="example.normalized",
version="1.0.0",
parameters=(("z", 1), ("a", ("x", 2))),
applicability="处理虚构测试输入。",
propose=no_changes,
)
assert modifier.parameters == (("a", ("x", 2)), ("z", 1))
@pytest.mark.parametrize("modifier_id", ["", "Upper", "has space", ".leading", "trailing."])
def test_invalid_modifier_id_is_rejected(modifier_id: str) -> None:
with pytest.raises(ModifierContractError, match="modifier_id"):
Modifier(
modifier_id=modifier_id,
version="1.0.0",
parameters=(),
applicability="测试。",
propose=no_changes,
)
@pytest.mark.parametrize("version", ["", "1", "1.0", "v1.0.0", "01.0.0", "1.0.0-beta"])
def test_invalid_version_is_rejected(version: str) -> None:
with pytest.raises(ModifierContractError, match="version"):
Modifier(
modifier_id="example.rule",
version=version,
parameters=(),
applicability="测试。",
propose=no_changes,
)
@pytest.mark.parametrize(
"parameters",
[
{"bad": {1}},
{"bad": float("inf")},
(("duplicate", 1), ("duplicate", 2)),
(("bad", object()),),
("not-a-pair",),
],
)
def test_invalid_parameters_are_rejected(parameters: object) -> None:
with pytest.raises(ModifierContractError):
Modifier(
modifier_id="example.rule",
version="1.0.0",
parameters=parameters, # type: ignore[arg-type]
applicability="测试。",
propose=no_changes,
)
def test_empty_applicability_and_non_callable_proposal_are_rejected() -> None:
with pytest.raises(ModifierContractError, match="applicability"):
Modifier(
modifier_id="example.rule",
version="1.0.0",
parameters=(),
applicability=" ",
propose=no_changes,
)
with pytest.raises(ModifierContractError, match="callable"):
Modifier(
modifier_id="example.rule",
version="1.0.0",
parameters=(),
applicability="测试。",
propose=cast(object, None), # type: ignore[arg-type]
)