diff --git a/scripts/snippets/model.py b/scripts/snippets/model.py index c09ffefef..7ab2b3608 100644 --- a/scripts/snippets/model.py +++ b/scripts/snippets/model.py @@ -220,3 +220,10 @@ class DirectiveSyntaxRule(str, Enum): ELSE_SELF_CLOSING = "else_self_closing" CLOSING_ATTRIBUTES = "closing_attributes" ELSE_ATTRIBUTES = "else_attributes" + + +@dataclass(frozen=True) +class ConditionPageValidation: + tags: tuple[IfVersionTag | ElseTag, ...] + conditions: tuple[IfVersionCondition, ...] + diagnostics: tuple[Diagnostic, ...] diff --git a/scripts/snippets/page_validation.py b/scripts/snippets/page_validation.py new file mode 100644 index 000000000..10afe3aec --- /dev/null +++ b/scripts/snippets/page_validation.py @@ -0,0 +1,50 @@ +from __future__ import annotations + +from pathlib import Path + +from .diagnostics import ( + directive_syntax_diagnostic, + if_version_attribute_diagnostics, + if_version_structure_diagnostics, +) +from .model import ConditionPageValidation, IfVersionTag +from .registry import RepositoryRegistry +from .syntax import DirectiveSyntaxError, parse_if_version_tags +from .validation import ( + validate_if_version_attributes, + validate_if_version_structure, +) + + +def validate_condition_page( + text: str, *, path: Path, registry: RepositoryRegistry +) -> ConditionPageValidation: + """Parse and validate conditional declarations without inspecting snippets.""" + + try: + tags = parse_if_version_tags(text) + except DirectiveSyntaxError as error: + return ConditionPageValidation( + tags=(), + conditions=(), + diagnostics=(directive_syntax_diagnostic(path, error),), + ) + + diagnostics = list( + if_version_structure_diagnostics( + path, validate_if_version_structure(text, tags) + ) + ) + conditions = [] + for tag in tags: + if not isinstance(tag, IfVersionTag) or tag.closing: + continue + result = validate_if_version_attributes(tag, registry) + diagnostics.extend(if_version_attribute_diagnostics(path, result.issues)) + if result.condition is not None: + conditions.append(result.condition) + return ConditionPageValidation( + tags=tags, + conditions=tuple(conditions), + diagnostics=tuple(diagnostics), + ) diff --git a/tests/test_condition_page_validation.py b/tests/test_condition_page_validation.py new file mode 100644 index 000000000..e60cae31e --- /dev/null +++ b/tests/test_condition_page_validation.py @@ -0,0 +1,68 @@ +from __future__ import annotations + +from pathlib import Path + +from scripts.snippets.model import ConditionPageValidation +from scripts.snippets.page_validation import validate_condition_page +from scripts.snippets.registry import ( + RepositoryConfig, + RepositoryRegistry, + RepositoryVisibility, +) + +PATH = Path("docs-main/validator.source.mdx") +REGISTRY = RepositoryRegistry( + ( + RepositoryConfig( + name="canton-network/splice", + url="https://github.com/canton-network/splice", + default_branch="main", + visibility=RepositoryVisibility.PUBLIC, + ), + ) +) + + +def validate(text: str) -> ConditionPageValidation: + return validate_condition_page(text, path=PATH, registry=REGISTRY) + + +def test_returns_validated_condition() -> None: + result = validate( + """ +new +""" + ) + + assert result.diagnostics == () + assert len(result.conditions) == 1 + assert result.conditions[0].repository == "canton-network/splice" + + +def test_reports_conditional_syntax_error() -> None: + result = validate( + '' + ) + + assert [diagnostic.code for diagnostic in result.diagnostics] == [ + "SNIP019" + ] + + +def test_reports_conditional_structure_error() -> None: + result = validate("orphan") + + assert "SNIP025" in { + diagnostic.code for diagnostic in result.diagnostics + } + + +def test_reports_condition_attribute_error() -> None: + result = validate( + '' + "new" + ) + + assert "SNIP021" in { + diagnostic.code for diagnostic in result.diagnostics + }