Skip to content
This repository was archived by the owner on Jun 13, 2025. It is now read-only.
Merged
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
105 changes: 104 additions & 1 deletion webhook_handlers/tests/test_github.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ def __getitem__(self, key):

WEBHOOK_SECRET = b"testixik8qdauiab1yiffydimvi72ekq"
DEFAULT_APP_ID = 1234
AI_FEATURES_GH_APP_ID = 9999


class GithubWebhookHandlerTests(APITestCase):
Expand All @@ -58,16 +59,23 @@ def inject_mocker(request, mocker):
def mock_webhook_secret(self, mocker):
mock_config_helper(mocker, configs={"github.webhook_secret": WEBHOOK_SECRET})

@pytest.fixture(autouse=True)
def mock_ai_features_app_id(self, mocker):
mock_config_helper(
mocker, configs={"github.ai_features_app_id": AI_FEATURES_GH_APP_ID}
)

@pytest.fixture(autouse=True)
def mock_default_app_id(self, mocker):
mock_config_helper(mocker, configs={"github.integration.id": DEFAULT_APP_ID})

def _post_event_data(self, event, data={}):
def _post_event_data(self, event, data={}, app_id=DEFAULT_APP_ID):
return self.client.post(
reverse("github-webhook"),
**{
GitHubHTTPHeaders.EVENT: event,
GitHubHTTPHeaders.DELIVERY_TOKEN: uuid.UUID(int=5),
GitHubHTTPHeaders.HOOK_INSTALLATION_TARGET_ID: app_id,
GitHubHTTPHeaders.SIGNATURE_256: "sha256="
+ hmac.new(
WEBHOOK_SECRET,
Expand Down Expand Up @@ -1420,3 +1428,98 @@ def test_repo_creation_doesnt_crash_for_forked_repo(self):
)

assert owner.repository_set.filter(name="testrepo").exists()

def test_check_codecov_ai_auto_enabled_reviews_enabled(self):
# Create an organization with AI PR review enabled
org_with_ai_enabled = OwnerFactory(
service=Service.GITHUB.value, yaml={"ai_pr_review": {"auto_review": True}}
)

response = self._post_event_data(
event=GitHubWebhookEvents.PULL_REQUEST,
data={
"action": "pull_request",
"repository": {
"id": 506003,
"name": "testrepo",
"private": False,
"default_branch": "main",
"owner": {"id": org_with_ai_enabled.service_id},
"fork": True,
"parent": {
"name": "mainrepo",
"language": "python",
"id": 7940284,
"private": False,
"default_branch": "main",
"owner": {"id": 8495712939, "login": "alogin"},
},
},
},
app_id=AI_FEATURES_GH_APP_ID,
)
assert response.data == {"auto_review_enabled": True}

def test_check_codecov_ai_auto_enabled_reviews_disabled(self):
# Test with AI PR review disabled
org_with_ai_disabled = OwnerFactory(
service=Service.GITHUB.value, yaml={"ai_pr_review": {"auto_review": False}}
)

response = self._post_event_data(
event=GitHubWebhookEvents.PULL_REQUEST,
data={
"action": "pull_request",
"repository": {
"id": 506004,
"name": "testrepo2",
"private": False,
"default_branch": "main",
"owner": {"id": org_with_ai_disabled.service_id},
},
},
app_id=AI_FEATURES_GH_APP_ID,
)
assert response.data == {"auto_review_enabled": False}

def test_check_codecov_ai_auto_enabled_reviews_no_config(self):
# Test with no yaml config
org_with_no_config = OwnerFactory(service=Service.GITHUB.value, yaml={})

response = self._post_event_data(
event=GitHubWebhookEvents.PULL_REQUEST,
data={
"action": "pull_request",
"repository": {
"id": 506005,
"name": "testrepo3",
"private": False,
"default_branch": "main",
"owner": {"id": org_with_no_config.service_id},
},
},
app_id=AI_FEATURES_GH_APP_ID,
)
assert response.data == {"auto_review_enabled": False}

def test_check_codecov_ai_auto_enabled_reviews_partial_config(self):
# Test with partial yaml config
org_with_partial_config = OwnerFactory(
service=Service.GITHUB.value, yaml={"ai_pr_review": {}}
)

response = self._post_event_data(
event=GitHubWebhookEvents.PULL_REQUEST,
data={
"action": "pull_request",
"repository": {
"id": 506006,
"name": "testrepo4",
"private": False,
"default_branch": "main",
"owner": {"id": org_with_partial_config.service_id},
},
},
app_id=AI_FEATURES_GH_APP_ID,
)
assert response.data == {"auto_review_enabled": False}
10 changes: 7 additions & 3 deletions webhook_handlers/views/github.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@

# This should probably go somewhere where it can be easily shared
regexp_ci_skip = re.compile(r"\[(ci|skip| |-){3,}\]").search
AI_FEATURES_GH_APP_ID = get_config("github", "ai_features_app_id")


class GithubWebhookHandler(APIView):
Expand Down Expand Up @@ -365,7 +366,10 @@ def status(self, request, *args, **kwargs):
return Response()

def pull_request(self, request, *args, **kwargs):
if request.headers.get(GitHubHTTPHeaders.HOOK_INSTALLATION_TARGET_ID):
if (
request.META.get(GitHubHTTPHeaders.HOOK_INSTALLATION_TARGET_ID, "")
== AI_FEATURES_GH_APP_ID
):
return self.check_codecov_ai_auto_enabled_reviews(request)

repo = self._get_repo(request)
Expand Down Expand Up @@ -404,8 +408,9 @@ def pull_request(self, request, *args, **kwargs):
def check_codecov_ai_auto_enabled_reviews(self, request):
org = Owner.objects.get(
service=self.service_name,
service_id=request.data["organization"]["id"],
service_id=request.data["repository"]["owner"]["id"],
)

auto_review_enabled = org.yaml.get("ai_pr_review", {}).get("auto_review", False)
return Response(
data={
Expand Down Expand Up @@ -768,7 +773,6 @@ def post(self, request, *args, **kwargs):
delivery=self.request.META.get(GitHubHTTPHeaders.DELIVERY_TOKEN),
),
)

self.validate_signature(request)

if handler := getattr(self, self.event, None):
Expand Down
Loading