This repository was archived by the owner on Feb 6, 2026. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 356
Add appconfig unit tests #749
Merged
chidozieononiwu
merged 2 commits into
Azure:main
from
chidozieononiwu:appconfig-unit-two
Jul 30, 2025
Merged
Changes from all commits
Commits
Show all changes
2 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
148 changes: 148 additions & 0 deletions
148
areas/appconfig/tests/AzureMcp.AppConfig.UnitTests/Account/AccountListCommandTests.cs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,148 @@ | ||
| // Copyright (c) Microsoft Corporation. | ||
| // Licensed under the MIT License. | ||
|
|
||
| using System.CommandLine.Parsing; | ||
| using System.Text.Json; | ||
| using AzureMcp.AppConfig.Commands.Account; | ||
| using AzureMcp.AppConfig.Models; | ||
| using AzureMcp.AppConfig.Services; | ||
| using AzureMcp.Core.Models.Command; | ||
| using AzureMcp.Core.Options; | ||
| using Microsoft.Extensions.DependencyInjection; | ||
| using Microsoft.Extensions.Logging; | ||
| using NSubstitute; | ||
| using NSubstitute.ExceptionExtensions; | ||
| using Xunit; | ||
| using static AzureMcp.AppConfig.Commands.Account.AccountListCommand; | ||
|
|
||
| namespace AzureMcp.AppConfig.UnitTests.Account; | ||
|
|
||
| [Trait("Area", "AppConfig")] | ||
| public class AccountListCommandTests | ||
| { | ||
| private readonly IServiceProvider _serviceProvider; | ||
| private readonly IAppConfigService _appConfigService; | ||
| private readonly ILogger<AccountListCommand> _logger; | ||
| private readonly AccountListCommand _command; | ||
| private readonly CommandContext _context; | ||
| private readonly Parser _parser; | ||
|
|
||
| public AccountListCommandTests() | ||
| { | ||
| _appConfigService = Substitute.For<IAppConfigService>(); | ||
| _logger = Substitute.For<ILogger<AccountListCommand>>(); | ||
|
|
||
| _command = new(_logger); | ||
| _parser = new(_command.GetCommand()); | ||
| _serviceProvider = new ServiceCollection() | ||
| .AddSingleton(_appConfigService) | ||
| .BuildServiceProvider(); | ||
| _context = new(_serviceProvider); | ||
| } | ||
|
|
||
| [Fact] | ||
| public async Task ExecuteAsync_ReturnsAccounts_WhenAccountsExist() | ||
| { | ||
| // Arrange | ||
| var expectedAccounts = new List<AppConfigurationAccount> | ||
| { | ||
| new() { Name = "account1", Location = "East US", Endpoint = "https://account1.azconfig.io" }, | ||
| new() { Name = "account2", Location = "West US", Endpoint = "https://account2.azconfig.io" } | ||
| }; | ||
| _appConfigService.GetAppConfigAccounts( | ||
| "sub123", | ||
| Arg.Any<string?>(), | ||
| Arg.Any<RetryPolicyOptions?>()) | ||
| .Returns(expectedAccounts); | ||
|
|
||
| var args = _parser.Parse(["--subscription", "sub123"]); | ||
|
|
||
| // Act | ||
| var response = await _command.ExecuteAsync(_context, args); | ||
|
|
||
| // Assert | ||
| Assert.Equal(200, response.Status); | ||
| Assert.NotNull(response.Results); | ||
|
|
||
| var json = JsonSerializer.Serialize(response.Results); | ||
| var result = JsonSerializer.Deserialize<AccountListCommandResult>(json, new JsonSerializerOptions | ||
| { | ||
| PropertyNamingPolicy = JsonNamingPolicy.CamelCase | ||
| }); | ||
|
|
||
| Assert.NotNull(result); | ||
| Assert.Equal(2, result.Accounts.Count); | ||
| Assert.Equal("account1", result.Accounts[0].Name); | ||
| Assert.Equal("account2", result.Accounts[1].Name); | ||
| } | ||
|
|
||
| [Fact] | ||
| public async Task ExecuteAsync_ReturnsNull_WhenNoAccountsExist() | ||
| { | ||
| // Arrange | ||
| var expectedAccounts = new List<AppConfigurationAccount>(); | ||
|
|
||
| _appConfigService.GetAppConfigAccounts( | ||
| Arg.Any<string>(), | ||
| Arg.Any<string>(), | ||
| Arg.Any<RetryPolicyOptions>()) | ||
| .Returns(expectedAccounts); | ||
|
|
||
| var args = _parser.Parse(["--subscription", "sub123"]); | ||
|
|
||
| // Act | ||
| var response = await _command.ExecuteAsync(_context, args); | ||
|
|
||
| // Assert | ||
| Assert.Equal(200, response.Status); | ||
| Assert.Null(response.Results); | ||
| } | ||
|
|
||
| [Fact] | ||
| public async Task ExecuteAsync_Returns500_WhenServiceThrowsException() | ||
| { | ||
| // Arrange | ||
| _appConfigService.GetAppConfigAccounts( | ||
| Arg.Any<string>(), | ||
| Arg.Any<string>(), | ||
| Arg.Any<RetryPolicyOptions>()) | ||
| .Returns(Task.FromException<List<AppConfigurationAccount>>(new Exception("Service error"))); | ||
|
|
||
| var args = _parser.Parse(["--subscription", "sub123"]); | ||
|
|
||
| // Act | ||
| var response = await _command.ExecuteAsync(_context, args); | ||
|
|
||
| // Assert | ||
| Assert.Equal(500, response.Status); | ||
| Assert.Contains("Service error", response.Message); | ||
| } | ||
|
|
||
| [Fact] | ||
| public async Task ExecuteAsync_Returns400_WhenSubscriptionIsMissing() | ||
| { | ||
| // Arrange && Act | ||
| var response = await _command.ExecuteAsync(_context, _parser.Parse([])); | ||
|
|
||
| // Assert | ||
| Assert.Equal(400, response.Status); | ||
| Assert.Contains("required", response.Message.ToLower()); | ||
| } | ||
|
|
||
| [Fact] | ||
| public async Task ExecuteAsync_Returns503_WhenServiceIsUnavailable() | ||
| { | ||
| // Arrange | ||
| _appConfigService.GetAppConfigAccounts(Arg.Any<string>(), Arg.Any<string>(), Arg.Any<RetryPolicyOptions>()) | ||
| .ThrowsAsync(new HttpRequestException("Service Unavailable", null, System.Net.HttpStatusCode.ServiceUnavailable)); | ||
|
|
||
| var args = _parser.Parse(["--subscription", "sub123"]); | ||
|
|
||
| // Act | ||
| var response = await _command.ExecuteAsync(_context, args); | ||
|
|
||
| // Assert | ||
| Assert.Equal(503, response.Status); | ||
| Assert.Contains("Service Unavailable", response.Message); | ||
| } | ||
| } | ||
17 changes: 17 additions & 0 deletions
17
areas/appconfig/tests/AzureMcp.AppConfig.UnitTests/AzureMcp.AppConfig.UnitTests.csproj
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,17 @@ | ||
| <Project Sdk="Microsoft.NET.Sdk"> | ||
| <PropertyGroup> | ||
| <OutputType>Exe</OutputType> | ||
| <IsTestProject>true</IsTestProject> | ||
| </PropertyGroup> | ||
| <ItemGroup> | ||
| <PackageReference Include="NSubstitute" /> | ||
| <PackageReference Include="NSubstitute.Analyzers.CSharp" /> | ||
| <PackageReference Include="xunit.v3" /> | ||
| <PackageReference Include="xunit.runner.visualstudio" /> | ||
| <PackageReference Include="coverlet.collector" /> | ||
| </ItemGroup> | ||
| <ItemGroup> | ||
| <ProjectReference Include="..\..\..\..\core\tests\AzureMcp.Tests\AzureMcp.Tests.csproj" /> | ||
| <ProjectReference Include="..\..\src\AzureMcp.AppConfig\AzureMcp.AppConfig.csproj" /> | ||
| </ItemGroup> | ||
| </Project> |
152 changes: 152 additions & 0 deletions
152
areas/appconfig/tests/AzureMcp.AppConfig.UnitTests/KeyValue/KeyValueDeleteCommandTests.cs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,152 @@ | ||
| // Copyright (c) Microsoft Corporation. | ||
| // Licensed under the MIT License. | ||
|
|
||
| using System.CommandLine.Parsing; | ||
| using System.Text.Json; | ||
| using AzureMcp.AppConfig.Commands.KeyValue; | ||
| using AzureMcp.AppConfig.Services; | ||
| using AzureMcp.Core.Models.Command; | ||
| using AzureMcp.Core.Options; | ||
| using Microsoft.Extensions.DependencyInjection; | ||
| using Microsoft.Extensions.Logging; | ||
| using NSubstitute; | ||
| using NSubstitute.ExceptionExtensions; | ||
| using Xunit; | ||
| using static AzureMcp.AppConfig.Commands.KeyValue.KeyValueDeleteCommand; | ||
|
|
||
| namespace AzureMcp.AppConfig.UnitTests.KeyValue; | ||
|
|
||
| [Trait("Area", "AppConfig")] | ||
| public class KeyValueDeleteCommandTests | ||
| { | ||
| private readonly IServiceProvider _serviceProvider; | ||
| private readonly IAppConfigService _appConfigService; | ||
| private readonly ILogger<KeyValueDeleteCommand> _logger; | ||
| private readonly KeyValueDeleteCommand _command; | ||
| private readonly CommandContext _context; | ||
| private readonly Parser _parser; | ||
|
|
||
| public KeyValueDeleteCommandTests() | ||
| { | ||
| _appConfigService = Substitute.For<IAppConfigService>(); | ||
| _logger = Substitute.For<ILogger<KeyValueDeleteCommand>>(); | ||
|
|
||
| _command = new(_logger); | ||
| _parser = new(_command.GetCommand()); | ||
| _serviceProvider = new ServiceCollection() | ||
| .AddSingleton(_appConfigService) | ||
| .BuildServiceProvider(); | ||
| _context = new(_serviceProvider); | ||
| } | ||
|
|
||
| [Fact] | ||
| public async Task ExecuteAsync_DeletesKeyValue_WhenValidParametersProvided() | ||
| { | ||
| // Arrange | ||
| var args = _parser.Parse([ | ||
| "--subscription", "sub123", | ||
| "--account-name", "account1", | ||
| "--key", "my-key" | ||
| ]); | ||
|
|
||
| // Act | ||
| var response = await _command.ExecuteAsync(_context, args); | ||
|
|
||
| // Assert | ||
| Assert.Equal(200, response.Status); | ||
| await _appConfigService.Received(1).DeleteKeyValue( | ||
| "account1", | ||
| "my-key", | ||
| "sub123", | ||
| null, | ||
| Arg.Any<RetryPolicyOptions>(), | ||
| null); | ||
|
|
||
| var json = JsonSerializer.Serialize(response.Results); | ||
| var result = JsonSerializer.Deserialize<KeyValueDeleteCommandResult>(json, new JsonSerializerOptions | ||
| { | ||
| PropertyNamingPolicy = JsonNamingPolicy.CamelCase | ||
| }); | ||
|
|
||
| Assert.NotNull(result); | ||
| Assert.Equal("my-key", result.Key); | ||
| } | ||
|
|
||
| [Fact] | ||
| public async Task ExecuteAsync_DeletesKeyValueWithLabel_WhenLabelProvided() | ||
| { | ||
| // Arrange | ||
| var args = _parser.Parse([ | ||
| "--subscription", "sub123", | ||
| "--account-name", "account1", | ||
| "--key", "my-key", | ||
| "--label", "prod" | ||
| ]); | ||
|
|
||
| // Act | ||
| var response = await _command.ExecuteAsync(_context, args); | ||
|
|
||
| // Assert | ||
| Assert.Equal(200, response.Status); | ||
| await _appConfigService.Received(1).DeleteKeyValue( | ||
| "account1", | ||
| "my-key", | ||
| "sub123", null, | ||
|
chidozieononiwu marked this conversation as resolved.
|
||
| Arg.Any<RetryPolicyOptions>(), | ||
| "prod"); | ||
|
|
||
| var json = JsonSerializer.Serialize(response.Results); | ||
| var result = JsonSerializer.Deserialize<KeyValueDeleteCommandResult>(json, new JsonSerializerOptions | ||
| { | ||
| PropertyNamingPolicy = JsonNamingPolicy.CamelCase | ||
| }); | ||
|
|
||
| Assert.NotNull(result); | ||
| Assert.Equal("my-key", result.Key); | ||
| Assert.Equal("prod", result.Label); | ||
| } | ||
|
|
||
| [Fact] | ||
| public async Task ExecuteAsync_Returns500_WhenServiceThrowsException() | ||
| { | ||
| // Arrange | ||
| _appConfigService.DeleteKeyValue( | ||
| Arg.Any<string>(), | ||
| Arg.Any<string>(), | ||
| Arg.Any<string>(), | ||
| Arg.Any<string>(), | ||
| Arg.Any<RetryPolicyOptions>(), | ||
| Arg.Any<string>()) | ||
| .ThrowsAsync(new Exception("Failed to delete key-value")); | ||
|
|
||
| var args = _parser.Parse([ | ||
| "--subscription", "sub123", | ||
| "--account-name", "account1", | ||
| "--key", "my-key" | ||
| ]); | ||
|
|
||
| // Act | ||
| var response = await _command.ExecuteAsync(_context, args); | ||
|
|
||
| // Assert | ||
| Assert.Equal(500, response.Status); | ||
| Assert.Contains("Failed to delete key-value", response.Message); | ||
| } | ||
|
|
||
| [Theory] | ||
| [InlineData("--account-name", "account1", "--key", "my-key")] // Missing subscription | ||
| [InlineData("--subscription", "sub123", "--key", "my-key")] // Missing account-name | ||
| [InlineData("--subscription", "sub123", "--account-name", "account1")] // Missing key | ||
| public async Task ExecuteAsync_Returns400_WhenRequiredParametersAreMissing(params string[] args) | ||
| { | ||
| // Arrange | ||
| var parseResult = _parser.Parse(args); | ||
|
|
||
| // Act | ||
| var response = await _command.ExecuteAsync(_context, parseResult); | ||
|
|
||
| // Assert | ||
| Assert.Equal(400, response.Status); | ||
| Assert.Contains("required", response.Message.ToLower()); | ||
| } | ||
| } | ||
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.