agent-exec transport 13e9c9807fd03b453a03da384041d045/c2d965af28b4
This commit is contained in:
File diff suppressed because one or more lines are too long
@@ -66,3 +66,4 @@
|
||||
- [DHF 模板接入 RDM 文档生成管线](modules/documents/docs/proposals/2026-10-14-dhf-template-generation-plan.md):DHF 模板资产合同与文档生成转换。
|
||||
- [RDM 模板与两树一网对话文档生成](modules/documents/docs/proposals/2026-10-11-rdm-template-dialog-generation.md):历史资产盘点、模板版本与章节实例化、持久提案、文档级审签及 DOCX。
|
||||
- [RDM 最终目标核对与完整交付](modules/documents/docs/proposals/2026-10-11-rdm-final-target-completion.md):系统级项目、权限覆盖、私域协作、全部文档三权与完整业务链的需求映射。
|
||||
- [快慢通道 U2/U3 实施计划](modules/documents/docs/proposals/2026-10-13-fast-slow-u2-u3-plan.md):r0.5 [书面设计](modules/documents/docs/proposals/2026-10-14-fast-slow-u3-r05-terminal-evidence-and-fast-feedback-design.md)与[实施计划](modules/documents/docs/proposals/2026-10-14-fast-slow-u3-r05-terminal-evidence-and-fast-feedback-implementation-plan.md)已评审;r0.4 A01 FAIL,r0.5 真实 A01 NOT_RUN。r0.6 [传输完成性设计](modules/documents/docs/proposals/2026-10-14-fast-slow-u3-r06-transport-completeness-design.md)与[实施计划](modules/documents/docs/proposals/2026-10-14-fast-slow-u3-r06-transport-evidence-implementation-plan.md)已获人工批准,零付费实施中;付费探针与 A01 须另行资源提案。八个 PR 仍为草稿,见[核验报告](modules/documents/docs/reports/2026-10-13-fast-slow-u2-u3-validation.md)。
|
||||
|
||||
Vendored
+1
-1
Submodule deps/agent-intent updated: 343b1e6d9a...651a2e1e03
Vendored
+1
-1
Submodule deps/agent-native updated: c348441691...28f3ea7674
Vendored
+1
-1
Submodule deps/agent-squad updated: 490b0b2dfa...11ff3a0a9c
+1
-1
Submodule modules/backend updated: 69177feb3b...f010af2ff4
+1
-1
Submodule modules/core updated: 1a5ef6b75c...0cf03446c4
+1
-1
Submodule modules/documents updated: 6973a996ae...82b4619574
+1
-1
Submodule modules/frontend updated: 9df5517e9c...6f8e7ec604
+1
-1
Submodule modules/ui updated: 5c2b1036dd...fadabceade
+7
-3
@@ -9,6 +9,7 @@
|
||||
import os
|
||||
import shutil
|
||||
import subprocess
|
||||
import socket
|
||||
import tempfile
|
||||
import unittest
|
||||
import urllib.error
|
||||
@@ -362,9 +363,12 @@ class SvcDbRecoveryTests(unittest.TestCase):
|
||||
|
||||
def test_external_dsn_and_conffile_recover_on_bare_start(self):
|
||||
d = self.runtime("dev")
|
||||
self.seed_agent("dev", 18795,
|
||||
"--addr 127.0.0.1:18795 --data %s --plugins %s --auth auto"
|
||||
% (d / "data", d / "plugins"))
|
||||
with socket.socket() as reservation:
|
||||
reservation.bind(("127.0.0.1", 0))
|
||||
port = reservation.getsockname()[1]
|
||||
self.seed_agent("dev", port,
|
||||
"--addr 127.0.0.1:%d --data %s --plugins %s --auth auto"
|
||||
% (port, d / "data", d / "plugins"))
|
||||
external = "postgresql://extu:ext-pw-4@10.9.8.7:5432/extdb"
|
||||
self.clear_serve_record()
|
||||
self.svc("dev", "deploy", "--from", "local", "--dsn", external)
|
||||
|
||||
+22
-14
@@ -184,6 +184,12 @@ esac
|
||||
"""
|
||||
|
||||
|
||||
def free_fixture_port():
|
||||
with socket.socket() as reservation:
|
||||
reservation.bind(("127.0.0.1", 0))
|
||||
return reservation.getsockname()[1]
|
||||
|
||||
|
||||
class SvcAutoRecoverTests(unittest.TestCase):
|
||||
def setUp(self):
|
||||
tmp = tempfile.mkdtemp(prefix="svc-autorecover-")
|
||||
@@ -330,14 +336,14 @@ class SvcAutoRecoverTests(unittest.TestCase):
|
||||
# ---- 1. 期望态:start 成功置位 / 显式 stop 清除(含进程已死的 stop)----
|
||||
|
||||
def test_start_records_autostart_and_stop_clears(self):
|
||||
self.seed_agent("dev", 18801)
|
||||
self.seed_agent("dev", free_fixture_port())
|
||||
self.start_ok("dev")
|
||||
self.assertEqual(self.kv("dev", "autostart"), "1")
|
||||
self.svc("dev", "stop")
|
||||
self.assertIsNone(self.kv("dev", "autostart"))
|
||||
|
||||
def test_stop_on_dead_process_clears_autostart(self):
|
||||
self.seed_agent("dev", 18802)
|
||||
self.seed_agent("dev", free_fixture_port())
|
||||
self.start_ok("dev")
|
||||
self.kill9("dev")
|
||||
# 进程已死但 autostart 保留(等待 recover);显式 stop 表示"保持停止"。
|
||||
@@ -348,7 +354,8 @@ class SvcAutoRecoverTests(unittest.TestCase):
|
||||
# ---- 2. recover:崩溃拉回 / 运行中不动 / 显式停止不复活 / 未启动不碰 ----
|
||||
|
||||
def test_recover_restarts_sigkilled_service(self):
|
||||
self.seed_agent("dev", 18803)
|
||||
port = free_fixture_port()
|
||||
self.seed_agent("dev", port)
|
||||
self.start_ok("dev")
|
||||
old_pid = self.kill9("dev")
|
||||
result = self.svc("recover", "--quiet")
|
||||
@@ -361,10 +368,10 @@ class SvcAutoRecoverTests(unittest.TestCase):
|
||||
ok_events = self.history_events("dev", "recover")
|
||||
self.assertTrue(any('"result":"ok"' in l for l in ok_events), ok_events)
|
||||
# 恢复用的仍是持久化 exec=(svc.env 登记未漂移)。
|
||||
self.assertIn("--addr 127.0.0.1:18803", self.kv("dev", "exec"))
|
||||
self.assertIn("--addr 127.0.0.1:%d" % port, self.kv("dev", "exec"))
|
||||
|
||||
def test_recover_noop_while_running(self):
|
||||
self.seed_agent("dev", 18804)
|
||||
self.seed_agent("dev", free_fixture_port())
|
||||
self.start_ok("dev")
|
||||
pid = self.pid_of("dev")
|
||||
result = self.svc("recover")
|
||||
@@ -373,7 +380,7 @@ class SvcAutoRecoverTests(unittest.TestCase):
|
||||
self.assertEqual(self.history_events("dev", "recover"), [])
|
||||
|
||||
def test_recover_respects_explicit_stop(self):
|
||||
self.seed_agent("dev", 18805)
|
||||
self.seed_agent("dev", free_fixture_port())
|
||||
self.start_ok("dev")
|
||||
self.svc("dev", "stop")
|
||||
result = self.svc("recover")
|
||||
@@ -382,7 +389,7 @@ class SvcAutoRecoverTests(unittest.TestCase):
|
||||
self.assertEqual(self.history_events("dev", "recover"), [])
|
||||
|
||||
def test_recover_ignores_never_started_service(self):
|
||||
self.seed_agent("dev", 18806) # 从未 start:无 autostart 键
|
||||
self.seed_agent("dev", free_fixture_port()) # 从未 start:无 autostart 键
|
||||
result = self.svc("recover")
|
||||
self.assertEqual(result.returncode, 0, result.stdout)
|
||||
self.assertFalse(self.pid_alive(self.pid_of("dev")))
|
||||
@@ -391,7 +398,7 @@ class SvcAutoRecoverTests(unittest.TestCase):
|
||||
# ---- 3. 并发防护:生命周期锁被持有时 recover 跳过 ----
|
||||
|
||||
def test_recover_skips_when_lock_held(self):
|
||||
self.seed_agent("dev", 18807)
|
||||
self.seed_agent("dev", free_fixture_port())
|
||||
self.start_ok("dev")
|
||||
self.kill9("dev")
|
||||
lock = self.runtime("dev") / ".start.lock"
|
||||
@@ -411,11 +418,12 @@ class SvcAutoRecoverTests(unittest.TestCase):
|
||||
# ---- 4. 恢复失败路径:端口被外部进程占用 → fail 事件 + 非零退出 ----
|
||||
|
||||
def test_recover_fails_when_port_stolen(self):
|
||||
self.seed_agent("dev", 18808)
|
||||
port = free_fixture_port()
|
||||
self.seed_agent("dev", port)
|
||||
self.start_ok("dev")
|
||||
self.kill9("dev")
|
||||
thief = subprocess.Popen(
|
||||
["python3", "-m", "http.server", "18808", "--bind", "127.0.0.1"],
|
||||
["python3", "-m", "http.server", str(port), "--bind", "127.0.0.1"],
|
||||
stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
|
||||
self.addCleanup(thief.terminate)
|
||||
# 等 thief 真正完成 bind 再 recover——慢环境里 Popen 返回时端口尚未监听,
|
||||
@@ -426,7 +434,7 @@ class SvcAutoRecoverTests(unittest.TestCase):
|
||||
s = socket.socket()
|
||||
s.settimeout(0.2)
|
||||
try:
|
||||
s.connect(("127.0.0.1", 18808))
|
||||
s.connect(("127.0.0.1", port))
|
||||
bound = True
|
||||
except OSError:
|
||||
pass
|
||||
@@ -479,7 +487,7 @@ class SvcAutoRecoverTests(unittest.TestCase):
|
||||
"""模拟 update 误清 pidfile 后本 bin 仍占口:裸 start 应回收并拉起新进程。"""
|
||||
if not self._ss_can_attribute_pid():
|
||||
self.skipTest("ss -ltnp 无法归因 PID(reclaim_own_port 需此能力)")
|
||||
port = 18812
|
||||
port = free_fixture_port()
|
||||
self.seed_agent("dev", port)
|
||||
self.start_ok("dev")
|
||||
old = self.pid_of("dev")
|
||||
@@ -500,7 +508,7 @@ class SvcAutoRecoverTests(unittest.TestCase):
|
||||
|
||||
def test_start_rejects_foreign_port_occupant(self):
|
||||
"""外部 python http.server 占口:start 必须失败并提示非本服务。"""
|
||||
port = 18813
|
||||
port = free_fixture_port()
|
||||
self.seed_agent("dev", port)
|
||||
thief = subprocess.Popen(
|
||||
["python3", "-m", "http.server", str(port), "--bind", "127.0.0.1"],
|
||||
@@ -537,7 +545,7 @@ class SvcAutoRecoverTests(unittest.TestCase):
|
||||
self.assertFalse((unit_dir / "agent-system-svc-recover.service").exists())
|
||||
|
||||
def test_recover_reserved_name_rejected(self):
|
||||
self.seed_agent("recover", 18809) # 保留字不可作服务名
|
||||
self.seed_agent("recover", free_fixture_port()) # 保留字不可作服务名
|
||||
result = self.svc("recover", "recover", check=False)
|
||||
self.assertEqual(result.returncode, 2, result.stdout)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user