microsoft/teams.net

Public

mirrored from https://github.com/microsoft/teams.netAvailable

CodeCommitsIssuesPull requestsActionsInsightsSecurity
fix/msal-agentic-cache

Branches

Tags

  • No tags available.
0Branches0Tags
Go to file
Add file
Code

Clone

HTTPS

Download ZIP

core/test/Microsoft.Teams.Apps.UnitTests/CitationEntityTests.cs

407lines · modecode

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4using Microsoft.Teams.Apps.Schema;
5using Microsoft.Teams.Apps.Schema.Entities;
6using Microsoft.Teams.Core.Schema;
7
8namespace Microsoft.Teams.Apps.UnitTests;
9
10public class CitationEntityTests
11{
12 [Fact]
13 public void AddCitation_CreatesEntityWithClaim()
14 {
15 TeamsActivity activity = TeamsActivity.CreateBuilder()
16 .AddCitation(1, new CitationAppearance
17 {
18 Name = "Test Document",
19 Abstract = "Test abstract content"
20 })
21 .Build();
22
23 CitationEntity? citation = activity.GetCitation();
24
25 Assert.NotNull(activity.Entities);
26 Assert.Single(activity.Entities);
27 Assert.IsType<CitationEntity>(activity.Entities[0]);
28 Assert.NotNull(citation);
29 Assert.NotNull(citation.Citation);
30 Assert.Single(citation.Citation);
31 Assert.Equal(1, citation.Citation[0].Position);
32 Assert.Equal("Test Document", citation.Citation[0].Appearance.Name);
33 Assert.Equal("Test abstract content", citation.Citation[0].Appearance.Abstract);
34 }
35
36 [Fact]
37 public void AddCitation_MultipleCitations_AccumulateOnSameEntity()
38 {
39 TeamsActivity activity = TeamsActivity.CreateBuilder()
40 .AddCitation(1, new CitationAppearance
41 {
42 Name = "Document One",
43 Abstract = "First abstract"
44 })
45 .AddCitation(2, new CitationAppearance
46 {
47 Name = "Document Two",
48 Abstract = "Second abstract"
49 })
50 .Build();
51
52 CitationEntity? citation = activity.GetCitation();
53
54 Assert.NotNull(activity.Entities);
55 Assert.Single(activity.Entities);
56 Assert.NotNull(citation);
57 Assert.NotNull(citation.Citation);
58 Assert.Equal(2, citation.Citation.Count);
59 Assert.Equal(1, citation.Citation[0].Position);
60 Assert.Equal(2, citation.Citation[1].Position);
61 Assert.Equal("Document One", citation.Citation[0].Appearance.Name);
62 Assert.Equal("Document Two", citation.Citation[1].Appearance.Name);
63 }
64
65 [Fact]
66 public void AddAIGenerated_SetsAdditionalType()
67 {
68 TeamsActivity activity = TeamsActivity.CreateBuilder()
69 .AddAIGenerated()
70 .Build();
71
72 OMessageEntity? messageEntity = activity.GetMessageEntity();
73
74 Assert.NotNull(activity.Entities);
75 Assert.Single(activity.Entities);
76 Assert.IsType<OMessageEntity>(activity.Entities[0]);
77 Assert.NotNull(messageEntity);
78 Assert.NotNull(messageEntity.AdditionalType);
79 Assert.Contains("AIGeneratedContent", messageEntity.AdditionalType);
80 }
81
82 [Fact]
83 public void AddAIGenerated_CalledTwice_DoesNotDuplicate()
84 {
85 TeamsActivity activity = TeamsActivity.CreateBuilder()
86 .AddAIGenerated()
87 .AddAIGenerated()
88 .Build();
89
90 Assert.NotNull(activity.Entities);
91 Assert.Single(activity.Entities);
92 OMessageEntity? messageEntity = activity.Entities[0] as OMessageEntity;
93 Assert.NotNull(messageEntity?.AdditionalType);
94 Assert.Single(messageEntity.AdditionalType);
95 }
96
97 [Fact]
98 public void AddAIGenerated_ThenAddCitation_PreservesAILabel()
99 {
100 TeamsActivity activity = TeamsActivity.CreateBuilder()
101 .AddAIGenerated()
102 .AddCitation(1, new CitationAppearance
103 {
104 Name = "Test Doc",
105 Abstract = "Test abstract"
106 })
107 .Build();
108
109 CitationEntity? citation = activity.GetCitation();
110
111 Assert.NotNull(activity.Entities);
112 Assert.Single(activity.Entities);
113 Assert.IsType<CitationEntity>(activity.Entities[0]);
114 Assert.NotNull(citation);
115 Assert.NotNull(citation.AdditionalType);
116 Assert.Contains("AIGeneratedContent", citation.AdditionalType);
117 Assert.NotNull(citation.Citation);
118 Assert.Single(citation.Citation);
119 }
120
121 [Fact]
122 public void AddFeedback_SetsFeedbackLoopEnabled()
123 {
124 TeamsActivity activity = TeamsActivity.CreateBuilder()
125 .AddFeedback()
126 .Build();
127
128 Assert.NotNull(activity.ChannelData);
129 Assert.True(activity.ChannelData.FeedbackLoopEnabled);
130 }
131
132 [Fact]
133 public void AddFeedback_WithMode_SetsFeedbackLoopAndClearsFeedbackLoopEnabled()
134 {
135 TeamsActivity activity = TeamsActivity.CreateBuilder()
136 .AddFeedback(FeedbackType.Custom)
137 .Build();
138
139 Assert.NotNull(activity.ChannelData);
140 Assert.Null(activity.ChannelData.FeedbackLoopEnabled);
141 Assert.NotNull(activity.ChannelData.FeedbackLoop);
142 Assert.Equal(FeedbackType.Custom, activity.ChannelData.FeedbackLoop.Type);
143 }
144
145 [Fact]
146 public void AddCitation_WithAllAppearanceFields_SetsCorrectly()
147 {
148 TeamsActivity activity = TeamsActivity.CreateBuilder()
149 .AddCitation(1, new CitationAppearance
150 {
151 Name = "Full Document",
152 Abstract = "Full abstract",
153 Text = "{\"type\":\"AdaptiveCard\"}",
154 Url = new Uri("https://example.com/doc"),
155 EncodingFormat = EncodingFormats.AdaptiveCard,
156 Icon = CitationIcon.MicrosoftWord,
157 Keywords = ["keyword1", "keyword2"],
158 UsageInfo = new SensitiveUsageEntity { Name = "Confidential" }
159 })
160 .Build();
161
162 CitationEntity? citation = activity.GetCitation();
163
164 Assert.NotNull(citation);
165 Assert.NotNull(citation.Citation);
166 CitationAppearanceDocument appearance = citation.Citation[0].Appearance;
167 Assert.Equal("Full Document", appearance.Name);
168 Assert.Equal("Full abstract", appearance.Abstract);
169 Assert.Equal("{\"type\":\"AdaptiveCard\"}", appearance.Text);
170 Assert.Equal(new Uri("https://example.com/doc"), appearance.Url);
171 Assert.Equal(EncodingFormats.AdaptiveCard, appearance.EncodingFormat);
172 Assert.NotNull(appearance.Image);
173 Assert.Equal(CitationIcon.MicrosoftWord, appearance.Image.Name);
174 Assert.NotNull(appearance.Keywords);
175 Assert.Equal(2, appearance.Keywords.Count);
176 Assert.NotNull(appearance.UsageInfo);
177 Assert.Equal("Confidential", appearance.UsageInfo.Name);
178 }
179
180 [Fact]
181 public void CitationEntity_RoundTrip_Serialization()
182 {
183 TeamsActivity activity = TeamsActivity.CreateBuilder()
184 .AddAIGenerated()
185 .AddCitation(1, new CitationAppearance
186 {
187 Name = "Test Document",
188 Abstract = "Test abstract content",
189 Url = new Uri("https://example.com"),
190 Icon = CitationIcon.Pdf,
191 Keywords = ["test", "citation"]
192 })
193 .AddFeedback()
194 .Build();
195
196 string json = activity.ToJson();
197
198 Assert.Contains("\"citation\"", json);
199 Assert.Contains("Test Document", json);
200 Assert.Contains("Test abstract content", json);
201 Assert.Contains("https://example.com", json);
202 Assert.Contains("AIGeneratedContent", json);
203 Assert.Contains("Claim", json);
204 Assert.Contains("DigitalDocument", json);
205 Assert.Contains("PDF", json);
206 Assert.Contains("feedbackLoopEnabled", json);
207 }
208
209 [Fact]
210 public void CitationEntity_Rebase_SurvivesRoundTrip()
211 {
212 TeamsActivity activity = TeamsActivity.CreateBuilder()
213 .AddAIGenerated()
214 .AddCitation(1, new CitationAppearance
215 {
216 Name = "Rebase Test Doc",
217 Abstract = "Rebase test abstract",
218 Icon = CitationIcon.MicrosoftExcel
219 })
220 .Build();
221
222 // Verify entities are serialized correctly via the TeamsActivity JSON output
223 // CoreActivity no longer has Entities; they are in Properties dict and extracted by TeamsActivity
224 Assert.NotNull(activity.Entities);
225 Assert.Single(activity.Entities);
226
227 string activityJson = activity.ToJson();
228 Assert.Contains("citation", activityJson);
229 Assert.Contains("Rebase Test Doc", activityJson);
230 Assert.Contains("Rebase test abstract", activityJson);
231 Assert.Contains("AIGeneratedContent", activityJson);
232 Assert.Contains("Microsoft Excel", activityJson);
233 }
234
235 [Fact]
236 public void Fixture_AdaptiveCardActivity_DeserializesAIGeneratedEntity()
237 {
238 string json = """
239 {
240 "type": "message",
241 "channelId": "msteams",
242 "entities": [
243 {
244 "type": "https://schema.org/Message",
245 "@context": "https://schema.org",
246 "@type": "Message",
247 "additionalType": [
248 "AIGeneratedContent"
249 ]
250 }
251 ]
252 }
253 """;
254
255 CoreActivity coreActivity = CoreActivity.FromJsonString(json);
256 TeamsActivity activity = TeamsActivity.FromActivity(coreActivity);
257
258 Assert.NotNull(activity.Entities);
259 Assert.Single(activity.Entities);
260
261 Entity entity = activity.Entities[0];
262 Assert.Equal("https://schema.org/Message", entity.Type);
263 Assert.Equal("Message", entity.OType);
264
265 // Should deserialize as CitationEntity (since @type is "Message")
266 CitationEntity? citationEntity = entity as CitationEntity;
267 Assert.NotNull(citationEntity);
268 Assert.NotNull(citationEntity.AdditionalType);
269 Assert.Contains("AIGeneratedContent", citationEntity.AdditionalType);
270 }
271
272 [Fact]
273 public void Fixture_SensitiveUsageEntity_DeserializesByOType()
274 {
275 string json = """
276 {
277 "type": "message",
278 "entities": [
279 {
280 "type": "https://schema.org/Message",
281 "@context": "https://schema.org",
282 "@type": "CreativeWork",
283 "name": "Confidential",
284 "description": "This is sensitive content"
285 }
286 ]
287 }
288 """;
289
290 CoreActivity coreActivity = CoreActivity.FromJsonString(json);
291 TeamsActivity activity = TeamsActivity.FromActivity(coreActivity);
292
293 Assert.NotNull(activity.Entities);
294 Assert.Single(activity.Entities);
295
296 SensitiveUsageEntity? entity = activity.Entities[0] as SensitiveUsageEntity;
297 Assert.NotNull(entity);
298 Assert.Equal("Confidential", entity.Name);
299 Assert.Equal("This is sensitive content", entity.Description);
300 }
301
302 [Fact]
303 public void OMessageEntity_WithUnknownOType_DeserializesAsOMessageEntity()
304 {
305 string json = """
306 {
307 "type": "message",
308 "entities": [
309 {
310 "type": "https://schema.org/Message",
311 "@context": "https://schema.org",
312 "@type": "UnknownType"
313 }
314 ]
315 }
316 """;
317
318 CoreActivity coreActivity = CoreActivity.FromJsonString(json);
319 TeamsActivity activity = TeamsActivity.FromActivity(coreActivity);
320
321 Assert.NotNull(activity.Entities);
322 Assert.Single(activity.Entities);
323
324 Entity entity = activity.Entities[0];
325 Assert.IsType<OMessageEntity>(entity);
326 Assert.Equal("UnknownType", entity.OType);
327 }
328
329 [Fact]
330 public void Fixture_CitationEntity_DeserializesWithClaims()
331 {
332 string json = """
333 {
334 "type": "message",
335 "entities": [
336 {
337 "type": "https://schema.org/Message",
338 "@context": "https://schema.org",
339 "@type": "Message",
340 "additionalType": ["AIGeneratedContent"],
341 "citation": [
342 {
343 "@type": "Claim",
344 "position": 1,
345 "appearance": {
346 "@type": "DigitalDocument",
347 "name": "Test Document",
348 "abstract": "Test abstract",
349 "url": "https://example.com/doc",
350 "encodingFormat": "application/vnd.microsoft.card.adaptive"
351 }
352 }
353 ]
354 }
355 ]
356 }
357 """;
358
359 CoreActivity coreActivity = CoreActivity.FromJsonString(json);
360 TeamsActivity activity = TeamsActivity.FromActivity(coreActivity);
361
362 Assert.NotNull(activity.Entities);
363 Assert.Single(activity.Entities);
364
365 CitationEntity? citationEntity = activity.Entities[0] as CitationEntity;
366 Assert.NotNull(citationEntity);
367 Assert.NotNull(citationEntity.AdditionalType);
368 Assert.Contains("AIGeneratedContent", citationEntity.AdditionalType);
369 Assert.NotNull(citationEntity.Citation);
370 Assert.Single(citationEntity.Citation);
371 Assert.Equal(1, citationEntity.Citation[0].Position);
372 Assert.Equal("Test Document", citationEntity.Citation[0].Appearance.Name);
373 Assert.Equal("Test abstract", citationEntity.Citation[0].Appearance.Abstract);
374 Assert.Equal(EncodingFormats.AdaptiveCard, citationEntity.Citation[0].Appearance.EncodingFormat);
375 }
376
377 [Fact]
378 public void CitationEntity_CopyConstructor_PreservesData()
379 {
380 CitationEntity original = new();
381 original.AdditionalType = ["AIGeneratedContent"];
382 original.Citation = [
383 new CitationClaim
384 {
385 Position = 1,
386 Appearance = new CitationAppearanceDocument
387 {
388 Name = "Doc",
389 Abstract = "Abstract"
390 }
391 }
392 ];
393
394 CitationEntity copy = new(original);
395
396 Assert.NotNull(copy.AdditionalType);
397 Assert.Contains("AIGeneratedContent", copy.AdditionalType);
398 Assert.NotNull(copy.Citation);
399 Assert.Single(copy.Citation);
400 Assert.Equal(1, copy.Citation[0].Position);
401 Assert.Equal("Doc", copy.Citation[0].Appearance.Name);
402
403 // Ensure it's a deep copy (modifying copy doesn't affect original)
404 copy.AdditionalType.Add("NewType");
405 Assert.Single(original.AdditionalType);
406 }
407}
408