ResourceLogQueryCommandTests.cs•10.4 kB
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
using System.CommandLine;
using System.CommandLine.Parsing;
using System.Text.Json.Nodes;
using AzureMcp.Core.Models.Command;
using AzureMcp.Core.Options;
using AzureMcp.Monitor.Commands.Log;
using AzureMcp.Monitor.Services;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using NSubstitute;
using Xunit;
namespace AzureMcp.Monitor.UnitTests.Log;
[Trait("Area", "Monitor")]
public sealed class ResourceLogQueryCommandTests
{
    private readonly IServiceProvider _serviceProvider;
    private readonly IMonitorService _monitorService;
    private readonly ILogger<ResourceLogQueryCommand> _logger;
    private readonly ResourceLogQueryCommand _command;
    private readonly CommandContext _context;
    private readonly Parser _parser;
    private const string _knownSubscription = "knownSubscription";
    private const string _knownResourceId = "/subscriptions/sub123/resourceGroups/rg1/providers/Microsoft.Storage/storageAccounts/storage1";
    private const string _knownTable = "StorageEvents";
    private const string _knownQuery = "| limit 10";
    private const string _knownTenant = "knownTenant";
    private const string _knownHours = "24";
    private const string _knownLimit = "100";
    public ResourceLogQueryCommandTests()
    {
        _monitorService = Substitute.For<IMonitorService>();
        _logger = Substitute.For<ILogger<ResourceLogQueryCommand>>();
        var collection = new ServiceCollection();
        collection.AddSingleton(_monitorService);
        _serviceProvider = collection.BuildServiceProvider();
        _command = new(_logger);
        _context = new(_serviceProvider);
        _parser = new(_command.GetCommand());
    }
    [Theory]
    [InlineData($"--subscription {_knownSubscription} --resource-id {_knownResourceId} --table {_knownTable} --query {_knownQuery}", true)]
    [InlineData($"--subscription {_knownSubscription} --resource-id {_knownResourceId} --table {_knownTable} --query {_knownQuery} --hours {_knownHours} --limit {_knownLimit}", true)]
    [InlineData($"--subscription {_knownSubscription} --table {_knownTable} --query {_knownQuery}", false)] // missing resource-id
    [InlineData($"--subscription {_knownSubscription}", false)]
    [InlineData("", false)]
    public async Task ExecuteAsync_ValidatesInputCorrectly(string args, bool shouldSucceed)
    {
        // Arrange
        if (shouldSucceed)
        {
            var mockResults = new List<JsonNode>
            {
                JsonNode.Parse(@"{""TimeGenerated"": ""2023-01-01T12:00:00Z"", ""Message"": ""Resource log entry""}") ?? JsonNode.Parse("{}") ?? new JsonObject(),
                JsonNode.Parse(@"{""TimeGenerated"": ""2023-01-01T12:01:00Z"", ""Message"": ""Another resource log entry""}") ?? JsonNode.Parse("{}") ?? new JsonObject()
            };
            _monitorService.QueryResourceLogs(
                _knownSubscription,
                _knownResourceId,
                _knownQuery,
                _knownTable,
                Arg.Any<int?>(),
                Arg.Any<int?>(),
                Arg.Any<string>(),
                Arg.Any<RetryPolicyOptions>())
                .Returns(mockResults);
        }
        // Act
        var response = await _command.ExecuteAsync(_context, _parser.Parse(args));
        // Assert
        Assert.Equal(shouldSucceed ? 200 : 400, response.Status);
        if (shouldSucceed)
        {
            Assert.NotNull(response.Results);
            Assert.Equal("Success", response.Message);
        }
        else
        {
            Assert.Contains("required", response.Message.ToLower());
        }
    }
    [Fact]
    public async Task ExecuteAsync_ReturnsQueryResults()
    {
        // Arrange
        var mockResults = new List<JsonNode>
        {
            JsonNode.Parse($@"{{""TimeGenerated"": ""2023-01-01T12:00:00Z"", ""ResourceId"": ""{_knownResourceId}"", ""Level"": ""Info""}}") ?? new JsonObject(),
            JsonNode.Parse($@"{{""TimeGenerated"": ""2023-01-01T12:01:00Z"", ""ResourceId"": ""{_knownResourceId}"", ""Level"": ""Warning""}}") ?? new JsonObject(),
            JsonNode.Parse($@"{{""TimeGenerated"": ""2023-01-01T12:02:00Z"", ""ResourceId"": ""{_knownResourceId}"", ""Level"": ""Error""}}") ?? new JsonObject()
        };
        _monitorService.QueryResourceLogs(
            _knownSubscription,
            _knownResourceId,
            _knownQuery,
            _knownTable,
            Arg.Any<int?>(),
            Arg.Any<int?>(),
            Arg.Any<string>(),
            Arg.Any<RetryPolicyOptions>())
            .Returns(mockResults);
        var args = _parser.Parse([
            "--subscription", _knownSubscription,
            "--resource-id", _knownResourceId,
            "--table", _knownTable,
            "--query", _knownQuery
        ]);
        // Act
        var response = await _command.ExecuteAsync(_context, args);
        // Assert
        Assert.Equal(200, response.Status);
        Assert.NotNull(response.Results);
        // Verify the mock was called
        await _monitorService.Received(1).QueryResourceLogs(
            _knownSubscription,
            _knownResourceId,
            _knownQuery,
            _knownTable,
            Arg.Any<int?>(),
            Arg.Any<int?>(),
            Arg.Any<string>(),
            Arg.Any<RetryPolicyOptions>());
    }
    [Fact]
    public async Task ExecuteAsync_CallsServiceWithCorrectParameters()
    {
        // Arrange
        var mockResults = new List<JsonNode> { JsonNode.Parse(@"{""result"": ""data""}") ?? new JsonObject() };
        _monitorService.QueryResourceLogs(
            _knownSubscription,
            _knownResourceId,
            _knownQuery,
            _knownTable,
            int.Parse(_knownHours),
            int.Parse(_knownLimit),
            _knownTenant,
            Arg.Any<RetryPolicyOptions>())
            .Returns(mockResults);
        var args = _parser.Parse([
            "--subscription", _knownSubscription,
            "--resource-id", _knownResourceId,
            "--table", _knownTable,
            "--query", _knownQuery,
            "--hours", _knownHours,
            "--limit", _knownLimit,
            "--tenant", _knownTenant
        ]);
        // Act
        var response = await _command.ExecuteAsync(_context, args);
        // Assert
        Assert.Equal(200, response.Status);
        await _monitorService.Received(1).QueryResourceLogs(
            _knownSubscription,
            _knownResourceId,
            _knownQuery,
            _knownTable,
            int.Parse(_knownHours),
            int.Parse(_knownLimit),
            _knownTenant,
            Arg.Any<RetryPolicyOptions>());
    }
    [Fact]
    public async Task ExecuteAsync_WithDefaultParameters_UsesExpectedDefaults()
    {
        // Arrange
        var mockResults = new List<JsonNode> { JsonNode.Parse(@"{""result"": ""data""}") ?? new JsonObject() };
        _monitorService.QueryResourceLogs(
            _knownSubscription,
            _knownResourceId,
            _knownQuery,
            _knownTable,
            Arg.Any<int?>(),
            Arg.Any<int?>(),
            Arg.Any<string>(),
            Arg.Any<RetryPolicyOptions>())
            .Returns(mockResults);
        var args = _parser.Parse([
            "--subscription", _knownSubscription,
            "--resource-id", _knownResourceId,
            "--table", _knownTable,
            "--query", _knownQuery
        ]);
        // Act
        var response = await _command.ExecuteAsync(_context, args);
        // Assert
        Assert.Equal(200, response.Status);
        await _monitorService.Received(1).QueryResourceLogs(
            _knownSubscription,
            _knownResourceId,
            _knownQuery,
            _knownTable,
            Arg.Any<int?>(), // Default hours
            Arg.Any<int?>(), // Default limit
            Arg.Any<string>(),
            Arg.Any<RetryPolicyOptions>());
    }
    [Fact]
    public async Task ExecuteAsync_HandlesServiceErrors()
    {
        // Arrange
        _monitorService.QueryResourceLogs(
            _knownSubscription,
            _knownResourceId,
            _knownQuery,
            _knownTable,
            Arg.Any<int?>(),
            Arg.Any<int?>(),
            Arg.Any<string>(),
            Arg.Any<RetryPolicyOptions>())
            .Returns(Task.FromException<List<JsonNode>>(new Exception("Test error")));
        var args = _parser.Parse([
            "--subscription", _knownSubscription,
            "--resource-id", _knownResourceId,
            "--table", _knownTable,
            "--query", _knownQuery
        ]);
        // Act
        var response = await _command.ExecuteAsync(_context, args);
        // Assert
        Assert.Equal(500, response.Status);
        Assert.Contains("Test error", response.Message);
        Assert.Contains("troubleshooting", response.Message);
    }
    [Fact]
    public async Task ExecuteAsync_WithComplexResourceId_HandlesCorrectly()
    {
        // Arrange
        var complexResourceId = "/subscriptions/12345678-1234-1234-1234-123456789012/resourceGroups/my-rg/providers/Microsoft.Compute/virtualMachines/my-vm";
        var query = "| where Level == 'Error'";
        var table = "VMEvents";
        var mockResults = new List<JsonNode> { JsonNode.Parse(@"{""result"": ""vm data""}") ?? new JsonObject() };
        _monitorService.QueryResourceLogs(
            _knownSubscription,
            complexResourceId,
            query,
            table,
            Arg.Any<int?>(),
            Arg.Any<int?>(),
            Arg.Any<string>(),
            Arg.Any<RetryPolicyOptions>())
            .Returns(mockResults);
        var args = _parser.Parse([
            "--subscription", _knownSubscription,
            "--resource-id", complexResourceId,
            "--table", table,
            "--query", query
        ]);
        // Act
        var response = await _command.ExecuteAsync(_context, args);
        // Assert
        Assert.Equal(200, response.Status);
        await _monitorService.Received(1).QueryResourceLogs(
            _knownSubscription,
            complexResourceId,
            query,
            table,
            Arg.Any<int?>(),
            Arg.Any<int?>(),
            Arg.Any<string>(),
            Arg.Any<RetryPolicyOptions>());
    }
}