ad6695245b
ACK 0.19.0 hands test-environment deploys to the deployer skill, documents bug-fix as a first-class scenario, and adds docs/ack/regression.yaml harvest plus a /ack regression run.
205 lines
6.7 KiB
Python
205 lines
6.7 KiB
Python
from __future__ import annotations
|
|
|
|
import subprocess
|
|
import sys
|
|
import tempfile
|
|
import unittest
|
|
from pathlib import Path
|
|
|
|
|
|
REPO_ROOT = Path(__file__).resolve().parents[1]
|
|
SCRIPTS_DIR = REPO_ROOT / "skills" / "ack" / "scripts"
|
|
sys.path.insert(0, str(SCRIPTS_DIR))
|
|
|
|
import validate_knowledge # noqa: E402
|
|
import validate_delivery # noqa: E402
|
|
import validate_tasks # noqa: E402
|
|
from yaml_subset import YamlSubsetError, load_yaml_subset # noqa: E402
|
|
|
|
|
|
class AckYamlSubsetTests(unittest.TestCase):
|
|
def test_real_templates_and_examples_parse_under_clean_python(self) -> None:
|
|
script = """
|
|
import sys
|
|
from pathlib import Path
|
|
sys.path.insert(0, str(Path('skills/ack/scripts').resolve()))
|
|
from yaml_subset import load_yaml_subset
|
|
paths = (
|
|
Path('skills/ack/templates/tasks.template.yaml'),
|
|
Path('skills/ack/examples/tasks.example.yaml'),
|
|
Path('skills/ack/templates/knowledge.template.yaml'),
|
|
Path('skills/ack/examples/knowledge.example.yaml'),
|
|
Path('skills/ack/templates/delivery.template.yaml'),
|
|
Path('skills/ack/examples/delivery.example.yaml'),
|
|
Path('skills/ack/templates/regression.template.yaml'),
|
|
Path('skills/ack/examples/regression.example.yaml'),
|
|
)
|
|
for path in paths:
|
|
value = load_yaml_subset(path.read_text(encoding='utf-8'))
|
|
if not isinstance(value, dict):
|
|
raise SystemExit(f'{path}: top-level value is not a mapping')
|
|
print('parsed=8')
|
|
"""
|
|
result = subprocess.run(
|
|
[sys.executable, "-S", "-c", script],
|
|
cwd=REPO_ROOT,
|
|
text=True,
|
|
capture_output=True,
|
|
check=False,
|
|
)
|
|
|
|
self.assertEqual(result.returncode, 0, result.stderr)
|
|
self.assertEqual(result.stdout.strip(), "parsed=8")
|
|
|
|
def test_tasks_validator_runs_without_site_packages(self) -> None:
|
|
result = subprocess.run(
|
|
[
|
|
sys.executable,
|
|
"-S",
|
|
str(SCRIPTS_DIR / "validate_tasks.py"),
|
|
str(REPO_ROOT / "skills" / "ack" / "examples" / "tasks.example.yaml"),
|
|
],
|
|
cwd=REPO_ROOT,
|
|
text=True,
|
|
capture_output=True,
|
|
check=False,
|
|
)
|
|
|
|
self.assertEqual(result.returncode, 0, result.stderr)
|
|
self.assertIn("任务板校验通过", result.stdout)
|
|
|
|
def test_delivery_validator_runs_without_site_packages(self) -> None:
|
|
result = subprocess.run(
|
|
[
|
|
sys.executable,
|
|
"-S",
|
|
str(SCRIPTS_DIR / "validate_delivery.py"),
|
|
str(REPO_ROOT / "skills" / "ack" / "templates" / "delivery.template.yaml"),
|
|
],
|
|
cwd=REPO_ROOT,
|
|
text=True,
|
|
capture_output=True,
|
|
check=False,
|
|
)
|
|
|
|
self.assertEqual(result.returncode, 0, result.stderr)
|
|
self.assertIn("交付契约校验通过", result.stdout)
|
|
|
|
def test_supported_subset_types_and_block_scalars(self) -> None:
|
|
document = load_yaml_subset(
|
|
"""
|
|
# comment
|
|
root:
|
|
list:
|
|
- null
|
|
- true
|
|
- -2
|
|
- name: 'single quoted'
|
|
flags: [false, "double quoted", {count: 3}]
|
|
emptyList: []
|
|
emptyMap: {}
|
|
folded: >
|
|
first line
|
|
second line
|
|
|
|
next paragraph
|
|
literal: |
|
|
first line
|
|
second line
|
|
"""
|
|
)
|
|
|
|
self.assertEqual(document["root"]["list"][:3], [None, True, -2])
|
|
self.assertEqual(
|
|
document["root"]["list"][3],
|
|
{
|
|
"name": "single quoted",
|
|
"flags": [False, "double quoted", {"count": 3}],
|
|
},
|
|
)
|
|
self.assertEqual(document["root"]["emptyList"], [])
|
|
self.assertEqual(document["root"]["emptyMap"], {})
|
|
self.assertEqual(
|
|
document["root"]["folded"],
|
|
"first line second line\nnext paragraph\n",
|
|
)
|
|
self.assertEqual(
|
|
document["root"]["literal"],
|
|
"first line\nsecond line\n",
|
|
)
|
|
|
|
def test_quoted_mapping_key_supports_yaml_single_quote_escape(self) -> None:
|
|
self.assertEqual(load_yaml_subset("'owner''s-key': value\n"), {"owner's-key": "value"})
|
|
|
|
def test_subset_rejects_duplicate_keys_at_any_depth(self) -> None:
|
|
invalid_documents = (
|
|
"name: first\nname: second\n",
|
|
"outer:\n name: first\n name: second\n",
|
|
"outer: {name: first, name: second}\n",
|
|
)
|
|
for document in invalid_documents:
|
|
with self.subTest(document=document), self.assertRaises(YamlSubsetError):
|
|
load_yaml_subset(document)
|
|
|
|
def test_subset_rejects_unsupported_yaml_instead_of_guessing(self) -> None:
|
|
invalid_documents = (
|
|
"root: &node\n value: 1\ncopy: *node\n",
|
|
"root: !custom value\n",
|
|
"root: {<<: {value: 1}}\n",
|
|
"---\nroot: value\n",
|
|
"root: >-\n value\n",
|
|
"root: 1.25\n",
|
|
"root:\n\tchild: value\n",
|
|
)
|
|
for document in invalid_documents:
|
|
with self.subTest(document=document), self.assertRaises(YamlSubsetError):
|
|
load_yaml_subset(document)
|
|
|
|
|
|
class AckDocumentLoaderTests(unittest.TestCase):
|
|
def test_tasks_yaml_loader_rejects_duplicate_keys_with_pyyaml(self) -> None:
|
|
with tempfile.TemporaryDirectory() as temp_dir:
|
|
path = Path(temp_dir) / "tasks.yaml"
|
|
path.write_text("version: 1\nversion: 2\n", encoding="utf-8")
|
|
|
|
with self.assertRaises(SystemExit) as raised:
|
|
validate_tasks.load_document(path)
|
|
|
|
self.assertEqual(raised.exception.code, 1)
|
|
|
|
def test_knowledge_yaml_loader_rejects_alias_graphs(self) -> None:
|
|
with self.assertRaises(SystemExit) as raised:
|
|
validate_knowledge.load_yaml_text(
|
|
"root: &root\n child: *root\n",
|
|
"知识库",
|
|
)
|
|
|
|
self.assertEqual(raised.exception.code, 1)
|
|
|
|
def test_tasks_json_loader_rejects_duplicate_keys(self) -> None:
|
|
with tempfile.TemporaryDirectory() as temp_dir:
|
|
path = Path(temp_dir) / "tasks.json"
|
|
path.write_text('{"version": 1, "version": 2}', encoding="utf-8")
|
|
|
|
with self.assertRaises(SystemExit) as raised:
|
|
validate_tasks.load_document(path)
|
|
|
|
self.assertEqual(raised.exception.code, 1)
|
|
|
|
def test_knowledge_json_loader_rejects_nested_duplicate_keys(self) -> None:
|
|
with tempfile.TemporaryDirectory() as temp_dir:
|
|
path = Path(temp_dir) / "knowledge.json"
|
|
path.write_text(
|
|
'{"project": {"name": "first", "name": "second"}}',
|
|
encoding="utf-8",
|
|
)
|
|
|
|
with self.assertRaises(SystemExit) as raised:
|
|
validate_knowledge.load_yaml(path, "知识库")
|
|
|
|
self.assertEqual(raised.exception.code, 1)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|