/ tests / test_config.py
test_config.py
  1  """Tests for the configuration module."""
  2  
  3  import os
  4  import sys
  5  from pathlib import Path
  6  from arxiv_mcp_server.config import Settings
  7  from unittest.mock import patch
  8  
  9  
 10  @patch.object(Path, "mkdir")
 11  @patch.object(Path, "resolve")
 12  def test_storage_path_default(mock_resolve, mock_mkdir):
 13      """Test that the default storage path is correctly constructed."""
 14      # Setup the mock to return the path itself when resolved
 15      mock_resolve.side_effect = lambda: Path.home() / ".arxiv-mcp-server" / "papers"
 16  
 17      settings = Settings()
 18      expected_path = Path.home() / ".arxiv-mcp-server" / "papers"
 19      assert settings.STORAGE_PATH == expected_path.resolve()
 20      # Verify mkdir was called with parents=True and exist_ok=True
 21      mock_mkdir.assert_called_once_with(parents=True, exist_ok=True)
 22  
 23  
 24  @patch.object(Path, "mkdir")
 25  @patch.object(Path, "resolve")
 26  def test_storage_path_from_args(mock_resolve, mock_mkdir):
 27      """Test that the storage path from command line args is correctly parsed."""
 28      test_path = "/tmp/test_storage"
 29      mock_resolve.side_effect = lambda: Path(test_path)
 30  
 31      with patch.object(sys, "argv", ["program", "--storage-path", test_path]):
 32          settings = Settings()
 33          assert settings.STORAGE_PATH == Path(test_path).resolve()
 34      mock_mkdir.assert_called_once_with(parents=True, exist_ok=True)
 35  
 36  
 37  @patch.object(Path, "mkdir")
 38  @patch.object(Path, "resolve")
 39  def test_storage_path_platform_compatibility(mock_resolve, mock_mkdir):
 40      """Test that the storage path works correctly on different platforms."""
 41      # Test with a path format that would be valid on both Windows and Unix
 42      test_paths = [
 43          # Unix-style path
 44          "/path/to/storage",
 45          # Windows-style path
 46          "C:\\path\\to\\storage",
 47          # Path with spaces
 48          "/path with spaces/to/storage",
 49          # Path with non-ASCII characters
 50          "/path/to/störâgè",
 51      ]
 52  
 53      for test_path in test_paths:
 54          # Reset mocks for each iteration
 55          mock_resolve.reset_mock()
 56          mock_mkdir.reset_mock()
 57  
 58          # Set up the mock to return the path itself
 59          mock_resolve.side_effect = lambda: Path(test_path)
 60  
 61          with patch.object(sys, "argv", ["program", "--storage-path", test_path]):
 62              settings = Settings()
 63              resolved_path = settings.STORAGE_PATH
 64  
 65              # Verify that Path constructor was called with the test path
 66              assert resolved_path == Path(test_path).resolve()
 67  
 68              # Verify that mkdir was called
 69              mock_mkdir.assert_called_once_with(parents=True, exist_ok=True)
 70  
 71  
 72  def test_storage_path_creates_missing_directory():
 73      """Test that directories are actually created for the storage path."""
 74      import tempfile
 75  
 76      # Create a temporary directory for our test
 77      with tempfile.TemporaryDirectory() as tmpdir:
 78          # Create a path that doesn't exist yet
 79          test_path = os.path.join(tmpdir, "deeply", "nested", "directory", "structure")
 80  
 81          # Make sure it doesn't exist yet
 82          assert not os.path.exists(test_path)
 83  
 84          # Patch the arguments to use this path
 85          with patch.object(sys, "argv", ["program", "--storage-path", test_path]):
 86              # Access the STORAGE_PATH property which should create the directories
 87              settings = Settings()
 88              storage_path = settings.STORAGE_PATH
 89  
 90              # Verify the directory was created
 91              assert os.path.exists(test_path)
 92              assert os.path.isdir(test_path)
 93  
 94              # Verify the paths refer to the same location
 95              # Use Path.samefile to handle symlinks (like /var -> /private/var on macOS)
 96              assert Path(storage_path).samefile(test_path)
 97  
 98  
 99  def test_path_normalization_with_windows_paths():
100      """Test Windows-specific path handling using string operations only."""
101      # Windows-style paths - we'll test the normalization and joining logic
102      windows_style_paths = [
103          # Drive letter with backslashes
104          "C:\\Users\\username\\Documents\\Papers",
105          # UNC path (network share)
106          "\\\\server\\share\\papers",
107          # Drive letter with forward slashes (also valid on Windows)
108          "C:/Users/username/Documents/Papers",
109          # Windows-style path with spaces
110          "C:\\Program Files\\arXiv\\papers",
111          # Windows-style path with mixed slashes
112          "C:\\Users/username\\Documents/Papers",
113      ]
114  
115      # Test that our config works with these path formats
116      for windows_path in windows_style_paths:
117          assert Path(windows_path)  # This should not raise an error
118  
119          # Test path joining logic works correctly
120          subpath = Path(windows_path) / "subdir"
121          assert str(subpath).endswith("subdir")
122  
123          # The following check is problematic on real Windows systems
124          # where the path separator may be different
125          # Check only that the base path is contained in the result (ignoring separator differences)
126          base_path_norm = windows_path.replace("\\", "/").replace("//", "/")
127          subpath_norm = str(subpath).replace("\\", "/").replace("//", "/")
128          assert base_path_norm in subpath_norm
129  
130          # Instead of checking exact string equality, verify the Path objects are equivalent
131          assert subpath == Path(windows_path).joinpath("subdir")