| File: Publishing\BicepGenerationSimpleTests.cs | Web Access |
| Project: src\tests\Aspire.Hosting.Radius.Tests\Aspire.Hosting.Radius.Tests.csproj (Aspire.Hosting.Radius.Tests) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. #pragma warning disable ASPIREPIPELINES001 using Aspire.Hosting.ApplicationModel; using Aspire.Hosting.Pipelines; using Aspire.Hosting.Radius.Publishing; using Aspire.Hosting.Utils; using Microsoft.Extensions.DependencyInjection; namespace Aspire.Hosting.Radius.Tests.Publishing; public class BicepGenerationSimpleTests { [Fact] public void SimpleSingleContainer_GeneratesValidBicep_WithExtensionDirective() { // Arrange using var builder = TestDistributedApplicationBuilder.Create(DistributedApplicationOperation.Publish); var env = builder.AddRadiusEnvironment("myenv"); builder.AddContainer("api", "myapp/api", "latest"); using var app = builder.Build(); var model = app.Services.GetRequiredService<DistributedApplicationModel>(); var radiusEnv = model.Resources.OfType<RadiusEnvironmentResource>().First(); RadiusTestHelper.AttachDeploymentTargets(radiusEnv, model); var context = new RadiusBicepPublishingContext(radiusEnv); // Act var bicep = context.GenerateBicep(model); // Assert Assert.StartsWith("extension radius", bicep); } [Fact] public void SimpleSingleContainer_GeneratesEnvironmentBlock() { using var builder = TestDistributedApplicationBuilder.Create(DistributedApplicationOperation.Publish); builder.AddRadiusEnvironment("myenv"); builder.AddContainer("api", "myapp/api", "latest"); using var app = builder.Build(); var model = app.Services.GetRequiredService<DistributedApplicationModel>(); var radiusEnv = model.Resources.OfType<RadiusEnvironmentResource>().First(); RadiusTestHelper.AttachDeploymentTargets(radiusEnv, model); var context = new RadiusBicepPublishingContext(radiusEnv); var bicep = context.GenerateBicep(model); Assert.Contains("Radius.Core/environments@2025-08-01-preview", bicep); Assert.Contains("name: 'myenv'", bicep); Assert.Contains("recipePacks:", bicep); } [Fact] public void SimpleSingleContainer_GeneratesApplicationBlock() { using var builder = TestDistributedApplicationBuilder.Create(DistributedApplicationOperation.Publish); builder.AddRadiusEnvironment("myenv"); builder.AddContainer("api", "myapp/api", "latest"); using var app = builder.Build(); var model = app.Services.GetRequiredService<DistributedApplicationModel>(); var radiusEnv = model.Resources.OfType<RadiusEnvironmentResource>().First(); RadiusTestHelper.AttachDeploymentTargets(radiusEnv, model); var context = new RadiusBicepPublishingContext(radiusEnv); var bicep = context.GenerateBicep(model); Assert.Contains("Radius.Core/applications@2025-08-01-preview", bicep); Assert.Contains("environment: myenv.id", bicep); } [Fact] public void SimpleSingleContainer_GeneratesContainerBlock() { using var builder = TestDistributedApplicationBuilder.Create(DistributedApplicationOperation.Publish); builder.AddRadiusEnvironment("myenv"); builder.AddContainer("api", "myapp/api", "latest"); using var app = builder.Build(); var model = app.Services.GetRequiredService<DistributedApplicationModel>(); var radiusEnv = model.Resources.OfType<RadiusEnvironmentResource>().First(); RadiusTestHelper.AttachDeploymentTargets(radiusEnv, model); var context = new RadiusBicepPublishingContext(radiusEnv); var bicep = context.GenerateBicep(model); Assert.Contains("Radius.Compute/containers@2025-08-01-preview", bicep); Assert.Contains("name: 'api'", bicep); Assert.Contains("image: 'myapp/api:latest'", bicep); Assert.Contains("application: app.id", bicep); } [Fact] public void SimpleSingleContainer_GeneratesRecipePackBlock() { using var builder = TestDistributedApplicationBuilder.Create(DistributedApplicationOperation.Publish); builder.AddRadiusEnvironment("myenv"); builder.AddContainer("api", "myapp/api", "latest"); using var app = builder.Build(); var model = app.Services.GetRequiredService<DistributedApplicationModel>(); var radiusEnv = model.Resources.OfType<RadiusEnvironmentResource>().First(); RadiusTestHelper.AttachDeploymentTargets(radiusEnv, model); var context = new RadiusBicepPublishingContext(radiusEnv); var bicep = context.GenerateBicep(model); Assert.Contains("Radius.Core/recipePacks@2025-08-01-preview", bicep); } [Fact] public void SimpleSingleContainer_EmitsEnvironmentReference() { // The Radius.Compute/containers v2 schema requires properties.environment so the // control plane can resolve the recipe pack that provisions the container. Without // it, shipped Radius cannot deploy the native container (this was the gap that // previously blocked the native path on older Radius installs). using var builder = TestDistributedApplicationBuilder.Create(DistributedApplicationOperation.Publish); builder.AddRadiusEnvironment("myenv"); builder.AddContainer("api", "myapp/api", "latest"); using var app = builder.Build(); var model = app.Services.GetRequiredService<DistributedApplicationModel>(); var radiusEnv = model.Resources.OfType<RadiusEnvironmentResource>().First(); RadiusTestHelper.AttachDeploymentTargets(radiusEnv, model); var context = new RadiusBicepPublishingContext(radiusEnv); var bicep = context.GenerateBicep(model); Assert.Contains("Radius.Compute/containers@2025-08-01-preview", bicep); Assert.Contains("environment: myenv.id", bicep); } [Fact] public void SimpleSingleContainer_RegistersDefaultContainerRecipeInPack() { // Shipped Radius has no built-in recipe for Radius.Compute/containers, so the // publisher must register the published container recipe in the env's recipe pack; // otherwise `rad deploy` fails ("no recipe pack found for Radius.Compute/containers"). using var builder = TestDistributedApplicationBuilder.Create(DistributedApplicationOperation.Publish); builder.AddRadiusEnvironment("myenv"); builder.AddContainer("api", "myapp/api", "latest"); using var app = builder.Build(); var model = app.Services.GetRequiredService<DistributedApplicationModel>(); var radiusEnv = model.Resources.OfType<RadiusEnvironmentResource>().First(); RadiusTestHelper.AttachDeploymentTargets(radiusEnv, model); var context = new RadiusBicepPublishingContext(radiusEnv); var bicep = context.GenerateBicep(model); Assert.Contains("'Radius.Compute/containers': {", bicep); Assert.Contains("ghcr.io/radius-project/kube-recipes/containers:latest", bicep); } [Fact] public void GenerateBicep_ProjectResourceWithoutContainerImage_ThrowsActionableError() { // The Aspire.Hosting.Radius integration does not yet build or push project images. // Without a clear failure at publish time the user would only see ImagePullBackOff // inside the cluster after `aspire deploy` (surfaced by Radius/Kubernetes, not // Aspire) — opaque and hard to attribute. Fail fast with a remediation hint // naming the missing prereq. using var builder = TestDistributedApplicationBuilder.Create(DistributedApplicationOperation.Publish); var env = builder.AddRadiusEnvironment("myenv"); builder.AddProject<TestProjectMetadata>("webapp"); using var app = builder.Build(); var model = app.Services.GetRequiredService<DistributedApplicationModel>(); RadiusTestHelper.AttachDeploymentTargets(env.Resource, model); var context = new RadiusBicepPublishingContext(env.Resource); var ex = Assert.Throws<InvalidOperationException>(() => context.GenerateBicep(model)); Assert.Contains("webapp", ex.Message); Assert.Contains("WithContainerImage", ex.Message); } [Fact] public void EnvironmentResource_HasPipelineStepAnnotation() { using var builder = TestDistributedApplicationBuilder.Create(DistributedApplicationOperation.Publish); var env = builder.AddRadiusEnvironment("myenv"); var annotations = env.Resource.Annotations.OfType<PipelineStepAnnotation>().ToList(); // A single PipelineStepAnnotation with a multi-step factory is registered in // RadiusEnvironmentResource..ctor (mirroring KubernetesEnvironmentResource). // The factory expands to three pipeline steps at execution time: // 1. prepare-deployment-targets-{name} — attaches DeploymentTargetAnnotation (A1) // 2. publish-radius-{name} — emits app.bicep + bicepconfig.json // 3. deploy-radius-{name} — invokes `rad deploy` Assert.Single(annotations); } private sealed class TestProjectMetadata : IProjectMetadata { public string ProjectPath => "testproject"; public LaunchSettings LaunchSettings { get; } = new(); } }