115 lines
3.4 KiB
Python
115 lines
3.4 KiB
Python
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]
|
|
)
|