File: KubernetesPublisherTests.cs
Web Access
Project: src\tests\Aspire.Hosting.Kubernetes.Tests\Aspire.Hosting.Kubernetes.Tests.csproj (Aspire.Hosting.Kubernetes.Tests)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using System.Runtime.CompilerServices;
using Aspire.Hosting.ApplicationModel;
using Aspire.Hosting.Utils;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Extensions.Options;
using Xunit;
 
namespace Aspire.Hosting.Kubernetes.Tests;
 
[Collection(KubernetesPublisherFixture.CollectionName)]
public class KubernetesPublisherTests(KubernetesPublisherFixture fixture)
{
    private static bool s_publisherHasRun;
 
    private static readonly Dictionary<string, string> s_expectedFilesCache = new()
    {
        ["Chart.yaml"] = ExpectedValues.Chart,
        ["values.yaml"] = ExpectedValues.Values,
        ["templates/project1/deployment.yaml"] = ExpectedValues.ProjectOneDeployment,
        ["templates/project1/configmap.yaml"] = ExpectedValues.ProjectOneConfigMap,
        ["templates/myapp/deployment.yaml"] = ExpectedValues.MyAppDeployment,
        ["templates/myapp/service.yaml"] = ExpectedValues.MyAppService,
        ["templates/myapp/configmap.yaml"] = ExpectedValues.MyAppConfigMap,
        ["templates/myapp/secret.yaml"] = ExpectedValues.MyAppSecret,
    };
 
    public static TheoryData<string> GetExpectedFiles() => new(s_expectedFilesCache.Keys);
 
    [Theory, MemberData(nameof(GetExpectedFiles))]
    public async Task PublishAsync_GeneratesValidHelmChart(string expectedFile)
    {
        if (!s_publisherHasRun)
        {
            // Arrange
            ArgumentNullException.ThrowIfNull(fixture.TempDirectoryInstance);
            var options = new OptionsMonitor(
                new()
                {
                    OutputPath = fixture.TempDirectoryInstance.Path,
                });
 
            var builder = TestDistributedApplicationBuilder.Create(DistributedApplicationOperation.Publish);
 
            var param0 = builder.AddParameter("param0");
            var param1 = builder.AddParameter("param1", secret: true);
            var param2 = builder.AddParameter("param2", "default", publishValueAsDefault: true);
            var param3 = builder.AddResource(ParameterResourceBuilderExtensions.CreateDefaultPasswordParameter(builder, "param3"));
            var cs = builder.AddConnectionString("cs", ReferenceExpression.Create($"Url={param0}, Secret={param1}"));
 
            // Add a container to the application
            var api = builder.AddContainer("myapp", "mcr.microsoft.com/dotnet/aspnet:8.0")
                .WithEnvironment("ASPNETCORE_ENVIRONMENT", "Development")
                .WithHttpEndpoint(targetPort: 8080)
                .WithEnvironment("param0", param0)
                .WithEnvironment("param1", param1)
                .WithEnvironment("param2", param2)
                .WithEnvironment("param3", param3)
                .WithReference(cs)
                .WithVolume("logs", "/logs")
                .WithArgs("--cs", cs.Resource);
 
            builder.AddProject<TestProject>("project1", launchProfileName: null)
                .WithReference(api.GetEndpoint("http"));
 
            var app = builder.Build();
 
            await ExecuteBeforeStartHooksAsync(app, default);
 
            var model = app.Services.GetRequiredService<DistributedApplicationModel>();
 
            await ExecuteBeforeStartHooksAsync(app, default);
 
            var publisher = new KubernetesPublisher(
                "test", options,
                NullLogger<KubernetesPublisher>.Instance,
                builder.ExecutionContext);
 
            // Act
            await publisher.PublishAsync(model, CancellationToken.None);
            s_publisherHasRun = true;
        }
 
        ArgumentNullException.ThrowIfNull(fixture.TempDirectoryInstance);
 
        // Assert
        var file = Path.Combine(fixture.TempDirectoryInstance.Path, expectedFile);
        Assert.True(File.Exists(file), $"File not found: {file}");
        var outputContent = await File.ReadAllTextAsync(file);
        Assert.Equal(s_expectedFilesCache[expectedFile], outputContent, ignoreAllWhiteSpace: true, ignoreLineEndingDifferences: true);
    }
 
    [UnsafeAccessor(UnsafeAccessorKind.Method, Name = "ExecuteBeforeStartHooksAsync")]
    private static extern Task ExecuteBeforeStartHooksAsync(DistributedApplication app, CancellationToken cancellationToken);
 
    private sealed class OptionsMonitor(KubernetesPublisherOptions options) : IOptionsMonitor<KubernetesPublisherOptions>
    {
        public KubernetesPublisherOptions Get(string? name) => options;
 
        public IDisposable OnChange(Action<KubernetesPublisherOptions, string> listener) => null!;
 
        public KubernetesPublisherOptions CurrentValue => options;
    }
 
    private sealed class TestProject : IProjectMetadata
    {
        public string ProjectPath => "another-path";
 
        public LaunchSettings? LaunchSettings { get; set; }
    }
}