Merge branch 'main' into rename
Keep pouch naming and .pouch/ack project state, and bring in ACK regression mode, deployer test-environment binding, and manage-release updates from main.
This commit is contained in:
@@ -65,10 +65,18 @@ class SkillInitTests(unittest.TestCase):
|
||||
'profiles: {}\n',
|
||||
encoding="utf-8",
|
||||
)
|
||||
(skill / "templates" / "regression.template.yaml").write_text(
|
||||
'updatedAt: "<YYYY-MM-DDTHH:mm:ss+TZ>"\n'
|
||||
'project:\n'
|
||||
' name: "<project_name>"\n'
|
||||
'cases: []\n',
|
||||
encoding="utf-8",
|
||||
)
|
||||
for validator_name in (
|
||||
"validate_tasks.py",
|
||||
"validate_knowledge.py",
|
||||
"validate_delivery.py",
|
||||
"validate_regression.py",
|
||||
):
|
||||
(skill / "scripts" / validator_name).write_text(
|
||||
"raise SystemExit(0)\n",
|
||||
@@ -105,15 +113,19 @@ class SkillInitTests(unittest.TestCase):
|
||||
tasks_content = (target / "tasks.yaml").read_text(encoding="utf-8")
|
||||
knowledge_content = (target / "knowledge.yaml").read_text(encoding="utf-8")
|
||||
delivery_content = (target / "delivery.yaml").read_text(encoding="utf-8")
|
||||
regression_content = (target / "regression.yaml").read_text(encoding="utf-8")
|
||||
self.assertIn("# sample-app", project_content)
|
||||
self.assertIn("version=1.2.3", project_content)
|
||||
self.assertIn(f'repoPath: "{project}"', tasks_content)
|
||||
self.assertIn(f'repoPath: "{project}"', knowledge_content)
|
||||
self.assertIn('name: "sample-app"', delivery_content)
|
||||
self.assertIn("enabled: false", delivery_content)
|
||||
self.assertIn('name: "sample-app"', regression_content)
|
||||
self.assertIn("cases: []", regression_content)
|
||||
self.assertNotIn("<project_name>", tasks_content)
|
||||
self.assertNotIn("<project_name>", knowledge_content)
|
||||
self.assertNotIn("<project_name>", delivery_content)
|
||||
self.assertNotIn("<project_name>", regression_content)
|
||||
|
||||
def test_init_refuses_to_overwrite_existing_files(self) -> None:
|
||||
project = self.home / "existing-app"
|
||||
@@ -130,6 +142,7 @@ class SkillInitTests(unittest.TestCase):
|
||||
self.assertFalse((target / "tasks.yaml").exists())
|
||||
self.assertFalse((target / "knowledge.yaml").exists())
|
||||
self.assertFalse((target / "delivery.yaml").exists())
|
||||
self.assertFalse((target / "regression.yaml").exists())
|
||||
|
||||
def test_init_refuses_to_overwrite_existing_knowledge_file(self) -> None:
|
||||
project = self.home / "existing-knowledge-app"
|
||||
@@ -161,6 +174,22 @@ class SkillInitTests(unittest.TestCase):
|
||||
self.assertFalse((target / "project.md").exists())
|
||||
self.assertFalse((target / "tasks.yaml").exists())
|
||||
self.assertFalse((target / "knowledge.yaml").exists())
|
||||
self.assertFalse((target / "regression.yaml").exists())
|
||||
|
||||
def test_init_refuses_to_overwrite_existing_regression_file(self) -> None:
|
||||
project = self.home / "existing-regression-app"
|
||||
target = project / ".pouch" / "ack"
|
||||
target.mkdir(parents=True)
|
||||
existing = target / "regression.yaml"
|
||||
existing.write_text("keep me", encoding="utf-8")
|
||||
|
||||
result = self.run_pouch("init", "ack", "--project", str(project))
|
||||
|
||||
self.assertNotEqual(result.returncode, 0)
|
||||
self.assertIn("拒绝覆盖已有路径", result.stderr)
|
||||
self.assertEqual(existing.read_text(encoding="utf-8"), "keep me")
|
||||
self.assertFalse((target / "project.md").exists())
|
||||
self.assertFalse((target / "tasks.yaml").exists())
|
||||
|
||||
def test_init_rejects_symlinked_destination_directories(self) -> None:
|
||||
for symlink_level in (".pouch", "ack"):
|
||||
@@ -336,7 +365,13 @@ class SkillInitTests(unittest.TestCase):
|
||||
self.assertEqual(list((project / ".pouch" / "ack").iterdir()), [])
|
||||
self.assertEqual(
|
||||
sorted(path.name for path in moved_target.iterdir()),
|
||||
["delivery.yaml", "knowledge.yaml", "project.md", "tasks.yaml"],
|
||||
[
|
||||
"delivery.yaml",
|
||||
"knowledge.yaml",
|
||||
"project.md",
|
||||
"regression.yaml",
|
||||
"tasks.yaml",
|
||||
],
|
||||
)
|
||||
|
||||
def test_transaction_container_replacement_cannot_forge_payload(self) -> None:
|
||||
@@ -395,7 +430,13 @@ class SkillInitTests(unittest.TestCase):
|
||||
self.assertFalse((target / "marker").exists())
|
||||
self.assertEqual(
|
||||
sorted(path.name for path in target.iterdir()),
|
||||
["delivery.yaml", "knowledge.yaml", "project.md", "tasks.yaml"],
|
||||
[
|
||||
"delivery.yaml",
|
||||
"knowledge.yaml",
|
||||
"project.md",
|
||||
"regression.yaml",
|
||||
"tasks.yaml",
|
||||
],
|
||||
)
|
||||
|
||||
def test_post_publish_fsync_failure_preserves_complete_state(self) -> None:
|
||||
@@ -407,7 +448,7 @@ class SkillInitTests(unittest.TestCase):
|
||||
def fail_directory_fsync_after_publish(file_descriptor: int) -> None:
|
||||
nonlocal calls
|
||||
calls += 1
|
||||
if calls == 8:
|
||||
if calls == 9:
|
||||
raise OSError("simulated directory fsync failure")
|
||||
real_fsync(file_descriptor)
|
||||
|
||||
@@ -433,7 +474,13 @@ class SkillInitTests(unittest.TestCase):
|
||||
target = project / ".pouch" / "ack"
|
||||
self.assertEqual(
|
||||
sorted(path.name for path in target.iterdir()),
|
||||
["delivery.yaml", "knowledge.yaml", "project.md", "tasks.yaml"],
|
||||
[
|
||||
"delivery.yaml",
|
||||
"knowledge.yaml",
|
||||
"project.md",
|
||||
"regression.yaml",
|
||||
"tasks.yaml",
|
||||
],
|
||||
)
|
||||
|
||||
def test_project_root_replacement_aborts_before_publish(self) -> None:
|
||||
@@ -520,7 +567,13 @@ class SkillInitTests(unittest.TestCase):
|
||||
target = moved_project / ".pouch" / "ack"
|
||||
self.assertEqual(
|
||||
sorted(path.name for path in target.iterdir()),
|
||||
["delivery.yaml", "knowledge.yaml", "project.md", "tasks.yaml"],
|
||||
[
|
||||
"delivery.yaml",
|
||||
"knowledge.yaml",
|
||||
"project.md",
|
||||
"regression.yaml",
|
||||
"tasks.yaml",
|
||||
],
|
||||
)
|
||||
|
||||
def test_pouch_dir_replacement_aborts_before_publish(self) -> None:
|
||||
@@ -615,7 +668,13 @@ class SkillInitTests(unittest.TestCase):
|
||||
target = moved_pouch / "ack"
|
||||
self.assertEqual(
|
||||
sorted(path.name for path in target.iterdir()),
|
||||
["delivery.yaml", "knowledge.yaml", "project.md", "tasks.yaml"],
|
||||
[
|
||||
"delivery.yaml",
|
||||
"knowledge.yaml",
|
||||
"project.md",
|
||||
"regression.yaml",
|
||||
"tasks.yaml",
|
||||
],
|
||||
)
|
||||
|
||||
def test_ack_init_requires_knowledge_template(self) -> None:
|
||||
@@ -652,11 +711,29 @@ class SkillInitTests(unittest.TestCase):
|
||||
self.assertIn("delivery.template.yaml", result.stderr)
|
||||
self.assertFalse((project / ".pouch" / "ack").exists())
|
||||
|
||||
def test_ack_init_requires_regression_template(self) -> None:
|
||||
project = self.home / "missing-regression-template-app"
|
||||
project.mkdir()
|
||||
(
|
||||
self.skills_home
|
||||
/ "skills"
|
||||
/ "ack"
|
||||
/ "templates"
|
||||
/ "regression.template.yaml"
|
||||
).unlink()
|
||||
|
||||
result = self.run_pouch("init", "ack", "--project", str(project))
|
||||
|
||||
self.assertNotEqual(result.returncode, 0)
|
||||
self.assertIn("regression.template.yaml", result.stderr)
|
||||
self.assertFalse((project / ".pouch" / "ack").exists())
|
||||
|
||||
def test_ack_init_requires_all_validators(self) -> None:
|
||||
for validator_name in (
|
||||
"validate_tasks.py",
|
||||
"validate_knowledge.py",
|
||||
"validate_delivery.py",
|
||||
"validate_regression.py",
|
||||
):
|
||||
with self.subTest(validator_name=validator_name):
|
||||
project = self.home / f"missing-{validator_name}-app"
|
||||
@@ -715,6 +792,21 @@ class SkillInitTests(unittest.TestCase):
|
||||
self.assertIn("初始化交付契约校验失败", result.stderr)
|
||||
self.assertFalse((project / ".pouch" / "ack").exists())
|
||||
|
||||
def test_regression_validator_failure_leaves_no_partial_initialization(self) -> None:
|
||||
project = self.home / "invalid-regression-app"
|
||||
project.mkdir()
|
||||
validator = (
|
||||
self.skills_home / "skills" / "ack" / "scripts" / "validate_regression.py"
|
||||
)
|
||||
validator.write_text("raise SystemExit(1)\n", encoding="utf-8")
|
||||
|
||||
result = self.run_pouch("init", "ack", "--project", str(project))
|
||||
|
||||
self.assertNotEqual(result.returncode, 0)
|
||||
self.assertNotIn("Traceback", result.stderr)
|
||||
self.assertIn("初始化回归目录校验失败", result.stderr)
|
||||
self.assertFalse((project / ".pouch" / "ack").exists())
|
||||
|
||||
def test_validator_cannot_replace_staged_bytes_before_install(self) -> None:
|
||||
project = self.home / "mutated-staging-app"
|
||||
project.mkdir()
|
||||
|
||||
Reference in New Issue
Block a user