AppConfigCommandTests.cs•17.6 kB
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
using System.Text.Json;
using AzureMcp.AppConfig.Services;
using AzureMcp.Core.Services.Azure.Subscription;
using AzureMcp.Core.Services.Azure.Tenant;
using AzureMcp.Core.Services.Caching;
using AzureMcp.Tests;
using AzureMcp.Tests.Client;
using AzureMcp.Tests.Client.Helpers;
using Microsoft.Extensions.Caching.Memory;
using Xunit;
namespace AzureMcp.AppConfig.LiveTests;
public class AppConfigCommandTests : CommandTestsBase,
    IClassFixture<LiveTestFixture>
{
    private const string AccountsKey = "accounts";
    private const string SettingsKey = "settings";
    private readonly AppConfigService _appConfigService;
    private readonly string _subscriptionId;
    private readonly string _accountName;
    public AppConfigCommandTests(LiveTestFixture liveTestFixture, ITestOutputHelper output) : base(liveTestFixture, output)
    {
        var memoryCache = new MemoryCache(Microsoft.Extensions.Options.Options.Create(new MemoryCacheOptions()));
        var cacheService = new CacheService(memoryCache);
        var tenantService = new TenantService(cacheService);
        var subscriptionService = new SubscriptionService(cacheService, tenantService);
        _appConfigService = new AppConfigService(subscriptionService, tenantService);
        _subscriptionId = Settings.SubscriptionId;
        _accountName = Settings.ResourceBaseName;
    }
    [Fact]
    public async Task Should_list_appconfig_accounts()
    {
        // act
        var result = await CallToolAsync(
            "azmcp_appconfig_account_list",
            new()
            {
                { "subscription", _subscriptionId }
            });
        // assert
        var accountsArray = result.AssertProperty(AccountsKey);
        Assert.Equal(JsonValueKind.Array, accountsArray.ValueKind);
        Assert.NotEmpty(accountsArray.EnumerateArray());
        Assert.Contains(accountsArray.EnumerateArray(), acc => acc.GetProperty("name").GetString() == _accountName);
    }
    [Fact]
    public async Task Should_list_appconfig_kvs()
    {
        // arrange
        const string key0 = "foo";
        const string value0 = "fo-value";
        const string key1 = "bar";
        const string value1 = "bar-value";
        await _appConfigService.SetKeyValue(_accountName, key0, value0, _subscriptionId);
        await _appConfigService.SetKeyValue(_accountName, key1, value1, _subscriptionId);
        // act
        var result = await CallToolAsync(
            "azmcp_appconfig_kv_list",
            new()
            {
                { "subscription", _subscriptionId },
                { "account", _accountName }
            });
        // assert
        var kvsArray = result.AssertProperty(SettingsKey);
        Assert.Equal(JsonValueKind.Array, kvsArray.ValueKind);
        Assert.NotEmpty(kvsArray.EnumerateArray());
        var foo = kvsArray.EnumerateArray().FirstOrDefault(kv => kv.GetProperty("key").GetString() == key0);
        var bar = kvsArray.EnumerateArray().FirstOrDefault(kv => kv.GetProperty("key").GetString() == key1);
        Assert.Equal(JsonValueKind.Object, foo.ValueKind);
        Assert.Equal(value0, foo.GetProperty("value").GetString());
        Assert.Equal(JsonValueKind.Object, bar.ValueKind);
        Assert.Equal(value1, bar.GetProperty("value").GetString());
    }
    [Fact]
    public async Task Should_list_appconfig_kvs_with_key_and_label()
    {
        // arrange
        const string key = "foo1";
        const string value = "foo-value";
        const string label = "foobar";
        await _appConfigService.SetKeyValue(_accountName, key, value, _subscriptionId, label: label);
        // act
        var result = await CallToolAsync(
            "azmcp_appconfig_kv_list",
            new()
            {
                { "subscription", _subscriptionId },
                { "account", _accountName },
                { "key", key },
                { "label", label }
            });
        // assert
        var kvsArray = result.AssertProperty(SettingsKey);
        Assert.Equal(JsonValueKind.Array, kvsArray.ValueKind);
        Assert.NotEmpty(kvsArray.EnumerateArray());
        var found = kvsArray.EnumerateArray().FirstOrDefault(kv => kv.GetProperty("key").GetString() == key && kv.GetProperty("label").GetString() == label);
        Assert.Equal(JsonValueKind.Object, found.ValueKind);
        Assert.Equal(value, found.GetProperty("value").GetString());
    }
    [Fact]
    public async Task Should_lock_appconfig_kv_with_key_and_label()
    {
        // arrange
        const string key = "foo2";
        const string value = "foo-value";
        const string label = "staging";
        const string newValue = "new-value";
        try
        {
            // if it exists, unlock it
            await _appConfigService.UnlockKeyValue(_accountName, key, _subscriptionId, label: label);
        }
        catch
        {
        }
        // make sure it exists
        await _appConfigService.SetKeyValue(_accountName, key, value, _subscriptionId, label: label);
        // act
        var result = await CallToolAsync(
            "azmcp_appconfig_kv_lock",
            new()
            {
                { "subscription", _subscriptionId },
                { "account", _accountName },
                { "key", key },
                { "label", label }
            });
        // assert
        await Assert.ThrowsAnyAsync<Exception>(() => _appConfigService.SetKeyValue(_accountName, key, newValue, _subscriptionId, label: label));
    }
    [Fact]
    public async Task Should_lock_appconfig_kv_with_key()
    {
        // arrange
        const string key = "foo3";
        const string value = "foo-value";
        const string newValue = "new-value";
        try
        {
            // if it exists, unlock it
            await _appConfigService.UnlockKeyValue(_accountName, key, _subscriptionId);
        }
        catch
        {
        }
        // make sure it exists
        await _appConfigService.SetKeyValue(_accountName, key, value, _subscriptionId);
        // act
        var result = await CallToolAsync(
            "azmcp_appconfig_kv_lock",
            new()
            {
                { "subscription", _subscriptionId },
                { "account", _accountName },
                { "key", key }
            });
        // assert
        await Assert.ThrowsAnyAsync<Exception>(() => _appConfigService.SetKeyValue(_accountName, key, newValue, _subscriptionId));
    }
    [Fact]
    public async Task Should_unlock_appconfig_kv_with_key_and_label()
    {
        // arrange
        const string key = "foo4";
        const string value = "foo-value";
        const string label = "staging";
        const string newValue = "new-value";
        try
        {
            // if it exists, unlock it
            await _appConfigService.UnlockKeyValue(_accountName, key, _subscriptionId, label: label);
        }
        catch
        {
        }
        // make sure it exists
        await _appConfigService.SetKeyValue(_accountName, key, value, _subscriptionId, label: label);
        await _appConfigService.LockKeyValue(_accountName, key, _subscriptionId, label: label);
        // act
        _ = await CallToolAsync(
            "azmcp_appconfig_kv_unlock",
            new()
            {
                { "subscription", _subscriptionId },
                { "account", _accountName },
                { "key", key },
                { "label", "staging" }
            });
        // assert
        try
        {
            await _appConfigService.SetKeyValue(_accountName, key, newValue, _subscriptionId, label: label);
        }
        catch (Exception ex)
        {
            Assert.Fail($"Failed to set value after unlock: {ex.Message}");
        }
    }
    [Fact]
    public async Task Should_unlock_appconfig_kv_with_key()
    {
        // arrange
        const string key = "foo5";
        const string value = "foo-value";
        const string newValue = "new-value";
        try
        {
            // if it exists, unlock it
            await _appConfigService.UnlockKeyValue(_accountName, key, _subscriptionId);
        }
        catch
        {
        }
        // make sure it exists
        await _appConfigService.SetKeyValue(_accountName, key, value, _subscriptionId);
        await _appConfigService.LockKeyValue(_accountName, key, _subscriptionId);
        // act
        _ = await CallToolAsync(
            "azmcp_appconfig_kv_unlock",
            new()
            {
                { "subscription", _subscriptionId },
                { "account", _accountName },
                { "key", key }
            });
        // assert
        try
        {
            await _appConfigService.SetKeyValue(_accountName, key, newValue, _subscriptionId);
        }
        catch (Exception ex)
        {
            Assert.Fail($"Failed to set value after unlock: {ex.Message}");
        }
    }
    [Fact]
    public async Task Should_show_appconfig_kv()
    {
        // arrange
        const string key = "foo6";
        const string value = "foo-value";
        const string label = "staging";
        await _appConfigService.SetKeyValue(_accountName, key, value, _subscriptionId, label: label);
        // act
        var result = await CallToolAsync(
            "azmcp_appconfig_kv_show",
            new()
            {
                { "subscription", _subscriptionId },
                { "account", _accountName },
                { "key", key },
                { "label", label }
            });
        // assert
        var setting = result.AssertProperty("setting");
        Assert.Equal(JsonValueKind.Object, setting.ValueKind);
        var valueRead = setting.AssertProperty("value");
        Assert.Equal(JsonValueKind.String, valueRead.ValueKind);
        Assert.Equal(value, valueRead.GetString());
    }
    [Fact]
    public async Task Should_set_and_delete_appconfig_kv()
    {
        // arrange
        const string key = "foo7";
        const string value = "funkyfoo";
        // act and assert
        var result = await CallToolAsync(
            "azmcp_appconfig_kv_set",
            new()
            {
                { "subscription", _subscriptionId },
                { "account", _accountName },
                { "key", key },
                { "value", value }
            });
        var valueRead = result.AssertProperty("value");
        Assert.Equal(value, valueRead.GetString());
        result = await CallToolAsync(
            "azmcp_appconfig_kv_delete",
            new()
            {
                { "subscription", _subscriptionId },
                { "account", _accountName },
                { "key", key }
            });
        var keyProperty = result.AssertProperty("key");
        Assert.Equal(key, keyProperty.GetString());
    }
    [Fact]
    [Trait("Category", "Live")]
    public async Task Should_set_and_get_appconfig_kv_with_content_type()
    {
        // arrange
        const string key = "config-with-content-type";
        const string value = @"{""property"":""value""}";
        const string contentType = "application/json";
        // act - set key-value with content type
        var setResult = await CallToolAsync(
            "azmcp_appconfig_kv_set",
            new()
            {
                { "subscription", _subscriptionId },
                { "account", _accountName },
                { "key", key },
                { "value", value },
                { "content-type", contentType }
            });
        // assert - verify the set result
        var valueRead = setResult.AssertProperty("value");
        Assert.Equal(value, valueRead.GetString());
        var contentTypeRead = setResult.AssertProperty("contentType");
        Assert.Equal(JsonValueKind.String, contentTypeRead.ValueKind);
        Assert.Equal(contentType, contentTypeRead.GetString());
        // act - get the key-value to verify content type was stored
        var getResult = await CallToolAsync(
            "azmcp_appconfig_kv_show",
            new()
            {
                { "subscription", _subscriptionId },
                { "account", _accountName },
                { "key", key }
            });
        // assert - verify the get result
        var setting = getResult.AssertProperty("setting");
        Assert.Equal(JsonValueKind.Object, setting.ValueKind);
        valueRead = setting.AssertProperty("value");
        Assert.Equal(JsonValueKind.String, valueRead.ValueKind);
        Assert.Equal(value, valueRead.GetString());
        contentTypeRead = setting.AssertProperty("contentType");
        Assert.Equal(JsonValueKind.String, contentTypeRead.ValueKind);
        Assert.Equal(contentType, contentTypeRead.GetString());
    }
    [Fact]
    [Trait("Category", "Live")]
    public async Task Should_set_and_get_content_type_directly_using_service()
    {
        // arrange
        const string key = "service-content-type-test";
        const string value = @"{""name"":""testValue"",""enabled"":true}";
        const string contentType = "application/json; charset=utf-8";
        // act - set key-value with content type
        await _appConfigService.SetKeyValue(
           _accountName,
           key,
           value,
           _subscriptionId,
           contentType: contentType);
        // act - get key-value to verify content type was preserved
        var setting = await _appConfigService.GetKeyValue(
            _accountName,
            key,
            _subscriptionId);
        // assert - verify content type was properly set and retrieved
        Assert.Equal(key, setting.Key);
        Assert.Equal(value, setting.Value);
        Assert.Equal(contentType, setting.ContentType);
    }
    [Fact]
    [Trait("Category", "Live")]
    public async Task Should_set_kv_with_single_tag()
    {
        // arrange
        const string key = "tag-test-single";
        const string value = "tag-test-value";
        const string tagKey = "environment";
        const string tagValue = "production";
        // act - set key-value with a single tag
        var setResult = await CallToolAsync(
            "azmcp_appconfig_kv_set",
            new()
            {
                    { "subscription", _subscriptionId },
                    { "account", _accountName },
                    { "key", key },
                    { "value", value },
                    { "tags", $"{tagKey}={tagValue}" }
            });
        // assert - verify the set result
        var valueRead = setResult.AssertProperty("value");
        Assert.Equal(value, valueRead.GetString());
        var tagsRead = setResult.AssertProperty("tags");
        Assert.Equal(JsonValueKind.Array, tagsRead.ValueKind);
        Assert.Single(tagsRead.EnumerateArray());
        Assert.Equal($"{tagKey}={tagValue}", tagsRead.EnumerateArray().First().GetString());
    }
    [Fact]
    [Trait("Category", "Live")]
    public async Task Should_set_kv_with_multiple_tags()
    {
        // arrange
        const string key = "tag-test-multiple";
        const string value = "tag-test-value-multiple";
        var tags = new string[] { "environment=staging", "version=1.0.0", "region=westus2" };
        // act - set key-value with multiple tags
        var setResult = await CallToolAsync(
            "azmcp_appconfig_kv_set",
            new()
            {
                    { "subscription", _subscriptionId },
                    { "account", _accountName },
                    { "key", key },
                    { "value", value },
                    { "tags", tags }
            });
        // assert - verify the set result
        var valueRead = setResult.AssertProperty("value");
        Assert.Equal(value, valueRead.GetString());
        var tagsRead = setResult.AssertProperty("tags");
        Assert.Equal(JsonValueKind.Array, tagsRead.ValueKind);
        var tagArray = tagsRead.EnumerateArray().ToArray();
        Assert.Equal(tags.Length, tagArray.Length);
        foreach (var tag in tags)
        {
            Assert.Contains(tagArray, t => t.GetString() == tag);
        }
    }
    [Fact]
    [Trait("Category", "Live")]
    public async Task Should_set_kv_with_tags_containing_spaces()
    {
        // arrange
        const string key = "tag-test-spaces";
        const string value = "tag-test-value-spaces";
        var tags = new string[]
        {
            "complex key=complex value with spaces",
            "deployment environment=Production US West",
            "created by=Azure MCP Test"
        };
        // act - set key-value with tags containing spaces
        var setResult = await CallToolAsync(
            "azmcp_appconfig_kv_set",
            new()
            {
                    { "subscription", _subscriptionId },
                    { "account", _accountName },
                    { "key", key },
                    { "value", value },
                    { "tags", tags }
            });
        // assert - verify the set result
        var valueRead = setResult.AssertProperty("value");
        Assert.Equal(value, valueRead.GetString());
        var tagsRead = setResult.AssertProperty("tags");
        Assert.Equal(JsonValueKind.Array, tagsRead.ValueKind);
        var tagArray = tagsRead.EnumerateArray().ToArray();
        Assert.Equal(3, tagArray.Length);
        foreach (var tag in tags)
        {
            Assert.Contains(tagArray, t => t.GetString() == tag);
        }
    }
}