Skip to content

Commit 1039e58

Browse files
jor2Jordan-Williams2
andauthored
fix: total found is incorrectly set to 0 (#37)
Co-authored-by: Jordan-Williams2 <[email protected]>
1 parent 0dfb979 commit 1039e58

File tree

2 files changed

+149
-3
lines changed

2 files changed

+149
-3
lines changed

test/test_search_modules.py

Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1208,3 +1208,137 @@ def test_negative_downloads(self):
12081208
verified=True,
12091209
published_at=datetime.now(),
12101210
)
1211+
1212+
1213+
class TestTotalFoundBug:
1214+
"""Test for issue #21 - total_found incorrectly set to 0."""
1215+
1216+
@pytest.mark.asyncio
1217+
async def test_total_found_reflects_actual_total_from_registry(self):
1218+
"""
1219+
Test that total_found reflects the total count from the Terraform Registry,
1220+
not 0 or the number of returned modules.
1221+
1222+
This test reproduces issue #21 where total_found was being set to 0
1223+
even though modules were returned.
1224+
"""
1225+
config = Config()
1226+
1227+
# Create mock clients
1228+
mock_terraform_client = AsyncMock()
1229+
mock_github_client = MagicMock() # Use MagicMock for GitHub client since parse_github_url is sync
1230+
1231+
# Mock the async context manager methods
1232+
mock_terraform_client.__aenter__.return_value = mock_terraform_client
1233+
mock_terraform_client.__aexit__.return_value = None
1234+
mock_github_client.__aenter__.return_value = mock_github_client
1235+
mock_github_client.__aexit__.return_value = None
1236+
1237+
# Mock GitHub client to validate all repositories
1238+
def mock_parse_url(url):
1239+
# Extract owner/repo from the URL
1240+
if "terraform-ibm-vsi" in url:
1241+
return ("terraform-ibm-modules", "terraform-ibm-vsi")
1242+
elif "landing-zone-vsi" in url:
1243+
return ("terraform-ibm-modules", "terraform-ibm-landing-zone-vsi")
1244+
elif "vpc-vsi" in url:
1245+
return ("terraform-ibm-modules", "terraform-ibm-vpc-vsi")
1246+
return ("terraform-ibm-modules", "test-repo")
1247+
1248+
mock_github_client.parse_github_url.side_effect = mock_parse_url
1249+
1250+
# Mock get_repository_info to return valid repo data
1251+
async def mock_get_repo_info(owner, repo_name):
1252+
return {
1253+
"archived": False,
1254+
"topics": ["core-team"], # Required topic
1255+
}
1256+
1257+
mock_github_client.get_repository_info.side_effect = mock_get_repo_info
1258+
1259+
# Simulate a search that requires multiple batches
1260+
# Registry has 15 total modules, but we need to fetch them in batches
1261+
# First batch returns total_count=15, second batch is missing total_count
1262+
batch_count = 0
1263+
1264+
async def mock_search(*args, **kwargs):
1265+
nonlocal batch_count
1266+
batch_count += 1
1267+
1268+
if batch_count == 1:
1269+
# First batch: has total_count=15
1270+
return {
1271+
"modules": [
1272+
{
1273+
"id": "terraform-ibm-modules/vpc-vsi/ibm",
1274+
"namespace": "terraform-ibm-modules",
1275+
"name": "vpc-vsi",
1276+
"provider": "ibm",
1277+
"version": "2.0.0",
1278+
"description": "VPC VSI module",
1279+
"source": "https://github.com/terraform-ibm-modules/terraform-ibm-vpc-vsi",
1280+
"downloads": 3000,
1281+
"verified": True,
1282+
"published_at": "2025-09-01T00:00:00.000Z",
1283+
},
1284+
],
1285+
"meta": {
1286+
"limit": 10,
1287+
"offset": 0,
1288+
"total_count": 15, # Total modules matching "vsi" query
1289+
},
1290+
}
1291+
else:
1292+
# Second batch: missing total_count (simulates API inconsistency)
1293+
# This causes the bug - total_count gets reset to 0
1294+
return {
1295+
"modules": [
1296+
{
1297+
"id": "terraform-ibm-modules/vsi/ibm",
1298+
"namespace": "terraform-ibm-modules",
1299+
"name": "vsi",
1300+
"provider": "ibm",
1301+
"version": "1.0.0",
1302+
"description": "VSI module",
1303+
"source": "https://github.com/terraform-ibm-modules/terraform-ibm-vsi",
1304+
"downloads": 1000,
1305+
"verified": True,
1306+
"published_at": "2025-09-01T00:00:00.000Z",
1307+
},
1308+
{
1309+
"id": "terraform-ibm-modules/landing-zone-vsi/ibm",
1310+
"namespace": "terraform-ibm-modules",
1311+
"name": "landing-zone-vsi",
1312+
"provider": "ibm",
1313+
"version": "5.10.0",
1314+
"description": "Landing Zone VSI module",
1315+
"source": "https://github.com/terraform-ibm-modules/terraform-ibm-landing-zone-vsi",
1316+
"downloads": 2000,
1317+
"verified": True,
1318+
"published_at": "2025-09-01T00:00:00.000Z",
1319+
},
1320+
],
1321+
"meta": {
1322+
"limit": 10,
1323+
"offset": 50,
1324+
# total_count is missing here - causes bug!
1325+
},
1326+
}
1327+
1328+
mock_terraform_client.search_modules = mock_search
1329+
1330+
request = ModuleSearchRequest(query="vsi", limit=3)
1331+
1332+
# Patch the context managers
1333+
with patch("tim_mcp.tools.search.TerraformClient", return_value=mock_terraform_client), \
1334+
patch("tim_mcp.tools.search.GitHubClient", return_value=mock_github_client):
1335+
result = await search_modules_impl(request, config)
1336+
1337+
# The bug: total_found is set to 0 instead of 15
1338+
# This assertion should FAIL initially, demonstrating the bug
1339+
assert result.total_found == 15, \
1340+
f"Expected total_found=15 (total from registry), but got {result.total_found}"
1341+
1342+
# Verify we got the expected modules
1343+
assert len(result.modules) == 3
1344+
assert result.query == "vsi"

tim_mcp/tools/search.py

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,9 @@ async def search_modules_impl(
9999
batch_size = 50 # Fetch modules in smaller batches
100100
max_attempts = 10 # Prevent infinite loops
101101

102+
# Track the total count from the first API response
103+
total_count = 0
104+
102105
attempt = 0
103106
while len(validated_modules) < request.limit and attempt < max_attempts:
104107
attempt += 1
@@ -116,7 +119,11 @@ async def search_modules_impl(
116119

117120
# Extract metadata from response
118121
meta = api_response["meta"]
119-
total_count = meta.get("total_count", 0)
122+
123+
# Capture total_count from the first batch only
124+
# Subsequent batches might have inconsistent or missing total_count
125+
if attempt == 1:
126+
total_count = meta.get("total_count", 0)
120127

121128
# If no more modules available, break
122129
if not api_response["modules"]:
@@ -174,17 +181,22 @@ async def search_modules_impl(
174181
# Ensure we don't exceed the requested limit
175182
final_modules = validated_modules[: request.limit]
176183

184+
# Since the Terraform Registry API doesn't return total_count,
185+
# we use the actual number of modules we found and validated
186+
# If total_count was provided in the API response, use that; otherwise use our count
187+
result_total = total_count if total_count > 0 else len(final_modules)
188+
177189
logger.info(
178190
"Search completed",
179-
total_found=total_count,
191+
total_found=result_total,
180192
modules_validated=len(final_modules),
181193
attempts=attempt,
182194
)
183195

184196
# Create and return the formatted response
185197
return ModuleSearchResponse(
186198
query=request.query,
187-
total_found=total_count,
199+
total_found=result_total,
188200
modules=final_modules,
189201
)
190202

0 commit comments

Comments
 (0)