diff --git a/servers/Azure.Mcp.Server/changelog-entries/1782414691894.yaml b/servers/Azure.Mcp.Server/changelog-entries/1782414691894.yaml new file mode 100644 index 0000000000..4a3aeebfdb --- /dev/null +++ b/servers/Azure.Mcp.Server/changelog-entries/1782414691894.yaml @@ -0,0 +1,3 @@ +changes: + - section: "Breaking Changes" + description: "Removed unused parameters from Resource Health tools." diff --git a/tools/Azure.Mcp.Tools.ResourceHealth/src/Azure.Mcp.Tools.ResourceHealth.csproj b/tools/Azure.Mcp.Tools.ResourceHealth/src/Azure.Mcp.Tools.ResourceHealth.csproj index 8e8f7b14ba..3fc45faaf0 100644 --- a/tools/Azure.Mcp.Tools.ResourceHealth/src/Azure.Mcp.Tools.ResourceHealth.csproj +++ b/tools/Azure.Mcp.Tools.ResourceHealth/src/Azure.Mcp.Tools.ResourceHealth.csproj @@ -7,12 +7,11 @@ - + - diff --git a/tools/Azure.Mcp.Tools.ResourceHealth/src/Commands/AvailabilityStatus/AvailabilityStatusGetCommand.cs b/tools/Azure.Mcp.Tools.ResourceHealth/src/Commands/AvailabilityStatus/AvailabilityStatusGetCommand.cs index dc522aa016..b563b29e42 100644 --- a/tools/Azure.Mcp.Tools.ResourceHealth/src/Commands/AvailabilityStatus/AvailabilityStatusGetCommand.cs +++ b/tools/Azure.Mcp.Tools.ResourceHealth/src/Commands/AvailabilityStatus/AvailabilityStatusGetCommand.cs @@ -2,13 +2,12 @@ // Licensed under the MIT License. using System.Net; +using Azure.Mcp.Core.Services.Azure.Subscription; using Azure.Mcp.Tools.ResourceHealth.Options.AvailabilityStatus; using Azure.Mcp.Tools.ResourceHealth.Services; using Microsoft.Extensions.Logging; using Microsoft.Mcp.Core.Commands; -using Microsoft.Mcp.Core.Extensions; using Microsoft.Mcp.Core.Models.Command; -using Microsoft.Mcp.Core.Models.Option; namespace Azure.Mcp.Tools.ResourceHealth.Commands.AvailabilityStatus; @@ -26,36 +25,14 @@ namespace Azure.Mcp.Tools.ResourceHealth.Commands.AvailabilityStatus; ReadOnly = true, Secret = false, LocalRequired = false)] -public sealed class AvailabilityStatusGetCommand(ILogger logger, IResourceHealthService resourceHealthService) - : BaseResourceHealthCommand() +public sealed class AvailabilityStatusGetCommand(ILogger logger, IResourceHealthService resourceHealthService, ISubscriptionResolver subscriptionResolver) + : BaseResourceHealthCommand(subscriptionResolver) { private readonly ILogger _logger = logger; private readonly IResourceHealthService _resourceHealthService = resourceHealthService; - protected override void RegisterOptions(Command command) + public override async Task ExecuteAsync(CommandContext context, AvailabilityStatusGetOptions options, CancellationToken cancellationToken) { - base.RegisterOptions(command); - command.Options.Add(ResourceHealthOptionDefinitions.ResourceId.AsOptional()); - command.Options.Add(OptionDefinitions.Common.ResourceGroup.AsOptional()); - } - - protected override AvailabilityStatusGetOptions BindOptions(ParseResult parseResult) - { - var options = base.BindOptions(parseResult); - options.ResourceId = parseResult.GetValueOrDefault(ResourceHealthOptionDefinitions.ResourceId.Name); - options.ResourceGroup ??= parseResult.GetValueOrDefault(OptionDefinitions.Common.ResourceGroup.Name); - return options; - } - - public override async Task ExecuteAsync(CommandContext context, ParseResult parseResult, CancellationToken cancellationToken) - { - if (!Validate(parseResult.CommandResult, context.Response).IsValid) - { - return context.Response; - } - - var options = BindOptions(parseResult); - try { List statuses; @@ -119,5 +96,5 @@ public override async Task ExecuteAsync(CommandContext context, _ => base.GetStatusCode(ex) }; - internal record AvailabilityStatusGetCommandResult(List Statuses); + public sealed record AvailabilityStatusGetCommandResult(List Statuses); } diff --git a/tools/Azure.Mcp.Tools.ResourceHealth/src/Commands/BaseResourceHealthCommand.cs b/tools/Azure.Mcp.Tools.ResourceHealth/src/Commands/BaseResourceHealthCommand.cs index 2494d48da8..7b0b7375da 100644 --- a/tools/Azure.Mcp.Tools.ResourceHealth/src/Commands/BaseResourceHealthCommand.cs +++ b/tools/Azure.Mcp.Tools.ResourceHealth/src/Commands/BaseResourceHealthCommand.cs @@ -4,16 +4,15 @@ using System.Diagnostics.CodeAnalysis; using System.Net; using Azure.Mcp.Core.Commands.Subscription; -using Azure.Mcp.Tools.ResourceHealth.Options; +using Azure.Mcp.Core.Options; +using Azure.Mcp.Core.Services.Azure.Subscription; using Azure.Mcp.Tools.ResourceHealth.Services; using Microsoft.Mcp.Core.Commands; namespace Azure.Mcp.Tools.ResourceHealth.Commands; -public abstract class BaseResourceHealthCommand< - [DynamicallyAccessedMembers(TrimAnnotations.CommandAnnotations)] T> - : SubscriptionCommand - where T : BaseResourceHealthOptions, new() +public abstract class BaseResourceHealthCommand<[DynamicallyAccessedMembers(TrimAnnotations.CommandAnnotations)] TOptions, TResult>(ISubscriptionResolver subscriptionResolver) + : SubscriptionCommand(subscriptionResolver) where TOptions : class, ISubscriptionOption { protected override string GetErrorMessage(Exception ex) => ex switch { diff --git a/tools/Azure.Mcp.Tools.ResourceHealth/src/Commands/ServiceHealthEvents/ServiceHealthEventsListCommand.cs b/tools/Azure.Mcp.Tools.ResourceHealth/src/Commands/ServiceHealthEvents/ServiceHealthEventsListCommand.cs index e553797c74..74ce8ee5ce 100644 --- a/tools/Azure.Mcp.Tools.ResourceHealth/src/Commands/ServiceHealthEvents/ServiceHealthEventsListCommand.cs +++ b/tools/Azure.Mcp.Tools.ResourceHealth/src/Commands/ServiceHealthEvents/ServiceHealthEventsListCommand.cs @@ -1,11 +1,11 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +using Azure.Mcp.Core.Services.Azure.Subscription; using Azure.Mcp.Tools.ResourceHealth.Options.ServiceHealthEvents; using Azure.Mcp.Tools.ResourceHealth.Services; using Microsoft.Extensions.Logging; using Microsoft.Mcp.Core.Commands; -using Microsoft.Mcp.Core.Extensions; using Microsoft.Mcp.Core.Models.Command; namespace Azure.Mcp.Tools.ResourceHealth.Commands.ServiceHealthEvents; @@ -24,69 +24,34 @@ namespace Azure.Mcp.Tools.ResourceHealth.Commands.ServiceHealthEvents; ReadOnly = true, Secret = false, LocalRequired = false)] -public sealed class ServiceHealthEventsListCommand(ILogger logger, IResourceHealthService resourceHealthService) - : BaseResourceHealthCommand() +public sealed class ServiceHealthEventsListCommand(ILogger logger, IResourceHealthService resourceHealthService, ISubscriptionResolver subscriptionResolver) + : BaseResourceHealthCommand(subscriptionResolver) { private readonly ILogger _logger = logger; private readonly IResourceHealthService _resourceHealthService = resourceHealthService; - private static readonly HashSet validEventTypes = new(StringComparer.OrdinalIgnoreCase) { "ServiceIssue", "PlannedMaintenance", "HealthAdvisory", "Security" }; - private static readonly HashSet validStatuses = new(StringComparer.OrdinalIgnoreCase) { "Active", "Resolved" }; + private static readonly HashSet s_validEventTypes = new(StringComparer.OrdinalIgnoreCase) { "ServiceIssue", "PlannedMaintenance", "HealthAdvisory", "Security" }; + private static readonly HashSet s_validStatuses = new(StringComparer.OrdinalIgnoreCase) { "Active", "Resolved" }; - protected override void RegisterOptions(Command command) + public override void ValidateOptions(ServiceHealthEventsListOptions options, ValidationResult validationResult) { - base.RegisterOptions(command); - command.Options.Add(ResourceHealthOptionDefinitions.EventType); - command.Options.Add(ResourceHealthOptionDefinitions.Status); - command.Options.Add(ResourceHealthOptionDefinitions.TrackingId); - command.Options.Add(ResourceHealthOptionDefinitions.Filter); - command.Options.Add(ResourceHealthOptionDefinitions.QueryStartTime); - command.Options.Add(ResourceHealthOptionDefinitions.QueryEndTime); + base.ValidateOptions(options, validationResult); - // Add validators for enum values - command.Validators.Add(commandResult => + // Validate event-type enum values + if (!string.IsNullOrEmpty(options.EventType) && !s_validEventTypes.Contains(options.EventType)) { - // Validate event-type enum values - if (commandResult.TryGetValue(ResourceHealthOptionDefinitions.EventType, out var eventType) && !string.IsNullOrEmpty(eventType)) - { - if (!validEventTypes.Contains(eventType)) - { - commandResult.AddError($"Invalid event-type '{eventType}'. Valid values are: {string.Join(", ", validEventTypes)}"); - } - } - - // Validate status enum values - if (commandResult.TryGetValue(ResourceHealthOptionDefinitions.Status, out var status) && !string.IsNullOrEmpty(status)) - { - if (!validStatuses.Contains(status)) - { - commandResult.AddError($"Invalid status '{status}'. Valid values are: {string.Join(", ", validStatuses)}"); - } - } - }); - } - - protected override ServiceHealthEventsListOptions BindOptions(ParseResult parseResult) - { - var options = base.BindOptions(parseResult); - options.EventType = parseResult.GetValueOrDefault(ResourceHealthOptionDefinitions.EventType); - options.Status = parseResult.GetValueOrDefault(ResourceHealthOptionDefinitions.Status); - options.TrackingId = parseResult.GetValueOrDefault(ResourceHealthOptionDefinitions.TrackingId); - options.Filter = parseResult.GetValueOrDefault(ResourceHealthOptionDefinitions.Filter); - options.QueryStartTime = parseResult.GetValueOrDefault(ResourceHealthOptionDefinitions.QueryStartTime); - options.QueryEndTime = parseResult.GetValueOrDefault(ResourceHealthOptionDefinitions.QueryEndTime); - return options; - } + validationResult.Errors.Add($"Invalid event-type '{options.EventType}'. Valid values are: {string.Join(", ", s_validEventTypes)}"); + } - public override async Task ExecuteAsync(CommandContext context, ParseResult parseResult, CancellationToken cancellationToken) - { - if (!Validate(parseResult.CommandResult, context.Response).IsValid) + // Validate status enum values + if (!string.IsNullOrEmpty(options.Status) && !s_validStatuses.Contains(options.Status)) { - return context.Response; + validationResult.Errors.Add($"Invalid status '{options.Status}'. Valid values are: {string.Join(", ", s_validStatuses)}"); } + } - var options = BindOptions(parseResult); - + public override async Task ExecuteAsync(CommandContext context, ServiceHealthEventsListOptions options, CancellationToken cancellationToken) + { try { var events = await _resourceHealthService.ListServiceHealthEventsAsync( @@ -112,5 +77,5 @@ public override async Task ExecuteAsync(CommandContext context, return context.Response; } - internal record ServiceHealthEventsListCommandResult(List Events); + public sealed record ServiceHealthEventsListCommandResult(List Events); } diff --git a/tools/Azure.Mcp.Tools.ResourceHealth/src/GlobalUsings.cs b/tools/Azure.Mcp.Tools.ResourceHealth/src/GlobalUsings.cs deleted file mode 100644 index b41cc886b4..0000000000 --- a/tools/Azure.Mcp.Tools.ResourceHealth/src/GlobalUsings.cs +++ /dev/null @@ -1,4 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -global using System.CommandLine; diff --git a/tools/Azure.Mcp.Tools.ResourceHealth/src/Options/AvailabilityStatus/AvailabilityStatusGetOptions.cs b/tools/Azure.Mcp.Tools.ResourceHealth/src/Options/AvailabilityStatus/AvailabilityStatusGetOptions.cs index 10c5c42200..4fa111bf36 100644 --- a/tools/Azure.Mcp.Tools.ResourceHealth/src/Options/AvailabilityStatus/AvailabilityStatusGetOptions.cs +++ b/tools/Azure.Mcp.Tools.ResourceHealth/src/Options/AvailabilityStatus/AvailabilityStatusGetOptions.cs @@ -1,12 +1,25 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -using System.Text.Json.Serialization; +using Azure.Mcp.Core.Options; +using Microsoft.Mcp.Core.Options; namespace Azure.Mcp.Tools.ResourceHealth.Options.AvailabilityStatus; -public class AvailabilityStatusGetOptions : BaseResourceHealthOptions +public sealed class AvailabilityStatusGetOptions : ISubscriptionOption { - [JsonPropertyName(ResourceHealthOptionDefinitions.ResourceIdName)] + [Option(Description = "The Azure resource ID to get health status for (e.g., /subscriptions/{sub}/resourceGroups/{rg}/providers/Microsoft.Compute/virtualMachines/{vm}).", Name = "resourceId")] public string? ResourceId { get; set; } + + [Option(Description = OptionDescriptions.ResourceGroup)] + public string? ResourceGroup { get; set; } + + [Option(Description = OptionDescriptions.Subscription)] + public string? Subscription { get; set; } + + [Option(Description = OptionDescriptions.Tenant)] + public string? Tenant { get; set; } + + [OptionContainer(Prefix = "retry")] + public RetryPolicyOptions? RetryPolicy { get; set; } } diff --git a/tools/Azure.Mcp.Tools.ResourceHealth/src/Options/BaseResourceHealthOptions.cs b/tools/Azure.Mcp.Tools.ResourceHealth/src/Options/BaseResourceHealthOptions.cs deleted file mode 100644 index 4077722360..0000000000 --- a/tools/Azure.Mcp.Tools.ResourceHealth/src/Options/BaseResourceHealthOptions.cs +++ /dev/null @@ -1,10 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -using Microsoft.Mcp.Core.Options; - -namespace Azure.Mcp.Tools.ResourceHealth.Options; - -public class BaseResourceHealthOptions : SubscriptionOptions -{ -} diff --git a/tools/Azure.Mcp.Tools.ResourceHealth/src/Options/ServiceHealthEvents/ServiceHealthEventsListOptions.cs b/tools/Azure.Mcp.Tools.ResourceHealth/src/Options/ServiceHealthEvents/ServiceHealthEventsListOptions.cs index b0ae952c2a..9d423523a9 100644 --- a/tools/Azure.Mcp.Tools.ResourceHealth/src/Options/ServiceHealthEvents/ServiceHealthEventsListOptions.cs +++ b/tools/Azure.Mcp.Tools.ResourceHealth/src/Options/ServiceHealthEvents/ServiceHealthEventsListOptions.cs @@ -1,25 +1,43 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +using Azure.Mcp.Core.Options; +using Microsoft.Mcp.Core.Options; + namespace Azure.Mcp.Tools.ResourceHealth.Options.ServiceHealthEvents; -public class ServiceHealthEventsListOptions : BaseResourceHealthOptions +public sealed class ServiceHealthEventsListOptions : ISubscriptionOption { /// Filter by event type (ServiceIssue, PlannedMaintenance, HealthAdvisory, Security). + [Option(Description = "Filter by event type (ServiceIssue, PlannedMaintenance, HealthAdvisory, Security). If not specified, all event types are included.")] public string? EventType { get; set; } /// Filter by status (Active, Resolved). + [Option(Description = "Filter by status (Active, Resolved). If not specified, all statuses are included.")] public string? Status { get; set; } /// Filter by tracking ID to get a specific service health event. + [Option(Description = "Filter by tracking ID to get a specific service health event.")] public string? TrackingId { get; set; } /// Additional OData filter expression to apply to the service health events query. + [Option(Description = "Additional OData filter expression to apply to the service health events query.")] public string? Filter { get; set; } /// Start time for the query in ISO 8601 format (e.g., 2024-01-01T00:00:00Z). + [Option(Description = "Start time for the query in ISO 8601 format (e.g., 2024-01-01T00:00:00Z). Events from this time onwards will be included.")] public string? QueryStartTime { get; set; } /// End time for the query in ISO 8601 format (e.g., 2024-01-31T23:59:59Z). + [Option(Description = "End time for the query in ISO 8601 format (e.g., 2024-01-31T23:59:59Z). Events up to this time will be included.")] public string? QueryEndTime { get; set; } + + [Option(Description = OptionDescriptions.Subscription)] + public string? Subscription { get; set; } + + [Option(Description = OptionDescriptions.Tenant)] + public string? Tenant { get; set; } + + [OptionContainer(Prefix = "retry")] + public RetryPolicyOptions? RetryPolicy { get; set; } } diff --git a/tools/Azure.Mcp.Tools.ResourceHealth/src/ResourceHealthOptionDefinitions.cs b/tools/Azure.Mcp.Tools.ResourceHealth/src/ResourceHealthOptionDefinitions.cs deleted file mode 100644 index 73e78d12e0..0000000000 --- a/tools/Azure.Mcp.Tools.ResourceHealth/src/ResourceHealthOptionDefinitions.cs +++ /dev/null @@ -1,65 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -namespace Azure.Mcp.Tools.ResourceHealth; - -public static class ResourceHealthOptionDefinitions -{ - public const string ResourceIdName = "resourceId"; - public const string EventTypeName = "event-type"; - public const string StatusName = "status"; - public const string TrackingIdName = "tracking-id"; - public const string FilterName = "filter"; - public const string QueryStartTimeName = "query-start-time"; - public const string QueryEndTimeName = "query-end-time"; - - public static readonly Option ResourceId = new( - $"--{ResourceIdName}" - ) - { - Description = "The Azure resource ID to get health status for (e.g., /subscriptions/{sub}/resourceGroups/{rg}/providers/Microsoft.Compute/virtualMachines/{vm}).", - Required = true - }; - - public static readonly Option EventType = new( - $"--{EventTypeName}" - ) - { - Description = "Filter by event type (ServiceIssue, PlannedMaintenance, HealthAdvisory, Security). If not specified, all event types are included." - }; - - public static readonly Option Status = new( - $"--{StatusName}" - ) - { - Description = "Filter by status (Active, Resolved). If not specified, all statuses are included." - }; - - public static readonly Option TrackingId = new( - $"--{TrackingIdName}" - ) - { - Description = "Filter by tracking ID to get a specific service health event." - }; - - public static readonly Option Filter = new( - $"--{FilterName}" - ) - { - Description = "Additional OData filter expression to apply to the service health events query." - }; - - public static readonly Option QueryStartTime = new( - $"--{QueryStartTimeName}" - ) - { - Description = "Start time for the query in ISO 8601 format (e.g., 2024-01-01T00:00:00Z). Events from this time onwards will be included." - }; - - public static readonly Option QueryEndTime = new( - $"--{QueryEndTimeName}" - ) - { - Description = "End time for the query in ISO 8601 format (e.g., 2024-01-31T23:59:59Z). Events up to this time will be included." - }; -} diff --git a/tools/Azure.Mcp.Tools.ResourceHealth/src/ResourceHealthSetup.cs b/tools/Azure.Mcp.Tools.ResourceHealth/src/ResourceHealthSetup.cs index 8616ef283e..966a4ec70f 100644 --- a/tools/Azure.Mcp.Tools.ResourceHealth/src/ResourceHealthSetup.cs +++ b/tools/Azure.Mcp.Tools.ResourceHealth/src/ResourceHealthSetup.cs @@ -19,9 +19,7 @@ public class ResourceHealthSetup : IAreaSetup public void ConfigureServices(IServiceCollection services) { services.AddSingleton(); - services.AddSingleton(); - services.AddSingleton(); } diff --git a/tools/Azure.Mcp.Tools.ResourceHealth/src/Services/ResourceHealthService.cs b/tools/Azure.Mcp.Tools.ResourceHealth/src/Services/ResourceHealthService.cs index 131d7d200b..ffa89c4b0a 100644 --- a/tools/Azure.Mcp.Tools.ResourceHealth/src/Services/ResourceHealthService.cs +++ b/tools/Azure.Mcp.Tools.ResourceHealth/src/Services/ResourceHealthService.cs @@ -10,7 +10,6 @@ using Azure.Mcp.Core.Services.Azure.Tenant; using Azure.Mcp.Tools.ResourceHealth.Models; using Azure.Mcp.Tools.ResourceHealth.Models.Internal; -using Microsoft.Extensions.Logging; using Microsoft.Mcp.Core.Options; namespace Azure.Mcp.Tools.ResourceHealth.Services; @@ -18,14 +17,12 @@ namespace Azure.Mcp.Tools.ResourceHealth.Services; public class ResourceHealthService( ISubscriptionService subscriptionService, ITenantService tenantService, - IHttpClientFactory httpClientFactory, - ILogger logger) + IHttpClientFactory httpClientFactory) : BaseAzureService(tenantService), IResourceHealthService { private readonly ISubscriptionService _subscriptionService = subscriptionService ?? throw new ArgumentNullException(nameof(subscriptionService)); private readonly ITenantService _tenantService = tenantService ?? throw new ArgumentNullException(nameof(tenantService)); private readonly IHttpClientFactory _httpClientFactory = httpClientFactory ?? throw new ArgumentNullException(nameof(httpClientFactory)); - private readonly ILogger _logger = logger ?? throw new ArgumentNullException(nameof(logger)); private const string ResourceHealthApiVersion = "2025-05-01"; diff --git a/tools/Azure.Mcp.Tools.ResourceHealth/tests/Azure.Mcp.Tools.ResourceHealth.Tests/AvailabilityStatus/AvailabilityStatusGetCommandTests.cs b/tools/Azure.Mcp.Tools.ResourceHealth/tests/Azure.Mcp.Tools.ResourceHealth.Tests/AvailabilityStatus/AvailabilityStatusGetCommandTests.cs index 57ff9836b9..469d773457 100644 --- a/tools/Azure.Mcp.Tools.ResourceHealth/tests/Azure.Mcp.Tools.ResourceHealth.Tests/AvailabilityStatus/AvailabilityStatusGetCommandTests.cs +++ b/tools/Azure.Mcp.Tools.ResourceHealth/tests/Azure.Mcp.Tools.ResourceHealth.Tests/AvailabilityStatus/AvailabilityStatusGetCommandTests.cs @@ -2,11 +2,10 @@ // Licensed under the MIT License. using System.Net; +using Azure.Mcp.Tests.Commands; using Azure.Mcp.Tools.ResourceHealth.Commands.AvailabilityStatus; using Azure.Mcp.Tools.ResourceHealth.Services; using Microsoft.Mcp.Core.Options; -using Microsoft.Mcp.Tests.Client; -using Microsoft.Mcp.Tests.Helpers; using NSubstitute; using NSubstitute.ExceptionExtensions; using Xunit; @@ -14,7 +13,7 @@ namespace Azure.Mcp.Tools.ResourceHealth.Tests.AvailabilityStatus; -public class AvailabilityStatusGetCommandTests : CommandUnitTestsBase +public class AvailabilityStatusGetCommandTests : SubscriptionCommandUnitTestsBase { #region Get (Single Resource) Tests @@ -220,27 +219,14 @@ public async Task ExecuteAsync_ReturnsBadRequest_WhenSubscriptionLookupFails() #region Validation Tests - [Theory] - [InlineData("--subscription")] - public async Task ExecuteAsync_ReturnsError_WhenRequiredParameterIsMissing(string missingParameter) + [Fact] + public async Task ExecuteAsync_ReturnsError_WhenRequiredParameterIsMissing() { - // The subscription option falls back to the Azure CLI profile or AZURE_SUBSCRIPTION_ID env var. - // Skip if a CLI profile default is present so the test only runs when - // the missing-subscription path is actually exercised. - TestEnvironment.SkipIfDefaultSubscriptionConfigured(); - - var argsList = new List(); - if (missingParameter != "--subscription") - { - argsList.Add("--subscription"); - argsList.Add("12345678-1234-1234-1234-123456789012"); - } - - var response = await ExecuteCommandAsync(argsList.ToArray()); + var response = await ExecuteCommandAsync([]); Assert.NotNull(response); Assert.Equal(HttpStatusCode.BadRequest, response.Status); - Assert.Equal($"Missing Required options: {missingParameter}", response.Message); + Assert.Contains($"Missing Required options: --subscription", response.Message); } #endregion diff --git a/tools/Azure.Mcp.Tools.ResourceHealth/tests/Azure.Mcp.Tools.ResourceHealth.Tests/ServiceHealthEvents/ServiceHealthEventsListCommandTests.cs b/tools/Azure.Mcp.Tools.ResourceHealth/tests/Azure.Mcp.Tools.ResourceHealth.Tests/ServiceHealthEvents/ServiceHealthEventsListCommandTests.cs index 78ae45bdae..e22bc67706 100644 --- a/tools/Azure.Mcp.Tools.ResourceHealth/tests/Azure.Mcp.Tools.ResourceHealth.Tests/ServiceHealthEvents/ServiceHealthEventsListCommandTests.cs +++ b/tools/Azure.Mcp.Tools.ResourceHealth/tests/Azure.Mcp.Tools.ResourceHealth.Tests/ServiceHealthEvents/ServiceHealthEventsListCommandTests.cs @@ -2,18 +2,17 @@ // Licensed under the MIT License. using System.Net; +using Azure.Mcp.Tests.Commands; using Azure.Mcp.Tools.ResourceHealth.Commands.ServiceHealthEvents; using Azure.Mcp.Tools.ResourceHealth.Services; using Microsoft.Mcp.Core.Options; -using Microsoft.Mcp.Tests.Client; -using Microsoft.Mcp.Tests.Helpers; using NSubstitute; using NSubstitute.ExceptionExtensions; using Xunit; namespace Azure.Mcp.Tools.ResourceHealth.Tests.ServiceHealthEvents; -public class ServiceHealthEventsListCommandTests : CommandUnitTestsBase +public class ServiceHealthEventsListCommandTests : SubscriptionCommandUnitTestsBase { [Theory] [InlineData("", false)] @@ -26,14 +25,6 @@ public class ServiceHealthEventsListCommandTests : CommandUnitTestsBase _logger = Substitute.For>(); public ResourceHealthServiceSsrfValidationTests() { _subscriptionService = Substitute.For(); _tenantService = Substitute.For(); _httpClientFactory = Substitute.For(); - _service = new ResourceHealthService(_subscriptionService, _tenantService, _httpClientFactory, _logger); + _service = new ResourceHealthService(_subscriptionService, _tenantService, _httpClientFactory); } private void SetupMocksForValidRequest(HttpResponseMessage response, string subscriptionId = "12345678-1234-1234-1234-123456789012")