-
Notifications
You must be signed in to change notification settings - Fork 201
Expand file tree
/
Copy pathtest_build_context_validation.py
More file actions
188 lines (153 loc) · 6.96 KB
/
test_build_context_validation.py
File metadata and controls
188 lines (153 loc) · 6.96 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
"""Integration tests for build_context memory URL validation."""
import pytest
from fastmcp import Client
@pytest.mark.asyncio
async def test_build_context_valid_urls(mcp_server, app, test_project):
"""Test that build_context works with valid memory URLs."""
async with Client(mcp_server) as client:
# Create a test note to ensure we have something to find
await client.call_tool(
"write_note",
{
"project": test_project.name,
"title": "URL Validation Test",
"folder": "testing",
"content": "# URL Validation Test\n\nThis note tests URL validation.",
"tags": "test,validation",
},
)
# Test various valid URL formats
valid_urls = [
"memory://testing/url-validation-test", # Full memory URL
"testing/url-validation-test", # Relative path
"testing/*", # Pattern matching
]
for url in valid_urls:
result = await client.call_tool(
"build_context", {"project": test_project.name, "url": url}
)
# Should return a valid GraphContext response
assert len(result.content) == 1
response = result.content[0].text # pyright: ignore [reportAttributeAccessIssue]
assert '"results"' in response # Should contain results structure
assert '"metadata"' in response # Should contain metadata
@pytest.mark.asyncio
async def test_build_context_invalid_urls_fail_validation(mcp_server, app, test_project):
"""Test that build_context properly validates and rejects invalid memory URLs."""
async with Client(mcp_server) as client:
# Test cases: (invalid_url, expected_error_fragment)
invalid_test_cases = [
("memory//test", "double slashes"),
("invalid://test", "protocol scheme"),
("notes<brackets>", "invalid characters"),
('notes"quotes"', "invalid characters"),
]
for invalid_url, expected_error in invalid_test_cases:
with pytest.raises(Exception) as exc_info:
await client.call_tool(
"build_context", {"project": test_project.name, "url": invalid_url}
)
error_message = str(exc_info.value).lower()
assert expected_error in error_message, (
f"URL '{invalid_url}' should fail with '{expected_error}' error"
)
@pytest.mark.asyncio
async def test_build_context_empty_urls_fail_validation(mcp_server, app, test_project):
"""Test that empty or whitespace-only URLs fail validation."""
async with Client(mcp_server) as client:
# These should fail validation
empty_urls = [
"", # Empty string
" ", # Whitespace only
]
for empty_url in empty_urls:
with pytest.raises(Exception) as exc_info:
await client.call_tool(
"build_context", {"project": test_project.name, "url": empty_url}
)
error_message = str(exc_info.value)
# Should fail with validation error
assert (
"cannot be empty" in error_message
or "empty or whitespace" in error_message
or "value_error" in error_message
or "should be non-empty" in error_message
)
@pytest.mark.asyncio
async def test_build_context_nonexistent_urls_return_empty_results(mcp_server, app, test_project):
"""Test that valid but nonexistent URLs return empty results (not errors)."""
async with Client(mcp_server) as client:
# These are valid URL formats but don't exist in the system
nonexistent_valid_urls = [
"memory://nonexistent/note",
"nonexistent/note",
"missing/*",
]
for url in nonexistent_valid_urls:
result = await client.call_tool(
"build_context", {"project": test_project.name, "url": url}
)
# Should return valid response with empty results
assert len(result.content) == 1
response = result.content[0].text # pyright: ignore [reportAttributeAccessIssue]
assert '"results":[]' in response # Empty results
assert '"total_results":0' in response # Zero count
assert '"metadata"' in response # But should have metadata
@pytest.mark.asyncio
async def test_build_context_error_messages_are_helpful(mcp_server, app, test_project):
"""Test that validation error messages provide helpful guidance."""
async with Client(mcp_server) as client:
# Test double slash error message
with pytest.raises(Exception) as exc_info:
await client.call_tool(
"build_context", {"project": test_project.name, "url": "memory//bad"}
)
error_msg = str(exc_info.value).lower()
# Should contain validation error info
assert (
"double slashes" in error_msg
or "value_error" in error_msg
or "validation error" in error_msg
)
# Test protocol scheme error message
with pytest.raises(Exception) as exc_info:
await client.call_tool(
"build_context", {"project": test_project.name, "url": "http://example.com"}
)
error_msg = str(exc_info.value).lower()
assert (
"protocol scheme" in error_msg
or "protocol" in error_msg
or "value_error" in error_msg
or "validation error" in error_msg
)
@pytest.mark.asyncio
async def test_build_context_pattern_matching_works(mcp_server, app, test_project):
"""Test that valid pattern matching URLs work correctly."""
async with Client(mcp_server) as client:
# Create multiple test notes
test_notes = [
("Pattern Test One", "patterns", "# Pattern Test One\n\nFirst pattern test."),
("Pattern Test Two", "patterns", "# Pattern Test Two\n\nSecond pattern test."),
("Other Note", "other", "# Other Note\n\nNot a pattern match."),
]
for title, folder, content in test_notes:
await client.call_tool(
"write_note",
{
"project": test_project.name,
"title": title,
"folder": folder,
"content": content,
},
)
# Test pattern matching
result = await client.call_tool(
"build_context", {"project": test_project.name, "url": "patterns/*"}
)
assert len(result.content) == 1
response = result.content[0].text # pyright: ignore [reportAttributeAccessIssue]
# Should find the pattern matches but not the other note
assert '"total_results":2' in response or '"primary_count":2' in response
assert "Pattern Test" in response
assert "Other Note" not in response