@@ -188,8 +188,8 @@ public async Task DeployAsync_WithContainer_Works()
188188 Assert . Equal ( "test.westus.azurecontainerapps.io" , containerAppEnv . Resource . Outputs [ "AZURE_CONTAINER_APPS_ENVIRONMENT_DEFAULT_DOMAIN" ] ) ;
189189 Assert . Equal ( "/subscriptions/test/resourceGroups/test-rg/providers/Microsoft.App/managedEnvironments/testenv" , containerAppEnv . Resource . Outputs [ "AZURE_CONTAINER_APPS_ENVIRONMENT_ID" ] ) ;
190190
191- // Assert - Verify ACR login command was called
192- Assert . Contains ( mockProcessRunner . ExecutedCommands ,
191+ // Assert - Verify ACR login command was not called since no image was pushed
192+ Assert . DoesNotContain ( mockProcessRunner . ExecutedCommands ,
193193 cmd => cmd . ExecutablePath . Contains ( "az" ) &&
194194 cmd . Arguments == "acr login --name testregistry" ) ;
195195
@@ -205,6 +205,57 @@ public async Task DeployAsync_WithContainer_Works()
205205 cmd . Arguments . StartsWith ( "push testregistry.azurecr.io/" ) ) ;
206206 }
207207
208+ [ Fact ]
209+ public async Task DeployAsync_WithDockerfile_Works ( )
210+ {
211+ // Arrange
212+ var mockProcessRunner = new MockProcessRunner ( ) ;
213+ using var builder = TestDistributedApplicationBuilder . Create ( DistributedApplicationOperation . Publish , publisher : "default" , isDeploy : true ) ;
214+ var deploymentOutputs = new Dictionary < string , object >
215+ {
216+ [ "env_AZURE_CONTAINER_REGISTRY_NAME" ] = new { type = "String" , value = "testregistry" } ,
217+ [ "env_AZURE_CONTAINER_REGISTRY_ENDPOINT" ] = new { type = "String" , value = "testregistry.azurecr.io" } ,
218+ [ "env_AZURE_CONTAINER_REGISTRY_MANAGED_IDENTITY_ID" ] = new { type = "String" , value = "/subscriptions/test/resourceGroups/test-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/test-identity" } ,
219+ [ "env_AZURE_CONTAINER_APPS_ENVIRONMENT_DEFAULT_DOMAIN" ] = new { type = "String" , value = "test.westus.azurecontainerapps.io" } ,
220+ [ "env_AZURE_CONTAINER_APPS_ENVIRONMENT_ID" ] = new { type = "String" , value = "/subscriptions/test/resourceGroups/test-rg/providers/Microsoft.App/managedEnvironments/testenv" }
221+ } ;
222+ var armClientProvider = new TestArmClientProvider ( deploymentOutputs ) ;
223+ ConfigureTestServices ( builder , armClientProvider : armClientProvider , processRunner : mockProcessRunner ) ;
224+
225+ var containerAppEnv = builder . AddAzureContainerAppEnvironment ( "env" ) ;
226+ var azureEnv = builder . AddAzureEnvironment ( ) ;
227+ builder . AddDockerfile ( "api" , "api.Dockerfile" ) ;
228+
229+ // Act
230+ using var app = builder . Build ( ) ;
231+ await app . StartAsync ( ) ;
232+ await app . WaitForShutdownAsync ( ) ;
233+
234+ // Assert that container environment outputs are propagated to outputs because they are
235+ // hoisted up for the container resource
236+ Assert . Equal ( "testregistry" , containerAppEnv . Resource . Outputs [ "AZURE_CONTAINER_REGISTRY_NAME" ] ) ;
237+ Assert . Equal ( "testregistry.azurecr.io" , containerAppEnv . Resource . Outputs [ "AZURE_CONTAINER_REGISTRY_ENDPOINT" ] ) ;
238+ Assert . Equal ( "/subscriptions/test/resourceGroups/test-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/test-identity" , containerAppEnv . Resource . Outputs [ "AZURE_CONTAINER_REGISTRY_MANAGED_IDENTITY_ID" ] ) ;
239+ Assert . Equal ( "test.westus.azurecontainerapps.io" , containerAppEnv . Resource . Outputs [ "AZURE_CONTAINER_APPS_ENVIRONMENT_DEFAULT_DOMAIN" ] ) ;
240+ Assert . Equal ( "/subscriptions/test/resourceGroups/test-rg/providers/Microsoft.App/managedEnvironments/testenv" , containerAppEnv . Resource . Outputs [ "AZURE_CONTAINER_APPS_ENVIRONMENT_ID" ] ) ;
241+
242+ // Assert - Verify ACR login command was called since Dockerfile image needs to be pushed
243+ Assert . Contains ( mockProcessRunner . ExecutedCommands ,
244+ cmd => cmd . ExecutablePath . Contains ( "az" ) &&
245+ cmd . Arguments == "acr login --name testregistry" ) ;
246+
247+ // Assert - Verify Docker tag and push called for Dockerfile
248+ Assert . Contains ( mockProcessRunner . ExecutedCommands ,
249+ cmd => cmd . ExecutablePath == "docker" &&
250+ cmd . Arguments != null &&
251+ cmd . Arguments . StartsWith ( "tag api testregistry.azurecr.io/" ) ) ;
252+
253+ Assert . Contains ( mockProcessRunner . ExecutedCommands ,
254+ cmd => cmd . ExecutablePath == "docker" &&
255+ cmd . Arguments != null &&
256+ cmd . Arguments . StartsWith ( "push testregistry.azurecr.io/" ) ) ;
257+ }
258+
208259 [ Fact ]
209260 public async Task DeployAsync_WithProjectResource_Works ( )
210261 {
@@ -256,6 +307,95 @@ public async Task DeployAsync_WithProjectResource_Works()
256307 cmd . Arguments . StartsWith ( "push testregistry.azurecr.io/" ) ) ;
257308 }
258309
310+ [ Fact ]
311+ public async Task DeployAsync_WithMultipleComputeEnvironments_Works ( )
312+ {
313+ // Arrange
314+ var mockProcessRunner = new MockProcessRunner ( ) ;
315+ using var builder = TestDistributedApplicationBuilder . Create ( DistributedApplicationOperation . Publish , publisher : "default" , isDeploy : true ) ;
316+ var deploymentOutputs = new Dictionary < string , object >
317+ {
318+ [ "aca_env_AZURE_CONTAINER_REGISTRY_NAME" ] = new { type = "String" , value = "acaregistry" } ,
319+ [ "aca_env_AZURE_CONTAINER_REGISTRY_ENDPOINT" ] = new { type = "String" , value = "acaregistry.azurecr.io" } ,
320+ [ "aca_env_AZURE_CONTAINER_REGISTRY_MANAGED_IDENTITY_ID" ] = new { type = "String" , value = "/subscriptions/test/resourceGroups/test-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/aca-identity" } ,
321+ [ "aca_env_AZURE_CONTAINER_APPS_ENVIRONMENT_DEFAULT_DOMAIN" ] = new { type = "String" , value = "aca.westus.azurecontainerapps.io" } ,
322+ [ "aca_env_AZURE_CONTAINER_APPS_ENVIRONMENT_ID" ] = new { type = "String" , value = "/subscriptions/test/resourceGroups/test-rg/providers/Microsoft.App/managedEnvironments/acaenv" } ,
323+ [ "aas_env_AZURE_CONTAINER_REGISTRY_NAME" ] = new { type = "String" , value = "aasregistry" } ,
324+ [ "aas_env_AZURE_CONTAINER_REGISTRY_ENDPOINT" ] = new { type = "String" , value = "aasregistry.azurecr.io" } ,
325+ [ "aas_env_AZURE_CONTAINER_REGISTRY_MANAGED_IDENTITY_ID" ] = new { type = "String" , value = "/subscriptions/test/resourceGroups/test-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/aas-identity" } ,
326+ [ "aas_env_planId" ] = new { type = "String" , value = "/subscriptions/test/resourceGroups/test-rg/providers/Microsoft.Web/serverfarms/aasplan" } ,
327+ [ "aas_env_AZURE_CONTAINER_REGISTRY_MANAGED_IDENTITY_CLIENT_ID" ] = new { type = "String" , value = "aas-client-id" }
328+ } ;
329+ var armClientProvider = new TestArmClientProvider ( deploymentOutputs ) ;
330+ ConfigureTestServices ( builder , armClientProvider : armClientProvider , processRunner : mockProcessRunner ) ;
331+
332+ var acaEnv = builder . AddAzureContainerAppEnvironment ( "aca-env" ) ;
333+ var aasEnv = builder . AddAzureAppServiceEnvironment ( "aas-env" ) ;
334+ var azureEnv = builder . AddAzureEnvironment ( ) ;
335+
336+ var storage = builder . AddAzureStorage ( "storage" ) ;
337+ storage . AddBlobContainer ( "mycontainer1" , blobContainerName : "test-container-1" ) ;
338+ storage . AddBlobContainer ( "mycontainer2" , blobContainerName : "test-container-2" ) ;
339+ storage . AddQueue ( "myqueue" , queueName : "my-queue" ) ;
340+
341+ builder . AddRedis ( "cache" ) . WithComputeEnvironment ( acaEnv ) ;
342+ builder . AddProject < Project > ( "api-service" , launchProfileName : null ) . WithComputeEnvironment ( aasEnv ) ;
343+ builder . AddDockerfile ( "python-app" , "python-app.Dockerfile" ) . WithComputeEnvironment ( acaEnv ) ;
344+
345+ // Act
346+ using var app = builder . Build ( ) ;
347+ await app . StartAsync ( ) ;
348+ await app . WaitForShutdownAsync ( ) ;
349+
350+ // Assert ACA environment outputs are properly set
351+ Assert . Equal ( "acaregistry" , acaEnv . Resource . Outputs [ "AZURE_CONTAINER_REGISTRY_NAME" ] ) ;
352+ Assert . Equal ( "acaregistry.azurecr.io" , acaEnv . Resource . Outputs [ "AZURE_CONTAINER_REGISTRY_ENDPOINT" ] ) ;
353+ Assert . Equal ( "/subscriptions/test/resourceGroups/test-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/aca-identity" , acaEnv . Resource . Outputs [ "AZURE_CONTAINER_REGISTRY_MANAGED_IDENTITY_ID" ] ) ;
354+ Assert . Equal ( "aca.westus.azurecontainerapps.io" , acaEnv . Resource . Outputs [ "AZURE_CONTAINER_APPS_ENVIRONMENT_DEFAULT_DOMAIN" ] ) ;
355+ Assert . Equal ( "/subscriptions/test/resourceGroups/test-rg/providers/Microsoft.App/managedEnvironments/acaenv" , acaEnv . Resource . Outputs [ "AZURE_CONTAINER_APPS_ENVIRONMENT_ID" ] ) ;
356+
357+ // Assert AAS environment outputs are properly set
358+ Assert . Equal ( "aasregistry" , aasEnv . Resource . Outputs [ "AZURE_CONTAINER_REGISTRY_NAME" ] ) ;
359+ Assert . Equal ( "aasregistry.azurecr.io" , aasEnv . Resource . Outputs [ "AZURE_CONTAINER_REGISTRY_ENDPOINT" ] ) ;
360+ Assert . Equal ( "/subscriptions/test/resourceGroups/test-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/aas-identity" , aasEnv . Resource . Outputs [ "AZURE_CONTAINER_REGISTRY_MANAGED_IDENTITY_ID" ] ) ;
361+ Assert . Equal ( "/subscriptions/test/resourceGroups/test-rg/providers/Microsoft.Web/serverfarms/aasplan" , aasEnv . Resource . Outputs [ "planId" ] ) ;
362+ Assert . Equal ( "aas-client-id" , aasEnv . Resource . Outputs [ "AZURE_CONTAINER_REGISTRY_MANAGED_IDENTITY_CLIENT_ID" ] ) ;
363+
364+ // Assert ACR login commands were called for both registries
365+ Assert . Contains ( mockProcessRunner . ExecutedCommands ,
366+ cmd => cmd . ExecutablePath . Contains ( "az" ) &&
367+ cmd . Arguments == "acr login --name acaregistry" ) ;
368+ Assert . Contains ( mockProcessRunner . ExecutedCommands ,
369+ cmd => cmd . ExecutablePath . Contains ( "az" ) &&
370+ cmd . Arguments == "acr login --name aasregistry" ) ;
371+
372+ // Assert Docker operations for project resource deployed to AAS environment
373+ Assert . Contains ( mockProcessRunner . ExecutedCommands ,
374+ cmd => cmd . ExecutablePath == "docker" &&
375+ cmd . Arguments != null &&
376+ cmd . Arguments . StartsWith ( "tag api-service aasregistry.azurecr.io/" ) ) ;
377+ Assert . Contains ( mockProcessRunner . ExecutedCommands ,
378+ cmd => cmd . ExecutablePath == "docker" &&
379+ cmd . Arguments != null &&
380+ cmd . Arguments . StartsWith ( "push aasregistry.azurecr.io/" ) ) ;
381+
382+ // Assert Docker operations NOT performed for existing container image deployed to ACA environment
383+ Assert . DoesNotContain ( mockProcessRunner . ExecutedCommands ,
384+ cmd => cmd . ExecutablePath == "docker" &&
385+ cmd . Arguments != null &&
386+ cmd . Arguments . StartsWith ( "tag cache acaregistry.azurecr.io/" ) ) ;
387+
388+ // Assert Docker operations for project resource deployed to ACA environment
389+ Assert . Contains ( mockProcessRunner . ExecutedCommands ,
390+ cmd => cmd . ExecutablePath == "docker" &&
391+ cmd . Arguments != null &&
392+ cmd . Arguments . StartsWith ( "tag python-app acaregistry.azurecr.io/" ) ) ;
393+ Assert . Contains ( mockProcessRunner . ExecutedCommands ,
394+ cmd => cmd . ExecutablePath == "docker" &&
395+ cmd . Arguments != null &&
396+ cmd . Arguments . StartsWith ( "push acaregistry.azurecr.io/" ) ) ;
397+ }
398+
259399 private static void ConfigureTestServices ( IDistributedApplicationTestingBuilder builder ,
260400 IInteractionService ? interactionService = null ,
261401 IBicepProvisioner ? bicepProvisioner = null ,
0 commit comments