diff --git a/internal/resolution/pm/nuget/cmd_factory.go b/internal/resolution/pm/nuget/cmd_factory.go index 75ca887a..1f71f550 100644 --- a/internal/resolution/pm/nuget/cmd_factory.go +++ b/internal/resolution/pm/nuget/cmd_factory.go @@ -31,6 +31,19 @@ type CmdFactory struct { execPath IExecPath } +var packagesConfigTemplate = ` + + + {{.TargetFrameworks}} + + + {{- range .Packages}} + + {{- end}} + + +` + func (cmdf CmdFactory) MakeInstallCmd(command string, file string) (*exec.Cmd, error) { // If the file is a packages.config file, convert it to a .csproj file @@ -86,7 +99,7 @@ func convertPackagesConfigToCsproj(filePath string) (string, error) { } targetFrameworksStr := collectUniqueTargetFrameworks(packages.Packages) - csprojContent, err := createCsprojContent(targetFrameworksStr, packages.Packages) + csprojContent, err := createCsprojContentWithTemplate(targetFrameworksStr, packages.Packages, packagesConfigTemplate) if err != nil { return "", err } @@ -137,19 +150,7 @@ func collectUniqueTargetFrameworks(packages []Package) string { return strings.Join(targetFrameworks, ";") } -func createCsprojContent(targetFrameworksStr string, packages []Package) (string, error) { - tmpl := ` - - - {{.TargetFrameworks}} - - - {{- range .Packages}} - - {{- end}} - - -` +func createCsprojContentWithTemplate(targetFrameworksStr string, packages []Package, tmpl string) (string, error) { tmplParsed, err := template.New("csproj").Parse(tmpl) if err != nil { return "", err @@ -168,6 +169,7 @@ func createCsprojContent(targetFrameworksStr string, packages []Package) (string } func writeContentToCsprojFile(newFilename string, content string) error { + csprojFile, err := os.Create(newFilename) if err != nil { return err @@ -175,9 +177,6 @@ func writeContentToCsprojFile(newFilename string, content string) error { defer csprojFile.Close() _, err = csprojFile.WriteString(content) - if err != nil { - return err - } - return nil + return err } diff --git a/internal/resolution/pm/nuget/cmd_factory_test.go b/internal/resolution/pm/nuget/cmd_factory_test.go index f971919b..44de7025 100644 --- a/internal/resolution/pm/nuget/cmd_factory_test.go +++ b/internal/resolution/pm/nuget/cmd_factory_test.go @@ -35,21 +35,64 @@ func TestMakeInstallCmdPackagsConfig(t *testing.T) { t.Fatalf("Failed to remove test file: %v", err) } } - func TestParsePackagesConfig(t *testing.T) { tests := []struct { - filePath string - wantError bool + name string + setup func() string // function to set up the test environment + teardown func() // function to clean up after the test + shouldFail bool }{ - {"testdata/valid/packages.config", false}, - {"testdata/invalid/packages.config", true}, + { + name: "Non-existent file", + setup: func() string { + return "nonexistent_file.config" + }, + shouldFail: true, + }, + { + name: "Unreadable file", + setup: func() string { + file, err := os.CreateTemp("", "unreadable_*.config") + if err != nil { + t.Fatal(err) + } + file.Chmod(0222) // write-only permissions + return file.Name() + }, + teardown: func() { + os.Remove("unreadable_file.config") // clean up the unreadable file + }, + shouldFail: true, + }, + { + name: "Malformed XML", + setup: func() string { + file, err := os.CreateTemp("", "malformed_*.config") + if err != nil { + t.Fatal(err) + } + file.WriteString("malformed xml content") + return file.Name() + }, + teardown: func() { + os.Remove("malformed_file.config") // clean up the malformed file + }, + shouldFail: true, + }, } - for _, tt := range tests { - _, err := parsePackagesConfig(tt.filePath) - if (err != nil) != tt.wantError { - t.Errorf("parsePackagesConfig(%q) = %v, want error: %v", tt.filePath, err, tt.wantError) - } + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + filePath := test.setup() + if test.teardown != nil { + defer test.teardown() // clean up after the test + } + + _, err := parsePackagesConfig(filePath) + if (err != nil) != test.shouldFail { + t.Errorf("parsePackagesConfig() error = %v, shouldFail = %v", err, test.shouldFail) + } + }) } } @@ -66,25 +109,6 @@ func TestCollectUniqueTargetFrameworks(t *testing.T) { } } -func TestCreateCsprojContent(t *testing.T) { - packages := []Package{ - {ID: "Test.Package.1", Version: "1.0.0", TargetFramework: "net45"}, - {ID: "Test.Package.2", Version: "2.0.0", TargetFramework: "net46"}, - } - targetFrameworksStr := "net45;net46" - - got, err := createCsprojContent(targetFrameworksStr, packages) - if err != nil { - t.Fatalf("createCsprojContent() failed: %v", err) - } - - // We're just checking if the function returns a non-empty string - // For a more rigorous test, we'd check the exact content of the string - if got == "" { - t.Errorf("createCsprojContent() returned an empty string") - } -} - func TestWriteContentToCsprojFile(t *testing.T) { newFilename := "testdata/test_output.csproj" content := "" @@ -124,3 +148,126 @@ func TestConvertPackagesConfigToCsproj(t *testing.T) { t.Fatalf("Failed to remove test file: %v", err) } } + +func TestWriteContentToCsprojFileErr(t *testing.T) { + tests := []struct { + name string + filename string + content string + shouldFail bool + setup func() // function to set up the environment for the test + teardown func() // function to clean up after the test + }{ + { + name: "Valid file name and content", + filename: "test.csproj", + content: "", + shouldFail: false, + teardown: func() { + os.Remove("test.csproj") // Clean up the created file + }, + }, + { + name: "Invalid file name", + filename: "", // Empty filename is invalid + content: "", + shouldFail: true, + }, + { + name: "Write to a read-only file", + filename: "readonly.csproj", + content: "", + shouldFail: true, + setup: func() { + // Create a read-only file + file, err := os.Create("readonly.csproj") + if err != nil { + panic(err) + } + file.Close() + os.Chmod("readonly.csproj", 0444) // Set file permissions to read-only + }, + teardown: func() { + os.Remove("readonly.csproj") // Clean up the read-only file + }, + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + if test.setup != nil { + test.setup() // set up the environment for the test + } + err := writeContentToCsprojFile(test.filename, test.content) + if (err != nil) != test.shouldFail { + t.Errorf("writeContentToCsprojFile() error = %v, shouldFail = %v", err, test.shouldFail) + } + if test.teardown != nil { + test.teardown() // clean up after the test + } + }) + } +} + +func TestCreateCsprojContent(t *testing.T) { + tests := []struct { + name string + targetFrameworksStr string + packages []Package + shouldFail bool + tmpl string + }{ + { + name: "Valid template action", + targetFrameworksStr: "netcoreapp3.1", + packages: []Package{{ID: "SomePackage", Version: "1.0.0"}}, + shouldFail: false, + tmpl: packagesConfigTemplate, + }, + { + name: "Invalid template action", + targetFrameworksStr: "netcoreapp3.1", + packages: []Package{{ID: "SomePackage", Version: "1.0.0"}}, + shouldFail: true, + tmpl: ` + + + {{.TargetFrameworks}} + + + {{- range .Packages}} + + {{- end}} + + + `, + }, + { + name: "Non-existent field", + targetFrameworksStr: "netcoreapp3.1", + packages: []Package{{ID: "SomePackage", Version: "1.0.0"}}, + shouldFail: true, + tmpl: ` + + + {{.NonExistentField}} + + + {{- range .Packages}} + + {{- end}} + + + `, + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + _, err := createCsprojContentWithTemplate(test.targetFrameworksStr, test.packages, test.tmpl) + if (err != nil) != test.shouldFail { + t.Errorf("createCsprojContentWithTemplate() error = %v, shouldFail = %v", err, test.shouldFail) + } + }) + } +}