|
1 | 1 | // Copyright (c) Microsoft Corporation.
|
2 | 2 | // Licensed under the MIT License.
|
3 | 3 |
|
| 4 | +using System.CommandLine; |
4 | 5 | using System.Net;
|
5 |
| -using System.Text; |
6 | 6 | using System.Text.Json;
|
7 |
| -using Azure.Core; |
8 | 7 | using Azure.Mcp.Core.Models.Command;
|
9 | 8 | using Azure.Mcp.Core.Services.Http;
|
10 | 9 | using Azure.Mcp.Tools.Extension.Commands;
|
11 |
| -using Azure.Mcp.Tools.Extension.Options; |
12 | 10 | using Azure.Mcp.Tools.Extension.Services;
|
13 | 11 | using Microsoft.Extensions.DependencyInjection;
|
14 | 12 | using Microsoft.Extensions.Logging;
|
15 | 13 | using NSubstitute;
|
| 14 | +using NSubstitute.ExceptionExtensions; |
16 | 15 | using Xunit;
|
17 | 16 |
|
18 | 17 | namespace Azure.Mcp.Tools.Extension.UnitTests;
|
19 | 18 |
|
20 | 19 | public sealed class CliGenerateCommandTests
|
21 | 20 | {
|
22 | 21 | private readonly IServiceProvider _serviceProvider;
|
23 |
| - private readonly ILogger<CliGenerateCommand> _logger; |
24 | 22 | private readonly IHttpClientService _httpClientService;
|
25 | 23 | private readonly ICliGenerateService _cliGenerateService;
|
| 24 | + private readonly ILogger<CliGenerateCommand> _logger; |
| 25 | + private readonly CliGenerateCommand _command; |
| 26 | + private readonly CommandContext _context; |
| 27 | + private readonly Command _commandDefinition; |
26 | 28 |
|
27 | 29 | public CliGenerateCommandTests()
|
28 | 30 | {
|
29 |
| - _logger = Substitute.For<ILogger<CliGenerateCommand>>(); |
30 |
| - |
31 | 31 | _httpClientService = Substitute.For<IHttpClientService>();
|
32 | 32 | _cliGenerateService = Substitute.For<ICliGenerateService>();
|
| 33 | + _logger = Substitute.For<ILogger<CliGenerateCommand>>(); |
33 | 34 |
|
34 | 35 | var collection = new ServiceCollection();
|
35 | 36 | collection.AddSingleton(_httpClientService);
|
36 | 37 | collection.AddSingleton(_cliGenerateService);
|
37 |
| - |
38 | 38 | _serviceProvider = collection.BuildServiceProvider();
|
| 39 | + _command = new(_logger); |
| 40 | + _context = new(_serviceProvider); |
| 41 | + _commandDefinition = _command.GetCommand(); |
39 | 42 | }
|
40 | 43 |
|
41 | 44 | [Fact]
|
42 |
| - public async Task ExecuteAsync_CanAcquireToken() |
| 45 | + public void Constructor_InitializesCommandCorrectly() |
43 | 46 | {
|
44 |
| - var command = new CliGenerateCommand(_logger); |
| 47 | + var command = _command.GetCommand(); |
| 48 | + Assert.Equal("generate", command.Name); |
| 49 | + Assert.NotNull(command.Description); |
| 50 | + Assert.NotEmpty(command.Description); |
| 51 | + } |
45 | 52 |
|
46 |
| - const string mockResponseBody = "mock response body"; |
47 |
| - _cliGenerateService.GenerateAzureCLICommandAsync(Arg.Any<string>()) |
48 |
| - .Returns( |
49 |
| - new HttpResponseMessage(HttpStatusCode.OK) |
| 53 | + [Theory] |
| 54 | + [InlineData("", false)] |
| 55 | + [InlineData("--intent mock_intent", false)] |
| 56 | + [InlineData("--cli-type az", false)] |
| 57 | + [InlineData("--cli-type wrong_cli_type", false)] |
| 58 | + [InlineData("--intent mock_intent --cli-type az", true)] |
| 59 | + public async Task ExecuteAsync_ValidatesInputCorrectly(string args, bool shouldSucceed) |
| 60 | + { |
| 61 | + // Arrange |
| 62 | + if (shouldSucceed) |
| 63 | + { |
| 64 | + _cliGenerateService.GenerateAzureCLICommandAsync(Arg.Any<string>()) |
| 65 | + .Returns(Task.FromResult(new HttpResponseMessage(HttpStatusCode.OK) |
50 | 66 | {
|
51 |
| - Content = new StringContent(mockResponseBody) |
52 |
| - } |
53 |
| - ); |
| 67 | + Content = new StringContent("Command") |
| 68 | + })); |
| 69 | + } |
54 | 70 |
|
55 |
| - var mockIntent = "\"Create a resource group named 'TestRG' in the 'eastus' region\""; |
56 |
| - var mockCliType = "\"az\""; |
57 |
| - var args = command.GetCommand().Parse($"--{ExtensionOptionDefinitions.CliGenerate.IntentName} {mockIntent} --{ExtensionOptionDefinitions.CliGenerate.CliTypeName} {mockCliType}"); |
58 |
| - var context = new CommandContext(_serviceProvider); |
| 71 | + // Build args from a single string in tests using the test-only splitter |
| 72 | + var parseResult = _commandDefinition.Parse(args); |
59 | 73 |
|
60 |
| - try |
61 |
| - { |
62 |
| - // Act |
63 |
| - var response = await command.ExecuteAsync(context, args); |
| 74 | + // Act |
| 75 | + var response = await _command.ExecuteAsync(_context, parseResult); |
64 | 76 |
|
65 |
| - // Assert |
66 |
| - Assert.NotNull(response); |
67 |
| - Assert.Equal(200, response.Status); |
| 77 | + // Assert |
| 78 | + Assert.Equal(shouldSucceed ? 200 : 400, response.Status); |
| 79 | + if (shouldSucceed) |
| 80 | + { |
68 | 81 | Assert.NotNull(response.Results);
|
69 |
| - using var stream = new MemoryStream(); |
70 |
| - using var writer = new Utf8JsonWriter(stream); |
71 |
| - response.Results.Write(writer); |
72 |
| - writer.Flush(); |
73 |
| - var resultString = Encoding.UTF8.GetString(stream.ToArray()); |
74 |
| - Assert.Contains(mockResponseBody, resultString); |
75 |
| - Assert.Contains(mockCliType, resultString); |
| 82 | + Assert.Equal("Success", response.Message); |
76 | 83 | }
|
77 |
| - finally |
| 84 | + else |
78 | 85 | {
|
79 |
| - // Cleanup |
80 |
| - // noop |
| 86 | + Assert.Contains("required", response.Message.ToLower()); |
81 | 87 | }
|
82 | 88 | }
|
| 89 | + |
| 90 | + [Fact] |
| 91 | + public async Task ExecuteAsync_DeserializationValidation() |
| 92 | + { |
| 93 | + // Arrange |
| 94 | + _cliGenerateService.GenerateAzureCLICommandAsync(Arg.Any<string>()) |
| 95 | + .Returns(Task.FromResult(new HttpResponseMessage(HttpStatusCode.OK) |
| 96 | + { |
| 97 | + Content = new StringContent("Command") |
| 98 | + })); |
| 99 | + |
| 100 | + var parseResult = _commandDefinition.Parse("--intent mock_intent --cli-type az"); |
| 101 | + |
| 102 | + // Act |
| 103 | + var response = await _command.ExecuteAsync(_context, parseResult); |
| 104 | + |
| 105 | + // Assert |
| 106 | + Assert.Equal(200, response.Status); |
| 107 | + Assert.NotNull(response.Results); |
| 108 | + |
| 109 | + var json = JsonSerializer.Serialize(response.Results); |
| 110 | + var result = JsonSerializer.Deserialize(json, ExtensionJsonContext.Default.CliGenerateResult); |
| 111 | + |
| 112 | + Assert.NotNull(result); |
| 113 | + Assert.Equal("az", result.CliType); |
| 114 | + Assert.Equal("Command", result.Command); |
| 115 | + } |
| 116 | + |
| 117 | + [Fact] |
| 118 | + public async Task ExecuteAsync_HandlesServiceErrors() |
| 119 | + { |
| 120 | + // Arrange |
| 121 | + _cliGenerateService.GenerateAzureCLICommandAsync(Arg.Any<string>()).ThrowsAsync(new Exception("Test error")); |
| 122 | + |
| 123 | + var parseResult = _commandDefinition.Parse("--intent mock_intent --cli-type az"); |
| 124 | + |
| 125 | + // Act |
| 126 | + var response = await _command.ExecuteAsync(_context, parseResult); |
| 127 | + |
| 128 | + // Assert |
| 129 | + Assert.Equal(500, response.Status); |
| 130 | + Assert.Contains("Test error", response.Message); |
| 131 | + } |
83 | 132 | }
|
0 commit comments