merge: fix/agentctl-queue-subparser-2026-09-23 (queue subparser + tests)
This commit is contained in:
@@ -1626,11 +1626,6 @@ def parser():
|
|||||||
hooks.add_parser('install');hc=hooks.add_parser('check');hc.add_argument('--merge',action='store_true');hr=hooks.add_parser('ref');hr.add_argument('state')
|
hooks.add_parser('install');hc=hooks.add_parser('check');hc.add_argument('--merge',action='store_true');hr=hooks.add_parser('ref');hr.add_argument('state')
|
||||||
v=sub.add_parser('validate');v.add_argument('--task',required=True);v.add_argument('command',nargs=argparse.REMAINDER)
|
v=sub.add_parser('validate');v.add_argument('--task',required=True);v.add_argument('command',nargs=argparse.REMAINDER)
|
||||||
i=sub.add_parser('integrate');i.add_argument('--task',required=True);i.add_argument('--expected-dev',required=True);i.add_argument('--request-id')
|
i=sub.add_parser('integrate');i.add_argument('--task',required=True);i.add_argument('--expected-dev',required=True);i.add_argument('--request-id')
|
||||||
q=sub.add_parser('queue').add_subparsers(dest='queue_action',required=True)
|
|
||||||
qe=q.add_parser('enqueue');qe.add_argument('--task',required=True);qe.add_argument('--expected-dev',default='dev');qe.add_argument('--head-sha');qe.add_argument('--priority',type=int,default=0);qe.add_argument('--request-id')
|
|
||||||
ql=q.add_parser('list');ql.add_argument('--all',action='store_true')
|
|
||||||
qc=q.add_parser('cancel');qc.add_argument('--id',required=True);qc.add_argument('--reason',required=True)
|
|
||||||
q.add_parser('schedule')
|
|
||||||
dep=sub.add_parser('deploy').add_subparsers(dest='deploy_action',required=True)
|
dep=sub.add_parser('deploy').add_subparsers(dest='deploy_action',required=True)
|
||||||
dr=dep.add_parser('run-loop')
|
dr=dep.add_parser('run-loop')
|
||||||
dr.add_argument('--task',required=True);dr.add_argument('--id',required=True)
|
dr.add_argument('--task',required=True);dr.add_argument('--id',required=True)
|
||||||
|
|||||||
@@ -654,6 +654,99 @@ class AgentctlTests(unittest.TestCase):
|
|||||||
self.assertEqual(result.returncode, 0, result.stdout + result.stderr)
|
self.assertEqual(result.returncode, 0, result.stdout + result.stderr)
|
||||||
self.assertEqual(self.git("status", "--porcelain", cwd=task), "")
|
self.assertEqual(self.git("status", "--porcelain", cwd=task), "")
|
||||||
|
|
||||||
|
def test_queue_subparser_registered_once(self):
|
||||||
|
"""Duplicate queue registration must not shadow handler-compatible flags."""
|
||||||
|
import importlib.util
|
||||||
|
spec = importlib.util.spec_from_file_location("agentctl_queue_parser", SOURCE)
|
||||||
|
mod = importlib.util.module_from_spec(spec)
|
||||||
|
spec.loader.exec_module(mod)
|
||||||
|
parser = mod.parser()
|
||||||
|
enqueue = parser.parse_args(
|
||||||
|
["queue", "enqueue", "--task", "alpha", "--expected-dev", "dev", "--head", "deadbeef"])
|
||||||
|
self.assertEqual(enqueue.head, "deadbeef")
|
||||||
|
self.assertFalse(hasattr(enqueue, "head_sha"))
|
||||||
|
cancel = parser.parse_args(
|
||||||
|
["queue", "cancel", "--task", "alpha", "--id", "item-1", "--reason", "test"])
|
||||||
|
self.assertEqual(cancel.task, "alpha")
|
||||||
|
schedule = parser.parse_args(["queue", "schedule", "--drain"])
|
||||||
|
self.assertTrue(schedule.drain)
|
||||||
|
# A second registration that only exposes --head-sha must not win.
|
||||||
|
with self.assertRaises(SystemExit):
|
||||||
|
parser.parse_args(
|
||||||
|
["queue", "enqueue", "--task", "alpha", "--expected-dev", "dev", "--head-sha", "deadbeef"])
|
||||||
|
|
||||||
|
def test_queue_enqueue_list_cancel_roundtrip(self):
|
||||||
|
task = self.initialized_task(scopes=(".",), capabilities=("git:dev",))
|
||||||
|
baseline = self.git("rev-parse", "dev")
|
||||||
|
(task / "src/a/feature.py").parent.mkdir(parents=True, exist_ok=True)
|
||||||
|
(task / "src/a/feature.py").write_text("feature = True\n", encoding="utf-8")
|
||||||
|
self.git("add", "src/a/feature.py", cwd=task)
|
||||||
|
change = self.run_lease(
|
||||||
|
"alpha", ["git:index", "path:."],
|
||||||
|
"import subprocess,sys;sys.exit(subprocess.run(['git','commit','-m','feature']).returncode)",
|
||||||
|
cwd=task)
|
||||||
|
self.assertEqual(change.returncode, 0, change.stderr)
|
||||||
|
candidate = self.initialized_task("candidate", scopes=(".",), capabilities=("git:dev",))
|
||||||
|
merged = self.run_lease(
|
||||||
|
"candidate", ["git:index", "git:dev", "path:."],
|
||||||
|
"import subprocess,sys;sys.exit(subprocess.run(['git','merge','--no-ff','-m','candidate','feature/alpha']).returncode)",
|
||||||
|
cwd=candidate)
|
||||||
|
self.assertEqual(merged.returncode, 0, merged.stderr)
|
||||||
|
head = self.git("rev-parse", "HEAD", cwd=candidate)
|
||||||
|
self.cli("validate", "--task", "candidate", "--", "git", "diff", "--check", baseline, cwd=candidate)
|
||||||
|
enqueued = json.loads(self.cli(
|
||||||
|
"queue", "enqueue", "--task", "candidate", "--expected-dev", baseline,
|
||||||
|
"--head", head, "--request-id", "q-alpha-1", cwd=candidate).stdout)
|
||||||
|
self.assertEqual(enqueued["status"], "enqueued")
|
||||||
|
self.assertEqual(enqueued["item"]["state"], "pending")
|
||||||
|
listed = json.loads(self.cli("queue", "list").stdout)
|
||||||
|
self.assertEqual(len(listed["items"]), 1)
|
||||||
|
self.assertEqual(listed["items"][0]["id"], "q-alpha-1")
|
||||||
|
foreign = self.cli(
|
||||||
|
"queue", "cancel", "--task", "alpha", "--id", "q-alpha-1", "--reason", "wrong owner",
|
||||||
|
cwd=task, check=False)
|
||||||
|
self.assertNotEqual(foreign.returncode, 0)
|
||||||
|
self.assertIn("PATH_OUT_OF_SCOPE", foreign.stderr)
|
||||||
|
cancelled = json.loads(self.cli(
|
||||||
|
"queue", "cancel", "--task", "candidate", "--id", "q-alpha-1", "--reason", "abort",
|
||||||
|
cwd=candidate).stdout)
|
||||||
|
self.assertEqual(cancelled["status"], "cancelled")
|
||||||
|
self.assertEqual(cancelled["item"]["state"], "cancelled")
|
||||||
|
|
||||||
|
def test_queue_schedule_promotes_validated_candidate(self):
|
||||||
|
task = self.initialized_task(scopes=(".",), capabilities=("git:dev",))
|
||||||
|
(task / "src/a/feature.py").parent.mkdir(parents=True, exist_ok=True)
|
||||||
|
(task / "src/a/feature.py").write_text("feature = True\n", encoding="utf-8")
|
||||||
|
self.git("add", "src/a/feature.py", cwd=task)
|
||||||
|
change = self.run_lease(
|
||||||
|
"alpha", ["git:index", "path:."],
|
||||||
|
"import subprocess,sys;sys.exit(subprocess.run(['git','commit','-m','feature']).returncode)",
|
||||||
|
cwd=task)
|
||||||
|
self.assertEqual(change.returncode, 0, change.stderr)
|
||||||
|
baseline = self.git("rev-parse", "dev")
|
||||||
|
candidate = self.initialized_task("candidate", scopes=(".",), capabilities=("git:dev",))
|
||||||
|
merged = self.run_lease(
|
||||||
|
"candidate", ["git:index", "git:dev", "path:."],
|
||||||
|
"import subprocess,sys;sys.exit(subprocess.run(['git','merge','--no-ff','-m','candidate','feature/alpha']).returncode)",
|
||||||
|
cwd=candidate)
|
||||||
|
self.assertEqual(merged.returncode, 0, merged.stderr)
|
||||||
|
head = self.git("rev-parse", "HEAD", cwd=candidate)
|
||||||
|
self.cli("validate", "--task", "candidate", "--", "git", "diff", "--check", baseline, cwd=candidate)
|
||||||
|
self.cli("queue", "enqueue", "--task", "candidate", "--expected-dev", baseline,
|
||||||
|
"--head", head, "--request-id", "q-sched-1", cwd=candidate)
|
||||||
|
denied = self.cli("queue", "schedule", cwd=candidate, check=False)
|
||||||
|
self.assertNotEqual(denied.returncode, 0)
|
||||||
|
self.assertIn("MAIN_WORKTREE_REQUIRED", denied.stderr)
|
||||||
|
scheduled_raw = self.cli("queue", "schedule", cwd=self.root)
|
||||||
|
scheduled = json.loads([line for line in scheduled_raw.stdout.splitlines() if line.startswith("{")][-1])
|
||||||
|
self.assertEqual(scheduled["status"], "ok")
|
||||||
|
self.assertEqual(scheduled["processed"], 1)
|
||||||
|
self.assertEqual(scheduled["results"][0]["status"], "completed")
|
||||||
|
self.assertEqual(self.git("rev-parse", "dev"), head)
|
||||||
|
listed = json.loads(self.cli("queue", "list", "--all").stdout)
|
||||||
|
item = next(i for i in listed["items"] if i["id"] == "q-sched-1")
|
||||||
|
self.assertEqual(item["state"], "completed")
|
||||||
|
|
||||||
|
|
||||||
class ProcessPlatformTests(unittest.TestCase):
|
class ProcessPlatformTests(unittest.TestCase):
|
||||||
@classmethod
|
@classmethod
|
||||||
|
|||||||
Reference in New Issue
Block a user