| from __future__ import annotations |
|
|
| import asyncio |
| from pathlib import Path |
|
|
| import pytest |
|
|
| from astrbot.core.skills.neo_skill_sync import NeoSkillSyncManager |
|
|
|
|
| class _FakeSkills: |
| async def list_releases(self, **kwargs): |
| _ = kwargs |
| return { |
| "items": [ |
| { |
| "id": "sr-1", |
| "skill_key": "etl/loader@v1", |
| "candidate_id": "sc-1", |
| "stage": "stable", |
| } |
| ], |
| "total": 1, |
| } |
|
|
| async def get_candidate(self, candidate_id: str): |
| assert candidate_id == "sc-1" |
| return { |
| "id": "sc-1", |
| "payload_ref": "blob:blob-1", |
| } |
|
|
| async def get_payload(self, payload_ref: str): |
| assert payload_ref == "blob:blob-1" |
| return { |
| "payload_ref": payload_ref, |
| "kind": "astrbot_skill_v1", |
| "payload": { |
| "skill_markdown": "---\ndescription: test\n---\n# title\ncontent", |
| }, |
| } |
|
|
|
|
| class _FakeClient: |
| def __init__(self): |
| self.skills = _FakeSkills() |
|
|
|
|
| def test_sync_release_writes_skill_and_map(monkeypatch, tmp_path: Path): |
| calls = {"active": [], "sandbox_sync": 0} |
|
|
| def _fake_set_skill_active(self, name, active): |
| calls["active"].append((name, active)) |
|
|
| async def _fake_sync_sandboxes(): |
| calls["sandbox_sync"] += 1 |
|
|
| monkeypatch.setattr( |
| "astrbot.core.skills.neo_skill_sync.SkillManager.set_skill_active", |
| _fake_set_skill_active, |
| ) |
| monkeypatch.setattr( |
| "astrbot.core.skills.neo_skill_sync.sync_skills_to_active_sandboxes", |
| _fake_sync_sandboxes, |
| ) |
|
|
| skills_root = tmp_path / "skills" |
| map_path = skills_root / "neo_skill_map.json" |
| mgr = NeoSkillSyncManager(skills_root=str(skills_root), map_path=str(map_path)) |
|
|
| result = asyncio.run( |
| mgr.sync_release(_FakeClient(), release_id="sr-1", require_stable=True) |
| ) |
|
|
| assert result.skill_key == "etl/loader@v1" |
| assert result.release_id == "sr-1" |
| assert result.local_skill_name.startswith("neo_") |
| assert calls["active"] == [(result.local_skill_name, True)] |
| assert calls["sandbox_sync"] == 1 |
|
|
| skill_md = skills_root / result.local_skill_name / "SKILL.md" |
| assert skill_md.exists() |
| assert "description: test" in skill_md.read_text(encoding="utf-8") |
|
|
| assert map_path.exists() |
| map_text = map_path.read_text(encoding="utf-8") |
| assert "etl/loader@v1" in map_text |
| assert result.local_skill_name in map_text |
|
|
|
|
| def test_sync_release_rejects_non_stable(monkeypatch, tmp_path: Path): |
| class _CanarySkills(_FakeSkills): |
| async def list_releases(self, **kwargs): |
| _ = kwargs |
| return { |
| "items": [ |
| { |
| "id": "sr-1", |
| "skill_key": "etl", |
| "candidate_id": "sc-1", |
| "stage": "canary", |
| } |
| ], |
| "total": 1, |
| } |
|
|
| class _CanaryClient: |
| def __init__(self): |
| self.skills = _CanarySkills() |
|
|
| async def _fake_sync_sandboxes(): |
| return |
|
|
| monkeypatch.setattr( |
| "astrbot.core.skills.neo_skill_sync.sync_skills_to_active_sandboxes", |
| _fake_sync_sandboxes, |
| ) |
| monkeypatch.setattr( |
| "astrbot.core.skills.neo_skill_sync.SkillManager.set_skill_active", |
| lambda self, name, active: None, |
| ) |
|
|
| mgr = NeoSkillSyncManager( |
| skills_root=str(tmp_path / "skills"), |
| map_path=str(tmp_path / "skills" / "neo_skill_map.json"), |
| ) |
| with pytest.raises(ValueError, match="Only stable releases"): |
| asyncio.run( |
| mgr.sync_release(_CanaryClient(), release_id="sr-1", require_stable=True) |
| ) |
|
|