@@ -95,3 +95,84 @@ 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_extracts_product_context_from_channel_data ():
102+ """BaggageMiddleware should extract productContext from channel_data when sub_channel is not set."""
103+ from microsoft_agents .activity import ChannelId
104+ from microsoft_agents_a365 .observability .core .constants import CHANNEL_LINK_KEY
105+
106+ middleware = BaggageMiddleware ()
107+
108+ # Create activity with ChannelId (no sub_channel) and channel_data with productContext
109+ activity = Activity (
110+ type = "message" ,
111+ text = "Hello" ,
112+ from_property = ChannelAccount (
113+ aad_object_id = "caller-id" ,
114+ name = "Caller" ,
115+ ),
116+ recipient = ChannelAccount (
117+ tenant_id = "tenant-123" ,
118+ name = "Agent" ,
119+ ),
120+ conversation = ConversationAccount (id = "conv-id" ),
121+ service_url = "https://example.com" ,
122+ channel_id = ChannelId (channel = "msteams" ), # No sub_channel
123+ channel_data = {"productContext" : "COPILOT" },
124+ )
125+
126+ adapter = MagicMock ()
127+ ctx = TurnContext (adapter , activity )
128+
129+ captured_channel_link = None
130+
131+ async def logic ():
132+ nonlocal captured_channel_link
133+ captured_channel_link = baggage .get_baggage (CHANNEL_LINK_KEY )
134+
135+ await middleware .on_turn (ctx , logic )
136+
137+ assert captured_channel_link == "COPILOT"
138+
139+
140+ @pytest .mark .asyncio
141+ async def test_baggage_middleware_sub_channel_takes_precedence_over_product_context ():
142+ """BaggageMiddleware should use sub_channel when both sub_channel and productContext are present."""
143+ from microsoft_agents .activity import ChannelId
144+ from microsoft_agents_a365 .observability .core .constants import CHANNEL_LINK_KEY
145+
146+ middleware = BaggageMiddleware ()
147+
148+ # Create activity with BOTH sub_channel and productContext in channel_data
149+ activity = Activity (
150+ type = "message" ,
151+ text = "Hello" ,
152+ from_property = ChannelAccount (
153+ aad_object_id = "caller-id" ,
154+ name = "Caller" ,
155+ ),
156+ recipient = ChannelAccount (
157+ tenant_id = "tenant-123" ,
158+ name = "Agent" ,
159+ ),
160+ conversation = ConversationAccount (id = "conv-id" ),
161+ service_url = "https://example.com" ,
162+ channel_id = ChannelId (channel = "msteams" , sub_channel = "teams-subchannel" ),
163+ channel_data = {"productContext" : "COPILOT" }, # Should be ignored
164+ )
165+
166+ adapter = MagicMock ()
167+ ctx = TurnContext (adapter , activity )
168+
169+ captured_channel_link = None
170+
171+ async def logic ():
172+ nonlocal captured_channel_link
173+ captured_channel_link = baggage .get_baggage (CHANNEL_LINK_KEY )
174+
175+ await middleware .on_turn (ctx , logic )
176+
177+ # sub_channel should take precedence, productContext should be ignored
178+ assert captured_channel_link == "teams-subchannel"
0 commit comments