fix(qg): find 14-deploy-log.md in origin/main when absent in feature worktree
ET-013: deployer writes 14-deploy-log.md and merges deploy artifacts into main via a separate PR, so the log lands in origin/main, not the feature branch worktree that check_deploy_status reads via _repo_path(repo, branch). Result: every successful deploy was falsely failed (Deploy log not found) and rolled back deploy->development. Fix: when the log is absent in the worktree, fall back to reading it from origin/main on the shared clone (git fetch origin main + git show origin/main:docs/work-items/<WI>/14-deploy-log.md). Lookup order: worktree -> origin/main -> not found. Fetch/show failures degrade to not found (never raise). Does not touch the merge-gate in gitea.py. Tests: origin/main SUCCESS->PASS (ET-013 case), origin/main FAILED->FAILED, absent everywhere->not found, fetch failure->degrades no exception, worktree log short-circuits main lookup.
This commit is contained in:
@@ -242,6 +242,65 @@ class TestCheckDeployStatus:
|
||||
passed, reason = check_deploy_status("enduro-trails", "ET-011")
|
||||
assert passed is False
|
||||
|
||||
# --- ET-013 path-sync fix: log written to origin/main via separate PR ---
|
||||
|
||||
def test_origin_main_success_passes_when_absent_in_worktree(self, monkeypatch):
|
||||
# Deployer merged 14-deploy-log.md into main via a separate PR; it is NOT
|
||||
# in the feature worktree. Gate must recover it from origin/main -> PASS.
|
||||
# (This is the exact ET-013 regression.)
|
||||
monkeypatch.setattr(
|
||||
"src.qg.checks._deploy_log_from_main",
|
||||
lambda repo, wi: "---\ndeploy_status: SUCCESS\nversion: v0.0.5\n---\n\nLive.\n",
|
||||
)
|
||||
passed, reason = check_deploy_status("enduro-trails", "ET-013")
|
||||
assert passed is True
|
||||
assert "SUCCESS" in reason
|
||||
|
||||
def test_origin_main_failed_fails(self, monkeypatch):
|
||||
# A genuine FAILED log in main must still fail.
|
||||
monkeypatch.setattr(
|
||||
"src.qg.checks._deploy_log_from_main",
|
||||
lambda repo, wi: "---\ndeploy_status: FAILED\nversion: v0.0.5\n---\n\nboom.\n",
|
||||
)
|
||||
passed, reason = check_deploy_status("enduro-trails", "ET-013")
|
||||
assert passed is False
|
||||
assert "FAILED" in reason
|
||||
|
||||
def test_absent_everywhere_fails(self, monkeypatch):
|
||||
# Not in worktree and origin/main lookup yields nothing -> not found.
|
||||
monkeypatch.setattr(
|
||||
"src.qg.checks._deploy_log_from_main", lambda repo, wi: None
|
||||
)
|
||||
passed, reason = check_deploy_status("enduro-trails", "ET-013")
|
||||
assert passed is False
|
||||
assert "not found" in reason.lower()
|
||||
|
||||
@patch("src.qg.checks.subprocess.run")
|
||||
@patch("src.qg.checks.os.path.isdir", return_value=True)
|
||||
def test_fetch_failure_degrades_no_exception(self, mock_isdir, mock_run):
|
||||
# git fetch/show raising (e.g. network) must degrade to "not found",
|
||||
# never propagate an exception out of the gate.
|
||||
import subprocess as _sp
|
||||
mock_run.side_effect = _sp.TimeoutExpired(cmd="git", timeout=30)
|
||||
passed, reason = check_deploy_status("enduro-trails", "ET-013")
|
||||
assert passed is False
|
||||
assert "not found" in reason.lower()
|
||||
|
||||
def test_worktree_log_short_circuits_main_lookup(self, setup_work_item_dir, monkeypatch):
|
||||
# If the log IS present in the worktree, origin/main must NOT be consulted.
|
||||
self._write_log(
|
||||
setup_work_item_dir,
|
||||
"---\ndeploy_status: SUCCESS\nversion: v0.0.3\n---\n\nDeployed OK.\n",
|
||||
)
|
||||
called = {"n": 0}
|
||||
def _boom(repo, wi):
|
||||
called["n"] += 1
|
||||
return None
|
||||
monkeypatch.setattr("src.qg.checks._deploy_log_from_main", _boom)
|
||||
passed, reason = check_deploy_status("enduro-trails", "ET-011")
|
||||
assert passed is True
|
||||
assert called["n"] == 0
|
||||
|
||||
def test_deploy_stage_qg_is_check_deploy_status(self):
|
||||
assert get_qg_for_stage("deploy") == "check_deploy_status"
|
||||
|
||||
|
||||
Reference in New Issue
Block a user