microsoft/teams.net

Public

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

CodeCommitsIssuesPull requestsActionsInsightsSecurity
v2.0.8

Branches

Tags

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

Clone

HTTPS

Download ZIP

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

417lines · 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
10#pragma warning disable ExperimentalTeamsQuotedReplies
11public class QuotedReplyEntityTests
12{
13 [Fact]
14 public void QuotedReplyEntity_HasCorrectType()
15 {
16 var entity = new QuotedReplyEntity();
17 Assert.Equal("quotedReply", entity.Type);
18 }
19
20 [Fact]
21 public void QuotedReplyEntity_SetsAndGetsQuotedReply()
22 {
23 var entity = new QuotedReplyEntity
24 {
25 QuotedReply = new QuotedReplyData
26 {
27 MessageId = "msg-123",
28 SenderId = "user-1",
29 SenderName = "Test User",
30 Preview = "Hello, world!",
31 Time = "1772050244572",
32 IsReplyDeleted = false,
33 ValidatedMessageReference = true
34 }
35 };
36
37 Assert.NotNull(entity.QuotedReply);
38 Assert.Equal("msg-123", entity.QuotedReply.MessageId);
39 Assert.Equal("user-1", entity.QuotedReply.SenderId);
40 Assert.Equal("Test User", entity.QuotedReply.SenderName);
41 Assert.Equal("Hello, world!", entity.QuotedReply.Preview);
42 Assert.Equal("1772050244572", entity.QuotedReply.Time);
43 Assert.False(entity.QuotedReply.IsReplyDeleted);
44 Assert.True(entity.QuotedReply.ValidatedMessageReference);
45 }
46
47 [Fact]
48 public void QuotedReplyEntity_ParameterizedConstructor_SetsMessageId()
49 {
50 var entity = new QuotedReplyEntity("msg-456");
51
52 Assert.Equal("quotedReply", entity.Type);
53 Assert.NotNull(entity.QuotedReply);
54 Assert.Equal("msg-456", entity.QuotedReply.MessageId);
55 }
56
57 [Fact]
58 public void QuotedReplyEntity_MinimalData()
59 {
60 var entity = new QuotedReplyEntity
61 {
62 QuotedReply = new QuotedReplyData { MessageId = "msg-1" }
63 };
64
65 Assert.NotNull(entity.QuotedReply);
66 Assert.Equal("msg-1", entity.QuotedReply.MessageId);
67 Assert.Null(entity.QuotedReply.SenderId);
68 Assert.Null(entity.QuotedReply.SenderName);
69 Assert.Null(entity.QuotedReply.Preview);
70 Assert.Null(entity.QuotedReply.Time);
71 Assert.Null(entity.QuotedReply.IsReplyDeleted);
72 Assert.Null(entity.QuotedReply.ValidatedMessageReference);
73 }
74
75 [Fact]
76 public void Fixture_QuotedReplyEntity_DeserializesFromJson()
77 {
78 string json = """
79 {
80 "type": "message",
81 "entities": [
82 {
83 "type": "quotedReply",
84 "quotedReply": {
85 "messageId": "1772050244572",
86 "senderId": "29:a6cdfb28-56f2-4912-b9c4-2181407c7dde",
87 "senderName": "Centralized Test Bot",
88 "preview": "Reply from bot.",
89 "time": "1772050244572",
90 "validatedMessageReference": true
91 }
92 }
93 ]
94 }
95 """;
96
97 CoreActivity coreActivity = CoreActivity.FromJsonString(json);
98 TeamsActivity activity = TeamsActivity.FromActivity(coreActivity);
99
100 Assert.NotNull(activity.Entities);
101 Assert.Single(activity.Entities);
102
103 var entity = activity.Entities[0] as QuotedReplyEntity;
104 Assert.NotNull(entity);
105 Assert.Equal("quotedReply", entity.Type);
106 Assert.NotNull(entity.QuotedReply);
107 Assert.Equal("1772050244572", entity.QuotedReply.MessageId);
108 Assert.Equal("29:a6cdfb28-56f2-4912-b9c4-2181407c7dde", entity.QuotedReply.SenderId);
109 Assert.Equal("Centralized Test Bot", entity.QuotedReply.SenderName);
110 Assert.Equal("Reply from bot.", entity.QuotedReply.Preview);
111 Assert.Equal("1772050244572", entity.QuotedReply.Time);
112 Assert.True(entity.QuotedReply.ValidatedMessageReference);
113 }
114
115 [Fact]
116 public void Fixture_QuotedReplyEntity_DeserializesMultipleQuotes()
117 {
118 string json = """
119 {
120 "type": "message",
121 "text": "<quoted messageId=\"msg-1\"/> first reply <quoted messageId=\"msg-2\"/> second reply",
122 "entities": [
123 {
124 "type": "quotedReply",
125 "quotedReply": {
126 "messageId": "msg-1",
127 "senderName": "User A",
128 "preview": "First message"
129 }
130 },
131 {
132 "type": "clientInfo",
133 "locale": "en-us"
134 },
135 {
136 "type": "quotedReply",
137 "quotedReply": {
138 "messageId": "msg-2",
139 "senderName": "User B",
140 "preview": "Second message"
141 }
142 }
143 ]
144 }
145 """;
146
147 CoreActivity coreActivity = CoreActivity.FromJsonString(json);
148 TeamsActivity activity = TeamsActivity.FromActivity(coreActivity);
149
150 Assert.NotNull(activity.Entities);
151 Assert.Equal(3, activity.Entities.Count);
152
153 var quotedReplies = activity.GetQuotedMessages().ToList();
154 Assert.Equal(2, quotedReplies.Count);
155 Assert.Equal("msg-1", quotedReplies[0].QuotedReply?.MessageId);
156 Assert.Equal("msg-2", quotedReplies[1].QuotedReply?.MessageId);
157 }
158
159 [Fact]
160 public void Fixture_QuotedReplyEntity_DeletedQuote()
161 {
162 string json = """
163 {
164 "type": "message",
165 "entities": [
166 {
167 "type": "quotedReply",
168 "quotedReply": {
169 "messageId": "deleted-msg-1",
170 "isReplyDeleted": true
171 }
172 }
173 ]
174 }
175 """;
176
177 CoreActivity coreActivity = CoreActivity.FromJsonString(json);
178 TeamsActivity activity = TeamsActivity.FromActivity(coreActivity);
179
180 var entity = activity.Entities?[0] as QuotedReplyEntity;
181 Assert.NotNull(entity);
182 Assert.True(entity.QuotedReply?.IsReplyDeleted);
183 Assert.Null(entity.QuotedReply?.SenderName);
184 Assert.Null(entity.QuotedReply?.Preview);
185 }
186
187 // Extension tests: GetQuotedMessages
188
189 [Fact]
190 public void GetQuotedMessages_FiltersCorrectly()
191 {
192 TeamsActivity activity = TeamsActivity.FromActivity(new CoreActivity(ActivityType.Message));
193 activity.Entities =
194 [
195 new ClientInfoEntity { Locale = "en-us" },
196 new QuotedReplyEntity { QuotedReply = new QuotedReplyData { MessageId = "msg-1" } },
197 new MentionEntity(new ConversationAccount { Id = "user-1", Name = "User" }, "<at>User</at>"),
198 new QuotedReplyEntity { QuotedReply = new QuotedReplyData { MessageId = "msg-2" } }
199 ];
200
201 var quotedReplies = activity.GetQuotedMessages().ToList();
202
203 Assert.Equal(2, quotedReplies.Count);
204 Assert.Equal("msg-1", quotedReplies[0].QuotedReply?.MessageId);
205 Assert.Equal("msg-2", quotedReplies[1].QuotedReply?.MessageId);
206 }
207
208 [Fact]
209 public void GetQuotedMessages_EmptyWhenNoEntities()
210 {
211 TeamsActivity activity = TeamsActivity.FromActivity(new CoreActivity(ActivityType.Message));
212 activity.Entities = null;
213
214 var quotedReplies = activity.GetQuotedMessages().ToList();
215
216 Assert.Empty(quotedReplies);
217 }
218
219 [Fact]
220 public void GetQuotedMessages_EmptyWhenNoQuotedReplyEntities()
221 {
222 TeamsActivity activity = TeamsActivity.FromActivity(new CoreActivity(ActivityType.Message));
223 activity.Entities = [new ClientInfoEntity { Locale = "en-us" }];
224
225 var quotedReplies = activity.GetQuotedMessages().ToList();
226
227 Assert.Empty(quotedReplies);
228 }
229
230 // Extension tests: AddQuote
231
232 [Fact]
233 public void AddQuote_AddsEntityAndPlaceholder()
234 {
235 MessageActivity activity = new("existing text");
236 activity.AddQuote("msg-1");
237
238 Assert.NotNull(activity.Entities);
239 Assert.Single(activity.Entities);
240 Assert.IsType<QuotedReplyEntity>(activity.Entities[0]);
241 var entity = (QuotedReplyEntity)activity.Entities[0];
242 Assert.Equal("msg-1", entity.QuotedReply?.MessageId);
243 Assert.Equal("existing text<quoted messageId=\"msg-1\"/>", activity.Text);
244 }
245
246 [Fact]
247 public void AddQuote_WithResponse_AppendsResponseText()
248 {
249 MessageActivity activity = new();
250 activity.AddQuote("msg-1", "my response");
251
252 Assert.Equal("<quoted messageId=\"msg-1\"/> my response", activity.Text);
253 }
254
255 [Fact]
256 public void AddQuote_MultiQuoteInterleaved()
257 {
258 MessageActivity activity = new();
259 activity.AddQuote("msg-1", "response to first");
260 activity.AddQuote("msg-2", "response to second");
261
262 Assert.Equal(
263 "<quoted messageId=\"msg-1\"/> response to first<quoted messageId=\"msg-2\"/> response to second",
264 activity.Text);
265 Assert.Equal(2, activity.Entities!.Count);
266 }
267
268 [Fact]
269 public void AddQuote_GroupedQuotes()
270 {
271 MessageActivity activity = new();
272 activity.AddQuote("msg-1");
273 activity.AddQuote("msg-2", "response to both");
274
275 Assert.Equal(
276 "<quoted messageId=\"msg-1\"/><quoted messageId=\"msg-2\"/> response to both",
277 activity.Text);
278 }
279
280 [Fact]
281 public void AddQuote_EmptyActivity()
282 {
283 MessageActivity activity = new();
284 activity.AddQuote("msg-1");
285
286 Assert.Equal("<quoted messageId=\"msg-1\"/>", activity.Text);
287 Assert.Single(activity.Entities!);
288 }
289
290 // Builder tests: WithQuote
291
292 [Fact]
293 public void Builder_WithQuote_AddsEntityAndPlaceholder()
294 {
295 TeamsActivity activity = TeamsActivity.CreateBuilder()
296 .AddQuote("msg-1")
297 .Build();
298
299 Assert.NotNull(activity.Entities);
300 Assert.Single(activity.Entities);
301 Assert.IsType<QuotedReplyEntity>(activity.Entities[0]);
302
303 // Check text via Properties (builder stores text there)
304 Assert.True(activity.Properties.TryGetValue("text", out object? text));
305 Assert.Equal("<quoted messageId=\"msg-1\"/>", text?.ToString());
306 }
307
308 [Fact]
309 public void Builder_WithQuote_WithResponse()
310 {
311 TeamsActivity activity = TeamsActivity.CreateBuilder()
312 .AddQuote("msg-1", "my response")
313 .Build();
314
315 Assert.True(activity.Properties.TryGetValue("text", out object? text));
316 Assert.Equal("<quoted messageId=\"msg-1\"/> my response", text?.ToString());
317 }
318
319 [Fact]
320 public void AddQuote_ToJson_ContainsQuotedReplyData()
321 {
322 MessageActivity activity = new("hello");
323 activity.AddQuote("msg-123", "my response");
324
325 string json = activity.ToJson();
326 Assert.Contains("\"quotedReply\"", json);
327 Assert.Contains("msg-123", json);
328 Assert.Contains("messageId", json);
329 }
330
331 // Extension tests: PrependQuote
332
333 [Fact]
334 public void PrependQuote_EmptyText_SetsPlaceholderOnly()
335 {
336 MessageActivity activity = new();
337 activity.PrependQuote("msg-1");
338
339 Assert.Equal("<quoted messageId=\"msg-1\"/>", activity.Text);
340 Assert.Single(activity.Entities!);
341 }
342
343 [Fact]
344 public void PrependQuote_NonEmptyText_PrependsPlaceholderWithSpace()
345 {
346 MessageActivity activity = new("hello world");
347 activity.PrependQuote("msg-1");
348
349 Assert.Equal("<quoted messageId=\"msg-1\"/> hello world", activity.Text);
350 }
351
352 [Fact]
353 public void PrependQuote_TrimsExistingText()
354 {
355 MessageActivity activity = new(" hello ");
356 activity.PrependQuote("msg-1");
357
358 Assert.Equal("<quoted messageId=\"msg-1\"/> hello", activity.Text);
359 }
360
361 [Fact]
362 public void PrependQuote_InsertsEntityAtIndexZero()
363 {
364 MessageActivity activity = new("existing");
365 activity.Entities = [new ClientInfoEntity { Locale = "en-us" }];
366
367 activity.PrependQuote("msg-1");
368
369 Assert.Equal(2, activity.Entities.Count);
370 Assert.IsType<QuotedReplyEntity>(activity.Entities[0]);
371 Assert.IsType<ClientInfoEntity>(activity.Entities[1]);
372 Assert.Equal("msg-1", ((QuotedReplyEntity)activity.Entities[0]).QuotedReply?.MessageId);
373 }
374
375 // Escaping tests
376
377 [Fact]
378 public void AddQuote_EscapesSpecialCharsInPlaceholder()
379 {
380 MessageActivity activity = new();
381 activity.AddQuote("msg<\"&>1");
382
383 // Placeholder uses XML-escaped attribute value; entity carries raw id
384 Assert.Equal("<quoted messageId=\"msg&lt;&quot;&amp;&gt;1\"/>", activity.Text);
385 var entity = (QuotedReplyEntity)activity.Entities![0];
386 Assert.Equal("msg<\"&>1", entity.QuotedReply?.MessageId);
387 }
388
389 [Fact]
390 public void Builder_WithQuote_EscapesSpecialCharsInPlaceholder()
391 {
392 TeamsActivity activity = TeamsActivity.CreateBuilder()
393 .AddQuote("a\"b")
394 .Build();
395
396 Assert.True(activity.Properties.TryGetValue("text", out object? text));
397 Assert.Equal("<quoted messageId=\"a&quot;b\"/>", text?.ToString());
398 }
399
400 [Fact]
401 public void Builder_WithQuote_MultipleQuotes()
402 {
403 TeamsActivity activity = TeamsActivity.CreateBuilder()
404 .AddQuote("msg-1", "first response")
405 .AddQuote("msg-2", "second response")
406 .Build();
407
408 Assert.NotNull(activity.Entities);
409 Assert.Equal(2, activity.Entities.Count);
410
411 Assert.True(activity.Properties.TryGetValue("text", out object? text));
412 Assert.Equal(
413 "<quoted messageId=\"msg-1\"/> first response<quoted messageId=\"msg-2\"/> second response",
414 text?.ToString());
415 }
416}
417#pragma warning restore ExperimentalTeamsQuotedReplies
418