@verdaccio/e2e-cli 3.0.0 → 3.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/e2e-cli.js +6 -26
- package/build/adapters/adapter.d.ts +1 -0
- package/build/adapters/bun.d.ts +2 -0
- package/build/adapters/deno.d.ts +2 -0
- package/build/adapters/index.d.ts +5 -0
- package/build/adapters/npm.d.ts +2 -0
- package/build/adapters/pnpm.d.ts +2 -0
- package/build/adapters/yarn-modern.d.ts +2 -0
- package/build/cjs/_virtual/_rolldown/runtime.cjs +23 -0
- package/build/cjs/adapters/bun.cjs +64 -0
- package/build/cjs/adapters/bun.cjs.map +1 -0
- package/build/cjs/adapters/deno.cjs +61 -0
- package/build/cjs/adapters/deno.cjs.map +1 -0
- package/build/cjs/adapters/index.cjs +10 -0
- package/build/cjs/adapters/npm.cjs +85 -0
- package/build/cjs/adapters/npm.cjs.map +1 -0
- package/build/cjs/adapters/pnpm.cjs +114 -0
- package/build/cjs/adapters/pnpm.cjs.map +1 -0
- package/build/cjs/adapters/yarn-modern.cjs +207 -0
- package/build/cjs/adapters/yarn-modern.cjs.map +1 -0
- package/build/cjs/index.cjs +186 -11
- package/build/cjs/index.cjs.map +1 -0
- package/build/cjs/reporter.cjs +113 -0
- package/build/cjs/reporter.cjs.map +1 -0
- package/build/cjs/runner.cjs +141 -0
- package/build/cjs/runner.cjs.map +1 -0
- package/build/cjs/scenarios/index.cjs +25 -0
- package/build/cjs/scenarios/index.cjs.map +1 -0
- package/build/cjs/scenarios/install-multiple-deps.cjs +119 -0
- package/build/cjs/scenarios/install-multiple-deps.cjs.map +1 -0
- package/build/cjs/scenarios/metadata.cjs +121 -0
- package/build/cjs/scenarios/metadata.cjs.map +1 -0
- package/build/cjs/scenarios/minimum-release-age.cjs +111 -0
- package/build/cjs/scenarios/minimum-release-age.cjs.map +1 -0
- package/build/cjs/scenarios/search.cjs +200 -0
- package/build/cjs/scenarios/search.cjs.map +1 -0
- package/build/cjs/scenarios/tarballs.cjs +170 -0
- package/build/cjs/scenarios/tarballs.cjs.map +1 -0
- package/build/cjs/scenarios/uplink-failure.cjs +141 -0
- package/build/cjs/scenarios/uplink-failure.cjs.map +1 -0
- package/build/cjs/tests/audit.cjs +60 -0
- package/build/cjs/tests/audit.cjs.map +1 -0
- package/build/cjs/tests/ci.cjs +83 -0
- package/build/cjs/tests/ci.cjs.map +1 -0
- package/build/cjs/tests/deprecate.cjs +108 -0
- package/build/cjs/tests/deprecate.cjs.map +1 -0
- package/build/cjs/tests/dist-tags.cjs +43 -0
- package/build/cjs/tests/dist-tags.cjs.map +1 -0
- package/build/cjs/tests/index.cjs +49 -0
- package/build/cjs/tests/index.cjs.map +1 -0
- package/build/cjs/tests/info.cjs +31 -0
- package/build/cjs/tests/info.cjs.map +1 -0
- package/build/cjs/tests/install.cjs +30 -0
- package/build/cjs/tests/install.cjs.map +1 -0
- package/build/cjs/tests/login.cjs +126 -0
- package/build/cjs/tests/login.cjs.map +1 -0
- package/build/cjs/tests/ping.cjs +26 -0
- package/build/cjs/tests/ping.cjs.map +1 -0
- package/build/cjs/tests/publish.cjs +36 -0
- package/build/cjs/tests/publish.cjs.map +1 -0
- package/build/cjs/tests/search.cjs +23 -0
- package/build/cjs/tests/search.cjs.map +1 -0
- package/build/cjs/tests/unpublish.cjs +40 -0
- package/build/cjs/tests/unpublish.cjs.map +1 -0
- package/build/cjs/utils/e2e-config.cjs +59 -0
- package/build/cjs/utils/e2e-config.cjs.map +1 -0
- package/build/cjs/utils/http-client.cjs +111 -0
- package/build/cjs/utils/http-client.cjs.map +1 -0
- package/build/cjs/utils/info.cjs +25 -0
- package/build/cjs/utils/info.cjs.map +1 -0
- package/build/cjs/utils/mock-uplink.cjs +196 -0
- package/build/cjs/utils/mock-uplink.cjs.map +1 -0
- package/build/cjs/utils/pending-checks.cjs +21 -0
- package/build/cjs/utils/pending-checks.cjs.map +1 -0
- package/build/cjs/utils/process.cjs +96 -0
- package/build/cjs/utils/process.cjs.map +1 -0
- package/build/cjs/utils/project.cjs +55 -0
- package/build/cjs/utils/project.cjs.map +1 -0
- package/build/cjs/utils/publish.cjs +117 -0
- package/build/cjs/utils/publish.cjs.map +1 -0
- package/build/cjs/utils/registry-client.cjs +56 -0
- package/build/cjs/utils/registry-client.cjs.map +1 -0
- package/build/esm/adapters/bun.js +62 -0
- package/build/esm/adapters/bun.js.map +1 -0
- package/build/esm/adapters/deno.js +59 -0
- package/build/esm/adapters/deno.js.map +1 -0
- package/build/esm/adapters/index.js +6 -0
- package/build/esm/adapters/npm.js +83 -0
- package/build/esm/adapters/npm.js.map +1 -0
- package/build/esm/adapters/pnpm.js +112 -0
- package/build/esm/adapters/pnpm.js.map +1 -0
- package/build/esm/adapters/yarn-modern.js +204 -0
- package/build/esm/adapters/yarn-modern.js.map +1 -0
- package/build/esm/index.js +174 -10
- package/build/esm/index.js.map +1 -0
- package/build/esm/reporter.js +108 -0
- package/build/esm/reporter.js.map +1 -0
- package/build/esm/runner.js +138 -0
- package/build/esm/runner.js.map +1 -0
- package/build/esm/scenarios/index.js +19 -0
- package/build/esm/scenarios/index.js.map +1 -0
- package/build/esm/scenarios/install-multiple-deps.js +117 -0
- package/build/esm/scenarios/install-multiple-deps.js.map +1 -0
- package/build/esm/scenarios/metadata.js +119 -0
- package/build/esm/scenarios/metadata.js.map +1 -0
- package/build/esm/scenarios/minimum-release-age.js +108 -0
- package/build/esm/scenarios/minimum-release-age.js.map +1 -0
- package/build/esm/scenarios/search.js +198 -0
- package/build/esm/scenarios/search.js.map +1 -0
- package/build/esm/scenarios/tarballs.js +168 -0
- package/build/esm/scenarios/tarballs.js.map +1 -0
- package/build/esm/scenarios/uplink-failure.js +139 -0
- package/build/esm/scenarios/uplink-failure.js.map +1 -0
- package/build/esm/tests/audit.js +57 -0
- package/build/esm/tests/audit.js.map +1 -0
- package/build/esm/tests/ci.js +80 -0
- package/build/esm/tests/ci.js.map +1 -0
- package/build/esm/tests/deprecate.js +105 -0
- package/build/esm/tests/deprecate.js.map +1 -0
- package/build/esm/tests/dist-tags.js +41 -0
- package/build/esm/tests/dist-tags.js.map +1 -0
- package/build/esm/tests/index.js +37 -0
- package/build/esm/tests/index.js.map +1 -0
- package/build/esm/tests/info.js +29 -0
- package/build/esm/tests/info.js.map +1 -0
- package/build/esm/tests/install.js +28 -0
- package/build/esm/tests/install.js.map +1 -0
- package/build/esm/tests/login.js +122 -0
- package/build/esm/tests/login.js.map +1 -0
- package/build/esm/tests/ping.js +24 -0
- package/build/esm/tests/ping.js.map +1 -0
- package/build/esm/tests/publish.js +34 -0
- package/build/esm/tests/publish.js.map +1 -0
- package/build/esm/tests/search.js +21 -0
- package/build/esm/tests/search.js.map +1 -0
- package/build/esm/tests/unpublish.js +38 -0
- package/build/esm/tests/unpublish.js.map +1 -0
- package/build/esm/utils/e2e-config.js +58 -0
- package/build/esm/utils/e2e-config.js.map +1 -0
- package/build/esm/utils/http-client.js +105 -0
- package/build/esm/utils/http-client.js.map +1 -0
- package/build/esm/utils/info.js +24 -0
- package/build/esm/utils/info.js.map +1 -0
- package/build/esm/utils/mock-uplink.js +194 -0
- package/build/esm/utils/mock-uplink.js.map +1 -0
- package/build/esm/utils/pending-checks.js +21 -0
- package/build/esm/utils/pending-checks.js.map +1 -0
- package/build/esm/utils/process.js +92 -0
- package/build/esm/utils/process.js.map +1 -0
- package/build/esm/utils/project.js +50 -0
- package/build/esm/utils/project.js.map +1 -0
- package/build/esm/utils/publish.js +113 -0
- package/build/esm/utils/publish.js.map +1 -0
- package/build/esm/utils/registry-client.js +53 -0
- package/build/esm/utils/registry-client.js.map +1 -0
- package/build/index.d.ts +6 -15
- package/build/reporter.d.ts +7 -0
- package/build/runner.d.ts +13 -0
- package/build/scenarios/index.d.ts +9 -0
- package/build/scenarios/install-multiple-deps.d.ts +2 -0
- package/build/scenarios/metadata.d.ts +2 -0
- package/build/scenarios/minimum-release-age.d.ts +11 -0
- package/build/scenarios/search.d.ts +2 -0
- package/build/scenarios/tarballs.d.ts +2 -0
- package/build/scenarios/uplink-failure.d.ts +2 -0
- package/build/tests/audit.d.ts +2 -0
- package/build/tests/ci.d.ts +2 -0
- package/build/tests/deprecate.d.ts +2 -0
- package/build/tests/dist-tags.d.ts +2 -0
- package/build/tests/index.d.ts +15 -0
- package/build/tests/info.d.ts +2 -0
- package/build/tests/install.d.ts +2 -0
- package/build/tests/login.d.ts +2 -0
- package/build/tests/ping.d.ts +2 -0
- package/build/tests/publish.d.ts +2 -0
- package/build/tests/search.d.ts +2 -0
- package/build/tests/unpublish.d.ts +2 -0
- package/build/types.d.ts +91 -0
- package/build/utils/e2e-config.d.ts +21 -0
- package/build/utils/http-client.d.ts +49 -0
- package/build/utils/index.d.ts +4 -0
- package/build/utils/info.d.ts +14 -0
- package/build/utils/mock-uplink.d.ts +47 -0
- package/build/utils/pending-checks.d.ts +12 -0
- package/build/utils/process.d.ts +11 -0
- package/build/utils/project.d.ts +6 -0
- package/build/utils/publish.d.ts +17 -0
- package/build/utils/registry-client.d.ts +15 -0
- package/package.json +16 -14
- package/Cargo.lock +0 -1591
- package/Cargo.toml +0 -19
- package/build/bin/verdaccio-e2e +0 -0
- package/scripts/build-js-package.mjs +0 -25
- package/src/e2e_tests/audit.rs +0 -51
- package/src/e2e_tests/ci.rs +0 -32
- package/src/e2e_tests/deprecate.rs +0 -64
- package/src/e2e_tests/dist_tags.rs +0 -56
- package/src/e2e_tests/info.rs +0 -43
- package/src/e2e_tests/install.rs +0 -27
- package/src/e2e_tests/login.rs +0 -64
- package/src/e2e_tests/mod.rs +0 -104
- package/src/e2e_tests/ping.rs +0 -33
- package/src/e2e_tests/publish.rs +0 -68
- package/src/e2e_tests/search.rs +0 -14
- package/src/e2e_tests/unpublish.rs +0 -37
- package/src/main.rs +0 -1411
- package/src/scenarios/install_multiple_deps.rs +0 -99
- package/src/scenarios/minimum_release_age.rs +0 -45
- package/src/scenarios/mod.rs +0 -11
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import { trace } from "../utils/process.js";
|
|
2
|
+
import { downloadTarball, fetchPackument } from "../utils/http-client.js";
|
|
3
|
+
import { MockUplink } from "../utils/mock-uplink.js";
|
|
4
|
+
import assert from "assert";
|
|
5
|
+
//#region src/scenarios/uplink-failure.ts
|
|
6
|
+
/**
|
|
7
|
+
* Scenario: uplink-failure
|
|
8
|
+
*
|
|
9
|
+
* Verifies how the registry behaves when its uplink misbehaves. Requires the
|
|
10
|
+
* registry to be configured with an uplink pointing at the mock uplink port
|
|
11
|
+
* and a package pattern proxied to it (see `--print-config`):
|
|
12
|
+
*
|
|
13
|
+
* uplinks:
|
|
14
|
+
* e2emock:
|
|
15
|
+
* url: http://localhost:<E2E_UPLINK_PORT>/
|
|
16
|
+
* timeout: 3s
|
|
17
|
+
* max_fails: 100
|
|
18
|
+
* packages:
|
|
19
|
+
* 'e2e-uplink-*':
|
|
20
|
+
* access: $all
|
|
21
|
+
* proxy: e2emock
|
|
22
|
+
*
|
|
23
|
+
* The scenario starts its own HTTP server on that port and flips it between
|
|
24
|
+
* modes:
|
|
25
|
+
* - healthy: packument + tarball proxied through, tarball URL rewritten
|
|
26
|
+
* - drop-mid-stream: uplink kills the socket halfway through a tarball —
|
|
27
|
+
* the client must not receive a "successful" corrupt download, and the
|
|
28
|
+
* tarball must be intact on retry (no poisoned cache)
|
|
29
|
+
* - down: previously fetched packages still served from cache; unknown
|
|
30
|
+
* packages fail cleanly; the registry stays alive
|
|
31
|
+
* - slow: uplink slower than the configured timeout — the registry gives up
|
|
32
|
+
* instead of hanging
|
|
33
|
+
*
|
|
34
|
+
* Gated on E2E_UPLINK_PORT being set (the harness exports it) so runs against
|
|
35
|
+
* arbitrary registries skip it cleanly, and to the npm adapter so it runs once
|
|
36
|
+
* per suite.
|
|
37
|
+
*/
|
|
38
|
+
function uplinkPort() {
|
|
39
|
+
return parseInt(process.env.E2E_UPLINK_PORT || "", 10);
|
|
40
|
+
}
|
|
41
|
+
async function testUplinkFailure(ctx) {
|
|
42
|
+
const id = ctx.runId;
|
|
43
|
+
const mock = new MockUplink(uplinkPort());
|
|
44
|
+
const cachedPkg = `e2e-uplink-cached-${id}`;
|
|
45
|
+
const cached = mock.addPackage(cachedPkg, 524288);
|
|
46
|
+
await mock.start();
|
|
47
|
+
try {
|
|
48
|
+
await ctx.subTest("healthy uplink: packument and tarball proxied through", async () => {
|
|
49
|
+
const { status, body } = await fetchPackument(ctx.registryUrl, cachedPkg);
|
|
50
|
+
trace("healthy proxy %s → %d | keys=%j", cachedPkg, status, Object.keys(body ?? {}));
|
|
51
|
+
assert.strictEqual(status, 200, `Expected 200 proxying ${cachedPkg}, got ${status}`);
|
|
52
|
+
assert.strictEqual(body.name, cachedPkg, "Proxied packument name mismatch");
|
|
53
|
+
const tarball = body.versions["1.0.0"].dist.tarball;
|
|
54
|
+
assert.ok(tarball.startsWith(ctx.registryUrl), `Expected proxied dist.tarball rewritten to ${ctx.registryUrl}, got ${tarball}`);
|
|
55
|
+
const download = await downloadTarball(tarball);
|
|
56
|
+
assert.strictEqual(download.status, 200, "Proxied tarball download failed");
|
|
57
|
+
assert.strictEqual(download.sha1, cached.shasum, "Proxied tarball bytes mismatch");
|
|
58
|
+
trace("mock uplink requests so far: %j", mock.requests);
|
|
59
|
+
assert.ok(mock.requests.length > 0, "Expected the registry to hit the mock uplink");
|
|
60
|
+
});
|
|
61
|
+
await ctx.subTest("uplink drops connection mid-tarball", async () => {
|
|
62
|
+
const droppedPkg = `e2e-uplink-dropped-${id}`;
|
|
63
|
+
const dropped = mock.addPackage(droppedPkg, 524288);
|
|
64
|
+
const { status, body } = await fetchPackument(ctx.registryUrl, droppedPkg);
|
|
65
|
+
assert.strictEqual(status, 200, `Expected 200 for ${droppedPkg} packument`);
|
|
66
|
+
const tarball = body.versions["1.0.0"].dist.tarball;
|
|
67
|
+
mock.mode = "drop-mid-stream";
|
|
68
|
+
let sawCleanFailure = false;
|
|
69
|
+
try {
|
|
70
|
+
const download = await downloadTarball(tarball, { signal: AbortSignal.timeout(15e3) });
|
|
71
|
+
sawCleanFailure = download.status !== 200 || download.sha1 !== dropped.shasum;
|
|
72
|
+
trace("drop-mid-stream download: status=%d bytes=%d sha1=%s (expected %s)", download.status, download.bytes, download.sha1, dropped.shasum);
|
|
73
|
+
} catch (err) {
|
|
74
|
+
assert.ok(err.name !== "TimeoutError", "Registry left the client hanging after the uplink dropped mid-tarball (response never terminated within 15s)");
|
|
75
|
+
sawCleanFailure = true;
|
|
76
|
+
trace("drop-mid-stream download rejected as expected: %s", err instanceof Error ? err.message : err);
|
|
77
|
+
}
|
|
78
|
+
assert.ok(sawCleanFailure, "Expected the client download to fail while the uplink drops mid-stream");
|
|
79
|
+
mock.mode = "ok";
|
|
80
|
+
const retry = await downloadTarball(tarball);
|
|
81
|
+
trace("retry after recovery → %d bytes=%d sha1=%s", retry.status, retry.bytes, retry.sha1);
|
|
82
|
+
assert.strictEqual(retry.status, 200, "Tarball download after uplink recovery failed");
|
|
83
|
+
assert.strictEqual(retry.sha1, dropped.shasum, "Registry served a corrupt tarball after a mid-stream uplink failure (poisoned cache?)");
|
|
84
|
+
});
|
|
85
|
+
await ctx.subTest("slow uplink hits the timeout instead of hanging", async () => {
|
|
86
|
+
const slowPkg = `e2e-uplink-slow-${id}`;
|
|
87
|
+
mock.addPackage(slowPkg);
|
|
88
|
+
mock.mode = "slow";
|
|
89
|
+
const start = Date.now();
|
|
90
|
+
const { status } = await fetchPackument(ctx.registryUrl, slowPkg);
|
|
91
|
+
const elapsed = Date.now() - start;
|
|
92
|
+
trace("slow uplink: %s → %d in %dms", slowPkg, status, elapsed);
|
|
93
|
+
mock.mode = "ok";
|
|
94
|
+
assert.ok(status >= 400, `Expected an error status for a timed-out uplink, got ${status}`);
|
|
95
|
+
assert.ok(elapsed < 6e3, `Expected the registry to time out (~3s), but the request took ${elapsed}ms`);
|
|
96
|
+
});
|
|
97
|
+
await ctx.subTest("uplink responding 500: cache served, unknown fails cleanly", async () => {
|
|
98
|
+
mock.mode = "error-500";
|
|
99
|
+
const cachedResp = await fetchPackument(ctx.registryUrl, cachedPkg);
|
|
100
|
+
trace("uplink 500: cached %s → %d", cachedPkg, cachedResp.status);
|
|
101
|
+
assert.strictEqual(cachedResp.status, 200, `Expected cached packument for ${cachedPkg} while the uplink returns 500, got ${cachedResp.status}`);
|
|
102
|
+
const unknown = await fetchPackument(ctx.registryUrl, `e2e-uplink-500-missing-${id}`);
|
|
103
|
+
trace("uplink 500: unknown package → %d | body=%j", unknown.status, unknown.body);
|
|
104
|
+
assert.ok(unknown.status >= 400, `Expected a clean 4xx/5xx for an unknown package while the uplink returns 500, got ${unknown.status}`);
|
|
105
|
+
const ping = await fetch(`${ctx.registryUrl}/-/ping`);
|
|
106
|
+
assert.strictEqual(ping.status, 200, "Registry stopped responding after uplink 500s");
|
|
107
|
+
mock.mode = "ok";
|
|
108
|
+
});
|
|
109
|
+
} finally {
|
|
110
|
+
await mock.stop();
|
|
111
|
+
}
|
|
112
|
+
await ctx.subTest("uplink down: cached package still served", async () => {
|
|
113
|
+
const { status, body } = await fetchPackument(ctx.registryUrl, cachedPkg);
|
|
114
|
+
trace("uplink down: cached %s → %d", cachedPkg, status);
|
|
115
|
+
assert.strictEqual(status, 200, `Expected cached packument for ${cachedPkg} while the uplink is down, got ${status}`);
|
|
116
|
+
const tarball = body.versions["1.0.0"].dist.tarball;
|
|
117
|
+
const download = await downloadTarball(tarball);
|
|
118
|
+
assert.strictEqual(download.status, 200, "Cached tarball not served while uplink is down");
|
|
119
|
+
assert.strictEqual(download.sha1, cached.shasum, "Cached tarball bytes mismatch");
|
|
120
|
+
});
|
|
121
|
+
await ctx.subTest("uplink down: unknown package fails cleanly", async () => {
|
|
122
|
+
const started = Date.now();
|
|
123
|
+
const { status } = await fetchPackument(ctx.registryUrl, `e2e-uplink-missing-${id}`);
|
|
124
|
+
trace("uplink down: unknown package → %d in %dms", status, Date.now() - started);
|
|
125
|
+
assert.ok(status >= 400, `Expected a clean 4xx/5xx for an unknown package with the uplink down, got ${status}`);
|
|
126
|
+
const ping = await fetch(`${ctx.registryUrl}/-/ping`);
|
|
127
|
+
assert.strictEqual(ping.status, 200, "Registry stopped responding after uplink failures");
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
var uplinkFailureScenario = {
|
|
131
|
+
name: "scenario:uplink-failure",
|
|
132
|
+
appliesTo: (adapter) => adapter.type === "npm" && Number.isFinite(uplinkPort()),
|
|
133
|
+
timeout: 12e4,
|
|
134
|
+
run: testUplinkFailure
|
|
135
|
+
};
|
|
136
|
+
//#endregion
|
|
137
|
+
export { uplinkFailureScenario };
|
|
138
|
+
|
|
139
|
+
//# sourceMappingURL=uplink-failure.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"uplink-failure.js","names":[],"sources":["../../../src/scenarios/uplink-failure.ts"],"sourcesContent":["import assert from 'assert';\n\nimport { TestContext, TestDefinition } from '../types';\nimport { downloadTarball, fetchPackument } from '../utils/http-client';\nimport { MockUplink } from '../utils/mock-uplink';\nimport { trace } from '../utils/process';\n\n/**\n * Scenario: uplink-failure\n *\n * Verifies how the registry behaves when its uplink misbehaves. Requires the\n * registry to be configured with an uplink pointing at the mock uplink port\n * and a package pattern proxied to it (see `--print-config`):\n *\n * uplinks:\n * e2emock:\n * url: http://localhost:<E2E_UPLINK_PORT>/\n * timeout: 3s\n * max_fails: 100\n * packages:\n * 'e2e-uplink-*':\n * access: $all\n * proxy: e2emock\n *\n * The scenario starts its own HTTP server on that port and flips it between\n * modes:\n * - healthy: packument + tarball proxied through, tarball URL rewritten\n * - drop-mid-stream: uplink kills the socket halfway through a tarball —\n * the client must not receive a \"successful\" corrupt download, and the\n * tarball must be intact on retry (no poisoned cache)\n * - down: previously fetched packages still served from cache; unknown\n * packages fail cleanly; the registry stays alive\n * - slow: uplink slower than the configured timeout — the registry gives up\n * instead of hanging\n *\n * Gated on E2E_UPLINK_PORT being set (the harness exports it) so runs against\n * arbitrary registries skip it cleanly, and to the npm adapter so it runs once\n * per suite.\n */\n\nfunction uplinkPort(): number {\n return parseInt(process.env.E2E_UPLINK_PORT || '', 10);\n}\n\nasync function testUplinkFailure(ctx: TestContext): Promise<void> {\n const id = ctx.runId;\n const mock = new MockUplink(uplinkPort());\n const cachedPkg = `e2e-uplink-cached-${id}`;\n const cached = mock.addPackage(cachedPkg, 512 * 1024);\n\n await mock.start();\n try {\n await ctx.subTest('healthy uplink: packument and tarball proxied through', async () => {\n const { status, body } = await fetchPackument(ctx.registryUrl, cachedPkg);\n trace('healthy proxy %s → %d | keys=%j', cachedPkg, status, Object.keys(body ?? {}));\n assert.strictEqual(status, 200, `Expected 200 proxying ${cachedPkg}, got ${status}`);\n assert.strictEqual(body.name, cachedPkg, 'Proxied packument name mismatch');\n const tarball: string = body.versions['1.0.0'].dist.tarball;\n assert.ok(\n tarball.startsWith(ctx.registryUrl),\n `Expected proxied dist.tarball rewritten to ${ctx.registryUrl}, got ${tarball}`\n );\n const download = await downloadTarball(tarball);\n assert.strictEqual(download.status, 200, 'Proxied tarball download failed');\n assert.strictEqual(download.sha1, cached.shasum, 'Proxied tarball bytes mismatch');\n trace('mock uplink requests so far: %j', mock.requests);\n assert.ok(mock.requests.length > 0, 'Expected the registry to hit the mock uplink');\n });\n\n await ctx.subTest('uplink drops connection mid-tarball', async () => {\n const droppedPkg = `e2e-uplink-dropped-${id}`;\n const dropped = mock.addPackage(droppedPkg, 512 * 1024);\n\n // Packument fetch is unaffected by drop-mid-stream (tarball route only).\n const { status, body } = await fetchPackument(ctx.registryUrl, droppedPkg);\n assert.strictEqual(status, 200, `Expected 200 for ${droppedPkg} packument`);\n const tarball: string = body.versions['1.0.0'].dist.tarball;\n\n mock.mode = 'drop-mid-stream';\n let sawCleanFailure = false;\n try {\n // The registry must terminate the client response promptly when its\n // uplink dies — a client left hanging is a bug, so cap the wait.\n const download = await downloadTarball(tarball, { signal: AbortSignal.timeout(15_000) });\n // A non-200, or a truncated/corrupt body, are both acceptable failures —\n // what must NOT happen is a \"successful\" download with wrong bytes\n // going undetected, so we check integrity ourselves.\n sawCleanFailure = download.status !== 200 || download.sha1 !== dropped.shasum;\n trace(\n 'drop-mid-stream download: status=%d bytes=%d sha1=%s (expected %s)',\n download.status,\n download.bytes,\n download.sha1,\n dropped.shasum\n );\n } catch (err) {\n assert.ok(\n (err as Error).name !== 'TimeoutError',\n 'Registry left the client hanging after the uplink dropped mid-tarball ' +\n '(response never terminated within 15s)'\n );\n sawCleanFailure = true;\n trace(\n 'drop-mid-stream download rejected as expected: %s',\n err instanceof Error ? err.message : err\n );\n }\n assert.ok(\n sawCleanFailure,\n 'Expected the client download to fail while the uplink drops mid-stream'\n );\n\n // Back to healthy: the tarball must be intact (no partial tarball cached).\n mock.mode = 'ok';\n const retry = await downloadTarball(tarball);\n trace('retry after recovery → %d bytes=%d sha1=%s', retry.status, retry.bytes, retry.sha1);\n assert.strictEqual(retry.status, 200, 'Tarball download after uplink recovery failed');\n assert.strictEqual(\n retry.sha1,\n dropped.shasum,\n 'Registry served a corrupt tarball after a mid-stream uplink failure (poisoned cache?)'\n );\n });\n\n await ctx.subTest('slow uplink hits the timeout instead of hanging', async () => {\n const slowPkg = `e2e-uplink-slow-${id}`;\n mock.addPackage(slowPkg);\n mock.mode = 'slow';\n const start = Date.now();\n const { status } = await fetchPackument(ctx.registryUrl, slowPkg);\n const elapsed = Date.now() - start;\n trace('slow uplink: %s → %d in %dms', slowPkg, status, elapsed);\n mock.mode = 'ok';\n assert.ok(status >= 400, `Expected an error status for a timed-out uplink, got ${status}`);\n // Configured uplink timeout is 3s with retry: 0 — anything close to the\n // mock's 10s answer means retries or timeouts are multiplying again\n // (the max_fails-as-retry-limit bug family).\n assert.ok(\n elapsed < 6_000,\n `Expected the registry to time out (~3s), but the request took ${elapsed}ms`\n );\n });\n\n await ctx.subTest('uplink responding 500: cache served, unknown fails cleanly', async () => {\n mock.mode = 'error-500';\n // The already-proxied package must keep working from cache.\n const cachedResp = await fetchPackument(ctx.registryUrl, cachedPkg);\n trace('uplink 500: cached %s → %d', cachedPkg, cachedResp.status);\n assert.strictEqual(\n cachedResp.status,\n 200,\n `Expected cached packument for ${cachedPkg} while the uplink returns 500, got ${cachedResp.status}`\n );\n // An unknown package must fail cleanly, not crash or hang.\n const unknown = await fetchPackument(ctx.registryUrl, `e2e-uplink-500-missing-${id}`);\n trace('uplink 500: unknown package → %d | body=%j', unknown.status, unknown.body);\n assert.ok(\n unknown.status >= 400,\n `Expected a clean 4xx/5xx for an unknown package while the uplink returns 500, got ${unknown.status}`\n );\n const ping = await fetch(`${ctx.registryUrl}/-/ping`);\n assert.strictEqual(ping.status, 200, 'Registry stopped responding after uplink 500s');\n mock.mode = 'ok';\n });\n } finally {\n await mock.stop();\n }\n\n await ctx.subTest('uplink down: cached package still served', async () => {\n const { status, body } = await fetchPackument(ctx.registryUrl, cachedPkg);\n trace('uplink down: cached %s → %d', cachedPkg, status);\n assert.strictEqual(\n status,\n 200,\n `Expected cached packument for ${cachedPkg} while the uplink is down, got ${status}`\n );\n const tarball: string = body.versions['1.0.0'].dist.tarball;\n const download = await downloadTarball(tarball);\n assert.strictEqual(download.status, 200, 'Cached tarball not served while uplink is down');\n assert.strictEqual(download.sha1, cached.shasum, 'Cached tarball bytes mismatch');\n });\n\n await ctx.subTest('uplink down: unknown package fails cleanly', async () => {\n const started = Date.now();\n const { status } = await fetchPackument(ctx.registryUrl, `e2e-uplink-missing-${id}`);\n trace('uplink down: unknown package → %d in %dms', status, Date.now() - started);\n assert.ok(\n status >= 400,\n `Expected a clean 4xx/5xx for an unknown package with the uplink down, got ${status}`\n );\n const ping = await fetch(`${ctx.registryUrl}/-/ping`);\n assert.strictEqual(ping.status, 200, 'Registry stopped responding after uplink failures');\n });\n}\n\nexport const uplinkFailureScenario: TestDefinition = {\n name: 'scenario:uplink-failure',\n // Needs the harness-provided uplink config; skipped cleanly otherwise.\n appliesTo: (adapter) => adapter.type === 'npm' && Number.isFinite(uplinkPort()),\n timeout: 120_000,\n run: testUplinkFailure,\n};\n"],"mappings":"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"}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { publishLocalPackage } from "../utils/publish.js";
|
|
2
|
+
import buildDebug from "debug";
|
|
3
|
+
import assert from "assert";
|
|
4
|
+
//#region src/tests/audit.ts
|
|
5
|
+
var debug = buildDebug("verdaccio:e2e-cli:test:audit");
|
|
6
|
+
async function testAudit(ctx) {
|
|
7
|
+
if (ctx.adapter.type !== "npm" && ctx.adapter.type !== "bun") {
|
|
8
|
+
debug("skipping audit test for %s (npm/bun only)", ctx.adapter.type);
|
|
9
|
+
return;
|
|
10
|
+
}
|
|
11
|
+
const packages = [`verdaccio-audit-${ctx.runId}`, `@verdaccio/audit-${ctx.runId}`];
|
|
12
|
+
const depName = `e2e-audit-dep-${ctx.runId}`;
|
|
13
|
+
await publishLocalPackage(ctx, depName, "1.0.0");
|
|
14
|
+
for (const pkgName of packages) {
|
|
15
|
+
const { tempFolder } = await ctx.adapter.prepareProject(pkgName, "1.0.0", ctx.registryUrl, ctx.port, ctx.token, { [depName]: "1.0.0" });
|
|
16
|
+
await ctx.adapter.exec({ cwd: tempFolder }, "install", ...ctx.adapter.registryArg(ctx.registryUrl));
|
|
17
|
+
try {
|
|
18
|
+
if ((await fetch(`${ctx.registryUrl}/-/npm/v1/security/audits/quick`, {
|
|
19
|
+
method: "POST",
|
|
20
|
+
headers: { "Content-Type": "application/json" },
|
|
21
|
+
body: "{}"
|
|
22
|
+
})).status === 404) {
|
|
23
|
+
debug("audit endpoint not supported by this registry (404), skipping");
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
} catch {
|
|
27
|
+
debug("audit endpoint probe failed, skipping");
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
let resp;
|
|
31
|
+
try {
|
|
32
|
+
resp = await ctx.adapter.exec({ cwd: tempFolder }, "audit", "--json", ...ctx.adapter.registryArg(ctx.registryUrl));
|
|
33
|
+
} catch (err) {
|
|
34
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
35
|
+
if (/503|ENOTFOUND|EAI_AGAIN|getaddrinfo|ECONNREFUSED|audit/i.test(message)) {
|
|
36
|
+
debug("audit upstream unreachable (offline?), skipping: %s", message);
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
throw err;
|
|
40
|
+
}
|
|
41
|
+
if (ctx.adapter.type === "bun") assert.ok(resp.stdout.length > 0 || resp.stderr.length > 0, "Expected audit output");
|
|
42
|
+
else {
|
|
43
|
+
const parsedBody = JSON.parse(resp.stdout);
|
|
44
|
+
assert.ok(parsedBody.auditReportVersion !== void 0, "Expected \"auditReportVersion\" in audit response");
|
|
45
|
+
assert.ok(parsedBody.vulnerabilities !== void 0, "Expected \"vulnerabilities\" in audit response");
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
var auditTest = {
|
|
50
|
+
name: "audit",
|
|
51
|
+
requires: ["audit"],
|
|
52
|
+
run: testAudit
|
|
53
|
+
};
|
|
54
|
+
//#endregion
|
|
55
|
+
export { auditTest };
|
|
56
|
+
|
|
57
|
+
//# sourceMappingURL=audit.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"audit.js","names":[],"sources":["../../../src/tests/audit.ts"],"sourcesContent":["import assert from 'assert';\nimport buildDebug from 'debug';\n\nimport { TestContext, TestDefinition } from '../types';\nimport { publishLocalPackage } from '../utils/publish';\n\nconst debug = buildDebug('verdaccio:e2e-cli:test:audit');\n\nasync function testAudit(ctx: TestContext): Promise<void> {\n // Audit is only reliable with npm and bun — pnpm/yarn audit output varies too much\n if (ctx.adapter.type !== 'npm' && ctx.adapter.type !== 'bun') {\n debug('skipping audit test for %s (npm/bun only)', ctx.adapter.type);\n return;\n }\n\n const packages = [`verdaccio-audit-${ctx.runId}`, `@verdaccio/audit-${ctx.runId}`];\n\n // Publish the dependency locally so metadata/tarball resolution never leaves\n // the registry under test (the suite must run offline). Only the audit\n // report request itself may travel upstream, and that is skipped below when\n // the endpoint is unavailable.\n const depName = `e2e-audit-dep-${ctx.runId}`;\n await publishLocalPackage(ctx, depName, '1.0.0');\n\n for (const pkgName of packages) {\n const { tempFolder } = await ctx.adapter.prepareProject(\n pkgName,\n '1.0.0',\n ctx.registryUrl,\n ctx.port,\n ctx.token,\n { [depName]: '1.0.0' }\n );\n\n // install is required to create package lock file\n await ctx.adapter.exec(\n { cwd: tempFolder },\n 'install',\n ...ctx.adapter.registryArg(ctx.registryUrl)\n );\n\n // npm audit exits with code 1 both when vulnerabilities are found AND when\n // the endpoint is unavailable. We need to distinguish between the two.\n // Probe the audit endpoint first to avoid false failures on registries\n // that don't support it (e.g. verdaccio next-7+).\n try {\n const probe = await fetch(`${ctx.registryUrl}/-/npm/v1/security/audits/quick`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: '{}',\n });\n if (probe.status === 404) {\n debug('audit endpoint not supported by this registry (404), skipping');\n return;\n }\n } catch {\n debug('audit endpoint probe failed, skipping');\n return;\n }\n\n let resp;\n try {\n resp = await ctx.adapter.exec(\n { cwd: tempFolder },\n 'audit',\n '--json',\n ...ctx.adapter.registryArg(ctx.registryUrl)\n );\n } catch (err) {\n // The registry proxies audit reports upstream; when running offline that\n // upstream is unreachable and the audit command fails — that is not a\n // registry bug, so skip instead of failing the suite.\n const message = err instanceof Error ? err.message : String(err);\n if (/503|ENOTFOUND|EAI_AGAIN|getaddrinfo|ECONNREFUSED|audit/i.test(message)) {\n debug('audit upstream unreachable (offline?), skipping: %s', message);\n return;\n }\n throw err;\n }\n\n if (ctx.adapter.type === 'bun') {\n // bun audit output may differ — exit code 0 is sufficient for basic validation\n assert.ok(resp.stdout.length > 0 || resp.stderr.length > 0, 'Expected audit output');\n } else {\n const parsedBody = JSON.parse(resp.stdout);\n assert.ok(\n parsedBody.auditReportVersion !== undefined,\n 'Expected \"auditReportVersion\" in audit response'\n );\n assert.ok(\n parsedBody.vulnerabilities !== undefined,\n 'Expected \"vulnerabilities\" in audit response'\n );\n }\n }\n}\n\nexport const auditTest: TestDefinition = {\n name: 'audit',\n requires: ['audit'],\n run: testAudit,\n};\n"],"mappings":";;;;AAMA,IAAM,QAAQ,WAAW,8BAA8B;AAEvD,eAAe,UAAU,KAAiC;CAExD,IAAI,IAAI,QAAQ,SAAS,SAAS,IAAI,QAAQ,SAAS,OAAO;EAC5D,MAAM,6CAA6C,IAAI,QAAQ,IAAI;EACnE;CACF;CAEA,MAAM,WAAW,CAAC,mBAAmB,IAAI,SAAS,oBAAoB,IAAI,OAAO;CAMjF,MAAM,UAAU,iBAAiB,IAAI;CACrC,MAAM,oBAAoB,KAAK,SAAS,OAAO;CAE/C,KAAK,MAAM,WAAW,UAAU;EAC9B,MAAM,EAAE,eAAe,MAAM,IAAI,QAAQ,eACvC,SACA,SACA,IAAI,aACJ,IAAI,MACJ,IAAI,OACJ,GAAG,UAAU,QAAQ,CACvB;EAGA,MAAM,IAAI,QAAQ,KAChB,EAAE,KAAK,WAAW,GAClB,WACA,GAAG,IAAI,QAAQ,YAAY,IAAI,WAAW,CAC5C;EAMA,IAAI;GAMF,KAAI,MALgB,MAAM,GAAG,IAAI,YAAY,kCAAkC;IAC7E,QAAQ;IACR,SAAS,EAAE,gBAAgB,mBAAmB;IAC9C,MAAM;GACR,CAAC,EAAA,CACS,WAAW,KAAK;IACxB,MAAM,+DAA+D;IACrE;GACF;EACF,QAAQ;GACN,MAAM,uCAAuC;GAC7C;EACF;EAEA,IAAI;EACJ,IAAI;GACF,OAAO,MAAM,IAAI,QAAQ,KACvB,EAAE,KAAK,WAAW,GAClB,SACA,UACA,GAAG,IAAI,QAAQ,YAAY,IAAI,WAAW,CAC5C;EACF,SAAS,KAAK;GAIZ,MAAM,UAAU,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;GAC/D,IAAI,0DAA0D,KAAK,OAAO,GAAG;IAC3E,MAAM,uDAAuD,OAAO;IACpE;GACF;GACA,MAAM;EACR;EAEA,IAAI,IAAI,QAAQ,SAAS,OAEvB,OAAO,GAAG,KAAK,OAAO,SAAS,KAAK,KAAK,OAAO,SAAS,GAAG,uBAAuB;OAC9E;GACL,MAAM,aAAa,KAAK,MAAM,KAAK,MAAM;GACzC,OAAO,GACL,WAAW,uBAAuB,KAAA,GAClC,mDACF;GACA,OAAO,GACL,WAAW,oBAAoB,KAAA,GAC/B,gDACF;EACF;CACF;AACF;AAEA,IAAa,YAA4B;CACvC,MAAM;CACN,UAAU,CAAC,OAAO;CAClB,KAAK;AACP"}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { publishLocalPackage } from "../utils/publish.js";
|
|
2
|
+
import buildDebug from "debug";
|
|
3
|
+
import { join } from "path";
|
|
4
|
+
import { rm } from "fs/promises";
|
|
5
|
+
import assert from "assert";
|
|
6
|
+
//#region src/tests/ci.ts
|
|
7
|
+
var debug = buildDebug("verdaccio:e2e-cli:test:ci");
|
|
8
|
+
/**
|
|
9
|
+
* Test: ci
|
|
10
|
+
*
|
|
11
|
+
* Verifies that lockfile-based install works correctly.
|
|
12
|
+
* Unlike `install`, CI commands skip metadata fetches and resolve
|
|
13
|
+
* tarballs directly from the URLs stored in the lockfile.
|
|
14
|
+
*
|
|
15
|
+
* Flow:
|
|
16
|
+
* 1. Prepare a project with dependencies
|
|
17
|
+
* 2. Run `install` to generate the lockfile
|
|
18
|
+
* 3. Remove node_modules
|
|
19
|
+
* 4. Run the CI-equivalent command (npm ci / pnpm install --frozen-lockfile / yarn --frozen-lockfile / yarn --immutable)
|
|
20
|
+
* 5. Verify it completes successfully
|
|
21
|
+
*/
|
|
22
|
+
function getCiArgs(ctx) {
|
|
23
|
+
switch (ctx.adapter.type) {
|
|
24
|
+
case "npm": return ["ci", ...ctx.adapter.registryArg(ctx.registryUrl)];
|
|
25
|
+
case "pnpm": return [
|
|
26
|
+
"install",
|
|
27
|
+
"--frozen-lockfile",
|
|
28
|
+
...ctx.adapter.registryArg(ctx.registryUrl)
|
|
29
|
+
];
|
|
30
|
+
case "yarn-modern": return ["install", "--immutable"];
|
|
31
|
+
case "bun": return [
|
|
32
|
+
"install",
|
|
33
|
+
"--frozen-lockfile",
|
|
34
|
+
...ctx.adapter.registryArg(ctx.registryUrl)
|
|
35
|
+
];
|
|
36
|
+
default: return ["ci", ...ctx.adapter.registryArg(ctx.registryUrl)];
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
async function testCi(ctx) {
|
|
40
|
+
if (ctx.adapter.type === "deno") {
|
|
41
|
+
debug("skipping ci test for deno (no ci command)");
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
const depName = `e2e-ci-dep-${ctx.runId}`;
|
|
45
|
+
await publishLocalPackage(ctx, depName, "1.0.0");
|
|
46
|
+
const { tempFolder } = await ctx.adapter.prepareProject(`ci-test-${ctx.runId}`, "1.0.0", ctx.registryUrl, ctx.port, ctx.token, { [depName]: "1.0.0" });
|
|
47
|
+
debug("running initial install to generate lockfile in %s", tempFolder);
|
|
48
|
+
await ctx.adapter.exec({ cwd: tempFolder }, "install", ...ctx.adapter.registryArg(ctx.registryUrl));
|
|
49
|
+
debug("lockfile generated, removing node_modules");
|
|
50
|
+
const nodeModulesPath = join(tempFolder, "node_modules");
|
|
51
|
+
await rm(nodeModulesPath, {
|
|
52
|
+
recursive: true,
|
|
53
|
+
force: true
|
|
54
|
+
});
|
|
55
|
+
const pnpmStorePath = join(tempFolder, ".pnpm-store");
|
|
56
|
+
await rm(pnpmStorePath, {
|
|
57
|
+
recursive: true,
|
|
58
|
+
force: true
|
|
59
|
+
});
|
|
60
|
+
debug("node_modules removed, running ci command");
|
|
61
|
+
const ciArgs = getCiArgs(ctx);
|
|
62
|
+
debug("ci args: %o", ciArgs);
|
|
63
|
+
const resp = await ctx.adapter.exec({ cwd: tempFolder }, ...ciArgs);
|
|
64
|
+
if (ctx.adapter.type === "npm") {
|
|
65
|
+
debug("npm ci output: %s", resp.stdout + resp.stderr);
|
|
66
|
+
assert.ok(true, "npm ci completed successfully");
|
|
67
|
+
} else {
|
|
68
|
+
debug("ci completed successfully for %s", ctx.adapter.type);
|
|
69
|
+
assert.ok(true, `CI install completed successfully for ${ctx.adapter.type}`);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
var ciTest = {
|
|
73
|
+
name: "ci",
|
|
74
|
+
requires: ["install"],
|
|
75
|
+
run: testCi
|
|
76
|
+
};
|
|
77
|
+
//#endregion
|
|
78
|
+
export { ciTest };
|
|
79
|
+
|
|
80
|
+
//# sourceMappingURL=ci.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ci.js","names":[],"sources":["../../../src/tests/ci.ts"],"sourcesContent":["import assert from 'assert';\nimport buildDebug from 'debug';\nimport { rm } from 'fs/promises';\nimport { join } from 'path';\n\nimport { TestContext, TestDefinition } from '../types';\nimport { publishLocalPackage } from '../utils/publish';\n\nconst debug = buildDebug('verdaccio:e2e-cli:test:ci');\n\n/**\n * Test: ci\n *\n * Verifies that lockfile-based install works correctly.\n * Unlike `install`, CI commands skip metadata fetches and resolve\n * tarballs directly from the URLs stored in the lockfile.\n *\n * Flow:\n * 1. Prepare a project with dependencies\n * 2. Run `install` to generate the lockfile\n * 3. Remove node_modules\n * 4. Run the CI-equivalent command (npm ci / pnpm install --frozen-lockfile / yarn --frozen-lockfile / yarn --immutable)\n * 5. Verify it completes successfully\n */\n\nfunction getCiArgs(ctx: TestContext): string[] {\n switch (ctx.adapter.type) {\n case 'npm':\n // `npm ci` — separate command, reads package-lock.json\n return ['ci', ...ctx.adapter.registryArg(ctx.registryUrl)];\n case 'pnpm':\n // `pnpm install --frozen-lockfile` — same command, strict mode\n return ['install', '--frozen-lockfile', ...ctx.adapter.registryArg(ctx.registryUrl)];\n case 'yarn-modern':\n // `yarn install --immutable`\n return ['install', '--immutable'];\n case 'bun':\n // `bun install --frozen-lockfile`\n return ['install', '--frozen-lockfile', ...ctx.adapter.registryArg(ctx.registryUrl)];\n default:\n return ['ci', ...ctx.adapter.registryArg(ctx.registryUrl)];\n }\n}\n\nasync function testCi(ctx: TestContext): Promise<void> {\n // deno doesn't have a CI/frozen-lockfile equivalent\n if (ctx.adapter.type === 'deno') {\n debug('skipping ci test for deno (no ci command)');\n return;\n }\n\n // Publish the dependency locally first — the suite must run offline\n const depName = `e2e-ci-dep-${ctx.runId}`;\n await publishLocalPackage(ctx, depName, '1.0.0');\n\n const { tempFolder } = await ctx.adapter.prepareProject(\n `ci-test-${ctx.runId}`,\n '1.0.0',\n ctx.registryUrl,\n ctx.port,\n ctx.token,\n { [depName]: '1.0.0' }\n );\n\n debug('running initial install to generate lockfile in %s', tempFolder);\n\n // Step 1: Run install to generate the lockfile\n await ctx.adapter.exec(\n { cwd: tempFolder },\n 'install',\n ...ctx.adapter.registryArg(ctx.registryUrl)\n );\n\n debug('lockfile generated, removing node_modules');\n\n // Step 2: Remove node_modules to force a clean install from lockfile\n const nodeModulesPath = join(tempFolder, 'node_modules');\n await rm(nodeModulesPath, { recursive: true, force: true });\n\n // For pnpm, also clean the virtual store\n const pnpmStorePath = join(tempFolder, '.pnpm-store');\n await rm(pnpmStorePath, { recursive: true, force: true });\n\n debug('node_modules removed, running ci command');\n\n // Step 3: Run CI-equivalent command (tarball-only, no metadata fetches)\n const ciArgs = getCiArgs(ctx);\n debug('ci args: %o', ciArgs);\n const resp = await ctx.adapter.exec({ cwd: tempFolder }, ...ciArgs);\n\n // Step 4: Verify success\n if (ctx.adapter.type === 'npm') {\n // npm ci with --json is not reliable, just check exit code 0\n // and verify output mentions added packages\n const output = resp.stdout + resp.stderr;\n debug('npm ci output: %s', output);\n assert.ok(true, 'npm ci completed successfully');\n } else {\n debug('ci completed successfully for %s', ctx.adapter.type);\n assert.ok(true, `CI install completed successfully for ${ctx.adapter.type}`);\n }\n}\n\nexport const ciTest: TestDefinition = {\n name: 'ci',\n requires: ['install'],\n run: testCi,\n};\n"],"mappings":";;;;;;AAQA,IAAM,QAAQ,WAAW,2BAA2B;;;;;;;;;;;;;;;AAiBpD,SAAS,UAAU,KAA4B;CAC7C,QAAQ,IAAI,QAAQ,MAApB;EACE,KAAK,OAEH,OAAO,CAAC,MAAM,GAAG,IAAI,QAAQ,YAAY,IAAI,WAAW,CAAC;EAC3D,KAAK,QAEH,OAAO;GAAC;GAAW;GAAqB,GAAG,IAAI,QAAQ,YAAY,IAAI,WAAW;EAAC;EACrF,KAAK,eAEH,OAAO,CAAC,WAAW,aAAa;EAClC,KAAK,OAEH,OAAO;GAAC;GAAW;GAAqB,GAAG,IAAI,QAAQ,YAAY,IAAI,WAAW;EAAC;EACrF,SACE,OAAO,CAAC,MAAM,GAAG,IAAI,QAAQ,YAAY,IAAI,WAAW,CAAC;CAC7D;AACF;AAEA,eAAe,OAAO,KAAiC;CAErD,IAAI,IAAI,QAAQ,SAAS,QAAQ;EAC/B,MAAM,2CAA2C;EACjD;CACF;CAGA,MAAM,UAAU,cAAc,IAAI;CAClC,MAAM,oBAAoB,KAAK,SAAS,OAAO;CAE/C,MAAM,EAAE,eAAe,MAAM,IAAI,QAAQ,eACvC,WAAW,IAAI,SACf,SACA,IAAI,aACJ,IAAI,MACJ,IAAI,OACJ,GAAG,UAAU,QAAQ,CACvB;CAEA,MAAM,sDAAsD,UAAU;CAGtE,MAAM,IAAI,QAAQ,KAChB,EAAE,KAAK,WAAW,GAClB,WACA,GAAG,IAAI,QAAQ,YAAY,IAAI,WAAW,CAC5C;CAEA,MAAM,2CAA2C;CAGjD,MAAM,kBAAkB,KAAK,YAAY,cAAc;CACvD,MAAM,GAAG,iBAAiB;EAAE,WAAW;EAAM,OAAO;CAAK,CAAC;CAG1D,MAAM,gBAAgB,KAAK,YAAY,aAAa;CACpD,MAAM,GAAG,eAAe;EAAE,WAAW;EAAM,OAAO;CAAK,CAAC;CAExD,MAAM,0CAA0C;CAGhD,MAAM,SAAS,UAAU,GAAG;CAC5B,MAAM,eAAe,MAAM;CAC3B,MAAM,OAAO,MAAM,IAAI,QAAQ,KAAK,EAAE,KAAK,WAAW,GAAG,GAAG,MAAM;CAGlE,IAAI,IAAI,QAAQ,SAAS,OAAO;EAI9B,MAAM,qBADS,KAAK,SAAS,KAAK,MACD;EACjC,OAAO,GAAG,MAAM,+BAA+B;CACjD,OAAO;EACL,MAAM,oCAAoC,IAAI,QAAQ,IAAI;EAC1D,OAAO,GAAG,MAAM,yCAAyC,IAAI,QAAQ,MAAM;CAC7E;AACF;AAEA,IAAa,SAAyB;CACpC,MAAM;CACN,UAAU,CAAC,SAAS;CACpB,KAAK;AACP"}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import { parseInfoJson } from "../utils/info.js";
|
|
2
|
+
import buildDebug from "debug";
|
|
3
|
+
import { join } from "path";
|
|
4
|
+
import { readFile, writeFile } from "fs/promises";
|
|
5
|
+
import assert from "assert";
|
|
6
|
+
//#region src/tests/deprecate.ts
|
|
7
|
+
var debug = buildDebug("verdaccio:e2e-cli:test:deprecate");
|
|
8
|
+
async function publishPkg(ctx, tempFolder, pkgName) {
|
|
9
|
+
if (ctx.adapter.type === "yarn-modern") {
|
|
10
|
+
debug("running yarn install before publish for %s", pkgName);
|
|
11
|
+
await ctx.adapter.exec({ cwd: tempFolder }, "install");
|
|
12
|
+
}
|
|
13
|
+
debug("publishing %s from %s", pkgName, tempFolder);
|
|
14
|
+
await ctx.adapter.exec({ cwd: tempFolder }, "publish", ...ctx.adapter.registryArg(ctx.registryUrl));
|
|
15
|
+
debug("published %s successfully", pkgName);
|
|
16
|
+
}
|
|
17
|
+
async function deprecate(ctx, tempFolder, packageVersion, message) {
|
|
18
|
+
debug("deprecating %s with message: %s", packageVersion, message || "(empty, un-deprecate)");
|
|
19
|
+
const jsonFlag = ctx.adapter.type === "yarn-modern" ? [] : ["--json"];
|
|
20
|
+
await ctx.adapter.exec({ cwd: tempFolder }, "deprecate", packageVersion, message, ...jsonFlag, ...ctx.adapter.registryArg(ctx.registryUrl));
|
|
21
|
+
debug("deprecate command completed for %s", packageVersion);
|
|
22
|
+
}
|
|
23
|
+
async function getInfo(ctx, tempFolder, pkgName) {
|
|
24
|
+
debug("fetching info for %s", pkgName);
|
|
25
|
+
const resp = await ctx.adapter.exec({ cwd: tempFolder }, "info", pkgName, "--json", ...ctx.adapter.registryArg(ctx.registryUrl));
|
|
26
|
+
const parsed = parseInfoJson(resp.stdout);
|
|
27
|
+
debug("info for %s: deprecated=%s", pkgName, parsed.deprecated ?? "(not set)");
|
|
28
|
+
return parsed;
|
|
29
|
+
}
|
|
30
|
+
async function bumpVersion(ctx, tempFolder) {
|
|
31
|
+
debug("bumping version (minor) in %s", tempFolder);
|
|
32
|
+
if (ctx.adapter.type === "yarn-modern") {
|
|
33
|
+
const pkgPath = join(tempFolder, "package.json");
|
|
34
|
+
const pkg = JSON.parse(await readFile(pkgPath, "utf8"));
|
|
35
|
+
const [major, minor, patch] = pkg.version.split(".").map(Number);
|
|
36
|
+
pkg.version = `${major}.${minor + 1}.${patch}`;
|
|
37
|
+
debug("bumped %s to %s", pkgPath, pkg.version);
|
|
38
|
+
await writeFile(pkgPath, JSON.stringify(pkg, null, 2));
|
|
39
|
+
} else await ctx.adapter.exec({ cwd: tempFolder }, "version", "minor", ...ctx.adapter.registryArg(ctx.registryUrl));
|
|
40
|
+
}
|
|
41
|
+
async function prepareDeprecateProject(ctx, pkgName, version) {
|
|
42
|
+
debug("preparing project for %s@%s", pkgName, version);
|
|
43
|
+
const { tempFolder } = await ctx.adapter.prepareProject(pkgName, version, ctx.registryUrl, ctx.port, ctx.token);
|
|
44
|
+
if (ctx.adapter.importPlugin) {
|
|
45
|
+
debug("importing npm-deprecate plugin into %s", tempFolder);
|
|
46
|
+
await ctx.adapter.importPlugin(tempFolder, "npm-deprecate");
|
|
47
|
+
}
|
|
48
|
+
return tempFolder;
|
|
49
|
+
}
|
|
50
|
+
async function testDeprecate(ctx) {
|
|
51
|
+
const id = ctx.runId;
|
|
52
|
+
const message = "some message";
|
|
53
|
+
await ctx.subTest("deprecate single version", async () => {
|
|
54
|
+
const pkgName = `@verdaccio/dep1-${id}`;
|
|
55
|
+
const tf = await prepareDeprecateProject(ctx, pkgName, "1.0.0");
|
|
56
|
+
await publishPkg(ctx, tf, pkgName);
|
|
57
|
+
await deprecate(ctx, tf, `${pkgName}@1.0.0`, message);
|
|
58
|
+
const info = await getInfo(ctx, tf, pkgName);
|
|
59
|
+
assert.strictEqual(info.name, pkgName);
|
|
60
|
+
assert.strictEqual(info.deprecated, message, "Package should be deprecated");
|
|
61
|
+
});
|
|
62
|
+
await ctx.subTest("un-deprecate", async () => {
|
|
63
|
+
const pkgName = `@verdaccio/dep2-${id}`;
|
|
64
|
+
const tf = await prepareDeprecateProject(ctx, pkgName, "1.0.0");
|
|
65
|
+
await publishPkg(ctx, tf, pkgName);
|
|
66
|
+
await deprecate(ctx, tf, `${pkgName}@1.0.0`, message);
|
|
67
|
+
const info1 = await getInfo(ctx, tf, pkgName);
|
|
68
|
+
assert.strictEqual(info1.deprecated, message);
|
|
69
|
+
await deprecate(ctx, tf, `${pkgName}@1.0.0`, "");
|
|
70
|
+
const info2 = await getInfo(ctx, tf, pkgName);
|
|
71
|
+
assert.strictEqual(info2.deprecated, void 0, "Package should be un-deprecated");
|
|
72
|
+
});
|
|
73
|
+
await ctx.subTest("deprecate multiple versions", async () => {
|
|
74
|
+
const pkgName = `@verdaccio/dep3-${id}`;
|
|
75
|
+
const tf = await prepareDeprecateProject(ctx, pkgName, "1.0.0");
|
|
76
|
+
await publishPkg(ctx, tf, pkgName);
|
|
77
|
+
for (let i = 0; i < 3; i++) {
|
|
78
|
+
await bumpVersion(ctx, tf);
|
|
79
|
+
await publishPkg(ctx, tf, pkgName);
|
|
80
|
+
}
|
|
81
|
+
await deprecate(ctx, tf, pkgName, message);
|
|
82
|
+
for (const v of [
|
|
83
|
+
"1.0.0",
|
|
84
|
+
"1.1.0",
|
|
85
|
+
"1.2.0",
|
|
86
|
+
"1.3.0"
|
|
87
|
+
]) {
|
|
88
|
+
const info = await getInfo(ctx, tf, `${pkgName}@${v}`);
|
|
89
|
+
assert.strictEqual(info.deprecated, message, `Version ${v} should be deprecated`);
|
|
90
|
+
}
|
|
91
|
+
await bumpVersion(ctx, tf);
|
|
92
|
+
await publishPkg(ctx, tf, pkgName);
|
|
93
|
+
const info = await getInfo(ctx, tf, `${pkgName}@1.4.0`);
|
|
94
|
+
assert.strictEqual(info.deprecated, void 0, "New version should not be deprecated");
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
var deprecateTest = {
|
|
98
|
+
name: "deprecate",
|
|
99
|
+
requires: ["deprecate"],
|
|
100
|
+
run: testDeprecate
|
|
101
|
+
};
|
|
102
|
+
//#endregion
|
|
103
|
+
export { deprecateTest };
|
|
104
|
+
|
|
105
|
+
//# sourceMappingURL=deprecate.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"deprecate.js","names":[],"sources":["../../../src/tests/deprecate.ts"],"sourcesContent":["import assert from 'assert';\nimport buildDebug from 'debug';\nimport { readFile, writeFile } from 'fs/promises';\nimport { join } from 'path';\n\nimport { TestContext, TestDefinition } from '../types';\nimport { parseInfoJson } from '../utils/info';\n\nconst debug = buildDebug('verdaccio:e2e-cli:test:deprecate');\n\nasync function publishPkg(ctx: TestContext, tempFolder: string, pkgName: string) {\n if (ctx.adapter.type === 'yarn-modern') {\n debug('running yarn install before publish for %s', pkgName);\n await ctx.adapter.exec({ cwd: tempFolder }, 'install');\n }\n debug('publishing %s from %s', pkgName, tempFolder);\n await ctx.adapter.exec(\n { cwd: tempFolder },\n 'publish',\n ...ctx.adapter.registryArg(ctx.registryUrl)\n );\n debug('published %s successfully', pkgName);\n}\n\nasync function deprecate(\n ctx: TestContext,\n tempFolder: string,\n packageVersion: string,\n message: string\n) {\n debug('deprecating %s with message: %s', packageVersion, message || '(empty, un-deprecate)');\n const jsonFlag = ctx.adapter.type === 'yarn-modern' ? [] : ['--json'];\n await ctx.adapter.exec(\n { cwd: tempFolder },\n 'deprecate',\n packageVersion,\n message,\n ...jsonFlag,\n ...ctx.adapter.registryArg(ctx.registryUrl)\n );\n debug('deprecate command completed for %s', packageVersion);\n}\n\nasync function getInfo(ctx: TestContext, tempFolder: string, pkgName: string) {\n debug('fetching info for %s', pkgName);\n const resp = await ctx.adapter.exec(\n { cwd: tempFolder },\n 'info',\n pkgName,\n '--json',\n ...ctx.adapter.registryArg(ctx.registryUrl)\n );\n const parsed = parseInfoJson(resp.stdout);\n debug('info for %s: deprecated=%s', pkgName, parsed.deprecated ?? '(not set)');\n return parsed;\n}\n\nasync function bumpVersion(ctx: TestContext, tempFolder: string) {\n debug('bumping version (minor) in %s', tempFolder);\n if (ctx.adapter.type === 'yarn-modern') {\n // Bump version by editing package.json directly to avoid\n // requiring the version plugin (not bundled in Yarn 3)\n const pkgPath = join(tempFolder, 'package.json');\n const pkg = JSON.parse(await readFile(pkgPath, 'utf8'));\n const [major, minor, patch] = pkg.version.split('.').map(Number);\n pkg.version = `${major}.${minor + 1}.${patch}`;\n debug('bumped %s to %s', pkgPath, pkg.version);\n await writeFile(pkgPath, JSON.stringify(pkg, null, 2));\n } else {\n await ctx.adapter.exec(\n { cwd: tempFolder },\n 'version',\n 'minor',\n ...ctx.adapter.registryArg(ctx.registryUrl)\n );\n }\n}\n\nasync function prepareDeprecateProject(\n ctx: TestContext,\n pkgName: string,\n version: string\n): Promise<string> {\n debug('preparing project for %s@%s', pkgName, version);\n const { tempFolder } = await ctx.adapter.prepareProject(\n pkgName,\n version,\n ctx.registryUrl,\n ctx.port,\n ctx.token\n );\n if (ctx.adapter.importPlugin) {\n debug('importing npm-deprecate plugin into %s', tempFolder);\n await ctx.adapter.importPlugin(tempFolder, 'npm-deprecate');\n }\n return tempFolder;\n}\n\nasync function testDeprecate(ctx: TestContext): Promise<void> {\n const id = ctx.runId;\n\n const message = 'some message';\n\n await ctx.subTest('deprecate single version', async () => {\n const pkgName = `@verdaccio/dep1-${id}`;\n const tf = await prepareDeprecateProject(ctx, pkgName, '1.0.0');\n await publishPkg(ctx, tf, pkgName);\n await deprecate(ctx, tf, `${pkgName}@1.0.0`, message);\n const info = await getInfo(ctx, tf, pkgName);\n assert.strictEqual(info.name, pkgName);\n assert.strictEqual(info.deprecated, message, 'Package should be deprecated');\n });\n\n await ctx.subTest('un-deprecate', async () => {\n const pkgName = `@verdaccio/dep2-${id}`;\n const tf = await prepareDeprecateProject(ctx, pkgName, '1.0.0');\n await publishPkg(ctx, tf, pkgName);\n await deprecate(ctx, tf, `${pkgName}@1.0.0`, message);\n const info1 = await getInfo(ctx, tf, pkgName);\n assert.strictEqual(info1.deprecated, message);\n await deprecate(ctx, tf, `${pkgName}@1.0.0`, '');\n const info2 = await getInfo(ctx, tf, pkgName);\n assert.strictEqual(info2.deprecated, undefined, 'Package should be un-deprecated');\n });\n\n await ctx.subTest('deprecate multiple versions', async () => {\n const pkgName = `@verdaccio/dep3-${id}`;\n const tf = await prepareDeprecateProject(ctx, pkgName, '1.0.0');\n await publishPkg(ctx, tf, pkgName);\n for (let i = 0; i < 3; i++) {\n await bumpVersion(ctx, tf);\n await publishPkg(ctx, tf, pkgName);\n }\n await deprecate(ctx, tf, pkgName, message);\n for (const v of ['1.0.0', '1.1.0', '1.2.0', '1.3.0']) {\n const info = await getInfo(ctx, tf, `${pkgName}@${v}`);\n assert.strictEqual(info.deprecated, message, `Version ${v} should be deprecated`);\n }\n await bumpVersion(ctx, tf);\n await publishPkg(ctx, tf, pkgName);\n const info = await getInfo(ctx, tf, `${pkgName}@1.4.0`);\n assert.strictEqual(info.deprecated, undefined, 'New version should not be deprecated');\n });\n}\n\nexport const deprecateTest: TestDefinition = {\n name: 'deprecate',\n requires: ['deprecate'],\n run: testDeprecate,\n};\n"],"mappings":"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"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import assert from "assert";
|
|
2
|
+
//#region src/tests/dist-tags.ts
|
|
3
|
+
async function publishPkg(ctx, tempFolder, extraArgs = []) {
|
|
4
|
+
await ctx.adapter.exec({ cwd: tempFolder }, "publish", ...extraArgs, ...ctx.adapter.registryArg(ctx.registryUrl));
|
|
5
|
+
}
|
|
6
|
+
async function bumpVersion(ctx, tempFolder) {
|
|
7
|
+
await ctx.adapter.exec({ cwd: tempFolder }, "version", "minor", ...ctx.adapter.registryArg(ctx.registryUrl));
|
|
8
|
+
}
|
|
9
|
+
async function testDistTags(ctx) {
|
|
10
|
+
const id = ctx.runId;
|
|
11
|
+
const pkgName1 = `@foo/dt1-${id}`;
|
|
12
|
+
const { tempFolder: tf1 } = await ctx.adapter.prepareProject(pkgName1, "1.0.0", ctx.registryUrl, ctx.port, ctx.token);
|
|
13
|
+
await publishPkg(ctx, tf1);
|
|
14
|
+
await bumpVersion(ctx, tf1);
|
|
15
|
+
await publishPkg(ctx, tf1, ["--tag", "beta"]);
|
|
16
|
+
const resp1 = await ctx.adapter.exec({ cwd: tf1 }, "dist-tag", "ls", "--json", ...ctx.adapter.registryArg(ctx.registryUrl));
|
|
17
|
+
assert.strictEqual(resp1.stdout, "beta: 1.1.0latest: 1.0.0");
|
|
18
|
+
const pkgName2 = `@verdaccio/dt2-${id}`;
|
|
19
|
+
const { tempFolder: tf2 } = await ctx.adapter.prepareProject(pkgName2, "1.0.0", ctx.registryUrl, ctx.port, ctx.token);
|
|
20
|
+
await publishPkg(ctx, tf2);
|
|
21
|
+
await bumpVersion(ctx, tf2);
|
|
22
|
+
await publishPkg(ctx, tf2, ["--tag", "beta"]);
|
|
23
|
+
const resp2 = await ctx.adapter.exec({ cwd: tf2 }, "dist-tag", "rm", `${pkgName2}@1.1.0`, "beta", ...ctx.adapter.registryArg(ctx.registryUrl));
|
|
24
|
+
assert.strictEqual(resp2.stdout, `-beta: ${pkgName2}@1.1.0`);
|
|
25
|
+
const pkgName3 = `@verdaccio/dt3-${id}`;
|
|
26
|
+
const { tempFolder: tf3 } = await ctx.adapter.prepareProject(pkgName3, "1.0.0", ctx.registryUrl, ctx.port, ctx.token);
|
|
27
|
+
await publishPkg(ctx, tf3);
|
|
28
|
+
await bumpVersion(ctx, tf3);
|
|
29
|
+
await publishPkg(ctx, tf3);
|
|
30
|
+
const resp3 = await ctx.adapter.exec({ cwd: tf3 }, "dist-tag", "add", `${pkgName3}@1.1.0`, "alfa", ...ctx.adapter.registryArg(ctx.registryUrl));
|
|
31
|
+
assert.strictEqual(resp3.stdout, `+alfa: ${pkgName3}@1.1.0`);
|
|
32
|
+
}
|
|
33
|
+
var distTagsTest = {
|
|
34
|
+
name: "dist-tags",
|
|
35
|
+
requires: ["dist-tag"],
|
|
36
|
+
run: testDistTags
|
|
37
|
+
};
|
|
38
|
+
//#endregion
|
|
39
|
+
export { distTagsTest };
|
|
40
|
+
|
|
41
|
+
//# sourceMappingURL=dist-tags.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dist-tags.js","names":[],"sources":["../../../src/tests/dist-tags.ts"],"sourcesContent":["import assert from 'assert';\n\nimport { TestContext, TestDefinition } from '../types';\n\nasync function publishPkg(ctx: TestContext, tempFolder: string, extraArgs: string[] = []) {\n await ctx.adapter.exec(\n { cwd: tempFolder },\n 'publish',\n ...extraArgs,\n ...ctx.adapter.registryArg(ctx.registryUrl)\n );\n}\n\nasync function bumpVersion(ctx: TestContext, tempFolder: string) {\n await ctx.adapter.exec(\n { cwd: tempFolder },\n 'version',\n 'minor',\n ...ctx.adapter.registryArg(ctx.registryUrl)\n );\n}\n\nasync function testDistTags(ctx: TestContext): Promise<void> {\n const id = ctx.runId;\n\n // Test 1: list dist-tags\n const pkgName1 = `@foo/dt1-${id}`;\n const { tempFolder: tf1 } = await ctx.adapter.prepareProject(\n pkgName1,\n '1.0.0',\n ctx.registryUrl,\n ctx.port,\n ctx.token\n );\n await publishPkg(ctx, tf1);\n await bumpVersion(ctx, tf1);\n await publishPkg(ctx, tf1, ['--tag', 'beta']);\n const resp1 = await ctx.adapter.exec(\n { cwd: tf1 },\n 'dist-tag',\n 'ls',\n '--json',\n ...ctx.adapter.registryArg(ctx.registryUrl)\n );\n assert.strictEqual(resp1.stdout, 'beta: 1.1.0latest: 1.0.0');\n\n // Test 2: remove tag\n const pkgName2 = `@verdaccio/dt2-${id}`;\n const { tempFolder: tf2 } = await ctx.adapter.prepareProject(\n pkgName2,\n '1.0.0',\n ctx.registryUrl,\n ctx.port,\n ctx.token\n );\n await publishPkg(ctx, tf2);\n await bumpVersion(ctx, tf2);\n await publishPkg(ctx, tf2, ['--tag', 'beta']);\n const resp2 = await ctx.adapter.exec(\n { cwd: tf2 },\n 'dist-tag',\n 'rm',\n `${pkgName2}@1.1.0`,\n 'beta',\n ...ctx.adapter.registryArg(ctx.registryUrl)\n );\n assert.strictEqual(resp2.stdout, `-beta: ${pkgName2}@1.1.0`);\n\n // Test 3: add tag\n const pkgName3 = `@verdaccio/dt3-${id}`;\n const { tempFolder: tf3 } = await ctx.adapter.prepareProject(\n pkgName3,\n '1.0.0',\n ctx.registryUrl,\n ctx.port,\n ctx.token\n );\n await publishPkg(ctx, tf3);\n await bumpVersion(ctx, tf3);\n await publishPkg(ctx, tf3);\n const resp3 = await ctx.adapter.exec(\n { cwd: tf3 },\n 'dist-tag',\n 'add',\n `${pkgName3}@1.1.0`,\n 'alfa',\n ...ctx.adapter.registryArg(ctx.registryUrl)\n );\n assert.strictEqual(resp3.stdout, `+alfa: ${pkgName3}@1.1.0`);\n}\n\nexport const distTagsTest: TestDefinition = {\n name: 'dist-tags',\n requires: ['dist-tag'],\n run: testDistTags,\n};\n"],"mappings":";;AAIA,eAAe,WAAW,KAAkB,YAAoB,YAAsB,CAAC,GAAG;CACxF,MAAM,IAAI,QAAQ,KAChB,EAAE,KAAK,WAAW,GAClB,WACA,GAAG,WACH,GAAG,IAAI,QAAQ,YAAY,IAAI,WAAW,CAC5C;AACF;AAEA,eAAe,YAAY,KAAkB,YAAoB;CAC/D,MAAM,IAAI,QAAQ,KAChB,EAAE,KAAK,WAAW,GAClB,WACA,SACA,GAAG,IAAI,QAAQ,YAAY,IAAI,WAAW,CAC5C;AACF;AAEA,eAAe,aAAa,KAAiC;CAC3D,MAAM,KAAK,IAAI;CAGf,MAAM,WAAW,YAAY;CAC7B,MAAM,EAAE,YAAY,QAAQ,MAAM,IAAI,QAAQ,eAC5C,UACA,SACA,IAAI,aACJ,IAAI,MACJ,IAAI,KACN;CACA,MAAM,WAAW,KAAK,GAAG;CACzB,MAAM,YAAY,KAAK,GAAG;CAC1B,MAAM,WAAW,KAAK,KAAK,CAAC,SAAS,MAAM,CAAC;CAC5C,MAAM,QAAQ,MAAM,IAAI,QAAQ,KAC9B,EAAE,KAAK,IAAI,GACX,YACA,MACA,UACA,GAAG,IAAI,QAAQ,YAAY,IAAI,WAAW,CAC5C;CACA,OAAO,YAAY,MAAM,QAAQ,0BAA0B;CAG3D,MAAM,WAAW,kBAAkB;CACnC,MAAM,EAAE,YAAY,QAAQ,MAAM,IAAI,QAAQ,eAC5C,UACA,SACA,IAAI,aACJ,IAAI,MACJ,IAAI,KACN;CACA,MAAM,WAAW,KAAK,GAAG;CACzB,MAAM,YAAY,KAAK,GAAG;CAC1B,MAAM,WAAW,KAAK,KAAK,CAAC,SAAS,MAAM,CAAC;CAC5C,MAAM,QAAQ,MAAM,IAAI,QAAQ,KAC9B,EAAE,KAAK,IAAI,GACX,YACA,MACA,GAAG,SAAS,SACZ,QACA,GAAG,IAAI,QAAQ,YAAY,IAAI,WAAW,CAC5C;CACA,OAAO,YAAY,MAAM,QAAQ,UAAU,SAAS,OAAO;CAG3D,MAAM,WAAW,kBAAkB;CACnC,MAAM,EAAE,YAAY,QAAQ,MAAM,IAAI,QAAQ,eAC5C,UACA,SACA,IAAI,aACJ,IAAI,MACJ,IAAI,KACN;CACA,MAAM,WAAW,KAAK,GAAG;CACzB,MAAM,YAAY,KAAK,GAAG;CAC1B,MAAM,WAAW,KAAK,GAAG;CACzB,MAAM,QAAQ,MAAM,IAAI,QAAQ,KAC9B,EAAE,KAAK,IAAI,GACX,YACA,OACA,GAAG,SAAS,SACZ,QACA,GAAG,IAAI,QAAQ,YAAY,IAAI,WAAW,CAC5C;CACA,OAAO,YAAY,MAAM,QAAQ,UAAU,SAAS,OAAO;AAC7D;AAEA,IAAa,eAA+B;CAC1C,MAAM;CACN,UAAU,CAAC,UAAU;CACrB,KAAK;AACP"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import "../scenarios/install-multiple-deps.js";
|
|
2
|
+
import "../scenarios/metadata.js";
|
|
3
|
+
import "../scenarios/minimum-release-age.js";
|
|
4
|
+
import "../scenarios/search.js";
|
|
5
|
+
import "../scenarios/tarballs.js";
|
|
6
|
+
import "../scenarios/uplink-failure.js";
|
|
7
|
+
import { allScenarios } from "../scenarios/index.js";
|
|
8
|
+
import { auditTest } from "./audit.js";
|
|
9
|
+
import { ciTest } from "./ci.js";
|
|
10
|
+
import { deprecateTest } from "./deprecate.js";
|
|
11
|
+
import { distTagsTest } from "./dist-tags.js";
|
|
12
|
+
import { infoTest } from "./info.js";
|
|
13
|
+
import { installTest } from "./install.js";
|
|
14
|
+
import { loginTest } from "./login.js";
|
|
15
|
+
import { pingTest } from "./ping.js";
|
|
16
|
+
import { publishTest } from "./publish.js";
|
|
17
|
+
import { searchTest } from "./search.js";
|
|
18
|
+
import { unpublishTest } from "./unpublish.js";
|
|
19
|
+
//#region src/tests/index.ts
|
|
20
|
+
var allTests = [
|
|
21
|
+
publishTest,
|
|
22
|
+
installTest,
|
|
23
|
+
ciTest,
|
|
24
|
+
auditTest,
|
|
25
|
+
infoTest,
|
|
26
|
+
deprecateTest,
|
|
27
|
+
distTagsTest,
|
|
28
|
+
loginTest,
|
|
29
|
+
pingTest,
|
|
30
|
+
searchTest,
|
|
31
|
+
unpublishTest,
|
|
32
|
+
...allScenarios
|
|
33
|
+
];
|
|
34
|
+
//#endregion
|
|
35
|
+
export { allScenarios, allTests, auditTest, ciTest, deprecateTest, distTagsTest, infoTest, installTest, loginTest, pingTest, publishTest, searchTest, unpublishTest };
|
|
36
|
+
|
|
37
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../../../src/tests/index.ts"],"sourcesContent":["import {\n allScenarios,\n installMultipleDepsScenario,\n metadataScenario,\n minimumReleaseAgeScenario,\n searchScenario,\n tarballsScenario,\n uplinkFailureScenario,\n} from '../scenarios';\nimport { TestDefinition } from '../types';\nimport { auditTest } from './audit';\nimport { ciTest } from './ci';\nimport { deprecateTest } from './deprecate';\nimport { distTagsTest } from './dist-tags';\nimport { infoTest } from './info';\nimport { installTest } from './install';\nimport { loginTest } from './login';\nimport { pingTest } from './ping';\nimport { publishTest } from './publish';\nimport { searchTest } from './search';\nimport { unpublishTest } from './unpublish';\n\nexport const allTests: TestDefinition[] = [\n publishTest,\n installTest,\n ciTest,\n auditTest,\n infoTest,\n deprecateTest,\n distTagsTest,\n loginTest,\n pingTest,\n searchTest,\n unpublishTest,\n // Scenarios (complex, multi-step tests)\n ...allScenarios,\n];\n\nexport {\n publishTest,\n installTest,\n ciTest,\n auditTest,\n infoTest,\n deprecateTest,\n distTagsTest,\n loginTest,\n pingTest,\n searchTest,\n unpublishTest,\n // Scenarios\n installMultipleDepsScenario,\n minimumReleaseAgeScenario,\n tarballsScenario,\n metadataScenario,\n searchScenario,\n uplinkFailureScenario,\n allScenarios,\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;;AAsBA,IAAa,WAA6B;CACxC;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CAEA,GAAG;AACL"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { parseInfoJson } from "../utils/info.js";
|
|
2
|
+
import { publishLocalPackage } from "../utils/publish.js";
|
|
3
|
+
import assert from "assert";
|
|
4
|
+
//#region src/tests/info.ts
|
|
5
|
+
async function testInfo(ctx) {
|
|
6
|
+
const depName = `e2e-info-dep-${ctx.runId}`;
|
|
7
|
+
const pkgName = `e2e-info-${ctx.runId}`;
|
|
8
|
+
await publishLocalPackage(ctx, depName, "1.0.0");
|
|
9
|
+
await publishLocalPackage(ctx, pkgName, "1.0.0", { [depName]: "1.0.0" });
|
|
10
|
+
const { tempFolder } = await ctx.adapter.prepareProject(`info-client-${ctx.runId}`, "1.0.0", ctx.registryUrl, ctx.port, ctx.token);
|
|
11
|
+
const resp = await ctx.adapter.exec({ cwd: tempFolder }, "info", pkgName, "--json", ...ctx.adapter.registryArg(ctx.registryUrl));
|
|
12
|
+
if (ctx.adapter.type === "deno") {
|
|
13
|
+
const output = resp.stdout + resp.stderr;
|
|
14
|
+
assert.ok(output.includes(pkgName), `Expected deno info output to reference ${pkgName}`);
|
|
15
|
+
} else {
|
|
16
|
+
const parsedBody = parseInfoJson(resp.stdout);
|
|
17
|
+
assert.strictEqual(parsedBody.name, pkgName, `Expected package name "${pkgName}"`);
|
|
18
|
+
assert.ok(parsedBody.dependencies !== void 0, "Expected \"dependencies\" to be defined");
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
var infoTest = {
|
|
22
|
+
name: "info",
|
|
23
|
+
requires: ["info"],
|
|
24
|
+
run: testInfo
|
|
25
|
+
};
|
|
26
|
+
//#endregion
|
|
27
|
+
export { infoTest };
|
|
28
|
+
|
|
29
|
+
//# sourceMappingURL=info.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"info.js","names":[],"sources":["../../../src/tests/info.ts"],"sourcesContent":["import assert from 'assert';\n\nimport { TestContext, TestDefinition } from '../types';\nimport { parseInfoJson } from '../utils/info';\nimport { publishLocalPackage } from '../utils/publish';\n\nasync function testInfo(ctx: TestContext): Promise<void> {\n // Publish the package we inspect (with one dependency, also local) — asking\n // for a real npmjs package would need an uplink and the suite must run\n // offline. deno resolves the dependency tree, so the dep must exist too.\n const depName = `e2e-info-dep-${ctx.runId}`;\n const pkgName = `e2e-info-${ctx.runId}`;\n await publishLocalPackage(ctx, depName, '1.0.0');\n await publishLocalPackage(ctx, pkgName, '1.0.0', { [depName]: '1.0.0' });\n\n // Run from a prepared project: it gives yarn modern a project context, and\n // its .npmrc (fresh token) shields the test from stale credentials in\n // ~/.npmrc.\n const { tempFolder } = await ctx.adapter.prepareProject(\n `info-client-${ctx.runId}`,\n '1.0.0',\n ctx.registryUrl,\n ctx.port,\n ctx.token\n );\n\n const resp = await ctx.adapter.exec(\n { cwd: tempFolder },\n 'info',\n pkgName,\n '--json',\n ...ctx.adapter.registryArg(ctx.registryUrl)\n );\n\n if (ctx.adapter.type === 'deno') {\n // deno info npm:<pkg> outputs a text dependency tree\n const output = resp.stdout + resp.stderr;\n assert.ok(output.includes(pkgName), `Expected deno info output to reference ${pkgName}`);\n } else {\n const parsedBody = parseInfoJson(resp.stdout);\n assert.strictEqual(parsedBody.name, pkgName, `Expected package name \"${pkgName}\"`);\n assert.ok(parsedBody.dependencies !== undefined, 'Expected \"dependencies\" to be defined');\n }\n}\n\nexport const infoTest: TestDefinition = {\n name: 'info',\n requires: ['info'],\n run: testInfo,\n};\n"],"mappings":";;;;AAMA,eAAe,SAAS,KAAiC;CAIvD,MAAM,UAAU,gBAAgB,IAAI;CACpC,MAAM,UAAU,YAAY,IAAI;CAChC,MAAM,oBAAoB,KAAK,SAAS,OAAO;CAC/C,MAAM,oBAAoB,KAAK,SAAS,SAAS,GAAG,UAAU,QAAQ,CAAC;CAKvE,MAAM,EAAE,eAAe,MAAM,IAAI,QAAQ,eACvC,eAAe,IAAI,SACnB,SACA,IAAI,aACJ,IAAI,MACJ,IAAI,KACN;CAEA,MAAM,OAAO,MAAM,IAAI,QAAQ,KAC7B,EAAE,KAAK,WAAW,GAClB,QACA,SACA,UACA,GAAG,IAAI,QAAQ,YAAY,IAAI,WAAW,CAC5C;CAEA,IAAI,IAAI,QAAQ,SAAS,QAAQ;EAE/B,MAAM,SAAS,KAAK,SAAS,KAAK;EAClC,OAAO,GAAG,OAAO,SAAS,OAAO,GAAG,0CAA0C,SAAS;CACzF,OAAO;EACL,MAAM,aAAa,cAAc,KAAK,MAAM;EAC5C,OAAO,YAAY,WAAW,MAAM,SAAS,0BAA0B,QAAQ,EAAE;EACjF,OAAO,GAAG,WAAW,iBAAiB,KAAA,GAAW,yCAAuC;CAC1F;AACF;AAEA,IAAa,WAA2B;CACtC,MAAM;CACN,UAAU,CAAC,MAAM;CACjB,KAAK;AACP"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { publishLocalPackage } from "../utils/publish.js";
|
|
2
|
+
import assert from "assert";
|
|
3
|
+
//#region src/tests/install.ts
|
|
4
|
+
async function testInstall(ctx) {
|
|
5
|
+
const depName = `e2e-install-dep-${ctx.runId}`;
|
|
6
|
+
await publishLocalPackage(ctx, depName, "1.0.0");
|
|
7
|
+
const { tempFolder } = await ctx.adapter.prepareProject(`install-test-${ctx.runId}`, "1.0.0", ctx.registryUrl, ctx.port, ctx.token, { [depName]: "1.0.0" });
|
|
8
|
+
const isNpm = ctx.adapter.type === "npm";
|
|
9
|
+
const args = isNpm ? [
|
|
10
|
+
"install",
|
|
11
|
+
"--json",
|
|
12
|
+
...ctx.adapter.registryArg(ctx.registryUrl)
|
|
13
|
+
] : ["install", ...ctx.adapter.registryArg(ctx.registryUrl)];
|
|
14
|
+
const resp = await ctx.adapter.exec({ cwd: tempFolder }, ...args);
|
|
15
|
+
if (isNpm) {
|
|
16
|
+
const parsedBody = JSON.parse(resp.stdout);
|
|
17
|
+
assert.ok(parsedBody.added !== void 0, "Expected \"added\" field in install response");
|
|
18
|
+
} else assert.ok(true, "Install completed successfully");
|
|
19
|
+
}
|
|
20
|
+
var installTest = {
|
|
21
|
+
name: "install",
|
|
22
|
+
requires: ["install"],
|
|
23
|
+
run: testInstall
|
|
24
|
+
};
|
|
25
|
+
//#endregion
|
|
26
|
+
export { installTest };
|
|
27
|
+
|
|
28
|
+
//# sourceMappingURL=install.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"install.js","names":[],"sources":["../../../src/tests/install.ts"],"sourcesContent":["import assert from 'assert';\n\nimport { TestContext, TestDefinition } from '../types';\nimport { publishLocalPackage } from '../utils/publish';\n\nasync function testInstall(ctx: TestContext): Promise<void> {\n // Publish the dependency locally first — the suite must work offline, so\n // nothing is ever resolved from an external registry.\n const depName = `e2e-install-dep-${ctx.runId}`;\n await publishLocalPackage(ctx, depName, '1.0.0');\n\n const { tempFolder } = await ctx.adapter.prepareProject(\n `install-test-${ctx.runId}`,\n '1.0.0',\n ctx.registryUrl,\n ctx.port,\n ctx.token,\n { [depName]: '1.0.0' }\n );\n\n const isNpm = ctx.adapter.type === 'npm';\n\n // pnpm doesn't support --json on install\n const args = isNpm\n ? ['install', '--json', ...ctx.adapter.registryArg(ctx.registryUrl)]\n : ['install', ...ctx.adapter.registryArg(ctx.registryUrl)];\n\n const resp = await ctx.adapter.exec({ cwd: tempFolder }, ...args);\n\n if (isNpm) {\n const parsedBody = JSON.parse(resp.stdout);\n assert.ok(parsedBody.added !== undefined, 'Expected \"added\" field in install response');\n } else {\n // for pnpm/yarn just verify it completed without error (exit code 0)\n assert.ok(true, 'Install completed successfully');\n }\n}\n\nexport const installTest: TestDefinition = {\n name: 'install',\n requires: ['install'],\n run: testInstall,\n};\n"],"mappings":";;;AAKA,eAAe,YAAY,KAAiC;CAG1D,MAAM,UAAU,mBAAmB,IAAI;CACvC,MAAM,oBAAoB,KAAK,SAAS,OAAO;CAE/C,MAAM,EAAE,eAAe,MAAM,IAAI,QAAQ,eACvC,gBAAgB,IAAI,SACpB,SACA,IAAI,aACJ,IAAI,MACJ,IAAI,OACJ,GAAG,UAAU,QAAQ,CACvB;CAEA,MAAM,QAAQ,IAAI,QAAQ,SAAS;CAGnC,MAAM,OAAO,QACT;EAAC;EAAW;EAAU,GAAG,IAAI,QAAQ,YAAY,IAAI,WAAW;CAAC,IACjE,CAAC,WAAW,GAAG,IAAI,QAAQ,YAAY,IAAI,WAAW,CAAC;CAE3D,MAAM,OAAO,MAAM,IAAI,QAAQ,KAAK,EAAE,KAAK,WAAW,GAAG,GAAG,IAAI;CAEhE,IAAI,OAAO;EACT,MAAM,aAAa,KAAK,MAAM,KAAK,MAAM;EACzC,OAAO,GAAG,WAAW,UAAU,KAAA,GAAW,8CAA4C;CACxF,OAEE,OAAO,GAAG,MAAM,gCAAgC;AAEpD;AAEA,IAAa,cAA8B;CACzC,MAAM;CACN,UAAU,CAAC,SAAS;CACpB,KAAK;AACP"}
|