Skip to content

Commit 0c34040

Browse files
Merge branch 'hotfix' into UT-10888-backend
2 parents 0710856 + f8640d9 commit 0c34040

File tree

3 files changed

+162
-13
lines changed

3 files changed

+162
-13
lines changed

src/backend/tests/agents/test_product.py

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
import sys
33
from unittest.mock import MagicMock
44

5-
# --- Fake missing Azure modules ---
65
sys.modules["azure.monitor.events"] = MagicMock()
76
sys.modules["azure.monitor.events.extension"] = MagicMock()
87

@@ -15,6 +14,12 @@
1514
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), "../../../")))
1615

1716
# Set required environment variables before importing modules that depend on them.
17+
import pytest
18+
19+
# Mock Azure SDK dependencies
20+
sys.modules["azure.monitor.events.extension"] = MagicMock()
21+
22+
# Set up environment variables
1823
os.environ["COSMOSDB_ENDPOINT"] = "https://mock-endpoint"
1924
os.environ["COSMOSDB_KEY"] = "mock-key"
2025
os.environ["COSMOSDB_DATABASE"] = "mock-database"
@@ -23,13 +28,12 @@
2328
os.environ["AZURE_OPENAI_API_VERSION"] = "2023-01-01"
2429
os.environ["AZURE_OPENAI_ENDPOINT"] = "https://mock-openai-endpoint"
2530

26-
# Import product functions and classes.
31+
32+
# Import the required functions for testing
2733
from src.backend.agents.product import (
2834
add_mobile_extras_pack,
2935
get_product_info,
30-
get_billing_date,
3136
update_inventory,
32-
add_new_product,
3337
schedule_product_launch,
3438
analyze_sales_data,
3539
get_customer_feedback,
@@ -87,13 +91,16 @@
8791
from src.backend.context.cosmos_memory import CosmosBufferedChatCompletionContext
8892
from src.backend.agents.base_agent import BaseAgent
8993

90-
# --- Tests for Product Functions ---
94+
)
95+
9196

97+
# Parameterized tests for repetitive cases
9298
@pytest.mark.asyncio
9399
@pytest.mark.parametrize(
94100
"function, args, expected_substrings",
95101
[
96102
(add_mobile_extras_pack, ("Roaming Pack", "2025-01-01"), ["Roaming Pack", "2025-01-01", "AGENT SUMMARY:"]),
103+
(add_mobile_extras_pack, ("Roaming Pack", "2025-01-01"), ["Roaming Pack", "2025-01-01"]),
97104
(get_product_info, (), ["Simulated Phone Plans", "Plan A"]),
98105
(update_inventory, ("Product A", 50), ["Inventory for", "Product A"]),
99106
(schedule_product_launch, ("New Product", "2025-02-01"), ["New Product", "2025-02-01"]),
@@ -106,6 +113,12 @@
106113
(handle_product_recall, ("Product A", "Defective batch"), ["Product recall for", "Defective batch"]),
107114
(set_product_discount, ("Product A", 15.0), ["Discount for", "15.0%"]),
108115
(manage_supply_chain, ("Product A", "Supplier X"), ["Supply chain for", "Supplier X"]),
116+
(handle_product_recall, ("Product A", "Defective batch"), ["Product recall for", "Defective batch"]),
117+
(set_product_discount, ("Product A", 15.0), ["Discount for", "15.0%"]),
118+
(manage_supply_chain, ("Product A", "Supplier X"), ["Supply chain for", "Supplier X"]),
119+
(check_inventory, ("Product A",), ["Inventory status for", "Product A"]),
120+
(update_product_price, ("Product A", 99.99), ["Price for", "$99.99"]),
121+
(provide_product_recommendations, ("High Performance",), ["Product recommendations", "High Performance"]),
109122
(forecast_product_demand, ("Product A", "Next Month"), ["Demand for", "Next Month"]),
110123
(handle_product_complaints, ("Product A", "Complaint about quality"), ["Complaint for", "Product A"]),
111124
(generate_product_report, ("Product A", "Sales"), ["Sales report for", "Product A"]),
@@ -172,3 +185,9 @@ def test_get_product_tools():
172185
assert any(isinstance(tool, FunctionTool) for tool in tools)
173186
names = [tool.name for tool in tools]
174187
assert "add_mobile_extras_pack" in names or "get_product_info" in names
188+
189+
# Specific test for monitoring market trends
190+
@pytest.mark.asyncio
191+
async def test_monitor_market_trends():
192+
result = await monitor_market_trends()
193+
assert "Market trends monitored" in result

src/backend/tests/context/test_cosmos_memory.py

Lines changed: 69 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,6 @@
2929
LLMMessage,
3030
)
3131

32-
# --- DummyModel for Testing ---
3332
class DummyModel(BaseDataModel):
3433
id: str
3534
session_id: str
@@ -413,3 +412,72 @@ async def test_close(cosmos_context):
413412
async def test_context_manager(cosmos_context):
414413
async with cosmos_context as ctx:
415414
assert ctx == cosmos_context
415+
=======
416+
import pytest
417+
from unittest.mock import AsyncMock, patch
418+
from azure.cosmos.partition_key import PartitionKey
419+
from src.backend.context.cosmos_memory import CosmosBufferedChatCompletionContext
420+
421+
422+
# Helper to create async iterable
423+
async def async_iterable(mock_items):
424+
"""Helper to create an async iterable."""
425+
for item in mock_items:
426+
yield item
427+
428+
429+
@pytest.fixture
430+
def mock_env_variables(monkeypatch):
431+
"""Mock all required environment variables."""
432+
env_vars = {
433+
"COSMOSDB_ENDPOINT": "https://mock-endpoint",
434+
"COSMOSDB_KEY": "mock-key",
435+
"COSMOSDB_DATABASE": "mock-database",
436+
"COSMOSDB_CONTAINER": "mock-container",
437+
"AZURE_OPENAI_DEPLOYMENT_NAME": "mock-deployment-name",
438+
"AZURE_OPENAI_API_VERSION": "2023-01-01",
439+
"AZURE_OPENAI_ENDPOINT": "https://mock-openai-endpoint",
440+
}
441+
for key, value in env_vars.items():
442+
monkeypatch.setenv(key, value)
443+
444+
445+
@pytest.fixture
446+
def mock_cosmos_client():
447+
"""Fixture for mocking Cosmos DB client and container."""
448+
mock_client = AsyncMock()
449+
mock_container = AsyncMock()
450+
mock_client.create_container_if_not_exists.return_value = mock_container
451+
452+
# Mocking context methods
453+
mock_context = AsyncMock()
454+
mock_context.store_message = AsyncMock()
455+
mock_context.retrieve_messages = AsyncMock(
456+
return_value=async_iterable([{"id": "test_id", "content": "test_content"}])
457+
)
458+
459+
return mock_client, mock_container, mock_context
460+
461+
462+
@pytest.fixture
463+
def mock_config(mock_cosmos_client):
464+
"""Fixture to patch Config with mock Cosmos DB client."""
465+
mock_client, _, _ = mock_cosmos_client
466+
with patch(
467+
"src.backend.config.Config.GetCosmosDatabaseClient", return_value=mock_client
468+
), patch("src.backend.config.Config.COSMOSDB_CONTAINER", "mock-container"):
469+
yield
470+
471+
472+
@pytest.mark.asyncio
473+
async def test_initialize(mock_config, mock_cosmos_client):
474+
"""Test if the Cosmos DB container is initialized correctly."""
475+
mock_client, mock_container, _ = mock_cosmos_client
476+
context = CosmosBufferedChatCompletionContext(
477+
session_id="test_session", user_id="test_user"
478+
)
479+
await context.initialize()
480+
mock_client.create_container_if_not_exists.assert_called_once_with(
481+
id="mock-container", partition_key=PartitionKey(path="/session_id")
482+
)
483+
assert context._container == mock_container

src/backend/tests/test_app.py

Lines changed: 69 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,18 @@
77

88
# --- MOCK EXTERNAL DEPENDENCIES ---
99
# Prevent import errors for Azure modules.
10+
from unittest.mock import MagicMock, patch
11+
import pytest
12+
from fastapi.testclient import TestClient
13+
14+
# Mock Azure dependencies to prevent import errors
1015
sys.modules["azure.monitor"] = MagicMock()
1116
sys.modules["azure.monitor.events.extension"] = MagicMock()
1217
sys.modules["azure.monitor.opentelemetry"] = MagicMock()
1318

19+
1420
# Set required environment variables
21+
# Mock environment variables before importing app
1522
os.environ["COSMOSDB_ENDPOINT"] = "https://mock-endpoint"
1623
os.environ["COSMOSDB_KEY"] = "mock-key"
1724
os.environ["COSMOSDB_DATABASE"] = "mock-database"
@@ -29,7 +36,6 @@
2936

3037
client = TestClient(app)
3138

32-
# --- FAKE CLASSES AND FUNCTIONS ---
3339
class FakePlan:
3440
id = "fake_plan_id"
3541
summary = "Fake plan summary"
@@ -114,14 +120,28 @@ async def get_all_messages(self):
114120
"ts": 123456789,
115121
}]
116122

117-
# --- PYTEST FIXTURE TO OVERRIDE DEPENDENCIES ---
123+
118124
@pytest.fixture(autouse=True)
119125
def override_dependencies(monkeypatch):
120126
# Override authentication so that the headers always yield a valid user.
127+
128+
# Mock telemetry initialization to prevent errors
129+
with patch("azure.monitor.opentelemetry.configure_azure_monitor", MagicMock()):
130+
from src.backend.app import app
131+
132+
# Initialize FastAPI test client
133+
client = TestClient(app)
134+
135+
136+
@pytest.fixture(autouse=True)
137+
def mock_dependencies(monkeypatch):
138+
"""Mock dependencies to simplify tests."""
139+
121140
monkeypatch.setattr(
122141
"src.backend.auth.auth_utils.get_authenticated_user_details",
123142
lambda headers: {"user_principal_id": "mock-user-id"},
124143
)
144+
125145
# Override the agent tools retrieval to return a tool with the expected values.
126146
monkeypatch.setattr(
127147
"src.backend.utils.retrieve_all_agent_tools",
@@ -135,25 +155,37 @@ def override_dependencies(monkeypatch):
135155
monkeypatch.setattr("src.backend.app.initialize_runtime_and_context", fake_initialize_runtime_and_context)
136156
monkeypatch.setattr("src.backend.app.CosmosBufferedChatCompletionContext", FakeCosmos)
137157
monkeypatch.setattr("src.backend.app.track_event_if_configured", lambda event, props: None)
138-
139-
# --- TEST CASES ---
140-
# Note: We remove extra fields (like "user_id") from payloads so that they match the expected schema.
158+
141159

142160
def test_input_task_invalid_json():
143161
invalid_json = "Invalid JSON data"
144162
headers = {"Authorization": "Bearer mock-token"}
145163
response = client.post("/input_task", data=invalid_json, headers=headers)
146164
assert response.status_code == 422
147165
assert "detail" in response.json()
166+
148167

149168
def test_input_task_missing_description():
150169
payload = {"session_id": ""}
151170
headers = {"Authorization": "Bearer mock-token"}
152171
response = client.post("/input_task", json=payload, headers=headers)
172+
monkeypatch.setattr(
173+
"src.backend.utils.retrieve_all_agent_tools",
174+
lambda: [{"agent": "test_agent", "function": "test_function"}],
175+
)
176+
177+
178+
def test_input_task_invalid_json():
179+
"""Test the case where the input JSON is invalid."""
180+
invalid_json = "Invalid JSON data"
181+
182+
headers = {"Authorization": "Bearer mock-token"}
183+
response = client.post("/input_task", data=invalid_json, headers=headers)
184+
153185
assert response.status_code == 422
154186
assert "detail" in response.json()
155187

156-
188+
157189
def test_human_feedback_valid():
158190
payload = {
159191
"step_id": "step1",
@@ -301,7 +333,37 @@ def test_get_plans_not_found():
301333
headers = {"Authorization": "Bearer mock-token"}
302334
response = client.get("/plans", params={"session_id": "nonexistent"}, headers=headers)
303335
assert response.status_code == 404
304-
assert response.json()["detail"] == "Plan not found"
336+
337+
338+
def test_input_task_missing_description():
339+
"""Test the case where the input task description is missing."""
340+
input_task = {
341+
"session_id": None,
342+
"user_id": "mock-user-id",
343+
}
344+
345+
headers = {"Authorization": "Bearer mock-token"}
346+
response = client.post("/input_task", json=input_task, headers=headers)
347+
348+
# Assert response for missing description
349+
assert response.status_code == 422
350+
assert "detail" in response.json()
351+
352+
353+
def test_basic_endpoint():
354+
"""Test a basic endpoint to ensure the app runs."""
355+
response = client.get("/")
356+
assert response.status_code == 404 # The root endpoint is not defined
357+
358+
359+
def test_input_task_empty_description():
360+
"""Tests if /input_task handles an empty description."""
361+
empty_task = {"session_id": None, "user_id": "mock-user-id", "description": ""}
362+
headers = {"Authorization": "Bearer mock-token"}
363+
response = client.post("/input_task", json=empty_task, headers=headers)
364+
365+
assert response.status_code == 422
366+
assert "detail" in response.json() # Assert error message for missing description
305367

306368

307369
if __name__ == "__main__":

0 commit comments

Comments
 (0)