mirror of
https://github.com/agentscope-ai/ReMe.git
synced 2026-08-28 05:25:04 +00:00
Some checks are pending
Pre-commit / run (ubuntu-latest) (push) Waiting to run
Tests ReMe / Unit Tests - py3.11 (push) Waiting to run
Tests ReMe / Unit Tests - py3.12 (push) Waiting to run
Tests ReMe / Unit Tests - py3.13 (push) Waiting to run
Windows Smoke / CLI smoke - py3.11 (push) Waiting to run
* feat(core): add shell command execution and memory status reporting - Introduce ShellStep for executing shell commands with timeout support - Add StatusStep to report memory estimates for stateful data components - Register shell and status commands in default configuration - Update documentation with new reme status and shell command capabilities - Implement comprehensive unit tests for both new step types - Add support for asynchronous command execution with proper error handling * feat(config): add log_config option to suppress config loading logs - Add log_config parameter to resolve_app_config function with default True - Conditionally log config loading messages based on log_config flag - Update reme.py and service_utils.py to use log_config=False for client calls - Suppress config logging in user-facing contexts to avoid output pollution refactor(shell): rename command parameter to cmd for clarity - Change 'command' to 'cmd' in default.yaml configuration schema - Rename 'timeout' to 'shell_timeout' to avoid parameter name collisions - Update ShellStep to accept both legacy and new parameter names - Maintain backward compatibility with existing command/timeout usage test(shell): add comprehensive tests for shell step parameter handling - Add test cases for new cmd and shell_timeout parameter names - Verify legacy command and timeout parameters still work - Test blank command rejection message updated to use cmd - Create integration test for shell parameter payload passing * fix(shell): ensure proper environment loading and process timeout handling - Move load_env() call to execute before parse_args() in main function - Add proper process group killing for timeout scenarios on POSIX systems - Implement recursive child process termination on Windows for proper cleanup - Change parameter name from 'timeout' to 'shell_timeout' in shell execution - Remove support for legacy 'command' and 'timeout' parameter names - Update test cases to verify new timeout behavior and parameter requirements - Add comments explaining component size tracking implementation details
100 lines
2.9 KiB
Python
100 lines
2.9 KiB
Python
"""Tests for asynchronous shell command execution."""
|
|
|
|
import asyncio
|
|
import time
|
|
|
|
from reme.components.application_context import ApplicationContext
|
|
from reme.enumeration import ComponentEnum
|
|
from reme.steps.common.shell import DEFAULT_TIMEOUT, ShellStep
|
|
from reme.components import R
|
|
|
|
|
|
def _run(coro):
|
|
"""Run a coroutine on a fresh event loop."""
|
|
asyncio.run(coro)
|
|
|
|
|
|
def test_shell_step_is_registered():
|
|
"""Importing common steps makes shell_step discoverable."""
|
|
assert R.get(ComponentEnum.STEP, "shell_step") is ShellStep
|
|
|
|
|
|
def test_shell_step_default_timeout_is_one_day():
|
|
"""Shell commands may run for one day when no timeout is supplied."""
|
|
assert DEFAULT_TIMEOUT == 86400
|
|
|
|
|
|
def test_shell_step_returns_stdout_from_workspace(tmp_path):
|
|
"""Successful commands return stdout and run in the configured workspace."""
|
|
|
|
async def run():
|
|
step = ShellStep(app_context=ApplicationContext(workspace_dir=str(tmp_path)))
|
|
response = await step(cmd="pwd")
|
|
|
|
assert response.success is True
|
|
assert response.answer.strip() == str(tmp_path)
|
|
assert response.metadata["returncode"] == 0
|
|
assert response.metadata["stderr"] == ""
|
|
|
|
_run(run())
|
|
|
|
|
|
def test_shell_step_reports_stderr_on_failure():
|
|
"""Failed commands expose stderr and their non-zero exit status."""
|
|
|
|
async def run():
|
|
step = ShellStep()
|
|
response = await step(cmd="echo boom >&2; exit 7")
|
|
|
|
assert response.success is False
|
|
assert response.answer == "boom\n"
|
|
assert response.metadata["returncode"] == 7
|
|
assert response.metadata["stderr"] == "boom\n"
|
|
|
|
_run(run())
|
|
|
|
|
|
def test_shell_step_times_out():
|
|
"""Timeout terminates child processes as well as their parent shell."""
|
|
|
|
async def run():
|
|
step = ShellStep()
|
|
started = time.monotonic()
|
|
response = await step(cmd="sleep 3 & wait", shell_timeout=0.01)
|
|
|
|
assert response.success is False
|
|
assert response.answer == "Shell command timed out after 0.01s"
|
|
assert response.metadata["shell_timeout"] == 0.01
|
|
assert time.monotonic() - started < 1
|
|
|
|
_run(run())
|
|
|
|
|
|
def test_shell_step_requires_a_command():
|
|
"""Blank commands are rejected without creating a subprocess."""
|
|
|
|
async def run():
|
|
response = await ShellStep()(cmd=" ")
|
|
|
|
assert response.success is False
|
|
assert response.answer == "cmd is required"
|
|
|
|
_run(run())
|
|
|
|
|
|
def test_shell_step_does_not_accept_legacy_parameter_names():
|
|
"""Only cmd and shell_timeout configure shell execution."""
|
|
|
|
async def run():
|
|
response = await ShellStep()(command="printf legacy", timeout=1)
|
|
|
|
assert response.success is False
|
|
assert response.answer == "cmd is required"
|
|
|
|
response = await ShellStep()(cmd="printf current", timeout=1)
|
|
|
|
assert response.success is True
|
|
assert response.answer == "current"
|
|
assert response.metadata["shell_timeout"] == DEFAULT_TIMEOUT
|
|
|
|
_run(run())
|