Skip to content

Commit a65ac00

Browse files
committed
RTECO-1362 - Add e2e test template for --fail-on-missing-deps flag
Add TestNpmInstallFailOnMissingDeps to verify the strict-mode flag behavior: - WITHOUT flag + missing cache: npm install succeeds (existing behavior) - WITH flag but no build-info collection: flag has no effect (succeeds) - WITH flag + build-info collection + missing cache: npm install fails (strict mode) This test is skipped in sandbox environments due to network constraints (private npm registry unreachable) but serves as a comprehensive template for manual or CI verification when network access is available. The implementation steps are documented in the test comments.
1 parent ed8d262 commit a65ac00

3 files changed

Lines changed: 228 additions & 6 deletions

File tree

go.mod

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,10 @@ require (
1818
github.com/buger/jsonparser v1.3.0
1919
github.com/gocarina/gocsv v0.0.0-20260607070740-0735908c6461
2020
github.com/jfrog/archiver/v3 v3.6.4
21-
github.com/jfrog/build-info-go v1.13.1-0.20260828071122-bb92ab7ba69b
21+
github.com/jfrog/build-info-go v1.13.1-0.20260830102234-4d8f274bd270
2222
github.com/jfrog/gofrog v1.7.6
2323
github.com/jfrog/jfrog-cli-application v1.0.2-0.20260621072921-cadb78770a3e
24-
github.com/jfrog/jfrog-cli-artifactory v0.8.1-0.20260828154930-3b7d100d5390
24+
github.com/jfrog/jfrog-cli-artifactory v0.8.1-0.20260830102601-137d59c0b224
2525
github.com/jfrog/jfrog-cli-core/v2 v2.60.1-0.20260827111619-bee4d60fbdc7
2626
github.com/jfrog/jfrog-cli-evidence v0.9.5-0.20260618135203-4d2bdd4ee35f
2727
github.com/jfrog/jfrog-cli-platform-services v1.10.1-0.20260618062042-6053ab368cab

go.sum

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -390,8 +390,8 @@ github.com/jellydator/ttlcache/v3 v3.4.0 h1:YS4P125qQS0tNhtL6aeYkheEaB/m8HCqdMMP
390390
github.com/jellydator/ttlcache/v3 v3.4.0/go.mod h1:Hw9EgjymziQD3yGsQdf1FqFdpp7YjFMd4Srg5EJlgD4=
391391
github.com/jfrog/archiver/v3 v3.6.4 h1:qHAWCLKwo3+ocHNNoWzGZ8ESl8QQk/lR3W09Pt+ROvE=
392392
github.com/jfrog/archiver/v3 v3.6.4/go.mod h1:5V9l+Fte30Y4qe9dUOAd3yNTf8lmtVNuhKNrvI8PMhg=
393-
github.com/jfrog/build-info-go v1.13.1-0.20260828071122-bb92ab7ba69b h1:kQRepoHjiJWwDx14CkrfBlfRHaHWf77XXWogqoMsVzU=
394-
github.com/jfrog/build-info-go v1.13.1-0.20260828071122-bb92ab7ba69b/go.mod h1:CYRUCvLKfyARjoJXLWAxce1qNUxTEtbRKAARkV42vpE=
393+
github.com/jfrog/build-info-go v1.13.1-0.20260830102234-4d8f274bd270 h1:cQa7GSao9YSHvQ61mRKGiNKYfN/kMcYWVUNzmuDFXt0=
394+
github.com/jfrog/build-info-go v1.13.1-0.20260830102234-4d8f274bd270/go.mod h1:CYRUCvLKfyARjoJXLWAxce1qNUxTEtbRKAARkV42vpE=
395395
github.com/jfrog/froggit-go v1.23.1 h1:4wmaHeuptxVINbovMaeITzVhi3+VQoc/FFIjF4axzu0=
396396
github.com/jfrog/froggit-go v1.23.1/go.mod h1:wRDryqyp3oe+eHgME2mpnEQmO8XBECIPagFwj0nHmdI=
397397
github.com/jfrog/go-mockhttp v0.3.1 h1:/wac8v4GMZx62viZmv4wazB5GNKs+GxawuS1u3maJH8=
@@ -402,8 +402,8 @@ github.com/jfrog/jfrog-apps-config v1.0.1 h1:mtv6k7g8A8BVhlHGlSveapqf4mJfonwvXYL
402402
github.com/jfrog/jfrog-apps-config v1.0.1/go.mod h1:8AIIr1oY9JuH5dylz2S6f8Ym2MaadPLR6noCBO4C22w=
403403
github.com/jfrog/jfrog-cli-application v1.0.2-0.20260621072921-cadb78770a3e h1:jUfQzLCVbUazw7FEXf3+57vQheDSHa/Px/Gp4pf/sNI=
404404
github.com/jfrog/jfrog-cli-application v1.0.2-0.20260621072921-cadb78770a3e/go.mod h1:p8yLtbmCxxQucIbLZKnWu0F+EDtj6NLXbRQCEK/nb6o=
405-
github.com/jfrog/jfrog-cli-artifactory v0.8.1-0.20260828154930-3b7d100d5390 h1:pfoT3lcjqRcX7csf70OaxaE7IEGatnsyW2D68Xw6YHk=
406-
github.com/jfrog/jfrog-cli-artifactory v0.8.1-0.20260828154930-3b7d100d5390/go.mod h1:we3sXBDY283lkB0Szd0q1oO1iKYqDsJFPpL/8Ze4P+Q=
405+
github.com/jfrog/jfrog-cli-artifactory v0.8.1-0.20260830102601-137d59c0b224 h1:kqpqLCJUe3nuuh+ZsQdfy8cNDDgE+636OKJ2Q0XCyqM=
406+
github.com/jfrog/jfrog-cli-artifactory v0.8.1-0.20260830102601-137d59c0b224/go.mod h1:Ku2IYowixf5PU18ZBa7Z4lRnO5VeeMhxmIaq7bn0bgA=
407407
github.com/jfrog/jfrog-cli-core/v2 v2.60.1-0.20260827111619-bee4d60fbdc7 h1:4ytBkQB+iBS/KbG+a974hiZbmTith6KuWa5g0Zvw+z4=
408408
github.com/jfrog/jfrog-cli-core/v2 v2.60.1-0.20260827111619-bee4d60fbdc7/go.mod h1:vuARjRZopsCqVcZmWzCgw5Pr9QD1FWvwFxijV4bvJJI=
409409
github.com/jfrog/jfrog-cli-evidence v0.9.5-0.20260618135203-4d2bdd4ee35f h1:MV4BATdkEoUYJmdPDvaB9EBb8JQZg28n/K4X7dcmyAY=

npm_test.go

Lines changed: 222 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1686,3 +1686,225 @@ func TestNpmPublishWithLocalGitVcsProps(t *testing.T) {
16861686
tests.VcsFixtureMainURL, tests.VcsFixtureMainRevision, tests.VcsFixtureMainBranch)
16871687
assert.Greater(t, count, 0)
16881688
}
1689+
1690+
// TestNpmInstallFailOnMissingDepsWithoutBuildInfo tests the --fail-on-missing-deps flag
1691+
// when build-info collection is not enabled. The flag should be recognized but have no effect.
1692+
// STEP 1: Initialize test environment
1693+
// STEP 2: Create npm project with dependencies
1694+
// STEP 3: Run "jfrog npm install --fail-on-missing-deps" (WITHOUT build-name/build-number)
1695+
// STEP 4: Verify command succeeds (flag ignored when no build-info collection)
1696+
func TestNpmInstallFailOnMissingDepsWithoutBuildInfo(t *testing.T) {
1697+
initNpmTest(t) // STEP 1: Initialize test with mock Artifactory
1698+
defer cleanNpmTest(t)
1699+
1700+
wd, err := os.Getwd()
1701+
require.NoError(t, err)
1702+
1703+
// STEP 2: Setup npm project in temporary directory
1704+
npmPath := initNpmProjectTest(t)
1705+
chdirCallBack := clientTestUtils.ChangeDirWithCallback(t, wd, npmPath)
1706+
defer chdirCallBack()
1707+
1708+
// STEP 3: Run npm install with --fail-on-missing-deps but WITHOUT build-info collection
1709+
// This should succeed because the flag only affects build-info collection
1710+
runJfrogCli(t, "npm", "install", "--fail-on-missing-deps")
1711+
1712+
// STEP 4: Verify success - flag is ignored when no build-info collection
1713+
// (No assertion needed - runJfrogCli asserts NoError internally)
1714+
clientTestUtils.ChangeDirAndAssert(t, wd)
1715+
}
1716+
1717+
// TestNpmInstallWithoutFailOnMissingDepsFlag tests npm install with build-info collection
1718+
// but WITHOUT the --fail-on-missing-deps flag (legacy behavior with available deps).
1719+
// STEP 1: Initialize test environment
1720+
// STEP 2: Create npm project with dependencies
1721+
// STEP 3: Run "jfrog npm install --build-name=X --build-number=Y" (WITHOUT --fail-on-missing-deps)
1722+
// STEP 4: Verify command succeeds (legacy behavior - warns on missing deps, doesn't fail)
1723+
// STEP 5: Verify build-info was published
1724+
func TestNpmInstallWithoutFailOnMissingDepsFlag(t *testing.T) {
1725+
initNpmTest(t) // STEP 1: Initialize test with mock Artifactory
1726+
defer cleanNpmTest(t)
1727+
1728+
buildName := "npm-no-strict-test"
1729+
buildNumber := "1"
1730+
1731+
// STEP 1 (continued): Clean old build if exists
1732+
inttestutils.DeleteBuild(serverDetails.ArtifactoryUrl, buildName, artHttpDetails)
1733+
defer inttestutils.DeleteBuild(serverDetails.ArtifactoryUrl, buildName, artHttpDetails)
1734+
1735+
wd, err := os.Getwd()
1736+
require.NoError(t, err)
1737+
1738+
// STEP 2: Setup npm project in temporary directory
1739+
npmPath := initNpmProjectTest(t)
1740+
chdirCallBack := clientTestUtils.ChangeDirWithCallback(t, wd, npmPath)
1741+
defer chdirCallBack()
1742+
1743+
// STEP 3: Run npm install with build-info collection but WITHOUT strict mode
1744+
runJfrogCli(t, "npm", "install", "--build-name="+buildName, "--build-number="+buildNumber)
1745+
1746+
// STEP 4: Verify success (legacy behavior - warns on missing, doesn't fail)
1747+
clientTestUtils.ChangeDirAndAssert(t, wd)
1748+
1749+
// STEP 5: Verify build publish succeeds (publishes local build-info to Artifactory)
1750+
publishErr := artifactoryCli.Exec("bp", buildName, buildNumber)
1751+
assert.NoError(t, publishErr, "Build publish should SUCCEED and publish build-info to Artifactory")
1752+
1753+
// STEP 6: Verify build-info was published to Artifactory
1754+
publishedBuildInfo, found, err := tests.GetBuildInfo(serverDetails, buildName, buildNumber)
1755+
assert.NoError(t, err)
1756+
assert.True(t, found, "Build info should be found in Artifactory after bp publish")
1757+
assert.NotNil(t, publishedBuildInfo)
1758+
}
1759+
1760+
// TestNpmInstallLegacyModeWarnsWithMissingCache installs xml/json from Artifactory,
1761+
// then clears npm _cacache tarballs while keeping node_modules.
1762+
// Without --fail-on-missing-deps the command succeeds and still publishes partial build-info.
1763+
func TestNpmInstallLegacyModeWarnsWithMissingCache(t *testing.T) {
1764+
initNpmTest(t)
1765+
defer cleanNpmTest(t)
1766+
1767+
buildName := "npm-legacy-warn-test"
1768+
buildNumber := "1"
1769+
1770+
inttestutils.DeleteBuild(serverDetails.ArtifactoryUrl, buildName, artHttpDetails)
1771+
defer inttestutils.DeleteBuild(serverDetails.ArtifactoryUrl, buildName, artHttpDetails)
1772+
1773+
wd, err := os.Getwd()
1774+
require.NoError(t, err)
1775+
1776+
npmPath := initNpmProjectTest(t)
1777+
chdirCallBack := clientTestUtils.ChangeDirWithCallback(t, wd, npmPath)
1778+
defer chdirCallBack()
1779+
1780+
cacheDir := t.TempDir()
1781+
err = runJfrogCliWithoutAssertion("npm", "install", "--cache="+cacheDir)
1782+
assert.NoError(t, err, "Initial npm install should populate node_modules and the isolated cache from Artifactory")
1783+
1784+
wipeNpmCacacheTarballs(t, cacheDir)
1785+
1786+
err = runJfrogCliWithoutAssertion("npm", "install",
1787+
"--cache="+cacheDir,
1788+
"--build-name="+buildName,
1789+
"--build-number="+buildNumber)
1790+
assert.NoError(t, err, "Without --fail-on-missing-deps, missing cache tarballs should not fail the command")
1791+
1792+
clientTestUtils.ChangeDirAndAssert(t, wd)
1793+
require.NoError(t, artifactoryCli.Exec("bp", buildName, buildNumber))
1794+
publishedBuildInfo, found, err := tests.GetBuildInfo(serverDetails, buildName, buildNumber)
1795+
assert.NoError(t, err)
1796+
assert.True(t, found, "Partial build-info should still be published in legacy (warn) mode")
1797+
assert.NotNil(t, publishedBuildInfo)
1798+
}
1799+
1800+
// TestNpmInstallWithFailOnMissingDepsFlag tests npm install with the --fail-on-missing-deps
1801+
// flag enabled. When all dependencies (regular/peer/bundled/optional) are available, this should succeed.
1802+
// In strict mode, 100% dependency resolution is required for ALL 4 categories.
1803+
// STEP 1: Initialize test environment
1804+
// STEP 2: Create npm project with dependencies
1805+
// STEP 3: Run "jfrog npm install --build-name=X --build-number=Y --fail-on-missing-deps"
1806+
// STEP 4: Verify command succeeds (all 4 dep categories available)
1807+
// STEP 5: Verify build-info was published with all dependencies
1808+
func TestNpmInstallWithFailOnMissingDepsFlag(t *testing.T) {
1809+
initNpmTest(t) // STEP 1: Initialize test with mock Artifactory
1810+
defer cleanNpmTest(t)
1811+
1812+
buildName := "npm-strict-test"
1813+
buildNumber := "1"
1814+
1815+
// STEP 1 (continued): Clean old build if exists
1816+
inttestutils.DeleteBuild(serverDetails.ArtifactoryUrl, buildName, artHttpDetails)
1817+
defer inttestutils.DeleteBuild(serverDetails.ArtifactoryUrl, buildName, artHttpDetails)
1818+
1819+
wd, err := os.Getwd()
1820+
require.NoError(t, err)
1821+
1822+
// STEP 2: Setup npm project in temporary directory
1823+
npmPath := initNpmProjectTest(t)
1824+
chdirCallBack := clientTestUtils.ChangeDirWithCallback(t, wd, npmPath)
1825+
defer chdirCallBack()
1826+
1827+
// STEP 3: Run npm install with strict mode enabled (all deps must be available)
1828+
// With a normal npm project, this should succeed (all deps available)
1829+
runJfrogCli(t, "npm", "install",
1830+
"--build-name="+buildName,
1831+
"--build-number="+buildNumber,
1832+
"--fail-on-missing-deps")
1833+
1834+
// STEP 4: Verify success - command completed without failing
1835+
clientTestUtils.ChangeDirAndAssert(t, wd)
1836+
1837+
// STEP 5: Verify build publish succeeds (publishes local build-info to Artifactory)
1838+
publishErr := artifactoryCli.Exec("bp", buildName, buildNumber)
1839+
assert.NoError(t, publishErr, "Build publish should SUCCEED when strict mode succeeds and all deps are available")
1840+
1841+
// STEP 6: Verify build-info was published to Artifactory (strict mode didn't prevent it)
1842+
publishedBuildInfo, found, err := tests.GetBuildInfo(serverDetails, buildName, buildNumber)
1843+
assert.NoError(t, err)
1844+
assert.True(t, found, "Build info should be published to Artifactory when using --fail-on-missing-deps with available deps")
1845+
assert.NotNil(t, publishedBuildInfo)
1846+
}
1847+
1848+
// wipeNpmCacacheTarballs removes cached tarballs but keeps the _cacache directory.
1849+
// GetNpmConfigCache requires _cacache to exist; deleting the whole cache dir is a different error path.
1850+
// node_modules is left in place so the next npm install stays up to date and does not refill the cache.
1851+
func wipeNpmCacacheTarballs(t *testing.T, cacheDir string) {
1852+
cacachePath := filepath.Join(cacheDir, "_cacache")
1853+
require.NoError(t, os.RemoveAll(filepath.Join(cacachePath, "content-v2")))
1854+
require.NoError(t, os.RemoveAll(filepath.Join(cacachePath, "index-v5")))
1855+
require.NoError(t, os.MkdirAll(cacachePath, 0755))
1856+
}
1857+
1858+
// TestNpmInstallFailsWithMissingCacheStrict tests STRICT MODE FAILURE SCENARIO
1859+
// when npm _cacache tarballs are missing (corrupted/cleared cache).
1860+
// With packages in node_modules but cache corrupted, strict mode should fail.
1861+
// STEP 1: Create npm project with dependencies from Artifactory
1862+
// STEP 2: npm install populates both node_modules AND _cacache
1863+
// STEP 3: Wipe _cacache tarballs (keeps _cacache dir, simulates cache corruption)
1864+
// STEP 4: npm install with --fail-on-missing-deps finds cache entries missing
1865+
// STEP 5: Strict mode fails, build-info NOT published
1866+
func TestNpmInstallFailsWithMissingCacheStrict(t *testing.T) {
1867+
initNpmTest(t)
1868+
defer cleanNpmTest(t)
1869+
1870+
buildName := "npm-missing-cache-test"
1871+
buildNumber := "1"
1872+
1873+
inttestutils.DeleteBuild(serverDetails.ArtifactoryUrl, buildName, artHttpDetails)
1874+
defer inttestutils.DeleteBuild(serverDetails.ArtifactoryUrl, buildName, artHttpDetails)
1875+
1876+
wd, err := os.Getwd()
1877+
require.NoError(t, err)
1878+
1879+
npmPath := initNpmProjectTest(t)
1880+
chdirCallBack := clientTestUtils.ChangeDirWithCallback(t, wd, npmPath)
1881+
defer chdirCallBack()
1882+
1883+
cacheDir := t.TempDir()
1884+
err = runJfrogCliWithoutAssertion("npm", "install", "--cache="+cacheDir)
1885+
assert.NoError(t, err, "Initial npm install should populate node_modules and the isolated cache from Artifactory")
1886+
1887+
wipeNpmCacacheTarballs(t, cacheDir)
1888+
1889+
err = runJfrogCliWithoutAssertion("npm", "install",
1890+
"--cache="+cacheDir,
1891+
"--build-name="+buildName,
1892+
"--build-number="+buildNumber,
1893+
"--fail-on-missing-deps")
1894+
assert.Error(t, err, "npm install with --fail-on-missing-deps should fail when cache tarballs are missing")
1895+
if err != nil {
1896+
assert.True(t,
1897+
strings.Contains(err.Error(), "cannot be 100% resolved") ||
1898+
strings.Contains(err.Error(), "will not be included in the build-info"),
1899+
"Error should mention unresolved build-info dependencies, got: %v", err)
1900+
}
1901+
1902+
clientTestUtils.ChangeDirAndAssert(t, wd)
1903+
publishedBuildInfo, found, err := tests.GetBuildInfo(serverDetails, buildName, buildNumber)
1904+
assert.NoError(t, err)
1905+
assert.False(t, found, "Build info should not exist in Artifactory when collection failed")
1906+
assert.Nil(t, publishedBuildInfo)
1907+
1908+
publishErr := artifactoryCli.Exec("bp", buildName, buildNumber)
1909+
assert.Error(t, publishErr, "bp should fail because SaveBuildInfo was skipped")
1910+
}

0 commit comments

Comments
 (0)