Skip to content

Commit 95a5861

Browse files
committed
tests(feat[test_git]) Add xfail tests for update_repo error path gaps
why: Several error paths in GitSync.update_repo() are not wrapped in try/except, so failures propagate as uncaught exceptions instead of being recorded in SyncResult. This causes vcspull to either crash or report false successes. what: - Add test_update_repo_submodule_failure_recorded (line 589 gap) - Add test_update_repo_symbolic_ref_failure_recorded (line 420-424 gap) - Add test_update_repo_remote_ref_not_found_recorded (line 462 gap) - All marked xfail(strict=True) pending fix
1 parent b9b14a4 commit 95a5861

1 file changed

Lines changed: 134 additions & 0 deletions

File tree

tests/sync/test_git.py

Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1059,3 +1059,137 @@ def test_update_repo_rev_list_head_failure_returns_sync_result(
10591059
assert result.errors[0].step == "rev-list-head"
10601060
assert result.errors[0].exception is not None
10611061
assert isinstance(result.errors[0].exception, exc.CommandError)
1062+
1063+
1064+
@pytest.mark.xfail(strict=True, reason="submodule.update not wrapped in try/except")
1065+
def test_update_repo_submodule_failure_recorded(
1066+
create_git_remote_bare_repo: CreateRepoPytestFixtureFn,
1067+
tmp_path: pathlib.Path,
1068+
) -> None:
1069+
"""Test that submodule.update() failure is recorded in SyncResult.
1070+
1071+
When .gitmodules references a non-existent submodule URL, the
1072+
``git submodule update`` call in update_repo() fails. Currently
1073+
this is not wrapped in try/except, so the exception propagates
1074+
instead of being recorded in SyncResult.
1075+
"""
1076+
git_server = create_git_remote_bare_repo()
1077+
git_repo = GitSync(
1078+
path=tmp_path / "myrepo",
1079+
url=git_server.as_uri(),
1080+
)
1081+
git_repo.obtain()
1082+
1083+
# Make an initial commit and push so update_repo has a valid HEAD
1084+
initial_file = git_repo.path / "initial_file"
1085+
initial_file.write_text("content", encoding="utf-8")
1086+
git_repo.run(["add", str(initial_file)])
1087+
git_repo.run(["commit", "-m", "initial commit"])
1088+
git_repo.run(["push"])
1089+
1090+
# Add a .gitmodules file that references a non-existent submodule URL
1091+
gitmodules = git_repo.path / ".gitmodules"
1092+
gitmodules.write_text(
1093+
'[submodule "broken"]\n\tpath = broken\n\turl = file:///nonexistent/repo.git\n',
1094+
encoding="utf-8",
1095+
)
1096+
git_repo.run(["add", ".gitmodules"])
1097+
git_repo.run(["commit", "-m", "add broken submodule ref"])
1098+
git_repo.run(["push"])
1099+
1100+
# Reset behind so update_repo triggers a fetch+checkout cycle
1101+
git_repo.run(["reset", "--hard", "HEAD^"])
1102+
1103+
result = git_repo.update_repo()
1104+
1105+
assert isinstance(result, SyncResult)
1106+
assert result.ok is False
1107+
assert len(result.errors) > 0
1108+
assert any(e.step == "submodule-update" for e in result.errors)
1109+
1110+
1111+
@pytest.mark.xfail(strict=True, reason="symbolic_ref not wrapped in try/except")
1112+
def test_update_repo_symbolic_ref_failure_recorded(
1113+
create_git_remote_bare_repo: CreateRepoPytestFixtureFn,
1114+
tmp_path: pathlib.Path,
1115+
) -> None:
1116+
"""Test that symbolic_ref failure on detached HEAD is recorded in SyncResult.
1117+
1118+
When a repo is in detached HEAD state and no ``rev`` is set,
1119+
``symbolic_ref --short HEAD`` fails. Currently this is not wrapped
1120+
in try/except, so the exception propagates instead of being
1121+
recorded in SyncResult.
1122+
"""
1123+
git_server = create_git_remote_bare_repo()
1124+
git_repo = GitSync(
1125+
path=tmp_path / "myrepo",
1126+
url=git_server.as_uri(),
1127+
)
1128+
git_repo.obtain()
1129+
1130+
# Make a commit and push so the repo has a valid HEAD
1131+
initial_file = git_repo.path / "initial_file"
1132+
initial_file.write_text("content", encoding="utf-8")
1133+
git_repo.run(["add", str(initial_file)])
1134+
git_repo.run(["commit", "-m", "initial commit"])
1135+
git_repo.run(["push"])
1136+
1137+
# Detach HEAD — symbolic_ref will fail
1138+
head_sha = git_repo.run(["rev-parse", "HEAD"]).strip()
1139+
git_repo.run(["checkout", head_sha])
1140+
1141+
# Ensure no rev is set so the code path hits symbolic_ref
1142+
git_repo.rev = None # type: ignore[assignment]
1143+
1144+
result = git_repo.update_repo()
1145+
1146+
assert isinstance(result, SyncResult)
1147+
assert result.ok is False
1148+
assert len(result.errors) > 0
1149+
assert any(e.step == "symbolic-ref" for e in result.errors)
1150+
1151+
1152+
@pytest.mark.xfail(
1153+
strict=True,
1154+
reason="GitRemoteRefNotFound raised instead of recorded in SyncResult",
1155+
)
1156+
def test_update_repo_remote_ref_not_found_recorded(
1157+
create_git_remote_bare_repo: CreateRepoPytestFixtureFn,
1158+
tmp_path: pathlib.Path,
1159+
) -> None:
1160+
"""Test that GitRemoteRefNotFound is caught and recorded in SyncResult.
1161+
1162+
When show-ref output contains ``refs/remotes/<tag>`` but the regex
1163+
match fails, ``GitRemoteRefNotFound`` is raised with a bare ``raise``.
1164+
It should instead be caught and recorded in SyncResult.
1165+
"""
1166+
from unittest.mock import patch
1167+
1168+
git_server = create_git_remote_bare_repo()
1169+
git_repo = GitSync(
1170+
path=tmp_path / "myrepo",
1171+
url=git_server.as_uri(),
1172+
)
1173+
git_repo.obtain()
1174+
1175+
# Make a commit and push so the repo has a valid HEAD
1176+
initial_file = git_repo.path / "initial_file"
1177+
initial_file.write_text("content", encoding="utf-8")
1178+
git_repo.run(["add", str(initial_file)])
1179+
git_repo.run(["commit", "-m", "initial commit"])
1180+
git_repo.run(["push"])
1181+
1182+
# Set rev so symbolic_ref is skipped
1183+
git_repo.rev = "master"
1184+
1185+
# Patch show_ref to return output that contains "refs/remotes/master"
1186+
# but in a format that the regex won't match, triggering
1187+
# GitRemoteRefNotFound
1188+
malformed_show_ref = "not-a-sha refs/remotes/master"
1189+
with patch.object(git_repo.cmd, "show_ref", return_value=malformed_show_ref):
1190+
result = git_repo.update_repo()
1191+
1192+
assert isinstance(result, SyncResult)
1193+
assert result.ok is False
1194+
assert len(result.errors) > 0
1195+
assert any(e.step == "remote-ref-not-found" for e in result.errors)

0 commit comments

Comments
 (0)