Skip to content

Commit bcd351f

Browse files
committed
Refactor Llama Stack utilities and update E2E tests
- Renamed `llama_stack_shields.py` to `llama_stack_utils.py` and expanded its functionality to manage both toolgroups and shields. - Removed the deprecated `llama_stack_tools.py` file. - Updated E2E test scenarios to utilize the new utility functions for unregistering toolgroups and clearing Llama Stack storage. - Enhanced feature files to include comments indicating pending fixes for skipped scenarios.
1 parent 62f8a53 commit bcd351f

6 files changed

Lines changed: 110 additions & 87 deletions

File tree

docs/e2e_testing.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ tests/e2e/
5858
├── utils/
5959
│ ├── utils.py # restart_container, switch_config, wait_for_container_health, etc.
6060
│ ├── prow_utils.py # Prow/OpenShift helpers (restore_llama_stack_pod, etc.)
61-
│ └── llama_stack_shields.py # Shield unregister/register (server mode, optional)
61+
│ └── llama_stack_utils.py # Toolgroups + shield unregister/register (server mode, optional)
6262
├── mock_mcp_server/ # Mock MCP server for MCP tests
6363
└── rag/ # RAG test data (e.g. for FAISS)
6464
```

tests/e2e/features/environment.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,13 @@
1616
from tests.e2e.utils.prow_utils import restore_llama_stack_pod
1717
from behave.runner import Context
1818

19-
from tests.e2e.utils.llama_stack_tools import unregister_mcp_toolgroups
20-
from tests.e2e.utils.llama_stack_shields import (
19+
from tests.e2e.utils.llama_stack_utils import (
2120
register_shield,
21+
unregister_mcp_toolgroups,
2222
unregister_shield,
2323
)
2424
from tests.e2e.utils.utils import (
25+
clear_llama_stack_storage,
2526
create_config_backup,
2627
is_prow_environment,
2728
remove_config_backup,
@@ -239,6 +240,8 @@ def before_scenario(context: Context, scenario: Scenario) -> None:
239240
if config_name is not None:
240241
if not context.is_library_mode:
241242
unregister_mcp_toolgroups()
243+
else:
244+
clear_llama_stack_storage()
242245
context.scenario_config = _get_config_path(config_name, mode_dir)
243246
switch_config(context.scenario_config)
244247
restart_container("lightspeed-stack")

tests/e2e/features/mcp.feature

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ Feature: MCP tests
77

88

99
# File-based
10-
@skip
10+
@skip # will be fixed by LCORE-1461
1111
@MCPFileAuthConfig
1212
Scenario: Check if tools endpoint succeeds when MCP file-based auth token is passed
1313
Given The system is in default state
@@ -46,7 +46,7 @@ Feature: MCP tests
4646
| Hello |
4747
And The token metrics should have increased
4848

49-
@skip
49+
@skip # will be fixed by LCORE-1461
5050
@InvalidMCPFileAuthConfig
5151
Scenario: Check if tools endpoint reports error when MCP file-based invalid auth token is passed
5252
Given The system is in default state
@@ -62,7 +62,7 @@ Feature: MCP tests
6262
}
6363
"""
6464

65-
@skip
65+
@skip # will be fixed by LCORE-1463
6666
@InvalidMCPFileAuthConfig
6767
Scenario: Check if query endpoint reports error when MCP file-based invalid auth token is passed
6868
Given The system is in default state
@@ -81,7 +81,7 @@ Feature: MCP tests
8181
}
8282
"""
8383

84-
@skip
84+
@skip # will be fixed by LCORE-1463
8585
@InvalidMCPFileAuthConfig
8686
Scenario: Check if streaming_query endpoint reports error when MCP file-based invalid auth token is passed
8787
Given The system is in default state
@@ -101,7 +101,7 @@ Feature: MCP tests
101101
"""
102102

103103
# Kubernetes
104-
@skip
104+
@skip # will be fixed by LCORE-1461
105105
@MCPKubernetesAuthConfig
106106
Scenario: Check if tools endpoint succeeds when MCP kubernetes auth token is passed
107107
Given The system is in default state
@@ -143,7 +143,7 @@ Feature: MCP tests
143143
| Hello |
144144
And The token metrics should have increased
145145

146-
@skip
146+
@skip # will be fixed by LCORE-1461
147147
@MCPKubernetesAuthConfig
148148
Scenario: Check if tools endpoint reports error when MCP kubernetes invalid auth token is passed
149149
Given The system is in default state
@@ -160,7 +160,7 @@ Feature: MCP tests
160160
}
161161
"""
162162

163-
@skip
163+
@skip # will be fixed by LCORE-1463
164164
@MCPKubernetesAuthConfig
165165
Scenario: Check if query endpoint reports error when MCP kubernetes invalid auth token is passed
166166
Given The system is in default state
@@ -180,7 +180,7 @@ Feature: MCP tests
180180
}
181181
"""
182182

183-
@skip
183+
@skip # will be fixed by LCORE-1463
184184
@MCPKubernetesAuthConfig
185185
Scenario: Check if streaming_query endpoint reports error when MCP kubernetes invalid auth token is passed
186186
Given The system is in default state
@@ -201,7 +201,7 @@ Feature: MCP tests
201201
"""
202202

203203
# Client-provided
204-
@skip
204+
@skip # will be fixed by LCORE-1462
205205
@MCPClientAuthConfig
206206
Scenario: Check if tools endpoint succeeds by skipping when MCP client-provided auth token is omitted
207207
Given The system is in default state

tests/e2e/utils/llama_stack_tools.py

Lines changed: 0 additions & 70 deletions
This file was deleted.
Lines changed: 56 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
1-
"""E2E helpers to unregister and re-register Llama Stack shields via the client API.
1+
"""E2E test utilities for Llama Stack (toolgroups and shields).
22
3-
Used by the @disable-shields tag: before the scenario we call client.shields.delete()
4-
to unregister the shield; after the scenario we call client.shields.register()
5-
to restore it. Only applies in server mode (Llama Stack as a separate service).
6-
Requires E2E_LLAMA_STACK_URL or E2E_LLAMA_HOSTNAME/E2E_LLAMA_PORT.
3+
This module provides functions to manage MCP toolgroups and shields on a running
4+
Llama Stack instance during end-to-end tests: unregister MCP toolgroups when
5+
switching configurations or testing MCP auth, and unregister/re-register shields
6+
(e.g. for the @disable-shields tag).
7+
8+
Only applies when running Llama Stack as a separate service (server mode).
9+
Requires E2E_LLAMA_STACK_URL or E2E_LLAMA_HOSTNAME and E2E_LLAMA_PORT.
710
"""
811

912
import asyncio
@@ -29,6 +32,54 @@ def _get_llama_stack_client() -> AsyncLlamaStackClient:
2932
return AsyncLlamaStackClient(base_url=base_url, api_key=api_key, timeout=timeout)
3033

3134

35+
# -----------------------------------------------------------------------------
36+
# Toolgroups
37+
# -----------------------------------------------------------------------------
38+
39+
40+
async def _unregister_toolgroup_async(identifier: str) -> None:
41+
"""Unregister a toolgroup by identifier; return (provider_id, provider_shield_id) for restore."""
42+
client = _get_llama_stack_client()
43+
try:
44+
await client.toolgroups.unregister(identifier)
45+
except APIConnectionError:
46+
raise
47+
except APIStatusError as e:
48+
# 400 "not found": toolgroup already absent, scenario can proceed
49+
if e.status_code == 400 and "not found" in str(e).lower():
50+
return None
51+
raise
52+
finally:
53+
await client.close()
54+
55+
56+
async def _unregister_mcp_toolgroups_async() -> None:
57+
"""Unregister all MCP toolgroups."""
58+
client = _get_llama_stack_client()
59+
try:
60+
toolgroups = await client.toolgroups.list()
61+
for toolgroup in toolgroups:
62+
if (
63+
toolgroup.identifier
64+
and toolgroup.provider_id == "model-context-protocol"
65+
):
66+
await _unregister_toolgroup_async(toolgroup.identifier)
67+
except APIConnectionError:
68+
raise
69+
finally:
70+
await client.close()
71+
72+
73+
def unregister_mcp_toolgroups() -> None:
74+
"""Unregister all MCP toolgroups."""
75+
asyncio.run(_unregister_mcp_toolgroups_async())
76+
77+
78+
# -----------------------------------------------------------------------------
79+
# Shields
80+
# -----------------------------------------------------------------------------
81+
82+
3283
async def _unregister_shield_async(identifier: str) -> Optional[tuple[str, str]]:
3384
"""Unregister a shield by identifier; return (provider_id, provider_shield_id) for restore."""
3485
client = _get_llama_stack_client()

tests/e2e/utils/utils.py

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -246,6 +246,45 @@ def remove_config_backup(backup_path: str) -> None:
246246
print(f"Warning: Could not remove backup file {backup_path}: {e}")
247247

248248

249+
# Llama Stack storage paths inside the lightspeed-stack container/pod (library mode).
250+
# Used when clearing embedded Llama Stack storage before MCP config scenarios.
251+
_LLAMA_STORAGE_ROOT = "/opt/app-root/src/.llama/storage"
252+
253+
254+
def clear_llama_stack_storage(container_name: str = "lightspeed-stack") -> None:
255+
"""Clear Llama Stack storage in library mode (embedded Llama Stack).
256+
257+
Removes SQLite/KV store databases and file storage contents so that
258+
toolgroups and other persisted state are reset. Used before MCP config
259+
scenarios when not running in server mode (no separate Llama Stack to
260+
unregister toolgroups from). Only runs when using Docker (skipped in Prow).
261+
262+
Parameters:
263+
container_name (str): Docker container name (default "lightspeed-stack").
264+
265+
Returns:
266+
None
267+
"""
268+
269+
storage_root = _LLAMA_STORAGE_ROOT
270+
commands = [
271+
f"rm -f {storage_root}/rag/kv_store.db",
272+
f"rm -f {storage_root}/sql_store.db",
273+
f"rm -rf {storage_root}/files/*",
274+
]
275+
try:
276+
for cmd in commands:
277+
subprocess.run(
278+
["docker", "exec", container_name, "sh", "-c", cmd],
279+
capture_output=True,
280+
text=True,
281+
timeout=10,
282+
check=False,
283+
)
284+
except subprocess.TimeoutExpired as e:
285+
print(f"Warning: Could not clear Llama Stack storage: {e}")
286+
287+
249288
def restart_container(container_name: str) -> None:
250289
"""Restart a Docker container by name and wait until it is healthy.
251290

0 commit comments

Comments
 (0)