aboutsummaryrefslogtreecommitdiff
path: root/tests/library/hardening.test.py
blob: 3733bd3ff8d67be0a1506b9c3afbe278bffa7d15 (plain)
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
"""Real-world corpus hardening tests for every hand-picked library validator.

Each corpus file under ``tests/library/corpora/<name>.toml`` lists ``accepts``
and ``rejects`` — strings the named validator must accept or reject exactly.
Adding a new validator only means dropping a new TOML file next to the others;
the parametrization discovers it at collection time.
"""

from __future__ import annotations

import tomllib
from collections.abc import Iterator
from pathlib import Path

import pytest

import edify.library as library_module
from edify import Pattern

_CORPUS_ROOT = Path(__file__).parent / "corpora"


def _corpus_cases() -> Iterator[tuple[str, Pattern, str, str]]:
    for corpus_path in sorted(_CORPUS_ROOT.glob("*.toml")):
        validator_name = corpus_path.stem
        validator = getattr(library_module, validator_name)
        if not isinstance(validator, Pattern):
            continue
        parsed = tomllib.loads(corpus_path.read_text())
        accepts: list[str] = parsed.get("accepts", [])
        rejects: list[str] = parsed.get("rejects", [])
        for accept_input in accepts:
            yield validator_name, validator, "accept", accept_input
        for reject_input in rejects:
            yield validator_name, validator, "reject", reject_input


_CASES: list[tuple[str, Pattern, str, str]] = list(_corpus_cases())


@pytest.mark.parametrize(
    ("validator_name", "validator", "expected_verdict", "input_string"),
    _CASES,
    ids=[
        f"{name}-{verdict}-{index}"
        for index, (name, _validator, verdict, _input_string) in enumerate(_CASES)
    ],
)
def test_library_validator_matches_the_committed_corpus(
    validator_name: str, validator: Pattern, expected_verdict: str, input_string: str
):
    observed = validator(input_string)
    if expected_verdict == "accept":
        assert observed is True, (
            f"validator {validator_name!r} rejected {input_string!r} but corpus expects accept"
        )
    else:
        assert observed is False, (
            f"validator {validator_name!r} accepted {input_string!r} but corpus expects reject"
        )