| from __future__ import annotations |
|
|
| import asyncio |
| from types import SimpleNamespace |
|
|
| from astrbot.core.agent.run_context import ContextWrapper |
| from astrbot.core.computer.tools.neo_skills import PromoteSkillCandidateTool |
|
|
|
|
| class _FakeSkills: |
| def __init__(self): |
| self.rollback_called_with = None |
|
|
| async def promote_candidate(self, candidate_id: str, stage: str = "canary"): |
| assert candidate_id == "cand-1" |
| assert stage == "stable" |
| return { |
| "id": "sr-1", |
| "skill_key": "k1", |
| "candidate_id": candidate_id, |
| "stage": stage, |
| } |
|
|
| async def rollback_release(self, release_id: str): |
| self.rollback_called_with = release_id |
| return {"id": "rb-1", "rollback_of": release_id} |
|
|
|
|
| class _FakeClient: |
| def __init__(self): |
| self.skills = _FakeSkills() |
|
|
|
|
| class _FakeBooter: |
| def __init__(self): |
| self.bay_client = _FakeClient() |
| self.sandbox = object() |
|
|
|
|
| def test_promote_stable_sync_failure_auto_rolls_back(monkeypatch): |
| async def _fake_get_booter(_ctx, _session_id): |
| return _FakeBooter() |
|
|
| async def _fake_sync_release(self, client, **kwargs): |
| _ = self, client, kwargs |
| raise ValueError("sync failed") |
|
|
| monkeypatch.setattr( |
| "astrbot.core.computer.tools.neo_skills.get_booter", |
| _fake_get_booter, |
| ) |
| monkeypatch.setattr( |
| "astrbot.core.computer.tools.neo_skills.NeoSkillSyncManager.sync_release", |
| _fake_sync_release, |
| ) |
|
|
| event = SimpleNamespace(role="admin", unified_msg_origin="session-1") |
| astr_ctx = SimpleNamespace(context=SimpleNamespace(), event=event) |
| run_ctx = ContextWrapper(context=astr_ctx) |
|
|
| tool = PromoteSkillCandidateTool() |
| result = asyncio.run( |
| tool.call( |
| run_ctx, |
| candidate_id="cand-1", |
| stage="stable", |
| sync_to_local=True, |
| ) |
| ) |
|
|
| assert isinstance(result, str) |
| assert "auto rollback succeeded" in result |
| assert "sync failed" in result |
|
|