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 }