microsoft/teams.net

Public

mirrored fromhttps://github.com/microsoft/teams.netAvailable

CodeCommitsIssuesPull requestsActionsInsightsSecurity
next/core-compat-sso-middleware

Branches

Tags

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

Clone

HTTPS

Download ZIP

core/test/IntegrationTests/CreateConversationDiagnosticTests.cs

332lines · modecode

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4using System.Text.Json;
5using Microsoft.Extensions.DependencyInjection;
6using Microsoft.Teams.Bot.Core;
7using Microsoft.Teams.Bot.Core.Schema;
8using Xunit.Abstractions;
9
10namespace IntegrationTests;
11
12/// <summary>
13/// Diagnostic tests exploring CreateConversation parameter combinations.
14/// These tests document what the Teams Bot Framework API accepts and rejects,
15/// capturing full request/response details including headers.
16/// </summary>
17public class CreateConversationDiagnosticTests : IClassFixture<IntegrationTestFixture>
18{
19 private static readonly JsonSerializerOptions JsonOpts = new()
20 {
21 PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
22 WriteIndented = true,
23 DefaultIgnoreCondition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingNull
24 };
25
26 private readonly IntegrationTestFixture _f;
27 private readonly ITestOutputHelper _output;
28
29 public CreateConversationDiagnosticTests(IntegrationTestFixture fixture, ITestOutputHelper output)
30 {
31 _f = fixture;
32 _f.OutputHelper = output;
33 _output = output;
34 }
35
36 private async Task<(string first, string? second, string? third)> GetMemberMrisAsync()
37 {
38 IList<ConversationAccount> members = await _f.ConversationClient.GetConversationMembersAsync(
39 _f.ConversationId, _f.ServiceUrl, _f.AgenticIdentity);
40 return (
41 members[0].Id!,
42 members.Count >= 2 ? members[1].Id : null,
43 members.Count >= 3 ? members[2].Id : null
44 );
45 }
46
47 /// <summary>
48 /// Sends a CreateConversation request using a raw HttpClient to capture full request/response details.
49 /// </summary>
50 private async Task<DiagnosticResult> SendDiagnosticRequestAsync(string label, ConversationParameters parameters)
51 {
52 string url = $"{_f.ServiceUrl.ToString().TrimEnd('/')}/v3/conversations";
53 string requestBody = JsonSerializer.Serialize(parameters, JsonOpts);
54
55 // Use the DI-configured HttpClient (has BotAuthenticationHandler for token)
56 HttpClient httpClient = _f.ServiceProvider.GetRequiredService<IHttpClientFactory>()
57 .CreateClient("BotConversationClient");
58
59 using HttpRequestMessage request = new(HttpMethod.Post, url);
60 request.Content = new StringContent(requestBody, System.Text.Encoding.UTF8, "application/json");
61
62 _output.WriteLine($"=== {label} ===");
63 _output.WriteLine($"POST {url}");
64 _output.WriteLine($"Request body:\n{requestBody}");
65
66 using HttpResponseMessage response = await httpClient.SendAsync(request);
67
68 string responseBody = await response.Content.ReadAsStringAsync();
69
70 _output.WriteLine($"\nHTTP {(int)response.StatusCode} {response.StatusCode}");
71
72 _output.WriteLine("\nResponse headers:");
73 foreach (var header in response.Headers)
74 {
75 _output.WriteLine($" {header.Key}: {string.Join(", ", header.Value)}");
76 }
77 foreach (var header in response.Content.Headers)
78 {
79 _output.WriteLine($" {header.Key}: {string.Join(", ", header.Value)}");
80 }
81
82 // Pretty-print JSON response
83 try
84 {
85 var parsed = JsonSerializer.Deserialize<JsonElement>(responseBody);
86
87 string pretty = JsonSerializer.Serialize(parsed, JsonOpts);
88 _output.WriteLine($"\nResponse body:\n{pretty}");
89 }
90 catch
91 {
92 _output.WriteLine($"\nResponse body:\n{responseBody}");
93 }
94
95 _output.WriteLine("");
96
97 return new DiagnosticResult
98 {
99 Label = label,
100 StatusCode = (int)response.StatusCode,
101 RequestBody = requestBody,
102 ResponseBody = responseBody,
103 ResponseHeaders = response.Headers.ToDictionary(h => h.Key, h => string.Join(", ", h.Value))
104 };
105 }
106
107 private record DiagnosticResult
108 {
109 public required string Label { get; init; }
110 public required int StatusCode { get; init; }
111 public required string RequestBody { get; init; }
112 public required string ResponseBody { get; init; }
113 public required Dictionary<string, string> ResponseHeaders { get; init; }
114 }
115
116 // =========================================================================
117 // 1:1 personal chat — baseline (known working)
118 // =========================================================================
119
120 [Fact]
121 public async Task PersonalChat_MinimalParams()
122 {
123 (string memberMri, _, _) = await GetMemberMrisAsync();
124 DiagnosticResult result = await SendDiagnosticRequestAsync("1:1 Personal Chat (minimal)", new()
125 {
126 IsGroup = false,
127 Members = [new() { Id = memberMri }],
128 TenantId = _f.TenantId
129 });
130 Assert.True(result.StatusCode is 200 or 201, $"Expected 2xx, got {result.StatusCode}");
131 }
132
133 [Fact]
134 public async Task PersonalChat_WithBot()
135 {
136 (string memberMri, _, _) = await GetMemberMrisAsync();
137 DiagnosticResult result = await SendDiagnosticRequestAsync("1:1 Personal Chat (with bot)", new()
138 {
139 IsGroup = false,
140 Bot = new() { Id = $"28:{_f.BotAppId}" },
141 Members = [new() { Id = memberMri }],
142 TenantId = _f.TenantId
143 });
144 Assert.True(result.StatusCode is 200 or 201, $"Expected 2xx, got {result.StatusCode}");
145 }
146
147 [Fact]
148 public async Task PersonalChat_WithInitialActivity()
149 {
150 (string memberMri, _, _) = await GetMemberMrisAsync();
151 DiagnosticResult result = await SendDiagnosticRequestAsync("1:1 Personal Chat (with activity)", new()
152 {
153 IsGroup = false,
154 Members = [new() { Id = memberMri }],
155 TenantId = _f.TenantId,
156 Activity = new CoreActivity
157 {
158 Type = ActivityType.Message,
159 Properties = { { "text", "[Diagnostic] 1:1 with initial activity" } }
160 }
161 });
162 Assert.True(result.StatusCode is 200 or 201, $"Expected 2xx, got {result.StatusCode}");
163 }
164
165 // =========================================================================
166 // Group chat variations
167 // =========================================================================
168
169 [Fact]
170 public async Task GroupChat_TwoMembers_NoBotNoChannelData()
171 {
172 (string first, string? second, _) = await GetMemberMrisAsync();
173 Assert.NotNull(second);
174 DiagnosticResult result = await SendDiagnosticRequestAsync("Group Chat: 2 members, no bot, no channelData", new()
175 {
176 IsGroup = true,
177 Members = [new() { Id = first }, new() { Id = second! }],
178 TenantId = _f.TenantId
179 });
180 Assert.Equal(400, result.StatusCode);
181 }
182
183 [Fact]
184 public async Task GroupChat_TwoMembers_WithBot()
185 {
186 (string first, string? second, _) = await GetMemberMrisAsync();
187 Assert.NotNull(second);
188 DiagnosticResult result = await SendDiagnosticRequestAsync("Group Chat: 2 members, bot=28:appId", new()
189 {
190 IsGroup = true,
191 Bot = new() { Id = $"28:{_f.BotAppId}" },
192 Members = [new() { Id = first }, new() { Id = second! }],
193 TenantId = _f.TenantId
194 });
195 Assert.Equal(400, result.StatusCode);
196 }
197
198 [Fact]
199 public async Task GroupChat_TwoMembers_WithBotAndChannelData()
200 {
201 (string first, string? second, _) = await GetMemberMrisAsync();
202 Assert.NotNull(second);
203 DiagnosticResult result = await SendDiagnosticRequestAsync("Group Chat: 2 members, bot, channelData.tenant", new()
204 {
205 IsGroup = true,
206 Bot = new() { Id = $"28:{_f.BotAppId}" },
207 Members = [new() { Id = first }, new() { Id = second! }],
208 TenantId = _f.TenantId,
209 ChannelData = new { tenant = new { id = _f.TenantId } }
210 });
211 Assert.Equal(400, result.StatusCode);
212 }
213
214 [Fact]
215 public async Task GroupChat_TwoMembers_WithTopicAndActivity()
216 {
217 (string first, string? second, _) = await GetMemberMrisAsync();
218 Assert.NotNull(second);
219 DiagnosticResult result = await SendDiagnosticRequestAsync("Group Chat: 2 members, bot, topic, activity, channelData", new()
220 {
221 IsGroup = true,
222 Bot = new() { Id = $"28:{_f.BotAppId}" },
223 Members = [new() { Id = first }, new() { Id = second! }],
224 TenantId = _f.TenantId,
225 TopicName = "Diagnostic group test",
226 ChannelData = new { tenant = new { id = _f.TenantId } },
227 Activity = new CoreActivity
228 {
229 Type = ActivityType.Message,
230 Properties = { { "text", "group chat init" } }
231 }
232 });
233 Assert.Equal(400, result.StatusCode);
234 }
235
236 [Fact]
237 public async Task GroupChat_OneMember_IsGroupTrue()
238 {
239 (string memberMri, _, _) = await GetMemberMrisAsync();
240 DiagnosticResult result = await SendDiagnosticRequestAsync("Group Chat: 1 member, isGroup=true", new()
241 {
242 IsGroup = true,
243 Members = [new() { Id = memberMri }],
244 TenantId = _f.TenantId
245 });
246 Assert.Equal(400, result.StatusCode);
247 }
248
249 [Fact]
250 public async Task GroupChat_OneMember_WithBot()
251 {
252 (string memberMri, _, _) = await GetMemberMrisAsync();
253 DiagnosticResult result = await SendDiagnosticRequestAsync("Group Chat: 1 member, bot, channelData.tenant", new()
254 {
255 IsGroup = true,
256 Bot = new() { Id = $"28:{_f.BotAppId}" },
257 Members = [new() { Id = memberMri }],
258 TenantId = _f.TenantId,
259 ChannelData = new { tenant = new { id = _f.TenantId } }
260 });
261 Assert.Equal(400, result.StatusCode);
262 }
263
264 [Fact]
265 public async Task GroupChat_ThreeMembers()
266 {
267 (string first, string? second, string? third) = await GetMemberMrisAsync();
268 Assert.NotNull(second);
269 Assert.NotNull(third);
270 DiagnosticResult result = await SendDiagnosticRequestAsync("Group Chat: 3 members, bot", new()
271 {
272 IsGroup = true,
273 Bot = new() { Id = $"28:{_f.BotAppId}" },
274 Members = [new() { Id = first }, new() { Id = second! }, new() { Id = third! }],
275 TenantId = _f.TenantId,
276 ChannelData = new { tenant = new { id = _f.TenantId } }
277 });
278 Assert.Equal(400, result.StatusCode);
279 }
280
281 // =========================================================================
282 // Channel thread variations
283 // =========================================================================
284
285 [Fact]
286 public async Task ChannelThread_WithActivity()
287 {
288 DiagnosticResult result = await SendDiagnosticRequestAsync("Channel Thread: with activity", new()
289 {
290 IsGroup = true,
291 ChannelData = new { channel = new { id = _f.ChannelId } },
292 Activity = new CoreActivity
293 {
294 Type = ActivityType.Message,
295 Properties = { { "text", "[Diagnostic] channel thread" } }
296 },
297 TenantId = _f.TenantId
298 });
299 Assert.True(result.StatusCode is 200 or 201, $"Expected 2xx, got {result.StatusCode}");
300 }
301
302 [Fact]
303 public async Task ChannelThread_NoActivity()
304 {
305 DiagnosticResult result = await SendDiagnosticRequestAsync("Channel Thread: without activity", new()
306 {
307 IsGroup = true,
308 ChannelData = new { channel = new { id = _f.ChannelId } },
309 TenantId = _f.TenantId
310 });
311 Assert.Equal(400, result.StatusCode);
312 }
313
314 [Fact]
315 public async Task ChannelThread_WithMembersAndActivity()
316 {
317 (string memberMri, _, _) = await GetMemberMrisAsync();
318 DiagnosticResult result = await SendDiagnosticRequestAsync("Channel Thread: with members and activity", new()
319 {
320 IsGroup = true,
321 Members = [new() { Id = memberMri }],
322 ChannelData = new { channel = new { id = _f.ChannelId } },
323 Activity = new CoreActivity
324 {
325 Type = ActivityType.Message,
326 Properties = { { "text", "[Diagnostic] channel thread with members" } }
327 },
328 TenantId = _f.TenantId
329 });
330 Assert.True(result.StatusCode is 200 or 201, $"Expected 2xx, got {result.StatusCode}");
331 }
332}
333