Skip to content

Commit f00224d

Browse files
committed
feat(integration-tests): Add integration tests for MCP server
1 parent 8cebaa4 commit f00224d

File tree

3 files changed

+1584
-132
lines changed

3 files changed

+1584
-132
lines changed

integration-tests/pyproject.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ readme = "README.md"
66
authors = [
77
{ name = "YScope Inc.", email = "[email protected]" }
88
]
9-
requires-python = ">=3.9"
9+
requires-python = ">=3.10"
1010

1111
[project.scripts]
1212
integration-tests = "integration_tests:main"
@@ -21,6 +21,7 @@ dev = [
2121
"ruff>=0.11.12",
2222
"pytest>=8.4.1",
2323
"pytest-env>=1.1.5",
24+
"fastmcp>=2.12.4",
2425
]
2526

2627
[tool.mypy]
Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
"""
2+
The integration tests for the CLP MCP server.
3+
4+
IMPORTANT: These tests are designed to be run **manually** against a deployed CLP package.
5+
We will migrate them to automated tests when the test environment includes docker compose.
6+
7+
### Environment Setup
8+
9+
Deploy the CLP package with MCP server. The test suites assume the MCP server is accessible
10+
at http://0.0.0.0:8000/mcp. If the MCP server is hosted at a different address, update the
11+
`MCP_SERVER_ENDPOINT` variable below accordingly.
12+
13+
### Log Dataset
14+
15+
The test dataset is taken from the `HDFS-11896` case study. To access the logs, visit the
16+
repository [y-scope/agent-dev](https://github.com/y-scope/agent-dev) and follow the instructions
17+
from `case-study/sync-logs-to-r2.sh`. The log file is located in `CLODS/HDFS/HDFS-11896/json`.
18+
Use the instructions below to ingest the logs into CLP package:
19+
```
20+
gzip -d CLODS/HDFS/HDFS-11896/json/hadoop-dn1__logs__hadoop.jsonl.gz
21+
sbin/compress.sh --timestamp-key ts CLODS/HDFS/HDFS-11896/json/hadoop-dn1__logs__hadoop.jsonl
22+
```
23+
"""
24+
25+
import pytest
26+
from fastmcp import Client
27+
28+
MCP_SERVER_ENDPOINT = "http://0.0.0.0:8000/mcp"
29+
30+
31+
@pytest.mark.skip(reason="expected to run manually against a deployed CLP package")
32+
@pytest.mark.asyncio
33+
async def test_get_instructions() -> None:
34+
"""Tests the get_instructions tool."""
35+
async with Client(MCP_SERVER_ENDPOINT) as client:
36+
res = await client.call_tool("get_instructions")
37+
assert isinstance(res.data, str)
38+
39+
40+
@pytest.mark.skip(reason="expected to run manually against a deployed CLP package")
41+
@pytest.mark.asyncio
42+
async def test_search_kql_query() -> None:
43+
"""Tests the search_kql_query tool."""
44+
async with Client(MCP_SERVER_ENDPOINT) as client:
45+
await client.call_tool("get_instructions")
46+
47+
query = "*filter*"
48+
result = await client.call_tool("search_kql_query", {"kql_query": query})
49+
50+
assert "items" in result.data
51+
assert isinstance(result.data["items"], list)
52+
assert "num_total_items" in result.data
53+
assert "num_total_pages" in result.data
54+
assert "num_items_per_page" in result.data
55+
assert "has_next" in result.data
56+
assert "has_previous" in result.data
57+
58+
59+
@pytest.mark.skip(reason="expected to run manually against a deployed CLP package")
60+
@pytest.mark.asyncio
61+
async def test_get_nth_page() -> None:
62+
"""Tests the get_nth_page tool."""
63+
async with Client(MCP_SERVER_ENDPOINT) as client:
64+
await client.call_tool("get_instructions")
65+
66+
query = "*filter*"
67+
await client.call_tool("search_kql_query", {"kql_query": query})
68+
69+
result = await client.call_tool("get_nth_page", {"page_index": 1})
70+
assert "items" in result.data
71+
assert isinstance(result.data["items"], list)
72+
assert "num_total_items" in result.data
73+
assert "num_total_pages" in result.data
74+
assert "num_items_per_page" in result.data
75+
assert "has_next" in result.data
76+
assert "has_previous" in result.data
77+
78+
79+
@pytest.mark.skip(reason="expected to run manually against a deployed CLP package")
80+
class TestSearchWithTimestamp:
81+
"""Tests the search_kql_query_with_timestamp tool."""
82+
83+
@pytest.mark.asyncio
84+
async def test_success_case(self) -> None:
85+
"""Tests the success case of the search_kql_query_with_timestamp tool."""
86+
async with Client(MCP_SERVER_ENDPOINT) as client:
87+
await client.call_tool("get_instructions")
88+
89+
query = "*filter*"
90+
result = await client.call_tool(
91+
"search_kql_query_with_timestamp",
92+
{
93+
"kql_query": query,
94+
"begin_timestamp": "2025-08-27T15:35:30.000Z",
95+
"end_timestamp": "2025-08-27T15:35:50.000Z",
96+
},
97+
)
98+
assert "items" in result.data
99+
assert isinstance(result.data["items"], list)
100+
assert "num_total_items" in result.data
101+
assert "num_total_pages" in result.data
102+
assert "num_items_per_page" in result.data
103+
assert "has_next" in result.data
104+
assert "has_previous" in result.data
105+
106+
@pytest.mark.asyncio
107+
async def test_invalid_timestamps(self) -> None:
108+
"""Tests the search_kql_query_with_timestamp tool with invalid timestamps."""
109+
async with Client(MCP_SERVER_ENDPOINT) as client:
110+
await client.call_tool("get_instructions")
111+
112+
query = "*filter*"
113+
result = await client.call_tool(
114+
"search_kql_query_with_timestamp",
115+
{
116+
"kql_query": query,
117+
"begin_timestamp": "2025-08-27T15:35:55.000Z",
118+
"end_timestamp": "2025-08-27T15:35:50.000Z",
119+
},
120+
)
121+
assert "Error" in result.data
122+
123+
@pytest.mark.asyncio
124+
async def test_query_failure(self) -> None:
125+
"""Tests the search_kql_query_with_timestamp tool with a query that is killed."""
126+
async with Client(MCP_SERVER_ENDPOINT) as client:
127+
await client.call_tool("get_instructions")
128+
129+
query = "*filter*"
130+
result = await client.call_tool(
131+
"search_kql_query_with_timestamp",
132+
{
133+
"kql_query": query,
134+
# This test (ab)uses a design in CLP: when the begin timestamp is set before
135+
# Unix epoch, the query will be killed. It works as of Oct. 22, 2025. The
136+
# future versions of CLP may change this behavior.
137+
"begin_timestamp": "1970-01-01T00:00:00.000Z",
138+
"end_timestamp": "1970-01-02T00:00:00.000Z",
139+
},
140+
)
141+
assert "Error" in result.data
142+
143+
144+
@pytest.mark.skip(reason="expected to run manually against a deployed CLP package")
145+
@pytest.mark.asyncio
146+
async def test_log_link() -> None:
147+
"""Tests that the search results contain log links."""
148+
async with Client(MCP_SERVER_ENDPOINT) as client:
149+
await client.call_tool("get_instructions")
150+
151+
query = "*error*"
152+
result = await client.call_tool("search_kql_query", {"kql_query": query})
153+
154+
for item in result.data["items"]:
155+
assert "link" in item

0 commit comments

Comments
 (0)