Skip to content

Commit dd7db0d

Browse files
committed
Remove redundant repositoryIds field from workspace data sources and associated references.
1 parent b06f561 commit dd7db0d

File tree

3 files changed

+188
-6
lines changed

3 files changed

+188
-6
lines changed

src/codealive_mcp_server.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,8 +64,6 @@
6464
- If a user is working within a git repository that matches a CodeAlive-indexed repository (by URL),
6565
you can suggest using CodeAlive's search and chat to understand the codebase
6666
- Data sources include repository URLs to help match with local git repositories
67-
- Workspaces include a list of repository IDs, allowing you to understand their composition
68-
- You can use repository IDs from workspaces to search or chat about specific repositories within a workspace
6967
7068
When analyzing search results:
7169
- Pay attention to file paths to understand the project structure

src/tests/test_datasources.py

Lines changed: 183 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,183 @@
1+
"""Tests for data sources tool."""
2+
3+
import json
4+
from unittest.mock import AsyncMock, MagicMock, patch
5+
6+
import pytest
7+
from fastmcp import Context
8+
9+
from tools.datasources import get_data_sources
10+
11+
12+
@pytest.mark.asyncio
13+
@patch('tools.datasources.get_api_key_from_context')
14+
async def test_get_data_sources_removes_repository_ids_from_workspaces(mock_get_api_key):
15+
"""Test that repositoryIds are removed from workspace data sources."""
16+
mock_get_api_key.return_value = "test-key"
17+
18+
# Mock context
19+
mock_ctx = MagicMock(spec=Context)
20+
mock_ctx.info = AsyncMock()
21+
mock_ctx.warning = AsyncMock()
22+
mock_ctx.error = AsyncMock()
23+
24+
mock_lifespan_context = MagicMock()
25+
mock_lifespan_context.base_url = "https://api.example.com"
26+
27+
# Mock client with response containing workspaces with repositoryIds
28+
mock_response = MagicMock()
29+
mock_response.json.return_value = [
30+
{
31+
"id": "repo-1",
32+
"name": "Test Repository",
33+
"type": "Repository",
34+
"url": "https://github.com/example/repo",
35+
"state": "Alive"
36+
},
37+
{
38+
"id": "workspace-1",
39+
"name": "Test Workspace",
40+
"type": "Workspace",
41+
"repositoryIds": ["repo-1", "repo-2", "repo-3"],
42+
"state": "Alive"
43+
}
44+
]
45+
mock_response.raise_for_status = MagicMock()
46+
47+
mock_client = AsyncMock()
48+
mock_client.get = AsyncMock(return_value=mock_response)
49+
mock_lifespan_context.client = mock_client
50+
51+
mock_ctx.request_context.lifespan_context = mock_lifespan_context
52+
53+
# Call the function
54+
result = await get_data_sources(mock_ctx, alive_only=True)
55+
56+
# Parse the result to verify repositoryIds were removed
57+
# The result format is: "Available data sources:\n{json}\n\nYou can use..."
58+
json_start = result.index('[')
59+
json_end = result.rindex(']') + 1
60+
data_sources = json.loads(result[json_start:json_end])
61+
62+
# Verify repository still has all fields
63+
repo = next(ds for ds in data_sources if ds["type"] == "Repository")
64+
assert repo["id"] == "repo-1"
65+
assert repo["name"] == "Test Repository"
66+
assert repo["url"] == "https://github.com/example/repo"
67+
assert "repositoryIds" not in repo
68+
69+
# Verify workspace has repositoryIds removed
70+
workspace = next(ds for ds in data_sources if ds["type"] == "Workspace")
71+
assert workspace["id"] == "workspace-1"
72+
assert workspace["name"] == "Test Workspace"
73+
assert "repositoryIds" not in workspace, "repositoryIds should be removed from workspace"
74+
75+
# Verify API was called correctly
76+
mock_client.get.assert_called_once_with(
77+
"/api/datasources/alive",
78+
headers={"Authorization": "Bearer test-key"}
79+
)
80+
81+
82+
@pytest.mark.asyncio
83+
@patch('tools.datasources.get_api_key_from_context')
84+
async def test_get_data_sources_preserves_other_workspace_fields(mock_get_api_key):
85+
"""Test that other workspace fields are preserved when removing repositoryIds."""
86+
mock_get_api_key.return_value = "test-key"
87+
88+
# Mock context
89+
mock_ctx = MagicMock(spec=Context)
90+
mock_ctx.info = AsyncMock()
91+
mock_ctx.warning = AsyncMock()
92+
mock_ctx.error = AsyncMock()
93+
94+
mock_lifespan_context = MagicMock()
95+
mock_lifespan_context.base_url = "https://api.example.com"
96+
97+
# Mock client with workspace containing various fields
98+
mock_response = MagicMock()
99+
mock_response.json.return_value = [
100+
{
101+
"id": "workspace-1",
102+
"name": "Test Workspace",
103+
"type": "Workspace",
104+
"state": "Alive",
105+
"repositoryIds": ["repo-1", "repo-2"],
106+
"customField": "custom-value",
107+
"createdAt": "2025-01-01T00:00:00Z"
108+
}
109+
]
110+
mock_response.raise_for_status = MagicMock()
111+
112+
mock_client = AsyncMock()
113+
mock_client.get = AsyncMock(return_value=mock_response)
114+
mock_lifespan_context.client = mock_client
115+
116+
mock_ctx.request_context.lifespan_context = mock_lifespan_context
117+
118+
# Call the function
119+
result = await get_data_sources(mock_ctx, alive_only=True)
120+
121+
# Parse the result
122+
json_start = result.index('[')
123+
json_end = result.rindex(']') + 1
124+
data_sources = json.loads(result[json_start:json_end])
125+
126+
workspace = data_sources[0]
127+
128+
# Verify repositoryIds removed but other fields preserved
129+
assert "repositoryIds" not in workspace
130+
assert workspace["id"] == "workspace-1"
131+
assert workspace["name"] == "Test Workspace"
132+
assert workspace["type"] == "Workspace"
133+
assert workspace["state"] == "Alive"
134+
assert workspace["customField"] == "custom-value"
135+
assert workspace["createdAt"] == "2025-01-01T00:00:00Z"
136+
137+
138+
@pytest.mark.asyncio
139+
@patch('tools.datasources.get_api_key_from_context')
140+
async def test_get_data_sources_handles_missing_repository_ids(mock_get_api_key):
141+
"""Test that function handles workspaces without repositoryIds field."""
142+
mock_get_api_key.return_value = "test-key"
143+
144+
# Mock context
145+
mock_ctx = MagicMock(spec=Context)
146+
mock_ctx.info = AsyncMock()
147+
mock_ctx.warning = AsyncMock()
148+
mock_ctx.error = AsyncMock()
149+
150+
mock_lifespan_context = MagicMock()
151+
mock_lifespan_context.base_url = "https://api.example.com"
152+
153+
# Mock client with workspace without repositoryIds
154+
mock_response = MagicMock()
155+
mock_response.json.return_value = [
156+
{
157+
"id": "workspace-1",
158+
"name": "Test Workspace",
159+
"type": "Workspace",
160+
"state": "Alive"
161+
}
162+
]
163+
mock_response.raise_for_status = MagicMock()
164+
165+
mock_client = AsyncMock()
166+
mock_client.get = AsyncMock(return_value=mock_response)
167+
mock_lifespan_context.client = mock_client
168+
169+
mock_ctx.request_context.lifespan_context = mock_lifespan_context
170+
171+
# Call the function - should not raise an error
172+
result = await get_data_sources(mock_ctx, alive_only=True)
173+
174+
# Parse the result
175+
json_start = result.index('[')
176+
json_end = result.rindex(']') + 1
177+
data_sources = json.loads(result[json_start:json_end])
178+
179+
# Verify workspace is intact
180+
workspace = data_sources[0]
181+
assert workspace["id"] == "workspace-1"
182+
assert workspace["name"] == "Test Workspace"
183+
assert "repositoryIds" not in workspace

src/tools/datasources.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,6 @@ async def get_data_sources(ctx: Context, alive_only: bool = True) -> str:
2727
- name: Human-readable name of the repository or workspace
2828
- type: The type of data source ("Repository" or "Workspace")
2929
- url: URL of the repository (for Repository type only)
30-
- repositoryIds: List of repository IDs included in the workspace (for Workspace type only)
3130
- state: The processing state of the data source (if alive_only=false)
3231
3332
Examples:
@@ -45,9 +44,6 @@ async def get_data_sources(ctx: Context, alive_only: bool = True) -> str:
4544
For repositories, the URL can be used to match with local git repositories
4645
to provide enhanced context for code understanding.
4746
48-
For workspaces, the repositoryIds can be used to identify and work with
49-
individual repositories that make up the workspace.
50-
5147
Use the returned data source IDs with the codebase_search and codebase_consultant functions.
5248
"""
5349
context: CodeAliveContext = ctx.request_context.lifespan_context
@@ -79,6 +75,11 @@ async def get_data_sources(ctx: Context, alive_only: bool = True) -> str:
7975
if not data_sources or len(data_sources) == 0:
8076
return "No data sources found. Please add a repository or workspace to CodeAlive before using this API."
8177

78+
# Remove repositoryIds from workspace data sources
79+
for data_source in data_sources:
80+
if data_source.get("type") == "Workspace" and "repositoryIds" in data_source:
81+
del data_source["repositoryIds"]
82+
8283
# Format the response as a readable string
8384
formatted_data = json.dumps(data_sources, indent=2)
8485
result = f"Available data sources:\n{formatted_data}"

0 commit comments

Comments
 (0)