codeflash-agent/packages/github-app/tests/test_github.py
Kevin Turcios 3b59d97647 squash
2026-04-13 14:12:17 -05:00

199 lines
5.9 KiB
Python

"""Tests for GitHub API helpers."""
from __future__ import annotations
import httpx
import respx
from github_app.github import (
add_labels,
build_file_summary,
create_check_run,
fetch_commit_diff,
fetch_pr_details,
fetch_pr_diff,
fetch_pr_files,
fetch_repo_labels,
post_comment,
post_review,
truncate_diff,
)
API = "https://api.github.com"
@respx.mock
async def test_fetch_pr_diff():
respx.get(f"{API}/repos/o/r/pulls/1").respond(text="diff content")
async with httpx.AsyncClient() as client:
result = await fetch_pr_diff(client, "o", "r", 1, "tok")
assert result == "diff content"
@respx.mock
async def test_fetch_pr_files():
def side_effect(request):
page = int(request.url.params.get("page", "1"))
if page == 1:
return httpx.Response(200, json=[{"filename": "a.py"}])
return httpx.Response(200, json=[])
respx.get(f"{API}/repos/o/r/pulls/1/files").mock(
side_effect=side_effect,
)
async with httpx.AsyncClient() as client:
result = await fetch_pr_files(client, "o", "r", 1, "tok")
assert result == [{"filename": "a.py"}]
@respx.mock
async def test_fetch_pr_files_paginated():
page1 = [{"filename": f"f{i}.py"} for i in range(100)]
page2 = [{"filename": "last.py"}]
def side_effect(request):
page = int(request.url.params.get("page", "1"))
if page == 1:
return httpx.Response(200, json=page1)
if page == 2:
return httpx.Response(200, json=page2)
return httpx.Response(200, json=[])
respx.get(f"{API}/repos/o/r/pulls/1/files").mock(
side_effect=side_effect,
)
async with httpx.AsyncClient() as client:
result = await fetch_pr_files(client, "o", "r", 1, "tok")
assert len(result) == 101
@respx.mock
async def test_fetch_pr_details():
pr = {"number": 1, "title": "Test"}
respx.get(f"{API}/repos/o/r/pulls/1").respond(json=pr)
async with httpx.AsyncClient() as client:
result = await fetch_pr_details(client, "o", "r", 1, "tok")
assert result["title"] == "Test"
@respx.mock
async def test_fetch_commit_diff():
respx.get(f"{API}/repos/o/r/commits/abc").respond(text="commit diff")
async with httpx.AsyncClient() as client:
result = await fetch_commit_diff(client, "o", "r", "abc", "tok")
assert result == "commit diff"
@respx.mock
async def test_fetch_repo_labels():
def side_effect(request):
page = int(request.url.params.get("page", "1"))
if page == 1:
return httpx.Response(
200,
json=[{"name": "bug"}, {"name": "enhancement"}],
)
return httpx.Response(200, json=[])
respx.get(f"{API}/repos/o/r/labels").mock(side_effect=side_effect)
async with httpx.AsyncClient() as client:
result = await fetch_repo_labels(client, "o", "r", "tok")
assert result == ["bug", "enhancement"]
@respx.mock
async def test_post_review():
route = respx.post(f"{API}/repos/o/r/pulls/1/reviews").respond(200)
async with httpx.AsyncClient() as client:
await post_review(client, "o", "r", 1, "body", "COMMENT", "tok")
assert route.called
@respx.mock
async def test_post_comment():
route = respx.post(f"{API}/repos/o/r/issues/1/comments").respond(200)
async with httpx.AsyncClient() as client:
await post_comment(client, "o", "r", 1, "body", "tok")
assert route.called
@respx.mock
async def test_add_labels():
route = respx.post(f"{API}/repos/o/r/issues/1/labels").respond(200)
async with httpx.AsyncClient() as client:
await add_labels(client, "o", "r", 1, ["bug"], "tok")
assert route.called
@respx.mock
async def test_create_check_run():
route = respx.post(f"{API}/repos/o/r/check-runs").respond(200)
async with httpx.AsyncClient() as client:
await create_check_run(
client,
"o",
"r",
"sha",
"name",
"neutral",
{"title": "t", "summary": "s"},
"tok",
)
assert route.called
def test_build_file_summary():
files = [
{"filename": "a.py", "status": "modified", "additions": 5, "deletions": 2},
{"filename": "b.py", "status": "added", "additions": 10, "deletions": 0},
]
result = build_file_summary(files)
assert "a.py" in result
assert "+5/-2" in result
assert "added" in result
def test_truncate_diff_short():
diff = "short diff"
assert truncate_diff(diff) == diff
def test_truncate_diff_long():
diff = "x" * 70_000
result = truncate_diff(diff)
assert len(result) < 70_000
assert "truncated" in result
@respx.mock
async def test_fetch_pr_files_pagination_cap():
"""Pagination stops at MAX_PAGES to prevent infinite loops."""
from github_app.github import MAX_PAGES
def side_effect(request):
# Always return data, simulating a repo with unlimited pages.
page = int(request.url.params.get("page", "1"))
return httpx.Response(200, json=[{"filename": f"f{page}.py"}])
respx.get(f"{API}/repos/o/r/pulls/1/files").mock(
side_effect=side_effect,
)
async with httpx.AsyncClient() as client:
result = await fetch_pr_files(client, "o", "r", 1, "tok")
# Should stop after MAX_PAGES pages, not loop forever.
assert len(result) == MAX_PAGES
@respx.mock
async def test_fetch_repo_labels_pagination_cap():
"""Label fetching stops at MAX_PAGES to prevent infinite loops."""
from github_app.github import MAX_PAGES
def side_effect(request):
page = int(request.url.params.get("page", "1"))
return httpx.Response(200, json=[{"name": f"label-{page}"}])
respx.get(f"{API}/repos/o/r/labels").mock(side_effect=side_effect)
async with httpx.AsyncClient() as client:
result = await fetch_repo_labels(client, "o", "r", "tok")
assert len(result) == MAX_PAGES