github.com/jfrog/jfrog-cli@v1.54.1/pip_test.go (about) 1 package main 2 3 import ( 4 "fmt" 5 "io" 6 "os" 7 "os/exec" 8 "path/filepath" 9 "strconv" 10 "testing" 11 12 gofrogcmd "github.com/jfrog/gofrog/io" 13 "github.com/jfrog/jfrog-cli-core/utils/coreutils" 14 "github.com/jfrog/jfrog-cli/inttestutils" 15 "github.com/jfrog/jfrog-cli/utils/tests" 16 "github.com/jfrog/jfrog-client-go/artifactory/buildinfo" 17 "github.com/jfrog/jfrog-client-go/utils/io/fileutils" 18 "github.com/stretchr/testify/assert" 19 "github.com/stretchr/testify/require" 20 ) 21 22 type PipCmd struct { 23 Command string 24 Options []string 25 } 26 27 func TestPipInstall(t *testing.T) { 28 // Init pip. 29 initPipTest(t) 30 31 // Add virtual-environment path to 'PATH' for executing all pip and python commands inside the virtual-environment. 32 pathValue := setPathEnvForPipInstall(t) 33 if t.Failed() { 34 t.FailNow() 35 } 36 defer os.Setenv("PATH", pathValue) 37 38 // Check pip env is clean. 39 validateEmptyPipEnv(t) 40 41 // Populate cli config with 'default' server. 42 oldHomeDir, newHomeDir := prepareHomeDir(t) 43 defer os.Setenv(coreutils.HomeDir, oldHomeDir) 44 defer os.RemoveAll(newHomeDir) 45 46 // Create test cases. 47 allTests := []struct { 48 name string 49 project string 50 outputFolder string 51 moduleId string 52 args []string 53 expectedDependencies int 54 cleanAfterExecution bool 55 }{ 56 {"setuppy", "setuppyproject", "setuppy", "jfrog-python-example", []string{"pip-install", ".", "--no-cache-dir", "--force-reinstall", "--build-name=" + tests.PipBuildName}, 3, true}, 57 {"setuppy-verbose", "setuppyproject", "setuppy-verbose", "jfrog-python-example", []string{"pip-install", ".", "--no-cache-dir", "--force-reinstall", "-v", "--build-name=" + tests.PipBuildName}, 3, true}, 58 {"setuppy-with-module", "setuppyproject", "setuppy-with-module", "setuppy-with-module", []string{"pip-install", ".", "--no-cache-dir", "--force-reinstall", "--build-name=" + tests.PipBuildName, "--module=setuppy-with-module"}, 3, true}, 59 {"requirements", "requirementsproject", "requirements", tests.PipBuildName, []string{"pip-install", "-r", "requirements.txt", "--no-cache-dir", "--force-reinstall", "--build-name=" + tests.PipBuildName}, 5, true}, 60 {"requirements-verbose", "requirementsproject", "requirements-verbose", tests.PipBuildName, []string{"pip-install", "-r", "requirements.txt", "--no-cache-dir", "--force-reinstall", "-v", "--build-name=" + tests.PipBuildName}, 5, false}, 61 {"requirements-use-cache", "requirementsproject", "requirements-verbose", "requirements-verbose-use-cache", []string{"pip-install", "-r", "requirements.txt", "--module=requirements-verbose-use-cache", "--build-name=" + tests.PipBuildName}, 5, true}, 62 } 63 64 // Run test cases. 65 for buildNumber, test := range allTests { 66 t.Run(test.name, func(t *testing.T) { 67 testPipCmd(t, test.name, createPipProject(t, test.outputFolder, test.project), strconv.Itoa(buildNumber), test.moduleId, test.expectedDependencies, test.args) 68 if test.cleanAfterExecution { 69 // cleanup 70 inttestutils.DeleteBuild(serverDetails.ArtifactoryUrl, tests.PipBuildName, artHttpDetails) 71 cleanPipTest(t, test.name) 72 } 73 }) 74 } 75 cleanPipTest(t, "cleanup") 76 tests.CleanFileSystem() 77 } 78 79 func testPipCmd(t *testing.T, outputFolder, projectPath, buildNumber, module string, expectedDependencies int, args []string) { 80 wd, err := os.Getwd() 81 assert.NoError(t, err) 82 err = os.Chdir(projectPath) 83 assert.NoError(t, err) 84 defer os.Chdir(wd) 85 86 args = append(args, "--build-number="+buildNumber) 87 88 err = artifactoryCli.WithoutCredentials().Exec(args...) 89 if err != nil { 90 assert.Fail(t, "Failed executing pip-install command", err.Error()) 91 cleanPipTest(t, outputFolder) 92 return 93 } 94 95 inttestutils.ValidateGeneratedBuildInfoModule(t, tests.PipBuildName, buildNumber, "", []string{module}, buildinfo.Pip) 96 assert.NoError(t, artifactoryCli.Exec("bp", tests.PipBuildName, buildNumber)) 97 98 publishedBuildInfo, found, err := tests.GetBuildInfo(serverDetails, tests.PipBuildName, buildNumber) 99 if err != nil { 100 assert.NoError(t, err) 101 return 102 } 103 if !found { 104 assert.True(t, found, "build info was expected to be found") 105 return 106 } 107 buildInfo := publishedBuildInfo.BuildInfo 108 require.NotEmpty(t, buildInfo.Modules, "Pip build info was not generated correctly, no modules were created.") 109 assert.Len(t, buildInfo.Modules[0].Dependencies, expectedDependencies, "Incorrect number of artifacts found in the build-info") 110 assert.Equal(t, module, buildInfo.Modules[0].Id, "Unexpected module name") 111 } 112 113 func cleanPipTest(t *testing.T, outFolder string) { 114 // Clean pip environment from installed packages. 115 pipFreezeCmd := &PipCmd{Command: "freeze", Options: []string{"--local"}} 116 out, err := gofrogcmd.RunCmdOutput(pipFreezeCmd) 117 if err != nil { 118 t.Fatal(err) 119 } 120 121 // If no packages to uninstall, return. 122 if out == "" { 123 return 124 } 125 126 // Save freeze output to file. 127 freezeTarget, err := fileutils.CreateFilePath(tests.Temp, outFolder+"-freeze.txt") 128 assert.NoError(t, err) 129 file, err := os.Create(freezeTarget) 130 assert.NoError(t, err) 131 defer file.Close() 132 _, err = file.Write([]byte(out)) 133 assert.NoError(t, err) 134 135 // Delete freezed packages. 136 pipUninstallCmd := &PipCmd{Command: "uninstall", Options: []string{"-y", "-r", freezeTarget}} 137 err = gofrogcmd.RunCmd(pipUninstallCmd) 138 if err != nil { 139 t.Fatal(err) 140 } 141 } 142 143 func createPipProject(t *testing.T, outFolder, projectName string) string { 144 projectSrc := filepath.Join(filepath.FromSlash(tests.GetTestResourcesPath()), "pip", projectName) 145 projectTarget := filepath.Join(tests.Out, outFolder+"-"+projectName) 146 err := fileutils.CreateDirIfNotExist(projectTarget) 147 assert.NoError(t, err) 148 149 // Copy pip-installation file. 150 err = fileutils.CopyDir(projectSrc, projectTarget, true, nil) 151 assert.NoError(t, err) 152 153 // Copy pip-config file. 154 configSrc := filepath.Join(filepath.FromSlash(tests.GetTestResourcesPath()), "pip", "pip.yaml") 155 configTarget := filepath.Join(projectTarget, ".jfrog", "projects") 156 tests.ReplaceTemplateVariables(configSrc, configTarget) 157 158 return projectTarget 159 } 160 161 func initPipTest(t *testing.T) { 162 if !*tests.TestPip { 163 t.Skip("Skipping Pip test. To run Pip test add the '-test.pip=true' option.") 164 } 165 require.True(t, isRepoExist(tests.PypiRemoteRepo), "Pypi test remote repository doesn't exist.") 166 require.True(t, isRepoExist(tests.PypiVirtualRepo), "Pypi test virtual repository doesn't exist.") 167 } 168 169 func setPathEnvForPipInstall(t *testing.T) string { 170 // Keep original value of 'PATH'. 171 pathValue, exists := os.LookupEnv("PATH") 172 if !exists { 173 t.Fatal("Couldn't find PATH variable, failing pip tests.") 174 } 175 176 // Append the path. 177 virtualEnvPath := *tests.PipVirtualEnv 178 if virtualEnvPath != "" { 179 var newPathValue string 180 if coreutils.IsWindows() { 181 newPathValue = fmt.Sprintf("%s;%s", virtualEnvPath, pathValue) 182 } else { 183 newPathValue = fmt.Sprintf("%s:%s", virtualEnvPath, pathValue) 184 } 185 err := os.Setenv("PATH", newPathValue) 186 if err != nil { 187 t.Fatal(err) 188 } 189 } 190 191 // Return original PATH value. 192 return pathValue 193 } 194 195 // Ensure that the provided pip virtual-environment is empty from installed packages. 196 func validateEmptyPipEnv(t *testing.T) { 197 //pipFreezeCmd := &PipFreezeCmd{Executable: "pip", Command: "freeze"} 198 pipFreezeCmd := &PipCmd{Command: "freeze", Options: []string{"--local"}} 199 out, err := gofrogcmd.RunCmdOutput(pipFreezeCmd) 200 if err != nil { 201 t.Fatal(err) 202 } 203 if out != "" { 204 t.Fatalf("Provided pip virtual-environment contains installed packages: %s\n. Please provide a clean environment.", out) 205 } 206 } 207 208 func (pfc *PipCmd) GetCmd() *exec.Cmd { 209 var cmd []string 210 cmd = append(cmd, "pip") 211 cmd = append(cmd, pfc.Command) 212 cmd = append(cmd, pfc.Options...) 213 return exec.Command(cmd[0], cmd[1:]...) 214 } 215 216 func (pfc *PipCmd) GetEnv() map[string]string { 217 return map[string]string{} 218 } 219 220 func (pfc *PipCmd) GetStdWriter() io.WriteCloser { 221 return nil 222 } 223 224 func (pfc *PipCmd) GetErrWriter() io.WriteCloser { 225 return nil 226 }