From a7509ab87744e9231cffee1f5ae0f46dac42e454 Mon Sep 17 00:00:00 2001 From: Xiaolu Dai Date: Thu, 2 Jan 2025 20:28:41 +0800 Subject: [PATCH 1/2] add uts for app_init, detect_confirm, infra_confirm, and scaffold --- cli/azd/internal/repository/app_init.go | 2 +- cli/azd/internal/repository/app_init_test.go | 612 ++++++++++++++++++ .../repository/detect_confirm_test.go | 190 ++++++ .../internal/repository/infra_confirm_test.go | 304 ++++++++- cli/azd/internal/scaffold/scaffold_test.go | 131 ++++ .../scaffold/templates/resources.bicept | 1 - 6 files changed, 1236 insertions(+), 4 deletions(-) diff --git a/cli/azd/internal/repository/app_init.go b/cli/azd/internal/repository/app_init.go index 3e4f69b427f..b7dee609289 100644 --- a/cli/azd/internal/repository/app_init.go +++ b/cli/azd/internal/repository/app_init.go @@ -917,7 +917,7 @@ func (i *Initializer) addMavenBuildHook( wrapperPathMap := map[string][]string{} for _, prj := range detect.Services { - if prj.Language == appdetect.Java { + if prj.Language == appdetect.Java && prj.Options != nil { if prj.Options[appdetect.JavaProjectOptionParentPomDir] != nil { parentPath := prj.Options[appdetect.JavaProjectOptionParentPomDir].(string) posixMavenWrapperPath := prj.Options[appdetect.JavaProjectOptionPosixMavenWrapperPath].(string) diff --git a/cli/azd/internal/repository/app_init_test.go b/cli/azd/internal/repository/app_init_test.go index 8669c6a09cd..ccd27e5aa44 100644 --- a/cli/azd/internal/repository/app_init_test.go +++ b/cli/azd/internal/repository/app_init_test.go @@ -127,6 +127,616 @@ func TestInitializer_prjConfigFromDetect(t *testing.T) { }, }, }, + { + name: "api with storage umi", + detect: detectConfirm{ + Services: []appdetect.Project{ + { + Language: appdetect.Java, + Path: "java", + AzureDeps: []appdetect.AzureDep{ + appdetect.AzureDepStorageAccount{ + ContainerNamePropertyMap: map[string]string{ + "spring.cloud.azure.container": "container1", + }, + }, + }, + }, + }, + AzureDeps: map[string]Pair{ + appdetect.AzureDepStorageAccount{}.ResourceDisplay(): { + appdetect.AzureDepStorageAccount{ + ContainerNamePropertyMap: map[string]string{ + "spring.cloud.azure.container": "container1", + }, + }, EntryKindDetected, + }, + }, + }, + interactions: []string{ + // prompt for auth type + "User assigned managed identity", + }, + want: project.ProjectConfig{ + Services: map[string]*project.ServiceConfig{ + "java": { + Language: project.ServiceLanguageJava, + Host: project.ContainerAppTarget, + RelativePath: "java", + }, + }, + Resources: map[string]*project.ResourceConfig{ + "java": { + Type: project.ResourceTypeHostContainerApp, + Name: "java", + Props: project.ContainerAppProps{ + Port: 8080, + }, + Uses: []string{"storage"}, + }, + "storage": { + Type: project.ResourceTypeStorage, + Props: project.StorageProps{ + Containers: []string{"container1"}, + AuthType: internal.AuthTypeUserAssignedManagedIdentity, + }, + }, + }, + }, + }, + { + name: "api with storage connection string", + detect: detectConfirm{ + Services: []appdetect.Project{ + { + Language: appdetect.Java, + Path: "java", + AzureDeps: []appdetect.AzureDep{ + appdetect.AzureDepStorageAccount{ + ContainerNamePropertyMap: map[string]string{ + "spring.cloud.azure.container": "container1", + }, + }, + }, + }, + }, + AzureDeps: map[string]Pair{ + appdetect.AzureDepStorageAccount{}.ResourceDisplay(): { + appdetect.AzureDepStorageAccount{ + ContainerNamePropertyMap: map[string]string{ + "spring.cloud.azure.container": "container1", + }, + }, EntryKindDetected, + }, + }, + }, + interactions: []string{ + // prompt for auth type + "Connection string", + }, + want: project.ProjectConfig{ + Services: map[string]*project.ServiceConfig{ + "java": { + Language: project.ServiceLanguageJava, + Host: project.ContainerAppTarget, + RelativePath: "java", + }, + }, + Resources: map[string]*project.ResourceConfig{ + "java": { + Type: project.ResourceTypeHostContainerApp, + Name: "java", + Props: project.ContainerAppProps{ + Port: 8080, + }, + Uses: []string{"storage"}, + }, + "storage": { + Type: project.ResourceTypeStorage, + Props: project.StorageProps{ + Containers: []string{"container1"}, + AuthType: internal.AuthTypeConnectionString, + }, + }, + }, + }, + }, + { + name: "api with service bus umi", + detect: detectConfirm{ + Services: []appdetect.Project{ + { + Language: appdetect.Java, + Path: "java", + AzureDeps: []appdetect.AzureDep{ + appdetect.AzureDepServiceBus{ + Queues: []string{"queue1"}, + IsJms: true, + }, + }, + }, + }, + AzureDeps: map[string]Pair{ + appdetect.AzureDepServiceBus{}.ResourceDisplay(): { + appdetect.AzureDepServiceBus{ + Queues: []string{"queue1"}, + IsJms: true, + }, EntryKindDetected, + }, + }, + }, + interactions: []string{ + // prompt for auth type + "User assigned managed identity", + }, + want: project.ProjectConfig{ + Services: map[string]*project.ServiceConfig{ + "java": { + Language: project.ServiceLanguageJava, + Host: project.ContainerAppTarget, + RelativePath: "java", + }, + }, + Resources: map[string]*project.ResourceConfig{ + "java": { + Type: project.ResourceTypeHostContainerApp, + Name: "java", + Props: project.ContainerAppProps{ + Port: 8080, + }, + Uses: []string{"servicebus"}, + }, + "servicebus": { + Type: project.ResourceTypeMessagingServiceBus, + Props: project.ServiceBusProps{ + Queues: []string{"queue1"}, + IsJms: true, + AuthType: internal.AuthTypeUserAssignedManagedIdentity, + }, + }, + }, + }, + }, + { + name: "api with service bus connection string", + detect: detectConfirm{ + Services: []appdetect.Project{ + { + Language: appdetect.Java, + Path: "java", + AzureDeps: []appdetect.AzureDep{ + appdetect.AzureDepServiceBus{ + Queues: []string{"queue1"}, + IsJms: true, + }, + }, + }, + }, + AzureDeps: map[string]Pair{ + appdetect.AzureDepServiceBus{}.ResourceDisplay(): { + appdetect.AzureDepServiceBus{ + Queues: []string{"queue1"}, + IsJms: true, + }, EntryKindDetected, + }, + }, + }, + interactions: []string{ + // prompt for auth type + "Connection string", + }, + want: project.ProjectConfig{ + Services: map[string]*project.ServiceConfig{ + "java": { + Language: project.ServiceLanguageJava, + Host: project.ContainerAppTarget, + RelativePath: "java", + }, + }, + Resources: map[string]*project.ResourceConfig{ + "java": { + Type: project.ResourceTypeHostContainerApp, + Name: "java", + Props: project.ContainerAppProps{ + Port: 8080, + }, + Uses: []string{"servicebus"}, + }, + "servicebus": { + Type: project.ResourceTypeMessagingServiceBus, + Props: project.ServiceBusProps{ + Queues: []string{"queue1"}, + IsJms: true, + AuthType: internal.AuthTypeConnectionString, + }, + }, + }, + }, + }, + { + name: "api with event hubs umi", + detect: detectConfirm{ + Services: []appdetect.Project{ + { + Language: appdetect.Java, + Path: "java", + AzureDeps: []appdetect.AzureDep{ + appdetect.AzureDepEventHubs{ + EventHubsNamePropertyMap: map[string]string{ + "spring.cloud.azure.eventhubs": "eventhub1", + }, + }, + }, + }, + }, + AzureDeps: map[string]Pair{ + appdetect.AzureDepEventHubs{}.ResourceDisplay(): { + appdetect.AzureDepEventHubs{ + EventHubsNamePropertyMap: map[string]string{ + "spring.cloud.azure.eventhubs": "eventhub1", + }, + }, EntryKindDetected, + }, + }, + }, + interactions: []string{ + // prompt for auth type + "User assigned managed identity", + }, + want: project.ProjectConfig{ + Services: map[string]*project.ServiceConfig{ + "java": { + Language: project.ServiceLanguageJava, + Host: project.ContainerAppTarget, + RelativePath: "java", + }, + }, + Resources: map[string]*project.ResourceConfig{ + "java": { + Type: project.ResourceTypeHostContainerApp, + Name: "java", + Props: project.ContainerAppProps{ + Port: 8080, + }, + Uses: []string{"eventhubs"}, + }, + "eventhubs": { + Type: project.ResourceTypeMessagingEventHubs, + Props: project.EventHubsProps{ + EventHubNames: []string{"eventhub1"}, + AuthType: internal.AuthTypeUserAssignedManagedIdentity, + }, + }, + }, + }, + }, + { + name: "api with event hubs connection string", + detect: detectConfirm{ + Services: []appdetect.Project{ + { + Language: appdetect.Java, + Path: "java", + AzureDeps: []appdetect.AzureDep{ + appdetect.AzureDepEventHubs{ + EventHubsNamePropertyMap: map[string]string{ + "spring.cloud.azure.eventhubs": "eventhub1", + }, + }, + }, + }, + }, + AzureDeps: map[string]Pair{ + appdetect.AzureDepEventHubs{}.ResourceDisplay(): { + appdetect.AzureDepEventHubs{ + EventHubsNamePropertyMap: map[string]string{ + "spring.cloud.azure.eventhubs": "eventhub1", + }, + }, EntryKindDetected, + }, + }, + }, + interactions: []string{ + // prompt for auth type + "Connection string", + }, + want: project.ProjectConfig{ + Services: map[string]*project.ServiceConfig{ + "java": { + Language: project.ServiceLanguageJava, + Host: project.ContainerAppTarget, + RelativePath: "java", + }, + }, + Resources: map[string]*project.ResourceConfig{ + "java": { + Type: project.ResourceTypeHostContainerApp, + Name: "java", + Props: project.ContainerAppProps{ + Port: 8080, + }, + Uses: []string{"eventhubs"}, + }, + "eventhubs": { + Type: project.ResourceTypeMessagingEventHubs, + Props: project.EventHubsProps{ + EventHubNames: []string{"eventhub1"}, + AuthType: internal.AuthTypeConnectionString, + }, + }, + }, + }, + }, + { + name: "api with event hubs kafka umi", + detect: detectConfirm{ + Services: []appdetect.Project{ + { + Language: appdetect.Java, + Path: "java", + AzureDeps: []appdetect.AzureDep{ + appdetect.AzureDepEventHubs{ + EventHubsNamePropertyMap: map[string]string{ + "spring.kafka.topic": "topic1", + }, + DependencyTypes: []appdetect.DependencyType{appdetect.SpringKafka}, + SpringBootVersion: "3.4.0", + }, + }, + }, + }, + AzureDeps: map[string]Pair{ + appdetect.AzureDepEventHubs{}.ResourceDisplay(): { + appdetect.AzureDepEventHubs{ + EventHubsNamePropertyMap: map[string]string{ + "spring.kafka.topic": "topic1", + }, + DependencyTypes: []appdetect.DependencyType{appdetect.SpringKafka}, + SpringBootVersion: "3.4.0", + }, EntryKindDetected, + }, + }, + }, + interactions: []string{ + // prompt for auth type + "User assigned managed identity", + }, + want: project.ProjectConfig{ + Services: map[string]*project.ServiceConfig{ + "java": { + Language: project.ServiceLanguageJava, + Host: project.ContainerAppTarget, + RelativePath: "java", + }, + }, + Resources: map[string]*project.ResourceConfig{ + "java": { + Type: project.ResourceTypeHostContainerApp, + Name: "java", + Props: project.ContainerAppProps{ + Port: 8080, + }, + Uses: []string{"kafka"}, + }, + "kafka": { + Type: project.ResourceTypeMessagingKafka, + Props: project.KafkaProps{ + Topics: []string{"topic1"}, + AuthType: internal.AuthTypeUserAssignedManagedIdentity, + SpringBootVersion: "3.4.0", + }, + }, + }, + }, + }, + { + name: "api with event hubs kafka connection string", + detect: detectConfirm{ + Services: []appdetect.Project{ + { + Language: appdetect.Java, + Path: "java", + AzureDeps: []appdetect.AzureDep{ + appdetect.AzureDepEventHubs{ + EventHubsNamePropertyMap: map[string]string{ + "spring.kafka.topic": "topic1", + }, + DependencyTypes: []appdetect.DependencyType{appdetect.SpringKafka}, + SpringBootVersion: "3.4.0", + }, + }, + }, + }, + AzureDeps: map[string]Pair{ + appdetect.AzureDepEventHubs{}.ResourceDisplay(): { + appdetect.AzureDepEventHubs{ + EventHubsNamePropertyMap: map[string]string{ + "spring.kafka.topic": "topic1", + }, + DependencyTypes: []appdetect.DependencyType{appdetect.SpringKafka}, + SpringBootVersion: "3.4.0", + }, EntryKindDetected, + }, + }, + }, + interactions: []string{ + // prompt for auth type + "Connection string", + }, + want: project.ProjectConfig{ + Services: map[string]*project.ServiceConfig{ + "java": { + Language: project.ServiceLanguageJava, + Host: project.ContainerAppTarget, + RelativePath: "java", + }, + }, + Resources: map[string]*project.ResourceConfig{ + "java": { + Type: project.ResourceTypeHostContainerApp, + Name: "java", + Props: project.ContainerAppProps{ + Port: 8080, + }, + Uses: []string{"kafka"}, + }, + "kafka": { + Type: project.ResourceTypeMessagingKafka, + Props: project.KafkaProps{ + Topics: []string{"topic1"}, + AuthType: internal.AuthTypeConnectionString, + SpringBootVersion: "3.4.0", + }, + }, + }, + }, + }, + { + name: "api with cosmos db", + detect: detectConfirm{ + Services: []appdetect.Project{ + { + Language: appdetect.Java, + Path: "java", + DatabaseDeps: []appdetect.DatabaseDep{ + appdetect.DbCosmos, + }, + }, + }, + Databases: map[appdetect.DatabaseDep]EntryKind{ + appdetect.DbCosmos: EntryKindDetected, + }, + }, + interactions: []string{ + "cosmosdbname", + }, + want: project.ProjectConfig{ + Services: map[string]*project.ServiceConfig{ + "java": { + Language: project.ServiceLanguageJava, + Host: project.ContainerAppTarget, + RelativePath: "java", + }, + }, + Resources: map[string]*project.ResourceConfig{ + "java": { + Type: project.ResourceTypeHostContainerApp, + Name: "java", + Props: project.ContainerAppProps{ + Port: 8080, + }, + Uses: []string{"cosmos"}, + }, + "cosmos": { + Name: "cosmos", + Type: project.ResourceTypeDbCosmos, + Props: project.CosmosDBProps{ + DatabaseName: "cosmosdbname", + }, + }, + }, + }, + }, + { + name: "api with postgresql", + detect: detectConfirm{ + Services: []appdetect.Project{ + { + Language: appdetect.Java, + Path: "java", + DatabaseDeps: []appdetect.DatabaseDep{ + appdetect.DbPostgres, + }, + }, + }, + Databases: map[appdetect.DatabaseDep]EntryKind{ + appdetect.DbPostgres: EntryKindDetected, + }, + }, + interactions: []string{ + "postgresql-db", + // prompt for auth type + // todo cannot use umi here for it will check the source code + "Username and password", + }, + want: project.ProjectConfig{ + Services: map[string]*project.ServiceConfig{ + "java": { + Language: project.ServiceLanguageJava, + Host: project.ContainerAppTarget, + RelativePath: "java", + }, + }, + Resources: map[string]*project.ResourceConfig{ + "java": { + Type: project.ResourceTypeHostContainerApp, + Name: "java", + Props: project.ContainerAppProps{ + Port: 8080, + }, + Uses: []string{"postgresql"}, + }, + "postgresql": { + Type: project.ResourceTypeDbPostgres, + Name: "postgresql", + Props: project.PostgresProps{ + DatabaseName: "postgresql-db", + AuthType: internal.AuthTypePassword, + }, + }, + }, + }, + }, + { + name: "api with mysql", + detect: detectConfirm{ + Services: []appdetect.Project{ + { + Language: appdetect.Java, + Path: "java", + DatabaseDeps: []appdetect.DatabaseDep{ + appdetect.DbMySql, + }, + }, + }, + Databases: map[appdetect.DatabaseDep]EntryKind{ + appdetect.DbMySql: EntryKindDetected, + }, + }, + interactions: []string{ + "mysql-db", + // prompt for auth type + // todo cannot use umi here for it will check the source code + "Username and password", + }, + want: project.ProjectConfig{ + Services: map[string]*project.ServiceConfig{ + "java": { + Language: project.ServiceLanguageJava, + Host: project.ContainerAppTarget, + RelativePath: "java", + }, + }, + Resources: map[string]*project.ResourceConfig{ + "java": { + Type: project.ResourceTypeHostContainerApp, + Name: "java", + Props: project.ContainerAppProps{ + Port: 8080, + }, + Uses: []string{"mysql"}, + }, + "mysql": { + Type: project.ResourceTypeDbMySQL, + Name: "mysql", + Props: project.MySQLProps{ + DatabaseName: "mysql-db", + AuthType: internal.AuthTypePassword, + }, + }, + }, + }, + }, { name: "api and web", detect: detectConfirm{ @@ -313,6 +923,8 @@ func TestInitializer_prjConfigFromDetect(t *testing.T) { } } + tt.detect.root = dir + spec, err := i.prjConfigFromDetect( context.Background(), dir, diff --git a/cli/azd/internal/repository/detect_confirm_test.go b/cli/azd/internal/repository/detect_confirm_test.go index 6a0a43be6ad..a46a6e8c054 100644 --- a/cli/azd/internal/repository/detect_confirm_test.go +++ b/cli/azd/internal/repository/detect_confirm_test.go @@ -75,6 +75,196 @@ func Test_detectConfirm_confirm(t *testing.T) { }, }, }, + { + name: "confirm single with storage resource", + detection: []appdetect.Project{ + { + Language: appdetect.Java, + Path: javaDir, + AzureDeps: []appdetect.AzureDep{ + appdetect.AzureDepStorageAccount{ + ContainerNamePropertyMap: map[string]string{ + "spring.cloud.azure.container": "container1", + }, + }, + }, + }, + }, + interactions: []string{ + "Confirm and continue initializing my app", + }, + want: []appdetect.Project{ + { + Language: appdetect.Java, + Path: javaDir, + AzureDeps: []appdetect.AzureDep{ + appdetect.AzureDepStorageAccount{ + ContainerNamePropertyMap: map[string]string{ + "spring.cloud.azure.container": "container1", + }, + }, + }, + }, + }, + }, + { + name: "confirm single with resources service bus", + detection: []appdetect.Project{ + { + Language: appdetect.Java, + Path: javaDir, + AzureDeps: []appdetect.AzureDep{ + appdetect.AzureDepServiceBus{ + Queues: []string{"queue1"}, + IsJms: true, + }, + }, + }, + }, + interactions: []string{ + "Confirm and continue initializing my app", + }, + want: []appdetect.Project{ + { + Language: appdetect.Java, + Path: javaDir, + AzureDeps: []appdetect.AzureDep{ + appdetect.AzureDepServiceBus{ + Queues: []string{"queue1"}, + IsJms: true, + }, + }, + }, + }, + }, + { + name: "confirm single with event hubs resource", + detection: []appdetect.Project{ + { + Language: appdetect.Java, + Path: javaDir, + AzureDeps: []appdetect.AzureDep{ + appdetect.AzureDepEventHubs{ + EventHubsNamePropertyMap: map[string]string{ + "spring.cloud.azure.eventhubs": "eventhub1", + }, + }, + }, + }, + }, + interactions: []string{ + "Confirm and continue initializing my app", + }, + want: []appdetect.Project{ + { + Language: appdetect.Java, + Path: javaDir, + AzureDeps: []appdetect.AzureDep{ + appdetect.AzureDepEventHubs{ + EventHubsNamePropertyMap: map[string]string{ + "spring.cloud.azure.eventhubs": "eventhub1", + }, + }, + }, + }, + }, + }, + { + name: "confirm single with cosmos db resource", + detection: []appdetect.Project{ + { + Language: appdetect.Java, + Path: javaDir, + DatabaseDeps: []appdetect.DatabaseDep{ + appdetect.DbCosmos, + }, + }, + }, + interactions: []string{ + "Confirm and continue initializing my app", + }, + want: []appdetect.Project{ + { + Language: appdetect.Java, + Path: javaDir, + DatabaseDeps: []appdetect.DatabaseDep{ + appdetect.DbCosmos, + }, + }, + }, + }, + { + name: "confirm single with postgresql resource", + detection: []appdetect.Project{ + { + Language: appdetect.Java, + Path: javaDir, + DatabaseDeps: []appdetect.DatabaseDep{ + appdetect.DbPostgres, + }, + }, + }, + interactions: []string{ + "Confirm and continue initializing my app", + }, + want: []appdetect.Project{ + { + Language: appdetect.Java, + Path: javaDir, + DatabaseDeps: []appdetect.DatabaseDep{ + appdetect.DbPostgres, + }, + }, + }, + }, + { + name: "confirm single with mysql resource", + detection: []appdetect.Project{ + { + Language: appdetect.Java, + Path: javaDir, + DatabaseDeps: []appdetect.DatabaseDep{ + appdetect.DbMySql, + }, + }, + }, + interactions: []string{ + "Confirm and continue initializing my app", + }, + want: []appdetect.Project{ + { + Language: appdetect.Java, + Path: javaDir, + DatabaseDeps: []appdetect.DatabaseDep{ + appdetect.DbMySql, + }, + }, + }, + }, + { + name: "confirm single with cosmos db mongo resource", + detection: []appdetect.Project{ + { + Language: appdetect.Java, + Path: javaDir, + DatabaseDeps: []appdetect.DatabaseDep{ + appdetect.DbMongo, + }, + }, + }, + interactions: []string{ + "Confirm and continue initializing my app", + }, + want: []appdetect.Project{ + { + Language: appdetect.Java, + Path: javaDir, + DatabaseDeps: []appdetect.DatabaseDep{ + appdetect.DbMongo, + }, + }, + }, + }, { name: "add a language", detection: []appdetect.Project{ diff --git a/cli/azd/internal/repository/infra_confirm_test.go b/cli/azd/internal/repository/infra_confirm_test.go index aeb7f2d89a5..0f854a09b16 100644 --- a/cli/azd/internal/repository/infra_confirm_test.go +++ b/cli/azd/internal/repository/infra_confirm_test.go @@ -3,6 +3,7 @@ package repository import ( "context" "fmt" + "github.com/azure/azure-dev/cli/azd/internal" "os" "path/filepath" "strings" @@ -22,7 +23,32 @@ func TestInitializer_infraSpecFromDetect(t *testing.T) { DatabaseName: "myappdb", AuthType: "userAssignedManagedIdentity", } - envs, _ := scaffold.GetServiceBindingEnvsForPostgres(*dbPostgres) + envsForPostgres, _ := scaffold.GetServiceBindingEnvsForPostgres(*dbPostgres) + scaffoldStorageAccount := scaffold.AzureDepStorageAccount{ + ContainerNames: []string{"container1"}, + AuthType: internal.AuthTypeConnectionString, + } + envsForStorage, _ := scaffold.GetServiceBindingEnvsForStorageAccount(scaffoldStorageAccount) + envsForMongo := scaffold.GetServiceBindingEnvsForMongo() + scaffoldServiceBus := scaffold.AzureDepServiceBus{ + Queues: []string{"queue1"}, + IsJms: true, + AuthType: internal.AuthTypeConnectionString, + } + envsForServiceBus, _ := scaffold.GetServiceBindingEnvsForServiceBus(scaffoldServiceBus) + scaffoldEventHubs := scaffold.AzureDepEventHubs{ + EventHubNames: []string{"eventhub1"}, + AuthType: internal.AuthTypeConnectionString, + UseKafka: true, + } + envsForEventHubs, _ := scaffold.GetServiceBindingEnvsForEventHubs(scaffoldEventHubs) + envsForCosmos := scaffold.GetServiceBindingEnvsForCosmos() + scaffoldMysql := scaffold.DatabaseMySql{ + DatabaseName: "mysql-db", + AuthType: internal.AuthTypePassword, + } + envsForMysql, _ := scaffold.GetServiceBindingEnvsForMysql(scaffoldMysql) + envsForCosmosMongo := scaffold.GetServiceBindingEnvsForMongo() tests := []struct { name string detect detectConfirm @@ -147,6 +173,277 @@ func TestInitializer_infraSpecFromDetect(t *testing.T) { }, }, }, + { + name: "api with storage", + detect: detectConfirm{ + Services: []appdetect.Project{ + { + Language: appdetect.Java, + Path: "java", + AzureDeps: []appdetect.AzureDep{ + appdetect.AzureDepStorageAccount{ + ContainerNamePropertyMap: map[string]string{ + "spring.cloud.azure.container": "container1", + }, + }, + }, + }, + }, + AzureDeps: map[string]Pair{ + "storage": { + first: appdetect.AzureDepStorageAccount{ + ContainerNamePropertyMap: map[string]string{ + "spring.cloud.azure.container": "container1", + }, + }, + second: EntryKindDetected, + }, + }, + }, + interactions: []string{ + "Connection string", + }, + want: scaffold.InfraSpec{ + Services: []scaffold.ServiceSpec{ + { + Name: "java", + Port: 8080, + Backend: &scaffold.Backend{}, + AzureStorageAccount: &scaffoldStorageAccount, + Envs: envsForStorage, + }, + }, + AzureStorageAccount: &scaffoldStorageAccount, + }, + }, + { + name: "api with mongo", + detect: detectConfirm{ + Services: []appdetect.Project{ + { + Language: appdetect.Java, + Path: "java", + DatabaseDeps: []appdetect.DatabaseDep{ + appdetect.DbMongo, + }, + }, + }, + Databases: map[appdetect.DatabaseDep]EntryKind{ + appdetect.DbMongo: EntryKindDetected, + }, + }, + interactions: []string{ + "mongodb-name", + }, + want: scaffold.InfraSpec{ + Services: []scaffold.ServiceSpec{ + { + Name: "java", + Port: 8080, + Backend: &scaffold.Backend{}, + DbCosmosMongo: &scaffold.DatabaseCosmosMongo{ + DatabaseName: "mongodb-name", + }, + Envs: envsForMongo, + }, + }, + DbCosmosMongo: &scaffold.DatabaseCosmosMongo{ + DatabaseName: "mongodb-name", + }, + }, + }, + { + name: "api with service bus", + detect: detectConfirm{ + Services: []appdetect.Project{ + { + Language: appdetect.Java, + Path: "java", + AzureDeps: []appdetect.AzureDep{ + appdetect.AzureDepServiceBus{ + Queues: []string{"queue1"}, + IsJms: true, + }, + }, + }, + }, + AzureDeps: map[string]Pair{ + "storage": { + first: appdetect.AzureDepServiceBus{ + Queues: []string{"queue1"}, + IsJms: true, + }, + second: EntryKindDetected, + }, + }, + }, + interactions: []string{ + "Connection string", + }, + want: scaffold.InfraSpec{ + Services: []scaffold.ServiceSpec{ + { + Name: "java", + Port: 8080, + Backend: &scaffold.Backend{}, + AzureServiceBus: &scaffoldServiceBus, + Envs: envsForServiceBus, + }, + }, + AzureServiceBus: &scaffoldServiceBus, + }, + }, + { + name: "api with event hubs", + detect: detectConfirm{ + Services: []appdetect.Project{ + { + Language: appdetect.Java, + Path: "java", + AzureDeps: []appdetect.AzureDep{ + appdetect.AzureDepEventHubs{ + EventHubsNamePropertyMap: map[string]string{ + "spring.cloud.azure.kafka": "eventhub1", + }, + DependencyTypes: []appdetect.DependencyType{appdetect.SpringKafka}, + }, + }, + }, + }, + AzureDeps: map[string]Pair{ + "eventhubs": { + first: appdetect.AzureDepEventHubs{ + EventHubsNamePropertyMap: map[string]string{ + "spring.cloud.azure.kafka": "eventhub1", + }, + DependencyTypes: []appdetect.DependencyType{appdetect.SpringKafka}, + }, + second: EntryKindDetected, + }, + }, + }, + interactions: []string{ + "Connection string", + }, + want: scaffold.InfraSpec{ + Services: []scaffold.ServiceSpec{ + { + Name: "java", + Port: 8080, + Backend: &scaffold.Backend{}, + AzureEventHubs: &scaffoldEventHubs, + Envs: envsForEventHubs, + }, + }, + AzureEventHubs: &scaffoldEventHubs, + }, + }, + { + name: "api with cosmos db", + detect: detectConfirm{ + Services: []appdetect.Project{ + { + Language: appdetect.Java, + Path: "java", + DatabaseDeps: []appdetect.DatabaseDep{ + appdetect.DbCosmos, + }, + }, + }, + Databases: map[appdetect.DatabaseDep]EntryKind{ + appdetect.DbCosmos: EntryKindDetected, + }, + }, + interactions: []string{ + "cosmos-db-name", + }, + want: scaffold.InfraSpec{ + Services: []scaffold.ServiceSpec{ + { + Name: "java", + Port: 8080, + Backend: &scaffold.Backend{}, + DbCosmos: &scaffold.DatabaseCosmosAccount{ + DatabaseName: "cosmos-db-name", + }, + Envs: envsForCosmos, + }, + }, + DbCosmos: &scaffold.DatabaseCosmosAccount{ + DatabaseName: "cosmos-db-name", + }, + }, + }, + { + name: "api with mysql", + detect: detectConfirm{ + Services: []appdetect.Project{ + { + Language: appdetect.Java, + Path: "java", + DatabaseDeps: []appdetect.DatabaseDep{ + appdetect.DbMySql, + }, + }, + }, + Databases: map[appdetect.DatabaseDep]EntryKind{ + appdetect.DbMySql: EntryKindDetected, + }, + }, + interactions: []string{ + // prompt for dbname + "mysql-db", + "Username and password", + }, + want: scaffold.InfraSpec{ + Services: []scaffold.ServiceSpec{ + { + Name: "java", + Port: 8080, + Backend: &scaffold.Backend{}, + DbMySql: &scaffoldMysql, + Envs: envsForMysql, + }, + }, + DbMySql: &scaffoldMysql, + }, + }, + { + name: "api with cosmos db mongo", + detect: detectConfirm{ + Services: []appdetect.Project{ + { + Language: appdetect.Java, + Path: "java", + DatabaseDeps: []appdetect.DatabaseDep{ + appdetect.DbMongo, + }, + }, + }, + Databases: map[appdetect.DatabaseDep]EntryKind{ + appdetect.DbMongo: EntryKindDetected, + }, + }, + interactions: []string{ + "cosmos-db-mongo-name", + }, + want: scaffold.InfraSpec{ + Services: []scaffold.ServiceSpec{ + { + Name: "java", + Port: 8080, + Backend: &scaffold.Backend{}, + DbCosmosMongo: &scaffold.DatabaseCosmosMongo{ + DatabaseName: "cosmos-db-mongo-name", + }, + Envs: envsForCosmosMongo, + }, + }, + DbCosmosMongo: &scaffold.DatabaseCosmosMongo{ + DatabaseName: "cosmos-db-mongo-name", + }, + }, + }, { name: "api and web with db", detect: detectConfirm{ @@ -195,7 +492,7 @@ func TestInitializer_infraSpecFromDetect(t *testing.T) { }, }, DbPostgres: dbPostgres, - Envs: envs, + Envs: envsForPostgres, }, { Name: "js", @@ -228,6 +525,9 @@ func TestInitializer_infraSpecFromDetect(t *testing.T) { nil), } + dir := t.TempDir() + tt.detect.root = dir + spec, err := i.infraSpecFromDetect(context.Background(), &tt.detect) // Print extra newline to avoid mangling `go test -v` final test result output while waiting for final stdin, diff --git a/cli/azd/internal/scaffold/scaffold_test.go b/cli/azd/internal/scaffold/scaffold_test.go index d5a7dc212fb..11315be2253 100644 --- a/cli/azd/internal/scaffold/scaffold_test.go +++ b/cli/azd/internal/scaffold/scaffold_test.go @@ -2,6 +2,7 @@ package scaffold import ( "context" + "github.com/azure/azure-dev/cli/azd/internal" "os" "path/filepath" "strings" @@ -168,6 +169,136 @@ func TestExecInfra(t *testing.T) { }, }, }, + { + "API with Storage Account", + InfraSpec{ + AzureStorageAccount: &AzureDepStorageAccount{ + ContainerNames: []string{"container1"}, + }, + Services: []ServiceSpec{ + { + Name: "api", + Port: 3100, + AzureStorageAccount: &AzureDepStorageAccount{}, + }, + }, + }, + }, + { + "API with Service Bus", + InfraSpec{ + AzureServiceBus: &AzureDepServiceBus{ + Queues: []string{"queue1"}, + AuthType: internal.AuthTypeUserAssignedManagedIdentity, + IsJms: true, + }, + Services: []ServiceSpec{ + { + Name: "api", + Port: 3100, + AzureServiceBus: &AzureDepServiceBus{ + Queues: []string{"queue1"}, + AuthType: internal.AuthTypeUserAssignedManagedIdentity, + IsJms: true, + }, + }, + }, + }, + }, + { + "API with Event Hubs", + InfraSpec{ + AzureEventHubs: &AzureDepEventHubs{ + EventHubNames: []string{"eventhub1"}, + AuthType: internal.AuthTypeUserAssignedManagedIdentity, + UseKafka: true, + SpringBootVersion: "3.4.0", + }, + Services: []ServiceSpec{ + { + Name: "api", + Port: 3100, + AzureEventHubs: &AzureDepEventHubs{ + EventHubNames: []string{"eventhub1"}, + AuthType: internal.AuthTypeUserAssignedManagedIdentity, + UseKafka: true, + SpringBootVersion: "3.4.0", + }, + }, + }, + }, + }, + { + "API with Cosmos DB", + InfraSpec{ + DbCosmos: &DatabaseCosmosAccount{ + DatabaseName: "cosmos-db", + Containers: []CosmosSqlDatabaseContainer{ + { + ContainerName: "container1", + PartitionKeyPaths: []string{"/partitionKey"}, + }, + }, + }, + Services: []ServiceSpec{ + { + Name: "api", + Port: 3100, + DbCosmos: &DatabaseCosmosAccount{ + DatabaseName: "cosmos-db", + Containers: []CosmosSqlDatabaseContainer{ + { + ContainerName: "container1", + PartitionKeyPaths: []string{"/partitionKey"}, + }, + }, + }, + }, + }, + }, + }, + { + "API with MySQL password", + InfraSpec{ + DbMySql: &DatabaseMySql{ + DatabaseName: "appdb", + DatabaseUser: "appuser", + AuthType: internal.AuthTypePassword, + }, + Services: []ServiceSpec{ + { + Name: "api", + Port: 3100, + DbMySql: &DatabaseMySql{ + DatabaseName: "appdb", + DatabaseUser: "appuser", + AuthType: internal.AuthTypePassword, + }, + }, + }, + }, + }, + { + "API with MySQL umi", + InfraSpec{ + DbMySql: &DatabaseMySql{ + DatabaseName: "appdb", + DatabaseUser: "appuser", + AuthType: internal.AuthTypeUserAssignedManagedIdentity, + }, + Services: []ServiceSpec{ + { + Name: "api", + Port: 3100, + DbMySql: &DatabaseMySql{ + DatabaseName: "appdb", + DatabaseUser: "appuser", + AuthType: internal.AuthTypeUserAssignedManagedIdentity, + }, + }, + }, + }, + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { diff --git a/cli/azd/resources/scaffold/templates/resources.bicept b/cli/azd/resources/scaffold/templates/resources.bicept index 7cc9db9d53d..62e27c438f0 100644 --- a/cli/azd/resources/scaffold/templates/resources.bicept +++ b/cli/azd/resources/scaffold/templates/resources.bicept @@ -108,7 +108,6 @@ module cosmos 'br/public:avm/res/document-db/database-account:0.8.1' = { } {{- end}} {{- if .DbCosmos }} -var cosmosDatabaseName = '{{ .DbCosmos.DatabaseName }}' module cosmos 'br/public:avm/res/document-db/database-account:0.8.1' = { name: 'cosmos' params: { From bda09b9ce1783a34995904b5ad48276a6634b766 Mon Sep 17 00:00:00 2001 From: Xiaolu Dai Date: Mon, 13 Jan 2025 10:57:59 +0800 Subject: [PATCH 2/2] address comments --- cli/azd/resources/scaffold/templates/resources.bicept | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cli/azd/resources/scaffold/templates/resources.bicept b/cli/azd/resources/scaffold/templates/resources.bicept index 62e27c438f0..60f3b920620 100644 --- a/cli/azd/resources/scaffold/templates/resources.bicept +++ b/cli/azd/resources/scaffold/templates/resources.bicept @@ -108,6 +108,7 @@ module cosmos 'br/public:avm/res/document-db/database-account:0.8.1' = { } {{- end}} {{- if .DbCosmos }} +var cosmosDatabaseName = '{{ .DbCosmos.DatabaseName }}' module cosmos 'br/public:avm/res/document-db/database-account:0.8.1' = { name: 'cosmos' params: { @@ -128,7 +129,7 @@ module cosmos 'br/public:avm/res/document-db/database-account:0.8.1' = { } sqlDatabases: [ { - name: '{{ .DbCosmos.DatabaseName }}' + name: cosmosDatabaseName containers: [ {{- range .DbCosmos.Containers}} {