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  }