"""tests for repository identifier resolution""" import pytest from tangled_mcp._tangled._client import _extract_error_message class TestExtractErrorMessage: """test error message extraction from various exception types""" def test_extracts_from_atproto_response(self): """extracts message from atproto RequestErrorBase structure""" class MockContent: message = "handle must be a valid handle" class MockResponse: content = MockContent() class MockException(Exception): response = MockResponse() msg = _extract_error_message(MockException()) assert msg == "handle must be a valid handle" def test_truncates_long_messages(self): """truncates messages longer than 200 chars""" long_msg = "x" * 300 class MockException(Exception): pass e = MockException(long_msg) msg = _extract_error_message(e) assert len(msg) == 203 # 200 + "..." assert msg.endswith("...") def test_handles_none_response(self): """handles exception with None response""" class MockException(Exception): response = None e = MockException("fallback message") msg = _extract_error_message(e) assert msg == "fallback message" class TestRepoIdentifierParsing: """test repository identifier format validation""" def test_invalid_format_no_slash(self): """test that identifiers without slash are rejected""" from tangled_mcp._tangled._client import resolve_repo_identifier with pytest.raises( ValueError, match="invalid repo format.*expected 'owner/repo'" ): resolve_repo_identifier("invalid") def test_invalid_format_empty(self): """test that empty identifiers are rejected""" from tangled_mcp._tangled._client import resolve_repo_identifier with pytest.raises( ValueError, match="invalid repo format.*expected 'owner/repo'" ): resolve_repo_identifier("") def test_valid_format_with_handle(self): """test that valid owner/repo format is accepted (parsing only)""" # note: we can't actually test resolution without credentials # but we can test that the format parsing works from tangled_mcp._tangled._client import resolve_repo_identifier # this will fail at the resolution step, but not at parsing with pytest.raises(Exception): # will fail during actual resolution resolve_repo_identifier("owner/repo") def test_valid_format_with_at_prefix(self): """test that @owner/repo and owner/repo resolve identically""" from tangled_mcp._tangled._client import resolve_repo_identifier # both formats should behave the same (@ is stripped internally) # they'll both fail resolution with fake handle, but in the same way with pytest.raises(ValueError, match="failed to resolve handle 'owner'"): resolve_repo_identifier("@owner/repo") with pytest.raises(ValueError, match="failed to resolve handle 'owner'"): resolve_repo_identifier("owner/repo") def test_valid_format_with_did(self): """test that did:plc:.../repo format is accepted""" from tangled_mcp._tangled._client import resolve_repo_identifier # this will fail at the resolution step, but not at parsing with pytest.raises(Exception): # will fail during actual resolution resolve_repo_identifier("did:plc:test123/repo")