diff --git a/src/gitingest/repository_clone.py b/src/gitingest/repository_clone.py index 57374ad..d251a6f 100644 --- a/src/gitingest/repository_clone.py +++ b/src/gitingest/repository_clone.py @@ -111,6 +111,11 @@ async def _check_repo_exists(url: str) -> bool: ------- bool True if the repository exists, False otherwise. + + Raises + ------ + RuntimeError + If the curl command returns an unexpected status code. """ proc = await asyncio.create_subprocess_exec( "curl", @@ -120,11 +125,20 @@ async def _check_repo_exists(url: str) -> bool: stderr=asyncio.subprocess.PIPE, ) stdout, _ = await proc.communicate() + if proc.returncode != 0: return False - # Check if stdout contains "404" status code - stdout_str = stdout.decode() - return "HTTP/1.1 404" not in stdout_str and "HTTP/2 404" not in stdout_str + + response = stdout.decode() + status_code = _get_status_code(response) + + if status_code in (200, 301): + return True + + if status_code in (404, 302): + return False + + raise RuntimeError(f"Unexpected status code: {status_code}") async def _run_git_command(*args: str) -> tuple[bytes, bytes]: @@ -157,3 +171,22 @@ async def _run_git_command(*args: str) -> tuple[bytes, bytes]: raise RuntimeError(f"Git command failed: {' '.join(args)}\nError: {error_message}") return stdout, stderr + + +def _get_status_code(response: str) -> int: + """ + Extract the status code from an HTTP response. + + Parameters + ---------- + response : str + The HTTP response string. + + Returns + ------- + int + The status code of the response + """ + status_line = response.splitlines()[0].strip() + status_code = int(status_line.split(" ", 2)[1]) + return status_code diff --git a/tests/test_repository_clone.py b/tests/test_repository_clone.py index 892bd04..3bfa3b2 100644 --- a/tests/test_repository_clone.py +++ b/tests/test_repository_clone.py @@ -204,8 +204,9 @@ async def test_clone_repo_commit_without_branch() -> None: @pytest.mark.asyncio async def test_check_repo_exists_with_redirect() -> None: """ - Test the `_check_repo_exists` function for handling HTTP redirects (302 Found). - Verifies that it correctly identifies the repository's existence. + Test the `_check_repo_exists` function when the repository URL returns a redirect response. + + Verifies that the function returns False when a 302 Found response is received. """ url = "https://github.com/user/repo" with patch("asyncio.create_subprocess_exec", new_callable=AsyncMock) as mock_exec: @@ -214,4 +215,21 @@ async def test_check_repo_exists_with_redirect() -> None: mock_process.returncode = 0 # Simulate successful request mock_exec.return_value = mock_process + assert await _check_repo_exists(url) is False + + +@pytest.mark.asyncio +async def test_check_repo_exists_with_permanent_redirect() -> None: + """ + Test the `_check_repo_exists` function when the repository URL returns a redirect response. + + Verifies that the function returns True when a 301 Found response is received. + """ + url = "https://github.com/user/repo" + with patch("asyncio.create_subprocess_exec", new_callable=AsyncMock) as mock_exec: + mock_process = AsyncMock() + mock_process.communicate.return_value = (b"HTTP/1.1 301 Found\n", b"") + mock_process.returncode = 0 # Simulate successful request + mock_exec.return_value = mock_process + assert await _check_repo_exists(url)