"""Regression: username rename must migrate mixed-case legacy owner keys. Before lowercasing was enforced everywhere, rows could be stored with owner='Admin' while auth usernames are normalized to 'admin'. A case- sensitive filter would skip those rows during rename (issue #1165). """ import importlib import sys import time import types from pathlib import Path from unittest.mock import MagicMock import pytest from tests.helpers.import_state import clear_module def _real_core_package(): root = Path(__file__).resolve().parent.parent core_path = str(root / "core") core = sys.modules.get("core") if core is None: core = types.ModuleType("core") sys.modules["core"] = core core.__path__ = [core_path] clear_module("core.auth") return core def _fresh_auth_manager(tmp_path): auth_mod = importlib.import_module("core.auth", package=_real_core_package()) auth_mod._hash_password = lambda password: f"hash:{password}" auth_mod._verify_password = lambda password, hashed: hashed == f"hash:{password}" return auth_mod.AuthManager(str(tmp_path / "auth.json")) def test_rename_user_updates_mixed_case_session_username(tmp_path): mgr = _fresh_auth_manager(tmp_path) assert mgr.create_user("admin", "pw-123456", is_admin=True) is True assert mgr.create_user("bob", "pw-123456") is True with mgr._sessions_lock: mgr._sessions["tok1"] = {"username": "Bob", "expiry": time.time() + 3600} assert mgr.rename_user("bob", "robert", "admin") is True with mgr._sessions_lock: assert mgr._sessions["tok1"]["username"] == "robert" def _has_real_sqlalchemy(): mod = sys.modules.get("sqlalchemy") if mod is None or isinstance(mod, MagicMock): return False return hasattr(mod, "create_engine") @pytest.mark.skipif(not _has_real_sqlalchemy(), reason="sqlalchemy not installed") def test_rename_owner_db_filter_is_case_insensitive(): from sqlalchemy import create_engine, func from sqlalchemy.orm import sessionmaker from core.database import Base, Session as DbSession engine = create_engine("sqlite:///:memory:") Base.metadata.create_all(bind=engine) db = sessionmaker(bind=engine)() db.add( DbSession( id="s1", name="chat", endpoint_url="http://localhost:8000", model="gpt-4", owner="Bob", ) ) db.commit() old_username = "bob" new_username = "robert" db.query(DbSession).filter(func.lower(DbSession.owner) == old_username).update( {"owner": new_username}, synchronize_session=False, ) db.commit() assert db.query(DbSession).first().owner == "robert"