@verdaccio/e2e-cli 2.10.4 → 3.1.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/build/adapters/index.d.ts +0 -1
- package/build/cjs/adapters/index.cjs +0 -2
- package/build/cjs/adapters/npm.cjs +10 -0
- package/build/cjs/adapters/npm.cjs.map +1 -1
- package/build/cjs/adapters/pnpm.cjs +11 -1
- package/build/cjs/adapters/pnpm.cjs.map +1 -1
- package/build/cjs/index.cjs +28 -8
- package/build/cjs/index.cjs.map +1 -1
- package/build/cjs/runner.cjs +1 -1
- package/build/cjs/runner.cjs.map +1 -1
- package/build/cjs/scenarios/index.cjs +16 -1
- package/build/cjs/scenarios/index.cjs.map +1 -1
- package/build/cjs/scenarios/install-multiple-deps.cjs +25 -23
- package/build/cjs/scenarios/install-multiple-deps.cjs.map +1 -1
- 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 +6 -8
- package/build/cjs/scenarios/minimum-release-age.cjs.map +1 -1
- package/build/cjs/scenarios/search.cjs +136 -0
- package/build/cjs/scenarios/search.cjs.map +1 -0
- package/build/cjs/scenarios/tarballs.cjs +147 -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 +15 -2
- package/build/cjs/tests/audit.cjs.map +1 -1
- package/build/cjs/tests/ci.cjs +4 -6
- package/build/cjs/tests/ci.cjs.map +1 -1
- package/build/cjs/tests/index.cjs +7 -3
- package/build/cjs/tests/index.cjs.map +1 -1
- package/build/cjs/tests/info.cjs +10 -21
- package/build/cjs/tests/info.cjs.map +1 -1
- package/build/cjs/tests/install.cjs +4 -1
- package/build/cjs/tests/install.cjs.map +1 -1
- package/build/cjs/tests/ping.cjs +3 -7
- package/build/cjs/tests/ping.cjs.map +1 -1
- package/build/cjs/tests/publish.cjs +0 -1
- package/build/cjs/tests/publish.cjs.map +1 -1
- package/build/cjs/tests/search.cjs +9 -5
- package/build/cjs/tests/search.cjs.map +1 -1
- 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/mock-uplink.cjs +196 -0
- package/build/cjs/utils/mock-uplink.cjs.map +1 -0
- package/build/cjs/utils/process.cjs +11 -0
- package/build/cjs/utils/process.cjs.map +1 -1
- package/build/cjs/utils/project.cjs +1 -1
- package/build/cjs/utils/project.cjs.map +1 -1
- package/build/cjs/utils/publish.cjs +117 -0
- package/build/cjs/utils/publish.cjs.map +1 -0
- package/build/esm/adapters/index.js +1 -2
- package/build/esm/adapters/npm.js +10 -0
- package/build/esm/adapters/npm.js.map +1 -1
- package/build/esm/adapters/pnpm.js +11 -1
- package/build/esm/adapters/pnpm.js.map +1 -1
- package/build/esm/index.js +29 -8
- package/build/esm/index.js.map +1 -1
- package/build/esm/runner.js +1 -1
- package/build/esm/runner.js.map +1 -1
- package/build/esm/scenarios/index.js +13 -2
- package/build/esm/scenarios/index.js.map +1 -1
- package/build/esm/scenarios/install-multiple-deps.js +25 -22
- package/build/esm/scenarios/install-multiple-deps.js.map +1 -1
- 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 +6 -7
- package/build/esm/scenarios/minimum-release-age.js.map +1 -1
- package/build/esm/scenarios/search.js +134 -0
- package/build/esm/scenarios/search.js.map +1 -0
- package/build/esm/scenarios/tarballs.js +145 -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 +15 -2
- package/build/esm/tests/audit.js.map +1 -1
- package/build/esm/tests/ci.js +4 -6
- package/build/esm/tests/ci.js.map +1 -1
- package/build/esm/tests/index.js +4 -0
- package/build/esm/tests/index.js.map +1 -1
- package/build/esm/tests/info.js +10 -21
- package/build/esm/tests/info.js.map +1 -1
- package/build/esm/tests/install.js +4 -1
- package/build/esm/tests/install.js.map +1 -1
- package/build/esm/tests/ping.js +3 -7
- package/build/esm/tests/ping.js.map +1 -1
- package/build/esm/tests/publish.js +0 -1
- package/build/esm/tests/publish.js.map +1 -1
- package/build/esm/tests/search.js +9 -5
- package/build/esm/tests/search.js.map +1 -1
- 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/mock-uplink.js +194 -0
- package/build/esm/utils/mock-uplink.js.map +1 -0
- package/build/esm/utils/process.js +11 -1
- package/build/esm/utils/process.js.map +1 -1
- package/build/esm/utils/project.js +1 -1
- package/build/esm/utils/project.js.map +1 -1
- package/build/esm/utils/publish.js +113 -0
- package/build/esm/utils/publish.js.map +1 -0
- package/build/index.d.ts +1 -1
- package/build/scenarios/index.d.ts +5 -1
- package/build/scenarios/metadata.d.ts +2 -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/index.d.ts +2 -2
- package/build/types.d.ts +12 -2
- package/build/utils/e2e-config.d.ts +21 -0
- package/build/utils/http-client.d.ts +49 -0
- package/build/utils/mock-uplink.d.ts +47 -0
- package/build/utils/process.d.ts +7 -0
- package/build/utils/publish.d.ts +17 -0
- package/package.json +1 -1
- package/build/adapters/yarn-classic.d.ts +0 -2
- package/build/cjs/adapters/yarn-classic.cjs +0 -98
- package/build/cjs/adapters/yarn-classic.cjs.map +0 -1
- package/build/esm/adapters/yarn-classic.js +0 -96
- package/build/esm/adapters/yarn-classic.js.map +0 -1
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
const require_runtime = require("../_virtual/_rolldown/runtime.cjs");
|
|
2
|
+
const require_process = require("../utils/process.cjs");
|
|
3
|
+
const require_http_client = require("../utils/http-client.cjs");
|
|
4
|
+
const require_mock_uplink = require("../utils/mock-uplink.cjs");
|
|
5
|
+
let assert = require("assert");
|
|
6
|
+
assert = require_runtime.__toESM(assert, 1);
|
|
7
|
+
//#region src/scenarios/uplink-failure.ts
|
|
8
|
+
/**
|
|
9
|
+
* Scenario: uplink-failure
|
|
10
|
+
*
|
|
11
|
+
* Verifies how the registry behaves when its uplink misbehaves. Requires the
|
|
12
|
+
* registry to be configured with an uplink pointing at the mock uplink port
|
|
13
|
+
* and a package pattern proxied to it (see `--print-config`):
|
|
14
|
+
*
|
|
15
|
+
* uplinks:
|
|
16
|
+
* e2emock:
|
|
17
|
+
* url: http://localhost:<E2E_UPLINK_PORT>/
|
|
18
|
+
* timeout: 3s
|
|
19
|
+
* max_fails: 100
|
|
20
|
+
* packages:
|
|
21
|
+
* 'e2e-uplink-*':
|
|
22
|
+
* access: $all
|
|
23
|
+
* proxy: e2emock
|
|
24
|
+
*
|
|
25
|
+
* The scenario starts its own HTTP server on that port and flips it between
|
|
26
|
+
* modes:
|
|
27
|
+
* - healthy: packument + tarball proxied through, tarball URL rewritten
|
|
28
|
+
* - drop-mid-stream: uplink kills the socket halfway through a tarball —
|
|
29
|
+
* the client must not receive a "successful" corrupt download, and the
|
|
30
|
+
* tarball must be intact on retry (no poisoned cache)
|
|
31
|
+
* - down: previously fetched packages still served from cache; unknown
|
|
32
|
+
* packages fail cleanly; the registry stays alive
|
|
33
|
+
* - slow: uplink slower than the configured timeout — the registry gives up
|
|
34
|
+
* instead of hanging
|
|
35
|
+
*
|
|
36
|
+
* Gated on E2E_UPLINK_PORT being set (the harness exports it) so runs against
|
|
37
|
+
* arbitrary registries skip it cleanly, and to the npm adapter so it runs once
|
|
38
|
+
* per suite.
|
|
39
|
+
*/
|
|
40
|
+
function uplinkPort() {
|
|
41
|
+
return parseInt(process.env.E2E_UPLINK_PORT || "", 10);
|
|
42
|
+
}
|
|
43
|
+
async function testUplinkFailure(ctx) {
|
|
44
|
+
const id = ctx.runId;
|
|
45
|
+
const mock = new require_mock_uplink.MockUplink(uplinkPort());
|
|
46
|
+
const cachedPkg = `e2e-uplink-cached-${id}`;
|
|
47
|
+
const cached = mock.addPackage(cachedPkg, 524288);
|
|
48
|
+
await mock.start();
|
|
49
|
+
try {
|
|
50
|
+
await ctx.subTest("healthy uplink: packument and tarball proxied through", async () => {
|
|
51
|
+
const { status, body } = await require_http_client.fetchPackument(ctx.registryUrl, cachedPkg);
|
|
52
|
+
require_process.trace("healthy proxy %s → %d | keys=%j", cachedPkg, status, Object.keys(body ?? {}));
|
|
53
|
+
assert.default.strictEqual(status, 200, `Expected 200 proxying ${cachedPkg}, got ${status}`);
|
|
54
|
+
assert.default.strictEqual(body.name, cachedPkg, "Proxied packument name mismatch");
|
|
55
|
+
const tarball = body.versions["1.0.0"].dist.tarball;
|
|
56
|
+
assert.default.ok(tarball.startsWith(ctx.registryUrl), `Expected proxied dist.tarball rewritten to ${ctx.registryUrl}, got ${tarball}`);
|
|
57
|
+
const download = await require_http_client.downloadTarball(tarball);
|
|
58
|
+
assert.default.strictEqual(download.status, 200, "Proxied tarball download failed");
|
|
59
|
+
assert.default.strictEqual(download.sha1, cached.shasum, "Proxied tarball bytes mismatch");
|
|
60
|
+
require_process.trace("mock uplink requests so far: %j", mock.requests);
|
|
61
|
+
assert.default.ok(mock.requests.length > 0, "Expected the registry to hit the mock uplink");
|
|
62
|
+
});
|
|
63
|
+
await ctx.subTest("uplink drops connection mid-tarball", async () => {
|
|
64
|
+
const droppedPkg = `e2e-uplink-dropped-${id}`;
|
|
65
|
+
const dropped = mock.addPackage(droppedPkg, 524288);
|
|
66
|
+
const { status, body } = await require_http_client.fetchPackument(ctx.registryUrl, droppedPkg);
|
|
67
|
+
assert.default.strictEqual(status, 200, `Expected 200 for ${droppedPkg} packument`);
|
|
68
|
+
const tarball = body.versions["1.0.0"].dist.tarball;
|
|
69
|
+
mock.mode = "drop-mid-stream";
|
|
70
|
+
let sawCleanFailure = false;
|
|
71
|
+
try {
|
|
72
|
+
const download = await require_http_client.downloadTarball(tarball, { signal: AbortSignal.timeout(15e3) });
|
|
73
|
+
sawCleanFailure = download.status !== 200 || download.sha1 !== dropped.shasum;
|
|
74
|
+
require_process.trace("drop-mid-stream download: status=%d bytes=%d sha1=%s (expected %s)", download.status, download.bytes, download.sha1, dropped.shasum);
|
|
75
|
+
} catch (err) {
|
|
76
|
+
assert.default.ok(err.name !== "TimeoutError", "Registry left the client hanging after the uplink dropped mid-tarball (response never terminated within 15s)");
|
|
77
|
+
sawCleanFailure = true;
|
|
78
|
+
require_process.trace("drop-mid-stream download rejected as expected: %s", err instanceof Error ? err.message : err);
|
|
79
|
+
}
|
|
80
|
+
assert.default.ok(sawCleanFailure, "Expected the client download to fail while the uplink drops mid-stream");
|
|
81
|
+
mock.mode = "ok";
|
|
82
|
+
const retry = await require_http_client.downloadTarball(tarball);
|
|
83
|
+
require_process.trace("retry after recovery → %d bytes=%d sha1=%s", retry.status, retry.bytes, retry.sha1);
|
|
84
|
+
assert.default.strictEqual(retry.status, 200, "Tarball download after uplink recovery failed");
|
|
85
|
+
assert.default.strictEqual(retry.sha1, dropped.shasum, "Registry served a corrupt tarball after a mid-stream uplink failure (poisoned cache?)");
|
|
86
|
+
});
|
|
87
|
+
await ctx.subTest("slow uplink hits the timeout instead of hanging", async () => {
|
|
88
|
+
const slowPkg = `e2e-uplink-slow-${id}`;
|
|
89
|
+
mock.addPackage(slowPkg);
|
|
90
|
+
mock.mode = "slow";
|
|
91
|
+
const start = Date.now();
|
|
92
|
+
const { status } = await require_http_client.fetchPackument(ctx.registryUrl, slowPkg);
|
|
93
|
+
const elapsed = Date.now() - start;
|
|
94
|
+
require_process.trace("slow uplink: %s → %d in %dms", slowPkg, status, elapsed);
|
|
95
|
+
mock.mode = "ok";
|
|
96
|
+
assert.default.ok(status >= 400, `Expected an error status for a timed-out uplink, got ${status}`);
|
|
97
|
+
assert.default.ok(elapsed < 6e3, `Expected the registry to time out (~3s), but the request took ${elapsed}ms`);
|
|
98
|
+
});
|
|
99
|
+
await ctx.subTest("uplink responding 500: cache served, unknown fails cleanly", async () => {
|
|
100
|
+
mock.mode = "error-500";
|
|
101
|
+
const cachedResp = await require_http_client.fetchPackument(ctx.registryUrl, cachedPkg);
|
|
102
|
+
require_process.trace("uplink 500: cached %s → %d", cachedPkg, cachedResp.status);
|
|
103
|
+
assert.default.strictEqual(cachedResp.status, 200, `Expected cached packument for ${cachedPkg} while the uplink returns 500, got ${cachedResp.status}`);
|
|
104
|
+
const unknown = await require_http_client.fetchPackument(ctx.registryUrl, `e2e-uplink-500-missing-${id}`);
|
|
105
|
+
require_process.trace("uplink 500: unknown package → %d | body=%j", unknown.status, unknown.body);
|
|
106
|
+
assert.default.ok(unknown.status >= 400, `Expected a clean 4xx/5xx for an unknown package while the uplink returns 500, got ${unknown.status}`);
|
|
107
|
+
const ping = await fetch(`${ctx.registryUrl}/-/ping`);
|
|
108
|
+
assert.default.strictEqual(ping.status, 200, "Registry stopped responding after uplink 500s");
|
|
109
|
+
mock.mode = "ok";
|
|
110
|
+
});
|
|
111
|
+
} finally {
|
|
112
|
+
await mock.stop();
|
|
113
|
+
}
|
|
114
|
+
await ctx.subTest("uplink down: cached package still served", async () => {
|
|
115
|
+
const { status, body } = await require_http_client.fetchPackument(ctx.registryUrl, cachedPkg);
|
|
116
|
+
require_process.trace("uplink down: cached %s → %d", cachedPkg, status);
|
|
117
|
+
assert.default.strictEqual(status, 200, `Expected cached packument for ${cachedPkg} while the uplink is down, got ${status}`);
|
|
118
|
+
const tarball = body.versions["1.0.0"].dist.tarball;
|
|
119
|
+
const download = await require_http_client.downloadTarball(tarball);
|
|
120
|
+
assert.default.strictEqual(download.status, 200, "Cached tarball not served while uplink is down");
|
|
121
|
+
assert.default.strictEqual(download.sha1, cached.shasum, "Cached tarball bytes mismatch");
|
|
122
|
+
});
|
|
123
|
+
await ctx.subTest("uplink down: unknown package fails cleanly", async () => {
|
|
124
|
+
const started = Date.now();
|
|
125
|
+
const { status } = await require_http_client.fetchPackument(ctx.registryUrl, `e2e-uplink-missing-${id}`);
|
|
126
|
+
require_process.trace("uplink down: unknown package → %d in %dms", status, Date.now() - started);
|
|
127
|
+
assert.default.ok(status >= 400, `Expected a clean 4xx/5xx for an unknown package with the uplink down, got ${status}`);
|
|
128
|
+
const ping = await fetch(`${ctx.registryUrl}/-/ping`);
|
|
129
|
+
assert.default.strictEqual(ping.status, 200, "Registry stopped responding after uplink failures");
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
var uplinkFailureScenario = {
|
|
133
|
+
name: "scenario:uplink-failure",
|
|
134
|
+
appliesTo: (adapter) => adapter.type === "npm" && Number.isFinite(uplinkPort()),
|
|
135
|
+
timeout: 12e4,
|
|
136
|
+
run: testUplinkFailure
|
|
137
|
+
};
|
|
138
|
+
//#endregion
|
|
139
|
+
exports.uplinkFailureScenario = uplinkFailureScenario;
|
|
140
|
+
|
|
141
|
+
//# sourceMappingURL=uplink-failure.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"uplink-failure.cjs","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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"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
const require_runtime = require("../_virtual/_rolldown/runtime.cjs");
|
|
2
|
+
const require_publish = require("../utils/publish.cjs");
|
|
2
3
|
let debug = require("debug");
|
|
3
4
|
debug = require_runtime.__toESM(debug, 1);
|
|
4
5
|
let assert = require("assert");
|
|
@@ -11,8 +12,10 @@ async function testAudit(ctx) {
|
|
|
11
12
|
return;
|
|
12
13
|
}
|
|
13
14
|
const packages = [`verdaccio-audit-${ctx.runId}`, `@verdaccio/audit-${ctx.runId}`];
|
|
15
|
+
const depName = `e2e-audit-dep-${ctx.runId}`;
|
|
16
|
+
await require_publish.publishLocalPackage(ctx, depName, "1.0.0");
|
|
14
17
|
for (const pkgName of packages) {
|
|
15
|
-
const { tempFolder } = await ctx.adapter.prepareProject(pkgName, "1.0.0", ctx.registryUrl, ctx.port, ctx.token, {
|
|
18
|
+
const { tempFolder } = await ctx.adapter.prepareProject(pkgName, "1.0.0", ctx.registryUrl, ctx.port, ctx.token, { [depName]: "1.0.0" });
|
|
16
19
|
await ctx.adapter.exec({ cwd: tempFolder }, "install", ...ctx.adapter.registryArg(ctx.registryUrl));
|
|
17
20
|
try {
|
|
18
21
|
if ((await fetch(`${ctx.registryUrl}/-/npm/v1/security/audits/quick`, {
|
|
@@ -27,7 +30,17 @@ async function testAudit(ctx) {
|
|
|
27
30
|
debug$1("audit endpoint probe failed, skipping");
|
|
28
31
|
return;
|
|
29
32
|
}
|
|
30
|
-
|
|
33
|
+
let resp;
|
|
34
|
+
try {
|
|
35
|
+
resp = await ctx.adapter.exec({ cwd: tempFolder }, "audit", "--json", ...ctx.adapter.registryArg(ctx.registryUrl));
|
|
36
|
+
} catch (err) {
|
|
37
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
38
|
+
if (/503|ENOTFOUND|EAI_AGAIN|getaddrinfo|ECONNREFUSED|audit/i.test(message)) {
|
|
39
|
+
debug$1("audit upstream unreachable (offline?), skipping: %s", message);
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
throw err;
|
|
43
|
+
}
|
|
31
44
|
if (ctx.adapter.type === "bun") assert.default.ok(resp.stdout.length > 0 || resp.stderr.length > 0, "Expected audit output");
|
|
32
45
|
else {
|
|
33
46
|
const parsedBody = JSON.parse(resp.stdout);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"audit.cjs","names":[],"sources":["../../../src/tests/audit.ts"],"sourcesContent":["import assert from 'assert';\nimport buildDebug from 'debug';\n\nimport { TestContext, TestDefinition } from '../types';\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 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 {
|
|
1
|
+
{"version":3,"file":"audit.cjs","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,WAAA,GAAQ,MAAA,QAAA,CAAW,8BAA8B;AAEvD,eAAe,UAAU,KAAiC;CAExD,IAAI,IAAI,QAAQ,SAAS,SAAS,IAAI,QAAQ,SAAS,OAAO;EAC5D,QAAM,6CAA6C,IAAI,QAAQ,IAAI;EACnE;CACF;CAEA,MAAM,WAAW,CAAC,mBAAmB,IAAI,SAAS,oBAAoB,IAAI,OAAO;CAMjF,MAAM,UAAU,iBAAiB,IAAI;CACrC,MAAM,gBAAA,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,QAAM,+DAA+D;IACrE;GACF;EACF,QAAQ;GACN,QAAM,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,QAAM,uDAAuD,OAAO;IACpE;GACF;GACA,MAAM;EACR;EAEA,IAAI,IAAI,QAAQ,SAAS,OAEvB,OAAA,QAAO,GAAG,KAAK,OAAO,SAAS,KAAK,KAAK,OAAO,SAAS,GAAG,uBAAuB;OAC9E;GACL,MAAM,aAAa,KAAK,MAAM,KAAK,MAAM;GACzC,OAAA,QAAO,GACL,WAAW,uBAAuB,KAAA,GAClC,mDACF;GACA,OAAA,QAAO,GACL,WAAW,oBAAoB,KAAA,GAC/B,gDACF;EACF;CACF;AACF;AAEA,IAAa,YAA4B;CACvC,MAAM;CACN,UAAU,CAAC,OAAO;CAClB,KAAK;AACP"}
|
package/build/cjs/tests/ci.cjs
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
const require_runtime = require("../_virtual/_rolldown/runtime.cjs");
|
|
2
|
+
const require_publish = require("../utils/publish.cjs");
|
|
2
3
|
let debug = require("debug");
|
|
3
4
|
debug = require_runtime.__toESM(debug, 1);
|
|
4
5
|
let path = require("path");
|
|
@@ -29,11 +30,6 @@ function getCiArgs(ctx) {
|
|
|
29
30
|
"--frozen-lockfile",
|
|
30
31
|
...ctx.adapter.registryArg(ctx.registryUrl)
|
|
31
32
|
];
|
|
32
|
-
case "yarn-classic": return [
|
|
33
|
-
"install",
|
|
34
|
-
"--frozen-lockfile",
|
|
35
|
-
...ctx.adapter.registryArg(ctx.registryUrl)
|
|
36
|
-
];
|
|
37
33
|
case "yarn-modern": return ["install", "--immutable"];
|
|
38
34
|
case "bun": return [
|
|
39
35
|
"install",
|
|
@@ -48,7 +44,9 @@ async function testCi(ctx) {
|
|
|
48
44
|
debug$1("skipping ci test for deno (no ci command)");
|
|
49
45
|
return;
|
|
50
46
|
}
|
|
51
|
-
const
|
|
47
|
+
const depName = `e2e-ci-dep-${ctx.runId}`;
|
|
48
|
+
await require_publish.publishLocalPackage(ctx, depName, "1.0.0");
|
|
49
|
+
const { tempFolder } = await ctx.adapter.prepareProject(`ci-test-${ctx.runId}`, "1.0.0", ctx.registryUrl, ctx.port, ctx.token, { [depName]: "1.0.0" });
|
|
52
50
|
debug$1("running initial install to generate lockfile in %s", tempFolder);
|
|
53
51
|
await ctx.adapter.exec({ cwd: tempFolder }, "install", ...ctx.adapter.registryArg(ctx.registryUrl));
|
|
54
52
|
debug$1("lockfile generated, removing node_modules");
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ci.cjs","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';\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-
|
|
1
|
+
{"version":3,"file":"ci.cjs","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,WAAA,GAAQ,MAAA,QAAA,CAAW,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,QAAM,2CAA2C;EACjD;CACF;CAGA,MAAM,UAAU,cAAc,IAAI;CAClC,MAAM,gBAAA,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,QAAM,sDAAsD,UAAU;CAGtE,MAAM,IAAI,QAAQ,KAChB,EAAE,KAAK,WAAW,GAClB,WACA,GAAG,IAAI,QAAQ,YAAY,IAAI,WAAW,CAC5C;CAEA,QAAM,2CAA2C;CAGjD,MAAM,mBAAA,GAAkB,KAAA,KAAA,CAAK,YAAY,cAAc;CACvD,OAAA,GAAM,YAAA,GAAA,CAAG,iBAAiB;EAAE,WAAW;EAAM,OAAO;CAAK,CAAC;CAG1D,MAAM,iBAAA,GAAgB,KAAA,KAAA,CAAK,YAAY,aAAa;CACpD,OAAA,GAAM,YAAA,GAAA,CAAG,eAAe;EAAE,WAAW;EAAM,OAAO;CAAK,CAAC;CAExD,QAAM,0CAA0C;CAGhD,MAAM,SAAS,UAAU,GAAG;CAC5B,QAAM,eAAe,MAAM;CAC3B,MAAM,OAAO,MAAM,IAAI,QAAQ,KAAK,EAAE,KAAK,WAAW,GAAG,GAAG,MAAM;CAGlE,IAAI,IAAI,QAAQ,SAAS,OAAO;EAI9B,QAAM,qBADS,KAAK,SAAS,KAAK,MACD;EACjC,OAAA,QAAO,GAAG,MAAM,+BAA+B;CACjD,OAAO;EACL,QAAM,oCAAoC,IAAI,QAAQ,IAAI;EAC1D,OAAA,QAAO,GAAG,MAAM,yCAAyC,IAAI,QAAQ,MAAM;CAC7E;AACF;AAEA,IAAa,SAAyB;CACpC,MAAM;CACN,UAAU,CAAC,SAAS;CACpB,KAAK;AACP"}
|
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
require("../scenarios/install-multiple-deps.cjs");
|
|
2
|
+
require("../scenarios/metadata.cjs");
|
|
2
3
|
require("../scenarios/minimum-release-age.cjs");
|
|
4
|
+
require("../scenarios/search.cjs");
|
|
5
|
+
require("../scenarios/tarballs.cjs");
|
|
6
|
+
require("../scenarios/uplink-failure.cjs");
|
|
3
7
|
const require_index = require("../scenarios/index.cjs");
|
|
4
8
|
const require_audit = require("./audit.cjs");
|
|
5
9
|
const require_ci = require("./ci.cjs");
|
|
@@ -10,7 +14,7 @@ const require_install = require("./install.cjs");
|
|
|
10
14
|
const require_login = require("./login.cjs");
|
|
11
15
|
const require_ping = require("./ping.cjs");
|
|
12
16
|
const require_publish = require("./publish.cjs");
|
|
13
|
-
const require_search = require("./search.cjs");
|
|
17
|
+
const require_search$1 = require("./search.cjs");
|
|
14
18
|
const require_unpublish = require("./unpublish.cjs");
|
|
15
19
|
//#region src/tests/index.ts
|
|
16
20
|
var allTests = [
|
|
@@ -23,7 +27,7 @@ var allTests = [
|
|
|
23
27
|
require_dist_tags.distTagsTest,
|
|
24
28
|
require_login.loginTest,
|
|
25
29
|
require_ping.pingTest,
|
|
26
|
-
require_search.searchTest,
|
|
30
|
+
require_search$1.searchTest,
|
|
27
31
|
require_unpublish.unpublishTest,
|
|
28
32
|
...require_index.allScenarios
|
|
29
33
|
];
|
|
@@ -39,7 +43,7 @@ exports.installTest = require_install.installTest;
|
|
|
39
43
|
exports.loginTest = require_login.loginTest;
|
|
40
44
|
exports.pingTest = require_ping.pingTest;
|
|
41
45
|
exports.publishTest = require_publish.publishTest;
|
|
42
|
-
exports.searchTest = require_search.searchTest;
|
|
46
|
+
exports.searchTest = require_search$1.searchTest;
|
|
43
47
|
exports.unpublishTest = require_unpublish.unpublishTest;
|
|
44
48
|
|
|
45
49
|
//# sourceMappingURL=index.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs","names":[],"sources":["../../../src/tests/index.ts"],"sourcesContent":["import {
|
|
1
|
+
{"version":3,"file":"index.cjs","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,gBAAA;CACA,gBAAA;CACA,WAAA;CACA,cAAA;CACA,aAAA;CACA,kBAAA;CACA,kBAAA;CACA,cAAA;CACA,aAAA;CACA,iBAAA;CACA,kBAAA;CAEA,GAAG,cAAA;AACL"}
|
package/build/cjs/tests/info.cjs
CHANGED
|
@@ -1,33 +1,22 @@
|
|
|
1
1
|
const require_runtime = require("../_virtual/_rolldown/runtime.cjs");
|
|
2
|
-
const require_project = require("../utils/project.cjs");
|
|
3
2
|
const require_info = require("../utils/info.cjs");
|
|
4
|
-
|
|
5
|
-
let fs_promises = require("fs/promises");
|
|
3
|
+
const require_publish = require("../utils/publish.cjs");
|
|
6
4
|
let assert = require("assert");
|
|
7
5
|
assert = require_runtime.__toESM(assert, 1);
|
|
8
6
|
//#region src/tests/info.ts
|
|
9
7
|
async function testInfo(ctx) {
|
|
10
|
-
const
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
})
|
|
15
|
-
const resp = await ctx.adapter.exec({ cwd: tempFolder }, "info",
|
|
16
|
-
if (ctx.adapter.type === "
|
|
17
|
-
const dataLine = resp.stdout.split("\n").filter(Boolean).find((l) => {
|
|
18
|
-
try {
|
|
19
|
-
return JSON.parse(l).type === "inspect";
|
|
20
|
-
} catch {
|
|
21
|
-
return false;
|
|
22
|
-
}
|
|
23
|
-
});
|
|
24
|
-
assert.default.ok(dataLine, "Expected yarn info NDJSON to contain an \"inspect\" entry");
|
|
25
|
-
} else if (ctx.adapter.type === "deno") {
|
|
8
|
+
const depName = `e2e-info-dep-${ctx.runId}`;
|
|
9
|
+
const pkgName = `e2e-info-${ctx.runId}`;
|
|
10
|
+
await require_publish.publishLocalPackage(ctx, depName, "1.0.0");
|
|
11
|
+
await require_publish.publishLocalPackage(ctx, pkgName, "1.0.0", { [depName]: "1.0.0" });
|
|
12
|
+
const { tempFolder } = await ctx.adapter.prepareProject(`info-client-${ctx.runId}`, "1.0.0", ctx.registryUrl, ctx.port, ctx.token);
|
|
13
|
+
const resp = await ctx.adapter.exec({ cwd: tempFolder }, "info", pkgName, "--json", ...ctx.adapter.registryArg(ctx.registryUrl));
|
|
14
|
+
if (ctx.adapter.type === "deno") {
|
|
26
15
|
const output = resp.stdout + resp.stderr;
|
|
27
|
-
assert.default.ok(output.includes(
|
|
16
|
+
assert.default.ok(output.includes(pkgName), `Expected deno info output to reference ${pkgName}`);
|
|
28
17
|
} else {
|
|
29
18
|
const parsedBody = require_info.parseInfoJson(resp.stdout);
|
|
30
|
-
assert.default.strictEqual(parsedBody.name,
|
|
19
|
+
assert.default.strictEqual(parsedBody.name, pkgName, `Expected package name "${pkgName}"`);
|
|
31
20
|
assert.default.ok(parsedBody.dependencies !== void 0, "Expected \"dependencies\" to be defined");
|
|
32
21
|
}
|
|
33
22
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"info.cjs","names":[],"sources":["../../../src/tests/info.ts"],"sourcesContent":["import assert from 'assert';\
|
|
1
|
+
{"version":3,"file":"info.cjs","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,gBAAA,oBAAoB,KAAK,SAAS,OAAO;CAC/C,MAAM,gBAAA,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,OAAA,QAAO,GAAG,OAAO,SAAS,OAAO,GAAG,0CAA0C,SAAS;CACzF,OAAO;EACL,MAAM,aAAa,aAAA,cAAc,KAAK,MAAM;EAC5C,OAAA,QAAO,YAAY,WAAW,MAAM,SAAS,0BAA0B,QAAQ,EAAE;EACjF,OAAA,QAAO,GAAG,WAAW,iBAAiB,KAAA,GAAW,yCAAuC;CAC1F;AACF;AAEA,IAAa,WAA2B;CACtC,MAAM;CACN,UAAU,CAAC,MAAM;CACjB,KAAK;AACP"}
|
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
const require_runtime = require("../_virtual/_rolldown/runtime.cjs");
|
|
2
|
+
const require_publish = require("../utils/publish.cjs");
|
|
2
3
|
let assert = require("assert");
|
|
3
4
|
assert = require_runtime.__toESM(assert, 1);
|
|
4
5
|
//#region src/tests/install.ts
|
|
5
6
|
async function testInstall(ctx) {
|
|
6
|
-
const
|
|
7
|
+
const depName = `e2e-install-dep-${ctx.runId}`;
|
|
8
|
+
await require_publish.publishLocalPackage(ctx, depName, "1.0.0");
|
|
9
|
+
const { tempFolder } = await ctx.adapter.prepareProject(`install-test-${ctx.runId}`, "1.0.0", ctx.registryUrl, ctx.port, ctx.token, { [depName]: "1.0.0" });
|
|
7
10
|
const isNpm = ctx.adapter.type === "npm";
|
|
8
11
|
const args = isNpm ? [
|
|
9
12
|
"install",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"install.cjs","names":[],"sources":["../../../src/tests/install.ts"],"sourcesContent":["import assert from 'assert';\n\nimport { TestContext, TestDefinition } from '../types';\n\nasync function testInstall(ctx: TestContext): Promise<void> {\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 {
|
|
1
|
+
{"version":3,"file":"install.cjs","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,gBAAA,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,OAAA,QAAO,GAAG,WAAW,UAAU,KAAA,GAAW,8CAA4C;CACxF,OAEE,OAAA,QAAO,GAAG,MAAM,gCAAgC;AAEpD;AAEA,IAAa,cAA8B;CACzC,MAAM;CACN,UAAU,CAAC,SAAS;CACpB,KAAK;AACP"}
|
package/build/cjs/tests/ping.cjs
CHANGED
|
@@ -4,13 +4,9 @@ assert = require_runtime.__toESM(assert, 1);
|
|
|
4
4
|
//#region src/tests/ping.ts
|
|
5
5
|
async function testPing(ctx) {
|
|
6
6
|
const type = ctx.adapter.type;
|
|
7
|
-
const
|
|
8
|
-
|
|
9
|
-
if (
|
|
10
|
-
const { tempFolder } = await ctx.adapter.prepareProject(`verdaccio-ping-${ctx.runId}`, "1.0.0", ctx.registryUrl, ctx.port, ctx.token);
|
|
11
|
-
cwd = tempFolder;
|
|
12
|
-
if (ctx.adapter.importPlugin) await ctx.adapter.importPlugin(tempFolder, "npm-ping");
|
|
13
|
-
}
|
|
7
|
+
const { tempFolder } = await ctx.adapter.prepareProject(`verdaccio-ping-${ctx.runId}`, "1.0.0", ctx.registryUrl, ctx.port, ctx.token);
|
|
8
|
+
const cwd = tempFolder;
|
|
9
|
+
if (type === "yarn-modern" && ctx.adapter.importPlugin) await ctx.adapter.importPlugin(tempFolder, "npm-ping");
|
|
14
10
|
const jsonFlag = type === "yarn-modern" ? [] : ["--json"];
|
|
15
11
|
const resp = await ctx.adapter.exec({ cwd }, "ping", ...jsonFlag, ...ctx.adapter.registryArg(ctx.registryUrl));
|
|
16
12
|
if (type === "yarn-modern") assert.default.ok(true, "ping succeeded");
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ping.cjs","names":[],"sources":["../../../src/tests/ping.ts"],"sourcesContent":["import assert from 'assert';\n\nimport { TestContext, TestDefinition } from '../types';\n\nasync function testPing(ctx: TestContext): Promise<void> {\n const type = ctx.adapter.type;\n
|
|
1
|
+
{"version":3,"file":"ping.cjs","names":[],"sources":["../../../src/tests/ping.ts"],"sourcesContent":["import assert from 'assert';\n\nimport { TestContext, TestDefinition } from '../types';\n\nasync function testPing(ctx: TestContext): Promise<void> {\n const type = ctx.adapter.type;\n\n // Always run from a prepared project: its .npmrc carries the fresh token and\n // shields the test from whatever the developer's global ~/.npmrc has for\n // this registry (a stale token there would turn into a spurious E401).\n const { tempFolder } = await ctx.adapter.prepareProject(\n `verdaccio-ping-${ctx.runId}`,\n '1.0.0',\n ctx.registryUrl,\n ctx.port,\n ctx.token\n );\n const cwd = tempFolder;\n if (type === 'yarn-modern' && ctx.adapter.importPlugin) {\n await ctx.adapter.importPlugin(tempFolder, 'npm-ping');\n }\n\n const jsonFlag = type === 'yarn-modern' ? [] : ['--json'];\n const resp = await ctx.adapter.exec(\n { cwd },\n 'ping',\n ...jsonFlag,\n ...ctx.adapter.registryArg(ctx.registryUrl)\n );\n\n if (type === 'yarn-modern') {\n // yarn npm ping succeeding (exit code 0) is sufficient proof the registry is reachable\n assert.ok(true, 'ping succeeded');\n } else {\n const parsedBody = JSON.parse(resp.stdout);\n assert.strictEqual(\n parsedBody.registry,\n ctx.registryUrl + '/',\n `Expected registry \"${ctx.registryUrl}/\" but got \"${parsedBody.registry}\"`\n );\n }\n}\n\nexport const pingTest: TestDefinition = {\n name: 'ping',\n requires: ['ping'],\n run: testPing,\n};\n"],"mappings":";;;;AAIA,eAAe,SAAS,KAAiC;CACvD,MAAM,OAAO,IAAI,QAAQ;CAKzB,MAAM,EAAE,eAAe,MAAM,IAAI,QAAQ,eACvC,kBAAkB,IAAI,SACtB,SACA,IAAI,aACJ,IAAI,MACJ,IAAI,KACN;CACA,MAAM,MAAM;CACZ,IAAI,SAAS,iBAAiB,IAAI,QAAQ,cACxC,MAAM,IAAI,QAAQ,aAAa,YAAY,UAAU;CAGvD,MAAM,WAAW,SAAS,gBAAgB,CAAC,IAAI,CAAC,QAAQ;CACxD,MAAM,OAAO,MAAM,IAAI,QAAQ,KAC7B,EAAE,IAAI,GACN,QACA,GAAG,UACH,GAAG,IAAI,QAAQ,YAAY,IAAI,WAAW,CAC5C;CAEA,IAAI,SAAS,eAEX,OAAA,QAAO,GAAG,MAAM,gBAAgB;MAC3B;EACL,MAAM,aAAa,KAAK,MAAM,KAAK,MAAM;EACzC,OAAA,QAAO,YACL,WAAW,UACX,IAAI,cAAc,KAClB,sBAAsB,IAAI,YAAY,cAAc,WAAW,SAAS,EAC1E;CACF;AACF;AAEA,IAAa,WAA2B;CACtC,MAAM;CACN,UAAU,CAAC,MAAM;CACjB,KAAK;AACP"}
|
|
@@ -15,7 +15,6 @@ async function testPublish(ctx) {
|
|
|
15
15
|
if (type === "yarn-modern") await ctx.adapter.exec({ cwd: tempFolder }, "install");
|
|
16
16
|
const resp = await ctx.adapter.exec({ cwd: tempFolder }, "publish", "--json", ...ctx.adapter.registryArg(ctx.registryUrl));
|
|
17
17
|
if (type === "yarn-modern") assert.default.ok(resp.stdout.match(/Package archive published/), `Expected "Package archive published" for ${pkgName} but got "${resp.stdout}"`);
|
|
18
|
-
else if (type === "yarn-classic") assert.default.ok(resp.stdout.length > 0, `Expected publish output for ${pkgName}`);
|
|
19
18
|
else if (type === "bun") assert.default.ok(true, `Publish succeeded for ${pkgName}`);
|
|
20
19
|
else try {
|
|
21
20
|
const parsedBody = JSON.parse(resp.stdout);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"publish.cjs","names":[],"sources":["../../../src/tests/publish.ts"],"sourcesContent":["import assert from 'assert';\n\nimport { TestContext, TestDefinition } from '../types';\n\nasync function testPublish(ctx: TestContext): Promise<void> {\n const packages = [\n `verdaccio-memory-${ctx.runId}`,\n `verdaccio-${ctx.runId}`,\n `@verdaccio/foo-${ctx.runId}`,\n `@verdaccio/some-foo-${ctx.runId}`,\n ];\n\n const type = ctx.adapter.type;\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 );\n\n // yarn modern requires install before publish to generate lockfile\n if (type === 'yarn-modern') {\n await ctx.adapter.exec({ cwd: tempFolder }, 'install');\n }\n\n const resp = await ctx.adapter.exec(\n { cwd: tempFolder },\n 'publish',\n '--json',\n ...ctx.adapter.registryArg(ctx.registryUrl)\n );\n\n if (type === 'yarn-modern') {\n assert.ok(\n resp.stdout.match(/Package archive published/),\n `Expected \"Package archive published\" for ${pkgName} but got \"${resp.stdout}\"`\n );\n } else if (type === '
|
|
1
|
+
{"version":3,"file":"publish.cjs","names":[],"sources":["../../../src/tests/publish.ts"],"sourcesContent":["import assert from 'assert';\n\nimport { TestContext, TestDefinition } from '../types';\n\nasync function testPublish(ctx: TestContext): Promise<void> {\n const packages = [\n `verdaccio-memory-${ctx.runId}`,\n `verdaccio-${ctx.runId}`,\n `@verdaccio/foo-${ctx.runId}`,\n `@verdaccio/some-foo-${ctx.runId}`,\n ];\n\n const type = ctx.adapter.type;\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 );\n\n // yarn modern requires install before publish to generate lockfile\n if (type === 'yarn-modern') {\n await ctx.adapter.exec({ cwd: tempFolder }, 'install');\n }\n\n const resp = await ctx.adapter.exec(\n { cwd: tempFolder },\n 'publish',\n '--json',\n ...ctx.adapter.registryArg(ctx.registryUrl)\n );\n\n if (type === 'yarn-modern') {\n assert.ok(\n resp.stdout.match(/Package archive published/),\n `Expected \"Package archive published\" for ${pkgName} but got \"${resp.stdout}\"`\n );\n } else if (type === 'bun') {\n // bun publish outputs text confirmation — exit code 0 is sufficient\n assert.ok(true, `Publish succeeded for ${pkgName}`);\n } else {\n // npm / pnpm\n try {\n const parsedBody = JSON.parse(resp.stdout);\n assert.strictEqual(\n parsedBody.name,\n pkgName,\n `Expected package name \"${pkgName}\" but got \"${parsedBody.name}\"`\n );\n assert.ok(parsedBody.files, `Expected files to be defined for ${pkgName}`);\n } catch {\n // pnpm v11+ native publish doesn't output JSON — exit code 0 is sufficient\n assert.ok(true, `Publish succeeded for ${pkgName}`);\n }\n }\n }\n}\n\nexport const publishTest: TestDefinition = {\n name: 'publish',\n requires: ['publish'],\n run: testPublish,\n};\n"],"mappings":";;;;AAIA,eAAe,YAAY,KAAiC;CAC1D,MAAM,WAAW;EACf,oBAAoB,IAAI;EACxB,aAAa,IAAI;EACjB,kBAAkB,IAAI;EACtB,uBAAuB,IAAI;CAC7B;CAEA,MAAM,OAAO,IAAI,QAAQ;CAEzB,KAAK,MAAM,WAAW,UAAU;EAC9B,MAAM,EAAE,eAAe,MAAM,IAAI,QAAQ,eACvC,SACA,SACA,IAAI,aACJ,IAAI,MACJ,IAAI,KACN;EAGA,IAAI,SAAS,eACX,MAAM,IAAI,QAAQ,KAAK,EAAE,KAAK,WAAW,GAAG,SAAS;EAGvD,MAAM,OAAO,MAAM,IAAI,QAAQ,KAC7B,EAAE,KAAK,WAAW,GAClB,WACA,UACA,GAAG,IAAI,QAAQ,YAAY,IAAI,WAAW,CAC5C;EAEA,IAAI,SAAS,eACX,OAAA,QAAO,GACL,KAAK,OAAO,MAAM,2BAA2B,GAC7C,4CAA4C,QAAQ,YAAY,KAAK,OAAO,EAC9E;OACK,IAAI,SAAS,OAElB,OAAA,QAAO,GAAG,MAAM,yBAAyB,SAAS;OAGlD,IAAI;GACF,MAAM,aAAa,KAAK,MAAM,KAAK,MAAM;GACzC,OAAA,QAAO,YACL,WAAW,MACX,SACA,0BAA0B,QAAQ,aAAa,WAAW,KAAK,EACjE;GACA,OAAA,QAAO,GAAG,WAAW,OAAO,oCAAoC,SAAS;EAC3E,QAAQ;GAEN,OAAA,QAAO,GAAG,MAAM,yBAAyB,SAAS;EACpD;CAEJ;AACF;AAEA,IAAa,cAA8B;CACzC,MAAM;CACN,UAAU,CAAC,SAAS;CACpB,KAAK;AACP"}
|
|
@@ -1,16 +1,20 @@
|
|
|
1
1
|
const require_runtime = require("../_virtual/_rolldown/runtime.cjs");
|
|
2
|
+
const require_publish = require("../utils/publish.cjs");
|
|
2
3
|
let assert = require("assert");
|
|
3
4
|
assert = require_runtime.__toESM(assert, 1);
|
|
4
5
|
//#region src/tests/search.ts
|
|
5
6
|
async function testSearch(ctx) {
|
|
6
|
-
const
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
7
|
+
const pkgName = `verdaccio-search-${ctx.runId}`;
|
|
8
|
+
await require_publish.publishLocalPackage(ctx, pkgName, "1.0.0");
|
|
9
|
+
const { tempFolder } = await ctx.adapter.prepareProject(`search-client-${ctx.runId}`, "1.0.0", ctx.registryUrl, ctx.port, ctx.token);
|
|
10
|
+
const resp = await ctx.adapter.exec({ cwd: tempFolder }, "search", pkgName, "--json", ...ctx.adapter.registryArg(ctx.registryUrl));
|
|
11
|
+
const pkgFind = JSON.parse(resp.stdout).find((item) => item.name === pkgName);
|
|
12
|
+
assert.default.ok(pkgFind, `Expected to find ${pkgName} in search results`);
|
|
13
|
+
assert.default.strictEqual(pkgFind.name, pkgName);
|
|
10
14
|
}
|
|
11
15
|
var searchTest = {
|
|
12
16
|
name: "search",
|
|
13
|
-
requires: ["search"],
|
|
17
|
+
requires: ["search", "publish"],
|
|
14
18
|
run: testSearch
|
|
15
19
|
};
|
|
16
20
|
//#endregion
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"search.cjs","names":[],"sources":["../../../src/tests/search.ts"],"sourcesContent":["import assert from 'assert';\n\nimport { TestContext, TestDefinition } from '../types';\n\nasync function testSearch(ctx: TestContext): Promise<void> {\n const resp = await ctx.adapter.exec(\n {},\n 'search',\n
|
|
1
|
+
{"version":3,"file":"search.cjs","names":[],"sources":["../../../src/tests/search.ts"],"sourcesContent":["import assert from 'assert';\n\nimport { TestContext, TestDefinition } from '../types';\nimport { publishLocalPackage } from '../utils/publish';\n\nasync function testSearch(ctx: TestContext): Promise<void> {\n // Publish the package we search for — searching an npmjs package would need\n // an uplink and the suite must run offline.\n const pkgName = `verdaccio-search-${ctx.runId}`;\n await publishLocalPackage(ctx, pkgName, '1.0.0');\n\n // Run from a prepared project so its .npmrc (fresh token) shields the test\n // from stale credentials in the developer's global ~/.npmrc.\n const { tempFolder } = await ctx.adapter.prepareProject(\n `search-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 'search',\n pkgName,\n '--json',\n ...ctx.adapter.registryArg(ctx.registryUrl)\n );\n const parsedBody = JSON.parse(resp.stdout);\n const pkgFind = parsedBody.find((item: any) => item.name === pkgName);\n assert.ok(pkgFind, `Expected to find ${pkgName} in search results`);\n assert.strictEqual(pkgFind.name, pkgName);\n}\n\nexport const searchTest: TestDefinition = {\n name: 'search',\n requires: ['search', 'publish'],\n run: testSearch,\n};\n"],"mappings":";;;;;AAKA,eAAe,WAAW,KAAiC;CAGzD,MAAM,UAAU,oBAAoB,IAAI;CACxC,MAAM,gBAAA,oBAAoB,KAAK,SAAS,OAAO;CAI/C,MAAM,EAAE,eAAe,MAAM,IAAI,QAAQ,eACvC,iBAAiB,IAAI,SACrB,SACA,IAAI,aACJ,IAAI,MACJ,IAAI,KACN;CAEA,MAAM,OAAO,MAAM,IAAI,QAAQ,KAC7B,EAAE,KAAK,WAAW,GAClB,UACA,SACA,UACA,GAAG,IAAI,QAAQ,YAAY,IAAI,WAAW,CAC5C;CAEA,MAAM,UADa,KAAK,MAAM,KAAK,MACnB,CAAA,CAAW,MAAM,SAAc,KAAK,SAAS,OAAO;CACpE,OAAA,QAAO,GAAG,SAAS,oBAAoB,QAAQ,mBAAmB;CAClE,OAAA,QAAO,YAAY,QAAQ,MAAM,OAAO;AAC1C;AAEA,IAAa,aAA6B;CACxC,MAAM;CACN,UAAU,CAAC,UAAU,SAAS;CAC9B,KAAK;AACP"}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
//#region src/utils/e2e-config.ts
|
|
2
|
+
/**
|
|
3
|
+
* Recommended Verdaccio config for running the full e2e-cli battery,
|
|
4
|
+
* printed by `verdaccio-e2e --print-config`. It is what the consuming repos'
|
|
5
|
+
* CI jobs and scripts/run-e2e.sh feed to `verdaccio --config`:
|
|
6
|
+
*
|
|
7
|
+
* - max_body_size raised so scenario:tarballs can publish ~30 MB packages
|
|
8
|
+
* (Verdaccio's default is 10mb)
|
|
9
|
+
* - an `e2emock` uplink + `e2e-uplink-*` package pattern wired to the mock
|
|
10
|
+
* uplink server that scenario:uplink-failure starts on E2E_UPLINK_PORT
|
|
11
|
+
* (short timeout so the "slow uplink" case completes quickly; high
|
|
12
|
+
* max_fails so simulated failures don't trip the circuit breaker for
|
|
13
|
+
* later sub-tests)
|
|
14
|
+
* - NO npmjs uplink: the whole suite publishes every package it consumes,
|
|
15
|
+
* so the registry under test never talks to the outside world and the
|
|
16
|
+
* battery runs fully offline (and deterministically in CI)
|
|
17
|
+
*
|
|
18
|
+
* Relative paths (storage/htpasswd) resolve next to the config file, so write
|
|
19
|
+
* it into a disposable directory.
|
|
20
|
+
*/
|
|
21
|
+
var DEFAULT_UPLINK_PORT = 4874;
|
|
22
|
+
function buildE2EConfig(uplinkPort = DEFAULT_UPLINK_PORT) {
|
|
23
|
+
return `# Generated by @verdaccio/e2e-cli --print-config
|
|
24
|
+
storage: ./storage
|
|
25
|
+
auth:
|
|
26
|
+
htpasswd:
|
|
27
|
+
file: ./htpasswd
|
|
28
|
+
max_users: 1000
|
|
29
|
+
web:
|
|
30
|
+
enable: true
|
|
31
|
+
title: Verdaccio E2E
|
|
32
|
+
max_body_size: 100mb
|
|
33
|
+
uplinks:
|
|
34
|
+
e2emock:
|
|
35
|
+
url: http://localhost:${uplinkPort}/
|
|
36
|
+
timeout: 3s
|
|
37
|
+
retry: 0
|
|
38
|
+
max_fails: 100
|
|
39
|
+
fail_timeout: 1s
|
|
40
|
+
packages:
|
|
41
|
+
'e2e-uplink-*':
|
|
42
|
+
access: $all
|
|
43
|
+
proxy: e2emock
|
|
44
|
+
'@*/*':
|
|
45
|
+
access: $all
|
|
46
|
+
publish: $authenticated
|
|
47
|
+
unpublish: $authenticated
|
|
48
|
+
'**':
|
|
49
|
+
access: $all
|
|
50
|
+
publish: $authenticated
|
|
51
|
+
unpublish: $authenticated
|
|
52
|
+
log: { type: stdout, format: pretty, level: warn }
|
|
53
|
+
`;
|
|
54
|
+
}
|
|
55
|
+
//#endregion
|
|
56
|
+
exports.DEFAULT_UPLINK_PORT = DEFAULT_UPLINK_PORT;
|
|
57
|
+
exports.buildE2EConfig = buildE2EConfig;
|
|
58
|
+
|
|
59
|
+
//# sourceMappingURL=e2e-config.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"e2e-config.cjs","names":[],"sources":["../../../src/utils/e2e-config.ts"],"sourcesContent":["/**\n * Recommended Verdaccio config for running the full e2e-cli battery,\n * printed by `verdaccio-e2e --print-config`. It is what the consuming repos'\n * CI jobs and scripts/run-e2e.sh feed to `verdaccio --config`:\n *\n * - max_body_size raised so scenario:tarballs can publish ~30 MB packages\n * (Verdaccio's default is 10mb)\n * - an `e2emock` uplink + `e2e-uplink-*` package pattern wired to the mock\n * uplink server that scenario:uplink-failure starts on E2E_UPLINK_PORT\n * (short timeout so the \"slow uplink\" case completes quickly; high\n * max_fails so simulated failures don't trip the circuit breaker for\n * later sub-tests)\n * - NO npmjs uplink: the whole suite publishes every package it consumes,\n * so the registry under test never talks to the outside world and the\n * battery runs fully offline (and deterministically in CI)\n *\n * Relative paths (storage/htpasswd) resolve next to the config file, so write\n * it into a disposable directory.\n */\nexport const DEFAULT_UPLINK_PORT = 4874;\n\nexport function buildE2EConfig(uplinkPort: number = DEFAULT_UPLINK_PORT): string {\n return `# Generated by @verdaccio/e2e-cli --print-config\nstorage: ./storage\nauth:\n htpasswd:\n file: ./htpasswd\n max_users: 1000\nweb:\n enable: true\n title: Verdaccio E2E\nmax_body_size: 100mb\nuplinks:\n e2emock:\n url: http://localhost:${uplinkPort}/\n timeout: 3s\n retry: 0\n max_fails: 100\n fail_timeout: 1s\npackages:\n 'e2e-uplink-*':\n access: $all\n proxy: e2emock\n '@*/*':\n access: $all\n publish: $authenticated\n unpublish: $authenticated\n '**':\n access: $all\n publish: $authenticated\n unpublish: $authenticated\nlog: { type: stdout, format: pretty, level: warn }\n`;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAmBA,IAAa,sBAAsB;AAEnC,SAAgB,eAAe,aAAqB,qBAA6B;CAC/E,OAAO;;;;;;;;;;;;4BAYmB,WAAW;;;;;;;;;;;;;;;;;;;AAmBvC"}
|