microsoft/teams.net

Public

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

CodeCommitsIssuesPull requestsActionsInsightsSecurity
samples/repro-da

Branches

Tags

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

Clone

HTTPS

Download ZIP

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

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