Files
codex-app-server/cloud/tests/test_skill_appendix_provenance.py
T

121 lines
4.9 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""Appendix provenance for the software-engineering skill.
Background (see cloud/skills/software-engineering/CHANGELOG.md):
SKILL.md claimed its appendix was "spliced verbatim from
templates/roles/ by a generation script, see CHANGELOG", but the pack
contained none of the three: no templates/, no CHANGELOG.md, no
script. The appendix was therefore unverifiable -- a drifted chapter
would pass CI silently.
This file pins the repair, and nothing else:
- tools/check_appendix.py passes on the real pack (all twelve
chapters match their templates/roles/ sources, CHANGELOG present
and mentioning templates/roles/);
- the same script FAILS (nonzero exit) on a temp copy with a source
deleted, a source tampered, or an extra source added -- so a future
"source missing" or "appendix drifted" state cannot slip through;
- --emit output is exactly the eleven headings plus the twelve
source bodies, so the spliced form is itself derived from the
sources.
Scope guard: these tests never edit SKILL.md or the sources. A
failure here is an editorial decision for the skill's own track
(edit the source, then re-issue the appendix), not a silent rewrite.
Seeding behaviour is pinned elsewhere (test_skill_seed_pinning.py):
templates/, tools/ and CHANGELOG.md never reach the tenant home,
because skill_seed copies SKILL.md only.
"""
import shutil
import subprocess
import sys
import tempfile
import unittest
from pathlib import Path
REPO_ROOT = Path(__file__).resolve().parents[2]
SKILL_DIR = (REPO_ROOT / "cloud" / "skills" / "software-engineering")
SCRIPT = SKILL_DIR / "tools" / "check_appendix.py"
EXPECTED_IDS = ["00-coordinator", "01-product-manager", "02-architect",
"03-designer", "04-client-developer", "05-backend",
"06-qa", "07-acceptance", "08-shared-core",
"09-ops", "10-iteration",
"11-auditor"]
def _run(skill_dir=None):
cmd = [sys.executable, str(SCRIPT)]
if skill_dir is not None:
cmd += ["--skill-dir", str(skill_dir)]
return subprocess.run(cmd, capture_output=True, text=True)
def _temp_skill_copy(testcase):
tmp = tempfile.TemporaryDirectory()
testcase.addCleanup(tmp.cleanup)
dest = Path(tmp.name) / "software-engineering"
shutil.copytree(SKILL_DIR, dest)
return dest
class AppendixProvenanceTestCase(unittest.TestCase):
def test_check_script_passes_on_real_pack(self):
self.assertTrue(SCRIPT.is_file(), "校验脚本缺失:%s" % SCRIPT)
self.assertTrue(
(SKILL_DIR / "CHANGELOG.md").is_file(),
"CHANGELOG.md 缺失,SKILL.md 的“见 CHANGELOG”仍是悬空引用")
result = _run()
self.assertEqual(
result.returncode, 0,
"校验脚本应在真实包上通过,stderr:%s" % result.stderr)
self.assertIn("12 chapters match", result.stdout)
def test_missing_source_fails(self):
skill = _temp_skill_copy(self)
(skill / "templates" / "roles" / "03-designer.md").unlink()
result = _run(skill)
self.assertNotEqual(
result.returncode, 0, "源缺失时校验必须失败退出非零")
self.assertIn("03-designer", result.stderr)
def test_tampered_source_fails(self):
skill = _temp_skill_copy(self)
target = skill / "templates" / "roles" / "06-qa.md"
text = target.read_text(encoding="utf-8")
target.write_text(text.replace("不把“没测”写成“通过”",
"不把“没测”写成“通过X”"),
encoding="utf-8")
result = _run(skill)
self.assertNotEqual(
result.returncode, 0, "源被改动时校验必须失败退出非零")
self.assertIn("06-qa", result.stderr)
def test_extra_source_fails(self):
skill = _temp_skill_copy(self)
(skill / "templates" / "roles" / "99-extra.md").write_text(
"# 角色模板:多余\n", encoding="utf-8")
result = _run(skill)
self.assertNotEqual(
result.returncode, 0, "多余源文件时校验必须失败退出非零")
self.assertIn("99-extra", result.stderr)
def test_emit_is_headings_plus_sources(self):
result = subprocess.run(
[sys.executable, str(SCRIPT), "--emit"],
capture_output=True, text=True)
self.assertEqual(result.returncode, 0, result.stderr)
expected_parts = []
for cid in EXPECTED_IDS:
heading = ("## 附录角色:%s(来源 templates/roles/%s.md)"
% (cid, cid))
source = (SKILL_DIR / "templates" / "roles"
/ (cid + ".md")).read_text(encoding="utf-8")
expected_parts.append(heading + "\n\n" + source.rstrip("\n"))
self.assertEqual(result.stdout, "\n\n".join(expected_parts) + "\n",
"--emit 输出须恰为 12 章标题 + 12 份源正文")
if __name__ == "__main__":
unittest.main()