1+ """Integration test for build_context with underscore in memory:// URLs."""
2+
3+ import pytest
4+ from fastmcp import Client
5+
6+
7+ @pytest .mark .asyncio
8+ async def test_build_context_underscore_normalization (mcp_server , app , test_project ):
9+ """Test that build_context normalizes underscores in relation types."""
10+
11+ async with Client (mcp_server ) as client :
12+ # Create parent note
13+ await client .call_tool (
14+ "write_note" ,
15+ {
16+ "project" : test_project .name ,
17+ "title" : "Parent Entity" ,
18+ "folder" : "testing" ,
19+ "content" : "# Parent Entity\n \n Main entity for testing underscore relations." ,
20+ "tags" : "test,parent" ,
21+ },
22+ )
23+
24+ # Create child notes with different relation formats
25+ await client .call_tool (
26+ "write_note" ,
27+ {
28+ "project" : test_project .name ,
29+ "title" : "Child with Underscore" ,
30+ "folder" : "testing" ,
31+ "content" : """# Child with Underscore
32+
33+ - part_of [[Parent Entity]]
34+ - related_to [[Parent Entity]]
35+ """ ,
36+ "tags" : "test,child" ,
37+ },
38+ )
39+
40+ await client .call_tool (
41+ "write_note" ,
42+ {
43+ "project" : test_project .name ,
44+ "title" : "Child with Hyphen" ,
45+ "folder" : "testing" ,
46+ "content" : """# Child with Hyphen
47+
48+ - part-of [[Parent Entity]]
49+ - related-to [[Parent Entity]]
50+ """ ,
51+ "tags" : "test,child" ,
52+ },
53+ )
54+
55+ # Test 1: Search with underscore format should return results
56+ result_underscore = await client .call_tool (
57+ "build_context" ,
58+ {
59+ "project" : test_project .name ,
60+ "url" : "memory://testing/parent-entity/part_of/*" , # Using underscore
61+ },
62+ )
63+
64+ # Parse response
65+ assert len (result_underscore .content ) == 1
66+ response_text = result_underscore .content [0 ].text # pyright: ignore
67+ assert '"results"' in response_text
68+
69+ # Both children should be found since they both have part_of/part-of relations
70+ # The system should normalize the underscore to hyphen internally
71+ assert "Child with Underscore" in response_text or "child-with-underscore" in response_text
72+ assert "Child with Hyphen" in response_text or "child-with-hyphen" in response_text
73+
74+ # Test 2: Search with hyphen format should also return results
75+ result_hyphen = await client .call_tool (
76+ "build_context" ,
77+ {
78+ "project" : test_project .name ,
79+ "url" : "memory://testing/parent-entity/part-of/*" , # Using hyphen
80+ },
81+ )
82+
83+ response_text_hyphen = result_hyphen .content [0 ].text # pyright: ignore
84+ assert '"results"' in response_text_hyphen
85+ assert "Child with Underscore" in response_text_hyphen or "child-with-underscore" in response_text_hyphen
86+ assert "Child with Hyphen" in response_text_hyphen or "child-with-hyphen" in response_text_hyphen
87+
88+ # Test 3: Test with related_to/related-to as well
89+ result_related = await client .call_tool (
90+ "build_context" ,
91+ {
92+ "project" : test_project .name ,
93+ "url" : "memory://testing/parent-entity/related_to/*" , # Using underscore
94+ },
95+ )
96+
97+ response_text_related = result_related .content [0 ].text # pyright: ignore
98+ assert '"results"' in response_text_related
99+
100+ # Test 4: Test exact path (non-wildcard) with underscore
101+ result_exact = await client .call_tool (
102+ "build_context" ,
103+ {
104+ "project" : test_project .name ,
105+ "url" : "memory://testing/parent-entity/part_of/child-with-underscore" ,
106+ },
107+ )
108+
109+ response_text_exact = result_exact .content [0 ].text # pyright: ignore
110+ assert '"results"' in response_text_exact
111+
112+
113+ @pytest .mark .asyncio
114+ async def test_build_context_complex_underscore_paths (mcp_server , app , test_project ):
115+ """Test build_context with complex paths containing underscores."""
116+
117+ async with Client (mcp_server ) as client :
118+ # Create notes with underscores in titles and relations
119+ await client .call_tool (
120+ "write_note" ,
121+ {
122+ "project" : test_project .name ,
123+ "title" : "workflow_manager_agent" ,
124+ "folder" : "specs" ,
125+ "content" : """# Workflow Manager Agent
126+
127+ Specification for the workflow manager agent.
128+ """ ,
129+ "tags" : "spec,workflow" ,
130+ },
131+ )
132+
133+ await client .call_tool (
134+ "write_note" ,
135+ {
136+ "project" : test_project .name ,
137+ "title" : "task_parser" ,
138+ "folder" : "components" ,
139+ "content" : """# Task Parser
140+
141+ - part_of [[workflow_manager_agent]]
142+ - implements_for [[workflow_manager_agent]]
143+ """ ,
144+ "tags" : "component,parser" ,
145+ },
146+ )
147+
148+ # Test with underscores in all parts of the path
149+ test_cases = [
150+ "memory://specs/workflow_manager_agent/part_of/*" ,
151+ "memory://specs/workflow-manager-agent/part_of/*" ,
152+ "memory://specs/workflow_manager_agent/part-of/*" ,
153+ "memory://specs/workflow-manager-agent/part-of/*" ,
154+ ]
155+
156+ for url in test_cases :
157+ result = await client .call_tool (
158+ "build_context" , {"project" : test_project .name , "url" : url }
159+ )
160+
161+ # All variations should work and find the related content
162+ assert len (result .content ) == 1
163+ response = result .content [0 ].text # pyright: ignore
164+ assert '"results"' in response
165+ # The task_parser should be found in all cases
166+ assert "task" in response .lower () and "parser" in response .lower (), f"Failed for URL: { url } "
0 commit comments