"""Unit tests for utils module."""
import importlib
import os
import sys
from pathlib import Path
from unittest.mock import patch
import pytest
from jiuwenswarm.common import utils
class TestPathResolution:
"""Test path resolution functions."""
@staticmethod
def test_get_root_dir():
"""Test get_root_dir returns a Path."""
root = utils.get_root_dir()
assert isinstance(root, Path)
assert root.exists()
@staticmethod
def test_get_config_dir():
"""Test get_config_dir returns a Path."""
config_dir = utils.get_config_dir()
assert isinstance(config_dir, Path)
@staticmethod
def test_get_workspace_dir():
"""Test get_workspace_dir returns a Path."""
workspace = utils.get_workspace_dir()
assert isinstance(workspace, Path)
@staticmethod
def test_get_config_file():
"""Test get_config_file returns config.yaml path."""
config_file = utils.get_config_file()
assert isinstance(config_file, Path)
assert config_file.name == "config.yaml"
@staticmethod
def test_get_agent_workspace_dir():
"""Test get_agent_workspace_dir returns agent workspace."""
agent_workspace = utils.get_agent_workspace_dir()
assert isinstance(agent_workspace, Path)
assert "agent" in str(agent_workspace)
@staticmethod
def test_path_caching():
"""Test that path results are cached."""
root1 = utils.get_root_dir()
root2 = utils.get_root_dir()
assert root1 == root2
class TestPackageDetection:
"""Test package installation detection."""
@staticmethod
def test_is_package_installation():
"""Test package installation detection."""
result = utils.is_package_installation()
assert isinstance(result, bool)
class TestLoggerSetup:
"""Test logger setup."""
@staticmethod
def test_setup_logger_default():
"""Test logger setup with default level from explicit override."""
logger = utils.setup_logger("INFO")
assert logger.name == "jiuwenswarm"
assert logger.level == 20
@staticmethod
def test_setup_logger_debug():
"""Test logger setup with DEBUG level."""
logger = utils.setup_logger("DEBUG")
assert logger.level == 10
@staticmethod
def test_setup_logger_error():
"""Test logger setup with ERROR level."""
logger = utils.setup_logger("ERROR")
assert logger.level == 40
@staticmethod
def test_logger_handlers():
"""Test that logger has console and five rotating log files."""
logger = utils.setup_logger("INFO")
handler_types = [type(h).__name__ for h in logger.handlers]
assert "StreamHandler" in handler_types
assert handler_types.count("SafeRotatingFileHandler") == 5
class TestUserWorkspace:
"""Test user workspace functions."""
@patch("jiuwenswarm.common.utils.get_user_workspace_dir")
@patch("jiuwenswarm.common.utils._find_package_root")
@patch("pathlib.Path.exists")
@patch("builtins.input")
def test_init_user_workspace_cancelled(
self, mock_input, mock_exists, mock_find_root, mock_get_workspace_dir, temp_workspace
):
"""Test user workspace initialization when user cancels."""
pass
class TestConstants:
"""Test module constants."""
@staticmethod
def test_get_user_home_defined():
"""Test get_user_home is defined and returns a Path."""
assert hasattr(utils, "get_user_home")
assert isinstance(utils.get_user_home(), Path)
@staticmethod
def test_get_user_workspace_dir_defined():
"""Test get_user_workspace_dir is defined."""
assert hasattr(utils, "get_user_workspace_dir")
assert isinstance(utils.get_user_workspace_dir(), Path)
assert ".jiuwenswarm" in str(utils.get_user_workspace_dir())
class TestMultiInstanceEnvVars:
"""Test environment variable support for multi-instance isolation (Phase 1)."""
@staticmethod
def test_workspace_env_var():
"""Test JIUWENSWARM_DATA_DIR environment variable overrides default workspace."""
setattr(utils, '_workspace_base_dir', None)
setattr(utils, '_user_home', None)
original_env = os.environ.pop("JIUWENSWARM_DATA_DIR", None)
original_home_env = os.environ.pop("JIUWENSWARM_HOME", None)
try:
default_workspace = utils.get_user_workspace_dir()
assert ".jiuwenswarm" in str(default_workspace)
setattr(utils, '_workspace_base_dir', None)
setattr(utils, '_user_home', None)
os.environ["JIUWENSWARM_DATA_DIR"] = "/custom/workspace/path"
custom_workspace = utils.get_user_workspace_dir()
assert custom_workspace == Path("/custom/workspace/path")
finally:
setattr(utils, '_workspace_base_dir', None)
setattr(utils, '_user_home', None)
os.environ.pop("JIUWENSWARM_DATA_DIR", None)
if original_env:
os.environ["JIUWENSWARM_DATA_DIR"] = original_env
if original_home_env:
os.environ["JIUWENSWARM_HOME"] = original_home_env
@staticmethod
def test_jiuwenswarm_home_env_var():
"""Test JIUWENSWARM_HOME environment variable overrides default home."""
setattr(utils, '_user_home', None)
original_home_env = os.environ.pop("JIUWENSWARM_HOME", None)
original_workspace_env = os.environ.pop("JIUWENSWARM_DATA_DIR", None)
try:
os.environ["JIUWENSWARM_HOME"] = "/custom/home"
custom_home = utils.get_user_home()
assert custom_home == Path("/custom/home")
setattr(utils, '_user_home', None)
os.environ.pop("JIUWENSWARM_HOME", None)
workspace = utils.get_user_workspace_dir()
assert isinstance(workspace, Path)
finally:
setattr(utils, '_user_home', None)
os.environ.pop("JIUWENSWARM_HOME", None)
os.environ.pop("JIUWENSWARM_DATA_DIR", None)
if original_home_env:
os.environ["JIUWENSWARM_HOME"] = original_home_env
if original_workspace_env:
os.environ["JIUWENSWARM_DATA_DIR"] = original_workspace_env
@staticmethod
def test_workspace_priority_over_home():
"""Test JIUWENSWARM_DATA_DIR takes priority over JIUWENSWARM_HOME for workspace."""
setattr(utils, '_workspace_base_dir', None)
setattr(utils, '_user_home', None)
original_home_env = os.environ.pop("JIUWENSWARM_HOME", None)
original_workspace_env = os.environ.pop("JIUWENSWARM_DATA_DIR", None)
try:
os.environ["JIUWENSWARM_HOME"] = "/home/a"
os.environ["JIUWENSWARM_DATA_DIR"] = "/workspace/b"
workspace = utils.get_user_workspace_dir()
assert workspace == Path("/workspace/b")
finally:
setattr(utils, '_workspace_base_dir', None)
setattr(utils, '_user_home', None)
os.environ.pop("JIUWENSWARM_HOME", None)
os.environ.pop("JIUWENSWARM_DATA_DIR", None)
if original_home_env:
os.environ["JIUWENSWARM_HOME"] = original_home_env
if original_workspace_env:
os.environ["JIUWENSWARM_DATA_DIR"] = original_workspace_env
class TestHardcodedPathsPhase2:
"""Test that hardcoded paths are fixed to use getter functions (Phase 2).
All assertions use absolute path strings for easy observation.
"""
@staticmethod
def test_cron_tools_path_equivalence():
"""Test cron_tools.py path matches expected structure (cross-platform)."""
from jiuwenswarm.common.utils import get_agent_home_dir, get_user_workspace_dir
workspace = get_user_workspace_dir()
expected_path = workspace / "agent" / "home" / "cron_jobs.json"
actual_path = get_agent_home_dir() / "cron_jobs.json"
assert str(actual_path.resolve()) == str(expected_path.resolve()), \
f"Expected: {expected_path.resolve()}, Got: {actual_path.resolve()}"
@staticmethod
def test_task_tools_path_structure():
"""Test task_tools.py path uses workspace (migrated from legacy jiuwenswarm_workspace)."""
setattr(utils, '_user_home', None)
setattr(utils, '_initialized', False)
setattr(utils, '_config_dir', None)
setattr(utils, '_workspace_dir', None)
setattr(utils, '_root_dir', None)
from jiuwenswarm.agents.harness.common.tools.task_tools import _get_task_data_path
from jiuwenswarm.common.utils import get_user_workspace_dir
workspace = get_user_workspace_dir()
expected_path = workspace / "agent" / "workspace" / "task-data.json"
actual_path = Path(_get_task_data_path())
assert str(actual_path.resolve()) == str(expected_path.resolve()), \
f"Expected: {expected_path.resolve()}, Got: {actual_path.resolve()}"
@staticmethod
def test_im_inbound_path_structure():
"""Test im_inbound.py uses DeepAgent standard USER.md path."""
setattr(utils, '_user_home', None)
setattr(utils, '_initialized', False)
setattr(utils, '_config_dir', None)
setattr(utils, '_workspace_dir', None)
setattr(utils, '_root_dir', None)
from jiuwenswarm.common.utils import get_deepagent_user_md_path, get_user_workspace_dir
workspace = get_user_workspace_dir()
expected_path = workspace / "agent" / "workspace" / "USER.md"
actual_path = get_deepagent_user_md_path()
assert str(actual_path.resolve()) == str(expected_path.resolve()), \
f"Expected: {expected_path.resolve()}, Got: {actual_path.resolve()}"
class TestAdditionalHardcodedPaths:
"""Test additional hardcoded paths fixed in config.py and rail_manager.py.
All assertions use absolute path strings for easy observation.
"""
@staticmethod
def test_rail_manager_path_structure():
"""Test rail_manager.py uses get_agent_workspace_dir() for extensions path."""
from jiuwenswarm.agents.harness.common.plugins.rail_manager import RailManager
from jiuwenswarm.common.utils import get_user_workspace_dir
workspace = get_user_workspace_dir()
expected_path = workspace / "agent" / "workspace" / "extensions"
rail_manager = RailManager()
extensions_dir = getattr(rail_manager, '_extensions_dir')
assert str(extensions_dir.resolve()) == str(expected_path.resolve()), \
f"Expected: {expected_path.resolve()}, Got: {extensions_dir.resolve()}"
@staticmethod
def test_config_module_dir_structure(tmp_path):
"""Test config.py _CONFIG_MODULE_DIR honors explicit config dir."""
config_dir = tmp_path / "config"
config_dir.mkdir()
import jiuwenswarm.common.config as config_module
with patch.dict(os.environ, {"JIUWENSWARM_CONFIG_DIR": str(config_dir)}):
config_module = importlib.reload(config_module)
module_config_dir = config_module.__dict__["_CONFIG_MODULE_DIR"]
assert str(module_config_dir.resolve()) == str(config_dir.resolve()), \
f"Expected: {config_dir.resolve()}, Got: {module_config_dir.resolve()}"
importlib.reload(config_module)
@staticmethod
def test_interactions_dir_structure():
"""Test get_interactions_dir() returns correct path structure."""
setattr(utils, '_user_home', None)
setattr(utils, '_workspace_base_dir', None)
from jiuwenswarm.common.utils import get_interactions_dir, get_user_workspace_dir
workspace = get_user_workspace_dir()
expected_path = workspace / "agent" / "workspace" / "interactions"
actual_path = get_interactions_dir()
assert str(actual_path.resolve()) == str(expected_path.resolve()), \
f"Expected: {expected_path.resolve()}, Got: {actual_path.resolve()}"