Skip to content

Commit a557901

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

File tree

1 file changed

+156
-0
lines changed

1 file changed

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

0 commit comments

Comments
 (0)