Skip to content

Commit 480e469

Browse files
CopilotnikhilNava
andcommitted
Revert logic type to Callable[[TurnContext], Awaitable] to match Middleware Protocol; consolidate tests
Co-authored-by: nikhilNava <211831449+nikhilNava@users.noreply.github.com>
1 parent e88a6dd commit 480e469

File tree

5 files changed

+39
-99
lines changed

5 files changed

+39
-99
lines changed

libraries/microsoft-agents-a365-observability-hosting/microsoft_agents_a365/observability/hosting/middleware/baggage_middleware.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ class BaggageMiddleware:
2323
async def on_turn(
2424
self,
2525
context: TurnContext,
26-
logic: Callable[[], Awaitable],
26+
logic: Callable[[TurnContext], Awaitable],
2727
) -> None:
2828
activity = context.activity
2929
is_async_reply = (

libraries/microsoft-agents-a365-observability-hosting/microsoft_agents_a365/observability/hosting/middleware/output_logging_middleware.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,7 @@ class OutputLoggingMiddleware:
126126
async def on_turn(
127127
self,
128128
context: TurnContext,
129-
logic: Callable[[], Awaitable],
129+
logic: Callable[[TurnContext], Awaitable],
130130
) -> None:
131131
agent_details = _derive_agent_details(context)
132132
tenant_details = _derive_tenant_details(context)

tests/observability/hosting/middleware/test_baggage_middleware.py

Lines changed: 1 addition & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# Copyright (c) Microsoft Corporation.
22
# Licensed under the MIT License.
33

4-
from unittest.mock import AsyncMock, MagicMock
4+
from unittest.mock import MagicMock
55

66
import pytest
77
from microsoft_agents.activity import (
@@ -95,15 +95,3 @@ async def logic():
9595
assert logic_called is True
9696
# Baggage should NOT be set because the middleware skipped it
9797
assert captured_caller_id is None
98-
99-
100-
@pytest.mark.asyncio
101-
async def test_baggage_middleware_calls_logic():
102-
"""BaggageMiddleware should always call the downstream logic."""
103-
middleware = BaggageMiddleware()
104-
ctx = _make_turn_context()
105-
106-
logic_mock = AsyncMock()
107-
await middleware.on_turn(ctx, logic_mock)
108-
109-
logic_mock.assert_awaited_once()

tests/observability/hosting/middleware/test_observability_hosting_manager.py

Lines changed: 34 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -24,73 +24,50 @@ def _reset_singleton():
2424
ObservabilityHostingManager.reset()
2525

2626

27-
def test_configure_returns_instance():
28-
"""configure() should return an ObservabilityHostingManager instance."""
29-
middleware_set = MagicMock()
30-
instance = ObservabilityHostingManager.configure(middleware_set, ObservabilityHostingOptions())
31-
assert isinstance(instance, ObservabilityHostingManager)
32-
33-
3427
def test_configure_is_singleton():
35-
"""Subsequent configure() calls should return the same instance."""
28+
"""configure() should return an ObservabilityHostingManager and be a singleton."""
3629
middleware_set = MagicMock()
3730
options = ObservabilityHostingOptions()
3831
first = ObservabilityHostingManager.configure(middleware_set, options)
32+
assert isinstance(first, ObservabilityHostingManager)
3933
second = ObservabilityHostingManager.configure(middleware_set, options)
4034
assert first is second
4135

4236

43-
def test_configure_registers_baggage_middleware_by_default():
44-
"""By default, BaggageMiddleware should be registered."""
45-
middleware_set = MagicMock()
46-
ObservabilityHostingManager.configure(middleware_set, ObservabilityHostingOptions())
47-
48-
# The middleware_set.use should have been called once (only BaggageMiddleware by default)
49-
assert middleware_set.use.call_count == 1
50-
registered = middleware_set.use.call_args_list[0][0][0]
51-
assert isinstance(registered, BaggageMiddleware)
52-
53-
54-
def test_configure_registers_both_middlewares():
55-
"""When output logging is enabled, both middlewares should be registered."""
56-
middleware_set = MagicMock()
57-
options = ObservabilityHostingOptions(enable_baggage=True, enable_output_logging=True)
58-
ObservabilityHostingManager.configure(middleware_set, options)
59-
60-
assert middleware_set.use.call_count == 2
61-
registered_types = [c[0][0] for c in middleware_set.use.call_args_list]
62-
assert isinstance(registered_types[0], BaggageMiddleware)
63-
assert isinstance(registered_types[1], OutputLoggingMiddleware)
64-
65-
66-
def test_configure_disables_baggage():
67-
"""When baggage is disabled, only output logging should be registered (if enabled)."""
68-
middleware_set = MagicMock()
69-
options = ObservabilityHostingOptions(enable_baggage=False, enable_output_logging=True)
70-
ObservabilityHostingManager.configure(middleware_set, options)
71-
72-
assert middleware_set.use.call_count == 1
73-
registered = middleware_set.use.call_args_list[0][0][0]
74-
assert isinstance(registered, OutputLoggingMiddleware)
75-
76-
77-
def test_configure_disables_all():
78-
"""When both are disabled, no middleware should be registered."""
37+
@pytest.mark.parametrize(
38+
"enable_baggage,enable_output_logging,expected_types",
39+
[
40+
(True, False, [BaggageMiddleware]),
41+
(True, True, [BaggageMiddleware, OutputLoggingMiddleware]),
42+
(False, True, [OutputLoggingMiddleware]),
43+
(False, False, []),
44+
],
45+
ids=["default_baggage_only", "both_enabled", "output_only", "none"],
46+
)
47+
def test_configure_registers_expected_middlewares(
48+
enable_baggage, enable_output_logging, expected_types
49+
):
50+
"""configure() should register the correct middlewares based on options."""
7951
middleware_set = MagicMock()
80-
options = ObservabilityHostingOptions(enable_baggage=False, enable_output_logging=False)
52+
options = ObservabilityHostingOptions(
53+
enable_baggage=enable_baggage, enable_output_logging=enable_output_logging
54+
)
8155
ObservabilityHostingManager.configure(middleware_set, options)
8256

83-
middleware_set.use.assert_not_called()
84-
85-
86-
def test_configure_raises_on_none_middleware_set():
87-
"""configure() should raise TypeError when middleware_set is None."""
88-
with pytest.raises(TypeError, match="middleware_set must not be None"):
89-
ObservabilityHostingManager.configure(None, ObservabilityHostingOptions())
57+
assert middleware_set.use.call_count == len(expected_types)
58+
for call, expected_type in zip(middleware_set.use.call_args_list, expected_types, strict=True):
59+
assert isinstance(call[0][0], expected_type)
9060

9161

92-
def test_configure_raises_on_none_options():
93-
"""configure() should raise TypeError when options is None."""
94-
middleware_set = MagicMock()
95-
with pytest.raises(TypeError, match="options must not be None"):
96-
ObservabilityHostingManager.configure(middleware_set, None)
62+
@pytest.mark.parametrize(
63+
"middleware_set,options,match",
64+
[
65+
(None, ObservabilityHostingOptions(), "middleware_set must not be None"),
66+
(MagicMock(), None, "options must not be None"),
67+
],
68+
ids=["none_middleware_set", "none_options"],
69+
)
70+
def test_configure_raises_on_none(middleware_set, options, match):
71+
"""configure() should raise TypeError when required args are None."""
72+
with pytest.raises(TypeError, match=match):
73+
ObservabilityHostingManager.configure(middleware_set, options)

tests/observability/hosting/middleware/test_output_logging_middleware.py

Lines changed: 2 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,7 @@ async def test_send_handler_skips_non_message_activities():
136136

137137
@pytest.mark.asyncio
138138
async def test_send_handler_creates_output_scope_for_messages():
139-
"""Send handler should create an OutputScope for message activities."""
139+
"""Send handler should create an OutputScope for message activities and dispose on success."""
140140
middleware = OutputLoggingMiddleware()
141141
ctx = _make_turn_context()
142142

@@ -159,6 +159,7 @@ async def test_send_handler_creates_output_scope_for_messages():
159159
mock_output_scope_cls.start.assert_called_once()
160160
send_next.assert_awaited_once()
161161
mock_scope.dispose.assert_called_once()
162+
mock_scope.record_error.assert_not_called()
162163

163164

164165
@pytest.mark.asyncio
@@ -216,29 +217,3 @@ async def test_send_handler_rethrows_errors():
216217

217218
mock_scope.record_error.assert_called_once_with(send_error)
218219
mock_scope.dispose.assert_called_once()
219-
220-
221-
@pytest.mark.asyncio
222-
async def test_send_handler_disposes_scope_on_success():
223-
"""Send handler should dispose the OutputScope even when send_next succeeds."""
224-
middleware = OutputLoggingMiddleware()
225-
ctx = _make_turn_context()
226-
227-
await middleware.on_turn(ctx, AsyncMock())
228-
229-
handler = ctx._on_send_activities[-1]
230-
231-
activities = [Activity(type="message", text="Reply")]
232-
send_next = AsyncMock()
233-
234-
with patch(
235-
"microsoft_agents_a365.observability.hosting.middleware"
236-
".output_logging_middleware.OutputScope"
237-
) as mock_output_scope_cls:
238-
mock_scope = MagicMock()
239-
mock_output_scope_cls.start.return_value = mock_scope
240-
241-
await handler(ctx, activities, send_next)
242-
243-
mock_scope.dispose.assert_called_once()
244-
mock_scope.record_error.assert_not_called()

0 commit comments

Comments
 (0)