|
#pragma warning disable ASPIRECOMPUTE001 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed.
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System.Text.Json.Nodes;
using Aspire.Hosting.ApplicationModel;
using Aspire.Hosting.Utils;
using Microsoft.Extensions.DependencyInjection;
using static Aspire.Hosting.Utils.AzureManifestUtils;
namespace Aspire.Hosting.Azure.Tests;
public class AzureAppServiceTests
{
[Fact]
public async Task AddContainerAppEnvironmentAddsDeploymentTargetWithContainerAppToProjectResources()
{
var builder = TestDistributedApplicationBuilder.Create(DistributedApplicationOperation.Publish);
var env = builder.AddAzureAppServiceEnvironment("env");
builder.AddProject<Project>("api", launchProfileName: null)
.WithHttpEndpoint()
.WithExternalHttpEndpoints()
.PublishAsAzureAppServiceWebsite((infrastructure, site) =>
{
site.SiteConfig.IsWebSocketsEnabled = true;
});
using var app = builder.Build();
await ExecuteBeforeStartHooksAsync(app, default);
var model = app.Services.GetRequiredService<DistributedApplicationModel>();
var container = Assert.IsType<IComputeResource>(Assert.Single(model.GetProjectResources()), exactMatch: false);
var target = container.GetDeploymentTargetAnnotation();
Assert.NotNull(target);
Assert.Same(env.Resource, target.ComputeEnvironment);
var resource = target?.DeploymentTarget as AzureProvisioningResource;
Assert.NotNull(resource);
var (manifest, bicep) = await GetManifestWithBicep(resource);
await Verify(manifest.ToString(), "json")
.AppendContentAsFile(bicep, "bicep");
}
[Fact]
public async Task AddContainerAppEnvironmentAddsEnvironmentResource()
{
var builder = TestDistributedApplicationBuilder.Create(DistributedApplicationOperation.Publish);
builder.AddAzureAppServiceEnvironment("env");
using var app = builder.Build();
await ExecuteBeforeStartHooksAsync(app, default);
var model = app.Services.GetRequiredService<DistributedApplicationModel>();
var environment = Assert.Single(model.Resources.OfType<AzureAppServiceEnvironmentResource>());
var (manifest, bicep) = await GetManifestWithBicep(environment);
await Verify(manifest.ToString(), "json")
.AppendContentAsFile(bicep, "bicep");
}
[Fact]
public async Task KeyvaultReferenceHandling()
{
var builder = TestDistributedApplicationBuilder.Create(DistributedApplicationOperation.Publish);
builder.AddAzureAppServiceEnvironment("env");
var db = builder.AddAzureCosmosDB("mydb").WithAccessKeyAuthentication();
db.AddCosmosDatabase("db");
builder.AddProject<Project>("api", launchProfileName: null)
.WithReference(db);
using var app = builder.Build();
await ExecuteBeforeStartHooksAsync(app, default);
var model = app.Services.GetRequiredService<DistributedApplicationModel>();
var container = Assert.Single(model.GetProjectResources());
container.TryGetLastAnnotation<DeploymentTargetAnnotation>(out var target);
var resource = target?.DeploymentTarget as AzureProvisioningResource;
Assert.NotNull(resource);
var (manifest, bicep) = await GetManifestWithBicep(resource);
await Verify(manifest.ToString(), "json")
.AppendContentAsFile(bicep, "bicep");
}
[Fact]
public async Task EndpointReferencesAreResolvedAcrossProjects()
{
var builder = TestDistributedApplicationBuilder.Create(DistributedApplicationOperation.Publish);
builder.AddAzureAppServiceEnvironment("env");
// Add 2 projects with endpoints
var project1 = builder.AddProject<Project>("project1", launchProfileName: null)
.WithHttpEndpoint()
.WithExternalHttpEndpoints();
var project2 = builder.AddProject<Project>("project2", launchProfileName: null)
.WithHttpEndpoint()
.WithExternalHttpEndpoints()
.WithReference(project1);
using var app = builder.Build();
await ExecuteBeforeStartHooksAsync(app, default);
var model = app.Services.GetRequiredService<DistributedApplicationModel>();
project2.Resource.TryGetLastAnnotation<DeploymentTargetAnnotation>(out var target);
var resource = target?.DeploymentTarget as AzureProvisioningResource;
Assert.NotNull(resource);
var (manifest, bicep) = await GetManifestWithBicep(resource);
await Verify(manifest.ToString(), "json")
.AppendContentAsFile(bicep, "bicep");
}
[Fact]
public async Task AzureAppServiceSupportBaitAndSwitchResources()
{
var builder = TestDistributedApplicationBuilder.Create(DistributedApplicationOperation.Publish);
builder.AddAzureAppServiceEnvironment("env");
builder.AddProject<Project>("api", launchProfileName: null)
.PublishAsDockerFile()
.WithHttpEndpoint(env: "PORT", targetPort: 80)
.WithExternalHttpEndpoints();
using var app = builder.Build();
var model = app.Services.GetRequiredService<DistributedApplicationModel>();
await ExecuteBeforeStartHooksAsync(app, default);
var container = Assert.Single(model.GetContainerResources());
container.TryGetLastAnnotation<DeploymentTargetAnnotation>(out var target);
var resource = target?.DeploymentTarget as AzureProvisioningResource;
Assert.NotNull(resource);
var (manifest, bicep) = await GetManifestWithBicep(resource);
await Verify(manifest.ToString(), "json")
.AppendContentAsFile(bicep, "bicep");
}
[Fact]
public async Task AddDockerfileWithAppServiceInfrastructureAddsDeploymentTargetWithAppServiceToContainerResources()
{
var builder = TestDistributedApplicationBuilder.Create(DistributedApplicationOperation.Publish);
builder.AddAzureAppServiceEnvironment("env");
var directory = Directory.CreateTempSubdirectory(".aspire-test");
// Contents of the Dockerfile are not important for this test
File.WriteAllText(Path.Combine(directory.FullName, "Dockerfile"), "");
builder.AddDockerfile("api", directory.FullName)
.WithHttpEndpoint(targetPort: 85, env: "PORT")
.WithExternalHttpEndpoints();
using var app = builder.Build();
await ExecuteBeforeStartHooksAsync(app, default);
var model = app.Services.GetRequiredService<DistributedApplicationModel>();
var container = Assert.Single(model.GetContainerResources());
container.TryGetLastAnnotation<DeploymentTargetAnnotation>(out var target);
var resource = target?.DeploymentTarget as AzureProvisioningResource;
Assert.NotNull(resource);
var (manifest, bicep) = await GetManifestWithBicep(resource);
await Verify(manifest.ToString(), "json")
.AppendContentAsFile(bicep, "bicep");
}
[Fact]
public async Task UnknownManifestExpressionProviderIsHandledWithAllocateParameter()
{
var builder = TestDistributedApplicationBuilder.Create(DistributedApplicationOperation.Publish);
builder.AddAzureAppServiceEnvironment("env");
var customProvider = new CustomManifestExpressionProvider();
var apiProject = builder.AddProject<Project>("api", launchProfileName: null)
.WithHttpEndpoint()
.WithEnvironment(context =>
{
context.EnvironmentVariables["CUSTOM_VALUE"] = customProvider;
})
.WithExternalHttpEndpoints();
using var app = builder.Build();
await ExecuteBeforeStartHooksAsync(app, default);
var model = app.Services.GetRequiredService<DistributedApplicationModel>();
var project = Assert.Single(model.GetProjectResources());
project.TryGetLastAnnotation<DeploymentTargetAnnotation>(out var target);
var resource = target?.DeploymentTarget as AzureProvisioningResource;
Assert.NotNull(resource);
var (manifest, bicep) = await GetManifestWithBicep(resource);
await Verify(manifest.ToString(), "json")
.AppendContentAsFile(bicep, "bicep");
}
[Fact]
public void AzureAppServiceEnvironmentImplementsIAzureComputeEnvironmentResource()
{
var builder = TestDistributedApplicationBuilder.Create();
var env = builder.AddAzureAppServiceEnvironment("env");
Assert.IsAssignableFrom<IAzureComputeEnvironmentResource>(env.Resource);
Assert.IsAssignableFrom<IComputeEnvironmentResource>(env.Resource);
}
[Fact]
public async Task PublishAsAzureAppServiceWebsite_ThrowsIfNoEnvironment()
{
static async Task RunTest(Action<IDistributedApplicationBuilder> action)
{
var builder = TestDistributedApplicationBuilder.Create(DistributedApplicationOperation.Publish);
// Do not add AddAzureAppServiceEnvironment
action(builder);
using var app = builder.Build();
var ex = await Assert.ThrowsAsync<InvalidOperationException>(() => ExecuteBeforeStartHooksAsync(app, default));
Assert.Contains("there are no 'AzureAppServiceEnvironmentResource' resources", ex.Message);
}
await RunTest(builder =>
builder.AddProject<Projects.ServiceA>("ServiceA")
.PublishAsAzureAppServiceWebsite((_, _) => { }));
await RunTest(builder =>
builder.AddContainer("api", "myimage")
.PublishAsAzureAppServiceWebsite((_, _) => { }));
await RunTest(builder =>
builder.AddExecutable("exe", "path/to/executable", ".")
.PublishAsDockerFile()
.PublishAsAzureAppServiceWebsite((_, _) => { }));
}
[Fact]
public async Task MultipleAzureAppServiceEnvironmentsSupported()
{
using var tempDir = new TempDirectory();
var builder = TestDistributedApplicationBuilder.Create(DistributedApplicationOperation.Publish, outputPath: tempDir.Path);
var env1 = builder.AddAzureAppServiceEnvironment("env1");
var env2 = builder.AddAzureAppServiceEnvironment("env2");
builder.AddProject<Projects.ServiceA>("ServiceA")
.WithExternalHttpEndpoints()
.WithComputeEnvironment(env1);
builder.AddProject<Projects.ServiceB>("ServiceB")
.WithExternalHttpEndpoints()
.WithComputeEnvironment(env2);
using var app = builder.Build();
// Publishing will stop the app when it is done
await app.RunAsync();
var verifySettings = new VerifySettings();
verifySettings.ScrubLines(line => line.Contains("\"path\"") && line.Contains(".csproj"));
await VerifyFile(
Path.Combine(tempDir.Path, "aspire-manifest.json"),
verifySettings);
}
private static Task<(JsonNode ManifestNode, string BicepText)> GetManifestWithBicep(IResource resource) =>
AzureManifestUtils.GetManifestWithBicep(resource, skipPreparer: true);
private sealed class Project : IProjectMetadata
{
public string ProjectPath => "/foo/bar/project.csproj";
}
private sealed class CustomManifestExpressionProvider : IManifestExpressionProvider
{
public string ValueExpression => "{customValue}";
}
}
|