File: ContainerRegistryResourceTests.cs
Web Access
Project: src\tests\Aspire.Hosting.Tests\Aspire.Hosting.Tests.csproj (Aspire.Hosting.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 ASPIRECOMPUTE003 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed.
#pragma warning disable ASPIREPIPELINES001 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed.
 
using Aspire.Hosting.Pipelines;
using Aspire.Hosting.Utils;
using Microsoft.Extensions.DependencyInjection;
 
namespace Aspire.Hosting.Tests;
 
public class ContainerRegistryResourceTests(ITestOutputHelper testOutputHelper)
{
    [Fact]
    public void AddContainerRegistryWithStrings()
    {
        using var builder = TestDistributedApplicationBuilder.Create(testOutputHelper);
 
        var registry = builder.AddContainerRegistry("docker-hub", "docker.io", "captainsafia");
 
        Assert.Equal("docker-hub", registry.Resource.Name);
 
        var containerRegistry = registry.Resource as IContainerRegistry;
        Assert.NotNull(containerRegistry);
        Assert.NotNull(containerRegistry.Name);
        Assert.NotNull(containerRegistry.Endpoint);
        Assert.NotNull(containerRegistry.Repository);
    }
 
    [Fact]
    public void AddContainerRegistryWithStringsWithoutRepository()
    {
        using var builder = TestDistributedApplicationBuilder.Create(testOutputHelper);
 
        var registry = builder.AddContainerRegistry("acr", "myregistry.azurecr.io");
 
        Assert.Equal("acr", registry.Resource.Name);
 
        var containerRegistry = registry.Resource as IContainerRegistry;
        Assert.NotNull(containerRegistry);
        Assert.NotNull(containerRegistry.Name);
        Assert.NotNull(containerRegistry.Endpoint);
        Assert.Null(containerRegistry.Repository);
    }
 
    [Fact]
    public void AddContainerRegistryWithParameters()
    {
        using var builder = TestDistributedApplicationBuilder.Create(testOutputHelper);
 
        var endpointParam = builder.AddParameter("registry-endpoint");
        var repositoryParam = builder.AddParameter("registry-repo");
        var registry = builder.AddContainerRegistry("my-registry", endpointParam, repositoryParam);
 
        Assert.Equal("my-registry", registry.Resource.Name);
 
        var containerRegistry = registry.Resource as IContainerRegistry;
        Assert.NotNull(containerRegistry);
        Assert.NotNull(containerRegistry.Name);
        Assert.NotNull(containerRegistry.Endpoint);
        Assert.NotNull(containerRegistry.Repository);
    }
 
    [Fact]
    public void AddContainerRegistryWithParametersWithoutRepository()
    {
        using var builder = TestDistributedApplicationBuilder.Create(testOutputHelper);
 
        var endpointParam = builder.AddParameter("registry-endpoint");
        var registry = builder.AddContainerRegistry("my-registry", endpointParam);
 
        Assert.Equal("my-registry", registry.Resource.Name);
 
        var containerRegistry = registry.Resource as IContainerRegistry;
        Assert.NotNull(containerRegistry);
        Assert.NotNull(containerRegistry.Name);
        Assert.NotNull(containerRegistry.Endpoint);
        Assert.Null(containerRegistry.Repository);
    }
 
    [Fact]
    public void AddContainerRegistryWithNullBuilderThrows()
    {
        IDistributedApplicationBuilder builder = null!;
 
        Assert.Throws<ArgumentNullException>(() => builder.AddContainerRegistry("registry", "docker.io"));
    }
 
    [Fact]
    public void AddContainerRegistryWithNullNameThrows()
    {
        using var builder = TestDistributedApplicationBuilder.Create(testOutputHelper);
 
        Assert.Throws<ArgumentNullException>(() => builder.AddContainerRegistry(null!, "docker.io"));
    }
 
    [Fact]
    public void AddContainerRegistryWithEmptyNameThrows()
    {
        using var builder = TestDistributedApplicationBuilder.Create(testOutputHelper);
 
        Assert.Throws<ArgumentException>(() => builder.AddContainerRegistry("", "docker.io"));
    }
 
    [Fact]
    public void AddContainerRegistryWithNullEndpointStringThrows()
    {
        using var builder = TestDistributedApplicationBuilder.Create(testOutputHelper);
 
        Assert.Throws<ArgumentNullException>(() => builder.AddContainerRegistry("registry", (string)null!));
    }
 
    [Fact]
    public void AddContainerRegistryWithEmptyEndpointStringThrows()
    {
        using var builder = TestDistributedApplicationBuilder.Create(testOutputHelper);
 
        Assert.Throws<ArgumentException>(() => builder.AddContainerRegistry("registry", ""));
    }
 
    [Fact]
    public void AddContainerRegistryWithNullEndpointParameterThrows()
    {
        using var builder = TestDistributedApplicationBuilder.Create(testOutputHelper);
 
        Assert.Throws<ArgumentNullException>(() => builder.AddContainerRegistry("registry", (IResourceBuilder<ParameterResource>)null!));
    }
 
    [Fact]
    public void ContainerRegistryResourceNotAddedToModelInRunMode()
    {
        // In run mode, the resource should not be added to the application model
        using var builder = TestDistributedApplicationBuilder.Create(DistributedApplicationOperation.Run);
 
        builder.AddContainerRegistry("docker-hub", "docker.io", "myuser");
 
        using var app = builder.Build();
        var appModel = app.Services.GetRequiredService<DistributedApplicationModel>();
 
        // Resource should NOT be in the model in run mode
        Assert.Empty(appModel.Resources.OfType<ContainerRegistryResource>());
    }
 
    [Fact]
    public void ContainerRegistryResourceIsAddedToModelInPublishMode()
    {
        // In publish mode, the resource should be added to the application model
        using var builder = TestDistributedApplicationBuilder.Create(DistributedApplicationOperation.Publish);
 
        builder.AddContainerRegistry("docker-hub", "docker.io", "myuser");
 
        using var app = builder.Build();
        var appModel = app.Services.GetRequiredService<DistributedApplicationModel>();
 
        var registry = Assert.Single(appModel.Resources.OfType<ContainerRegistryResource>());
        Assert.Equal("docker-hub", registry.Name);
    }
 
    [Fact]
    public void ContainerRegistryWithParametersNotAddedToModelInRunMode()
    {
        // In run mode, the resource should not be added to the application model
        using var builder = TestDistributedApplicationBuilder.Create(DistributedApplicationOperation.Run);
 
        var endpointParam = builder.AddParameter("registry-endpoint");
        builder.AddContainerRegistry("my-registry", endpointParam);
 
        using var app = builder.Build();
        var appModel = app.Services.GetRequiredService<DistributedApplicationModel>();
 
        // Resource should NOT be in the model in run mode
        Assert.Empty(appModel.Resources.OfType<ContainerRegistryResource>());
    }
 
    [Fact]
    public void ContainerRegistryWithParametersIsAddedToModelInPublishMode()
    {
        // In publish mode, the resource should be added to the application model
        using var builder = TestDistributedApplicationBuilder.Create(DistributedApplicationOperation.Publish);
 
        var endpointParam = builder.AddParameter("registry-endpoint");
        builder.AddContainerRegistry("my-registry", endpointParam);
 
        using var app = builder.Build();
        var appModel = app.Services.GetRequiredService<DistributedApplicationModel>();
 
        var registry = Assert.Single(appModel.Resources.OfType<ContainerRegistryResource>());
        Assert.Equal("my-registry", registry.Name);
    }
 
    [Fact]
    public async Task ContainerRegistryEndpointExpressionResolves()
    {
        using var builder = TestDistributedApplicationBuilder.Create(testOutputHelper);
 
        var registry = builder.AddContainerRegistry("docker-hub", "docker.io", "captainsafia");
 
        var containerRegistry = registry.Resource as IContainerRegistry;
        Assert.NotNull(containerRegistry);
 
        var endpointValue = await containerRegistry.Endpoint.GetValueAsync(default);
        Assert.Equal("docker.io", endpointValue);
    }
 
    [Fact]
    public async Task ContainerRegistryRepositoryExpressionResolves()
    {
        using var builder = TestDistributedApplicationBuilder.Create(testOutputHelper);
 
        var registry = builder.AddContainerRegistry("docker-hub", "docker.io", "captainsafia");
 
        var containerRegistry = registry.Resource as IContainerRegistry;
        Assert.NotNull(containerRegistry);
        Assert.NotNull(containerRegistry.Repository);
 
        var repositoryValue = await containerRegistry.Repository.GetValueAsync(default);
        Assert.Equal("captainsafia", repositoryValue);
    }
 
    [Fact]
    public async Task ContainerRegistryNameExpressionResolves()
    {
        using var builder = TestDistributedApplicationBuilder.Create(testOutputHelper);
 
        var registry = builder.AddContainerRegistry("docker-hub", "docker.io", "captainsafia");
 
        var containerRegistry = registry.Resource as IContainerRegistry;
        Assert.NotNull(containerRegistry);
 
        var nameValue = await containerRegistry.Name.GetValueAsync(default);
        Assert.Equal("docker-hub", nameValue);
    }
 
    [Fact]
    public async Task ContainerRegistryWithParameterEndpointResolves()
    {
        using var builder = TestDistributedApplicationBuilder.Create(testOutputHelper);
        builder.Configuration["Parameters:registry-endpoint"] = "ghcr.io";
 
        var endpointParam = builder.AddParameter("registry-endpoint");
        var registry = builder.AddContainerRegistry("ghcr", endpointParam);
 
        var containerRegistry = registry.Resource as IContainerRegistry;
        Assert.NotNull(containerRegistry);
 
        var endpointValue = await containerRegistry.Endpoint.GetValueAsync(default);
        Assert.Equal("ghcr.io", endpointValue);
    }
 
    [Fact]
    public async Task ContainerRegistryWithParameterRepositoryResolves()
    {
        using var builder = TestDistributedApplicationBuilder.Create(testOutputHelper);
        builder.Configuration["Parameters:registry-endpoint"] = "ghcr.io";
        builder.Configuration["Parameters:registry-repo"] = "captainsafia/my-repo";
 
        var endpointParam = builder.AddParameter("registry-endpoint");
        var repositoryParam = builder.AddParameter("registry-repo");
        var registry = builder.AddContainerRegistry("ghcr", endpointParam, repositoryParam);
 
        var containerRegistry = registry.Resource as IContainerRegistry;
        Assert.NotNull(containerRegistry);
        Assert.NotNull(containerRegistry.Repository);
 
        var repositoryValue = await containerRegistry.Repository.GetValueAsync(default);
        Assert.Equal("captainsafia/my-repo", repositoryValue);
    }
 
    [Fact]
    public void ContainerRegistryResourceImplementsIContainerRegistry()
    {
        using var builder = TestDistributedApplicationBuilder.Create(testOutputHelper);
 
        var registry = builder.AddContainerRegistry("docker-hub", "docker.io", "myuser");
 
        Assert.IsAssignableFrom<IContainerRegistry>(registry.Resource);
    }
 
    [Fact]
    public void MultipleContainerRegistriesCanBeAddedInPublishMode()
    {
        using var builder = TestDistributedApplicationBuilder.Create(DistributedApplicationOperation.Publish);
 
        builder.AddContainerRegistry("docker-hub", "docker.io", "user1");
        builder.AddContainerRegistry("ghcr", "ghcr.io", "user2/repo");
        builder.AddContainerRegistry("acr", "myregistry.azurecr.io");
 
        using var app = builder.Build();
        var appModel = app.Services.GetRequiredService<DistributedApplicationModel>();
 
        var registries = appModel.Resources.OfType<ContainerRegistryResource>().ToList();
        Assert.Equal(3, registries.Count);
        Assert.Contains(registries, r => r.Name == "docker-hub");
        Assert.Contains(registries, r => r.Name == "ghcr");
        Assert.Contains(registries, r => r.Name == "acr");
    }
 
    [Fact]
    public void WithContainerRegistryAddsAnnotation()
    {
        using var builder = TestDistributedApplicationBuilder.Create(testOutputHelper);
 
        var registry = builder.AddContainerRegistry("docker-hub", "docker.io", "myuser");
        var container = builder.AddContainer("mycontainer", "myimage")
            .WithContainerRegistry(registry);
 
        var annotation = container.Resource.Annotations.OfType<ContainerRegistryReferenceAnnotation>().SingleOrDefault();
        Assert.NotNull(annotation);
        Assert.Same(registry.Resource, annotation.Registry);
    }
 
    [Fact]
    public void WithContainerRegistryWithNullBuilderThrows()
    {
        using var builder = TestDistributedApplicationBuilder.Create(testOutputHelper);
 
        var registry = builder.AddContainerRegistry("docker-hub", "docker.io", "myuser");
        IResourceBuilder<ContainerResource> containerBuilder = null!;
 
        Assert.Throws<ArgumentNullException>(() => containerBuilder.WithContainerRegistry(registry));
    }
 
    [Fact]
    public void WithContainerRegistryWithNullRegistryThrows()
    {
        using var builder = TestDistributedApplicationBuilder.Create(testOutputHelper);
 
        var container = builder.AddContainer("mycontainer", "myimage");
        IResourceBuilder<ContainerRegistryResource> registry = null!;
 
        Assert.Throws<ArgumentNullException>(() => container.WithContainerRegistry(registry));
    }
 
    [Fact]
    public void ContainerWithDockerfileHasPushStepAndConfigurationAnnotations()
    {
        using var builder = TestDistributedApplicationBuilder.Create(DistributedApplicationOperation.Publish);
 
        builder.AddContainerRegistry("docker-hub", "docker.io", "myuser");
        var container = builder.AddDockerfile("mycontainer", "../myapp");
 
        var pipelineStepAnnotations = container.Resource.Annotations.OfType<PipelineStepAnnotation>().ToList();
        var pipelineConfigAnnotations = container.Resource.Annotations.OfType<PipelineConfigurationAnnotation>().ToList();
 
        Assert.NotEmpty(pipelineStepAnnotations);
        Assert.NotEmpty(pipelineConfigAnnotations);
    }
 
    [Fact]
    public void ProjectResourceHasPushStepAndConfigurationAnnotations()
    {
        using var builder = TestDistributedApplicationBuilder.Create(DistributedApplicationOperation.Publish);
 
        builder.AddContainerRegistry("docker-hub", "docker.io", "myuser");
        var project = builder.AddProject<Projects.ServiceA>("api");
 
        var pipelineStepAnnotations = project.Resource.Annotations.OfType<PipelineStepAnnotation>().ToList();
        var pipelineConfigAnnotations = project.Resource.Annotations.OfType<PipelineConfigurationAnnotation>().ToList();
 
        Assert.NotEmpty(pipelineStepAnnotations);
        Assert.NotEmpty(pipelineConfigAnnotations);
    }
 
    [Fact]
    public async Task ProjectResourcePushStepHasPushContainerImageTag()
    {
        using var builder = TestDistributedApplicationBuilder.Create(DistributedApplicationOperation.Publish);
 
        builder.AddContainerRegistry("docker-hub", "docker.io", "myuser");
        var project = builder.AddProject<Projects.ServiceA>("api");
 
        var pipelineStepAnnotation = Assert.Single(project.Resource.Annotations.OfType<PipelineStepAnnotation>());
 
        var factoryContext = new PipelineStepFactoryContext
        {
            PipelineContext = null!,
            Resource = project.Resource
        };
 
        var steps = (await pipelineStepAnnotation.CreateStepsAsync(factoryContext)).ToList();
 
        var pushStep = steps.FirstOrDefault(s => s.Name == "push-api");
        Assert.NotNull(pushStep);
        Assert.Contains(WellKnownPipelineTags.PushContainerImage, pushStep.Tags);
        Assert.Contains(WellKnownPipelineSteps.Push, pushStep.RequiredBySteps);
    }
 
    [Fact]
    public async Task ContainerWithDockerfilePushStepHasPushContainerImageTag()
    {
        using var builder = TestDistributedApplicationBuilder.Create(DistributedApplicationOperation.Publish);
 
        builder.AddContainerRegistry("docker-hub", "docker.io", "myuser");
        var container = builder.AddDockerfile("mycontainer", "../myapp");
 
        var pipelineStepAnnotation = Assert.Single(container.Resource.Annotations.OfType<PipelineStepAnnotation>());
 
        var factoryContext = new PipelineStepFactoryContext
        {
            PipelineContext = null!,
            Resource = container.Resource
        };
 
        var steps = (await pipelineStepAnnotation.CreateStepsAsync(factoryContext)).ToList();
 
        var pushStep = steps.FirstOrDefault(s => s.Name == "push-mycontainer");
        Assert.NotNull(pushStep);
        Assert.Contains(WellKnownPipelineTags.PushContainerImage, pushStep.Tags);
        Assert.Contains(WellKnownPipelineSteps.Push, pushStep.RequiredBySteps);
    }
 
    [Fact]
    public async Task ContainerWithoutDockerfileHasNoPushStep()
    {
        using var builder = TestDistributedApplicationBuilder.Create(DistributedApplicationOperation.Publish);
 
        builder.AddContainerRegistry("docker-hub", "docker.io", "myuser");
        var container = builder.AddContainer("mycontainer", "myimage");
 
        var pipelineStepAnnotations = container.Resource.Annotations.OfType<PipelineStepAnnotation>().ToList();
 
        foreach (var annotation in pipelineStepAnnotations)
        {
            var factoryContext = new PipelineStepFactoryContext
            {
                PipelineContext = null!,
                Resource = container.Resource
            };
 
            var steps = (await annotation.CreateStepsAsync(factoryContext)).ToList();
 
            Assert.DoesNotContain(steps, s => s.Tags.Contains(WellKnownPipelineTags.PushContainerImage));
        }
    }
 
    [Fact]
    public async Task ProjectResourcePushStepDependsOnBuildStep()
    {
        using var builder = TestDistributedApplicationBuilder.Create(DistributedApplicationOperation.Publish);
 
        builder.AddContainerRegistry("docker-hub", "docker.io", "myuser");
        var project = builder.AddProject<Projects.ServiceA>("api");
 
        var pipelineStepAnnotation = Assert.Single(project.Resource.Annotations.OfType<PipelineStepAnnotation>());
 
        var factoryContext = new PipelineStepFactoryContext
        {
            PipelineContext = null!,
            Resource = project.Resource
        };
 
        var steps = (await pipelineStepAnnotation.CreateStepsAsync(factoryContext)).ToList();
 
        var buildStep = steps.FirstOrDefault(s => s.Name == "build-api");
        var pushStep = steps.FirstOrDefault(s => s.Name == "push-api");
 
        Assert.NotNull(buildStep);
        Assert.NotNull(pushStep);
 
        Assert.Contains(WellKnownPipelineTags.BuildCompute, buildStep.Tags);
    }
 
    [Fact]
    public void WithContainerRegistryAddsAnnotationToProject()
    {
        using var builder = TestDistributedApplicationBuilder.Create(DistributedApplicationOperation.Publish);
 
        var registry = builder.AddContainerRegistry("docker-hub", "docker.io", "myuser");
        var project = builder.AddProject<Projects.ServiceA>("api")
            .WithContainerRegistry(registry);
 
        var annotation = project.Resource.Annotations.OfType<ContainerRegistryReferenceAnnotation>().FirstOrDefault();
        Assert.NotNull(annotation);
        Assert.Same(registry.Resource, annotation.Registry);
    }
 
    [Fact]
    public void MultipleRegistriesCanBeAddedWithExplicitSelection()
    {
        using var builder = TestDistributedApplicationBuilder.Create(DistributedApplicationOperation.Publish);
 
        var registry1 = builder.AddContainerRegistry("docker-hub", "docker.io", "user1");
        var registry2 = builder.AddContainerRegistry("ghcr", "ghcr.io", "user2");
 
        var project = builder.AddProject<Projects.ServiceA>("api")
            .WithContainerRegistry(registry1);
 
        var annotations = project.Resource.Annotations.OfType<ContainerRegistryReferenceAnnotation>().ToList();
        Assert.Single(annotations);
        Assert.Same(registry1.Resource, annotations[0].Registry);
    }
 
    [Fact]
    public void ContainerRegistryReferenceAnnotationHoldsCorrectRegistry()
    {
        using var builder = TestDistributedApplicationBuilder.Create(DistributedApplicationOperation.Publish);
 
        var registry = builder.AddContainerRegistry("acr", "myregistry.azurecr.io");
 
        var annotation = new ContainerRegistryReferenceAnnotation(registry.Resource);
 
        Assert.Same(registry.Resource, annotation.Registry);
    }
 
    [Fact]
    public async Task RegistryTargetAnnotationIsAddedToResourcesOnBeforeStartEvent()
    {
        using var builder = TestDistributedApplicationBuilder.Create(DistributedApplicationOperation.Publish);
 
        var registry = builder.AddContainerRegistry("docker-hub", "docker.io", "myuser");
        var project = builder.AddProject<Projects.ServiceA>("api");
 
        using var app = builder.Build();
        var appModel = app.Services.GetRequiredService<DistributedApplicationModel>();
 
        // Before BeforeStartEvent, the project should not have RegistryTargetAnnotation
        Assert.Empty(project.Resource.Annotations.OfType<RegistryTargetAnnotation>());
 
        // Simulate BeforeStartEvent
        var beforeStartEvent = new BeforeStartEvent(app.Services, appModel);
        await builder.Eventing.PublishAsync(beforeStartEvent);
 
        // After BeforeStartEvent, the project should have RegistryTargetAnnotation
        var registryTargetAnnotation = Assert.Single(project.Resource.Annotations.OfType<RegistryTargetAnnotation>());
        Assert.Same(registry.Resource, registryTargetAnnotation.Registry);
    }
 
    [Fact]
    public async Task MultipleRegistriesAddMultipleRegistryTargetAnnotations()
    {
        using var builder = TestDistributedApplicationBuilder.Create(DistributedApplicationOperation.Publish);
 
        var registry1 = builder.AddContainerRegistry("docker-hub", "docker.io", "user1");
        var registry2 = builder.AddContainerRegistry("ghcr", "ghcr.io", "user2");
        var project = builder.AddProject<Projects.ServiceA>("api");
 
        using var app = builder.Build();
        var appModel = app.Services.GetRequiredService<DistributedApplicationModel>();
 
        // Simulate BeforeStartEvent
        var beforeStartEvent = new BeforeStartEvent(app.Services, appModel);
        await builder.Eventing.PublishAsync(beforeStartEvent);
 
        // Project should have two RegistryTargetAnnotations
        var registryTargetAnnotations = project.Resource.Annotations.OfType<RegistryTargetAnnotation>().ToList();
        Assert.Equal(2, registryTargetAnnotations.Count);
 
        var registryResources = registryTargetAnnotations.Select(a => a.Registry).ToList();
        Assert.Contains(registry1.Resource, registryResources);
        Assert.Contains(registry2.Resource, registryResources);
    }
 
    [Fact]
    public async Task GetContainerRegistryReturnsRegistryFromRegistryTargetAnnotation()
    {
        using var builder = TestDistributedApplicationBuilder.Create(DistributedApplicationOperation.Publish);
 
        var registry = builder.AddContainerRegistry("docker-hub", "docker.io", "myuser");
        var project = builder.AddProject<Projects.ServiceA>("api");
 
        using var app = builder.Build();
        var appModel = app.Services.GetRequiredService<DistributedApplicationModel>();
 
        // Simulate BeforeStartEvent to add RegistryTargetAnnotation
        var beforeStartEvent = new BeforeStartEvent(app.Services, appModel);
        await builder.Eventing.PublishAsync(beforeStartEvent);
 
        // GetContainerRegistry should return the registry from RegistryTargetAnnotation
        var containerRegistry = project.Resource.GetContainerRegistry();
        Assert.Same(registry.Resource, containerRegistry);
    }
 
    [Fact]
    public async Task GetContainerRegistryPrefersContainerRegistryReferenceAnnotationOverRegistryTargetAnnotation()
    {
        using var builder = TestDistributedApplicationBuilder.Create(DistributedApplicationOperation.Publish);
 
        var registry1 = builder.AddContainerRegistry("docker-hub", "docker.io", "user1");
        var registry2 = builder.AddContainerRegistry("ghcr", "ghcr.io", "user2");
        var project = builder.AddProject<Projects.ServiceA>("api")
            .WithContainerRegistry(registry2); // Explicit preference for registry2
 
        using var app = builder.Build();
        var appModel = app.Services.GetRequiredService<DistributedApplicationModel>();
 
        // Simulate BeforeStartEvent to add RegistryTargetAnnotations
        var beforeStartEvent = new BeforeStartEvent(app.Services, appModel);
        await builder.Eventing.PublishAsync(beforeStartEvent);
 
        // GetContainerRegistry should return registry2 (from ContainerRegistryReferenceAnnotation)
        // even though both registries added RegistryTargetAnnotations
        var containerRegistry = project.Resource.GetContainerRegistry();
        Assert.Same(registry2.Resource, containerRegistry);
    }
 
    [Fact]
    public async Task GetContainerRegistryThrowsWhenMultipleRegistryTargetAnnotationsAndNoExplicitSelection()
    {
        using var builder = TestDistributedApplicationBuilder.Create(DistributedApplicationOperation.Publish);
 
        var registry1 = builder.AddContainerRegistry("docker-hub", "docker.io", "user1");
        var registry2 = builder.AddContainerRegistry("ghcr", "ghcr.io", "user2");
        var project = builder.AddProject<Projects.ServiceA>("api");
        // No explicit WithContainerRegistry call
 
        using var app = builder.Build();
        var appModel = app.Services.GetRequiredService<DistributedApplicationModel>();
 
        // Simulate BeforeStartEvent to add RegistryTargetAnnotations
        var beforeStartEvent = new BeforeStartEvent(app.Services, appModel);
        await builder.Eventing.PublishAsync(beforeStartEvent);
 
        // GetContainerRegistry should throw because there are multiple registries and no explicit selection
        var exception = Assert.Throws<InvalidOperationException>(project.Resource.GetContainerRegistry);
        Assert.Contains("multiple container registries", exception.Message);
        Assert.Contains("WithContainerRegistry", exception.Message);
    }
 
    [Fact]
    public void GetContainerRegistryThrowsWhenNoRegistryAvailable()
    {
        using var builder = TestDistributedApplicationBuilder.Create(DistributedApplicationOperation.Publish);
 
        var project = builder.AddProject<Projects.ServiceA>("api");
        // No container registry added
 
        // GetContainerRegistry should throw because there's no registry available
        var exception = Assert.Throws<InvalidOperationException>(project.Resource.GetContainerRegistry);
        Assert.Contains("does not have a container registry reference", exception.Message);
    }
 
    [Fact]
    public async Task RegistryTargetAnnotationIsAddedToAllResourcesInModel()
    {
        using var builder = TestDistributedApplicationBuilder.Create(DistributedApplicationOperation.Publish);
 
        var registry = builder.AddContainerRegistry("docker-hub", "docker.io", "myuser");
        var project1 = builder.AddProject<Projects.ServiceA>("api1");
        var project2 = builder.AddProject<Projects.ServiceB>("api2");
        var container = builder.AddContainer("redis", "redis:latest");
 
        using var app = builder.Build();
        var appModel = app.Services.GetRequiredService<DistributedApplicationModel>();
 
        // Simulate BeforeStartEvent
        var beforeStartEvent = new BeforeStartEvent(app.Services, appModel);
        await builder.Eventing.PublishAsync(beforeStartEvent);
 
        // All resources should have RegistryTargetAnnotation
        Assert.Single(project1.Resource.Annotations.OfType<RegistryTargetAnnotation>());
        Assert.Single(project2.Resource.Annotations.OfType<RegistryTargetAnnotation>());
        Assert.Single(container.Resource.Annotations.OfType<RegistryTargetAnnotation>());
    }
 
    [Fact]
    public async Task WithContainerRegistryOverridesDefaultRegistryTargetAnnotation()
    {
        using var builder = TestDistributedApplicationBuilder.Create(DistributedApplicationOperation.Publish);
 
        var defaultRegistry = builder.AddContainerRegistry("docker-hub", "docker.io", "default");
        var specificRegistry = builder.AddContainerRegistry("acr", "myregistry.azurecr.io", "specific");
 
        var project = builder.AddProject<Projects.ServiceA>("api")
            .WithContainerRegistry(specificRegistry);
 
        using var app = builder.Build();
        var appModel = app.Services.GetRequiredService<DistributedApplicationModel>();
 
        // Simulate BeforeStartEvent
        var beforeStartEvent = new BeforeStartEvent(app.Services, appModel);
        await builder.Eventing.PublishAsync(beforeStartEvent);
 
        // The project has both RegistryTargetAnnotations (from BeforeStartEvent) and ContainerRegistryReferenceAnnotation
        var registryTargetAnnotations = project.Resource.Annotations.OfType<RegistryTargetAnnotation>().ToList();
        Assert.Equal(2, registryTargetAnnotations.Count);
 
        var containerRegistryRefAnnotation = Assert.Single(project.Resource.Annotations.OfType<ContainerRegistryReferenceAnnotation>());
        Assert.Same(specificRegistry.Resource, containerRegistryRefAnnotation.Registry);
 
        // GetContainerRegistry should return specificRegistry because ContainerRegistryReferenceAnnotation takes precedence
        var containerRegistry = project.Resource.GetContainerRegistry();
        Assert.Same(specificRegistry.Resource, containerRegistry);
    }
 
    [Fact]
    public async Task AzureContainerRegistry_RegistryTargetAnnotationIsAddedToResourcesOnBeforeStartEvent()
    {
        using var builder = TestDistributedApplicationBuilder.Create(DistributedApplicationOperation.Publish);
 
        var acr = builder.AddAzureContainerRegistry("acr");
        var project = builder.AddProject<Projects.ServiceA>("api");
 
        using var app = builder.Build();
        var appModel = app.Services.GetRequiredService<DistributedApplicationModel>();
 
        // Before BeforeStartEvent, the project should not have RegistryTargetAnnotation
        Assert.Empty(project.Resource.Annotations.OfType<RegistryTargetAnnotation>());
 
        // Simulate BeforeStartEvent
        var beforeStartEvent = new BeforeStartEvent(app.Services, appModel);
        await builder.Eventing.PublishAsync(beforeStartEvent);
 
        // After BeforeStartEvent, the project should have RegistryTargetAnnotation
        var registryTargetAnnotation = Assert.Single(project.Resource.Annotations.OfType<RegistryTargetAnnotation>());
        Assert.Same(acr.Resource, registryTargetAnnotation.Registry);
    }
 
    [Fact]
    public async Task AzureContainerRegistry_RegistryTargetAnnotationIsAddedToAllResourcesInModel()
    {
        using var builder = TestDistributedApplicationBuilder.Create(DistributedApplicationOperation.Publish);
 
        var acr = builder.AddAzureContainerRegistry("acr");
        var project = builder.AddProject<Projects.ServiceA>("api");
        var container = builder.AddContainer("redis", "redis:latest");
 
        using var app = builder.Build();
        var appModel = app.Services.GetRequiredService<DistributedApplicationModel>();
 
        // Simulate BeforeStartEvent
        var beforeStartEvent = new BeforeStartEvent(app.Services, appModel);
        await builder.Eventing.PublishAsync(beforeStartEvent);
 
        // All resources should have RegistryTargetAnnotation
        Assert.Single(project.Resource.Annotations.OfType<RegistryTargetAnnotation>());
        Assert.Single(container.Resource.Annotations.OfType<RegistryTargetAnnotation>());
    }
 
    [Fact]
    public async Task AzureContainerRegistry_GetContainerRegistryReturnsRegistryFromRegistryTargetAnnotation()
    {
        using var builder = TestDistributedApplicationBuilder.Create(DistributedApplicationOperation.Publish);
 
        var acr = builder.AddAzureContainerRegistry("acr");
        var project = builder.AddProject<Projects.ServiceA>("api");
 
        using var app = builder.Build();
        var appModel = app.Services.GetRequiredService<DistributedApplicationModel>();
 
        // Simulate BeforeStartEvent to add RegistryTargetAnnotation
        var beforeStartEvent = new BeforeStartEvent(app.Services, appModel);
        await builder.Eventing.PublishAsync(beforeStartEvent);
 
        // GetContainerRegistry should return the registry from RegistryTargetAnnotation
        var containerRegistry = project.Resource.GetContainerRegistry();
        Assert.Same(acr.Resource, containerRegistry);
    }
 
    [Fact]
    public async Task AzureContainerRegistry_GetContainerRegistryPrefersContainerRegistryReferenceAnnotation()
    {
        using var builder = TestDistributedApplicationBuilder.Create(DistributedApplicationOperation.Publish);
 
        var acr = builder.AddAzureContainerRegistry("acr");
        var env = builder.AddAzureContainerAppEnvironment("env")
            .WithAzureContainerRegistry(acr);
 
        using var app = builder.Build();
        var appModel = app.Services.GetRequiredService<DistributedApplicationModel>();
 
        // Simulate BeforeStartEvent to add RegistryTargetAnnotations
        var beforeStartEvent = new BeforeStartEvent(app.Services, appModel);
        await builder.Eventing.PublishAsync(beforeStartEvent);
 
        // GetContainerRegistry should return acr (from ContainerRegistryReferenceAnnotation)
        var containerRegistry = env.Resource.GetContainerRegistry();
        Assert.Same(acr.Resource, containerRegistry);
    }
 
    [Fact]
    public async Task AzureContainerRegistryAndContainerRegistry_MultipleRegistriesAddMultipleRegistryTargetAnnotations()
    {
        using var builder = TestDistributedApplicationBuilder.Create(DistributedApplicationOperation.Publish);
 
        var acr = builder.AddAzureContainerRegistry("acr");
        var dockerHub = builder.AddContainerRegistry("docker-hub", "docker.io", "myuser");
        var project = builder.AddProject<Projects.ServiceA>("api");
 
        using var app = builder.Build();
        var appModel = app.Services.GetRequiredService<DistributedApplicationModel>();
 
        // Simulate BeforeStartEvent
        var beforeStartEvent = new BeforeStartEvent(app.Services, appModel);
        await builder.Eventing.PublishAsync(beforeStartEvent);
 
        // Project should have two RegistryTargetAnnotations (one from each registry)
        var registryTargetAnnotations = project.Resource.Annotations.OfType<RegistryTargetAnnotation>().ToList();
        Assert.Equal(2, registryTargetAnnotations.Count);
 
        var registryResources = registryTargetAnnotations.Select(a => a.Registry).ToList();
        Assert.Contains(acr.Resource, registryResources);
        Assert.Contains(dockerHub.Resource, registryResources);
    }
}