ReMe/tests/unit/test_config_parser.py
jinliyl 2e87b7a52e
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 execution and runtime memory status (#344)
* 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
2026-07-14 16:31:41 +08:00

118 lines
3.8 KiB
Python

"""Tests for configuration parsing helpers."""
from pathlib import Path
import pytest
from reme.config.config_parser import (
_expand_env_vars,
_load_config,
_read_config_file,
parse_args,
parse_dot_notation,
resolve_app_config,
)
def test_load_builtin_config_by_filename_with_suffix():
"""Built-in config names may include the YAML suffix."""
cfg = _load_config("default.yaml")
assert cfg["service"]["backend"] == "http"
def test_resolve_app_config_can_suppress_config_log(monkeypatch):
"""Client-side config resolution can avoid polluting command output."""
messages = []
class FakeLogger:
"""Capture config log messages."""
def info(self, message):
"""Record one INFO message."""
messages.append(message)
monkeypatch.setattr("reme.utils.get_logger", lambda **_kwargs: FakeLogger())
resolve_app_config(log_config=False)
assert not messages
def test_default_config_registers_daily_write_job():
"""``daily_write`` is exposed as a base job backed by ``daily_write_step``."""
cfg = _load_config("default.yaml")
job = cfg["jobs"]["daily_write"]
assert job["backend"] == "base"
assert job["steps"] == [{"backend": "daily_write_step"}]
assert job["parameters"]["required"] == ["name", "description", "session_id", "content"]
def test_default_config_registers_shell_job():
"""``shell`` exposes command execution through ``shell_step``."""
cfg = _load_config("default.yaml")
job = cfg["jobs"]["shell"]
assert job["backend"] == "base"
assert job["steps"] == [{"backend": "shell_step"}]
assert job["parameters"]["required"] == ["cmd"]
assert job["parameters"]["properties"]["shell_timeout"]["default"] == 86400
def test_default_config_keeps_frontmatter_chunk_metadata_opt_in():
"""Markdown frontmatter-to-chunk metadata is disabled by default for compatibility."""
cfg = _load_config("default.yaml")
markdown = cfg["components"]["file_chunker"]["markdown"]
assert markdown["include_frontmatter_in_metadata"] is False
# Allow-list defaults to empty; combined with the False above, chunk metadata stays empty.
assert markdown["include_frontmatter_keys_in_metadata"] == [] or markdown.get(
"include_frontmatter_keys_in_metadata",
) in (None, [])
def test_parse_args_rejects_non_key_value_extra_argument():
"""Extra CLI arguments must use key=value syntax."""
with pytest.raises(ValueError, match="expected key=value"):
parse_args("search", "hello")
@pytest.mark.parametrize("item", ["=1", ".a=1", "a.=1", "a..b=1"])
def test_parse_dot_notation_rejects_empty_key_segments(item):
"""Dot notation keys cannot contain empty path segments."""
with pytest.raises(ValueError, match="Invalid dot notation key"):
parse_dot_notation([item])
def test_read_config_file_rejects_non_mapping_root(tmp_path: Path):
"""Config files must contain a mapping at the root."""
config_path = tmp_path / "bad.yaml"
config_path.write_text("- item\n", encoding="utf-8")
with pytest.raises(ValueError, match="Config root must be a mapping"):
_read_config_file(config_path)
def test_expand_env_vars_converts_expanded_scalar_types(monkeypatch):
"""Expanded environment values keep YAML scalar typing."""
monkeypatch.setenv("PORT", "18080")
monkeypatch.setenv("ENABLED", "false")
expanded = _expand_env_vars(
{
"port": "${PORT}",
"enabled": "${ENABLED}",
"zip": "${ZIP:-007}",
"url": "http://${HOST:-localhost}:${PORT}",
"string_bool": '${STRING_BOOL:-"false"}',
},
)
assert expanded == {
"port": 18080,
"enabled": False,
"zip": "007",
"url": "http://localhost:18080",
"string_bool": "false",
}