@harperfast/integration-testing 0.1.0 → 0.3.0-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/README.md +71 -5
- package/dist/harperLifecycle.d.ts +176 -0
- package/dist/harperLifecycle.js +71 -40
- package/dist/harperLifecycle.js.map +1 -1
- package/dist/index.d.ts +3 -0
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/loopbackAddressPool.d.ts +50 -0
- package/dist/run.d.ts +2 -0
- package/dist/security/certGenUtils.d.ts +52 -0
- package/dist/security/certGenUtils.js +170 -0
- package/dist/security/certGenUtils.js.map +1 -0
- package/dist/security/crl/generate-test-certs.d.ts +34 -0
- package/dist/security/crl/generate-test-certs.js +81 -0
- package/dist/security/crl/generate-test-certs.js.map +1 -0
- package/dist/security/ocsp/generate-test-certs.d.ts +55 -0
- package/dist/security/ocsp/generate-test-certs.js +106 -0
- package/dist/security/ocsp/generate-test-certs.js.map +1 -0
- package/dist/security/ocspServer.d.ts +18 -0
- package/dist/security/ocspServer.js +132 -0
- package/dist/security/ocspServer.js.map +1 -0
- package/dist/securityServices.d.ts +49 -0
- package/dist/securityServices.js +120 -0
- package/dist/securityServices.js.map +1 -0
- package/dist/src/harperLifecycle.d.ts +156 -0
- package/dist/src/harperLifecycle.js +315 -0
- package/dist/src/harperLifecycle.js.map +1 -0
- package/dist/src/index.d.ts +3 -0
- package/dist/src/index.js +4 -0
- package/dist/src/index.js.map +1 -0
- package/dist/src/loopbackAddressPool.d.ts +50 -0
- package/dist/src/loopbackAddressPool.js +337 -0
- package/dist/src/loopbackAddressPool.js.map +1 -0
- package/dist/src/run.d.ts +2 -0
- package/dist/src/run.js +94 -0
- package/dist/src/run.js.map +1 -0
- package/dist/src/security/certGenUtils.d.ts +52 -0
- package/dist/src/security/certGenUtils.js +170 -0
- package/dist/src/security/certGenUtils.js.map +1 -0
- package/dist/src/security/crl/generate-test-certs.d.ts +34 -0
- package/dist/src/security/crl/generate-test-certs.js +81 -0
- package/dist/src/security/crl/generate-test-certs.js.map +1 -0
- package/dist/src/security/ocsp/generate-test-certs.d.ts +55 -0
- package/dist/src/security/ocsp/generate-test-certs.js +106 -0
- package/dist/src/security/ocsp/generate-test-certs.js.map +1 -0
- package/dist/src/security/ocspServer.d.ts +18 -0
- package/dist/src/security/ocspServer.js +132 -0
- package/dist/src/security/ocspServer.js.map +1 -0
- package/dist/src/securityServices.d.ts +49 -0
- package/dist/src/securityServices.js +120 -0
- package/dist/src/securityServices.js.map +1 -0
- package/dist/src/targz.d.ts +6 -0
- package/dist/src/targz.js +20 -0
- package/dist/src/targz.js.map +1 -0
- package/dist/targz.d.ts +6 -0
- package/package.json +5 -3
|
@@ -0,0 +1,315 @@
|
|
|
1
|
+
import { spawn, ChildProcess } from 'node:child_process';
|
|
2
|
+
import { createWriteStream, existsSync, rmSync } from 'node:fs';
|
|
3
|
+
import { join, basename } from 'node:path';
|
|
4
|
+
import { tmpdir } from 'node:os';
|
|
5
|
+
import { mkdtemp, mkdir, rm, cp } from 'node:fs/promises';
|
|
6
|
+
import {} from 'node:test';
|
|
7
|
+
import { getNextAvailableLoopbackAddress, releaseLoopbackAddress } from "./loopbackAddressPool.js";
|
|
8
|
+
import { ok, equal } from 'node:assert';
|
|
9
|
+
import { createRequire } from 'node:module';
|
|
10
|
+
/**
|
|
11
|
+
* Creates a plain object satisfying HarperTestContext, for use outside node:test
|
|
12
|
+
* (e.g. as a Playwright worker fixture).
|
|
13
|
+
*
|
|
14
|
+
* @param name Optional name for log directory naming (e.g. Playwright worker index).
|
|
15
|
+
*/
|
|
16
|
+
export function createHarperContext(name) {
|
|
17
|
+
return { name };
|
|
18
|
+
}
|
|
19
|
+
// Constants
|
|
20
|
+
const HTTP_PORT = 9926;
|
|
21
|
+
const HTTPS_PORT = 9927;
|
|
22
|
+
export const OPERATIONS_API_PORT = 9925;
|
|
23
|
+
export const DEFAULT_ADMIN_USERNAME = 'admin';
|
|
24
|
+
export const DEFAULT_ADMIN_PASSWORD = 'Abc1234!';
|
|
25
|
+
export const DEFAULT_STARTUP_TIMEOUT_MS = process.env.HARPER_INTEGRATION_TEST_STARTUP_TIMEOUT_MS
|
|
26
|
+
? parseInt(process.env.HARPER_INTEGRATION_TEST_STARTUP_TIMEOUT_MS, 10)
|
|
27
|
+
: 30000;
|
|
28
|
+
const LOG_DIR = process.env.HARPER_INTEGRATION_TEST_LOG_DIR;
|
|
29
|
+
/**
|
|
30
|
+
* Gets the path to the Harper CLI script.
|
|
31
|
+
*
|
|
32
|
+
* Resolution order:
|
|
33
|
+
* 1. `harperBinPath` argument
|
|
34
|
+
* 2. `HARPER_INTEGRATION_TEST_INSTALL_SCRIPT` environment variable
|
|
35
|
+
* 3. Auto-resolved from 'harper' package in node_modules
|
|
36
|
+
*
|
|
37
|
+
* @returns The absolute path to the Harper CLI entry script
|
|
38
|
+
* @throws {AssertionError} If the script cannot be found
|
|
39
|
+
*/
|
|
40
|
+
function getHarperScript(harperBinPath) {
|
|
41
|
+
// 1. Explicit option
|
|
42
|
+
if (harperBinPath) {
|
|
43
|
+
ok(existsSync(harperBinPath), `Harper script not found at provided harperBinPath: ${harperBinPath}`);
|
|
44
|
+
return harperBinPath;
|
|
45
|
+
}
|
|
46
|
+
// 2. Environment variable
|
|
47
|
+
const envPath = process.env.HARPER_INTEGRATION_TEST_INSTALL_SCRIPT;
|
|
48
|
+
if (envPath) {
|
|
49
|
+
ok(existsSync(envPath), `Harper script not found at HARPER_INTEGRATION_TEST_INSTALL_SCRIPT path: ${envPath}`);
|
|
50
|
+
return envPath;
|
|
51
|
+
}
|
|
52
|
+
// 3. Auto-resolve from node_modules
|
|
53
|
+
try {
|
|
54
|
+
const require = createRequire(import.meta.url);
|
|
55
|
+
const resolved = require.resolve('harper/dist/bin/harper.js');
|
|
56
|
+
if (existsSync(resolved)) {
|
|
57
|
+
return resolved;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
catch {
|
|
61
|
+
// harper package not found in node_modules
|
|
62
|
+
}
|
|
63
|
+
throw new Error(`Harper CLI script not found. Provide the path via:\n` +
|
|
64
|
+
` - harperBinPath option: startHarper(ctx, { harperBinPath: '/path/to/dist/bin/harper.js' })\n` +
|
|
65
|
+
` - HARPER_INTEGRATION_TEST_INSTALL_SCRIPT environment variable\n` +
|
|
66
|
+
` - Install 'harper' as a dependency in your project`);
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Sanitizes a string for use as a filesystem directory name.
|
|
70
|
+
*/
|
|
71
|
+
function sanitizeForFilesystem(name) {
|
|
72
|
+
return name
|
|
73
|
+
.replace(/[^a-zA-Z0-9_-]/g, '_')
|
|
74
|
+
.replace(/_+/g, '_')
|
|
75
|
+
.substring(0, 100);
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Runs a Harper CLI command and captures output.
|
|
79
|
+
*
|
|
80
|
+
* When `logDir` is provided, stdout and stderr are also written to files
|
|
81
|
+
* (`stdout.log` and `stderr.log`) in that directory.
|
|
82
|
+
*
|
|
83
|
+
* @throws {AssertionError} If the command exits with a non-zero status code
|
|
84
|
+
*/
|
|
85
|
+
function runHarperCommand({ args, env, completionMessage, logDir, harperBinPath, }) {
|
|
86
|
+
const harperScript = getHarperScript(harperBinPath);
|
|
87
|
+
const proc = spawn('node', ['--trace-warnings', '--force-node-api-uncaught-exceptions-policy=true', harperScript, ...args], {
|
|
88
|
+
env: { ...process.env, ...env },
|
|
89
|
+
});
|
|
90
|
+
let stdoutStream;
|
|
91
|
+
let stderrStream;
|
|
92
|
+
if (logDir) {
|
|
93
|
+
stdoutStream = createWriteStream(join(logDir, 'stdout.log'));
|
|
94
|
+
stderrStream = createWriteStream(join(logDir, 'stderr.log'));
|
|
95
|
+
}
|
|
96
|
+
return new Promise((resolve, reject) => {
|
|
97
|
+
let stdout = '';
|
|
98
|
+
let stderr = '';
|
|
99
|
+
let timer = setTimeout(() => {
|
|
100
|
+
let errorMessage = `Harper process timed out after ${DEFAULT_STARTUP_TIMEOUT_MS}ms`;
|
|
101
|
+
if (stdout) {
|
|
102
|
+
errorMessage += `\n\nstdout:\n${stdout}`;
|
|
103
|
+
}
|
|
104
|
+
if (stderr) {
|
|
105
|
+
errorMessage += `\n\nstderr:\n${stderr}`;
|
|
106
|
+
}
|
|
107
|
+
reject(errorMessage);
|
|
108
|
+
proc.kill();
|
|
109
|
+
}, DEFAULT_STARTUP_TIMEOUT_MS);
|
|
110
|
+
proc.stdout?.on('data', (data) => {
|
|
111
|
+
const dataString = data.toString();
|
|
112
|
+
stdoutStream?.write(data);
|
|
113
|
+
if (completionMessage && dataString.includes(completionMessage)) {
|
|
114
|
+
clearTimeout(timer);
|
|
115
|
+
resolve(proc);
|
|
116
|
+
}
|
|
117
|
+
stdout += dataString;
|
|
118
|
+
});
|
|
119
|
+
proc.stderr?.on('data', (data) => {
|
|
120
|
+
stderrStream?.write(data);
|
|
121
|
+
stderr += data.toString();
|
|
122
|
+
});
|
|
123
|
+
proc.on('error', (error) => {
|
|
124
|
+
reject(error);
|
|
125
|
+
});
|
|
126
|
+
proc.on('exit', (statusCode, signal) => {
|
|
127
|
+
clearTimeout(timer);
|
|
128
|
+
if (statusCode === 0) {
|
|
129
|
+
resolve(proc);
|
|
130
|
+
}
|
|
131
|
+
else {
|
|
132
|
+
let errorMessage = `Harper process failed with exit code/signal ${statusCode ?? signal}`;
|
|
133
|
+
stderrStream?.write(errorMessage);
|
|
134
|
+
if (stderr) {
|
|
135
|
+
errorMessage += `\n\nstderr:\n${stderr}`;
|
|
136
|
+
}
|
|
137
|
+
reject(errorMessage);
|
|
138
|
+
}
|
|
139
|
+
stdoutStream?.end();
|
|
140
|
+
stderrStream?.end();
|
|
141
|
+
});
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* Sets up a Harper instance with a component pre-installed from a local directory.
|
|
146
|
+
*
|
|
147
|
+
* Copies `fixturePath` into `{dataRootDir}/components/{name}` before Harper starts,
|
|
148
|
+
* so the component is available on the first request without a post-startup deploy.
|
|
149
|
+
* Use this when tests need a known route available at startup (e.g. mTLS cert tests).
|
|
150
|
+
*
|
|
151
|
+
* @param ctx - The test context to populate with Harper instance details
|
|
152
|
+
* @param fixturePath - Absolute path to the component directory to pre-install
|
|
153
|
+
* @param options - Optional configuration for the setup process
|
|
154
|
+
*/
|
|
155
|
+
export async function setupHarperWithFixture(ctx, fixturePath, options) {
|
|
156
|
+
const dataRootDirPrefix = join(process.env.HARPER_INTEGRATION_TEST_INSTALL_PARENT_DIR || tmpdir(), 'harper-integration-test-');
|
|
157
|
+
const dataRootDir = await mkdtemp(dataRootDirPrefix);
|
|
158
|
+
await cp(fixturePath, join(dataRootDir, 'components', basename(fixturePath)), { recursive: true });
|
|
159
|
+
ctx.harper = { dataRootDir };
|
|
160
|
+
return startHarper(ctx, options);
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* Sets up and starts a Harper instance for testing.
|
|
164
|
+
*
|
|
165
|
+
* @param ctx - The test context to populate with Harper instance details
|
|
166
|
+
* @param options - Optional configuration for the setup process
|
|
167
|
+
* @returns The context with the `harper` property populated
|
|
168
|
+
*
|
|
169
|
+
* @example
|
|
170
|
+
* ```ts
|
|
171
|
+
* suite('My tests', (ctx: ContextWithHarper) => {
|
|
172
|
+
* before(async () => {
|
|
173
|
+
* await startHarper(ctx);
|
|
174
|
+
* });
|
|
175
|
+
*
|
|
176
|
+
* after(async () => {
|
|
177
|
+
* await teardownHarper(ctx);
|
|
178
|
+
* });
|
|
179
|
+
*
|
|
180
|
+
* test('can connect', async () => {
|
|
181
|
+
* const response = await fetch(ctx.harper.httpURL);
|
|
182
|
+
* // ...
|
|
183
|
+
* });
|
|
184
|
+
* });
|
|
185
|
+
* ```
|
|
186
|
+
*/
|
|
187
|
+
export async function startHarper(ctx, options) {
|
|
188
|
+
const dataRootDirPrefix = join(process.env.HARPER_INTEGRATION_TEST_INSTALL_PARENT_DIR || tmpdir(), `harper-integration-test-`);
|
|
189
|
+
const dataRootDir = ctx.harper?.dataRootDir ?? (await mkdtemp(dataRootDirPrefix));
|
|
190
|
+
const loopbackAddress = ctx.harper?.hostname ?? (await getNextAvailableLoopbackAddress());
|
|
191
|
+
// Set up per-suite log directory when HARPER_INTEGRATION_TEST_LOG_DIR is configured
|
|
192
|
+
let logDir;
|
|
193
|
+
if (LOG_DIR) {
|
|
194
|
+
const suiteName = sanitizeForFilesystem(ctx.name || 'unknown');
|
|
195
|
+
logDir = join(LOG_DIR, `${suiteName}-${sanitizeForFilesystem(loopbackAddress)}`);
|
|
196
|
+
await mkdir(logDir, { recursive: true });
|
|
197
|
+
}
|
|
198
|
+
// Point Harper's log directory to the suite log dir so hdb.log is preserved for upload
|
|
199
|
+
const config = { ...options?.config };
|
|
200
|
+
if (logDir) {
|
|
201
|
+
config.logging = { ...config.logging, root: logDir };
|
|
202
|
+
// Clean up log directory on successful exit — only keep logs when tests fail
|
|
203
|
+
process.on('exit', (code) => {
|
|
204
|
+
if (code === 0) {
|
|
205
|
+
try {
|
|
206
|
+
rmSync(logDir, { recursive: true, force: true });
|
|
207
|
+
}
|
|
208
|
+
catch { }
|
|
209
|
+
}
|
|
210
|
+
});
|
|
211
|
+
}
|
|
212
|
+
const args = [
|
|
213
|
+
`--ROOTPATH=${dataRootDir}`,
|
|
214
|
+
'--DEFAULTS_MODE=dev',
|
|
215
|
+
`--HDB_ADMIN_USERNAME=${DEFAULT_ADMIN_USERNAME}`,
|
|
216
|
+
`--HDB_ADMIN_PASSWORD=${DEFAULT_ADMIN_PASSWORD}`,
|
|
217
|
+
'--THREADS_COUNT=1',
|
|
218
|
+
'--THREADS_DEBUG=false',
|
|
219
|
+
`--NODE_HOSTNAME=${loopbackAddress}`,
|
|
220
|
+
`--HTTP_PORT=${loopbackAddress}:${HTTP_PORT}`,
|
|
221
|
+
`--OPERATIONSAPI_NETWORK_PORT=${loopbackAddress}:${OPERATIONS_API_PORT}`,
|
|
222
|
+
'--LOGGING_LEVEL=debug',
|
|
223
|
+
'--LOGGING_STDSTREAMS=false',
|
|
224
|
+
];
|
|
225
|
+
// Bind secure port if HTTPS is needed (mTLS or other TLS config present)
|
|
226
|
+
if (options?.config?.http?.mtls !== undefined || options?.config?.tls !== undefined) {
|
|
227
|
+
args.push(`--HTTP_SECUREPORT=${loopbackAddress}:${HTTPS_PORT}`);
|
|
228
|
+
}
|
|
229
|
+
// HARPER_SET_CONFIG must be passed as an environment variable, not a CLI arg,
|
|
230
|
+
// because applyRuntimeEnvVarConfig reads from process.env.HARPER_SET_CONFIG
|
|
231
|
+
const harperEnv = {
|
|
232
|
+
HARPER_SET_CONFIG: JSON.stringify(config),
|
|
233
|
+
...options?.env,
|
|
234
|
+
};
|
|
235
|
+
const harperProcess = await runHarperCommand({
|
|
236
|
+
args,
|
|
237
|
+
env: harperEnv,
|
|
238
|
+
completionMessage: 'successfully started',
|
|
239
|
+
logDir,
|
|
240
|
+
harperBinPath: options?.harperBinPath,
|
|
241
|
+
});
|
|
242
|
+
ctx.harper = {
|
|
243
|
+
dataRootDir,
|
|
244
|
+
admin: {
|
|
245
|
+
username: DEFAULT_ADMIN_USERNAME,
|
|
246
|
+
password: DEFAULT_ADMIN_PASSWORD,
|
|
247
|
+
},
|
|
248
|
+
httpURL: `http://${loopbackAddress}:${HTTP_PORT}`,
|
|
249
|
+
operationsAPIURL: `http://${loopbackAddress}:${OPERATIONS_API_PORT}`,
|
|
250
|
+
hostname: loopbackAddress,
|
|
251
|
+
process: harperProcess,
|
|
252
|
+
logDir,
|
|
253
|
+
};
|
|
254
|
+
return ctx;
|
|
255
|
+
}
|
|
256
|
+
/**
|
|
257
|
+
* Kill harper process (can be used for teardown, or killing it before a restart)
|
|
258
|
+
* @param ctx
|
|
259
|
+
*/
|
|
260
|
+
export async function killHarper(ctx) {
|
|
261
|
+
await new Promise((resolve) => {
|
|
262
|
+
let timer;
|
|
263
|
+
ctx.harper.process.on('exit', () => {
|
|
264
|
+
resolve();
|
|
265
|
+
clearTimeout(timer);
|
|
266
|
+
});
|
|
267
|
+
ctx.harper.process.kill();
|
|
268
|
+
timer = setTimeout(() => {
|
|
269
|
+
try {
|
|
270
|
+
ctx.harper.process.kill('SIGKILL');
|
|
271
|
+
}
|
|
272
|
+
catch {
|
|
273
|
+
// possible that the process terminated but the exit event hasn't fired yet
|
|
274
|
+
}
|
|
275
|
+
resolve();
|
|
276
|
+
}, 200);
|
|
277
|
+
});
|
|
278
|
+
}
|
|
279
|
+
/**
|
|
280
|
+
* Tears down a Harper instance and cleans up all resources.
|
|
281
|
+
*
|
|
282
|
+
* This function stops the Harper instance, releases the loopback address,
|
|
283
|
+
* and removes the installation directory.
|
|
284
|
+
* @param ctx - The test context with Harper instance details
|
|
285
|
+
*
|
|
286
|
+
* @example
|
|
287
|
+
* ```ts
|
|
288
|
+
* suite('My tests', (ctx: ContextWithHarper) => {
|
|
289
|
+
* before(async () => {
|
|
290
|
+
* await startHarper(ctx);
|
|
291
|
+
* });
|
|
292
|
+
*
|
|
293
|
+
* after(async () => {
|
|
294
|
+
* await teardownHarper(ctx);
|
|
295
|
+
* });
|
|
296
|
+
* });
|
|
297
|
+
* ```
|
|
298
|
+
*/
|
|
299
|
+
export async function teardownHarper(ctx) {
|
|
300
|
+
await killHarper(ctx);
|
|
301
|
+
await releaseLoopbackAddress(ctx.harper.hostname);
|
|
302
|
+
// a few retries are typically necessary, might take a sec for a process to finish, especially since rocksdb may be flushing
|
|
303
|
+
await rm(ctx.harper.dataRootDir, { recursive: true, force: true, maxRetries: 4 });
|
|
304
|
+
}
|
|
305
|
+
export async function sendOperation(context, operation) {
|
|
306
|
+
const response = await fetch(context.operationsAPIURL, {
|
|
307
|
+
method: 'POST',
|
|
308
|
+
headers: { 'Content-Type': 'application/json' },
|
|
309
|
+
body: JSON.stringify(operation),
|
|
310
|
+
});
|
|
311
|
+
const responseData = await response.json();
|
|
312
|
+
equal(response.status, 200, JSON.stringify(responseData));
|
|
313
|
+
return responseData;
|
|
314
|
+
}
|
|
315
|
+
//# sourceMappingURL=harperLifecycle.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"harperLifecycle.js","sourceRoot":"","sources":["../../src/harperLifecycle.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
export { startHarper, setupHarperWithFixture, killHarper, teardownHarper, sendOperation, createHarperContext, OPERATIONS_API_PORT, DEFAULT_ADMIN_USERNAME, DEFAULT_ADMIN_PASSWORD, DEFAULT_STARTUP_TIMEOUT_MS, type StartHarperOptions, type HarperContext, type HarperTestContext, type StartedHarperTestContext, type ContextWithHarper, } from './harperLifecycle.ts';
|
|
2
|
+
export { validateLoopbackAddressPool, getNextAvailableLoopbackAddress, releaseLoopbackAddress, releaseAllLoopbackAddressesForCurrentProcess, } from './loopbackAddressPool.ts';
|
|
3
|
+
export { targz } from './targz.ts';
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export { startHarper, setupHarperWithFixture, killHarper, teardownHarper, sendOperation, createHarperContext, OPERATIONS_API_PORT, DEFAULT_ADMIN_USERNAME, DEFAULT_ADMIN_PASSWORD, DEFAULT_STARTUP_TIMEOUT_MS, } from "./harperLifecycle.js";
|
|
2
|
+
export { validateLoopbackAddressPool, getNextAvailableLoopbackAddress, releaseLoopbackAddress, releaseAllLoopbackAddressesForCurrentProcess, } from "./loopbackAddressPool.js";
|
|
3
|
+
export { targz } from "./targz.js";
|
|
4
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACN,WAAW,EACX,sBAAsB,EACtB,UAAU,EACV,cAAc,EACd,aAAa,EACb,mBAAmB,EACnB,mBAAmB,EACnB,sBAAsB,EACtB,sBAAsB,EACtB,0BAA0B,GAM1B,MAAM,sBAAsB,CAAC;AAE9B,OAAO,EACN,2BAA2B,EAC3B,+BAA+B,EAC/B,sBAAsB,EACtB,4CAA4C,GAC5C,MAAM,0BAA0B,CAAC;AAElC,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This method attempts to validate all loopback addresses in the pool by trying to
|
|
3
|
+
* bind to each one. It returns an object containing arrays of successfully bound
|
|
4
|
+
* loopback addresses and those that failed along with their errors.
|
|
5
|
+
*
|
|
6
|
+
* It will check all loopback addresses from 127.0.0.1 to 127.0.0.32 (by default).
|
|
7
|
+
*
|
|
8
|
+
* Use the HARPER_INTEGRATION_TEST_LOOPBACK_POOL_COUNT environment variable to
|
|
9
|
+
* adjust the number of loopback addresses to validate (up to 255).
|
|
10
|
+
*/
|
|
11
|
+
export declare function validateLoopbackAddressPool(): Promise<{
|
|
12
|
+
successful: string[];
|
|
13
|
+
failed: {
|
|
14
|
+
loopbackAddress: string;
|
|
15
|
+
error: Error;
|
|
16
|
+
}[];
|
|
17
|
+
}>;
|
|
18
|
+
/**
|
|
19
|
+
* Retrieves the next available loopback address from the pool using a file-based
|
|
20
|
+
* locking mechanism to safely allocate addresses across concurrent test processes.
|
|
21
|
+
*
|
|
22
|
+
* **How it works:**
|
|
23
|
+
* 1. Acquires a file-based lock to prevent race conditions with other processes
|
|
24
|
+
* 2. Reads the pool state (a JSON array of process IDs, with null for available slots)
|
|
25
|
+
* 3. Finds the first available (null) slot and assigns the current process PID to it
|
|
26
|
+
* 4. Writes the updated pool back to disk and releases the lock
|
|
27
|
+
* 5. Validates that the allocated address can actually be bound to
|
|
28
|
+
* 6. Returns the loopback address (e.g., "127.0.0.2")
|
|
29
|
+
*
|
|
30
|
+
* If no addresses are available, waits and retries until one becomes available.
|
|
31
|
+
*
|
|
32
|
+
* **Pool file location:** `${tmpdir()}/harper-integration-test-loopback-pool.json`
|
|
33
|
+
* **Lock file location:** `${tmpdir()}/harper-integration-test-loopback-pool.lock`
|
|
34
|
+
*
|
|
35
|
+
* @returns A promise that resolves with an allocated loopback address
|
|
36
|
+
* @throws {LoopbackAddressValidationError} If the allocated address cannot be bound to
|
|
37
|
+
*/
|
|
38
|
+
export declare function getNextAvailableLoopbackAddress(): Promise<string>;
|
|
39
|
+
/**
|
|
40
|
+
* Releases a loopback address back to the pool, making it available for other processes.
|
|
41
|
+
*
|
|
42
|
+
* @param address The loopback address to release (e.g., "127.0.0.1")
|
|
43
|
+
* @throws InvalidLoopbackAddressError if the address format is invalid
|
|
44
|
+
*/
|
|
45
|
+
export declare function releaseLoopbackAddress(address: string): Promise<void>;
|
|
46
|
+
/**
|
|
47
|
+
* Releases all loopback addresses assigned to the current process.
|
|
48
|
+
* Useful for cleanup during graceful shutdown.
|
|
49
|
+
*/
|
|
50
|
+
export declare function releaseAllLoopbackAddressesForCurrentProcess(): Promise<void>;
|