Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions AzureMcp.sln
Original file line number Diff line number Diff line change
Expand Up @@ -402,6 +402,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{37B0CE47
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Azure.Mcp.Tools.Postgres.UnitTests", "tools\Azure.Mcp.Tools.Postgres\tests\Azure.Mcp.Tools.Postgres.UnitTests\Azure.Mcp.Tools.Postgres.UnitTests.csproj", "{6CFDFF50-A41A-4A5E-A66A-670DB707495F}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Azure.Mcp.Tools.Postgres.LiveTests", "tools\Azure.Mcp.Tools.Postgres\tests\Azure.Mcp.Tools.Postgres.LiveTests\Azure.Mcp.Tools.Postgres.LiveTests.csproj", "{A8B9C7D6-E5F4-43A2-B1E8-9C7D6E5F4A32}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{7F099BC4-FAF9-0D5C-A860-45BB7185CE13}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Azure.Mcp.Tools.Quota.LiveTests", "tools\Azure.Mcp.Tools.Quota\tests\Azure.Mcp.Tools.Quota.LiveTests\Azure.Mcp.Tools.Quota.LiveTests.csproj", "{2C7B96D1-085F-4BCA-8D7D-3047765E3492}"
Expand Down Expand Up @@ -1518,6 +1520,18 @@ Global
{6CFDFF50-A41A-4A5E-A66A-670DB707495F}.Release|x64.Build.0 = Release|Any CPU
{6CFDFF50-A41A-4A5E-A66A-670DB707495F}.Release|x86.ActiveCfg = Release|Any CPU
{6CFDFF50-A41A-4A5E-A66A-670DB707495F}.Release|x86.Build.0 = Release|Any CPU
{A8B9C7D6-E5F4-43A2-B1E8-9C7D6E5F4A32}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{A8B9C7D6-E5F4-43A2-B1E8-9C7D6E5F4A32}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A8B9C7D6-E5F4-43A2-B1E8-9C7D6E5F4A32}.Debug|x64.ActiveCfg = Debug|Any CPU
{A8B9C7D6-E5F4-43A2-B1E8-9C7D6E5F4A32}.Debug|x64.Build.0 = Debug|Any CPU
{A8B9C7D6-E5F4-43A2-B1E8-9C7D6E5F4A32}.Debug|x86.ActiveCfg = Debug|Any CPU
{A8B9C7D6-E5F4-43A2-B1E8-9C7D6E5F4A32}.Debug|x86.Build.0 = Debug|Any CPU
{A8B9C7D6-E5F4-43A2-B1E8-9C7D6E5F4A32}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A8B9C7D6-E5F4-43A2-B1E8-9C7D6E5F4A32}.Release|Any CPU.Build.0 = Release|Any CPU
{A8B9C7D6-E5F4-43A2-B1E8-9C7D6E5F4A32}.Release|x64.ActiveCfg = Release|Any CPU
{A8B9C7D6-E5F4-43A2-B1E8-9C7D6E5F4A32}.Release|x64.Build.0 = Release|Any CPU
{A8B9C7D6-E5F4-43A2-B1E8-9C7D6E5F4A32}.Release|x86.ActiveCfg = Release|Any CPU
{A8B9C7D6-E5F4-43A2-B1E8-9C7D6E5F4A32}.Release|x86.Build.0 = Release|Any CPU
{2C7B96D1-085F-4BCA-8D7D-3047765E3492}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{2C7B96D1-085F-4BCA-8D7D-3047765E3492}.Debug|Any CPU.Build.0 = Debug|Any CPU
{2C7B96D1-085F-4BCA-8D7D-3047765E3492}.Debug|x64.ActiveCfg = Debug|Any CPU
Expand Down Expand Up @@ -1947,6 +1961,7 @@ Global
{7C1FC1A3-5E52-45A8-B136-0934ADD5580C} = {BE4DDD77-798F-8692-0C2A-2A9637256ED2}
{37B0CE47-14C8-F5BF-BDDD-13EEBE580A88} = {898C8C6E-FC1C-58E1-FB62-BBE77ED42888}
{6CFDFF50-A41A-4A5E-A66A-670DB707495F} = {37B0CE47-14C8-F5BF-BDDD-13EEBE580A88}
{A8B9C7D6-E5F4-43A2-B1E8-9C7D6E5F4A32} = {37B0CE47-14C8-F5BF-BDDD-13EEBE580A88}
{7F099BC4-FAF9-0D5C-A860-45BB7185CE13} = {4CBFCE90-00AC-3F19-DC8C-C81357708F76}
{2C7B96D1-085F-4BCA-8D7D-3047765E3492} = {7F099BC4-FAF9-0D5C-A860-45BB7185CE13}
{B3167A7D-CF85-4A5F-B1CF-F14013782F1D} = {7F099BC4-FAF9-0D5C-A860-45BB7185CE13}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<IsTestProject>true</IsTestProject>
<OutputType>Exe</OutputType>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\..\servers\Azure.Mcp.Server\src\Azure.Mcp.Server.csproj" />
<ProjectReference Include="..\..\..\..\core\Azure.Mcp.Core\tests\Azure.Mcp.Tests\Azure.Mcp.Tests.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="NSubstitute" />
<PackageReference Include="NSubstitute.Analyzers.CSharp" />
<PackageReference Include="xunit.v3" />
<PackageReference Include="xunit.runner.visualstudio" />
<PackageReference Include="coverlet.collector" />
<PackageReference Include="Azure.Identity" />
<PackageReference Include="Npgsql" />
</ItemGroup>
</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,288 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

using System.Text.Json;
using Azure.Mcp.Core.Services.Azure.ResourceGroup;
using Azure.Mcp.Core.Services.Azure.Subscription;
using Azure.Mcp.Core.Services.Azure.Tenant;
using Azure.Mcp.Core.Services.Caching;
using Azure.Mcp.Tests;
using Azure.Mcp.Tests.Client;
using Azure.Mcp.Tests.Client.Helpers;
using Azure.Mcp.Tools.Postgres.Services;
using Microsoft.Extensions.Caching.Memory;
using Xunit;

namespace Azure.Mcp.Tools.Postgres.LiveTests;

public class PostgresCommandTests : CommandTestsBase
{
private const string ServersKey = "servers";
private const string DatabasesKey = "databases";
private const string TablesKey = "tables";
private const string ConfigKey = "config";
private const string QueryResultKey = "queryResult";

private readonly PostgresService _postgresService;

public PostgresCommandTests(ITestOutputHelper output) : base(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);
var resourceGroupService = new ResourceGroupService(cacheService, subscriptionService);
_postgresService = new PostgresService(resourceGroupService);
}

[Fact]
public async Task Should_list_postgres_servers()
{
// act
var result = await CallToolAsync(
"azmcp_postgres_server_list",
new()
{
{ "subscription", Settings.SubscriptionId },
{ "resource-group", Settings.ResourceBaseName },
{ "user", "testuser" }
});

// assert
var serversArray = result.AssertProperty(ServersKey);
Assert.Equal(JsonValueKind.Array, serversArray.ValueKind);
Assert.NotEmpty(serversArray.EnumerateArray());

// Check that our test server is in the list
var serverName = $"{Settings.ResourceBaseName}-postgres";
Assert.Contains(serversArray.EnumerateArray(), server =>
server.GetString()?.Contains(serverName) == true);
}

[Fact]
public async Task Should_list_postgres_databases()
{
var serverName = $"{Settings.ResourceBaseName}-postgres";

// act
var result = await CallToolAsync(
"azmcp_postgres_database_list",
new()
{
{ "subscription", Settings.SubscriptionId },
{ "resource-group", Settings.ResourceBaseName },
{ "server", serverName }
});

// assert
var databasesArray = result.AssertProperty(DatabasesKey);
Assert.Equal(JsonValueKind.Array, databasesArray.ValueKind);
Assert.NotEmpty(databasesArray.EnumerateArray());

// Check that our test database is in the list
Assert.Contains(databasesArray.EnumerateArray(), db =>
db.GetString() == "testdb");
}

[Fact]
public async Task Should_get_postgres_server_config()
{
var serverName = $"{Settings.ResourceBaseName}-postgres";

// act
var result = await CallToolAsync(
"azmcp_postgres_server_config_get",
new()
{
{ "subscription", Settings.SubscriptionId },
{ "resource-group", Settings.ResourceBaseName },
{ "server", serverName }
});

// assert
var configProperty = result.AssertProperty(ConfigKey);
Assert.Equal(JsonValueKind.Object, configProperty.ValueKind);

// Verify some expected configuration properties exist
Assert.True(configProperty.TryGetProperty("sku", out _));
Assert.True(configProperty.TryGetProperty("properties", out _));
}

[Fact]
public async Task Should_execute_simple_database_query()
{
var serverName = $"{Settings.ResourceBaseName}-postgres";
var databaseName = "testdb";

// act - Execute a simple query to test connectivity
var result = await CallToolAsync(
"azmcp_postgres_database_query",
new()
{
{ "subscription", Settings.SubscriptionId },
{ "resource-group", Settings.ResourceBaseName },
{ "server", serverName },
{ "database", databaseName },
{ "query", "SELECT version();" }
});

// assert
var queryResult = result.AssertProperty(QueryResultKey);
Assert.Equal(JsonValueKind.Array, queryResult.ValueKind);
Assert.NotEmpty(queryResult.EnumerateArray());

// Verify the result contains version information
var firstRow = queryResult.EnumerateArray().First();
Assert.True(firstRow.TryGetProperty("version", out var versionProperty));
Assert.Contains("PostgreSQL", versionProperty.GetString()!);
}

[Fact]
public async Task Should_list_tables_in_database()
{
var serverName = $"{Settings.ResourceBaseName}-postgres";
var databaseName = "testdb";

// First create a test table
await CallToolAsync(
"azmcp_postgres_database_query",
new()
{
{ "subscription", Settings.SubscriptionId },
{ "resource-group", Settings.ResourceBaseName },
{ "server", serverName },
{ "database", databaseName },
{ "query", "CREATE TABLE IF NOT EXISTS test_table (id SERIAL PRIMARY KEY, name VARCHAR(100));" }
});

// act - List tables
var result = await CallToolAsync(
"azmcp_postgres_table_list",
new()
{
{ "subscription", Settings.SubscriptionId },
{ "resource-group", Settings.ResourceBaseName },
{ "server", serverName },
{ "database", databaseName }
});

// assert
var tablesArray = result.AssertProperty(TablesKey);
Assert.Equal(JsonValueKind.Array, tablesArray.ValueKind);

// Check that our test table is in the list
Assert.Contains(tablesArray.EnumerateArray(), table =>
table.GetString() == "test_table");
}

[Fact]
public async Task Should_get_table_schema()
{
var serverName = $"{Settings.ResourceBaseName}-postgres";
var databaseName = "testdb";
var tableName = "test_table";

// Ensure test table exists
await CallToolAsync(
"azmcp_postgres_database_query",
new()
{
{ "subscription", Settings.SubscriptionId },
{ "resource-group", Settings.ResourceBaseName },
{ "server", serverName },
{ "database", databaseName },
{ "query", "CREATE TABLE IF NOT EXISTS test_table (id SERIAL PRIMARY KEY, name VARCHAR(100));" }
});

// act
var result = await CallToolAsync(
"azmcp_postgres_table_schema_get",
new()
{
{ "subscription", Settings.SubscriptionId },
{ "resource-group", Settings.ResourceBaseName },
{ "server", serverName },
{ "database", databaseName },
{ "table", tableName }
});

// assert
var schemaProperty = result.AssertProperty("schema");
Assert.Equal(JsonValueKind.Array, schemaProperty.ValueKind);
Assert.NotEmpty(schemaProperty.EnumerateArray());

// Verify expected columns are present
var columns = schemaProperty.EnumerateArray().ToList();
Assert.Contains(columns, col =>
col.TryGetProperty("column_name", out var nameProperty) &&
nameProperty.GetString() == "id");
Assert.Contains(columns, col =>
col.TryGetProperty("column_name", out var nameProperty) &&
nameProperty.GetString() == "name");
}

[Fact]
public async Task Should_insert_and_query_data()
{
var serverName = $"{Settings.ResourceBaseName}-postgres";
var databaseName = "testdb";

// Create test table
await CallToolAsync(
"azmcp_postgres_database_query",
new()
{
{ "subscription", Settings.SubscriptionId },
{ "resource-group", Settings.ResourceBaseName },
{ "server", serverName },
{ "database", databaseName },
{ "query", "CREATE TABLE IF NOT EXISTS live_test_data (id SERIAL PRIMARY KEY, test_name VARCHAR(100), created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP);" }
});

// Insert test data
var testId = Guid.NewGuid().ToString();
await CallToolAsync(
"azmcp_postgres_database_query",
new()
{
{ "subscription", Settings.SubscriptionId },
{ "resource-group", Settings.ResourceBaseName },
{ "server", serverName },
{ "database", databaseName },
{ "query", $"INSERT INTO live_test_data (test_name) VALUES ('test_{testId}');" }
});

// act - Query the inserted data
var result = await CallToolAsync(
"azmcp_postgres_database_query",
new()
{
{ "subscription", Settings.SubscriptionId },
{ "resource-group", Settings.ResourceBaseName },
{ "server", serverName },
{ "database", databaseName },
{ "query", $"SELECT * FROM live_test_data WHERE test_name = 'test_{testId}';" }
});

// assert
var queryResult = result.AssertProperty(QueryResultKey);
Assert.Equal(JsonValueKind.Array, queryResult.ValueKind);
Assert.NotEmpty(queryResult.EnumerateArray());

var firstRow = queryResult.EnumerateArray().First();
Assert.True(firstRow.TryGetProperty("test_name", out var testNameProperty));
Assert.Equal($"test_{testId}", testNameProperty.GetString());

// Clean up
await CallToolAsync(
"azmcp_postgres_database_query",
new()
{
{ "subscription", Settings.SubscriptionId },
{ "resource-group", Settings.ResourceBaseName },
{ "server", serverName },
{ "database", databaseName },
{ "query", $"DELETE FROM live_test_data WHERE test_name = 'test_{testId}';" }
});
}
}
Loading
Loading