MCP server for tangled
1"""tests for repository identifier resolution"""
2
3import pytest
4
5from tangled_mcp._tangled._client import _extract_error_message
6
7
8class TestExtractErrorMessage:
9 """test error message extraction from various exception types"""
10
11 def test_extracts_from_atproto_response(self):
12 """extracts message from atproto RequestErrorBase structure"""
13
14 class MockContent:
15 message = "handle must be a valid handle"
16
17 class MockResponse:
18 content = MockContent()
19
20 class MockException(Exception):
21 response = MockResponse()
22
23 msg = _extract_error_message(MockException())
24 assert msg == "handle must be a valid handle"
25
26 def test_truncates_long_messages(self):
27 """truncates messages longer than 200 chars"""
28 long_msg = "x" * 300
29
30 class MockException(Exception):
31 pass
32
33 e = MockException(long_msg)
34 msg = _extract_error_message(e)
35 assert len(msg) == 203 # 200 + "..."
36 assert msg.endswith("...")
37
38 def test_handles_none_response(self):
39 """handles exception with None response"""
40
41 class MockException(Exception):
42 response = None
43
44 e = MockException("fallback message")
45 msg = _extract_error_message(e)
46 assert msg == "fallback message"
47
48
49class TestRepoIdentifierParsing:
50 """test repository identifier format validation"""
51
52 def test_invalid_format_no_slash(self):
53 """test that identifiers without slash are rejected"""
54 from tangled_mcp._tangled._client import resolve_repo_identifier
55
56 with pytest.raises(
57 ValueError, match="invalid repo format.*expected 'owner/repo'"
58 ):
59 resolve_repo_identifier("invalid")
60
61 def test_invalid_format_empty(self):
62 """test that empty identifiers are rejected"""
63 from tangled_mcp._tangled._client import resolve_repo_identifier
64
65 with pytest.raises(
66 ValueError, match="invalid repo format.*expected 'owner/repo'"
67 ):
68 resolve_repo_identifier("")
69
70 def test_valid_format_with_handle(self):
71 """test that valid owner/repo format is accepted (parsing only)"""
72 # note: we can't actually test resolution without credentials
73 # but we can test that the format parsing works
74 from tangled_mcp._tangled._client import resolve_repo_identifier
75
76 # this will fail at the resolution step, but not at parsing
77 with pytest.raises(Exception): # will fail during actual resolution
78 resolve_repo_identifier("owner/repo")
79
80 def test_valid_format_with_at_prefix(self):
81 """test that @owner/repo and owner/repo resolve identically"""
82 from tangled_mcp._tangled._client import resolve_repo_identifier
83
84 # both formats should behave the same (@ is stripped internally)
85 # they'll both fail resolution with fake handle, but in the same way
86 with pytest.raises(ValueError, match="failed to resolve handle 'owner'"):
87 resolve_repo_identifier("@owner/repo")
88
89 with pytest.raises(ValueError, match="failed to resolve handle 'owner'"):
90 resolve_repo_identifier("owner/repo")
91
92 def test_valid_format_with_did(self):
93 """test that did:plc:.../repo format is accepted"""
94 from tangled_mcp._tangled._client import resolve_repo_identifier
95
96 # this will fail at the resolution step, but not at parsing
97 with pytest.raises(Exception): # will fail during actual resolution
98 resolve_repo_identifier("did:plc:test123/repo")