|
|
@@ -347,3 +347,115 @@ def test_run_agent_produces_review_report(tmp_path, http_client):
|
|
|
f"review_report.md not found in {workspace}"
|
|
|
assert os.path.isfile(os.path.join(workspace, "review_result.json")), \
|
|
|
f"review_result.json not found in {workspace}"
|
|
|
+
|
|
|
+
|
|
|
+# ── Pack update (zip upload → agents on latest config) ───────────────────────
|
|
|
+# AUDIT 后新增功能: POST /agentpacks/{id}/update-upload。HTTP 层的 loopback
|
|
|
+# 门单测 + 核心传播逻辑直测(与 install 的测试策略一致)。
|
|
|
+
|
|
|
+def _pack_zip_with_prompt(tmp_path, pack_id: str, new_prompt: str,
|
|
|
+ new_version: str = "") -> str:
|
|
|
+ """复制内置包源码,改 entrypoint 的 systemPrompt(和可选版本号),打成 zip。"""
|
|
|
+ import shutil
|
|
|
+ repo_root = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
|
|
+ src = os.path.join(repo_root, "agentexample", "agentpacks", pack_id)
|
|
|
+ if not os.path.isdir(src):
|
|
|
+ pytest.skip(f"built-in pack not found on disk: {src}")
|
|
|
+ work = str(tmp_path / "pack_v2_src")
|
|
|
+ shutil.copytree(src, work)
|
|
|
+
|
|
|
+ with open(os.path.join(work, "manifest.yml"), encoding="utf-8") as f:
|
|
|
+ manifest = yaml.safe_load(f)
|
|
|
+ if new_version:
|
|
|
+ manifest["version"] = new_version
|
|
|
+ with open(os.path.join(work, "manifest.yml"), "w", encoding="utf-8") as f:
|
|
|
+ yaml.safe_dump(manifest, f, allow_unicode=True)
|
|
|
+
|
|
|
+ entry = os.path.join(work, manifest["entrypoint"])
|
|
|
+ with open(entry, encoding="utf-8") as f:
|
|
|
+ cfg = yaml.safe_load(f)
|
|
|
+ cfg["systemPrompt"] = new_prompt
|
|
|
+ with open(entry, "w", encoding="utf-8") as f:
|
|
|
+ yaml.safe_dump(cfg, f, allow_unicode=True)
|
|
|
+
|
|
|
+ zip_path = str(tmp_path / "pack_v2.zip")
|
|
|
+ with zipfile.ZipFile(zip_path, "w", zipfile.ZIP_DEFLATED) as zf:
|
|
|
+ for dirpath, _, filenames in os.walk(work):
|
|
|
+ for fname in filenames:
|
|
|
+ full = os.path.join(dirpath, fname)
|
|
|
+ zf.write(full, os.path.relpath(full, work))
|
|
|
+ return zip_path
|
|
|
+
|
|
|
+
|
|
|
+def test_update_upload_loopback_blocked(http_client, tmp_path):
|
|
|
+ """非 loopback 调 update-upload → 404(同 /install 的 oracle 防御)。"""
|
|
|
+ client, key, packs_dir, pack = http_client
|
|
|
+ zip_path = _pack_zip_with_prompt(tmp_path, REVIEWER_ID, "x")
|
|
|
+ with open(zip_path, "rb") as f:
|
|
|
+ r = client.post(
|
|
|
+ f"/api/v1/agentpacks/{REVIEWER_ID}/update-upload",
|
|
|
+ files={"file": ("pack.zip", f, "application/zip")},
|
|
|
+ headers=_auth(key),
|
|
|
+ )
|
|
|
+ assert r.status_code == 404
|
|
|
+
|
|
|
+
|
|
|
+def test_peek_manifest_id_check(tmp_path, http_client):
|
|
|
+ """peek_manifest_from_zip 返回正确 id(更新端点据此拒绝错包)。"""
|
|
|
+ from agentpaas.engine.agentpack_store import peek_manifest_from_zip
|
|
|
+ zip_path = _pack_zip_with_prompt(tmp_path, REVIEWER_ID, "NEW PROMPT")
|
|
|
+ m = peek_manifest_from_zip(zip_path)
|
|
|
+ assert m.id == REVIEWER_ID
|
|
|
+ # zip 不会被安装
|
|
|
+ assert not os.path.exists(
|
|
|
+ os.path.join(str(tmp_path), "agentpacks", REVIEWER_ID, "0.9.9"))
|
|
|
+
|
|
|
+
|
|
|
+def test_pack_update_refreshes_agents(http_client, tmp_path):
|
|
|
+ """更新包 → 由它创建的智能体生成新版本、配置换新、可回滚。"""
|
|
|
+ from agentpaas.api.v1.agentpacks import _refresh_agents_from_pack
|
|
|
+
|
|
|
+ client, key, packs_dir, pack = http_client
|
|
|
+
|
|
|
+ # 1. 从包创建智能体(HTTP)
|
|
|
+ r = client.post(
|
|
|
+ f"/api/v1/agentpacks/{REVIEWER_ID}/create-agent",
|
|
|
+ json={"name": "更新测试-审稿"},
|
|
|
+ headers=_auth(key),
|
|
|
+ )
|
|
|
+ assert r.status_code == 201, r.text[:300]
|
|
|
+ agent_id = r.json()["agent_id"]
|
|
|
+
|
|
|
+ g0 = client.get(f"/api/v1/agents/{agent_id}", headers=_auth(key)).json()
|
|
|
+ old_prompt = g0["config"]["systemPrompt"]
|
|
|
+ assert g0["current_version"] == 1
|
|
|
+
|
|
|
+ # 2. 构造新版包(新版本号 + 新 systemPrompt)并安装
|
|
|
+ NEW_PROMPT = "你是更新后的审稿专家 v2。"
|
|
|
+ zip_path = _pack_zip_with_prompt(
|
|
|
+ tmp_path, REVIEWER_ID, NEW_PROMPT, new_version="0.9.9")
|
|
|
+ new_pack = _install_pack(packs_dir, zip_path)
|
|
|
+ assert new_pack.version == "0.9.9"
|
|
|
+
|
|
|
+ # 3. 传播到智能体
|
|
|
+ refreshed = _refresh_agents_from_pack(new_pack)
|
|
|
+ assert any(a["agent_id"] == agent_id and a["version"] == 2
|
|
|
+ for a in refreshed)
|
|
|
+
|
|
|
+ # 4. 智能体现在跑最新配置
|
|
|
+ g1 = client.get(f"/api/v1/agents/{agent_id}", headers=_auth(key)).json()
|
|
|
+ assert g1["current_version"] == 2
|
|
|
+ assert g1["config"]["systemPrompt"] == NEW_PROMPT
|
|
|
+ assert g1["config"]["systemPrompt"] != old_prompt
|
|
|
+ # agent_dir 跟到新版本安装路径
|
|
|
+ assert g1["agent_dir"].endswith("0.9.9")
|
|
|
+
|
|
|
+ # 5. 旧配置仍可回滚
|
|
|
+ rb = client.post(
|
|
|
+ f"/api/v1/agents/{agent_id}/rollback",
|
|
|
+ json={"target_version": 1},
|
|
|
+ headers=_auth(key),
|
|
|
+ )
|
|
|
+ assert rb.status_code == 200, rb.text[:300]
|
|
|
+ g2 = client.get(f"/api/v1/agents/{agent_id}", headers=_auth(key)).json()
|
|
|
+ assert g2["config"]["systemPrompt"] == old_prompt
|