diff --git a/cli/azd/internal/appdetect/appdetect.go b/cli/azd/internal/appdetect/appdetect.go index e6103d3cbd7..38ea64830cf 100644 --- a/cli/azd/internal/appdetect/appdetect.go +++ b/cli/azd/internal/appdetect/appdetect.go @@ -102,6 +102,20 @@ func (f Dependency) IsWebUIFramework() bool { return false } +type RawDependency struct { + Kind RawDependencyKind + Name string + Version string +} + +type RawDependencyKind string + +const RawDependencyKindMaven RawDependencyKind = "maven" +const MavenDependencyNameMySqlConnectorJ = "com.mysql:mysql-connector-j" +const MavenDependencyNamePostgresql = "org.postgresql:postgresql" +const MavenDependencyNameSpringCloudAzureStarterJdbcPostgresql = "com.azure.spring:spring-cloud-azure-starter-jdbc-postgresql" +const MavenDependencyNameSpringBootMavenPlugin = "org.springframework.boot:spring-boot-maven-plugin" + // A type of database that is inferred through heuristics while scanning project information. type DatabaseDep string @@ -138,6 +152,9 @@ type Project struct { // Dependencies scanned in the project. Dependencies []Dependency + // RawDependencies scanned in the project. + RawDependencies []RawDependency + // Experimental: Database dependencies inferred through heuristics while scanning dependencies in the project. DatabaseDeps []DatabaseDep diff --git a/cli/azd/internal/appdetect/java.go b/cli/azd/internal/appdetect/java.go index 2bff79ce642..53b3bcdbbea 100644 --- a/cli/azd/internal/appdetect/java.go +++ b/cli/azd/internal/appdetect/java.go @@ -123,27 +123,35 @@ func readMavenProject(filePath string) (*mavenProject, error) { func detectDependencies(mavenProject *mavenProject, project *Project) (*Project, error) { databaseDepMap := map[DatabaseDep]struct{}{} for _, dep := range mavenProject.Dependencies { - if dep.GroupId == "com.mysql" && dep.ArtifactId == "mysql-connector-j" { + name := toDependencyName(dep.GroupId, dep.ArtifactId) + switch name { + case MavenDependencyNameMySqlConnectorJ: databaseDepMap[DbMySql] = struct{}{} - } - - if (dep.GroupId == "org.postgresql" && dep.ArtifactId == "postgresql") || - (dep.GroupId == "com.azure.spring" && dep.ArtifactId == "spring-cloud-azure-starter-jdbc-postgresql") { + project.RawDependencies = append(project.RawDependencies, + RawDependency{RawDependencyKindMaven, name, dep.Version}) + case MavenDependencyNamePostgresql, MavenDependencyNameSpringCloudAzureStarterJdbcPostgresql: databaseDepMap[DbPostgres] = struct{}{} - // todo: Only call this func when it's a spring-boot project - err := addPostgresqlConnectionProperties(project.Path) - if err != nil { - return nil, err - } + project.RawDependencies = append(project.RawDependencies, + RawDependency{RawDependencyKindMaven, name, dep.Version}) } } - if len(databaseDepMap) > 0 { project.DatabaseDeps = slices.SortedFunc(maps.Keys(databaseDepMap), func(a, b DatabaseDep) int { return strings.Compare(string(a), string(b)) }) } + for _, dep := range mavenProject.Build.Plugins { + name := toDependencyName(dep.GroupId, dep.ArtifactId) + if name == MavenDependencyNameSpringBootMavenPlugin { + project.RawDependencies = append(project.RawDependencies, + RawDependency{RawDependencyKindMaven, name, dep.Version}) + } + } return project, nil } + +func toDependencyName(groupId string, artifactId string) string { + return groupId + ":" + artifactId +} diff --git a/cli/azd/internal/appdetect/spring_boot_properties_file.go b/cli/azd/internal/appdetect/spring_boot_properties_file.go deleted file mode 100644 index 1a260240755..00000000000 --- a/cli/azd/internal/appdetect/spring_boot_properties_file.go +++ /dev/null @@ -1,187 +0,0 @@ -package appdetect - -import ( - "bufio" - "fmt" - "os" - "path/filepath" - "strings" - - "github.com/azure/azure-dev/cli/azd/internal" -) - -type property struct { - key string - value string -} -type propertyMergeFunc func(string, string) string - -const azureProfileName = "azure" - -var placeholderPostgresJdbcUrl = "jdbc:postgresql://" + internal.ToEnvPlaceHolder(internal.EnvNamePostgresHost) + - ":" + internal.ToEnvPlaceHolder(internal.EnvNamePostgresPort) + - "/" + internal.ToEnvPlaceHolder(internal.EnvNamePostgresDatabase) - -var postgresqlProperties = []property{ - {"spring.datasource.url", placeholderPostgresJdbcUrl}, - {"spring.datasource.username", internal.ToEnvPlaceHolder(internal.EnvNamePostgresUsername)}, - {"spring.datasource.password", internal.ToEnvPlaceHolder(internal.EnvNamePostgresPassword)}, -} - -var applicationPropertiesRelativePath = filepath.Join("src", "main", "resources", - "application.properties") -var applicationAzurePropertiesRelativePath = filepath.Join("src", "main", "resources", - "application-"+azureProfileName+".properties") - -// todo: support other file suffix. Example: application.yml, application.yaml -func addPostgresqlConnectionProperties(projectPath string) error { - err := addPostgresqlConnectionPropertiesIntoPropertyFile(projectPath) - if err != nil { - return err - } - return activeAzureProfile(projectPath) -} - -func addPostgresqlConnectionPropertiesIntoPropertyFile(projectPath string) error { - filePath := filepath.Join(projectPath, applicationAzurePropertiesRelativePath) - return updatePropertyFile(filePath, postgresqlProperties, keepNewValue) -} - -func keepNewValue(_ string, newValue string) string { - return newValue -} - -func activeAzureProfile(projectPath string) error { - filePath := filepath.Join(projectPath, applicationPropertiesRelativePath) - var newProperties = []property{ - {"spring.profiles.active", azureProfileName}, - } - return updatePropertyFile(filePath, newProperties, appendToCommaSeparatedValues) -} - -func appendToCommaSeparatedValues(commaSeparatedValues string, newValue string) string { - if commaSeparatedValues == "" { - return newValue - } - var values []string - for _, value := range strings.SplitN(commaSeparatedValues, ",", -1) { - value = strings.TrimSpace(value) - if value != "" { - values = append(values, value) - } - } - if !contains(values, azureProfileName) { - values = append(values, azureProfileName) - } - return strings.Join(values, ",") -} - -func contains(a []string, x string) bool { - for _, n := range a { - if x == n { - return true - } - } - return false -} - -func updatePropertyFile(filePath string, newProperties []property, function propertyMergeFunc) error { - err := createFileIfNotExist(filePath) - if err != nil { - return err - } - properties, err := readProperties(filePath) - if err != nil { - return err - } - properties = updateProperties(properties, newProperties, function) - err = writeProperties(filePath, properties) - if err != nil { - return err - } - return nil -} - -func createFileIfNotExist(filePath string) error { - dir := filepath.Dir(filePath) - if _, err := os.Stat(dir); os.IsNotExist(err) { - if err := os.MkdirAll(dir, os.ModePerm); err != nil { - return fmt.Errorf("failed to create directory: %w", err) - } - } - if _, err := os.Stat(filePath); os.IsNotExist(err) { - file, err := os.Create(filePath) - if err != nil { - return fmt.Errorf("failed to create file: %w", err) - } - defer file.Close() - } - return nil -} - -func readProperties(filePath string) ([]property, error) { - file, err := os.Open(filePath) - if err != nil { - return nil, err - } - defer file.Close() - - var properties []property - scanner := bufio.NewScanner(file) - for scanner.Scan() { - line := scanner.Text() - if strings.TrimSpace(line) == "" || strings.HasPrefix(line, "#") { - continue - } - parts := strings.SplitN(line, "=", 2) - if len(parts) == 2 { - key := strings.TrimSpace(parts[0]) - value := strings.TrimSpace(parts[1]) - properties = append(properties, property{key, value}) - } - } - - if err := scanner.Err(); err != nil { - return nil, err - } - - return properties, nil -} - -func updateProperties(properties []property, - newProperties []property, function propertyMergeFunc) []property { - for _, newProperty := range newProperties { - if index := getKeyIndex(properties, newProperty.key); index != -1 { - properties[index].value = function(properties[index].value, newProperty.value) - } else { - properties = append(properties, newProperty) - } - } - return properties -} - -func getKeyIndex(properties []property, key string) int { - for i, prop := range properties { - if prop.key == key { - return i - } - } - return -1 -} - -func writeProperties(filePath string, properties []property) error { - file, err := os.Create(filePath) - if err != nil { - return err - } - defer file.Close() - - writer := bufio.NewWriter(file) - for _, p := range properties { - _, err := writer.WriteString(fmt.Sprintf("%s=%s\n", p.key, p.value)) - if err != nil { - return err - } - } - return writer.Flush() -} diff --git a/cli/azd/internal/appdetect/spring_boot_properties_file_test.go b/cli/azd/internal/appdetect/spring_boot_properties_file_test.go deleted file mode 100644 index c3bc8f4f101..00000000000 --- a/cli/azd/internal/appdetect/spring_boot_properties_file_test.go +++ /dev/null @@ -1,142 +0,0 @@ -package appdetect - -import ( - "os" - "path/filepath" - "reflect" - "testing" - - "github.com/azure/azure-dev/cli/azd/internal" - "github.com/stretchr/testify/assert" -) - -const postgresPropertiesOriginalContent = `spring.datasource.url=jdbc:postgresql://localhost:5432/dbname?sslmode=require -spring.datasource.username=admin -spring.datasource.password=secret -` - -var postgresPropertiesUpdatedContent = `spring.datasource.url=` + placeholderPostgresJdbcUrl + ` -spring.datasource.username=` + internal.ToEnvPlaceHolder(internal.EnvNamePostgresUsername) + ` -spring.datasource.password=` + internal.ToEnvPlaceHolder(internal.EnvNamePostgresPassword) + ` -` - -var postgresPropertiesOriginalMap = []property{ - {"spring.datasource.url", "jdbc:postgresql://localhost:5432/dbname?sslmode=require"}, - {"spring.datasource.username", "admin"}, - {"spring.datasource.password", "secret"}, -} - -func TestAddPostgresqlConnectionProperties(t *testing.T) { - tests := []struct { - name string - inputApplicationPropertiesContent string - inputApplicationAzurePropertiesContent string - outputApplicationPropertiesContent string - outputApplicationAzurePropertiesContent string - }{ - { - name: "no content", - inputApplicationPropertiesContent: "", - inputApplicationAzurePropertiesContent: "", - outputApplicationPropertiesContent: "spring.profiles.active=" + azureProfileName + "\n", - outputApplicationAzurePropertiesContent: postgresPropertiesUpdatedContent, - }, - { - name: "override original content", - inputApplicationPropertiesContent: "spring.profiles.active=" + azureProfileName, - inputApplicationAzurePropertiesContent: postgresPropertiesOriginalContent, - outputApplicationPropertiesContent: "spring.profiles.active=" + azureProfileName + "\n", - outputApplicationAzurePropertiesContent: postgresPropertiesUpdatedContent, - }, - { - name: "append original content", - inputApplicationPropertiesContent: "aaa=xxx\n" + "spring.profiles.active=production , cloud,,", - inputApplicationAzurePropertiesContent: "bbb=yyy\n" + postgresPropertiesOriginalContent, - outputApplicationPropertiesContent: "aaa=xxx\n" + "spring.profiles.active=production,cloud," + - azureProfileName + "\n", - outputApplicationAzurePropertiesContent: "bbb=yyy\n" + postgresPropertiesUpdatedContent, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - tempDir := t.TempDir() - applicationPropertiesPath := filepath.Join(tempDir, applicationPropertiesRelativePath) - createFileIfContentIsNotEmpty(t, applicationPropertiesPath, tt.inputApplicationPropertiesContent) - applicationAzurePropertiesPath := filepath.Join(tempDir, applicationAzurePropertiesRelativePath) - createFileIfContentIsNotEmpty(t, applicationAzurePropertiesPath, tt.inputApplicationAzurePropertiesContent) - err := addPostgresqlConnectionProperties(tempDir) - assert.NoError(t, err) - assertFileContent(t, applicationPropertiesPath, tt.outputApplicationPropertiesContent) - assertFileContent(t, applicationAzurePropertiesPath, tt.outputApplicationAzurePropertiesContent) - }) - } -} - -func createFileIfContentIsNotEmpty(t *testing.T, path string, content string) { - if content == "" { - return - } - - dir := filepath.Dir(path) - if _, err := os.Stat(dir); os.IsNotExist(err) { - err := os.MkdirAll(dir, os.ModePerm) - assert.NoError(t, err) - } - file, err := os.Create(path) - assert.NoError(t, err) - defer file.Close() - err = os.WriteFile(path, []byte(content), 0600) - assert.NoError(t, err) -} - -func assertFileContent(t *testing.T, path string, content string) { - actualContent, err := os.ReadFile(path) - assert.NoError(t, err) - assert.Equal(t, content, string(actualContent)) -} - -func TestReadProperties(t *testing.T) { - tempFile, err := os.CreateTemp("", "test.properties") - if err != nil { - t.Fatal(err) - } - defer os.Remove(tempFile.Name()) - - if _, err := tempFile.Write([]byte(postgresPropertiesOriginalContent)); err != nil { - t.Fatal(err) - } - if err := tempFile.Close(); err != nil { - t.Fatal(err) - } - - properties, err := readProperties(tempFile.Name()) - if err != nil { - t.Fatalf("readProperties() error = %v", err) - } - - if !reflect.DeepEqual(properties, postgresPropertiesOriginalMap) { - t.Errorf("readProperties() = %v, want %v", properties, postgresPropertiesOriginalMap) - } -} - -func TestWriteProperties(t *testing.T) { - tempFile, err := os.CreateTemp("", "test.properties") - if err != nil { - t.Fatal(err) - } - defer os.Remove(tempFile.Name()) - - if err := writeProperties(tempFile.Name(), postgresPropertiesOriginalMap); err != nil { - t.Fatalf("writeProperties() error = %v", err) - } - - content, err := os.ReadFile(tempFile.Name()) - if err != nil { - t.Fatal(err) - } - - if string(content) != postgresPropertiesOriginalContent { - t.Errorf("writeProperties() = %v, want %v", string(content), postgresPropertiesOriginalContent) - } -} diff --git a/cli/azd/internal/repository/app_init.go b/cli/azd/internal/repository/app_init.go index cb211bc14b1..d2734b57c32 100644 --- a/cli/azd/internal/repository/app_init.go +++ b/cli/azd/internal/repository/app_init.go @@ -1,6 +1,7 @@ package repository import ( + "bufio" "context" "fmt" "maps" @@ -301,6 +302,31 @@ func (i *Initializer) InitFromApp( }) } + for _, proj := range projects { + isSpringBootProject := false + for _, dep := range proj.RawDependencies { + if dep.Kind == appdetect.RawDependencyKindMaven && dep.Name == appdetect.MavenDependencyNameSpringBootMavenPlugin { + isSpringBootProject = true + break + } + } + if !isSpringBootProject { + continue + } + for _, dep := range proj.RawDependencies { + if dep.Kind != appdetect.RawDependencyKindMaven { + continue + } + switch dep.Name { + case appdetect.MavenDependencyNamePostgresql, appdetect.MavenDependencyNameSpringCloudAzureStarterJdbcPostgresql: + err := addPostgresqlConnectionProperties(proj.Path) + if err != nil { + return err + } + } + } + } + return nil } @@ -578,3 +604,179 @@ func ServiceFromDetect( return svc, nil } + +type property struct { + key string + value string +} +type propertyMergeFunc func(string, string) string + +const azureProfileName = "azure" + +var placeholderPostgresJdbcUrl = "jdbc:postgresql://" + internal.ToEnvPlaceHolder(internal.EnvNamePostgresHost) + + ":" + internal.ToEnvPlaceHolder(internal.EnvNamePostgresPort) + + "/" + internal.ToEnvPlaceHolder(internal.EnvNamePostgresDatabase) + +var postgresqlProperties = []property{ + {"spring.datasource.url", placeholderPostgresJdbcUrl}, + {"spring.datasource.username", internal.ToEnvPlaceHolder(internal.EnvNamePostgresUsername)}, + {"spring.datasource.password", internal.ToEnvPlaceHolder(internal.EnvNamePostgresPassword)}, +} + +var applicationPropertiesRelativePath = filepath.Join("src", "main", "resources", + "application.properties") +var applicationAzurePropertiesRelativePath = filepath.Join("src", "main", "resources", + "application-"+azureProfileName+".properties") + +// todo: support other file suffix. Example: application.yml, application.yaml +func addPostgresqlConnectionProperties(projectPath string) error { + err := addPostgresqlConnectionPropertiesIntoPropertyFile(projectPath) + if err != nil { + return err + } + return activeAzureProfile(projectPath) +} + +func addPostgresqlConnectionPropertiesIntoPropertyFile(projectPath string) error { + filePath := filepath.Join(projectPath, applicationAzurePropertiesRelativePath) + return updatePropertyFile(filePath, postgresqlProperties, keepNewValue) +} + +func keepNewValue(_ string, newValue string) string { + return newValue +} + +func activeAzureProfile(projectPath string) error { + filePath := filepath.Join(projectPath, applicationPropertiesRelativePath) + var newProperties = []property{ + {"spring.profiles.active", azureProfileName}, + } + return updatePropertyFile(filePath, newProperties, appendToCommaSeparatedValues) +} + +func appendToCommaSeparatedValues(commaSeparatedValues string, newValue string) string { + if commaSeparatedValues == "" { + return newValue + } + var values []string + for _, value := range strings.SplitN(commaSeparatedValues, ",", -1) { + value = strings.TrimSpace(value) + if value != "" { + values = append(values, value) + } + } + if !contains(values, azureProfileName) { + values = append(values, azureProfileName) + } + return strings.Join(values, ",") +} + +func contains(a []string, x string) bool { + for _, n := range a { + if x == n { + return true + } + } + return false +} + +func updatePropertyFile(filePath string, newProperties []property, function propertyMergeFunc) error { + err := createFileIfNotExist(filePath) + if err != nil { + return err + } + properties, err := readProperties(filePath) + if err != nil { + return err + } + properties = updateProperties(properties, newProperties, function) + err = writeProperties(filePath, properties) + if err != nil { + return err + } + return nil +} + +func createFileIfNotExist(filePath string) error { + dir := filepath.Dir(filePath) + if _, err := os.Stat(dir); os.IsNotExist(err) { + if err := os.MkdirAll(dir, os.ModePerm); err != nil { + return fmt.Errorf("failed to create directory: %w", err) + } + } + if _, err := os.Stat(filePath); os.IsNotExist(err) { + file, err := os.Create(filePath) + if err != nil { + return fmt.Errorf("failed to create file: %w", err) + } + defer file.Close() + } + return nil +} + +func readProperties(filePath string) ([]property, error) { + file, err := os.Open(filePath) + if err != nil { + return nil, err + } + defer file.Close() + + var properties []property + scanner := bufio.NewScanner(file) + for scanner.Scan() { + line := scanner.Text() + if strings.TrimSpace(line) == "" || strings.HasPrefix(line, "#") { + continue + } + parts := strings.SplitN(line, "=", 2) + if len(parts) == 2 { + key := strings.TrimSpace(parts[0]) + value := strings.TrimSpace(parts[1]) + properties = append(properties, property{key, value}) + } + } + + if err := scanner.Err(); err != nil { + return nil, err + } + + return properties, nil +} + +func updateProperties(properties []property, + newProperties []property, function propertyMergeFunc) []property { + for _, newProperty := range newProperties { + if index := getKeyIndex(properties, newProperty.key); index != -1 { + properties[index].value = function(properties[index].value, newProperty.value) + } else { + properties = append(properties, newProperty) + } + } + return properties +} + +func getKeyIndex(properties []property, key string) int { + for i, prop := range properties { + if prop.key == key { + return i + } + } + return -1 +} + +func writeProperties(filePath string, properties []property) error { + file, err := os.Create(filePath) + if err != nil { + return err + } + defer file.Close() + + writer := bufio.NewWriter(file) + for _, p := range properties { + _, err := writer.WriteString(fmt.Sprintf("%s=%s\n", p.key, p.value)) + if err != nil { + return err + } + } + return writer.Flush() +} diff --git a/cli/azd/internal/repository/app_init_test.go b/cli/azd/internal/repository/app_init_test.go index 6a0b7a7dac6..71823cf3540 100644 --- a/cli/azd/internal/repository/app_init_test.go +++ b/cli/azd/internal/repository/app_init_test.go @@ -5,6 +5,7 @@ import ( "fmt" "os" "path/filepath" + "reflect" "strings" "testing" @@ -12,6 +13,7 @@ import ( "github.com/azure/azure-dev/cli/azd/internal/appdetect" "github.com/azure/azure-dev/cli/azd/pkg/input" "github.com/azure/azure-dev/cli/azd/pkg/project" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -319,3 +321,134 @@ func TestInitializer_prjConfigFromDetect(t *testing.T) { }) } } + +const postgresPropertiesOriginalContent = `spring.datasource.url=jdbc:postgresql://localhost:5432/dbname?sslmode=require +spring.datasource.username=admin +spring.datasource.password=secret +` + +var postgresPropertiesUpdatedContent = `spring.datasource.url=` + placeholderPostgresJdbcUrl + ` +spring.datasource.username=` + internal.ToEnvPlaceHolder(internal.EnvNamePostgresUsername) + ` +spring.datasource.password=` + internal.ToEnvPlaceHolder(internal.EnvNamePostgresPassword) + ` +` + +var postgresPropertiesOriginalMap = []property{ + {"spring.datasource.url", "jdbc:postgresql://localhost:5432/dbname?sslmode=require"}, + {"spring.datasource.username", "admin"}, + {"spring.datasource.password", "secret"}, +} + +func TestAddPostgresqlConnectionProperties(t *testing.T) { + tests := []struct { + name string + inputApplicationPropertiesContent string + inputApplicationAzurePropertiesContent string + outputApplicationPropertiesContent string + outputApplicationAzurePropertiesContent string + }{ + { + name: "no content", + inputApplicationPropertiesContent: "", + inputApplicationAzurePropertiesContent: "", + outputApplicationPropertiesContent: "spring.profiles.active=" + azureProfileName + "\n", + outputApplicationAzurePropertiesContent: postgresPropertiesUpdatedContent, + }, + { + name: "override original content", + inputApplicationPropertiesContent: "spring.profiles.active=" + azureProfileName, + inputApplicationAzurePropertiesContent: postgresPropertiesOriginalContent, + outputApplicationPropertiesContent: "spring.profiles.active=" + azureProfileName + "\n", + outputApplicationAzurePropertiesContent: postgresPropertiesUpdatedContent, + }, + { + name: "append original content", + inputApplicationPropertiesContent: "aaa=xxx\n" + "spring.profiles.active=production , cloud,,", + inputApplicationAzurePropertiesContent: "bbb=yyy\n" + postgresPropertiesOriginalContent, + outputApplicationPropertiesContent: "aaa=xxx\n" + "spring.profiles.active=production,cloud," + + azureProfileName + "\n", + outputApplicationAzurePropertiesContent: "bbb=yyy\n" + postgresPropertiesUpdatedContent, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + tempDir := t.TempDir() + applicationPropertiesPath := filepath.Join(tempDir, applicationPropertiesRelativePath) + createFileIfContentIsNotEmpty(t, applicationPropertiesPath, tt.inputApplicationPropertiesContent) + applicationAzurePropertiesPath := filepath.Join(tempDir, applicationAzurePropertiesRelativePath) + createFileIfContentIsNotEmpty(t, applicationAzurePropertiesPath, tt.inputApplicationAzurePropertiesContent) + err := addPostgresqlConnectionProperties(tempDir) + assert.NoError(t, err) + assertFileContent(t, applicationPropertiesPath, tt.outputApplicationPropertiesContent) + assertFileContent(t, applicationAzurePropertiesPath, tt.outputApplicationAzurePropertiesContent) + }) + } +} + +func createFileIfContentIsNotEmpty(t *testing.T, path string, content string) { + if content == "" { + return + } + + dir := filepath.Dir(path) + if _, err := os.Stat(dir); os.IsNotExist(err) { + err := os.MkdirAll(dir, os.ModePerm) + assert.NoError(t, err) + } + file, err := os.Create(path) + assert.NoError(t, err) + defer file.Close() + err = os.WriteFile(path, []byte(content), 0600) + assert.NoError(t, err) +} + +func assertFileContent(t *testing.T, path string, content string) { + actualContent, err := os.ReadFile(path) + assert.NoError(t, err) + assert.Equal(t, content, string(actualContent)) +} + +func TestReadProperties(t *testing.T) { + tempFile, err := os.CreateTemp("", "test.properties") + if err != nil { + t.Fatal(err) + } + defer os.Remove(tempFile.Name()) + + if _, err := tempFile.Write([]byte(postgresPropertiesOriginalContent)); err != nil { + t.Fatal(err) + } + if err := tempFile.Close(); err != nil { + t.Fatal(err) + } + + properties, err := readProperties(tempFile.Name()) + if err != nil { + t.Fatalf("readProperties() error = %v", err) + } + + if !reflect.DeepEqual(properties, postgresPropertiesOriginalMap) { + t.Errorf("readProperties() = %v, want %v", properties, postgresPropertiesOriginalMap) + } +} + +func TestWriteProperties(t *testing.T) { + tempFile, err := os.CreateTemp("", "test.properties") + if err != nil { + t.Fatal(err) + } + defer os.Remove(tempFile.Name()) + + if err := writeProperties(tempFile.Name(), postgresPropertiesOriginalMap); err != nil { + t.Fatalf("writeProperties() error = %v", err) + } + + content, err := os.ReadFile(tempFile.Name()) + if err != nil { + t.Fatal(err) + } + + if string(content) != postgresPropertiesOriginalContent { + t.Errorf("writeProperties() = %v, want %v", string(content), postgresPropertiesOriginalContent) + } +}