Skip to main content
Glama

Azure MCP Server

Official
MIT License
1,161
  • Linux
  • Apple
MetricsQueryCommandTests.cs33.9 kB
// Copyright (c) Microsoft Corporation. // Licensed under the MIT License. using System.CommandLine; using System.CommandLine.Parsing; using System.Text.Json; using AzureMcp.Core.Models.Command; using AzureMcp.Core.Options; using AzureMcp.Monitor.Commands.Metrics; using AzureMcp.Monitor.Models; using AzureMcp.Monitor.Services; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using NSubstitute; using Xunit; namespace AzureMcp.Monitor.UnitTests.Metrics; public class MetricsQueryCommandTests { private readonly IServiceProvider _serviceProvider; private readonly IMonitorMetricsService _service; private readonly ILogger<MetricsQueryCommand> _logger; private readonly MetricsQueryCommand _command; public MetricsQueryCommandTests() { _service = Substitute.For<IMonitorMetricsService>(); _logger = Substitute.For<ILogger<MetricsQueryCommand>>(); var collection = new ServiceCollection(); collection.AddSingleton(_service); _serviceProvider = collection.BuildServiceProvider(); _command = new(_logger); } #region Constructor and Properties Tests [Fact] public void Constructor_InitializesCommandCorrectly() { // Act var command = _command.GetCommand(); // Assert Assert.Equal("query", command.Name); Assert.Equal("Query Azure Monitor Metrics", _command.Title); Assert.NotNull(command.Description); Assert.NotEmpty(command.Description); Assert.Contains("Query Azure Monitor metrics for a resource", command.Description); } [Fact] public void Name_ReturnsCorrectValue() { // Act & Assert Assert.Equal("query", _command.Name); } [Fact] public void Title_ReturnsCorrectValue() { // Act & Assert Assert.Equal("Query Azure Monitor Metrics", _command.Title); } #endregion #region Option Registration Tests [Fact] public void RegisterOptions_AddsAllExpectedOptions() { // Act var command = _command.GetCommand(); // Assert - Check that all expected options are registered var options = command.Options.Select(o => o.Name).ToList(); // Base options from BaseMetricsCommand Assert.Contains("resource-group", options); Assert.Contains("resource-type", options); Assert.Contains("resource", options); // MetricsQueryCommand specific options Assert.Contains("metric-names", options); Assert.Contains("start-time", options); Assert.Contains("end-time", options); Assert.Contains("interval", options); Assert.Contains("aggregation", options); Assert.Contains("filter", options); Assert.Contains("metric-namespace", options); Assert.Contains("max-buckets", options); // Verify required options are marked as required var requiredOptions = command.Options.Where(o => o.IsRequired).Select(o => o.Name).ToList(); Assert.Contains("resource", requiredOptions); Assert.Contains("metric-names", requiredOptions); } #endregion #region Option Binding Tests [Fact] public async Task ExecuteAsync_BindsAllOptionsCorrectly() { // Arrange var args = "--subscription sub1 --resource-group rg1 --resource-type Microsoft.Storage/storageAccounts --resource sa1 " + "--metric-names CPU,Memory --start-time 2023-01-01T00:00:00Z --end-time 2023-01-02T00:00:00Z " + "--interval PT1M --aggregation Average --filter \"dimension eq 'value'\" --metric-namespace Microsoft.Storage " + "--max-buckets 100"; _service.QueryMetricsAsync( Arg.Any<string>(), Arg.Any<string?>(), Arg.Any<string?>(), Arg.Any<string>(), Arg.Any<string>(), Arg.Any<IEnumerable<string>>(), Arg.Any<string?>(), Arg.Any<string?>(), Arg.Any<string?>(), Arg.Any<string?>(), Arg.Any<string?>(), Arg.Any<string?>(), Arg.Any<RetryPolicyOptions?>()) .Returns(new List<MetricResult>()); var context = new CommandContext(_serviceProvider); var parseResult = _command.GetCommand().Parse(args); // Act await _command.ExecuteAsync(context, parseResult); // Assert - Verify all parameters were passed correctly to the service await _service.Received(1).QueryMetricsAsync( "sub1", // subscription "rg1", // resource group "Microsoft.Storage/storageAccounts", // resource type "sa1", // resource name "Microsoft.Storage", // metric namespace Arg.Is<IEnumerable<string>>(m => m.SequenceEqual(new[] { "CPU", "Memory" })), // metric names "2023-01-01T00:00:00Z", // start time "2023-01-02T00:00:00Z", // end time "PT1M", // interval "Average", // aggregation "dimension eq 'value'", // filter null, // tenant Arg.Any<RetryPolicyOptions?>()); // retry policy } [Fact] public async Task ExecuteAsync_HandlesOptionalParameters() { // Arrange var args = "--subscription sub1 --resource sa1 --metric-names CPU --metric-namespace microsoft.compute/virtualmachines"; _service.QueryMetricsAsync( Arg.Any<string>(), Arg.Any<string?>(), Arg.Any<string?>(), Arg.Any<string>(), Arg.Any<string>(), Arg.Any<IEnumerable<string>>(), Arg.Any<string?>(), Arg.Any<string?>(), Arg.Any<string?>(), Arg.Any<string?>(), Arg.Any<string?>(), Arg.Any<string?>(), Arg.Any<RetryPolicyOptions?>()) .Returns(new List<MetricResult>()); var context = new CommandContext(_serviceProvider); var parseResult = _command.GetCommand().Parse(args); // Act await _command.ExecuteAsync(context, parseResult); // Assert - Verify optional parameters are null when not provided await _service.Received(1).QueryMetricsAsync( Arg.Is<string>(t => t == "sub1"), // subscription Arg.Is<string?>(t => t == null), // resource group (not provided) Arg.Is<string?>(t => t == null), // resource type (not provided) Arg.Is<string>(t => t == "sa1"), // resource name Arg.Is<string>(t => t == "microsoft.compute/virtualmachines"), // metric namespace (not provided) Arg.Is<IEnumerable<string>>(m => m.SequenceEqual(new[] { "CPU" })), // metric names Arg.Any<string>(), // start time (default) Arg.Any<string>(), // end time (default) Arg.Is<string?>(t => t == null), // interval (not provided) Arg.Is<string?>(t => t == null), // aggregation (not provided) Arg.Is<string?>(t => t == null), // filter (not provided) Arg.Is<string?>(t => t == null), // tenant Arg.Any<RetryPolicyOptions?>()); // retry policy } #endregion #region Validation Tests [Theory] [InlineData("CPU", true)] [InlineData("CPU,Memory", true)] [InlineData("CPU, Memory, Disk", true)] [InlineData(",", false)] [InlineData("CPU,", false)] [InlineData(",CPU", false)] public async Task Validate_MetricNames_ValidatesCorrectly(string metricNames, bool shouldBeValid) { // Arrange var args = $"--subscription sub1 --resource sa1 --metric-namespace microsoft.compute/virtualmachines --metric-names \"{metricNames}\""; var parseResult = _command.GetCommand().Parse(args); var commandResult = parseResult.CommandResult; var context = new CommandContext(_serviceProvider); // Act var result = await _command.ExecuteAsync(context, parseResult); // Assert if (!shouldBeValid) { Assert.NotNull(result.Message); Assert.Contains("Invalid format for --metric-names", result.Message); Assert.Equal(400, result.Status); } else { Assert.Equal("Success", result.Message); Assert.Equal(200, result.Status); // Default status should remain unchanged for valid cases } } #endregion #region ExecuteAsync Tests - Success Scenarios [Theory] [InlineData("--subscription sub1 --resource sa1 --metric-names CPU --metric-namespace microsoft.compute/virtualmachines")] [InlineData("--subscription sub1 --resource-group rg1 --resource-type Microsoft.Storage/storageAccounts --resource sa1 --metric-names CPU --metric-namespace microsoft.compute/virtualmachines")] [InlineData("--subscription sub1 --resource sa1 --metric-names CPU,Memory --metric-namespace microsoft.compute/virtualmachines")] [InlineData("--subscription sub1 --resource sa1 --metric-namespace microsoft.compute/virtualmachines --metric-names CPU --start-time 2023-01-01T00:00:00Z --end-time 2023-01-02T00:00:00Z")] public async Task ExecuteAsync_ValidInput_ReturnsSuccess(string args) { // Arrange var expectedResults = new List<MetricResult> { new() { Name = "CPU", Unit = "Percent", TimeSeries = new List<MetricTimeSeries> { new() { Metadata = new Dictionary<string, string>(), Start = DateTime.UtcNow.AddHours(-1), End = DateTime.UtcNow, Interval = "PT1M", AvgBuckets = new double[] { 45.5, 50.2, 48.1 } } } } }; _service.QueryMetricsAsync( Arg.Any<string>(), Arg.Any<string?>(), Arg.Any<string?>(), Arg.Any<string>(), Arg.Any<string>(), Arg.Any<IEnumerable<string>>(), Arg.Any<string?>(), Arg.Any<string?>(), Arg.Any<string?>(), Arg.Any<string?>(), Arg.Any<string?>(), Arg.Any<string?>(), Arg.Any<RetryPolicyOptions?>()) .Returns(expectedResults); var context = new CommandContext(_serviceProvider); var parseResult = _command.GetCommand().Parse(args); // Act var response = await _command.ExecuteAsync(context, parseResult); // Assert Assert.Equal(200, response.Status); Assert.NotNull(response.Results); Assert.Equal("Success", response.Message); // Verify the actual content of the results var results = GetResult(response.Results); Assert.NotNull(results); Assert.Single(results); Assert.Equal("CPU", results[0].Name); Assert.Equal("Percent", results[0].Unit); Assert.Single(results[0].TimeSeries); Assert.Equal(new double[] { 45.5, 50.2, 48.1 }, results[0].TimeSeries[0].AvgBuckets); } [Fact] public async Task ExecuteAsync_EmptyResults_ReturnsSuccessWithNullResults() { // Arrange _service.QueryMetricsAsync( Arg.Any<string>(), Arg.Any<string?>(), Arg.Any<string?>(), Arg.Any<string>(), Arg.Any<string>(), Arg.Any<IEnumerable<string>>(), Arg.Any<string?>(), Arg.Any<string?>(), Arg.Any<string?>(), Arg.Any<string?>(), Arg.Any<string?>(), Arg.Any<string?>(), Arg.Any<RetryPolicyOptions?>()) .Returns(new List<MetricResult>()); var context = new CommandContext(_serviceProvider); var parseResult = _command.GetCommand().Parse("--subscription sub1 --resource sa1 --metric-names CPU --metric-namespace microsoft.compute/virtualmachines"); // Act var response = await _command.ExecuteAsync(context, parseResult); // Assert Assert.Equal(200, response.Status); Assert.Null(response.Results); } [Fact] public async Task ExecuteAsync_CallsServiceWithCorrectParameters() { // Arrange _service.QueryMetricsAsync( Arg.Any<string>(), Arg.Any<string?>(), Arg.Any<string?>(), Arg.Any<string>(), Arg.Any<string>(), Arg.Any<IEnumerable<string>>(), Arg.Any<string?>(), Arg.Any<string?>(), Arg.Any<string?>(), Arg.Any<string?>(), Arg.Any<string?>(), Arg.Any<string?>(), Arg.Any<RetryPolicyOptions?>()) .Returns(new List<MetricResult>()); var context = new CommandContext(_serviceProvider); var parseResult = _command.GetCommand().Parse( "--subscription sub1 --resource-group rg1 --resource-type Microsoft.Storage/storageAccounts --metric-namespace microsoft.compute/virtualmachines " + "--resource sa1 --metric-names CPU,Memory --start-time 2023-01-01T00:00:00Z " + "--end-time 2023-01-02T00:00:00Z --interval PT1M --aggregation Average"); // Act await _command.ExecuteAsync(context, parseResult); // Assert await _service.Received(1).QueryMetricsAsync( "sub1", "rg1", "Microsoft.Storage/storageAccounts", "sa1", "microsoft.compute/virtualmachines", Arg.Is<IEnumerable<string>>(m => m.SequenceEqual(new[] { "CPU", "Memory" })), "2023-01-01T00:00:00Z", "2023-01-02T00:00:00Z", "PT1M", "Average", null, null, Arg.Any<RetryPolicyOptions?>()); } #endregion #region ExecuteAsync Tests - Validation Failures [Theory] [InlineData("--subscription sub1 --metric-names CPU")] // Missing resource [InlineData("--subscription sub1 --resource sa1")] // Missing metric-names public async Task ExecuteAsync_InvalidInput_ReturnsBadRequest(string args) { // Arrange var context = new CommandContext(_serviceProvider); var parseResult = _command.GetCommand().Parse(args); // Act var response = await _command.ExecuteAsync(context, parseResult); // Assert Assert.Equal(400, response.Status); Assert.NotEmpty(response.Message); Assert.Null(response.Results); } #endregion #region ExecuteAsync Tests - Bucket Limit Validation [Fact] public async Task ExecuteAsync_ExceedsBucketLimit_ReturnsBadRequest() { // Arrange var resultsWithTooManyBuckets = new List<MetricResult> { new() { Name = "CPU", Unit = "Percent", TimeSeries = new List<MetricTimeSeries> { new() { Metadata = new Dictionary<string, string>(), Start = DateTime.UtcNow.AddHours(-1), End = DateTime.UtcNow, Interval = "PT1M", AvgBuckets = new double[51] // Exceeds default limit of 50 } } } }; _service.QueryMetricsAsync( Arg.Any<string>(), Arg.Any<string?>(), Arg.Any<string?>(), Arg.Any<string>(), Arg.Any<string>(), Arg.Any<IEnumerable<string>>(), Arg.Any<string?>(), Arg.Any<string?>(), Arg.Any<string?>(), Arg.Any<string?>(), Arg.Any<string?>(), Arg.Any<string?>(), Arg.Any<RetryPolicyOptions?>()) .Returns(resultsWithTooManyBuckets); var context = new CommandContext(_serviceProvider); var parseResult = _command.GetCommand().Parse("--subscription sub1 --resource sa1 --metric-names CPU --metric-namespace microsoft.compute/virtualmachines"); // Act var response = await _command.ExecuteAsync(context, parseResult); // Assert Assert.Equal(400, response.Status); Assert.Contains("exceeds the maximum allowed limit of 50", response.Message); Assert.Contains("CPU", response.Message); Assert.Contains("51 time buckets", response.Message); Assert.Null(response.Results); } [Fact] public async Task ExecuteAsync_ExceedsCustomBucketLimit_ReturnsBadRequest() { // Arrange var resultsWithTooManyBuckets = new List<MetricResult> { new() { Name = "Memory", Unit = "Bytes", TimeSeries = new List<MetricTimeSeries> { new() { Metadata = new Dictionary<string, string>(), Start = DateTime.UtcNow.AddHours(-1), End = DateTime.UtcNow, Interval = "PT1M", MaxBuckets = new double[26] // Exceeds custom limit of 25 } } } }; _service.QueryMetricsAsync( Arg.Any<string>(), Arg.Any<string?>(), Arg.Any<string?>(), Arg.Any<string>(), Arg.Any<string>(), Arg.Any<IEnumerable<string>>(), Arg.Any<string?>(), Arg.Any<string?>(), Arg.Any<string?>(), Arg.Any<string?>(), Arg.Any<string?>(), Arg.Any<string?>(), Arg.Any<RetryPolicyOptions?>()) .Returns(resultsWithTooManyBuckets); var context = new CommandContext(_serviceProvider); var parseResult = _command.GetCommand().Parse("--subscription sub1 --resource sa1 --metric-names Memory --max-buckets 25 --metric-namespace microsoft.compute/virtualmachines"); // Act var response = await _command.ExecuteAsync(context, parseResult); // Assert Assert.Equal(400, response.Status); Assert.Contains("exceeds the maximum allowed limit of 25", response.Message); Assert.Contains("Memory", response.Message); Assert.Contains("26 time buckets", response.Message); } [Theory] [InlineData("AvgBuckets")] [InlineData("MinBuckets")] [InlineData("MaxBuckets")] [InlineData("TotalBuckets")] [InlineData("CountBuckets")] public async Task ExecuteAsync_ChecksAllBucketTypes_ForLimitExceeded(string bucketType) { // Arrange var timeSeries = new MetricTimeSeries { Metadata = new Dictionary<string, string>(), Start = DateTime.UtcNow.AddHours(-1), End = DateTime.UtcNow, Interval = "PT1M" }; // Set the specific bucket type to exceed limit var largeBucketArray = new double[51]; switch (bucketType) { case "AvgBuckets": timeSeries.AvgBuckets = largeBucketArray; break; case "MinBuckets": timeSeries.MinBuckets = largeBucketArray; break; case "MaxBuckets": timeSeries.MaxBuckets = largeBucketArray; break; case "TotalBuckets": timeSeries.TotalBuckets = largeBucketArray; break; case "CountBuckets": timeSeries.CountBuckets = largeBucketArray; break; } var results = new List<MetricResult> { new() { Name = "TestMetric", Unit = "Count", TimeSeries = new List<MetricTimeSeries> { timeSeries } } }; _service.QueryMetricsAsync( Arg.Any<string>(), Arg.Any<string?>(), Arg.Any<string?>(), Arg.Any<string>(), Arg.Any<string>(), Arg.Any<IEnumerable<string>>(), Arg.Any<string?>(), Arg.Any<string?>(), Arg.Any<string?>(), Arg.Any<string?>(), Arg.Any<string?>(), Arg.Any<string?>(), Arg.Any<RetryPolicyOptions?>()) .Returns(results); var context = new CommandContext(_serviceProvider); var parseResult = _command.GetCommand().Parse("--subscription sub1 --resource sa1 --metric-names TestMetric --metric-namespace microsoft.compute/virtualmachines"); // Act var response = await _command.ExecuteAsync(context, parseResult); // Assert Assert.Equal(400, response.Status); Assert.Contains("exceeds the maximum allowed limit", response.Message); } [Fact] public async Task ExecuteAsync_WithinBucketLimit_ReturnsSuccess() { // Arrange var resultsWithinLimit = new List<MetricResult> { new() { Name = "CPU", Unit = "Percent", TimeSeries = new List<MetricTimeSeries> { new() { Metadata = new Dictionary<string, string>(), Start = DateTime.UtcNow.AddHours(-1), End = DateTime.UtcNow, Interval = "PT1M", AvgBuckets = new double[50] // Exactly at the limit } } } }; _service.QueryMetricsAsync( Arg.Any<string>(), Arg.Any<string?>(), Arg.Any<string?>(), Arg.Any<string>(), Arg.Any<string>(), Arg.Any<IEnumerable<string>>(), Arg.Any<string?>(), Arg.Any<string?>(), Arg.Any<string?>(), Arg.Any<string?>(), Arg.Any<string?>(), Arg.Any<string?>(), Arg.Any<RetryPolicyOptions?>()) .Returns(resultsWithinLimit); var context = new CommandContext(_serviceProvider); var parseResult = _command.GetCommand().Parse("--subscription sub1 --resource sa1 --metric-names CPU --metric-namespace microsoft.compute/virtualmachines"); // Act var response = await _command.ExecuteAsync(context, parseResult); // Assert Assert.Equal(200, response.Status); Assert.NotNull(response.Results); } [Fact] public async Task ExecuteAsync_BucketLimitExceeded_LogsWarning() { // Arrange var resultsWithTooManyBuckets = new List<MetricResult> { new() { Name = "CPU", Unit = "Percent", TimeSeries = new List<MetricTimeSeries> { new() { Metadata = new Dictionary<string, string>(), Start = DateTime.UtcNow.AddHours(-1), End = DateTime.UtcNow, Interval = "PT1M", AvgBuckets = new double[51] } } } }; _service.QueryMetricsAsync( Arg.Any<string>(), Arg.Any<string?>(), Arg.Any<string?>(), Arg.Any<string>(), Arg.Any<string>(), Arg.Any<IEnumerable<string>>(), Arg.Any<string?>(), Arg.Any<string?>(), Arg.Any<string?>(), Arg.Any<string?>(), Arg.Any<string?>(), Arg.Any<string?>(), Arg.Any<RetryPolicyOptions?>()) .Returns(resultsWithTooManyBuckets); var context = new CommandContext(_serviceProvider); var parseResult = _command.GetCommand().Parse("--subscription sub1 --resource sa1 --metric-names CPU --metric-namespace microsoft.compute/virtualmachines"); // Act await _command.ExecuteAsync(context, parseResult); // Assert _logger.Received(1).Log( LogLevel.Warning, Arg.Any<EventId>(), Arg.Is<object>(o => o.ToString()!.Contains("Bucket limit exceeded")), Arg.Any<Exception?>(), Arg.Any<Func<object, Exception?, string>>()); } #endregion #region ExecuteAsync Tests - Error Handling [Fact] public async Task ExecuteAsync_ServiceThrowsException_ReturnsInternalServerError() { // Arrange var expectedException = new Exception("Service unavailable"); _service.When(x => x.QueryMetricsAsync( Arg.Any<string>(), Arg.Any<string?>(), Arg.Any<string?>(), Arg.Any<string>(), Arg.Any<string>(), Arg.Any<IEnumerable<string>>(), Arg.Any<string?>(), Arg.Any<string?>(), Arg.Any<string?>(), Arg.Any<string?>(), Arg.Any<string?>(), Arg.Any<string?>(), Arg.Any<RetryPolicyOptions?>())) .Do(x => throw expectedException); var context = new CommandContext(_serviceProvider); var parseResult = _command.GetCommand().Parse("--subscription sub1 --resource sa1 --metric-names CPU --metric-namespace microsoft.compute/virtualmachines"); // Act var response = await _command.ExecuteAsync(context, parseResult); // Assert Assert.Equal(500, response.Status); Assert.Contains("Service unavailable", response.Message); Assert.Contains("troubleshooting", response.Message); } [Fact] public async Task ExecuteAsync_ServiceThrowsException_LogsError() { // Arrange var expectedException = new Exception("Service error"); _service.When(x => x.QueryMetricsAsync( Arg.Any<string>(), Arg.Any<string?>(), Arg.Any<string?>(), Arg.Any<string>(), Arg.Any<string>(), Arg.Any<IEnumerable<string>>(), Arg.Any<string?>(), Arg.Any<string?>(), Arg.Any<string?>(), Arg.Any<string?>(), Arg.Any<string?>(), Arg.Any<string?>(), Arg.Any<RetryPolicyOptions?>())) .Do(x => throw expectedException); var context = new CommandContext(_serviceProvider); var parseResult = _command.GetCommand().Parse("--subscription sub1 --resource sa1 --metric-names CPU --metric-namespace microsoft.compute/virtualmachines"); // Act await _command.ExecuteAsync(context, parseResult); // Assert _logger.Received(1).Log( LogLevel.Error, Arg.Any<EventId>(), Arg.Is<object>(o => o.ToString()!.Contains("Error querying metrics")), expectedException, Arg.Any<Func<object, Exception?, string>>()); } #endregion #region Edge Cases and Integration Tests [Fact] public async Task ExecuteAsync_MultipleMetricsWithMixedBucketCounts_ValidatesEach() { // Arrange var results = new List<MetricResult> { new() { Name = "CPU", Unit = "Percent", TimeSeries = new List<MetricTimeSeries> { new() { AvgBuckets = new double[30] // Within limit } } }, new() { Name = "Memory", Unit = "Bytes", TimeSeries = new List<MetricTimeSeries> { new() { AvgBuckets = new double[51] // Exceeds limit } } } }; _service.QueryMetricsAsync( Arg.Any<string>(), Arg.Any<string?>(), Arg.Any<string?>(), Arg.Any<string>(), Arg.Any<string>(), Arg.Any<IEnumerable<string>>(), Arg.Any<string?>(), Arg.Any<string?>(), Arg.Any<string?>(), Arg.Any<string?>(), Arg.Any<string?>(), Arg.Any<string?>(), Arg.Any<RetryPolicyOptions?>()) .Returns(results); var context = new CommandContext(_serviceProvider); var parseResult = _command.GetCommand().Parse("--subscription sub1 --resource sa1 --metric-names CPU,Memory --metric-namespace microsoft.compute/virtualmachines"); // Act var response = await _command.ExecuteAsync(context, parseResult); // Assert Assert.Equal(400, response.Status); Assert.Contains("Memory", response.Message); Assert.Contains("51 time buckets", response.Message); } [Fact] public async Task ExecuteAsync_MultipleTimeSeriesPerMetric_ValidatesAll() { // Arrange var results = new List<MetricResult> { new() { Name = "CPU", Unit = "Percent", TimeSeries = new List<MetricTimeSeries> { new() { AvgBuckets = new double[30] // Within limit }, new() { AvgBuckets = new double[51] // Exceeds limit } } } }; _service.QueryMetricsAsync( Arg.Any<string>(), Arg.Any<string?>(), Arg.Any<string?>(), Arg.Any<string>(), Arg.Any<string>(), Arg.Any<IEnumerable<string>>(), Arg.Any<string?>(), Arg.Any<string?>(), Arg.Any<string?>(), Arg.Any<string?>(), Arg.Any<string?>(), Arg.Any<string?>(), Arg.Any<RetryPolicyOptions?>()) .Returns(results); var context = new CommandContext(_serviceProvider); var parseResult = _command.GetCommand().Parse("--subscription sub1 --resource sa1 --metric-names CPU --metric-namespace microsoft.compute/virtualmachines"); // Act var response = await _command.ExecuteAsync(context, parseResult); // Assert Assert.Equal(400, response.Status); Assert.Contains("CPU", response.Message); Assert.Contains("51 time buckets", response.Message); } [Fact] public async Task ExecuteAsync_NullBuckets_DoesNotCountTowardsLimit() { // Arrange var results = new List<MetricResult> { new() { Name = "CPU", Unit = "Percent", TimeSeries = new List<MetricTimeSeries> { new() { AvgBuckets = null, MinBuckets = null, MaxBuckets = null, TotalBuckets = null, CountBuckets = null } } } }; _service.QueryMetricsAsync( Arg.Any<string>(), Arg.Any<string?>(), Arg.Any<string?>(), Arg.Any<string>(), Arg.Any<string>(), Arg.Any<IEnumerable<string>>(), Arg.Any<string?>(), Arg.Any<string?>(), Arg.Any<string?>(), Arg.Any<string?>(), Arg.Any<string?>(), Arg.Any<string?>(), Arg.Any<RetryPolicyOptions?>()) .Returns(results); var context = new CommandContext(_serviceProvider); var parseResult = _command.GetCommand().Parse("--subscription sub1 --resource sa1 --metric-names CPU --metric-namespace microsoft.compute/virtualmachines"); // Act var response = await _command.ExecuteAsync(context, parseResult); // Assert Assert.Equal(200, response.Status); Assert.NotNull(response.Results); } [Fact] public async Task ExecuteAsync_NullResults_ReturnsSuccessWithNullResults() { // Arrange _service.QueryMetricsAsync( Arg.Any<string>(), Arg.Any<string?>(), Arg.Any<string?>(), Arg.Any<string>(), Arg.Any<string>(), Arg.Any<IEnumerable<string>>(), Arg.Any<string?>(), Arg.Any<string?>(), Arg.Any<string?>(), Arg.Any<string?>(), Arg.Any<string?>(), Arg.Any<string?>(), Arg.Any<RetryPolicyOptions?>()) .Returns(Task.FromResult((List<MetricResult>)null!)); var context = new CommandContext(_serviceProvider); var parseResult = _command.GetCommand().Parse("--subscription sub1 --resource sa1 --metric-names CPU --metric-namespace microsoft.compute/virtualmachines"); // Act var response = await _command.ExecuteAsync(context, parseResult); // Assert Assert.Equal(200, response.Status); Assert.Null(response.Results); } #endregion private List<MetricResult>? GetResult(ResponseResult? result) { if (result == null) { return null; } var json = JsonSerializer.Serialize(result); return JsonSerializer.Deserialize<MetricsQueryCommandResult>(json)?.results; } private record MetricsQueryCommandResult(List<MetricResult> results) { } }

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/Azure/azure-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server