Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions tests/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
*.egg-info/
29 changes: 17 additions & 12 deletions tests/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,23 +2,28 @@

## Introduction

This directory contains end-to-end tests for the OCI MCP Servers using the Behave framework.
This directory contains end-to-end tests for the OCI MCP Servers using the [Behave framework](https://behave.readthedocs.io/en/latest/).

## Prerequisites
## Pre-requisites

1. Ensure you have the necessary OCI configuration set up on your system.
1. Ensure that you have `uv` installed from the [Quick Start](https://github.com/oracle/mcp#quick-start) section of the main README.
2. Ensure that you have your OCI profile set up from the [Authentication](https://github.com/oracle/mcp#authentication) section of the main README.
3. Ensure that you have your local development environment set up from the [Local development](https://github.com/oracle/mcp#local-development) section of the main README.
4. Ensure that you have downloaded `ollama`, started the `ollama` server, and fetched a large language model from the [Client configuration - MCPHost](https://github.com/oracle/mcp#mcphost) section of the main README.
1. When following the instructions in the step above, there is no need to start `mcphost` for these E2E tests. You only need to install and start `ollama` for running these tests.

## Configuration

1. In the e2e directory, copy `.env.template` to `.env`.
1. In the `tests/e2e/features` directory, copy `.env.template` to `.env`.
2. Fill in the required environment variables in `.env`:
- TENANCY_OCID: Your Oracle Cloud tenancy OCID.
- TENANCY_NAME: Your Oracle Cloud tenancy name.
- COMPARTMENT_OCID: The OCID of the compartment to test against (defaults to TENANCY_OCID if not set).
- USER_OCID: Your Oracle Cloud user OCID.
- USER_NAME: Your Oracle Cloud user name.
- REGION: Your home region name (defaults to us-ashburn-1 if not set)
- MODEL: LLM model that you are running (defaults to got-oss:20b if not set)
- MODEL: LLM model that you are running (defaults to gpt-oss:20b if not set). This should be the model that you fetched in the pre-requisites section.
- OCI_CONFIG_PROFILE: The name of your OCI profile set up in the pre-requisites section (defaults to DEFAULT if not set).

You can copy the following into a `.env` file
```bash
Expand All @@ -29,22 +34,22 @@ This directory contains end-to-end tests for the OCI MCP Servers using the Behav
USER_NAME=
REGION=
MODEL=
OCI_CONFIG_PROFILE=
```

## Running the Tests

1. Ensure the `ollama` is installed and started.
2. Following the instructions in the [Getting Started - MCPHost](https://github.com/shrug-labs/oci-mcp#mcphost) section.
2. Run the tests using the command:
```bash
cd tests/e2e
behave
1. If you have not already activated your virtual environment from the [Local development](https://github.com/oracle/mcp#local-development) section, please do so.
2. In the `tests` directory, install the test dependencies using this command: `uv pip install .`
3. In the `tests/e2e` directory, run all of the tests by using this command: `behave`
1. Run specific features by using this command: `behave features/<name of your feature file>`. Example: `behave features/oci-compute-mcp-server.feature`
2. Run specific scenarios by using this command: `behave -n "Name of your scenario"`. Example: `behave -n "OCI Compute MCP Server"`

## Notes

- The tests use the configuration from the `.env` file.
- The `mcphost.json` file is used to configure the MCP server.


----
<small>Copyright (c) 2025, Oracle and/or its affiliates. Licensed under the [Universal Permissive License v1.0](https://oss.oracle.com/licenses/upl).</small>

1 change: 1 addition & 0 deletions tests/e2e/features/.env.template
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,4 @@ TENANCY_NAME=
COMPARTMENT_OCID=
USER_OCID=
USER_NAME=
OCI_PROFILE_NAME=
12 changes: 10 additions & 2 deletions tests/e2e/features/environment.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ def set_default(obj, attribute, value):
set_default(config, "COMPARTMENT_OCID", config["TENANCY_OCID"])
set_default(config, "USER_NAME", "")
set_default(config, "REGION", "us-ashburn-1")
set_default(config, "OCI_CONFIG_PROFILE", "DEFAULT")

except FileNotFoundError:
raise EnvironmentError(
Expand All @@ -42,7 +43,7 @@ def set_default(obj, attribute, value):
)
)

_system_prompt = f"""You are an Oracle Cloud Infrastructure expert generative chat assistant. Limit your answers to OCI. OCID is synonymous with ID. The logged in user's OCID is '{config["USER_OCID"]}', and the display name is {config["USER_NAME"]}. The tenancy OCID is '{config["TENANCY_OCID"]}', which is also called the root compartment. The active (current) tenancy name is {config["TENANCY_NAME"]}. The active (current) region is {config["REGION"]}. The active (current) compartment is '{config["COMPARTMENT_OCID"]}'. If the user makes a request that relies on a tool that requires a compartment id, and the user doesn't specify one, don't ask the user for the compartment id and use the root compartment instead. If I ask you for a list of things, prefer either a tabular or text-based approach over dumping them in a code block. When formatting your response, don't use bullets or lists within tables. When a user makes a request, you must first attempt to fulfill it by using the available MCP tools. These tools are connected to our live data sources and provide the most accurate and real-time information. Only after exhausting the capabilities of the MCP tools should you resort to other methods, such as using a general web search, if the MCP tools cannot provide the necessary information. If there is an error in calling the run_oci_command tool, then try to use the get_oci_command_help tool to get more information on the command and retry with the updated information. Don't send back emojis in the responses.""" # noqa ES501
_system_prompt = f"""You are an Oracle Cloud Infrastructure expert generative chat assistant. Limit your answers to OCI. OCID is synonymous with ID. The logged in user's OCID is '{config["USER_OCID"]}', and the display name is {config["USER_NAME"]}. The tenancy OCID is '{config["TENANCY_OCID"]}', which is also called the root compartment. The active (current) tenancy name is {config["TENANCY_NAME"]}. The active (current) region is {config["REGION"]}. The active (current) compartment is '{config["COMPARTMENT_OCID"]}'. If the user makes a request that relies on a tool that requires a compartment id, and the user doesn't specify one, don't ask the user for the compartment id and use the active (current) compartment instead. If I ask you for a list of things, prefer either a tabular or text-based approach over dumping them in a code block. When formatting your response, don't use bullets or lists within tables. When a user makes a request, you must first attempt to fulfill it by using the available MCP tools. These tools are connected to our live data sources and provide the most accurate and real-time information. Only after exhausting the capabilities of the MCP tools should you resort to other methods, such as using a general web search, if the MCP tools cannot provide the necessary information. If there is an error in calling the run_oci_command tool, then try to use the get_oci_command_help tool to get more information on the command and retry with the updated information. Don't send back emojis in the responses.""" # noqa ES501


def set_mcp_servers(context):
Expand All @@ -66,9 +67,16 @@ def before_all(context):
# set the current configured mcp servers
set_mcp_servers(context)

test_env = os.environ.copy()
test_env["OCI_CONFIG_PROFILE"] = config["OCI_CONFIG_PROFILE"]
test_env["GGML_CUDA_ENABLE_UNIFIED_MEMORY"] = (
"1" # Solution from https://github.com/ollama/ollama/issues/9711
)

# load the MCP servers
context.bridge_proc = subprocess.Popen(
["uvx", "ollama-mcp-bridge", "--config", config["MCP_HOST_FILE"]]
["uvx", "ollama-mcp-bridge", "--config", config["MCP_HOST_FILE"]],
env=test_env,
)
print("Waiting for servers to start...", flush=True)
time.sleep(
Expand Down
6 changes: 0 additions & 6 deletions tests/e2e/features/general-prompts.feature
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,3 @@ Feature: OCI MCP Servers
When I send a request with the prompt "What tools do you have? List all by name"
Then the response should contain a list of tools available

Scenario: List the instances in the root compartment
Given the MCP server is running with OCI tools
And the ollama model with the tools is properly working
When I send a request with the prompt "list my instances in the root compartment and limit to 5"
Then the response should contain a list of instances

10 changes: 0 additions & 10 deletions tests/e2e/features/mcphost.json
Original file line number Diff line number Diff line change
@@ -1,15 +1,5 @@
{
"mcpServers": {
"oracle-datetime-helper-mcp-server": {
"disabled": false,
"timeout": 60,
"type": "stdio",
"command": "uv",
"args": [
"run",
"oracle.datetime-helper-mcp-server"
]
},
"oracle-oci-api-mcp-server": {
"disabled": false,
"timeout": 60,
Expand Down
42 changes: 39 additions & 3 deletions tests/e2e/features/oci-compute-mcp-server.feature
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,52 @@


Feature: OCI Compute MCP Server
Scenario: List the tools available in the agent
Scenario: List the compute tools available in the agent
Given the MCP server is running with OCI tools
And the ollama model with the tools is properly working
When I send a request with the prompt "What tools do you have"
When I send a request with the prompt "What compute tools do you have"
Then the response should contain a list of compute tools available

Scenario: List the running compute instances
Given the MCP server is running with OCI tools
And the ollama model with the tools is properly working
When I send a request with the prompt "list my running instances and limit to 5"
Then the response should contain a list of running instances

Scenario: Get compute instance details
Given the MCP server is running with OCI tools
And the ollama model with the tools is properly working
When I send a request with the prompt "list my running instances and limit to 5, then get the details of the first instance in the list"
Then the response should contain the details of an instance

Scenario: List the images
Given the MCP server is running with OCI tools
And the ollama model with the tools is properly working
When I send a request with the prompt "list the possible compute images and limit to 5"
Then the response should contain a list of images

Scenario: Get image details
Given the MCP server is running with OCI tools
And the ollama model with the tools is properly working
When I send a request with the prompt "list the possible compute images and limit to 5, then get the details of the first image in the list"
Then the response should contain the details of an image

Scenario: List the vnic attachments
Given the MCP server is running with OCI tools
And the ollama model with the tools is properly working
When I send a request with the prompt "list my running instances and list the vnics attachments on the first instance in the list"
Then the response should contain a list of vnic attachments

Scenario: Get vnic attachment details
Given the MCP server is running with OCI tools
And the ollama model with the tools is properly working
When I send a request with the prompt "list my running instances and list the vnics attachments on the first instance in the list, then fetch the details of the first vnic attachment in that list"
Then the response should contain the details of a vnic attachment

Scenario: Security review of compute instances in specific region
Given the MCP server is running with OCI tools
And the ollama model with the tools is properly working
When I send a request with the prompt "Can you review the security configuration of my compute instances in San Jose and let me know if there are any recommended improvements or best practices to strengthen their security posture?"
When I send a request with the prompt "Can you review the security configuration of my compute instances in Ashburn and let me know if there are any recommended improvements or best practices to strengthen their security posture?"
Then the response should contain security analysis
And the response should mention security best practices
And the response should reference regional considerations
15 changes: 3 additions & 12 deletions tests/e2e/features/steps/general-prompts.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,9 @@ def step_impl_prompt(context, prompt):

# check if all thinking is really done...
result = context.response.json()
thinking = result["message"]["thinking"]
content = result["message"]["content"]
message = getattr(result, "message", None)
thinking = getattr(message, "thinking", None)
content = getattr(message, "content", None)
print("thinking & content", thinking, content, flush=True)
if content is None and thinking is not None:
print("Getting the next response...")
Expand All @@ -60,13 +61,3 @@ def step_impl_tools_available(context):
assert (
tool_server in result["message"]["content"]
), f"{tool_server} is missing from tools."


@then("the response should contain a list of instances")
def step_impl_list_instances(context):
result = context.response.json()
print("Instances", result)
assert "content" in result["message"], "Response does not contain a content key."
assert (
"ocid1.instance" in result["message"]["content"]
), "tools could not be queried."
53 changes: 52 additions & 1 deletion tests/e2e/features/steps/oci-compute-mcp-server-steps.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,57 @@ def step_impl_compute_tools_available(context):
), "Compute tools could not be queried."


@then("the response should contain a list of running instances")
def step_impl_list_instances(context):
result = context.response.json()
assert "content" in result["message"], "Response does not contain a content key."
assert (
"ocid1.instance" in result["message"]["content"]
), "List of instances not found."
assert "RUNNING" in result["message"]["content"], "Instances are not RUNNING"


@then("the response should contain the details of an instance")
def step_impl_get_instance(context):
result = context.response.json()
assert "content" in result["message"], "Response does not contain a content key."
assert (
"ocid1.instance" in result["message"]["content"]
), "Instance details not found."


@then("the response should contain a list of images")
def step_impl_list_images(context):
result = context.response.json()
assert "content" in result["message"], "Response does not contain a content key."
assert "ocid1.image" in result["message"]["content"], "List of instances not found."


@then("the response should contain the details of an image")
def step_impl_get_image(context):
result = context.response.json()
assert "content" in result["message"], "Response does not contain a content key."
assert "ocid1.image" in result["message"]["content"], "Image details not found."


@then("the response should contain a list of vnic attachments")
def step_impl_list_vnic_attachments(context):
result = context.response.json()
assert "content" in result["message"], "Response does not contain a content key."
assert (
"ocid1.vnicattachment" in result["message"]["content"]
), "List of vnic attachments not found."


@then("the response should contain the details of a vnic attachment")
def step_impl_get_vnic_attachment(context):
result = context.response.json()
assert "content" in result["message"], "Response does not contain a content key."
assert (
"ocid1.vnicattachment" in result["message"]["content"]
), "Vnic attachment details not found."


@then("the response should contain security analysis")
def step_impl_security_analysis(context):
response_json = context.response.json()
Expand Down Expand Up @@ -64,5 +115,5 @@ def step_impl_regional_considerations(context):
content = response_json["message"]["content"].lower()
assert any(
keyword in content
for keyword in ["san jose", "us-phoenix-1", "region", "regional", "location"]
for keyword in ["ashburn", "us-ashburn-1", "region", "regional", "location"]
), "Regional considerations were not mentioned in the response."
4 changes: 2 additions & 2 deletions tests/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ license = "UPL-1.0"
license-files = ["LICENSE.txt"]
dependencies = [
"fastmcp==2.13.0",
"oci==2.160.0"
"oci==2.160.0",
"behave>=1.2.6"
]

[dependency-groups]
Expand All @@ -17,5 +18,4 @@ dev = [
"pytest-asyncio>=1.2.0",
"pytest-bdd>=6.1.1",
"pytest-cov>=7.0.0",
"behave>=1.2.6"
]
Loading