test(market): publish temporary QA agent (#88)

Temporary QA-only Agent pointer for validating the real publish,
install, and delist lifecycle.\n\n- Source repo is public and MIT
licensed\n- Pointer is pinned to immutable commit
dd4abbe342f25d0569dba152ce1be3fb867c3474\n- No user data, credentials,
or third-party content\n- A follow-up PR will remove this entry after
installation verification
This commit is contained in:
2026-08-23 18:51:06 +08:00
committed by GitHub
parent 6538f01555
commit 4bc2d41b12
4 changed files with 72 additions and 7 deletions

View File

@@ -0,0 +1,37 @@
{
"id": "qa-market-publish-agent-20260823",
"name": "QA Market Publish Agent 20260823",
"category": "development",
"tags": ["qa", "market", "lifecycle"],
"icon": "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\"><rect width=\"24\" height=\"24\" rx=\"6\" fill=\"#FF9500\"/><path d=\"M7 8h4v7H9l2 3H8l-2-3V9a1 1 0 0 1 1-1Zm6 0h4a1 1 0 0 1 1 1v9h-2v-3h-2v3h-2V9a1 1 0 0 1 1-1Zm1 2v3h2v-3h-2Z\" fill=\"white\"/></svg>",
"latestVersion": "1.0.0",
"i18n": {
"en-US": {
"name": "QA Market Publish Agent 20260823",
"shortDesc": "Temporary Agent for verifying the real publish, install, and delist lifecycle."
},
"zh-CN": {
"name": "QA 市场发布测试 Agent 20260823",
"shortDesc": "用于验证真实上架、安装与下架闭环的一次性 Agent。"
}
},
"maintainer": {
"name": "yi-ge",
"verified": false,
"account": "yi-ge",
"url": "https://github.com/yi-ge/qa-market-publish-agent-20260823"
},
"stewardship": "community",
"license": "MIT",
"redistribution": "allowed",
"source": {
"kind": "git",
"repoUrl": "https://github.com/yi-ge/qa-market-publish-agent-20260823.git",
"repoBranch": "main",
"ref": "dd4abbe342f25d0569dba152ce1be3fb867c3474"
},
"avatar": {
"t": "QA",
"bg": "linear-gradient(135deg, #FF9500, #FF6B00)"
}
}

View File

@@ -1,6 +1,6 @@
{
"name": "DesireCore Official Market",
"version": "1.2.26",
"version": "1.2.27",
"schemaVersion": "1.1.0",
"supportedLocales": ["zh-CN", "en-US"],
"defaultLocale": "en-US",
@@ -26,9 +26,9 @@
"url": "https://github.com/desirecore/market.git"
},
"stats": {
"totalAgents": 1,
"totalAgents": 2,
"totalSkills": 60,
"lastUpdated": "2026-08-09"
"lastUpdated": "2026-08-23"
},
"features": [
"curated-index",

View File

@@ -9,8 +9,10 @@ from __future__ import annotations
import importlib.util
import sys
import tempfile
import unittest
from pathlib import Path
from unittest.mock import patch
VALIDATOR_PATH = Path(__file__).with_name("validate-i18n.py")
@@ -84,5 +86,21 @@ class BuiltinSkillManifestTests(unittest.TestCase):
self.assertEqual(1, len(sort_issues))
class PublishableAgentCatalogTests(unittest.TestCase):
def test_counts_inline_and_pointer_agents(self) -> None:
with tempfile.TemporaryDirectory() as tmp:
root = Path(tmp)
(root / "agents" / "inline-agent").mkdir(parents=True)
(root / "agents" / "inline-agent" / "agent.json").write_text("{}")
(root / "agents" / "pointer-agent").mkdir(parents=True)
(root / "agents" / "pointer-agent" / "entry.json").write_text("{}")
with patch.object(VALIDATOR, "REPO_ROOT", root):
inline, pointers = VALIDATOR.count_publishable_agents()
self.assertEqual(["inline-agent"], inline)
self.assertEqual(["pointer-agent"], pointers)
if __name__ == "__main__":
unittest.main()

View File

@@ -348,6 +348,13 @@ def count_publishable_skills() -> tuple[list[str], list[str]]:
return skill_md_names, entry_names
def count_publishable_agents() -> tuple[list[str], list[str]]:
"""Return inline and pointer Agent IDs represented by the Market catalog."""
agent_json_names = sorted(p.parent.name for p in (REPO_ROOT / "agents").glob("*/agent.json"))
entry_names = sorted(p.parent.name for p in (REPO_ROOT / "agents").glob("*/entry.json"))
return agent_json_names, entry_names
def validate_builtin_skills(report: Report, skill_md_names: list[str]) -> None:
builtin_path = REPO_ROOT / "builtin-skills.json"
builtin = load_json(builtin_path, report, "builtin-skills")
@@ -513,14 +520,17 @@ def validate_url(report: Report, path: str, url: str) -> None:
def validate_market_catalog(report: Report, manifest: dict[str, Any], category_ids: set[str], online: bool) -> None:
agent_files = sorted((REPO_ROOT / "agents").glob("*/agent.json"))
agent_json_names, agent_entry_names = count_publishable_agents()
agent_files = [REPO_ROOT / "agents" / name / "agent.json" for name in agent_json_names]
agent_entry_files = [REPO_ROOT / "agents" / name / "entry.json" for name in agent_entry_names]
skill_md_names, entry_names = count_publishable_skills()
skill_entry_files = sorted((REPO_ROOT / "skills").glob("*/entry.json"))
stats = manifest.get("stats")
if not isinstance(stats, dict):
report.add(Issue("manifest.json", "market-stats", "stats must be an object"))
else:
expected_agents = len(agent_files)
expected_agents = len(agent_json_names) + len(agent_entry_names)
expected_skills = len(skill_md_names) + len(entry_names)
if stats.get("totalAgents") != expected_agents:
report.add(Issue(
@@ -535,7 +545,7 @@ def validate_market_catalog(report: Report, manifest: dict[str, Any], category_i
features = manifest.get("features") or []
if isinstance(features, list) and "verified-only" in features:
for entry_file in sorted((REPO_ROOT / "skills").glob("*/entry.json")):
for entry_file in [*agent_entry_files, *skill_entry_files]:
entry = load_json(entry_file, report, "entry-json")
maintainer = entry.get("maintainer") if isinstance(entry, dict) else None
verified = maintainer.get("verified") if isinstance(maintainer, dict) else None
@@ -549,7 +559,7 @@ def validate_market_catalog(report: Report, manifest: dict[str, Any], category_i
validate_builtin_skills(report, skill_md_names)
for agent_file in agent_files:
validate_agent_json(report, agent_file, category_ids)
for entry_file in sorted((REPO_ROOT / "skills").glob("*/entry.json")):
for entry_file in [*agent_entry_files, *skill_entry_files]:
validate_entry_json(report, entry_file, category_ids, online)