"""Tests for multi-tenancy helpers.""" import pytest from src.core.multi_tenancy import ( sanitize_user_id, get_qdrant_collection_name, get_wikijs_namespace, get_neo4j_user_label, validate_user_id, validate_required_user, is_path_in_user_namespace, ) class TestSanitizeUserId: """Test user ID sanitization.""" def test_lowercase_conversion(self): assert sanitize_user_id("JohnDoe") == "johndoe" def test_email_conversion(self): assert sanitize_user_id("john@example.com") == "john_at_example_com" def test_dot_conversion(self): assert sanitize_user_id("john.doe") == "john_doe" def test_space_conversion(self): assert sanitize_user_id("John Doe") == "john_doe" def test_special_chars_removal(self): assert sanitize_user_id("john-doe!") == "john_doe" def test_consecutive_underscores(self): assert sanitize_user_id("john__doe") == "john_doe" def test_leading_trailing_underscores(self): assert sanitize_user_id("_john_") == "john" class TestQdrantCollectionName: """Test Qdrant collection name generation.""" def test_simple_user(self): assert get_qdrant_collection_name("jpmschweitzer") == "library_desk_jpmschweitzer" def test_email_user(self): assert get_qdrant_collection_name("john@example.com") == "library_desk_john_at_example_com" def test_test_tenant(self): assert get_qdrant_collection_name("llm_tester") == "library_desk_llm_tester" class TestWikijsNamespace: """Test Wiki.js namespace generation.""" def test_simple_user(self): assert get_wikijs_namespace("jpmschweitzer") == "/users/jpmschweitzer" def test_email_user(self): assert get_wikijs_namespace("john@example.com") == "/users/john_at_example_com" def test_starts_with_slash(self): namespace = get_wikijs_namespace("testuser") assert namespace.startswith("/") class TestNeo4jUserLabel: """Test Neo4j user label generation.""" def test_simple_user(self): assert get_neo4j_user_label("jpmschweitzer") == "User_Jpmschweitzer_Document" def test_email_user(self): result = get_neo4j_user_label("john@example.com") # Should be title case assert result == "User_John_At_Example_Com_Document" def test_title_case(self): result = get_neo4j_user_label("john_doe") assert result == "User_John_Doe_Document" class TestValidateUserId: """Test user ID validation.""" def test_valid_simple(self): assert validate_user_id("jpmschweitzer") is True def test_valid_email(self): assert validate_user_id("john@example.com") is True def test_empty_invalid(self): assert validate_user_id("") is False def test_too_long_invalid(self): assert validate_user_id("a" * 101) is False def test_no_alphanumeric_invalid(self): assert validate_user_id("___") is False class TestValidateRequiredUser: """Test the required-user validator (no default tenant).""" def test_no_default_user_constant(self): """The DEFAULT_USER escape hatch must not exist anymore.""" import src.core.multi_tenancy as mt assert not hasattr(mt, "DEFAULT_USER") def test_valid_user_returned(self): assert validate_required_user("llm_tester") == "llm_tester" def test_valid_user_stripped(self): assert validate_required_user(" llm_tester ") == "llm_tester" def test_empty_rejected(self): with pytest.raises(ValueError): validate_required_user("") def test_whitespace_rejected(self): with pytest.raises(ValueError): validate_required_user(" ") def test_none_rejected(self): with pytest.raises(ValueError): validate_required_user(None) def test_no_alphanumeric_rejected(self): with pytest.raises(ValueError): validate_required_user("___") def test_too_long_rejected(self): with pytest.raises(ValueError): validate_required_user("a" * 101) class TestPathInNamespace: """Test path namespace checking.""" def test_path_in_namespace(self): assert is_path_in_user_namespace("/users/jpmschweitzer/projects", "jpmschweitzer") is True def test_path_not_in_namespace(self): assert is_path_in_user_namespace("/users/other/projects", "jpmschweitzer") is False def test_public_path_not_in_namespace(self): assert is_path_in_user_namespace("/public/docs", "jpmschweitzer") is False def test_root_path(self): assert is_path_in_user_namespace("/users/test", "test") is True