mirror of
https://github.com/catlog22/Claude-Code-Workflow.git
synced 2026-02-05 01:50:27 +08:00
fix(registry): normalize path case for comparison on Windows
Adds case normalization for path comparison on Windows to handle case-insensitive filesystem behavior. Preserves case-sensitivity on Unix. Fixes: ISS-1766921318981-13 Solution-ID: SOL-1735386000-13 Issue-ID: ISS-1766921318981-13 Task-ID: T1
This commit is contained in:
@@ -2,6 +2,7 @@
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import platform
|
||||
import sqlite3
|
||||
import threading
|
||||
import time
|
||||
@@ -153,6 +154,17 @@ class RegistryStore:
|
||||
except sqlite3.DatabaseError as exc:
|
||||
raise StorageError(f"Failed to initialize registry schema: {exc}") from exc
|
||||
|
||||
def _normalize_path_for_comparison(self, path: Path) -> str:
|
||||
"""Normalize paths for comparisons and storage.
|
||||
|
||||
Windows paths are treated as case-insensitive, so normalize to lowercase.
|
||||
Unix platforms preserve case sensitivity.
|
||||
"""
|
||||
path_str = str(path)
|
||||
if platform.system() == "Windows":
|
||||
return path_str.lower()
|
||||
return path_str
|
||||
|
||||
# === Project Operations ===
|
||||
|
||||
def register_project(self, source_root: Path, index_root: Path) -> ProjectInfo:
|
||||
@@ -167,7 +179,7 @@ class RegistryStore:
|
||||
"""
|
||||
with self._lock:
|
||||
conn = self._get_connection()
|
||||
source_root_str = str(source_root.resolve())
|
||||
source_root_str = self._normalize_path_for_comparison(source_root.resolve())
|
||||
index_root_str = str(index_root.resolve())
|
||||
now = time.time()
|
||||
|
||||
@@ -205,7 +217,7 @@ class RegistryStore:
|
||||
"""
|
||||
with self._lock:
|
||||
conn = self._get_connection()
|
||||
source_root_str = str(source_root.resolve())
|
||||
source_root_str = self._normalize_path_for_comparison(source_root.resolve())
|
||||
|
||||
row = conn.execute(
|
||||
"SELECT id FROM projects WHERE source_root=?", (source_root_str,)
|
||||
@@ -229,7 +241,7 @@ class RegistryStore:
|
||||
"""
|
||||
with self._lock:
|
||||
conn = self._get_connection()
|
||||
source_root_str = str(source_root.resolve())
|
||||
source_root_str = self._normalize_path_for_comparison(source_root.resolve())
|
||||
|
||||
row = conn.execute(
|
||||
"SELECT * FROM projects WHERE source_root=?", (source_root_str,)
|
||||
@@ -291,7 +303,7 @@ class RegistryStore:
|
||||
"""
|
||||
with self._lock:
|
||||
conn = self._get_connection()
|
||||
source_root_str = str(source_root.resolve())
|
||||
source_root_str = self._normalize_path_for_comparison(source_root.resolve())
|
||||
|
||||
conn.execute(
|
||||
"""
|
||||
@@ -312,7 +324,7 @@ class RegistryStore:
|
||||
"""
|
||||
with self._lock:
|
||||
conn = self._get_connection()
|
||||
source_root_str = str(source_root.resolve())
|
||||
source_root_str = self._normalize_path_for_comparison(source_root.resolve())
|
||||
|
||||
conn.execute(
|
||||
"UPDATE projects SET status=? WHERE source_root=?",
|
||||
@@ -344,7 +356,7 @@ class RegistryStore:
|
||||
"""
|
||||
with self._lock:
|
||||
conn = self._get_connection()
|
||||
source_path_str = str(source_path.resolve())
|
||||
source_path_str = self._normalize_path_for_comparison(source_path.resolve())
|
||||
index_path_str = str(index_path.resolve())
|
||||
now = time.time()
|
||||
|
||||
@@ -385,7 +397,7 @@ class RegistryStore:
|
||||
"""
|
||||
with self._lock:
|
||||
conn = self._get_connection()
|
||||
source_path_str = str(source_path.resolve())
|
||||
source_path_str = self._normalize_path_for_comparison(source_path.resolve())
|
||||
|
||||
row = conn.execute(
|
||||
"SELECT id FROM dir_mapping WHERE source_path=?", (source_path_str,)
|
||||
@@ -409,7 +421,7 @@ class RegistryStore:
|
||||
"""
|
||||
with self._lock:
|
||||
conn = self._get_connection()
|
||||
source_path_str = str(source_path.resolve())
|
||||
source_path_str = self._normalize_path_for_comparison(source_path.resolve())
|
||||
|
||||
row = conn.execute(
|
||||
"SELECT index_path FROM dir_mapping WHERE source_path=?",
|
||||
@@ -441,7 +453,7 @@ class RegistryStore:
|
||||
paths_to_check = []
|
||||
current = source_path_resolved
|
||||
while True:
|
||||
paths_to_check.append(str(current))
|
||||
paths_to_check.append(self._normalize_path_for_comparison(current))
|
||||
parent = current.parent
|
||||
if parent == current: # Reached filesystem root
|
||||
break
|
||||
@@ -476,7 +488,8 @@ class RegistryStore:
|
||||
"""
|
||||
with self._lock:
|
||||
conn = self._get_connection()
|
||||
source_path_resolved = str(Path(source_path).resolve())
|
||||
resolved_path = Path(source_path).resolve()
|
||||
source_path_resolved = self._normalize_path_for_comparison(resolved_path)
|
||||
|
||||
# First try exact match on projects table
|
||||
row = conn.execute(
|
||||
@@ -494,9 +507,9 @@ class RegistryStore:
|
||||
# Try finding project that contains this path
|
||||
# Build list of all parent paths
|
||||
paths_to_check = []
|
||||
current = Path(source_path_resolved)
|
||||
current = resolved_path
|
||||
while True:
|
||||
paths_to_check.append(str(current))
|
||||
paths_to_check.append(self._normalize_path_for_comparison(current))
|
||||
parent = current.parent
|
||||
if parent == current:
|
||||
break
|
||||
@@ -552,7 +565,7 @@ class RegistryStore:
|
||||
"""
|
||||
with self._lock:
|
||||
conn = self._get_connection()
|
||||
source_path_str = str(source_path.resolve())
|
||||
source_path_str = self._normalize_path_for_comparison(source_path.resolve())
|
||||
|
||||
# First get the parent's depth
|
||||
parent_row = conn.execute(
|
||||
@@ -587,7 +600,7 @@ class RegistryStore:
|
||||
"""
|
||||
with self._lock:
|
||||
conn = self._get_connection()
|
||||
source_path_str = str(source_path.resolve())
|
||||
source_path_str = self._normalize_path_for_comparison(source_path.resolve())
|
||||
|
||||
conn.execute(
|
||||
"""
|
||||
|
||||
69
codex-lens/tests/test_registry.py
Normal file
69
codex-lens/tests/test_registry.py
Normal file
@@ -0,0 +1,69 @@
|
||||
"""Tests for RegistryStore path handling."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
|
||||
from codexlens.storage.registry import RegistryStore
|
||||
|
||||
|
||||
def _swap_case(path: Path) -> str:
|
||||
return str(path).swapcase()
|
||||
|
||||
|
||||
def test_path_case_normalization_windows(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None:
|
||||
"""On Windows, path comparisons should be case-insensitive."""
|
||||
import codexlens.storage.registry as registry
|
||||
|
||||
monkeypatch.setattr(registry.platform, "system", lambda: "Windows")
|
||||
|
||||
db_path = tmp_path / "registry.db"
|
||||
source_root = tmp_path / "MyProject"
|
||||
index_root = tmp_path / "indexes"
|
||||
|
||||
with RegistryStore(db_path=db_path) as store:
|
||||
store.register_project(source_root, index_root)
|
||||
|
||||
result = store.find_by_source_path(_swap_case(source_root))
|
||||
assert result is not None
|
||||
assert result["source_root"] == str(source_root.resolve()).lower()
|
||||
|
||||
|
||||
def test_path_case_sensitivity_non_windows(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None:
|
||||
"""On Unix, path comparisons should remain case-sensitive."""
|
||||
import codexlens.storage.registry as registry
|
||||
|
||||
monkeypatch.setattr(registry.platform, "system", lambda: "Linux")
|
||||
|
||||
db_path = tmp_path / "registry.db"
|
||||
source_root = tmp_path / "MyProject"
|
||||
index_root = tmp_path / "indexes"
|
||||
|
||||
with RegistryStore(db_path=db_path) as store:
|
||||
store.register_project(source_root, index_root)
|
||||
assert store.find_by_source_path(_swap_case(source_root)) is None
|
||||
|
||||
|
||||
def test_find_nearest_index(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None:
|
||||
"""Nearest ancestor lookup should be case-insensitive on Windows."""
|
||||
import codexlens.storage.registry as registry
|
||||
|
||||
monkeypatch.setattr(registry.platform, "system", lambda: "Windows")
|
||||
|
||||
db_path = tmp_path / "registry.db"
|
||||
source_root = tmp_path / "MyProject"
|
||||
index_root = tmp_path / "indexes"
|
||||
index_db = index_root / "_index.db"
|
||||
|
||||
with RegistryStore(db_path=db_path) as store:
|
||||
project = store.register_project(source_root, index_root)
|
||||
mapping = store.register_dir(project.id, source_root, index_db, depth=0)
|
||||
|
||||
query_path = Path(_swap_case(source_root)) / "SubDir" / "file.py"
|
||||
found = store.find_nearest_index(query_path)
|
||||
|
||||
assert found is not None
|
||||
assert found.id == mapping.id
|
||||
|
||||
Reference in New Issue
Block a user