Skip to content

Commit 2282054

Browse files
utils_kernel test file
1 parent ada0f77 commit 2282054

File tree

1 file changed

+225
-0
lines changed

1 file changed

+225
-0
lines changed
Lines changed: 225 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,225 @@
1+
# src/tests/backend/test_utils_kernel.py
2+
import os
3+
import sys
4+
import json
5+
import asyncio
6+
import pytest
7+
import types
8+
import requests
9+
10+
# Stub out app_config.config so utils_kernel can import it
11+
import types as _types
12+
import sys as _sys
13+
14+
class _DummyConfigImport:
15+
def create_kernel(self):
16+
from backend.utils_kernel import DummyKernel
17+
return DummyKernel()
18+
19+
app_cfg = _types.ModuleType("app_config")
20+
app_cfg.config = _DummyConfigImport()
21+
_sys.modules["app_config"] = app_cfg
22+
23+
# Ensure src is on path
24+
ROOT = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..'))
25+
SRC = os.path.join(ROOT, 'src')
26+
if SRC not in sys.path:
27+
sys.path.insert(0, SRC)
28+
29+
# Stub semantic_kernel and its submodules
30+
sk_pkg = types.ModuleType('semantic_kernel')
31+
sk_pkg.__path__ = []
32+
sk_funcs = types.ModuleType('semantic_kernel.functions')
33+
def kernel_function(name=None, description=None):
34+
def decorator(func): return func
35+
return decorator
36+
sk_funcs.kernel_function = kernel_function
37+
sk_funcs.KernelFunction = lambda *args, **kwargs: (lambda f: f)
38+
sk_pkg.Kernel = type('Kernel', (), {})
39+
40+
sys.modules['semantic_kernel'] = sk_pkg
41+
sys.modules['semantic_kernel.functions'] = sk_funcs
42+
43+
# Stub semantic_kernel.agents.azure_ai.azure_ai_agent.AzureAIAgent
44+
agents_pkg = types.ModuleType('semantic_kernel.agents')
45+
agents_pkg.__path__ = []
46+
az_pkg = types.ModuleType('semantic_kernel.agents.azure_ai')
47+
az_pkg.__path__ = []
48+
aazure_pkg = types.ModuleType('semantic_kernel.agents.azure_ai.azure_ai_agent')
49+
class AzureAIAgent:
50+
def __init__(self): pass
51+
aazure_pkg.AzureAIAgent = AzureAIAgent
52+
53+
sys.modules['semantic_kernel.agents'] = agents_pkg
54+
sys.modules['semantic_kernel.agents.azure_ai'] = az_pkg
55+
sys.modules['semantic_kernel.agents.azure_ai.azure_ai_agent'] = aazure_pkg
56+
57+
# Stub azure.identity.DefaultAzureCredential
58+
azure_pkg = types.ModuleType('azure')
59+
identity_pkg = types.ModuleType('azure.identity')
60+
def dummy_credential():
61+
class C:
62+
def get_token(self, scope): return types.SimpleNamespace(token='token')
63+
return C()
64+
identity_pkg.DefaultAzureCredential = dummy_credential
65+
azure_pkg.identity = identity_pkg
66+
sys.modules['azure'] = azure_pkg
67+
sys.modules['azure.identity'] = identity_pkg
68+
69+
# Stub models.messages_kernel.AgentType
70+
models_pkg = types.ModuleType('models')
71+
msgs_mod = types.ModuleType('models.messages_kernel')
72+
from enum import Enum
73+
class AgentType(Enum):
74+
HR = 'hr_agent'
75+
PROCUREMENT = 'procurement_agent'
76+
GENERIC = 'generic'
77+
PRODUCT = 'product_agent'
78+
MARKETING = 'marketing_agent'
79+
TECH_SUPPORT = 'tech_support_agent'
80+
HUMAN = 'human_agent'
81+
PLANNER = 'planner_agent'
82+
GROUP_CHAT_MANAGER = 'group_chat_manager'
83+
msgs_mod.AgentType = AgentType
84+
models_pkg.messages_kernel = msgs_mod
85+
sys.modules['models'] = models_pkg
86+
sys.modules['models.messages_kernel'] = msgs_mod
87+
88+
# Stub context.cosmos_memory_kernel.CosmosMemoryContext
89+
context_pkg = types.ModuleType('context')
90+
cos_pkg = types.ModuleType('context.cosmos_memory_kernel')
91+
class _TempCosmos:
92+
def __init__(self, session_id, user_id):
93+
self.session_id = session_id
94+
self.user_id = user_id
95+
cos_pkg.CosmosMemoryContext = _TempCosmos
96+
context_pkg.cosmos_memory_kernel = cos_pkg
97+
sys.modules['context'] = context_pkg
98+
sys.modules['context.cosmos_memory_kernel'] = cos_pkg
99+
100+
# Stub kernel_agents and agent classes
101+
ka_pkg = types.ModuleType('kernel_agents')
102+
ka_pkg.__path__ = []
103+
submods = [
104+
'agent_factory','generic_agent','group_chat_manager','hr_agent',
105+
'human_agent','marketing_agent','planner_agent','procurement_agent',
106+
'product_agent','tech_support_agent'
107+
]
108+
for sub in submods:
109+
m = types.ModuleType(f'kernel_agents.{sub}')
110+
sys.modules[f'kernel_agents.{sub}'] = m
111+
setattr(ka_pkg, sub, m)
112+
# Stub AgentFactory
113+
class AgentFactory:
114+
@staticmethod
115+
async def create_all_agents(session_id, user_id, temperature):
116+
return {}
117+
sys.modules['kernel_agents.agent_factory'].AgentFactory = AgentFactory
118+
# Stub other agent classes
119+
for sub in submods:
120+
mod = sys.modules[f'kernel_agents.{sub}']
121+
cls_name = ''.join(part.title() for part in sub.split('_'))
122+
setattr(mod, cls_name, type(cls_name, (), {}))
123+
sys.modules['kernel_agents'] = ka_pkg
124+
125+
# Import module under test
126+
from backend.utils_kernel import (
127+
initialize_runtime_and_context,
128+
get_agents,
129+
load_tools_from_json_files,
130+
rai_success,
131+
agent_instances,
132+
config,
133+
CosmosMemoryContext
134+
)
135+
136+
# Dummy Kernel for testing
137+
class DummyKernel:
138+
pass
139+
140+
class DummyConfig:
141+
def create_kernel(self): return DummyKernel()
142+
143+
# Setup overrides
144+
def setup_module(module):
145+
import backend.utils_kernel as uk
146+
uk.config = DummyConfig()
147+
uk.CosmosMemoryContext = _TempCosmos
148+
149+
@pytest.mark.asyncio
150+
async def test_initialize_runtime_and_context_valid():
151+
kernel, mem = await initialize_runtime_and_context(user_id='u1')
152+
assert isinstance(kernel, DummyKernel)
153+
assert mem.user_id == 'u1'
154+
155+
@pytest.mark.asyncio
156+
async def test_initialize_runtime_and_context_invalid():
157+
with pytest.raises(ValueError):
158+
await initialize_runtime_and_context()
159+
160+
@pytest.mark.asyncio
161+
async def test_get_agents_caching(monkeypatch):
162+
class DummyAgent:
163+
def __init__(self, name): self.name = name
164+
async def fake_create_all_agents(session_id, user_id, temperature):
165+
return {AgentType.HR: DummyAgent('hr'), AgentType.PRODUCT: DummyAgent('prod')}
166+
import backend.utils_kernel as uk
167+
# Override the AgentFactory class in utils_kernel module completely
168+
FakeFactory = type('AgentFactory', (), {'create_all_agents': staticmethod(fake_create_all_agents)})
169+
monkeypatch.setattr(uk, 'AgentFactory', FakeFactory)
170+
171+
agent_instances.clear()
172+
agents = await get_agents('s', 'u')
173+
assert isinstance(agents, dict)
174+
agents2 = await get_agents('s', 'u')
175+
assert agents2 is agents
176+
177+
def test_load_tools_from_json_files(tmp_path, monkeypatch, caplog):
178+
tools_dir = tmp_path / 'tools'
179+
tools_dir.mkdir()
180+
data = {'tools':[{'name':'foo','description':'desc','parameters':{'a':1}}]}
181+
(tools_dir / 'hr_tools.json').write_text(json.dumps(data))
182+
(tools_dir / 'bad.json').write_text('{bad')
183+
import backend.utils_kernel as uk
184+
monkeypatch.setattr(uk.os.path, 'dirname', lambda _: str(tmp_path))
185+
caplog.set_level('WARNING')
186+
funcs = load_tools_from_json_files()
187+
assert any(f['function']=='foo' for f in funcs)
188+
assert 'Error loading tool file bad.json' in caplog.text
189+
190+
@pytest.mark.asyncio
191+
async def test_rai_success_missing_env(monkeypatch):
192+
monkeypatch.delenv('AZURE_OPENAI_ENDPOINT', raising=False)
193+
monkeypatch.delenv('AZURE_OPENAI_API_VERSION', raising=False)
194+
monkeypatch.delenv('AZURE_OPENAI_MODEL_NAME', raising=False)
195+
class Cred:
196+
def get_token(self, _): return types.SimpleNamespace(token='t')
197+
monkeypatch.setattr('backend.utils_kernel.DefaultAzureCredential', lambda: Cred())
198+
res = await rai_success('x')
199+
assert res is True
200+
201+
@pytest.mark.asyncio
202+
async def test_rai_success_api(monkeypatch):
203+
monkeypatch.setenv('AZURE_OPENAI_ENDPOINT','http://e')
204+
monkeypatch.setenv('AZURE_OPENAI_API_VERSION','v')
205+
monkeypatch.setenv('AZURE_OPENAI_MODEL_NAME','n')
206+
class Cred:
207+
def get_token(self, _): return types.SimpleNamespace(token='t')
208+
monkeypatch.setattr('backend.utils_kernel.DefaultAzureCredential', lambda: Cred())
209+
class Resp:
210+
status_code=200
211+
def json(self): return {'choices':[{'message':{'content':'FALSE'}}]}
212+
def raise_for_status(self): pass
213+
monkeypatch.setattr(requests, 'post', lambda *a, **k: Resp())
214+
res = await rai_success('y')
215+
assert res is True
216+
217+
# New test to cover no-tools-dir path
218+
219+
def test_load_tools_from_json_files_no_dir(tmp_path, monkeypatch):
220+
# No 'tools' subdirectory exists
221+
import backend.utils_kernel as uk
222+
# Make dirname() point to a path without tools folder
223+
monkeypatch.setattr(uk.os.path, 'dirname', lambda _: str(tmp_path))
224+
funcs = load_tools_from_json_files()
225+
assert funcs == []

0 commit comments

Comments
 (0)