File: AddJavaContainerTests.cs
Web Access
Project: src\tests\Aspire.Hosting.Java.Tests\Aspire.Hosting.Java.Tests.csproj (Aspire.Hosting.Java.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.Tests.Utils;
using Aspire.Hosting.Utils;
using Microsoft.Extensions.DependencyInjection;
 
namespace Aspire.Hosting.Java.Tests;
 
public class AddJavaContainerTests
{
    [Fact]
    public void AddJavaContainerShouldThrowWhenBuilderIsNull()
    {
        IDistributedApplicationBuilder builder = null!;
 
        var action = () => builder.AddJavaContainer("catalog", "mycompany/catalog");
 
        var exception = Assert.Throws<ArgumentNullException>(action);
        Assert.Equal(nameof(builder), exception.ParamName);
    }
 
    [Theory]
    [InlineData(null)]
    [InlineData("")]
    [InlineData("  ")]
    public void AddJavaContainerShouldThrowWhenNameIsNullOrWhitespace(string? name)
    {
        using var builder = TestDistributedApplicationBuilder.Create().WithResourceCleanUp(true);
 
        var action = () => builder.AddJavaContainer(name!, "mycompany/catalog");
 
        var exception = name is null
            ? Assert.Throws<ArgumentNullException>(action)
            : Assert.Throws<ArgumentException>(action);
        Assert.Equal("name", exception.ParamName);
    }
 
    [Theory]
    [InlineData(null)]
    [InlineData("")]
    [InlineData("  ")]
    public void AddJavaContainerShouldThrowWhenImageIsNullOrWhitespace(string? image)
    {
        using var builder = TestDistributedApplicationBuilder.Create().WithResourceCleanUp(true);
 
        var action = () => builder.AddJavaContainer("catalog", image!);
 
        var exception = image is null
            ? Assert.Throws<ArgumentNullException>(action)
            : Assert.Throws<ArgumentException>(action);
        Assert.Equal("image", exception.ParamName);
    }
 
    [Fact]
    public void AddJavaContainer_UsesTheRequestedImageAndTag()
    {
        using var builder = TestDistributedApplicationBuilder.Create().WithResourceCleanUp(true);
 
        var app = builder.AddJavaContainer("catalog", "mycompany/catalog", "1.4.0");
 
        var image = Assert.Single(app.Resource.Annotations.OfType<ContainerImageAnnotation>());
        Assert.Equal("mycompany/catalog", image.Image);
        Assert.Equal("1.4.0", image.Tag);
    }
 
    [Fact]
    public void AddJavaContainer_WithoutTag_LeavesTheTagToTheContainerRuntime()
    {
        using var builder = TestDistributedApplicationBuilder.Create().WithResourceCleanUp(true);
 
        var app = builder.AddJavaContainer("catalog", "mycompany/catalog");
 
        var image = Assert.Single(app.Resource.Annotations.OfType<ContainerImageAnnotation>());
        Assert.Equal("mycompany/catalog", image.Image);
        Assert.Equal("latest", image.Tag);
    }
 
    [Fact]
    public void AddJavaContainer_IsDiscoverableAndUsesTheJavaIcon()
    {
        using var builder = TestDistributedApplicationBuilder.Create().WithResourceCleanUp(true);
 
        var app = builder.AddJavaContainer("catalog", "mycompany/catalog");
 
        Assert.IsAssignableFrom<IResourceWithServiceDiscovery>(app.Resource);
        Assert.IsAssignableFrom<IJavaAppResource>(app.Resource);
        var icon = Assert.Single(app.Resource.Annotations.OfType<ResourceIconAnnotation>());
        Assert.Equal("DrinkCoffee", icon.IconName);
    }
 
    [Fact]
    public void AddJavaContainer_DeclaresNoEndpoint()
    {
        using var builder = TestDistributedApplicationBuilder.Create().WithResourceCleanUp(true);
 
        // The port belongs to the image, so callers pick it with WithHttpEndpoint. Asserting the
        // absence here is what keeps a default 8080 endpoint from being reintroduced silently.
        var app = builder.AddJavaContainer("catalog", "mycompany/catalog");
 
        Assert.Empty(app.Resource.Annotations.OfType<EndpointAnnotation>());
    }
 
    [Fact]
    public async Task AddJavaContainer_ExportsTelemetryToAspire()
    {
        using var builder = TestDistributedApplicationBuilder.Create();
        builder.Configuration["ASPIRE_DASHBOARD_OTLP_ENDPOINT_URL"] = "http://localhost:4317";
 
        var app = builder.AddJavaContainer("catalog", "mycompany/catalog");
 
        // Built rather than evaluated against TestServiceProvider because resolving a container's OTLP
        // endpoint needs the DCP options that decide how the container reaches the host, which is also
        // why the asserted host is not the configured localhost.
        using var built = builder.Build();
        var envVars = await EnvironmentVariableEvaluator.GetEnvironmentVariablesAsync(
            app.Resource, serviceProvider: built.Services.GetRequiredService<IServiceProvider>());
 
        Assert.Equal("http://host.docker.internal:4317", envVars["OTEL_EXPORTER_OTLP_ENDPOINT"]);
    }
 
    [Fact]
    public async Task WithJvmArgs_AppliesToAContainerToo()
    {
        using var builder = TestDistributedApplicationBuilder.Create();
        builder.Configuration["ASPIRE_DASHBOARD_OTLP_ENDPOINT_URL"] = "http://localhost:4317";
 
        var app = builder.AddJavaContainer("catalog", "mycompany/catalog")
            .WithJvmArgs(["-Xmx512m"])
            .WithJvmArgs(["-javaagent:/app/opentelemetry-javaagent.jar"]);
 
        using var built = builder.Build();
        var envVars = await EnvironmentVariableEvaluator.GetEnvironmentVariablesAsync(
            app.Resource, serviceProvider: built.Services.GetRequiredService<IServiceProvider>());
 
        Assert.Equal("-Xmx512m -javaagent:/app/opentelemetry-javaagent.jar", envVars["JAVA_TOOL_OPTIONS"]);
    }
 
    [Fact]
    public void AddJavaContainer_KeepsTheJvmBuiltInCertificateAuthorities()
    {
        using var builder = TestDistributedApplicationBuilder.Create().WithResourceCleanUp(true);
 
        var app = builder.AddJavaContainer("catalog", "mycompany/catalog");
 
        // The JVM's trust store setting replaces the default authorities rather than adding to them,
        // so the bundle Aspire generates has to carry the system roots as well.
        var scope = Assert.Single(app.Resource.Annotations.OfType<CertificateAuthorityCollectionAnnotation>());
        Assert.Equal(CertificateTrustScope.System, scope.Scope);
    }
}