Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions scripts/snippets/model.py
Original file line number Diff line number Diff line change
Expand Up @@ -179,3 +179,15 @@ class SnippetAttributeIssue:
class SnippetConditionContext:
snippet: SnippetTag
condition: IfVersionCondition | None


class CandidateConditionRule(str, Enum):
CONDITION_REQUIRED = "condition_required"
IDENTITY_MISMATCH = "identity_mismatch"


@dataclass(frozen=True)
class CandidateConditionIssue:
rule: CandidateConditionRule
span: Span
message: str
38 changes: 38 additions & 0 deletions scripts/snippets/semantics.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
from __future__ import annotations

from .model import (
CandidateConditionIssue,
CandidateConditionRule,
ElseTag,
IfVersionCondition,
IfVersionTag,
Expand Down Expand Up @@ -172,3 +174,39 @@ def map_snippet_condition_contexts(
)
)
return tuple(contexts)


def validate_candidate_condition(
source: (
ImmutableSourceReference | PullRequestSnippetSource | LocalSourceReference
),
context: SnippetConditionContext,
) -> tuple[CandidateConditionIssue, ...]:
"""Require candidate source identity to match its enclosing condition."""

if not isinstance(source, PullRequestSnippetSource):
return ()
condition = context.condition
if condition is None:
return (
CandidateConditionIssue(
rule=CandidateConditionRule.CONDITION_REQUIRED,
span=context.snippet.span,
message="Candidate pull-request snippet must be inside IfVersion",
),
)
if (
source.repository != condition.repository
or source.pull_request != condition.contains_pull_request
):
return (
CandidateConditionIssue(
rule=CandidateConditionRule.IDENTITY_MISMATCH,
span=context.snippet.span,
message=(
"Candidate snippet does not match its enclosing IfVersion "
f"({condition.repository}#{condition.contains_pull_request})"
),
),
)
return ()
65 changes: 65 additions & 0 deletions tests/test_candidate_snippet_condition_validation.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
from __future__ import annotations

from scripts.snippets.model import (
CandidateConditionRule,
IfVersionCondition,
ImmutableSourceReference,
PullRequestSnippetSource,
SnippetConditionContext,
SnippetTag,
Span,
)
from scripts.snippets.semantics import validate_candidate_condition

SPAN = Span(start=0, end=1, line=1, column=1)
SNIPPET = SnippetTag(attributes=(), span=SPAN)
CONDITION = IfVersionCondition(
repository="canton-network/splice",
contains_pull_request=6123,
span=SPAN,
)
CANDIDATE = PullRequestSnippetSource(
repository="canton-network/splice",
pull_request=6123,
path="apps/file.yaml",
)


def test_accepts_candidate_matching_enclosing_condition() -> None:
context = SnippetConditionContext(snippet=SNIPPET, condition=CONDITION)

assert validate_candidate_condition(CANDIDATE, context) == ()


def test_requires_condition_around_candidate() -> None:
context = SnippetConditionContext(snippet=SNIPPET, condition=None)

issues = validate_candidate_condition(CANDIDATE, context)
assert {issue.rule for issue in issues} == {
CandidateConditionRule.CONDITION_REQUIRED
}


def test_requires_repository_and_pr_identity_match() -> None:
context = SnippetConditionContext(snippet=SNIPPET, condition=CONDITION)
other_candidate = PullRequestSnippetSource(
repository="canton-network/splice",
pull_request=6124,
path="apps/file.yaml",
)

issues = validate_candidate_condition(other_candidate, context)
assert {issue.rule for issue in issues} == {
CandidateConditionRule.IDENTITY_MISMATCH
}


def test_does_not_restrict_non_candidate_sources() -> None:
immutable = ImmutableSourceReference(
repository="canton-network/splice",
commit="2c941ea9e834d7602d388f3271c0f864025ea756",
path="apps/file.yaml",
)
context = SnippetConditionContext(snippet=SNIPPET, condition=None)

assert validate_candidate_condition(immutable, context) == ()