diff --git a/core/Azure.Mcp.Core/tests/Azure.Mcp.Core.UnitTests/Areas/Server/CommandFactoryHelpers.cs b/core/Azure.Mcp.Core/tests/Azure.Mcp.Core.UnitTests/Areas/Server/CommandFactoryHelpers.cs index 9f825d872b..58865ffda0 100644 --- a/core/Azure.Mcp.Core/tests/Azure.Mcp.Core.UnitTests/Areas/Server/CommandFactoryHelpers.cs +++ b/core/Azure.Mcp.Core/tests/Azure.Mcp.Core.UnitTests/Areas/Server/CommandFactoryHelpers.cs @@ -12,6 +12,7 @@ using Azure.Mcp.Tools.Advisor; using Azure.Mcp.Tools.Aks; using Azure.Mcp.Tools.AppConfig; +using Azure.Mcp.Tools.AppService; using Azure.Mcp.Tools.AppLens; using Azure.Mcp.Tools.Authorization; using Azure.Mcp.Tools.AzureBestPractices; @@ -67,6 +68,7 @@ public static ICommandFactory CreateCommandFactory(IServiceProvider? serviceProv new AdvisorSetup(), new AksSetup(), new AppConfigSetup(), + new AppServiceSetup(), new AppLensSetup(), new AuthorizationSetup(), new AzureBestPracticesSetup(), @@ -132,6 +134,7 @@ public static IServiceCollection SetupCommonServices() new AdvisorSetup(), new AksSetup(), new AppConfigSetup(), + new AppServiceSetup(), new AppLensSetup(), new AuthorizationSetup(), new AzureBestPracticesSetup(), diff --git a/servers/Azure.Mcp.Server/changelog-entries/1772482309825.yaml b/servers/Azure.Mcp.Server/changelog-entries/1772482309825.yaml new file mode 100644 index 0000000000..a40d03a64e --- /dev/null +++ b/servers/Azure.Mcp.Server/changelog-entries/1772482309825.yaml @@ -0,0 +1,4 @@ +pr: 1900 +changes: + - section: "Other Changes" + description: "AppService: Improve testability by removing dependency on CommandContext.ServiceProvider in ExecuteAsync" diff --git a/tools/Azure.Mcp.Tools.AppService/src/Commands/Database/DatabaseAddCommand.cs b/tools/Azure.Mcp.Tools.AppService/src/Commands/Database/DatabaseAddCommand.cs index 0ae5baf574..26926fa5fe 100644 --- a/tools/Azure.Mcp.Tools.AppService/src/Commands/Database/DatabaseAddCommand.cs +++ b/tools/Azure.Mcp.Tools.AppService/src/Commands/Database/DatabaseAddCommand.cs @@ -11,11 +11,12 @@ namespace Azure.Mcp.Tools.AppService.Commands.Database; -public sealed class DatabaseAddCommand(ILogger logger) +public sealed class DatabaseAddCommand(ILogger logger, IAppServiceService appServiceService) : BaseAppServiceCommand(resourceGroupRequired: true, appRequired: true) { private const string CommandTitle = "Add Database to App Service"; private readonly ILogger _logger = logger; + private readonly IAppServiceService _appServiceService = appServiceService; public override string Id => "14be1264-82c8-4a4c-8271-7cfe1fbebbc8"; @@ -73,8 +74,7 @@ public override async Task ExecuteAsync(CommandContext context, { context.Activity?.AddTag("subscription", options.Subscription); - var appServiceService = context.GetService(); - var connectionInfo = await appServiceService.AddDatabaseAsync( + var connectionInfo = await _appServiceService.AddDatabaseAsync( options.AppName!, options.ResourceGroup!, options.DatabaseType!, diff --git a/tools/Azure.Mcp.Tools.AppService/src/Commands/Webapp/Settings/AppSettingsGetCommand.cs b/tools/Azure.Mcp.Tools.AppService/src/Commands/Webapp/Settings/AppSettingsGetCommand.cs index cfe5ec299a..af6c300fe3 100644 --- a/tools/Azure.Mcp.Tools.AppService/src/Commands/Webapp/Settings/AppSettingsGetCommand.cs +++ b/tools/Azure.Mcp.Tools.AppService/src/Commands/Webapp/Settings/AppSettingsGetCommand.cs @@ -9,11 +9,12 @@ namespace Azure.Mcp.Tools.AppService.Commands.Webapp.Settings; -public sealed class AppSettingsGetCommand(ILogger logger) +public sealed class AppSettingsGetCommand(ILogger logger, IAppServiceService appServiceService) : BaseAppServiceCommand(resourceGroupRequired: true, appRequired: true) { private const string CommandTitle = "Gets Azure App Service Web App Application Settings"; private readonly ILogger _logger = logger; + private readonly IAppServiceService _appServiceService = appServiceService; public override string Id => "825ef21f-392f-4cd4-8272-7e7dce12e293"; public override string Name => "get-appsettings"; @@ -53,8 +54,7 @@ public override async Task ExecuteAsync(CommandContext context, { context.Activity?.AddTag("subscription", options.Subscription); - var appServiceService = context.GetService(); - var appSettings = await appServiceService.GetAppSettingsAsync( + var appSettings = await _appServiceService.GetAppSettingsAsync( options.Subscription!, options.ResourceGroup!, options.AppName!, diff --git a/tools/Azure.Mcp.Tools.AppService/src/Commands/Webapp/Settings/AppSettingsUpdateCommand.cs b/tools/Azure.Mcp.Tools.AppService/src/Commands/Webapp/Settings/AppSettingsUpdateCommand.cs index 5df483fcea..f262aa94e6 100644 --- a/tools/Azure.Mcp.Tools.AppService/src/Commands/Webapp/Settings/AppSettingsUpdateCommand.cs +++ b/tools/Azure.Mcp.Tools.AppService/src/Commands/Webapp/Settings/AppSettingsUpdateCommand.cs @@ -11,11 +11,12 @@ namespace Azure.Mcp.Tools.AppService.Commands.Webapp.Settings; -public sealed class AppSettingsUpdateCommand(ILogger logger) +public sealed class AppSettingsUpdateCommand(ILogger logger, IAppServiceService appServiceService) : BaseAppServiceCommand(resourceGroupRequired: true, appRequired: true) { private const string CommandTitle = "Updates Azure App Service Web App Application Settings"; private readonly ILogger _logger = logger; + private readonly IAppServiceService _appServiceService = appServiceService; public override string Id => "08ca52a3-f766-4c62-9597-702f629efaf6"; public override string Name => "update-appsettings"; @@ -113,8 +114,7 @@ public override async Task ExecuteAsync(CommandContext context, { context.Activity?.AddTag("subscription", options.Subscription); - var appServiceService = context.GetService(); - var updateResult = await appServiceService.UpdateAppSettingsAsync( + var updateResult = await _appServiceService.UpdateAppSettingsAsync( options.Subscription!, options.ResourceGroup!, options.AppName!, diff --git a/tools/Azure.Mcp.Tools.AppService/src/Commands/Webapp/WebappGetCommand.cs b/tools/Azure.Mcp.Tools.AppService/src/Commands/Webapp/WebappGetCommand.cs index 46569f48b2..846405e992 100644 --- a/tools/Azure.Mcp.Tools.AppService/src/Commands/Webapp/WebappGetCommand.cs +++ b/tools/Azure.Mcp.Tools.AppService/src/Commands/Webapp/WebappGetCommand.cs @@ -13,11 +13,12 @@ namespace Azure.Mcp.Tools.AppService.Commands.Webapp; -public sealed class WebappGetCommand(ILogger logger) +public sealed class WebappGetCommand(ILogger logger, IAppServiceService appServiceService) : BaseAppServiceCommand(resourceGroupRequired: false) { private const string CommandTitle = "Gets Azure App Service Web App Details"; private readonly ILogger _logger = logger; + private readonly IAppServiceService _appServiceService = appServiceService; public override string Id => "4412f1af-16e7-46db-8305-33e3d7ae06de"; public override string Name => "get"; @@ -71,8 +72,7 @@ public override async Task ExecuteAsync(CommandContext context, { context.Activity?.AddTag("subscription", options.Subscription); - var appServiceService = context.GetService(); - var webapps = await appServiceService.GetWebAppsAsync( + var webapps = await _appServiceService.GetWebAppsAsync( options.Subscription!, options.ResourceGroup, options.AppName, diff --git a/tools/Azure.Mcp.Tools.AppService/tests/Azure.Mcp.Tools.AppService.UnitTests/Commands/Database/DatabaseAddCommandTests.cs b/tools/Azure.Mcp.Tools.AppService/tests/Azure.Mcp.Tools.AppService.UnitTests/Commands/Database/DatabaseAddCommandTests.cs index f080bcaf3b..a4051ffd3b 100644 --- a/tools/Azure.Mcp.Tools.AppService/tests/Azure.Mcp.Tools.AppService.UnitTests/Commands/Database/DatabaseAddCommandTests.cs +++ b/tools/Azure.Mcp.Tools.AppService/tests/Azure.Mcp.Tools.AppService.UnitTests/Commands/Database/DatabaseAddCommandTests.cs @@ -19,17 +19,16 @@ namespace Azure.Mcp.Tools.AppService.UnitTests.Commands.Database; public class DatabaseAddCommandTests { private readonly IAppServiceService _appServiceService; - private readonly IServiceProvider _serviceProvider; private readonly ILogger _logger; + private readonly DatabaseAddCommand _command; + private readonly CommandContext _context; public DatabaseAddCommandTests() { _appServiceService = Substitute.For(); _logger = Substitute.For>(); - var collection = new ServiceCollection(); - collection.AddSingleton(_appServiceService); - collection.AddSingleton(_logger); - _serviceProvider = collection.BuildServiceProvider(); + _command = new(_logger, _appServiceService); + _context = new(new ServiceCollection().BuildServiceProvider()); } [Theory] @@ -117,12 +116,10 @@ await _appServiceService.Received(1).AddDatabaseAsync( public async Task ExecuteAsync_MissingRequiredParameter_ReturnsErrorResponse(params string[] commandArgs) { // Arrange - var command = new DatabaseAddCommand(_logger); - var args = command.GetCommand().Parse(commandArgs); - var context = new CommandContext(_serviceProvider); + var args = _command.GetCommand().Parse(commandArgs); // Act - var response = await command.ExecuteAsync(context, args, TestContext.Current.CancellationToken); + var response = await _command.ExecuteAsync(_context, args, TestContext.Current.CancellationToken); // Assert Assert.NotNull(response); @@ -178,11 +175,9 @@ public async Task ExecuteAsync_WithVariousParameters_AcceptsParameters( parameters.Add("retry-delay", retryDelay.Value); // Execute the command directly in unit tests rather than via the tool runner helper - var command = new DatabaseAddCommand(_logger); var argList = parameters.SelectMany(kvp => new[] { $"--{kvp.Key}", kvp.Value?.ToString() ?? string.Empty }).ToArray(); - var parseResult = command.GetCommand().Parse(argList); - var context = new CommandContext(_serviceProvider); - var response = await command.ExecuteAsync(context, parseResult, TestContext.Current.CancellationToken); + var parseResult = _command.GetCommand().Parse(argList); + var response = await _command.ExecuteAsync(_context, parseResult, TestContext.Current.CancellationToken); // Test actual command execution and proper error handling Assert.NotNull(response); @@ -240,8 +235,7 @@ public async Task ExecuteAsync_ServiceThrowsException_ReturnsErrorResponse() Arg.Any()) .ThrowsAsync(new InvalidOperationException("Service error")); - var command = new DatabaseAddCommand(_logger); - var args = command.GetCommand().Parse([ + var args = _command.GetCommand().Parse([ "--subscription", subscription, "--resource-group", resourceGroup, "--app", appName, @@ -249,10 +243,9 @@ public async Task ExecuteAsync_ServiceThrowsException_ReturnsErrorResponse() "--database-server", databaseServer, "--database", databaseName ]); - var context = new CommandContext(_serviceProvider); // Act - var response = await command.ExecuteAsync(context, args, TestContext.Current.CancellationToken); + var response = await _command.ExecuteAsync(_context, args, TestContext.Current.CancellationToken); // Assert Assert.NotNull(response); Assert.Equal(HttpStatusCode.InternalServerError, response.Status); diff --git a/tools/Azure.Mcp.Tools.AppService/tests/Azure.Mcp.Tools.AppService.UnitTests/Commands/Webapp/Settings/AppSettingsGetCommandTests.cs b/tools/Azure.Mcp.Tools.AppService/tests/Azure.Mcp.Tools.AppService.UnitTests/Commands/Webapp/Settings/AppSettingsGetCommandTests.cs index 5fa48d0a71..b1bbee4d15 100644 --- a/tools/Azure.Mcp.Tools.AppService/tests/Azure.Mcp.Tools.AppService.UnitTests/Commands/Webapp/Settings/AppSettingsGetCommandTests.cs +++ b/tools/Azure.Mcp.Tools.AppService/tests/Azure.Mcp.Tools.AppService.UnitTests/Commands/Webapp/Settings/AppSettingsGetCommandTests.cs @@ -21,7 +21,6 @@ namespace Azure.Mcp.Tools.AppService.UnitTests.Commands.Webapp.Settings; public class AppSettingsGetCommandTests { private readonly IAppServiceService _appServiceService; - private readonly IServiceProvider _serviceProvider; private readonly ILogger _logger; private readonly AppSettingsGetCommand _command; private readonly CommandContext _context; @@ -32,11 +31,8 @@ public AppSettingsGetCommandTests() _appServiceService = Substitute.For(); _logger = Substitute.For>(); - var collection = new ServiceCollection().AddSingleton(_appServiceService); - _serviceProvider = collection.BuildServiceProvider(); - - _command = new(_logger); - _context = new(_serviceProvider); + _command = new(_logger, _appServiceService); + _context = new(new ServiceCollection().BuildServiceProvider()); _commandDefinition = _command.GetCommand(); } diff --git a/tools/Azure.Mcp.Tools.AppService/tests/Azure.Mcp.Tools.AppService.UnitTests/Commands/Webapp/Settings/AppSettingsUpdateCommandTests.cs b/tools/Azure.Mcp.Tools.AppService/tests/Azure.Mcp.Tools.AppService.UnitTests/Commands/Webapp/Settings/AppSettingsUpdateCommandTests.cs index 546b7a4f20..391b0583c5 100644 --- a/tools/Azure.Mcp.Tools.AppService/tests/Azure.Mcp.Tools.AppService.UnitTests/Commands/Webapp/Settings/AppSettingsUpdateCommandTests.cs +++ b/tools/Azure.Mcp.Tools.AppService/tests/Azure.Mcp.Tools.AppService.UnitTests/Commands/Webapp/Settings/AppSettingsUpdateCommandTests.cs @@ -21,7 +21,6 @@ namespace Azure.Mcp.Tools.AppService.UnitTests.Commands.Webapp.Settings; public class AppSettingsUpdateCommandTests { private readonly IAppServiceService _appServiceService; - private readonly IServiceProvider _serviceProvider; private readonly ILogger _logger; private readonly AppSettingsUpdateCommand _command; private readonly CommandContext _context; @@ -32,11 +31,8 @@ public AppSettingsUpdateCommandTests() _appServiceService = Substitute.For(); _logger = Substitute.For>(); - var collection = new ServiceCollection().AddSingleton(_appServiceService); - _serviceProvider = collection.BuildServiceProvider(); - - _command = new(_logger); - _context = new(_serviceProvider); + _command = new(_logger, _appServiceService); + _context = new(new ServiceCollection().BuildServiceProvider()); _commandDefinition = _command.GetCommand(); } diff --git a/tools/Azure.Mcp.Tools.AppService/tests/Azure.Mcp.Tools.AppService.UnitTests/Commands/Webapp/WebappGetCommandTests.cs b/tools/Azure.Mcp.Tools.AppService/tests/Azure.Mcp.Tools.AppService.UnitTests/Commands/Webapp/WebappGetCommandTests.cs index 914036337e..1efafcf76d 100644 --- a/tools/Azure.Mcp.Tools.AppService/tests/Azure.Mcp.Tools.AppService.UnitTests/Commands/Webapp/WebappGetCommandTests.cs +++ b/tools/Azure.Mcp.Tools.AppService/tests/Azure.Mcp.Tools.AppService.UnitTests/Commands/Webapp/WebappGetCommandTests.cs @@ -22,7 +22,6 @@ namespace Azure.Mcp.Tools.AppService.UnitTests.Commands.Webapp; public class WebappGetCommandTests { private readonly IAppServiceService _appServiceService; - private readonly IServiceProvider _serviceProvider; private readonly ILogger _logger; private readonly WebappGetCommand _command; private readonly CommandContext _context; @@ -33,11 +32,8 @@ public WebappGetCommandTests() _appServiceService = Substitute.For(); _logger = Substitute.For>(); - var collection = new ServiceCollection().AddSingleton(_appServiceService); - _serviceProvider = collection.BuildServiceProvider(); - - _command = new(_logger); - _context = new(_serviceProvider); + _command = new(_logger, _appServiceService); + _context = new(new ServiceCollection().BuildServiceProvider()); _commandDefinition = _command.GetCommand(); }