Files
Nexus/tests/chain/test_script_exec_flow.py.bak-20260707-064723
T
2026-07-08 22:31:31 +08:00

174 lines
4.8 KiB
Plaintext

"""CH-EXEC-001 — create script → execute → poll completed."""
from __future__ import annotations
from unittest.mock import AsyncMock, MagicMock
import pytest
from server.api.dependencies import get_script_service
from server.api.schemas import ScriptCreate, ScriptExecute
from server.api.scripts import create_script, execute_command, list_executions
pytestmark = pytest.mark.chain
_BG_TASKS: list = []
class _MemRedis:
def __init__(self) -> None:
self._data: dict[str, str] = {}
async def set(self, key: str, value: str, ex: int | None = None) -> None:
self._data[key] = value
async def get(self, key: str) -> str | None:
return self._data.get(key)
async def delete(self, key: str) -> None:
self._data.pop(key, None)
async def rpush(self, key: str, value: str) -> None:
self._data.setdefault(key, "[]")
async def lrange(self, key: str, start: int, end: int) -> list:
return []
async def sadd(self, key: str, member: str) -> None:
self._data.setdefault(f"set:{key}", set()).add(member)
async def srem(self, key: str, member: str) -> None:
self._data.get(f"set:{key}", set()).discard(member)
class _SessionCtx:
def __init__(self, session):
self._session = session
async def __aenter__(self):
return self._session
async def __aexit__(self, exc_type, exc, tb) -> None:
if exc:
await self._session.rollback()
def _chain_exec_env(db_session, monkeypatch) -> None:
mem = _MemRedis()
monkeypatch.setattr(
"server.infrastructure.redis.script_execution_store.get_redis",
lambda: mem,
)
monkeypatch.setattr(
"server.infrastructure.database.session.AsyncSessionLocal",
lambda: _SessionCtx(db_session),
)
monkeypatch.setattr(
"server.application.services.script_execution_notify.notify_script_progress",
AsyncMock(),
)
monkeypatch.setattr(
"server.application.services.script_execution_notify.notify_script_complete",
AsyncMock(),
)
async def fake_dispatch(self, server_ids, command, timeout=30, **kwargs):
return {
str(sid): {
"exit_code": 0,
"stdout": "ok\n",
"stderr": "",
"status": "completed",
"phase": "done",
}
for sid in server_ids
}
monkeypatch.setattr(
"server.application.services.script_service.ScriptService._dispatch_to_servers",
fake_dispatch,
)
_BG_TASKS.clear()
def capture_create_task(coro):
_BG_TASKS.append(coro)
return MagicMock()
monkeypatch.setattr(
"server.application.services.script_service.asyncio.create_task",
capture_create_task,
)
@pytest.mark.asyncio
async def test_create_script_then_list_executions(db_session, test_admin, mock_request):
mock_request.state.db = db_session
service = await get_script_service(mock_request)
created = await create_script(
ScriptCreate(name="chain-exec-script", content="echo chain", category="ops"),
mock_request,
test_admin["admin"],
service,
db_session,
)
assert created["id"] > 0
board = await list_executions(
limit=20,
offset=0,
status=None,
script_id=created["id"],
admin=test_admin["admin"],
service=service,
)
assert board["total"] >= 0
assert isinstance(board["items"], list)
@pytest.mark.asyncio
async def test_execute_script_reaches_completed(
db_session,
test_admin,
test_server,
mock_request,
monkeypatch,
):
_chain_exec_env(db_session, monkeypatch)
mock_request.state.db = db_session
service = await get_script_service(mock_request)
created = await create_script(
ScriptCreate(name="chain-exec-run", content="echo done", category="ops"),
mock_request,
test_admin["admin"],
service,
db_session,
)
started = await execute_command(
ScriptExecute(script_id=created["id"], server_ids=[test_server.id], timeout=30),
test_admin["admin"],
service,
)
assert started["status"] == "running"
assert started["id"] > 0
assert len(_BG_TASKS) == 1
await _BG_TASKS[0]
detail = await service.get_execution_detail(started["id"])
assert detail is not None
assert detail["status"] == "completed"
assert detail["results"][str(test_server.id)]["exit_code"] == 0
from server.infrastructure.redis import script_execution_store as ses
live = await ses.load_live(started["id"])
assert live is not None
await ses.flush_to_mysql(service.execution_repo, live)
row = await service.get_execution(started["id"])
assert row is not None
assert row.status == "completed"