microsoft/teams.net

Public

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

CodeCommitsIssuesPull requestsActionsInsightsSecurity
docs/update-release-process

Branches

Tags

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

Clone

HTTPS

Download ZIP

core/test/IntegrationTests/IntegrationTestFixture.cs

196lines · modecode

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4using System.Linq;
5using MartinCostello.Logging.XUnit;
6using Microsoft.Extensions.Configuration;
7using Microsoft.Extensions.DependencyInjection;
8using Microsoft.Extensions.Logging;
9using Microsoft.Teams.Apps;
10using Microsoft.Teams.Apps.Api.Clients;
11using Microsoft.Teams.Apps.Schema;
12using Microsoft.Teams.Core;
13using Microsoft.Teams.Core.Schema;
14using Xunit.Abstractions;
15
16namespace IntegrationTests;
17
18/// <summary>
19/// Shared fixture that configures DI, acquires tokens, and exposes clients for integration tests.
20/// Reused across test classes via IClassFixture to avoid repeated token acquisition.
21/// </summary>
22public class IntegrationTestFixture : IAsyncLifetime, IDisposable, ITestOutputHelperAccessor
23{
24 public ServiceProvider ServiceProvider { get; }
25 public ConversationClient ConversationClient { get; }
26 public ApiClient ApiClient { get; }
27
28 public Uri ServiceUrl { get; }
29 public string ConversationId { get; }
30 public string UserId { get; }
31 public string TeamId { get; }
32 public string ChannelId { get; }
33 public string MeetingId { get; }
34 public string TenantId { get; }
35 public string BotAppId { get; }
36 public string? UserId2 { get; }
37 public AgenticIdentity? AgenticIdentity { get; }
38
39 /// <summary>
40 /// True when running against the canary service endpoint.
41 /// </summary>
42 public bool IsCanary => ServiceUrl.Host.Contains("canary", StringComparison.OrdinalIgnoreCase);
43
44 /// <summary>
45 /// Cached conversation members — fetched once during InitializeAsync to avoid
46 /// repeated /members calls that trigger throttling (429).
47 /// </summary>
48 public IList<TeamsChannelAccount?>? CachedMembers { get; private set; }
49
50 /// <summary>
51 /// First member MRI from cache (convenience for tests needing a valid member ID).
52 /// </summary>
53 public string? MemberMri1 => CachedMembers?.FirstOrDefault()?.Id;
54
55 /// <summary>
56 /// Second member MRI from cache (for group chat tests requiring 2+ members).
57 /// </summary>
58 public string? MemberMri2 => CachedMembers?.Skip(1).FirstOrDefault()?.Id;
59
60 /// <summary>
61 /// Third member MRI from cache.
62 /// </summary>
63 public string? MemberMri3 => CachedMembers?.Skip(2).FirstOrDefault()?.Id;
64
65 /// <summary>
66 /// Set by each test class constructor to route ILogger output to xUnit's test output.
67 /// </summary>
68 public ITestOutputHelper? OutputHelper { get; set; }
69
70 public IntegrationTestFixture()
71 {
72 IConfiguration configuration = new ConfigurationBuilder()
73 .SetBasePath(AppDomain.CurrentDomain.BaseDirectory)
74 .AddEnvironmentVariables()
75 .Build();
76
77 ServiceCollection services = new();
78 services.AddLogging(builder =>
79 {
80 builder.AddXUnit(this);
81 builder.AddFilter("System.Net", LogLevel.Warning);
82 builder.AddFilter("Microsoft.Identity", LogLevel.Error);
83 builder.AddFilter("Microsoft.Teams", LogLevel.Information);
84 });
85 services.AddSingleton(configuration);
86 services.AddTeamsBotApplication();
87
88 ServiceProvider = services.BuildServiceProvider();
89 ConversationClient = ServiceProvider.GetRequiredService<ConversationClient>();
90 ApiClient = ServiceProvider.GetRequiredService<ApiClient>();
91
92 ServiceUrl = new Uri(Env("TEST_SERVICEURL", "https://smba.trafficmanager.net/teams/"));
93 ConversationId = Env("TEST_CONVERSATIONID");
94 UserId = Env("TEST_USER_ID");
95 TeamId = Env("TEST_TEAMID");
96 ChannelId = Env("TEST_CHANNELID");
97 MeetingId = Env("TEST_MEETINGID");
98 TenantId = Env("TEST_TENANTID");
99 BotAppId = Env("AzureAd__ClientId");
100 UserId2 = Environment.GetEnvironmentVariable("TEST_USER_ID_2");
101
102 string? agenticAppId = Environment.GetEnvironmentVariable("TEST_AGENTIC_APPID");
103 string? agenticUserId = Environment.GetEnvironmentVariable("TEST_AGENTIC_USERID");
104
105 if (!string.IsNullOrEmpty(agenticAppId) && !string.IsNullOrEmpty(agenticUserId))
106 {
107 string appBlueprintId = Env("AzureAd__ClientId");
108 ChannelAccount recipient = new()
109 {
110 AgenticAppBlueprintId = appBlueprintId,
111 AgenticAppId = agenticAppId,
112 AgenticUserId = agenticUserId
113 };
114 AgenticIdentity = AgenticIdentity.FromAccount(recipient);
115 }
116 }
117
118 /// <summary>
119 /// Fetches and caches conversation members once for the entire test run.
120 /// Filters out the bot itself and null entries. Fails fast if no usable members are found.
121 /// </summary>
122 public async Task InitializeAsync()
123 {
124 ApiClient scoped = ScopedApiClient;
125 IList<TeamsChannelAccount?> raw = await scoped.Conversations.Members.GetAsync(ConversationId, AgenticIdentity);
126
127 string botMri = $"28:{BotAppId}";
128 CachedMembers = raw
129 .Where(m => m?.Id is not null && !m.Id.Equals(botMri, StringComparison.OrdinalIgnoreCase))
130 .ToList();
131
132 if (CachedMembers.Count == 0)
133 {
134 throw new InvalidOperationException(
135 "No usable members found in test conversation (all null or bot-only). " +
136 "Ensure the conversation has at least 2 non-bot members.");
137 }
138 }
139
140 public Task DisposeAsync() => Task.CompletedTask;
141
142 public ApiClient ScopedApiClient => ApiClient.ForServiceUrl(ServiceUrl);
143
144 public void Dispose()
145 {
146 ServiceProvider.Dispose();
147 GC.SuppressFinalize(this);
148 }
149
150 private static string Env(string name, string? fallback = null) =>
151 Environment.GetEnvironmentVariable(name)
152 ?? fallback
153 ?? throw new InvalidOperationException($"{name} environment variable not set");
154
155 internal static ChannelAccount GetChannelAccountWithAgenticProperties()
156 {
157 string agenticUserId = Env("TEST_AGENTIC_USERID");
158 string agenticAppId = Env("TEST_AGENTIC_APPID");
159 string agenticAppBlueprintId = Env("AzureAd__ClientId");
160
161 if (string.IsNullOrEmpty(agenticUserId))
162 {
163 return new ChannelAccount();
164 }
165
166 ChannelAccount account = new()
167 {
168 Id = agenticUserId,
169 Name = "Agentic User",
170 AgenticAppBlueprintId = agenticAppBlueprintId,
171 AgenticAppId = agenticAppId,
172 AgenticUserId = agenticUserId
173 };
174 return account;
175 }
176
177 internal static AgenticIdentity GetAgenticIdentity()
178 {
179 string agenticUserId = Env("TEST_AGENTIC_USERID");
180 string agenticAppId = Env("TEST_AGENTIC_APPID");
181 string agenticAppBlueprintId = Env("AzureAd__ClientId");
182
183 if (string.IsNullOrEmpty(agenticUserId))
184 {
185 return null!;
186 }
187
188 AgenticIdentity identity = new()
189 {
190 AgenticUserId = agenticUserId,
191 AgenticAppId = agenticAppId,
192 AgenticAppBlueprintId = agenticAppBlueprintId
193 };
194 return identity;
195 }
196}
197