// Copyright (c) Microsoft Corporation. // Licensed under the MIT License. using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Teams.Core.Hosting; namespace Microsoft.Teams.Core.UnitTests.Hosting; public class BotConfigTests { private static ServiceCollection BuildServices(Dictionary configData) { IConfigurationRoot configuration = new ConfigurationBuilder() .AddInMemoryCollection(configData) .Build(); ServiceCollection services = new(); services.AddSingleton(configuration); services.AddLogging(); return services; } [Fact] public void Resolve_OpenIdMetadataUrl_DefaultsToPublicCloud_WhenNotConfigured() { ServiceCollection services = BuildServices(new Dictionary { ["AzureAd:ClientId"] = "client-id", ["AzureAd:TenantId"] = "tenant-id", }); BotConfig config = BotConfig.Resolve(services); Assert.Equal("https://login.botframework.com/v1/.well-known/openid-configuration", config.OpenIdMetadataUrl); } [Fact] public void Resolve_EntraInstance_DefaultsToPublicCloud_WhenNotConfigured() { ServiceCollection services = BuildServices(new Dictionary { ["AzureAd:ClientId"] = "client-id", ["AzureAd:TenantId"] = "tenant-id", }); BotConfig config = BotConfig.Resolve(services); Assert.Equal("https://login.microsoftonline.com/", config.EntraInstance); } [Theory] [InlineData("https://login.botframework.azure.us/v1/.well-known/openid-configuration")] [InlineData("https://login.botframework.azure.cn/v1/.well-known/openid-configuration")] public void Resolve_OpenIdMetadataUrl_HonorsBotFrameworkOverride(string configured) { ServiceCollection services = BuildServices(new Dictionary { ["AzureAd:ClientId"] = "client-id", ["AzureAd:TenantId"] = "tenant-id", ["BotFramework:OpenIdMetadataUrl"] = configured, }); BotConfig config = BotConfig.Resolve(services); Assert.Equal(configured, config.OpenIdMetadataUrl); } [Theory] [InlineData("https://login.microsoftonline.us/")] [InlineData("https://login.partner.microsoftonline.cn/")] public void Resolve_EntraInstance_HonorsAzureAdInstanceOverride(string configured) { ServiceCollection services = BuildServices(new Dictionary { ["AzureAd:ClientId"] = "client-id", ["AzureAd:TenantId"] = "tenant-id", ["AzureAd:Instance"] = configured, }); BotConfig config = BotConfig.Resolve(services); Assert.Equal(configured, config.EntraInstance); } [Fact] public void Resolve_BotTokenIssuer_DefaultsToPublicCloud_WhenNotConfigured() { ServiceCollection services = BuildServices(new Dictionary { ["AzureAd:ClientId"] = "client-id", ["AzureAd:TenantId"] = "tenant-id", }); BotConfig config = BotConfig.Resolve(services); Assert.Equal("https://api.botframework.com", config.BotTokenIssuer); } [Theory] [InlineData("https://api.botframework.us")] [InlineData("https://api.botframework.azure.cn")] public void Resolve_BotTokenIssuer_HonorsBotFrameworkOverride(string configured) { ServiceCollection services = BuildServices(new Dictionary { ["AzureAd:ClientId"] = "client-id", ["AzureAd:TenantId"] = "tenant-id", ["BotFramework:BotTokenIssuer"] = configured, }); BotConfig config = BotConfig.Resolve(services); Assert.Equal(configured, config.BotTokenIssuer); } [Fact] public void Resolve_BotFrameworkSection_IsIndependentOfAzureAdSectionName() { ServiceCollection services = BuildServices(new Dictionary { ["CustomAuth:ClientId"] = "client-id", ["BotFramework:OpenIdMetadataUrl"] = "https://login.botframework.azure.us/v1/.well-known/openid-configuration", ["BotFramework:BotTokenIssuer"] = "https://api.botframework.us", }); BotConfig config = BotConfig.Resolve(services, "CustomAuth"); Assert.Equal("https://login.botframework.azure.us/v1/.well-known/openid-configuration", config.OpenIdMetadataUrl); Assert.Equal("https://api.botframework.us", config.BotTokenIssuer); } [Fact] public void Resolve_ThrowsInvalidOperationException_WhenOpenIdMetadataUrlIsNotAbsoluteUri() { ServiceCollection services = BuildServices(new Dictionary { ["AzureAd:ClientId"] = "client-id", ["BotFramework:OpenIdMetadataUrl"] = "not-a-uri", }); InvalidOperationException ex = Assert.Throws(() => BotConfig.Resolve(services)); Assert.Contains("BotFramework:OpenIdMetadataUrl", ex.Message, StringComparison.Ordinal); } [Fact] public void Resolve_ThrowsInvalidOperationException_WhenBotTokenIssuerIsNotAbsoluteUri() { ServiceCollection services = BuildServices(new Dictionary { ["AzureAd:ClientId"] = "client-id", ["BotFramework:BotTokenIssuer"] = "not a uri", }); InvalidOperationException ex = Assert.Throws(() => BotConfig.Resolve(services)); Assert.Contains("BotFramework:BotTokenIssuer", ex.Message, StringComparison.Ordinal); } [Fact] public void Resolve_ThrowsInvalidOperationException_WhenInstanceIsNotAbsoluteUri() { ServiceCollection services = BuildServices(new Dictionary { ["AzureAd:ClientId"] = "client-id", ["AzureAd:Instance"] = "login.microsoftonline.us", }); InvalidOperationException ex = Assert.Throws(() => BotConfig.Resolve(services)); Assert.Contains("AzureAd:Instance", ex.Message, StringComparison.Ordinal); } [Fact] public void Resolve_DoesNotThrow_WhenOverridesAreValidAbsoluteUris() { ServiceCollection services = BuildServices(new Dictionary { ["AzureAd:ClientId"] = "client-id", ["AzureAd:Instance"] = "https://login.microsoftonline.us/", ["BotFramework:OpenIdMetadataUrl"] = "https://login.botframework.azure.us/v1/.well-known/openid-configuration", ["BotFramework:BotTokenIssuer"] = "https://api.botframework.us", }); Exception? caught = Record.Exception(() => BotConfig.Resolve(services)); Assert.Null(caught); } }