Skip to content

Commit 557816b

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 557816b

3 files changed

Lines changed: 266 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: 260 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1686,3 +1686,263 @@ 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 tests LEGACY BEHAVIOR
1761+
// when dependencies are missing from cache but flag is NOT used.
1762+
// STEP 1: Create npm project and populate cache
1763+
// STEP 2: Delete npm cache (keep node_modules)
1764+
// STEP 3: Run jfrog npm install WITHOUT --fail-on-missing-deps
1765+
// STEP 4: Verify command SUCCEEDS (legacy mode warns but doesn't fail)
1766+
// STEP 5: Verify build-info is still published (warnings don't block build-info)
1767+
// This validates the WARN behavior that was the default before strict flag was added.
1768+
func TestNpmInstallLegacyModeWarnsWithMissingCache(t *testing.T) {
1769+
initNpmTest(t)
1770+
defer cleanNpmTest(t)
1771+
1772+
buildName := "npm-legacy-warn-test"
1773+
buildNumber := "1"
1774+
1775+
inttestutils.DeleteBuild(serverDetails.ArtifactoryUrl, buildName, artHttpDetails)
1776+
defer inttestutils.DeleteBuild(serverDetails.ArtifactoryUrl, buildName, artHttpDetails)
1777+
1778+
wd, err := os.Getwd()
1779+
require.NoError(t, err)
1780+
1781+
// STEP 1: Create npm project and populate cache
1782+
npmPath := initNpmProjectTest(t)
1783+
chdirCallBack := clientTestUtils.ChangeDirWithCallback(t, wd, npmPath)
1784+
defer chdirCallBack()
1785+
1786+
// First npm install to populate cache
1787+
err = runJfrogCliWithoutAssertion("npm", "install")
1788+
assert.NoError(t, err, "Initial npm install should succeed to populate cache")
1789+
1790+
// STEP 2: Delete npm cache (but keep node_modules)
1791+
// NOTE: Get the ACTUAL npm cache path (may be custom, not default ~/.npm)
1792+
cacheCmd := exec.Command("npm", "get", "cache")
1793+
cacheDirBytes, err := cacheCmd.Output()
1794+
assert.NoError(t, err, "Failed to get npm cache directory")
1795+
cacheDir := strings.TrimSpace(string(cacheDirBytes))
1796+
npmCachePath := filepath.Join(cacheDir, "_cacache")
1797+
if cacheStat, err := os.Stat(npmCachePath); err == nil && cacheStat.IsDir() {
1798+
err = os.RemoveAll(npmCachePath)
1799+
assert.NoError(t, err, "Failed to delete npm cache directory")
1800+
}
1801+
1802+
// STEP 3: Run jfrog npm install WITHOUT --fail-on-missing-deps
1803+
// Legacy behavior: should WARN about missing deps but SUCCEED
1804+
err = runJfrogCliWithoutAssertion("npm", "install",
1805+
"--build-name="+buildName,
1806+
"--build-number="+buildNumber)
1807+
// Note: We can't easily assert the warning message was logged in e2e tests,
1808+
// but we verify that despite missing cache, the command SUCCEEDS (not FAILS)
1809+
1810+
// STEP 4 & 5: Verify SUCCESS (legacy mode doesn't fail on missing deps)
1811+
assert.NoError(t, err,
1812+
"npm install WITHOUT --fail-on-missing-deps should SUCCEED even with missing cache (legacy behavior)")
1813+
1814+
// This test verifies: missing cache WITHOUT strict flag → command SUCCEEDS + WARNS but still publishes build-info
1815+
clientTestUtils.ChangeDirAndAssert(t, wd)
1816+
1817+
// Verify build publish succeeds (legacy mode allows partial build-info)
1818+
publishErr := artifactoryCli.Exec("bp", buildName, buildNumber)
1819+
assert.NoError(t, publishErr, "Build publish should SUCCEED even when some deps are missing (legacy mode)")
1820+
1821+
// Verify build-info was published to Artifactory (even with warnings about missing deps)
1822+
publishedBuildInfo, found, err := tests.GetBuildInfo(serverDetails, buildName, buildNumber)
1823+
assert.NoError(t, err)
1824+
assert.True(t, found, "Build info should be published to Artifactory even with legacy warn mode (deps won't be included but build-info is)")
1825+
assert.NotNil(t, publishedBuildInfo)
1826+
}
1827+
1828+
// TestNpmInstallWithFailOnMissingDepsFlag tests npm install with the --fail-on-missing-deps
1829+
// flag enabled. When all dependencies are available, this should succeed.
1830+
// STEP 1: Initialize test environment
1831+
// STEP 2: Create npm project with dependencies
1832+
// STEP 3: Run "jfrog npm install --build-name=X --build-number=Y --fail-on-missing-deps"
1833+
// STEP 4: Verify command succeeds (all deps available in npm cache)
1834+
// STEP 5: Verify build-info was published with all dependencies
1835+
func TestNpmInstallWithFailOnMissingDepsFlag(t *testing.T) {
1836+
initNpmTest(t) // STEP 1: Initialize test with mock Artifactory
1837+
defer cleanNpmTest(t)
1838+
1839+
buildName := "npm-strict-test"
1840+
buildNumber := "1"
1841+
1842+
// STEP 1 (continued): Clean old build if exists
1843+
inttestutils.DeleteBuild(serverDetails.ArtifactoryUrl, buildName, artHttpDetails)
1844+
defer inttestutils.DeleteBuild(serverDetails.ArtifactoryUrl, buildName, artHttpDetails)
1845+
1846+
wd, err := os.Getwd()
1847+
require.NoError(t, err)
1848+
1849+
// STEP 2: Setup npm project in temporary directory
1850+
npmPath := initNpmProjectTest(t)
1851+
chdirCallBack := clientTestUtils.ChangeDirWithCallback(t, wd, npmPath)
1852+
defer chdirCallBack()
1853+
1854+
// STEP 3: Run npm install with strict mode enabled (all deps must be available)
1855+
// With a normal npm project, this should succeed (all deps available)
1856+
runJfrogCli(t, "npm", "install",
1857+
"--build-name="+buildName,
1858+
"--build-number="+buildNumber,
1859+
"--fail-on-missing-deps")
1860+
1861+
// STEP 4: Verify success - command completed without failing
1862+
clientTestUtils.ChangeDirAndAssert(t, wd)
1863+
1864+
// STEP 5: Verify build publish succeeds (publishes local build-info to Artifactory)
1865+
publishErr := artifactoryCli.Exec("bp", buildName, buildNumber)
1866+
assert.NoError(t, publishErr, "Build publish should SUCCEED when strict mode succeeds and all deps are available")
1867+
1868+
// STEP 6: Verify build-info was published to Artifactory (strict mode didn't prevent it)
1869+
publishedBuildInfo, found, err := tests.GetBuildInfo(serverDetails, buildName, buildNumber)
1870+
assert.NoError(t, err)
1871+
assert.True(t, found, "Build info should be published to Artifactory when using --fail-on-missing-deps with available deps")
1872+
assert.NotNil(t, publishedBuildInfo)
1873+
}
1874+
1875+
// TestNpmInstallFailsWithMissingCacheStrict tests the FAILURE SCENARIO
1876+
// when npm cache is missing but node_modules exist.
1877+
// This reproduces real-world scenarios: CI cache cleared, network issues, disk space reclaimed.
1878+
// STEP 1: Create npm project and run npm install (populate cache + node_modules)
1879+
// STEP 2: Delete npm cache (keep node_modules)
1880+
// STEP 3: Run jfrog npm install with --fail-on-missing-deps
1881+
// STEP 4: npm install succeeds (packages in node_modules), but strict mode fails (cache missing)
1882+
// STEP 5: Verify error occurs and message mentions missing dependencies
1883+
func TestNpmInstallFailsWithMissingCacheStrict(t *testing.T) {
1884+
initNpmTest(t)
1885+
defer cleanNpmTest(t)
1886+
1887+
buildName := "npm-missing-cache-test"
1888+
buildNumber := "1"
1889+
1890+
inttestutils.DeleteBuild(serverDetails.ArtifactoryUrl, buildName, artHttpDetails)
1891+
defer inttestutils.DeleteBuild(serverDetails.ArtifactoryUrl, buildName, artHttpDetails)
1892+
1893+
wd, err := os.Getwd()
1894+
require.NoError(t, err)
1895+
1896+
// STEP 1: Create npm project and populate cache
1897+
npmPath := initNpmProjectTest(t)
1898+
chdirCallBack := clientTestUtils.ChangeDirWithCallback(t, wd, npmPath)
1899+
defer chdirCallBack()
1900+
1901+
// Run npm install to populate both node_modules AND cache
1902+
err = runJfrogCliWithoutAssertion("npm", "install")
1903+
assert.NoError(t, err, "Initial npm install should succeed to populate cache")
1904+
1905+
// STEP 2: Delete npm cache (but keep node_modules)
1906+
// This simulates real-world cache loss scenarios:
1907+
// - CI cache cleared between builds
1908+
// - Network interruption during cache sync
1909+
// - Disk space reclaimed
1910+
// NOTE: Get the ACTUAL npm cache path (may be custom, not default ~/.npm)
1911+
cacheCmd := exec.Command("npm", "get", "cache")
1912+
cacheDirBytes, err := cacheCmd.Output()
1913+
assert.NoError(t, err, "Failed to get npm cache directory")
1914+
cacheDir := strings.TrimSpace(string(cacheDirBytes))
1915+
npmCachePath := filepath.Join(cacheDir, "_cacache")
1916+
if cacheStat, err := os.Stat(npmCachePath); err == nil && cacheStat.IsDir() {
1917+
err = os.RemoveAll(npmCachePath)
1918+
assert.NoError(t, err, "Failed to delete npm cache directory")
1919+
}
1920+
1921+
// STEP 3: Run jfrog npm install with strict mode
1922+
// npm install will SUCCEED (packages already in node_modules)
1923+
// But CalculateNpmDependenciesList will find cache empty
1924+
// Handler will FAIL (strict mode with missing cache)
1925+
err = runJfrogCliWithoutAssertion("npm", "install",
1926+
"--build-name="+buildName,
1927+
"--build-number="+buildNumber,
1928+
"--fail-on-missing-deps")
1929+
1930+
// STEP 4 & 5: Verify FAILURE
1931+
assert.Error(t, err,
1932+
"npm install with --fail-on-missing-deps should FAIL when cache is missing")
1933+
assert.Contains(t, err.Error(), "dependencies will not be included",
1934+
"Error message should mention missing dependencies in build-info")
1935+
1936+
// STEP 6: Verify build-info was NOT published (error thrown before SaveBuildInfo)
1937+
clientTestUtils.ChangeDirAndAssert(t, wd)
1938+
1939+
publishedBuildInfo, found, err := tests.GetBuildInfo(serverDetails, buildName, buildNumber)
1940+
assert.NoError(t, err)
1941+
assert.False(t, found, "Build info should NOT be published when strict mode fails")
1942+
assert.Nil(t, publishedBuildInfo, "Build info should be nil when command fails")
1943+
1944+
// STEP 7: Verify that `jf rt bp` (build publish) would fail because there's no build-info
1945+
// This proves that strict mode prevented build-info collection completely
1946+
publishErr := artifactoryCli.Exec("bp", buildName, buildNumber)
1947+
assert.Error(t, publishErr, "Build publish should FAIL because no build-info was collected (strict mode prevented it)")
1948+
}

0 commit comments

Comments
 (0)