Skip to content

Commit cd078e4

Browse files
committed
Test
1 parent d0197f8 commit cd078e4

8 files changed

Lines changed: 186 additions & 4 deletions

File tree

.github/workflows/test.yml

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,10 +46,12 @@ jobs:
4646
uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae
4747
with:
4848
path: .pytest_cache
49-
key: pytest-cache-${{ runner.os }}-py${{ matrix.python-version }}-${{ github.ref_name }}-${{ github.sha }}
49+
key: pytest-cache-${{ runner.os }}-py${{ matrix.python-version }}-${{ github.ref_name }}-${{
50+
github.sha }}
5051
restore-keys: |
5152
pytest-cache-${{ runner.os }}-py${{ matrix.python-version }}-${{ github.ref_name }}-
5253
- name: Run unit tests
5354
run: make test-unit
5455
- name: Run entire test suite
55-
run: make test-integration
56+
# run: make test-integration
57+
run: uv run pytest ./tests/system/test_ai_agentic_test_app.py::TestAgenticApp

pytest.ini

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,6 @@ markers =
55

66
junit_family =
77
xunit2
8+
9+
log_cli = true
10+
log_cli_level = INFO

tests/system/__init__.py

Whitespace-only changes.

tests/system/test_ai_agentic_test_app.py

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,15 @@
1313
# under the License.
1414

1515

16+
import logging
17+
1618
import pytest
1719

1820
from splunklib.binding import HTTPError
1921
from tests.ai_testlib import AITestCase
2022

23+
logger = logging.getLogger(__name__)
24+
2125

2226
class TestAgenticApp(AITestCase):
2327
def test_agentic_app(self) -> None:
@@ -107,6 +111,34 @@ def test_agentic_app_with_remote_tools(self) -> None:
107111
app.delete()
108112
self.restart_splunk() # app removal requires a restart
109113

114+
def test_remote_tools_have_tags(self) -> None:
115+
pytest.importorskip("langchain_openai")
116+
self.requires_splunk_10_2()
117+
118+
# Skip test in case the instance does not have a /splunk-mcp-server.tgz file.
119+
try:
120+
resp = self.service.get("agentic_app/has_mcp_app_file")
121+
assert resp.status == 200
122+
except HTTPError as e:
123+
if e.status == 404:
124+
pytest.skip("Splunk MCP Server App file not found on Splunk instance")
125+
raise
126+
127+
app = self.service.apps.create(name="/splunk-mcp-server.tgz", filename=True) # pyright: ignore[reportUnknownVariableType]
128+
129+
resp = self.service.post(
130+
"agentic_app/tags",
131+
body=self.test_llm_settings.model_dump_json(),
132+
)
133+
134+
assert resp.status == 200
135+
body = str(resp.body) # pyright: ignore[reportUnknownArgumentType]
136+
logger.info("tool-tags response: %s", body)
137+
assert "tags" in body
138+
139+
app.delete()
140+
self.restart_splunk() # app removal requires a restart
141+
110142
def requires_splunk_10_2(self) -> None:
111143
if self.service.splunk_version[0] < 10 or self.service.splunk_version[1] < 2:
112144
pytest.skip("Python 3.13 not available on splunk < 10.2")

tests/system/test_apps/ai_agentic_test_app/bin/indexes.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,11 @@
1414

1515
import os
1616
import sys
17+
from typing import override
1718

1819
sys.path.insert(0, "/splunklib-deps")
1920
sys.path.insert(0, os.path.join(os.path.dirname(__file__), "lib"))
2021

21-
from typing import override
22-
2322
from pydantic import BaseModel, Field
2423

2524
from splunklib.ai.agent import Agent
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
# Copyright © 2011-2026 Splunk, Inc.
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License"): you may
4+
# not use this file except in compliance with the License. You may obtain
5+
# a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11+
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12+
# License for the specific language governing permissions and limitations
13+
# under the License.
14+
15+
import json
16+
import os
17+
import sys
18+
from typing import override
19+
20+
sys.path.insert(0, "/splunklib-deps")
21+
sys.path.insert(0, os.path.join(os.path.dirname(__file__), "lib"))
22+
23+
24+
from splunklib.ai.agent import Agent
25+
from splunklib.ai.tool_settings import RemoteToolSettings, ToolAllowlist, ToolSettings
26+
from tests.cre_testlib import CRETestHandler
27+
28+
# BUG: For some reason the CRE process is started with a overridden trust store path, that
29+
# does not exist on the filesystem. As a workaround in such case if it does not exist,
30+
# remove the env, this causes the default CAs to be used instead.
31+
CA_TRUST_STORE = "/opt/splunk/openssl/cert.pem"
32+
if os.environ.get("SSL_CERT_FILE") == CA_TRUST_STORE and not os.path.exists(
33+
CA_TRUST_STORE
34+
):
35+
os.environ["SSL_CERT_FILE"] = ""
36+
37+
# This app uses the splunk_get_indexes remote tool (from Splunk MCP Server App).
38+
# Requires that the MCP Server App is installed.
39+
40+
41+
class ToolTagsHandler(CRETestHandler):
42+
@override
43+
async def run(self) -> None:
44+
async with Agent(
45+
model=await self.model(),
46+
system_prompt="You are a helpful Splunk assistant",
47+
tool_settings=ToolSettings(
48+
local=False,
49+
remote=RemoteToolSettings(
50+
allowlist=ToolAllowlist(names=["splunk_get_indexes"])
51+
),
52+
),
53+
service=self.service,
54+
) as agent:
55+
assert len(agent.tools) > 0, "No remote tools loaded"
56+
57+
assert len([t for t in agent.tools if len(t.tags) > 0]) > 0, (
58+
f"No tools have tags. Tools: {[t.name for t in agent.tools]}"
59+
)
60+
61+
self.response.write(
62+
json.dumps(
63+
{"tools": [{"name": t.name, "tags": t.tags} for t in agent.tools]}
64+
)
65+
)
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
# Copyright © 2011-2026 Splunk, Inc.
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License"): you may
4+
# not use this file except in compliance with the License. You may obtain
5+
# a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11+
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12+
# License for the specific language governing permissions and limitations
13+
# under the License.
14+
15+
import json
16+
import os
17+
import sys
18+
from uuid import uuid4
19+
20+
sys.path.insert(0, "/splunklib-deps")
21+
sys.path.insert(0, os.path.join(os.path.dirname(__file__), "lib"))
22+
23+
from typing import override
24+
25+
from splunklib.ai.agent import (
26+
_get_splunk_username, # pyright: ignore[reportPrivateUsage]
27+
)
28+
from splunklib.ai.tools import (
29+
_list_all_tools, # pyright: ignore[reportPrivateUsage]
30+
connect_remote_mcp,
31+
)
32+
from tests.cre_testlib import CRETestHandler
33+
34+
# BUG: For some reason the CRE process is started with a overridden trust store path, that
35+
# does not exist on the filesystem. As a workaround in such case if it does not exist,
36+
# remove the env, this causes the default CAs to be used instead.
37+
CA_TRUST_STORE = "/opt/splunk/openssl/cert.pem"
38+
if os.environ.get("SSL_CERT_FILE") == CA_TRUST_STORE and not os.path.exists(
39+
CA_TRUST_STORE
40+
):
41+
os.environ["SSL_CERT_FILE"] = ""
42+
43+
# This handler connects directly to the Splunk MCP Server App and logs the raw
44+
# MCP tool list response, verifying that tool metadata includes tags.
45+
46+
47+
class ToolTagsHandler(CRETestHandler):
48+
@override
49+
async def run(self) -> None:
50+
import asyncio
51+
52+
splunk_username = await asyncio.to_thread(
53+
lambda: _get_splunk_username(self.service)
54+
)
55+
56+
async with connect_remote_mcp(
57+
service=self.service,
58+
app_id="ai_agentic_test_app",
59+
trace_id=str(uuid4()),
60+
splunk_username=splunk_username,
61+
) as session:
62+
assert session is not None, "MCP Server App not available"
63+
64+
raw_tools = await _list_all_tools(session)
65+
66+
assert len(raw_tools) > 0, "No tools returned by MCP Server App"
67+
68+
tools_with_tags = [
69+
t for t in raw_tools if ((t.meta or {}).get("splunk") or {}).get("tags")
70+
]
71+
assert len(tools_with_tags) > 0, (
72+
f"No tools have tags. Raw tools: {json.dumps([t.model_dump() for t in raw_tools], indent=2)}"
73+
)
74+
75+
self.response.write(json.dumps([t.model_dump() for t in raw_tools], indent=2))

tests/system/test_apps/ai_agentic_test_app/default/restmap.conf

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,3 +15,9 @@ match = /agentic_app/has_mcp_app_file
1515
scripttype = python
1616
handler = mcp_app_file_exists.Handler
1717
python.required = 3.13
18+
19+
[script:tool_tags]
20+
match = /agentic_app/tool-tags
21+
scripttype = python
22+
handler = tool_tags.ToolTagsHandler
23+
python.required = 3.13

0 commit comments

Comments
 (0)