Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 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
29 changes: 19 additions & 10 deletions src/rules/conditions/filesystem.py
Original file line number Diff line number Diff line change
Expand Up @@ -116,16 +116,25 @@ async def validate(self, parameters: dict[str, Any], event: dict[str, Any]) -> b
return len(matching_files) > 0

def _get_changed_files(self, event: dict[str, Any]) -> list[str]:
"""Extract the list of changed files from the event."""
event_type = event.get("event_type", "")
if event_type == "pull_request":
# TODO: Pull request—fetch changed files via GitHub API. Placeholder for now.
return []
elif event_type == "push":
# Push event—files in commits, not implemented.
return []
else:
return []
"""Extract changed file paths from enriched PR data or push commits."""
changed_files = event.get("changed_files", [])
if changed_files:
return [
f["filename"] if isinstance(f, dict) else f
for f in changed_files
if (f.get("filename") if isinstance(f, dict) else f)
]

commits = event.get("commits", [])
if commits:
seen: set[str] = set()
for commit in commits:
for key in ("added", "modified", "removed"):
for path in commit.get(key, []):
seen.add(path)
return sorted(seen)
Comment thread
coderabbitai[bot] marked this conversation as resolved.

return []

@staticmethod
def _glob_to_regex(glob_pattern: str) -> str:
Expand Down
68 changes: 68 additions & 0 deletions tests/unit/rules/conditions/test_filesystem.py
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,74 @@ def test_glob_to_regex_conversion(self) -> None:
assert FilePatternCondition._glob_to_regex("src/*.js") == "^src/.*\\.js$"
assert FilePatternCondition._glob_to_regex("file?.txt") == "^file.\\.txt$"

def test_get_changed_files_from_pr_enriched_data(self) -> None:
"""Test extracting files from enriched PR changed_files (list of dicts)."""
condition = FilePatternCondition()
event = {
"changed_files": [
{"filename": "src/main.py", "status": "modified", "additions": 10, "deletions": 2},
{"filename": "tests/test_main.py", "status": "added", "additions": 30, "deletions": 0},
]
}
result = condition._get_changed_files(event)
assert result == ["src/main.py", "tests/test_main.py"]

def test_get_changed_files_from_pr_plain_strings(self) -> None:
"""Test extracting files when changed_files contains plain strings."""
condition = FilePatternCondition()
event = {"changed_files": ["src/main.py", "README.md"]}
result = condition._get_changed_files(event)
assert result == ["src/main.py", "README.md"]

def test_get_changed_files_from_push_commits(self) -> None:
"""Test extracting files from push event commit arrays."""
condition = FilePatternCondition()
event = {
"commits": [
{"added": ["new_file.py"], "modified": ["src/main.py"], "removed": []},
{"added": [], "modified": ["src/main.py"], "removed": ["old.py"]},
]
}
result = condition._get_changed_files(event)
assert result == ["new_file.py", "old.py", "src/main.py"]

def test_get_changed_files_empty_event(self) -> None:
"""Test that an empty event returns no files."""
condition = FilePatternCondition()
assert condition._get_changed_files({}) == []

@pytest.mark.asyncio
async def test_evaluate_with_real_pr_event(self) -> None:
"""Test full evaluate flow with enriched PR data (no mocking)."""
condition = FilePatternCondition()
context = {
"parameters": {"pattern": "*.py", "condition_type": "files_match_pattern"},
"event": {
"changed_files": [
{"filename": "src/app.py", "status": "modified", "additions": 5, "deletions": 1},
{"filename": "docs/readme.md", "status": "modified", "additions": 2, "deletions": 0},
]
},
}
violations = await condition.evaluate(context)
assert len(violations) == 0

@pytest.mark.asyncio
async def test_evaluate_with_real_push_event(self) -> None:
"""Test full evaluate flow with push commit data (no mocking)."""
condition = FilePatternCondition()
context = {
"parameters": {"pattern": "*.yaml", "condition_type": "files_not_match_pattern"},
"event": {
"commits": [
{"added": ["config/app.yaml"], "modified": [], "removed": []},
]
},
}
violations = await condition.evaluate(context)
assert len(violations) == 1
assert "forbidden pattern" in violations[0].message


class TestMaxFileSizeCondition:
"""Tests for MaxFileSizeCondition class."""
Expand Down