github.com/ouraigua/jenkins-library@v0.0.0-20231028010029-fbeaf2f3aa9b/pkg/npm/npm_test.go (about)

     1  //go:build unit
     2  // +build unit
     3  
     4  package npm
     5  
     6  import (
     7  	"fmt"
     8  	"path/filepath"
     9  	"testing"
    10  
    11  	"github.com/SAP/jenkins-library/pkg/mock"
    12  	"github.com/stretchr/testify/assert"
    13  )
    14  
    15  type npmMockUtilsBundle struct {
    16  	*mock.FilesMock
    17  	execRunner *mock.ExecMockRunner
    18  }
    19  
    20  func (u *npmMockUtilsBundle) GetExecRunner() ExecRunner {
    21  	return u.execRunner
    22  }
    23  
    24  func newNpmMockUtilsBundle() npmMockUtilsBundle {
    25  	utils := npmMockUtilsBundle{FilesMock: &mock.FilesMock{}, execRunner: &mock.ExecMockRunner{}}
    26  	return utils
    27  }
    28  
    29  func TestNpm(t *testing.T) {
    30  	t.Run("find package.json files with one package.json", func(t *testing.T) {
    31  		utils := newNpmMockUtilsBundle()
    32  		utils.AddFile("package.json", []byte("{\"name\": \"Test\" }"))
    33  
    34  		options := ExecutorOptions{}
    35  
    36  		exec := &Execute{
    37  			Utils:   &utils,
    38  			Options: options,
    39  		}
    40  
    41  		packageJSONFiles := exec.FindPackageJSONFiles()
    42  
    43  		assert.Equal(t, []string{"package.json"}, packageJSONFiles)
    44  
    45  	})
    46  
    47  	t.Run("find package.json files with two package.json and default filter", func(t *testing.T) {
    48  		utils := newNpmMockUtilsBundle()
    49  		utils.AddFile("package.json", []byte("{}"))
    50  		utils.AddFile(filepath.Join("src", "package.json"), []byte("{}"))          // should NOT be filtered out
    51  		utils.AddFile(filepath.Join("node_modules", "package.json"), []byte("{}")) // is filtered out
    52  		utils.AddFile(filepath.Join("gen", "package.json"), []byte("{}"))          // is filtered out
    53  
    54  		options := ExecutorOptions{}
    55  
    56  		exec := &Execute{
    57  			Utils:   &utils,
    58  			Options: options,
    59  		}
    60  
    61  		packageJSONFiles := exec.FindPackageJSONFiles()
    62  
    63  		assert.Equal(t, []string{"package.json", filepath.Join("src", "package.json")}, packageJSONFiles)
    64  	})
    65  
    66  	t.Run("find package.json files with two package.json and excludes", func(t *testing.T) {
    67  		utils := newNpmMockUtilsBundle()
    68  		utils.AddFile("package.json", []byte("{}"))
    69  		utils.AddFile(filepath.Join("src", "package.json"), []byte("{}"))                  // should NOT be filtered out
    70  		utils.AddFile(filepath.Join("notfiltered", "package.json"), []byte("{}"))          // should NOT be filtered out
    71  		utils.AddFile(filepath.Join("Path", "To", "filter", "package.json"), []byte("{}")) // should NOT be filtered out
    72  		utils.AddFile(filepath.Join("node_modules", "package.json"), []byte("{}"))         // is filtered out
    73  		utils.AddFile(filepath.Join("gen", "package.json"), []byte("{}"))                  // is filtered out
    74  		utils.AddFile(filepath.Join("filter", "package.json"), []byte("{}"))               // is filtered out
    75  		utils.AddFile(filepath.Join("filterPath", "package.json"), []byte("{}"))           // is filtered out
    76  		utils.AddFile(filepath.Join("filter", "Path", "To", "package.json"), []byte("{}")) // is filtered out
    77  
    78  		options := ExecutorOptions{}
    79  
    80  		exec := &Execute{
    81  			Utils:   &utils,
    82  			Options: options,
    83  		}
    84  
    85  		packageJSONFiles, err := exec.FindPackageJSONFilesWithExcludes([]string{"filter/**", "filterPath/package.json"})
    86  
    87  		if assert.NoError(t, err) {
    88  			assert.Equal(t, []string{filepath.Join("Path", "To", "filter", "package.json"), filepath.Join("notfiltered", "package.json"), "package.json", filepath.Join("src", "package.json")}, packageJSONFiles)
    89  		}
    90  	})
    91  
    92  	t.Run("find package.json files with script", func(t *testing.T) {
    93  		utils := newNpmMockUtilsBundle()
    94  		utils.AddFile("package.json", []byte("{\"scripts\": { \"ci-lint\": \"exit 0\" } }"))
    95  		utils.AddFile(filepath.Join("src", "package.json"), []byte("{ \"name\": \"test\" }"))
    96  		utils.AddFile(filepath.Join("test", "package.json"), []byte("{ \"scripts\": { \"test\": \"exit 0\" } }"))
    97  
    98  		options := ExecutorOptions{}
    99  
   100  		exec := &Execute{
   101  			Utils:   &utils,
   102  			Options: options,
   103  		}
   104  
   105  		packageJSONFilesWithScript, err := exec.FindPackageJSONFilesWithScript([]string{"package.json", filepath.Join("src", "package.json"), filepath.Join("test", "package.json")}, "ci-lint")
   106  
   107  		if assert.NoError(t, err) {
   108  			assert.Equal(t, []string{"package.json"}, packageJSONFilesWithScript)
   109  		}
   110  	})
   111  
   112  	t.Run("Install deps for package.json with package-lock.json", func(t *testing.T) {
   113  		utils := newNpmMockUtilsBundle()
   114  		utils.AddFile("package.json", []byte("{\"scripts\": { \"ci-lint\": \"exit 0\" } }"))
   115  		utils.AddFile("package-lock.json", []byte("{}"))
   116  
   117  		options := ExecutorOptions{}
   118  		options.DefaultNpmRegistry = "foo.bar"
   119  
   120  		exec := &Execute{
   121  			Utils:   &utils,
   122  			Options: options,
   123  		}
   124  		err := exec.install("package.json")
   125  
   126  		if assert.NoError(t, err) {
   127  			if assert.Equal(t, 2, len(utils.execRunner.Calls)) {
   128  				assert.Equal(t, mock.ExecCall{Exec: "npm", Params: []string{"ci"}}, utils.execRunner.Calls[1])
   129  			}
   130  		}
   131  	})
   132  
   133  	t.Run("Install deps for package.json without package-lock.json", func(t *testing.T) {
   134  		utils := newNpmMockUtilsBundle()
   135  		utils.AddFile("package.json", []byte("{\"scripts\": { \"ci-lint\": \"exit 0\" } }"))
   136  
   137  		options := ExecutorOptions{}
   138  		options.DefaultNpmRegistry = "foo.bar"
   139  
   140  		exec := &Execute{
   141  			Utils:   &utils,
   142  			Options: options,
   143  		}
   144  		err := exec.install("package.json")
   145  
   146  		if assert.NoError(t, err) {
   147  			if assert.Equal(t, 2, len(utils.execRunner.Calls)) {
   148  				assert.Equal(t, mock.ExecCall{Exec: "npm", Params: []string{"install"}}, utils.execRunner.Calls[1])
   149  			}
   150  		}
   151  	})
   152  
   153  	t.Run("Install deps for package.json with yarn.lock", func(t *testing.T) {
   154  		utils := newNpmMockUtilsBundle()
   155  		utils.AddFile("package.json", []byte("{\"scripts\": { \"ci-lint\": \"exit 0\" } }"))
   156  		utils.AddFile("yarn.lock", []byte("{}"))
   157  
   158  		options := ExecutorOptions{}
   159  		options.DefaultNpmRegistry = "foo.bar"
   160  
   161  		exec := &Execute{
   162  			Utils:   &utils,
   163  			Options: options,
   164  		}
   165  		err := exec.install("package.json")
   166  
   167  		if assert.NoError(t, err) {
   168  			if assert.Equal(t, 2, len(utils.execRunner.Calls)) {
   169  				assert.Equal(t, mock.ExecCall{Exec: "yarn", Params: []string{"install", "--frozen-lockfile"}}, utils.execRunner.Calls[1])
   170  			}
   171  		}
   172  	})
   173  
   174  	t.Run("Install all deps", func(t *testing.T) {
   175  		utils := newNpmMockUtilsBundle()
   176  		utils.AddFile("package.json", []byte("{\"scripts\": { \"ci-lint\": \"exit 0\" } }"))
   177  		utils.AddFile("package-lock.json", []byte("{}"))
   178  		utils.AddFile(filepath.Join("src", "package.json"), []byte("{\"scripts\": { \"ci-lint\": \"exit 0\" } }"))
   179  		utils.AddFile(filepath.Join("src", "package-lock.json"), []byte("{}"))
   180  
   181  		options := ExecutorOptions{}
   182  		options.DefaultNpmRegistry = "foo.bar"
   183  
   184  		exec := &Execute{
   185  			Utils:   &utils,
   186  			Options: options,
   187  		}
   188  		err := exec.InstallAllDependencies([]string{"package.json", filepath.Join("src", "package.json")})
   189  
   190  		if assert.NoError(t, err) {
   191  			if assert.Equal(t, 4, len(utils.execRunner.Calls)) {
   192  				assert.Equal(t, mock.ExecCall{Exec: "npm", Params: []string{"ci"}}, utils.execRunner.Calls[1])
   193  				assert.Equal(t, mock.ExecCall{Exec: "npm", Params: []string{"ci"}}, utils.execRunner.Calls[3])
   194  			}
   195  		}
   196  	})
   197  
   198  	t.Run("check if yarn.lock and package-lock exist", func(t *testing.T) {
   199  		utils := newNpmMockUtilsBundle()
   200  		utils.AddFile("package.json", []byte("{\"scripts\": { \"ci-lint\": \"exit 0\" } }"))
   201  		utils.AddFile("yarn.lock", []byte("{}"))
   202  		utils.AddFile("package-lock.json", []byte("{}"))
   203  
   204  		options := ExecutorOptions{}
   205  
   206  		exec := &Execute{
   207  			Utils:   &utils,
   208  			Options: options,
   209  		}
   210  		packageLock, yarnLock, err := exec.checkIfLockFilesExist()
   211  
   212  		if assert.NoError(t, err) {
   213  			assert.True(t, packageLock)
   214  			assert.True(t, yarnLock)
   215  		}
   216  	})
   217  
   218  	t.Run("check that yarn.lock and package-lock do not exist", func(t *testing.T) {
   219  		utils := newNpmMockUtilsBundle()
   220  		utils.AddFile("package.json", []byte("{\"scripts\": { \"ci-lint\": \"exit 0\" } }"))
   221  
   222  		options := ExecutorOptions{}
   223  
   224  		exec := &Execute{
   225  			Utils:   &utils,
   226  			Options: options,
   227  		}
   228  		packageLock, yarnLock, err := exec.checkIfLockFilesExist()
   229  
   230  		if assert.NoError(t, err) {
   231  			assert.False(t, packageLock)
   232  			assert.False(t, yarnLock)
   233  		}
   234  	})
   235  
   236  	t.Run("check Execute script", func(t *testing.T) {
   237  		utils := newNpmMockUtilsBundle()
   238  		utils.AddFile("package.json", []byte("{\"scripts\": { \"ci-lint\": \"exit 0\" } }"))
   239  
   240  		options := ExecutorOptions{}
   241  
   242  		exec := &Execute{
   243  			Utils:   &utils,
   244  			Options: options,
   245  		}
   246  		err := exec.executeScript("package.json", "ci-lint", []string{"--silent"}, []string{"--tag", "tag1"})
   247  
   248  		if assert.NoError(t, err) {
   249  			if assert.Equal(t, 2, len(utils.execRunner.Calls)) {
   250  				assert.Equal(t, mock.ExecCall{Exec: "npm", Params: []string{"run", "ci-lint", "--silent", "--", "--tag", "tag1"}}, utils.execRunner.Calls[1])
   251  			}
   252  		}
   253  	})
   254  
   255  	t.Run("check Execute all scripts", func(t *testing.T) {
   256  		utils := newNpmMockUtilsBundle()
   257  		utils.AddFile("package.json", []byte("{\"scripts\": { \"ci-lint\": \"exit 0\" } }"))
   258  		utils.AddFile(filepath.Join("src", "package.json"), []byte("{\"scripts\": { \"ci-build\": \"exit 0\" } }"))
   259  
   260  		options := ExecutorOptions{}
   261  		runScripts := []string{"ci-lint", "ci-build"}
   262  
   263  		exec := &Execute{
   264  			Utils:   &utils,
   265  			Options: options,
   266  		}
   267  		err := exec.RunScriptsInAllPackages(runScripts, nil, nil, false, nil, nil)
   268  
   269  		if assert.NoError(t, err) {
   270  			if assert.Equal(t, 4, len(utils.execRunner.Calls)) {
   271  				assert.Equal(t, mock.ExecCall{Exec: "npm", Params: []string{"run", "ci-lint"}}, utils.execRunner.Calls[1])
   272  				assert.Equal(t, mock.ExecCall{Exec: "npm", Params: []string{"run", "ci-build"}}, utils.execRunner.Calls[3])
   273  			}
   274  		}
   275  	})
   276  
   277  	t.Run("check Execute all scripts with buildDescriptorList", func(t *testing.T) {
   278  		utils := newNpmMockUtilsBundle()
   279  		utils.AddFile("package.json", []byte("{\"scripts\": { \"ci-lint\": \"exit 0\" } }"))                        // is filtered out
   280  		utils.AddFile(filepath.Join("src", "package.json"), []byte("{\"scripts\": { \"ci-build\": \"exit 0\" } }")) // should NOT be filtered out
   281  
   282  		options := ExecutorOptions{}
   283  		runScripts := []string{"ci-lint", "ci-build"}
   284  		buildDescriptorList := []string{filepath.Join("src", "package.json")}
   285  
   286  		exec := &Execute{
   287  			Utils:   &utils,
   288  			Options: options,
   289  		}
   290  		err := exec.RunScriptsInAllPackages(runScripts, nil, nil, false, nil, buildDescriptorList)
   291  
   292  		if assert.NoError(t, err) {
   293  			if assert.Equal(t, 2, len(utils.execRunner.Calls)) {
   294  				assert.Equal(t, mock.ExecCall{Exec: "npm", Params: []string{"run", "ci-build"}}, utils.execRunner.Calls[1])
   295  			}
   296  		}
   297  	})
   298  
   299  	t.Run("check set npm registry", func(t *testing.T) {
   300  		utils := newNpmMockUtilsBundle()
   301  		utils.AddFile("package.json", []byte("{\"scripts\": { \"ci-lint\": \"exit 0\" } }"))
   302  		utils.AddFile(filepath.Join("src", "package.json"), []byte("{\"scripts\": { \"ci-build\": \"exit 0\" } }"))
   303  		utils.execRunner = &mock.ExecMockRunner{StdoutReturn: map[string]string{"npm config get registry": "undefined"}}
   304  		options := ExecutorOptions{}
   305  		options.DefaultNpmRegistry = "https://example.org/npm"
   306  
   307  		exec := &Execute{
   308  			Utils:   &utils,
   309  			Options: options,
   310  		}
   311  		err := exec.SetNpmRegistries()
   312  
   313  		if assert.NoError(t, err) {
   314  			if assert.Equal(t, 2, len(utils.execRunner.Calls)) {
   315  				assert.Equal(t, mock.ExecCall{Exec: "npm", Params: []string{"config", "get", "registry"}}, utils.execRunner.Calls[0])
   316  				assert.Equal(t, mock.ExecCall{Exec: "npm", Params: []string{"config", "set", "registry", exec.Options.DefaultNpmRegistry}}, utils.execRunner.Calls[1])
   317  			}
   318  		}
   319  	})
   320  
   321  	t.Run("Call run-scripts with virtual frame buffer", func(t *testing.T) {
   322  		utils := newNpmMockUtilsBundle()
   323  		utils.AddFile("package.json", []byte("{\"scripts\": { \"foo\": \"\" } }"))
   324  
   325  		options := ExecutorOptions{}
   326  
   327  		exec := &Execute{
   328  			Utils:   &utils,
   329  			Options: options,
   330  		}
   331  		err := exec.RunScriptsInAllPackages([]string{"foo"}, nil, nil, true, nil, nil)
   332  
   333  		assert.Contains(t, utils.execRunner.Env, "DISPLAY=:99")
   334  		assert.NoError(t, err)
   335  		if assert.Len(t, utils.execRunner.Calls, 3) {
   336  			xvfbCall := utils.execRunner.Calls[0]
   337  			assert.Equal(t, "Xvfb", xvfbCall.Exec)
   338  			assert.Equal(t, []string{"-ac", ":99", "-screen", "0", "1280x1024x16"}, xvfbCall.Params)
   339  			assert.True(t, xvfbCall.Async)
   340  			assert.True(t, xvfbCall.Execution.Killed)
   341  
   342  			assert.Equal(t, mock.ExecCall{Exec: "npm", Params: []string{"run", "foo"}}, utils.execRunner.Calls[2])
   343  		}
   344  	})
   345  
   346  	t.Run("Create BOM with cyclonedx-npm", func(t *testing.T) {
   347  		utils := newNpmMockUtilsBundle()
   348  		utils.AddFile("package.json", []byte("{\"scripts\": { \"ci-lint\": \"exit 0\" } }"))
   349  		utils.AddFile("package-lock.json", []byte("{}"))
   350  		utils.AddFile(filepath.Join("src", "package.json"), []byte("{\"scripts\": { \"ci-lint\": \"exit 0\" } }"))
   351  		utils.AddFile(filepath.Join("src", "package-lock.json"), []byte("{}"))
   352  
   353  		options := ExecutorOptions{}
   354  		options.DefaultNpmRegistry = "foo.bar"
   355  
   356  		exec := &Execute{
   357  			Utils:   &utils,
   358  			Options: options,
   359  		}
   360  		err := exec.CreateBOM([]string{"package.json", filepath.Join("src", "package.json")})
   361  		cycloneDxNpmInstallParams := []string{"install", "--no-save", "@cyclonedx/cyclonedx-npm@1.11.0", "--prefix", "./tmp"}
   362  		cycloneDxNpmRunParams := []string{
   363  			"--output-format",
   364  			"XML",
   365  			"--spec-version",
   366  			cycloneDxSchemaVersion,
   367  			"--output-file",
   368  		}
   369  
   370  		if assert.NoError(t, err) {
   371  			if assert.Equal(t, 3, len(utils.execRunner.Calls)) {
   372  				assert.Equal(t, mock.ExecCall{Exec: "npm", Params: cycloneDxNpmInstallParams}, utils.execRunner.Calls[0])
   373  				assert.Equal(t, mock.ExecCall{Exec: "./tmp/node_modules/.bin/cyclonedx-npm", Params: append(cycloneDxNpmRunParams, "bom-npm.xml", "package.json")}, utils.execRunner.Calls[1])
   374  				assert.Equal(t, mock.ExecCall{Exec: "./tmp/node_modules/.bin/cyclonedx-npm", Params: append(cycloneDxNpmRunParams, filepath.Join("src", "bom-npm.xml"), filepath.Join("src", "package.json"))}, utils.execRunner.Calls[2])
   375  			}
   376  
   377  		}
   378  	})
   379  
   380  	t.Run("Create BOM with fallback cyclonedx/bom", func(t *testing.T) {
   381  		utils := newNpmMockUtilsBundle()
   382  		utils.AddFile("package.json", []byte("{\"scripts\": { \"ci-lint\": \"exit 0\" } }"))
   383  		utils.AddFile("package-lock.json", []byte("{}"))
   384  		utils.AddFile(filepath.Join("src", "package.json"), []byte("{\"scripts\": { \"ci-lint\": \"exit 0\" } }"))
   385  		utils.AddFile(filepath.Join("src", "package-lock.json"), []byte("{}"))
   386  		utils.execRunner.ShouldFailOnCommand = map[string]error{"npm install --no-save @cyclonedx/cyclonedx-npm@1.11.0 --prefix ./tmp": fmt.Errorf("failed to install CycloneDX BOM")}
   387  
   388  		options := ExecutorOptions{}
   389  		options.DefaultNpmRegistry = "foo.bar"
   390  
   391  		exec := &Execute{
   392  			Utils:   &utils,
   393  			Options: options,
   394  		}
   395  		err := exec.CreateBOM([]string{"package.json", filepath.Join("src", "package.json")})
   396  		cycloneDxNpmInstallParams := []string{"install", "--no-save", "@cyclonedx/cyclonedx-npm@1.11.0", "--prefix", "./tmp"}
   397  
   398  		cycloneDxBomInstallParams := []string{"install", cycloneDxBomPackageVersion, "--no-save"}
   399  		cycloneDxBomRunParams := []string{
   400  			"cyclonedx-bom",
   401  			"--output",
   402  		}
   403  
   404  		if assert.NoError(t, err) {
   405  			if assert.Equal(t, 4, len(utils.execRunner.Calls)) {
   406  				assert.Equal(t, mock.ExecCall{Exec: "npm", Params: cycloneDxNpmInstallParams}, utils.execRunner.Calls[0])
   407  				assert.Equal(t, mock.ExecCall{Exec: "npm", Params: cycloneDxBomInstallParams}, utils.execRunner.Calls[1])
   408  				assert.Equal(t, mock.ExecCall{Exec: "npx", Params: append(cycloneDxBomRunParams, "bom-npm.xml", ".")}, utils.execRunner.Calls[2])
   409  				assert.Equal(t, mock.ExecCall{Exec: "npx", Params: append(cycloneDxBomRunParams, filepath.Join("src", "bom-npm.xml"), filepath.Join("src"))}, utils.execRunner.Calls[3])
   410  			}
   411  
   412  		}
   413  	})
   414  }