Skip to content

Commit 67e3933

Browse files
unit test fixed
1 parent 9e7e1c5 commit 67e3933

5 files changed

Lines changed: 404 additions & 0 deletions

File tree

src/tests/ContentProcessorAPI/helpers/test_azure_credential_utils.py

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
import sys
88
from unittest.mock import MagicMock, patch
99

10+
import pytest
11+
1012
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "..", "..", "ContentProcessorAPI")))
1113

1214
import app.utils.azure_credential_utils as azure_credential_utils # noqa: E402
@@ -48,3 +50,43 @@ def test_get_azure_credential_non_dev_env(
4850
mock_managed_identity_credential.assert_called_once_with(client_id="test-client-id")
4951
mock_default_azure_credential.assert_not_called()
5052
assert credential == mock_managed_credential
53+
54+
55+
@pytest.mark.asyncio
56+
@patch("app.utils.azure_credential_utils.os.getenv")
57+
@patch("app.utils.azure_credential_utils.AioDefaultAzureCredential")
58+
@patch("app.utils.azure_credential_utils.AioManagedIdentityCredential")
59+
async def test_get_azure_credential_async_dev_env(
60+
mock_aio_managed, mock_aio_default, mock_getenv
61+
):
62+
"""Test get_azure_credential_async in dev environment."""
63+
mock_getenv.return_value = "dev"
64+
mock_cred = MagicMock()
65+
mock_aio_default.return_value = mock_cred
66+
67+
credential = await azure_credential_utils.get_azure_credential_async()
68+
69+
mock_getenv.assert_called_once_with("APP_ENV", "prod")
70+
mock_aio_default.assert_called_once()
71+
mock_aio_managed.assert_not_called()
72+
assert credential == mock_cred
73+
74+
75+
@pytest.mark.asyncio
76+
@patch("app.utils.azure_credential_utils.os.getenv")
77+
@patch("app.utils.azure_credential_utils.AioDefaultAzureCredential")
78+
@patch("app.utils.azure_credential_utils.AioManagedIdentityCredential")
79+
async def test_get_azure_credential_async_non_dev_env(
80+
mock_aio_managed, mock_aio_default, mock_getenv
81+
):
82+
"""Test get_azure_credential_async in non-dev environment."""
83+
mock_getenv.return_value = "prod"
84+
mock_cred = MagicMock()
85+
mock_aio_managed.return_value = mock_cred
86+
87+
credential = await azure_credential_utils.get_azure_credential_async(client_id="test-id")
88+
89+
mock_getenv.assert_called_once_with("APP_ENV", "prod")
90+
mock_aio_managed.assert_called_once_with(client_id="test-id")
91+
mock_aio_default.assert_not_called()
92+
assert credential == mock_cred
Lines changed: 190 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,190 @@
1+
# Copyright (c) Microsoft Corporation.
2+
# Licensed under the MIT License.
3+
4+
"""Unit tests for the OpenTelemetry span-noise filter."""
5+
6+
import os
7+
import sys
8+
from unittest.mock import MagicMock, patch
9+
10+
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "..", "..", "ContentProcessorAPI")))
11+
12+
from app.utils.telemetry_filter import install_noise_filter # noqa: E402
13+
14+
15+
@patch("app.utils.telemetry_filter.trace")
16+
def test_install_noise_filter_no_active_processor(mock_trace):
17+
"""Early return when the provider has no _active_span_processor."""
18+
mock_provider = MagicMock(spec=[]) # no attributes at all
19+
mock_trace.get_tracer_provider.return_value = mock_provider
20+
21+
install_noise_filter(noisy_names=frozenset({"Foo"}))
22+
# Nothing to wrap, should return without error
23+
24+
25+
@patch("app.utils.telemetry_filter.trace")
26+
def test_install_noise_filter_wraps_inner_processors(mock_trace):
27+
"""Wraps each processor in _span_processors tuple."""
28+
inner_proc_1 = MagicMock()
29+
inner_proc_2 = MagicMock()
30+
31+
active_proc = MagicMock()
32+
active_proc._span_processors = (inner_proc_1, inner_proc_2)
33+
34+
mock_provider = MagicMock()
35+
mock_provider._active_span_processor = active_proc
36+
mock_trace.get_tracer_provider.return_value = mock_provider
37+
38+
install_noise_filter(
39+
noisy_names=frozenset({"NoisySpan"}),
40+
noisy_suffixes=(".noisy",),
41+
)
42+
43+
# The tuple should have been replaced with wrapped processors
44+
assert len(active_proc._span_processors) == 2
45+
# Each element should be a _Filter wrapping the original
46+
for wrapped in active_proc._span_processors:
47+
assert hasattr(wrapped, "_inner")
48+
49+
50+
@patch("app.utils.telemetry_filter.trace")
51+
def test_install_noise_filter_wraps_direct_processor(mock_trace):
52+
"""Wraps the processor directly when _span_processors is absent."""
53+
active_proc = MagicMock(spec=["on_start", "on_end", "shutdown", "force_flush"])
54+
# No _span_processors attribute
55+
56+
mock_provider = MagicMock()
57+
mock_provider._active_span_processor = active_proc
58+
mock_trace.get_tracer_provider.return_value = mock_provider
59+
60+
install_noise_filter(noisy_names=frozenset({"NoisySpan"}))
61+
62+
# Provider should now have a wrapped processor
63+
new_proc = mock_provider._active_span_processor
64+
assert hasattr(new_proc, "_inner")
65+
assert new_proc._inner is active_proc
66+
67+
68+
@patch("app.utils.telemetry_filter.trace")
69+
def test_filter_on_start_delegates(mock_trace):
70+
"""_Filter.on_start delegates to the inner processor."""
71+
inner_proc = MagicMock()
72+
active_proc = MagicMock()
73+
active_proc._span_processors = (inner_proc,)
74+
75+
mock_provider = MagicMock()
76+
mock_provider._active_span_processor = active_proc
77+
mock_trace.get_tracer_provider.return_value = mock_provider
78+
79+
install_noise_filter(noisy_names=frozenset({"NoisySpan"}))
80+
81+
wrapped = active_proc._span_processors[0]
82+
mock_span = MagicMock()
83+
mock_context = MagicMock()
84+
wrapped.on_start(mock_span, mock_context)
85+
86+
inner_proc.on_start.assert_called_once_with(mock_span, mock_context)
87+
88+
89+
@patch("app.utils.telemetry_filter.trace")
90+
def test_filter_on_end_drops_noisy_name(mock_trace):
91+
"""_Filter.on_end drops spans whose name is in noisy_names."""
92+
inner_proc = MagicMock()
93+
active_proc = MagicMock()
94+
active_proc._span_processors = (inner_proc,)
95+
96+
mock_provider = MagicMock()
97+
mock_provider._active_span_processor = active_proc
98+
mock_trace.get_tracer_provider.return_value = mock_provider
99+
100+
install_noise_filter(noisy_names=frozenset({"MsiToken.Refresh"}))
101+
102+
wrapped = active_proc._span_processors[0]
103+
mock_span = MagicMock()
104+
mock_span.name = "MsiToken.Refresh"
105+
wrapped.on_end(mock_span)
106+
107+
inner_proc.on_end.assert_not_called()
108+
109+
110+
@patch("app.utils.telemetry_filter.trace")
111+
def test_filter_on_end_drops_noisy_suffix(mock_trace):
112+
"""_Filter.on_end drops spans whose name ends with a noisy suffix."""
113+
inner_proc = MagicMock()
114+
active_proc = MagicMock()
115+
active_proc._span_processors = (inner_proc,)
116+
117+
mock_provider = MagicMock()
118+
mock_provider._active_span_processor = active_proc
119+
mock_trace.get_tracer_provider.return_value = mock_provider
120+
121+
install_noise_filter(noisy_suffixes=(" send", " receive"))
122+
123+
wrapped = active_proc._span_processors[0]
124+
mock_span = MagicMock()
125+
mock_span.name = "ServiceBusReceiver receive"
126+
wrapped.on_end(mock_span)
127+
128+
inner_proc.on_end.assert_not_called()
129+
130+
131+
@patch("app.utils.telemetry_filter.trace")
132+
def test_filter_on_end_passes_non_noisy(mock_trace):
133+
"""_Filter.on_end passes through non-noisy spans."""
134+
inner_proc = MagicMock()
135+
active_proc = MagicMock()
136+
active_proc._span_processors = (inner_proc,)
137+
138+
mock_provider = MagicMock()
139+
mock_provider._active_span_processor = active_proc
140+
mock_trace.get_tracer_provider.return_value = mock_provider
141+
142+
install_noise_filter(
143+
noisy_names=frozenset({"NoisySpan"}),
144+
noisy_suffixes=(".noisy",),
145+
)
146+
147+
wrapped = active_proc._span_processors[0]
148+
mock_span = MagicMock()
149+
mock_span.name = "ImportantOperation"
150+
wrapped.on_end(mock_span)
151+
152+
inner_proc.on_end.assert_called_once_with(mock_span)
153+
154+
155+
@patch("app.utils.telemetry_filter.trace")
156+
def test_filter_shutdown_delegates(mock_trace):
157+
"""_Filter.shutdown delegates to the inner processor."""
158+
inner_proc = MagicMock()
159+
active_proc = MagicMock()
160+
active_proc._span_processors = (inner_proc,)
161+
162+
mock_provider = MagicMock()
163+
mock_provider._active_span_processor = active_proc
164+
mock_trace.get_tracer_provider.return_value = mock_provider
165+
166+
install_noise_filter(noisy_names=frozenset())
167+
168+
wrapped = active_proc._span_processors[0]
169+
wrapped.shutdown()
170+
171+
inner_proc.shutdown.assert_called_once()
172+
173+
174+
@patch("app.utils.telemetry_filter.trace")
175+
def test_filter_force_flush_delegates(mock_trace):
176+
"""_Filter.force_flush delegates to the inner processor."""
177+
inner_proc = MagicMock()
178+
active_proc = MagicMock()
179+
active_proc._span_processors = (inner_proc,)
180+
181+
mock_provider = MagicMock()
182+
mock_provider._active_span_processor = active_proc
183+
mock_trace.get_tracer_provider.return_value = mock_provider
184+
185+
install_noise_filter(noisy_names=frozenset())
186+
187+
wrapped = active_proc._span_processors[0]
188+
wrapped.force_flush(timeout_millis=5000)
189+
190+
inner_proc.force_flush.assert_called_once_with(5000)

src/tests/ContentProcessorAPI/libs/test_cosmos_db_helper.py

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -195,3 +195,45 @@ def test_update_document_by_query(mock_certifi, mock_mongo_client):
195195

196196
assert result == mock_result
197197
mock_container.update_one.assert_called_once_with(query, {"$set": update})
198+
199+
200+
@patch("app.libs.azure.cosmos_db.helper.MongoClient")
201+
@patch("app.libs.azure.cosmos_db.helper.certifi.where")
202+
def test_init_with_indexes(mock_certifi, mock_mongo_client):
203+
"""Test CosmosMongDBHelper initialization with indexes creates missing indexes."""
204+
mock_certifi.return_value = "/path/to/cert"
205+
mock_client = MagicMock()
206+
mock_mongo_client.return_value = mock_client
207+
mock_db = MagicMock()
208+
mock_client.__getitem__.return_value = mock_db
209+
mock_db.list_collection_names.return_value = ["test_container"]
210+
mock_container = MagicMock()
211+
mock_db.__getitem__.return_value = mock_container
212+
mock_container.index_information.return_value = {}
213+
214+
helper = CosmosMongDBHelper(
215+
connection_string="mongodb://test",
216+
db_name="test_db",
217+
container_name="test_container",
218+
indexes=[("field1", 1), ("field2", -1)],
219+
)
220+
221+
assert mock_container.create_index.call_count == 2
222+
223+
224+
@patch("app.libs.azure.cosmos_db.helper.MongoClient")
225+
@patch("app.libs.azure.cosmos_db.helper.certifi.where")
226+
def test_create_container_when_missing(mock_certifi, mock_mongo_client):
227+
"""Test _create_container creates collection when it does not exist."""
228+
mock_certifi.return_value = "/path/to/cert"
229+
mock_client = MagicMock()
230+
mock_mongo_client.return_value = mock_client
231+
mock_db = MagicMock()
232+
mock_client.__getitem__.return_value = mock_db
233+
mock_db.list_collection_names.return_value = []
234+
mock_container = MagicMock()
235+
mock_db.__getitem__.return_value = mock_container
236+
237+
CosmosMongDBHelper("mongodb://test", "test_db", "new_container")
238+
239+
mock_db.create_collection.assert_called_once_with("new_container")

0 commit comments

Comments
 (0)