Skip to content
This repository was archived by the owner on Sep 4, 2025. It is now read-only.

Commit 5ca3874

Browse files
authored
Added missing Monitor unit tests (#701)
* Added missing Monitor unit tests * Refactored bit
1 parent 54eed65 commit 5ca3874

11 files changed

+1150
-6
lines changed

areas/monitor/tests/AzureMcp.Monitor.UnitTests/HealthModels/Entity/EntityGetHealthCommandTests.cs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@
99
using AzureMcp.Core.Options;
1010
using AzureMcp.Monitor.Commands.HealthModels.Entity;
1111
using AzureMcp.Monitor.Services;
12-
using AzureMcp.Tests;
1312
using Microsoft.Extensions.DependencyInjection;
1413
using Microsoft.Extensions.Logging;
1514
using ModelContextProtocol.Server;
Lines changed: 294 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,294 @@
1+
// Copyright (c) Microsoft Corporation.
2+
// Licensed under the MIT License.
3+
4+
using System.CommandLine;
5+
using System.CommandLine.Parsing;
6+
using System.Text.Json.Nodes;
7+
using AzureMcp.Core.Models.Command;
8+
using AzureMcp.Core.Options;
9+
using AzureMcp.Monitor.Commands.Log;
10+
using AzureMcp.Monitor.Services;
11+
using Microsoft.Extensions.DependencyInjection;
12+
using Microsoft.Extensions.Logging;
13+
using NSubstitute;
14+
using Xunit;
15+
16+
namespace AzureMcp.Monitor.UnitTests.Log;
17+
18+
[Trait("Area", "Monitor")]
19+
public sealed class ResourceLogQueryCommandTests
20+
{
21+
private readonly IServiceProvider _serviceProvider;
22+
private readonly IMonitorService _monitorService;
23+
private readonly ILogger<ResourceLogQueryCommand> _logger;
24+
private readonly ResourceLogQueryCommand _command;
25+
private readonly CommandContext _context;
26+
private readonly Parser _parser;
27+
28+
private const string _knownSubscription = "knownSubscription";
29+
private const string _knownResourceId = "/subscriptions/sub123/resourceGroups/rg1/providers/Microsoft.Storage/storageAccounts/storage1";
30+
private const string _knownTable = "StorageEvents";
31+
private const string _knownQuery = "| limit 10";
32+
private const string _knownTenant = "knownTenant";
33+
private const string _knownHours = "24";
34+
private const string _knownLimit = "100";
35+
36+
public ResourceLogQueryCommandTests()
37+
{
38+
_monitorService = Substitute.For<IMonitorService>();
39+
_logger = Substitute.For<ILogger<ResourceLogQueryCommand>>();
40+
41+
var collection = new ServiceCollection();
42+
collection.AddSingleton(_monitorService);
43+
_serviceProvider = collection.BuildServiceProvider();
44+
45+
_command = new(_logger);
46+
_context = new(_serviceProvider);
47+
_parser = new(_command.GetCommand());
48+
}
49+
50+
[Theory]
51+
[InlineData($"--subscription {_knownSubscription} --resource-id {_knownResourceId} --table {_knownTable} --query {_knownQuery}", true)]
52+
[InlineData($"--subscription {_knownSubscription} --resource-id {_knownResourceId} --table {_knownTable} --query {_knownQuery} --hours {_knownHours} --limit {_knownLimit}", true)]
53+
[InlineData($"--subscription {_knownSubscription} --table {_knownTable} --query {_knownQuery}", false)] // missing resource-id
54+
[InlineData($"--subscription {_knownSubscription}", false)]
55+
[InlineData("", false)]
56+
public async Task ExecuteAsync_ValidatesInputCorrectly(string args, bool shouldSucceed)
57+
{
58+
// Arrange
59+
if (shouldSucceed)
60+
{
61+
var mockResults = new List<JsonNode>
62+
{
63+
JsonNode.Parse(@"{""TimeGenerated"": ""2023-01-01T12:00:00Z"", ""Message"": ""Resource log entry""}") ?? JsonNode.Parse("{}") ?? new JsonObject(),
64+
JsonNode.Parse(@"{""TimeGenerated"": ""2023-01-01T12:01:00Z"", ""Message"": ""Another resource log entry""}") ?? JsonNode.Parse("{}") ?? new JsonObject()
65+
};
66+
_monitorService.QueryResourceLogs(
67+
_knownSubscription,
68+
_knownResourceId,
69+
_knownQuery,
70+
_knownTable,
71+
Arg.Any<int?>(),
72+
Arg.Any<int?>(),
73+
Arg.Any<string>(),
74+
Arg.Any<RetryPolicyOptions>())
75+
.Returns(mockResults);
76+
}
77+
78+
// Act
79+
var response = await _command.ExecuteAsync(_context, _parser.Parse(args));
80+
81+
// Assert
82+
Assert.Equal(shouldSucceed ? 200 : 400, response.Status);
83+
if (shouldSucceed)
84+
{
85+
Assert.NotNull(response.Results);
86+
Assert.Equal("Success", response.Message);
87+
}
88+
else
89+
{
90+
Assert.Contains("required", response.Message.ToLower());
91+
}
92+
}
93+
94+
[Fact]
95+
public async Task ExecuteAsync_ReturnsQueryResults()
96+
{
97+
// Arrange
98+
var mockResults = new List<JsonNode>
99+
{
100+
JsonNode.Parse($@"{{""TimeGenerated"": ""2023-01-01T12:00:00Z"", ""ResourceId"": ""{_knownResourceId}"", ""Level"": ""Info""}}") ?? new JsonObject(),
101+
JsonNode.Parse($@"{{""TimeGenerated"": ""2023-01-01T12:01:00Z"", ""ResourceId"": ""{_knownResourceId}"", ""Level"": ""Warning""}}") ?? new JsonObject(),
102+
JsonNode.Parse($@"{{""TimeGenerated"": ""2023-01-01T12:02:00Z"", ""ResourceId"": ""{_knownResourceId}"", ""Level"": ""Error""}}") ?? new JsonObject()
103+
};
104+
_monitorService.QueryResourceLogs(
105+
_knownSubscription,
106+
_knownResourceId,
107+
_knownQuery,
108+
_knownTable,
109+
Arg.Any<int?>(),
110+
Arg.Any<int?>(),
111+
Arg.Any<string>(),
112+
Arg.Any<RetryPolicyOptions>())
113+
.Returns(mockResults);
114+
115+
var args = _parser.Parse([
116+
"--subscription", _knownSubscription,
117+
"--resource-id", _knownResourceId,
118+
"--table", _knownTable,
119+
"--query", _knownQuery
120+
]);
121+
122+
// Act
123+
var response = await _command.ExecuteAsync(_context, args);
124+
125+
// Assert
126+
Assert.Equal(200, response.Status);
127+
Assert.NotNull(response.Results);
128+
129+
// Verify the mock was called
130+
await _monitorService.Received(1).QueryResourceLogs(
131+
_knownSubscription,
132+
_knownResourceId,
133+
_knownQuery,
134+
_knownTable,
135+
Arg.Any<int?>(),
136+
Arg.Any<int?>(),
137+
Arg.Any<string>(),
138+
Arg.Any<RetryPolicyOptions>());
139+
}
140+
141+
[Fact]
142+
public async Task ExecuteAsync_CallsServiceWithCorrectParameters()
143+
{
144+
// Arrange
145+
var mockResults = new List<JsonNode> { JsonNode.Parse(@"{""result"": ""data""}") ?? new JsonObject() };
146+
_monitorService.QueryResourceLogs(
147+
_knownSubscription,
148+
_knownResourceId,
149+
_knownQuery,
150+
_knownTable,
151+
int.Parse(_knownHours),
152+
int.Parse(_knownLimit),
153+
_knownTenant,
154+
Arg.Any<RetryPolicyOptions>())
155+
.Returns(mockResults);
156+
157+
var args = _parser.Parse([
158+
"--subscription", _knownSubscription,
159+
"--resource-id", _knownResourceId,
160+
"--table", _knownTable,
161+
"--query", _knownQuery,
162+
"--hours", _knownHours,
163+
"--limit", _knownLimit,
164+
"--tenant", _knownTenant
165+
]);
166+
167+
// Act
168+
var response = await _command.ExecuteAsync(_context, args);
169+
170+
// Assert
171+
Assert.Equal(200, response.Status);
172+
await _monitorService.Received(1).QueryResourceLogs(
173+
_knownSubscription,
174+
_knownResourceId,
175+
_knownQuery,
176+
_knownTable,
177+
int.Parse(_knownHours),
178+
int.Parse(_knownLimit),
179+
_knownTenant,
180+
Arg.Any<RetryPolicyOptions>());
181+
}
182+
183+
[Fact]
184+
public async Task ExecuteAsync_WithDefaultParameters_UsesExpectedDefaults()
185+
{
186+
// Arrange
187+
var mockResults = new List<JsonNode> { JsonNode.Parse(@"{""result"": ""data""}") ?? new JsonObject() };
188+
_monitorService.QueryResourceLogs(
189+
_knownSubscription,
190+
_knownResourceId,
191+
_knownQuery,
192+
_knownTable,
193+
Arg.Any<int?>(),
194+
Arg.Any<int?>(),
195+
Arg.Any<string>(),
196+
Arg.Any<RetryPolicyOptions>())
197+
.Returns(mockResults);
198+
199+
var args = _parser.Parse([
200+
"--subscription", _knownSubscription,
201+
"--resource-id", _knownResourceId,
202+
"--table", _knownTable,
203+
"--query", _knownQuery
204+
]);
205+
206+
// Act
207+
var response = await _command.ExecuteAsync(_context, args);
208+
209+
// Assert
210+
Assert.Equal(200, response.Status);
211+
await _monitorService.Received(1).QueryResourceLogs(
212+
_knownSubscription,
213+
_knownResourceId,
214+
_knownQuery,
215+
_knownTable,
216+
Arg.Any<int?>(), // Default hours
217+
Arg.Any<int?>(), // Default limit
218+
Arg.Any<string>(),
219+
Arg.Any<RetryPolicyOptions>());
220+
}
221+
222+
[Fact]
223+
public async Task ExecuteAsync_HandlesServiceErrors()
224+
{
225+
// Arrange
226+
_monitorService.QueryResourceLogs(
227+
_knownSubscription,
228+
_knownResourceId,
229+
_knownQuery,
230+
_knownTable,
231+
Arg.Any<int?>(),
232+
Arg.Any<int?>(),
233+
Arg.Any<string>(),
234+
Arg.Any<RetryPolicyOptions>())
235+
.Returns(Task.FromException<List<JsonNode>>(new Exception("Test error")));
236+
237+
var args = _parser.Parse([
238+
"--subscription", _knownSubscription,
239+
"--resource-id", _knownResourceId,
240+
"--table", _knownTable,
241+
"--query", _knownQuery
242+
]);
243+
244+
// Act
245+
var response = await _command.ExecuteAsync(_context, args);
246+
247+
// Assert
248+
Assert.Equal(500, response.Status);
249+
Assert.Contains("Test error", response.Message);
250+
Assert.Contains("troubleshooting", response.Message);
251+
}
252+
253+
[Fact]
254+
public async Task ExecuteAsync_WithComplexResourceId_HandlesCorrectly()
255+
{
256+
// Arrange
257+
var complexResourceId = "/subscriptions/12345678-1234-1234-1234-123456789012/resourceGroups/my-rg/providers/Microsoft.Compute/virtualMachines/my-vm";
258+
var query = "| where Level == 'Error'";
259+
var table = "VMEvents";
260+
var mockResults = new List<JsonNode> { JsonNode.Parse(@"{""result"": ""vm data""}") ?? new JsonObject() };
261+
_monitorService.QueryResourceLogs(
262+
_knownSubscription,
263+
complexResourceId,
264+
query,
265+
table,
266+
Arg.Any<int?>(),
267+
Arg.Any<int?>(),
268+
Arg.Any<string>(),
269+
Arg.Any<RetryPolicyOptions>())
270+
.Returns(mockResults);
271+
272+
var args = _parser.Parse([
273+
"--subscription", _knownSubscription,
274+
"--resource-id", complexResourceId,
275+
"--table", table,
276+
"--query", query
277+
]);
278+
279+
// Act
280+
var response = await _command.ExecuteAsync(_context, args);
281+
282+
// Assert
283+
Assert.Equal(200, response.Status);
284+
await _monitorService.Received(1).QueryResourceLogs(
285+
_knownSubscription,
286+
complexResourceId,
287+
query,
288+
table,
289+
Arg.Any<int?>(),
290+
Arg.Any<int?>(),
291+
Arg.Any<string>(),
292+
Arg.Any<RetryPolicyOptions>());
293+
}
294+
}

0 commit comments

Comments
 (0)