File: AddAzureKustoTests.cs
Web Access
Project: src\tests\Aspire.Hosting.Azure.Kusto.Tests\Aspire.Hosting.Azure.Kusto.Tests.csproj (Aspire.Hosting.Azure.Kusto.Tests)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using Aspire.Hosting.ApplicationModel;
using Aspire.Hosting.Utils;
 
namespace Aspire.Hosting.Azure.Kusto.Tests;
 
public class AddAzureKustoTests
{
    [Fact]
    public void AddAzureKustoCluster_ShouldCreateAzureKustoClusterResourceWithCorrectName()
    {
        // Arrange
        const string name = "kusto";
        using var builder = TestDistributedApplicationBuilder.Create();
 
        // Act
        var resourceBuilder = builder.AddAzureKustoCluster(name);
 
        // Assert
        Assert.NotNull(resourceBuilder);
        Assert.NotNull(resourceBuilder.Resource);
        Assert.Equal(name, resourceBuilder.Resource.Name);
        Assert.IsType<AzureKustoClusterResource>(resourceBuilder.Resource);
    }
 
    [Theory]
    [InlineData(null, "latest")]
    [InlineData("custom-tag", "custom-tag")]
    public void RunAsEmulator_ShouldConfigureContainerImageWithCorrectTag(string? customTag, string expectedTag)
    {
        // Arrange
        using var builder = TestDistributedApplicationBuilder.Create();
 
        // Act
        var resourceBuilder = builder.AddAzureKustoCluster("test-kusto").RunAsEmulator(containerBuilder =>
        {
            if (!string.IsNullOrEmpty(customTag))
            {
                containerBuilder.WithImageTag(customTag);
            }
        });
 
        // Assert
        var containerAnnotation = resourceBuilder.Resource.Annotations.OfType<ContainerImageAnnotation>().SingleOrDefault();
        Assert.NotNull(containerAnnotation);
        Assert.Equal(expectedTag, containerAnnotation.Tag);
    }
 
    [Theory]
    [InlineData(null)]
    [InlineData(9090)]
    public void RunAsEmulator_ShouldConfigureHttpEndpoint(int? port)
    {
        // Arrange
        using var builder = TestDistributedApplicationBuilder.Create();
 
        // Act
        var resourceBuilder = builder.AddAzureKustoCluster("kusto").RunAsEmulator(containerBuilder =>
        {
            containerBuilder.WithEndpoint("http", endpoint => endpoint.Port = port);
        });
 
        // Assert
        var endpointAnnotations = resourceBuilder.Resource.Annotations.OfType<EndpointAnnotation>().ToList();
        var httpEndpoint = endpointAnnotations.SingleOrDefault(e => e.Name == "http");
 
        Assert.NotNull(httpEndpoint);
        Assert.Equal(port, httpEndpoint.Port);
        Assert.Equal(AzureKustoEmulatorContainerDefaults.DefaultTargetPort, httpEndpoint.TargetPort);
        Assert.Equal("http", httpEndpoint.UriScheme);
    }
 
    [Fact]
    public void AddAzureKustoCluster_ShouldExcludeFromManifest()
    {
        // Arrange
        using var builder = TestDistributedApplicationBuilder.Create();
 
        // Act
        var resourceBuilder = builder.AddAzureKustoCluster("kusto");
 
        // Assert
        var manifestExclusionAnnotation = resourceBuilder.Resource.Annotations.OfType<ManifestPublishingCallbackAnnotation>().SingleOrDefault();
        Assert.Same(ManifestPublishingCallbackAnnotation.Ignore, manifestExclusionAnnotation);
    }
 
    [Fact]
    public void RunAsEmulator_ShouldAddEmulatorResourceAnnotation()
    {
        // Arrange
        using var builder = TestDistributedApplicationBuilder.Create();
        const string resourceName = "test-kusto";
 
        // Act
        var resourceBuilder = builder.AddAzureKustoCluster(resourceName).RunAsEmulator();
 
        // Assert
        var emulatorAnnotation = resourceBuilder.Resource.Annotations.OfType<EmulatorResourceAnnotation>().SingleOrDefault();
        Assert.NotNull(emulatorAnnotation);
    }
 
    [Fact]
    public void RunAsEmulator_RespectsConfigurationCallback()
    {
        // Arrange
        using var builder = TestDistributedApplicationBuilder.Create();
 
        // Act
        var resourceBuilder = builder.AddAzureKustoCluster("kusto").RunAsEmulator(builder =>
        {
            builder.WithAnnotation(new ContainerNameAnnotation() { Name = "custom-kusto-emulator" });
        });
 
        // Assert
        var annotation = resourceBuilder.Resource.Annotations.OfType<ContainerNameAnnotation>().SingleOrDefault();
        Assert.NotNull(annotation);
        Assert.Equal("custom-kusto-emulator", annotation.Name);
    }
 
    [Theory]
    [InlineData("")]
    [InlineData(" ")]
 
    public void AddAzureKustoCluster_WithInvalidName_ShouldThrowArgumentException(string? invalidName)
    {
        // Arrange
        using var builder = TestDistributedApplicationBuilder.Create();
 
        // Act & Assert
        Assert.Throws<ArgumentException>(() => builder.AddAzureKustoCluster(invalidName!));
    }
 
    [Theory]
    [InlineData(null)]
    public void AddAzureKustoCluster_WithNullName_ShouldThrowArgumentNullException(string? invalidName)
    {
        // Arrange
        using var builder = TestDistributedApplicationBuilder.Create();
 
        // Act & Assert
        Assert.Throws<ArgumentNullException>(() => builder.AddAzureKustoCluster(invalidName!));
    }
 
    [Fact]
    public void AddAzureKustoCluster_WithNullBuilder_ShouldThrowArgumentNullException()
    {
        // Act & Assert
        Assert.Throws<ArgumentNullException>(() => ((IDistributedApplicationBuilder)null!).AddAzureKustoCluster("test"));
    }
 
    [Fact]
    public void RunAsEmulator_WithNullBuilder_ShouldThrowArgumentNullException()
    {
        // Act & Assert
        Assert.Throws<ArgumentNullException>(() => ((IResourceBuilder<AzureKustoClusterResource>)null!).RunAsEmulator());
    }
 
    [Fact]
    public void AzureKustoClusterResource_ShouldImplementIResourceWithConnectionString()
    {
        // Arrange
        using var builder = TestDistributedApplicationBuilder.Create();
 
        // Act
        var resourceBuilder = builder.AddAzureKustoCluster("kusto");
 
        // Assert
        Assert.IsType<IResourceWithConnectionString>(resourceBuilder.Resource, exactMatch: false);
    }
 
    [Theory]
    [InlineData(true)]
    [InlineData(false)]
    public void IsEmulator_ShouldReturnWhenRunAsEmulator(bool runAsEmulator)
    {
        // Arrange
        using var builder = TestDistributedApplicationBuilder.Create();
        var resourceBuilder = builder.AddAzureKustoCluster("test-kusto");
 
        // Act
        if (runAsEmulator)
        {
            resourceBuilder = resourceBuilder.RunAsEmulator();
        }
 
        // Assert
        Assert.Equal(runAsEmulator, resourceBuilder.Resource.IsEmulator);
    }
 
    [Theory]
    [InlineData(true)]
    [InlineData(false)]
    public void RunAsEmulator_WithVolume_ShouldConfigureVolumeAnnotation(bool isReadOnly)
    {
        // Arrange
        using var builder = TestDistributedApplicationBuilder.Create();
 
        // Act
        var resourceBuilder = builder.AddAzureKustoCluster("test-kusto").RunAsEmulator(containerBuilder =>
        {
            containerBuilder.WithVolume($"{builder.GetVolumePrefix()}-test-kusto-data", "/data", isReadOnly);
        });
 
        // Assert
        var volumeAnnotation = resourceBuilder.Resource.Annotations.OfType<ContainerMountAnnotation>().SingleOrDefault();
        Assert.NotNull(volumeAnnotation);
        Assert.Equal($"{builder.GetVolumePrefix()}-test-kusto-data", volumeAnnotation.Source);
        Assert.Equal("/data", volumeAnnotation.Target);
        Assert.Equal(ContainerMountType.Volume, volumeAnnotation.Type);
        Assert.Equal(isReadOnly, volumeAnnotation.IsReadOnly);
    }
 
    [Theory]
    [InlineData(true)]
    [InlineData(false)]
    public void RunAsEmulator_WithBindMount_ShouldConfigureBindMountAnnotation(bool isReadOnly)
    {
        // Arrange
        using var builder = TestDistributedApplicationBuilder.Create();
 
        // Act
        var resourceBuilder = builder.AddAzureKustoCluster("test-kusto").RunAsEmulator(containerBuilder =>
        {
            containerBuilder.WithBindMount("./custom-data", "/data", isReadOnly);
        });
 
        // Assert
        var mountAnnotation = resourceBuilder.Resource.Annotations.OfType<ContainerMountAnnotation>().SingleOrDefault();
        Assert.NotNull(mountAnnotation);
        Assert.Equal(Path.Combine(builder.AppHostDirectory, "custom-data"), mountAnnotation.Source);
        Assert.Equal("/data", mountAnnotation.Target);
        Assert.Equal(ContainerMountType.BindMount, mountAnnotation.Type);
        Assert.Equal(isReadOnly, mountAnnotation.IsReadOnly);
    }
 
    [Fact]
    public void RunAsEmulator_WithBothVolumeAndBindMount_ShouldHaveBothAnnotations()
    {
        // Arrange
        using var builder = TestDistributedApplicationBuilder.Create();
 
        // Act
        var resourceBuilder = builder.AddAzureKustoCluster("test-kusto").RunAsEmulator(containerBuilder =>
        {
            containerBuilder.WithVolume("volume-data", "/data")
                           .WithBindMount("./config", "/app/config", isReadOnly: true);
        });
 
        // Assert
        var mountAnnotations = resourceBuilder.Resource.Annotations.OfType<ContainerMountAnnotation>().ToList();
        Assert.Equal(2, mountAnnotations.Count);
 
        var volumeAnnotation = mountAnnotations.SingleOrDefault(a => a.Type == ContainerMountType.Volume);
        Assert.NotNull(volumeAnnotation);
        Assert.Equal("volume-data", volumeAnnotation.Source);
        Assert.Equal("/data", volumeAnnotation.Target);
 
        var bindMountAnnotation = mountAnnotations.SingleOrDefault(a => a.Type == ContainerMountType.BindMount);
        Assert.NotNull(bindMountAnnotation);
        Assert.Equal("/app/config", bindMountAnnotation.Target);
        Assert.True(bindMountAnnotation.IsReadOnly);
    }
 
    [Fact]
    public void RunAsEmulator_WithCustomImage_ShouldUseSpecifiedValues()
    {
        // Arrange
        using var builder = TestDistributedApplicationBuilder.Create();
        const string customRegistry = "custom.registry.com";
        const string customImage = "custom-kusto-image";
        const string customTag = "custom-tag";
 
        // Act
        var resourceBuilder = builder.AddAzureKustoCluster("test-kusto").RunAsEmulator(containerBuilder =>
        {
            containerBuilder.WithImageRegistry(customRegistry).WithImage(customImage).WithImageTag(customTag);
        });
 
        // Assert
        var containerImageAnnotation = resourceBuilder.Resource.Annotations.OfType<ContainerImageAnnotation>().SingleOrDefault();
        Assert.NotNull(containerImageAnnotation);
        Assert.Equal(customRegistry, containerImageAnnotation.Registry);
        Assert.Equal(customImage, containerImageAnnotation.Image);
        Assert.Equal(customTag, containerImageAnnotation.Tag);
    }
 
    [Fact]
    public void RunAsEmulator_WithCustomLifetime_ShouldConfigureLifetimeAnnotation()
    {
        // Arrange
        using var builder = TestDistributedApplicationBuilder.Create();
 
        // Act
        var resourceBuilder = builder.AddAzureKustoCluster("test-kusto").RunAsEmulator(containerBuilder =>
        {
            containerBuilder.WithLifetime(ContainerLifetime.Persistent);
        });
 
        // Assert
        var lifetimeAnnotation = resourceBuilder.Resource.Annotations.OfType<ContainerLifetimeAnnotation>().SingleOrDefault();
        Assert.NotNull(lifetimeAnnotation);
        Assert.Equal(ContainerLifetime.Persistent, lifetimeAnnotation.Lifetime);
    }
 
    [Fact]
    public void AddAzureKustoCluster_ShouldAddHealthCheckAnnotation()
    {
        // Arrange
        const string name = "kusto";
        using var builder = TestDistributedApplicationBuilder.Create();
 
        // Act
        var kustoServer = builder.AddAzureKustoCluster(name);
 
        // Assert
        Assert.Single(kustoServer.Resource.Annotations, annotation => annotation is HealthCheckAnnotation hca && hca.Key == $"{name}_check");
    }
 
    [Fact]
    public void AddDatabase_ShouldAddHealthCheckAnnotation()
    {
        // Arrange
        const string name = "db";
        using var builder = TestDistributedApplicationBuilder.Create();
        var kusto = builder.AddAzureKustoCluster("kusto");
 
        // Act
        var database = kusto.AddDatabase(name);
 
        // Assert
        Assert.Single(database.Resource.Annotations, annotation => annotation is HealthCheckAnnotation hca && hca.Key == $"{name}_check");
    }
 
    [Theory]
    [InlineData(9090)]
    [InlineData(8080)]
    [InlineData(1234)]
    public void WithHttpPort_ShouldSetHttpEndpointPort(int port)
    {
        // Arrange
        using var builder = TestDistributedApplicationBuilder.Create();
 
        // Act
        var resourceBuilder = builder.AddAzureKustoCluster("kusto")
            .RunAsEmulator(c => c.WithHttpPort(port));
 
        // Assert
        var endpointAnnotations = resourceBuilder.Resource.Annotations.OfType<EndpointAnnotation>().ToList();
        var httpEndpoint = endpointAnnotations.SingleOrDefault(e => e.Name == "http");
 
        Assert.NotNull(httpEndpoint);
        Assert.Equal(port, httpEndpoint.Port);
        Assert.Equal(AzureKustoEmulatorContainerDefaults.DefaultTargetPort, httpEndpoint.TargetPort);
        Assert.Equal("http", httpEndpoint.UriScheme);
    }
 
    [Fact]
    public void WithHttpPort_ShouldThrowArgumentNullException_WhenBuilderIsNull()
    {
        // Arrange
        IResourceBuilder<AzureKustoClusterResource> builder = null!;
 
        // Act & Assert
        var exception = Assert.Throws<ArgumentNullException>(() => builder.RunAsEmulator(c => c.WithHttpPort(8080)));
        Assert.Equal("builder", exception.ParamName);
    }
}