agent-exec transport ba0e59b31b8b4e18bc7e8df0ab680c80/ccf1734d8c5b
This commit is contained in:
@@ -940,8 +940,9 @@ class TaskRecoveryTests(unittest.TestCase):
|
||||
|
||||
def test_local_precheck_without_cached_ref_allows_comparable_forward_target(self):
|
||||
"""Cached refs/remotes/origin/main is absent; origin advertises
|
||||
the task's R0 (or a forward descendant that is locally
|
||||
available). Local precheck must continue to advertise
|
||||
a forward descendant of the task's R0 (the descendant is
|
||||
locally available so ancestry can be compared against the
|
||||
registered baseline). Local precheck must continue to advertise
|
||||
LOCAL_READY — the explicit missing-tracking-ref continuation
|
||||
contract — because the registered task context is the same as
|
||||
the cached ref would have represented, and the ls-remote
|
||||
@@ -957,11 +958,20 @@ class TaskRecoveryTests(unittest.TestCase):
|
||||
f = self.fixture
|
||||
head_before = f.git(self.wt, "rev-parse", "HEAD").stdout
|
||||
r0 = f.git(self.wt, "rev-parse", "origin/main").stdout.strip()
|
||||
# Advance the bare remote so the advertised target is a real
|
||||
# forward descendant of R0 (not R0 itself). The descendant must
|
||||
# be locally available so the wrapper can advertise it AND the
|
||||
# production `cat-file -e` check inside target_history() succeeds
|
||||
# when the descendant is compared against the registered R0.
|
||||
f.advance_remote()
|
||||
forward = f.git(self.wt, "rev-parse", "origin/main").stdout.strip()
|
||||
self.assertNotEqual(forward, r0)
|
||||
self.assertEqual(f.git(f.repo, "merge-base", r0, forward).stdout.strip(), r0)
|
||||
f.git(self.wt, "branch", "--unset-upstream", "feature/test")
|
||||
f.git(self.wt, "update-ref", "-d", "refs/remotes/origin/main")
|
||||
|
||||
bin_dir, wrapper_log = self.write_no_cache_wrapper(
|
||||
f.root / "no-cache-comparable-bin", r0)
|
||||
f.root / "no-cache-comparable-bin", forward)
|
||||
path_env = f"{bin_dir}{os.pathsep}{f.env.get('PATH', os.environ['PATH'])}"
|
||||
wrapper_env = f.env | {"PATH": path_env}
|
||||
|
||||
@@ -969,12 +979,12 @@ class TaskRecoveryTests(unittest.TestCase):
|
||||
env=wrapper_env | {"TASK_WORK_MODE": "local"})
|
||||
|
||||
log = wrapper_log.read_text()
|
||||
self.assertIn(f"configured advertised-target={r0}", log)
|
||||
self.assertIn(f"configured advertised-target={forward}", log)
|
||||
self.assertIn("fetch-intercepted", log)
|
||||
# Reachable advertised cases must verify the wrapper actually
|
||||
# served the expected ls-remote response (the registered R0), not
|
||||
# only that the wrapper binary was loaded.
|
||||
self.assertIn(f"ls-remote-served advertised-target={r0}", log)
|
||||
# served the expected ls-remote response (a forward descendant of
|
||||
# the registered R0), not only that the wrapper binary was loaded.
|
||||
self.assertIn(f"ls-remote-served advertised-target={forward}", log)
|
||||
|
||||
self.assertEqual(local.returncode, 0, local.stdout)
|
||||
self.assertIn("[verdict] LOCAL_READY", local.stdout)
|
||||
@@ -1203,6 +1213,83 @@ class TaskRecoveryTests(unittest.TestCase):
|
||||
"the registered task card must not embed the receipt sha256 "
|
||||
"when the resume refuses to import the receipt")
|
||||
|
||||
def test_local_precheck_rejects_reachable_auth_failure(self):
|
||||
"""Standalone: capture a valid TASK_WORK_MODE=local precheck
|
||||
receipt against the real bare remote (origin is reachable and
|
||||
serves refs/heads/main normally), then point the worktree at a
|
||||
disposable wrapper that makes `fetch` and
|
||||
`ls-remote origin refs/heads/main` both fail with explicit
|
||||
"Authentication failed (HTTP 401)" and run `task precheck` in
|
||||
local mode.
|
||||
|
||||
The wrapper proves the wrapper was actually used (configured
|
||||
marker, fetch-auth-failure, ls-remote-auth-failure). The
|
||||
precheck must NOT yield the local-continuation positive signal:
|
||||
it must refuse with REMOTE_UNVERIFIED, must not advertise
|
||||
LOCAL_READY, and HEAD must remain untouched.
|
||||
|
||||
Currently RED on current code: when fetch and ls-remote both
|
||||
fail (auth error), `target_history` sees an empty listing and
|
||||
short-circuits to "ok", so the unreachable-fallback branch falls
|
||||
through to LOCAL_READY even though the authoritative target
|
||||
head cannot be confirmed. This test records the expected red
|
||||
result so the post-fix implementation refuses the auth-failure
|
||||
scenario instead of silently promoting the registered task to
|
||||
local continuation.
|
||||
|
||||
The test owns its own disposable git wrapper so the auth
|
||||
failure scenario does not leak into the wrapper shared with
|
||||
the no-cache tests; the existing
|
||||
test_local_resume_rejects_reachable_auth_failure (unreachable
|
||||
origin under resume) and test_local_child_resume_rejects_missing_parent_sha
|
||||
(unreachable origin with a child task missing parent_head_sha)
|
||||
are preserved."""
|
||||
f = self.fixture
|
||||
head_before = f.git(self.wt, "rev-parse", "HEAD").stdout
|
||||
# (1) Build the disposable auth-failure wrapper so the wrapper
|
||||
# intercepts fetch (128, Authentication failed) and
|
||||
# ls-remote origin refs/heads/main (128, Authentication failed,
|
||||
# no value served) while everything else delegates to the real
|
||||
# git. The wrapper's own marker line proves it was loaded.
|
||||
bin_dir, wrapper_log = self.write_no_cache_wrapper(
|
||||
f.root / "auth-failure-precheck-bin", "",
|
||||
log_name="auth-failure-precheck.log",
|
||||
auth_fail=True)
|
||||
path_env = f"{bin_dir}{os.pathsep}{f.env.get('PATH', os.environ['PATH'])}"
|
||||
wrapper_env = (f.env | {"PATH": path_env}
|
||||
| {"TASK_WORK_MODE": "local"})
|
||||
# (2) Local precheck under the auth-failure wrapper must refuse.
|
||||
local = f.task(self.wt, "precheck", check=False, env=wrapper_env)
|
||||
# Proof the wrapper was actually used.
|
||||
log = wrapper_log.read_text()
|
||||
self.assertIn("configured auth-failure-mode", log)
|
||||
self.assertIn("fetch-auth-failure", log)
|
||||
self.assertIn("ls-remote-auth-failure", log)
|
||||
# Auth-failure scenarios must assert the wrapper actually
|
||||
# served the expected (auth) response, not only that the
|
||||
# wrapper initialized.
|
||||
self.assertNotEqual(local.returncode, 0,
|
||||
f"local precheck must reject when fetch and ls-remote both "
|
||||
f"return Authentication failed (HTTP 401); current code "
|
||||
f"falls through target_history's empty-listing short-"
|
||||
f"circuit and reaches LOCAL_READY, returning 0; "
|
||||
f"stdout={local.stdout}")
|
||||
self.assertIn("[verdict] REMOTE_UNVERIFIED", local.stdout,
|
||||
"the precheck must surface REMOTE_UNVERIFIED when the reachable "
|
||||
"origin returns Authentication failed (HTTP 401) instead of an "
|
||||
"advertised target head; the post-fix implementation must not "
|
||||
"promote the registered task to local continuation under an "
|
||||
f"authoritative-but-unverifiable remote; stdout={local.stdout}")
|
||||
self.assertNotIn("[verdict] LOCAL_READY", local.stdout,
|
||||
"the precheck must NOT advertise LOCAL_READY when the "
|
||||
"reachable origin returns Authentication failed (HTTP 401); "
|
||||
"accepting the registered task would advertise local "
|
||||
"continuation for a target head that cannot be confirmed "
|
||||
f"against origin; stdout={local.stdout}")
|
||||
self.assertEqual(f.git(self.wt, "rev-parse", "HEAD").stdout, head_before,
|
||||
"the task HEAD must not move when the precheck refuses to "
|
||||
"advertise LOCAL_READY")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
|
||||
Reference in New Issue
Block a user