@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
package/README.md
CHANGED
|
@@ -97,30 +97,96 @@ Kills Harper, releases the loopback address back to the pool, and removes the in
|
|
|
97
97
|
|
|
98
98
|
Helper to POST an operation to the Operations API and assert HTTP 200.
|
|
99
99
|
|
|
100
|
-
### `
|
|
100
|
+
### `createHarperContext(name?)`
|
|
101
101
|
|
|
102
|
-
|
|
102
|
+
Creates a plain object satisfying `HarperTestContext`, for use outside `node:test` (e.g. Playwright worker fixtures). The `name` is optional and used for log directory naming.
|
|
103
|
+
|
|
104
|
+
```ts
|
|
105
|
+
// Playwright example
|
|
106
|
+
const ctx = createHarperContext(`worker-${workerInfo.workerIndex}`);
|
|
107
|
+
await startHarper(ctx);
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
### Types
|
|
111
|
+
|
|
112
|
+
There are four related TypeScript types — it helps to understand how they compose.
|
|
113
|
+
|
|
114
|
+
**`HarperContext`** is the instance data object stored at `ctx.harper` after `startHarper()` resolves. It describes a running Harper instance:
|
|
103
115
|
|
|
104
116
|
```ts
|
|
105
117
|
interface HarperContext {
|
|
106
|
-
dataRootDir: string; // Harper install directory
|
|
118
|
+
dataRootDir: string; // absolute path to the Harper install directory
|
|
107
119
|
admin: { username: string; password: string };
|
|
108
120
|
httpURL: string; // e.g. 'http://127.0.0.2:9926'
|
|
109
121
|
operationsAPIURL: string; // e.g. 'http://127.0.0.2:9925'
|
|
110
122
|
hostname: string; // e.g. '127.0.0.2'
|
|
111
123
|
process: ChildProcess;
|
|
112
|
-
logDir?: string;
|
|
124
|
+
logDir?: string; // set when HARPER_INTEGRATION_TEST_LOG_DIR is configured
|
|
125
|
+
startupOutput?: { stdout: string; stderr: string }; // captured startup output
|
|
126
|
+
}
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
**`HarperTestContext`** is the minimal context shape accepted by `startHarper()`, `setupHarperWithFixture()`, `killHarper()`, and `teardownHarper()`. It is intentionally loose so it can be satisfied by a plain object or a `node:test` context:
|
|
130
|
+
|
|
131
|
+
```ts
|
|
132
|
+
interface HarperTestContext {
|
|
133
|
+
name?: string; // used for log directory naming
|
|
134
|
+
harper?: Partial<HarperContext>; // populated by startHarper()
|
|
135
|
+
}
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
**`StartedHarperTestContext`** is the same as `HarperTestContext` but with `harper` guaranteed to be a fully populated `HarperContext`. It is the return type of `startHarper()` and `setupHarperWithFixture()`, and the required parameter type of `killHarper()` and `teardownHarper()`:
|
|
139
|
+
|
|
140
|
+
```ts
|
|
141
|
+
interface StartedHarperTestContext extends HarperTestContext {
|
|
142
|
+
harper: HarperContext;
|
|
113
143
|
}
|
|
114
144
|
```
|
|
115
145
|
|
|
146
|
+
**`ContextWithHarper`** is for `node:test` only. It extends both `SuiteContext` and `TestContext` from `node:test` with `harper: HarperContext`, so you can use it as the typed context parameter in a `suite()` callback:
|
|
147
|
+
|
|
148
|
+
```ts
|
|
149
|
+
// node:test usage
|
|
150
|
+
suite('my suite', (ctx: ContextWithHarper) => {
|
|
151
|
+
before(async () => { await startHarper(ctx); });
|
|
152
|
+
after(async () => { await teardownHarper(ctx); });
|
|
153
|
+
|
|
154
|
+
test('example', async () => {
|
|
155
|
+
const res = await fetch(ctx.harper.httpURL);
|
|
156
|
+
});
|
|
157
|
+
});
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
If you are not using `node:test`, use `createHarperContext()` to create a plain `HarperTestContext` instead.
|
|
161
|
+
|
|
116
162
|
### Server Log Capture
|
|
117
163
|
|
|
118
|
-
When `HARPER_INTEGRATION_TEST_LOG_DIR` is set, each Harper instance writes its logs to a per-suite subdirectory. Logs
|
|
164
|
+
When `HARPER_INTEGRATION_TEST_LOG_DIR` is set, each Harper instance writes its logs to a per-suite subdirectory. Logs are preserved for the lifetime of the log directory. In CI, combine with artifact upload steps that run on failure to capture logs from failing runs.
|
|
119
165
|
|
|
120
166
|
```sh
|
|
121
167
|
HARPER_INTEGRATION_TEST_LOG_DIR=/tmp/harper-test-logs npx harper-integration-test-run "integrationTests/**/*.test.ts"
|
|
122
168
|
```
|
|
123
169
|
|
|
170
|
+
Additionally, `startupOutput` on `HarperContext` provides the captured stdout/stderr from Harper startup for programmatic access (e.g. attaching to Playwright test results).
|
|
171
|
+
|
|
172
|
+
### `HarperStartupError`
|
|
173
|
+
|
|
174
|
+
When Harper fails to start or times out, a `HarperStartupError` is thrown. It extends `Error` and includes structured `stdout` and `stderr` properties for diagnostics:
|
|
175
|
+
|
|
176
|
+
```ts
|
|
177
|
+
import { HarperStartupError } from '@harperfast/integration-testing';
|
|
178
|
+
|
|
179
|
+
try {
|
|
180
|
+
await startHarper(ctx);
|
|
181
|
+
} catch (error) {
|
|
182
|
+
if (error instanceof HarperStartupError) {
|
|
183
|
+
console.error('Startup failed:', error.message);
|
|
184
|
+
console.error('stdout:', error.stdout);
|
|
185
|
+
console.error('stderr:', error.stderr);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
```
|
|
189
|
+
|
|
124
190
|
### `targz(dirPath)`
|
|
125
191
|
|
|
126
192
|
Packs and compresses a directory into a base64-encoded tar.gz string. Useful for `deploy_component` Operations API calls.
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
import { ChildProcess } from 'node:child_process';
|
|
2
|
+
import { type SuiteContext, type TestContext } from 'node:test';
|
|
3
|
+
/**
|
|
4
|
+
* Minimal context interface required by startHarper/teardownHarper.
|
|
5
|
+
*
|
|
6
|
+
* This is intentionally loose so it can be satisfied by:
|
|
7
|
+
* - node:test SuiteContext/TestContext objects (via ContextWithHarper)
|
|
8
|
+
* - Plain objects (e.g. Playwright worker fixtures: `createHarperContext()`)
|
|
9
|
+
*/
|
|
10
|
+
export interface HarperTestContext {
|
|
11
|
+
/** Optional name used for log directory naming (e.g. suite name or Playwright worker index). */
|
|
12
|
+
name?: string;
|
|
13
|
+
/** Populated by startHarper(). May be pre-seeded with dataRootDir/hostname to reuse across restarts. */
|
|
14
|
+
harper?: Partial<HarperContext>;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* A started context — harper is fully populated after startHarper() resolves.
|
|
18
|
+
*/
|
|
19
|
+
export interface StartedHarperTestContext extends HarperTestContext {
|
|
20
|
+
harper: HarperContext;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Creates a plain object satisfying HarperTestContext, for use outside node:test
|
|
24
|
+
* (e.g. as a Playwright worker fixture).
|
|
25
|
+
*
|
|
26
|
+
* @param name Optional name for log directory naming (e.g. Playwright worker index).
|
|
27
|
+
*/
|
|
28
|
+
export declare function createHarperContext(name?: string): HarperTestContext;
|
|
29
|
+
export declare const OPERATIONS_API_PORT = 9925;
|
|
30
|
+
export declare const DEFAULT_ADMIN_USERNAME = "admin";
|
|
31
|
+
export declare const DEFAULT_ADMIN_PASSWORD = "Abc1234!";
|
|
32
|
+
export declare const DEFAULT_STARTUP_TIMEOUT_MS: number;
|
|
33
|
+
/**
|
|
34
|
+
* The runtime to use for running Harper during tests.
|
|
35
|
+
* Set via the HARPER_RUNTIME environment variable ('node' or 'bun').
|
|
36
|
+
* Defaults to 'node'.
|
|
37
|
+
*/
|
|
38
|
+
export declare const HARPER_RUNTIME: 'node' | 'bun';
|
|
39
|
+
/**
|
|
40
|
+
* Options for setting up a Harper instance.
|
|
41
|
+
*/
|
|
42
|
+
export interface StartHarperOptions {
|
|
43
|
+
/**
|
|
44
|
+
* Timeout in milliseconds to wait for Harper to start.
|
|
45
|
+
* @default 30000
|
|
46
|
+
*/
|
|
47
|
+
startupTimeoutMs?: number;
|
|
48
|
+
/**
|
|
49
|
+
* Additional configuration options to pass to the Harper CLI.
|
|
50
|
+
*/
|
|
51
|
+
config?: any;
|
|
52
|
+
/**
|
|
53
|
+
* Environment variables to set when running Harper.
|
|
54
|
+
*/
|
|
55
|
+
env?: any;
|
|
56
|
+
/**
|
|
57
|
+
* Explicit path to the Harper CLI script (dist/bin/harper.js).
|
|
58
|
+
* If not provided, resolution order is:
|
|
59
|
+
* 1. This option
|
|
60
|
+
* 2. HARPER_INTEGRATION_TEST_INSTALL_SCRIPT environment variable
|
|
61
|
+
* 3. Auto-resolved from 'harper' package in node_modules
|
|
62
|
+
*/
|
|
63
|
+
harperBinPath?: string;
|
|
64
|
+
}
|
|
65
|
+
export interface HarperContext {
|
|
66
|
+
/** Absolute path to the Harper installation directory */
|
|
67
|
+
dataRootDir: string;
|
|
68
|
+
/** Admin credentials for the Harper instance */
|
|
69
|
+
admin: {
|
|
70
|
+
/** Admin username (default: 'admin') */
|
|
71
|
+
username: string;
|
|
72
|
+
/** Admin password (default: 'Abc1234!') */
|
|
73
|
+
password: string;
|
|
74
|
+
};
|
|
75
|
+
/** HTTP URL for the Harper instance (e.g., 'http://127.0.0.2:9926') */
|
|
76
|
+
httpURL: string;
|
|
77
|
+
/** Operations API URL (e.g., 'http://127.0.0.2:9925') */
|
|
78
|
+
operationsAPIURL: string;
|
|
79
|
+
/** Assigned loopback IP address (e.g., '127.0.0.2') */
|
|
80
|
+
hostname: string;
|
|
81
|
+
/** Child process for the Harper instance */
|
|
82
|
+
process: ChildProcess;
|
|
83
|
+
/** Absolute path to the log directory for this suite (only set when HARPER_INTEGRATION_TEST_LOG_DIR is configured) */
|
|
84
|
+
logDir?: string;
|
|
85
|
+
/** Captured stdout/stderr from Harper startup, up to the point it reported ready. */
|
|
86
|
+
startupOutput?: {
|
|
87
|
+
stdout: string;
|
|
88
|
+
stderr: string;
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Test context interface with Harper instance details, for use with node:test.
|
|
93
|
+
*
|
|
94
|
+
* This interface is populated by `startHarper()` and contains
|
|
95
|
+
* all necessary information to interact with the test Harper instance.
|
|
96
|
+
*
|
|
97
|
+
* For use outside node:test (e.g. Playwright), use `createHarperContext()` to
|
|
98
|
+
* create a plain object satisfying `HarperTestContext` instead.
|
|
99
|
+
*/
|
|
100
|
+
export interface ContextWithHarper extends SuiteContext, TestContext {
|
|
101
|
+
harper: HarperContext;
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Error thrown when a Harper process fails to start or times out.
|
|
105
|
+
* Includes captured stdout and stderr for diagnostics.
|
|
106
|
+
*/
|
|
107
|
+
export declare class HarperStartupError extends Error {
|
|
108
|
+
stdout: string;
|
|
109
|
+
stderr: string;
|
|
110
|
+
constructor(message: string, stdout: string, stderr: string);
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Sets up a Harper instance with a component pre-installed from a local directory.
|
|
114
|
+
*
|
|
115
|
+
* Copies `fixturePath` into `{dataRootDir}/components/{name}` before Harper starts,
|
|
116
|
+
* so the component is available on the first request without a post-startup deploy.
|
|
117
|
+
* Use this when tests need a known route available at startup (e.g. mTLS cert tests).
|
|
118
|
+
*
|
|
119
|
+
* @param ctx - The test context to populate with Harper instance details
|
|
120
|
+
* @param fixturePath - Absolute path to the component directory to pre-install
|
|
121
|
+
* @param options - Optional configuration for the setup process
|
|
122
|
+
*/
|
|
123
|
+
export declare function setupHarperWithFixture(ctx: HarperTestContext, fixturePath: string, options?: StartHarperOptions): Promise<StartedHarperTestContext>;
|
|
124
|
+
/**
|
|
125
|
+
* Sets up and starts a Harper instance for testing.
|
|
126
|
+
*
|
|
127
|
+
* @param ctx - The test context to populate with Harper instance details
|
|
128
|
+
* @param options - Optional configuration for the setup process
|
|
129
|
+
* @returns The context with the `harper` property populated
|
|
130
|
+
*
|
|
131
|
+
* @example
|
|
132
|
+
* ```ts
|
|
133
|
+
* suite('My tests', (ctx: ContextWithHarper) => {
|
|
134
|
+
* before(async () => {
|
|
135
|
+
* await startHarper(ctx);
|
|
136
|
+
* });
|
|
137
|
+
*
|
|
138
|
+
* after(async () => {
|
|
139
|
+
* await teardownHarper(ctx);
|
|
140
|
+
* });
|
|
141
|
+
*
|
|
142
|
+
* test('can connect', async () => {
|
|
143
|
+
* const response = await fetch(ctx.harper.httpURL);
|
|
144
|
+
* // ...
|
|
145
|
+
* });
|
|
146
|
+
* });
|
|
147
|
+
* ```
|
|
148
|
+
*/
|
|
149
|
+
export declare function startHarper(ctx: HarperTestContext, options?: StartHarperOptions): Promise<StartedHarperTestContext>;
|
|
150
|
+
/**
|
|
151
|
+
* Kill harper process (can be used for teardown, or killing it before a restart)
|
|
152
|
+
* @param ctx
|
|
153
|
+
*/
|
|
154
|
+
export declare function killHarper(ctx: StartedHarperTestContext): Promise<void>;
|
|
155
|
+
/**
|
|
156
|
+
* Tears down a Harper instance and cleans up all resources.
|
|
157
|
+
*
|
|
158
|
+
* This function stops the Harper instance, releases the loopback address,
|
|
159
|
+
* and removes the installation directory.
|
|
160
|
+
* @param ctx - The test context with Harper instance details
|
|
161
|
+
*
|
|
162
|
+
* @example
|
|
163
|
+
* ```ts
|
|
164
|
+
* suite('My tests', (ctx: ContextWithHarper) => {
|
|
165
|
+
* before(async () => {
|
|
166
|
+
* await startHarper(ctx);
|
|
167
|
+
* });
|
|
168
|
+
*
|
|
169
|
+
* after(async () => {
|
|
170
|
+
* await teardownHarper(ctx);
|
|
171
|
+
* });
|
|
172
|
+
* });
|
|
173
|
+
* ```
|
|
174
|
+
*/
|
|
175
|
+
export declare function teardownHarper(ctx: StartedHarperTestContext): Promise<void>;
|
|
176
|
+
export declare function sendOperation(context: HarperContext, operation: any): Promise<any>;
|
package/dist/harperLifecycle.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { spawn, ChildProcess } from 'node:child_process';
|
|
2
|
-
import { createWriteStream, existsSync
|
|
2
|
+
import { createWriteStream, existsSync } from 'node:fs';
|
|
3
3
|
import { join, basename } from 'node:path';
|
|
4
4
|
import { tmpdir } from 'node:os';
|
|
5
5
|
import { mkdtemp, mkdir, rm, cp } from 'node:fs/promises';
|
|
@@ -19,13 +19,19 @@ export function createHarperContext(name) {
|
|
|
19
19
|
// Constants
|
|
20
20
|
const HTTP_PORT = 9926;
|
|
21
21
|
const HTTPS_PORT = 9927;
|
|
22
|
+
const MQTT_PORT = 1883;
|
|
23
|
+
const MQTTS_PORT = 8883;
|
|
22
24
|
export const OPERATIONS_API_PORT = 9925;
|
|
23
25
|
export const DEFAULT_ADMIN_USERNAME = 'admin';
|
|
24
26
|
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;
|
|
27
|
+
export const DEFAULT_STARTUP_TIMEOUT_MS = parseInt(process.env.HARPER_INTEGRATION_TEST_STARTUP_TIMEOUT_MS || '', 10) || 60000;
|
|
28
28
|
const LOG_DIR = process.env.HARPER_INTEGRATION_TEST_LOG_DIR;
|
|
29
|
+
/**
|
|
30
|
+
* The runtime to use for running Harper during tests.
|
|
31
|
+
* Set via the HARPER_RUNTIME environment variable ('node' or 'bun').
|
|
32
|
+
* Defaults to 'node'.
|
|
33
|
+
*/
|
|
34
|
+
export const HARPER_RUNTIME = process.env.HARPER_RUNTIME || 'node';
|
|
29
35
|
/**
|
|
30
36
|
* Gets the path to the Harper CLI script.
|
|
31
37
|
*
|
|
@@ -65,6 +71,14 @@ function getHarperScript(harperBinPath) {
|
|
|
65
71
|
` - HARPER_INTEGRATION_TEST_INSTALL_SCRIPT environment variable\n` +
|
|
66
72
|
` - Install 'harper' as a dependency in your project`);
|
|
67
73
|
}
|
|
74
|
+
/**
|
|
75
|
+
* Strips ANSI escape sequences (colors, bold, underline, cursor movement, etc.) from a string.
|
|
76
|
+
*/
|
|
77
|
+
// eslint-disable-next-line no-control-regex
|
|
78
|
+
const ANSI_REGEX = /[\u001b\u009b][[()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-ORZcf-nq-uy=><~]/g;
|
|
79
|
+
function stripAnsi(str) {
|
|
80
|
+
return str.replace(ANSI_REGEX, '');
|
|
81
|
+
}
|
|
68
82
|
/**
|
|
69
83
|
* Sanitizes a string for use as a filesystem directory name.
|
|
70
84
|
*/
|
|
@@ -74,17 +88,42 @@ function sanitizeForFilesystem(name) {
|
|
|
74
88
|
.replace(/_+/g, '_')
|
|
75
89
|
.substring(0, 100);
|
|
76
90
|
}
|
|
91
|
+
/**
|
|
92
|
+
* Error thrown when a Harper process fails to start or times out.
|
|
93
|
+
* Includes captured stdout and stderr for diagnostics.
|
|
94
|
+
*/
|
|
95
|
+
export class HarperStartupError extends Error {
|
|
96
|
+
stdout;
|
|
97
|
+
stderr;
|
|
98
|
+
constructor(message, stdout, stderr) {
|
|
99
|
+
let fullMessage = message;
|
|
100
|
+
if (stdout) {
|
|
101
|
+
fullMessage += `\n\nstdout:\n${stdout}`;
|
|
102
|
+
}
|
|
103
|
+
if (stderr) {
|
|
104
|
+
fullMessage += `\n\nstderr:\n${stderr}`;
|
|
105
|
+
}
|
|
106
|
+
super(fullMessage);
|
|
107
|
+
this.name = 'HarperStartupError';
|
|
108
|
+
this.stdout = stdout;
|
|
109
|
+
this.stderr = stderr;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
77
112
|
/**
|
|
78
113
|
* Runs a Harper CLI command and captures output.
|
|
79
114
|
*
|
|
80
115
|
* When `logDir` is provided, stdout and stderr are also written to files
|
|
81
116
|
* (`stdout.log` and `stderr.log`) in that directory.
|
|
82
117
|
*
|
|
83
|
-
* @throws {
|
|
118
|
+
* @throws {HarperStartupError} If the command times out or exits with a non-zero status code
|
|
84
119
|
*/
|
|
85
|
-
function runHarperCommand({ args, env, completionMessage, logDir, harperBinPath, }) {
|
|
120
|
+
function runHarperCommand({ args, env, completionMessage, logDir, harperBinPath, timeoutMs, }) {
|
|
86
121
|
const harperScript = getHarperScript(harperBinPath);
|
|
87
|
-
const
|
|
122
|
+
const runtime = HARPER_RUNTIME;
|
|
123
|
+
const runtimeArgs = runtime === 'bun'
|
|
124
|
+
? [harperScript, ...args]
|
|
125
|
+
: ['--trace-warnings', '--force-node-api-uncaught-exceptions-policy=true', harperScript, ...args];
|
|
126
|
+
const proc = spawn(runtime, runtimeArgs, {
|
|
88
127
|
env: { ...process.env, ...env },
|
|
89
128
|
});
|
|
90
129
|
let stdoutStream;
|
|
@@ -93,32 +132,27 @@ function runHarperCommand({ args, env, completionMessage, logDir, harperBinPath,
|
|
|
93
132
|
stdoutStream = createWriteStream(join(logDir, 'stdout.log'));
|
|
94
133
|
stderrStream = createWriteStream(join(logDir, 'stderr.log'));
|
|
95
134
|
}
|
|
135
|
+
const effectiveTimeout = timeoutMs ?? DEFAULT_STARTUP_TIMEOUT_MS;
|
|
96
136
|
return new Promise((resolve, reject) => {
|
|
97
137
|
let stdout = '';
|
|
98
138
|
let stderr = '';
|
|
99
139
|
let timer = setTimeout(() => {
|
|
100
|
-
|
|
101
|
-
if (stdout) {
|
|
102
|
-
errorMessage += `\n\nstdout:\n${stdout}`;
|
|
103
|
-
}
|
|
104
|
-
if (stderr) {
|
|
105
|
-
errorMessage += `\n\nstderr:\n${stderr}`;
|
|
106
|
-
}
|
|
107
|
-
reject(errorMessage);
|
|
140
|
+
reject(new HarperStartupError(`Harper process timed out after ${effectiveTimeout}ms`, stdout, stderr));
|
|
108
141
|
proc.kill();
|
|
109
|
-
},
|
|
142
|
+
}, effectiveTimeout);
|
|
110
143
|
proc.stdout?.on('data', (data) => {
|
|
111
|
-
const dataString = data.toString();
|
|
112
|
-
stdoutStream?.write(
|
|
144
|
+
const dataString = stripAnsi(data.toString());
|
|
145
|
+
stdoutStream?.write(dataString);
|
|
113
146
|
if (completionMessage && dataString.includes(completionMessage)) {
|
|
114
147
|
clearTimeout(timer);
|
|
115
|
-
resolve(proc);
|
|
148
|
+
resolve({ process: proc, stdout, stderr });
|
|
116
149
|
}
|
|
117
150
|
stdout += dataString;
|
|
118
151
|
});
|
|
119
152
|
proc.stderr?.on('data', (data) => {
|
|
120
|
-
|
|
121
|
-
|
|
153
|
+
const dataString = stripAnsi(data.toString());
|
|
154
|
+
stderrStream?.write(dataString);
|
|
155
|
+
stderr += dataString;
|
|
122
156
|
});
|
|
123
157
|
proc.on('error', (error) => {
|
|
124
158
|
reject(error);
|
|
@@ -126,15 +160,12 @@ function runHarperCommand({ args, env, completionMessage, logDir, harperBinPath,
|
|
|
126
160
|
proc.on('exit', (statusCode, signal) => {
|
|
127
161
|
clearTimeout(timer);
|
|
128
162
|
if (statusCode === 0) {
|
|
129
|
-
resolve(proc);
|
|
163
|
+
resolve({ process: proc, stdout, stderr });
|
|
130
164
|
}
|
|
131
165
|
else {
|
|
132
|
-
|
|
166
|
+
const errorMessage = `Harper process failed with exit code/signal ${statusCode ?? signal}`;
|
|
133
167
|
stderrStream?.write(errorMessage);
|
|
134
|
-
|
|
135
|
-
errorMessage += `\n\nstderr:\n${stderr}`;
|
|
136
|
-
}
|
|
137
|
-
reject(errorMessage);
|
|
168
|
+
reject(new HarperStartupError(errorMessage, stdout, stderr));
|
|
138
169
|
}
|
|
139
170
|
stdoutStream?.end();
|
|
140
171
|
stderrStream?.end();
|
|
@@ -192,26 +223,18 @@ export async function startHarper(ctx, options) {
|
|
|
192
223
|
let logDir;
|
|
193
224
|
if (LOG_DIR) {
|
|
194
225
|
const suiteName = sanitizeForFilesystem(ctx.name || 'unknown');
|
|
195
|
-
|
|
226
|
+
const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
|
|
227
|
+
logDir = join(LOG_DIR, `${suiteName}-${sanitizeForFilesystem(loopbackAddress)}-${timestamp}`);
|
|
196
228
|
await mkdir(logDir, { recursive: true });
|
|
197
229
|
}
|
|
198
230
|
// Point Harper's log directory to the suite log dir so hdb.log is preserved for upload
|
|
199
231
|
const config = { ...options?.config };
|
|
200
232
|
if (logDir) {
|
|
201
233
|
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
234
|
}
|
|
212
235
|
const args = [
|
|
213
236
|
`--ROOTPATH=${dataRootDir}`,
|
|
214
|
-
|
|
237
|
+
`--AUTHENTICATION_AUTHORIZELOCAL=true`,
|
|
215
238
|
`--HDB_ADMIN_USERNAME=${DEFAULT_ADMIN_USERNAME}`,
|
|
216
239
|
`--HDB_ADMIN_PASSWORD=${DEFAULT_ADMIN_PASSWORD}`,
|
|
217
240
|
'--THREADS_COUNT=1',
|
|
@@ -219,6 +242,8 @@ export async function startHarper(ctx, options) {
|
|
|
219
242
|
`--NODE_HOSTNAME=${loopbackAddress}`,
|
|
220
243
|
`--HTTP_PORT=${loopbackAddress}:${HTTP_PORT}`,
|
|
221
244
|
`--OPERATIONSAPI_NETWORK_PORT=${loopbackAddress}:${OPERATIONS_API_PORT}`,
|
|
245
|
+
`--MQTT_NETWORK_PORT=${loopbackAddress}:${MQTT_PORT}`,
|
|
246
|
+
`--MQTT_NETWORK_SECUREPORT=${loopbackAddress}:${MQTTS_PORT}`,
|
|
222
247
|
'--LOGGING_LEVEL=debug',
|
|
223
248
|
'--LOGGING_STDSTREAMS=false',
|
|
224
249
|
];
|
|
@@ -232,12 +257,13 @@ export async function startHarper(ctx, options) {
|
|
|
232
257
|
HARPER_SET_CONFIG: JSON.stringify(config),
|
|
233
258
|
...options?.env,
|
|
234
259
|
};
|
|
235
|
-
const
|
|
260
|
+
const result = await runHarperCommand({
|
|
236
261
|
args,
|
|
237
262
|
env: harperEnv,
|
|
238
263
|
completionMessage: 'successfully started',
|
|
239
264
|
logDir,
|
|
240
265
|
harperBinPath: options?.harperBinPath,
|
|
266
|
+
timeoutMs: options?.startupTimeoutMs,
|
|
241
267
|
});
|
|
242
268
|
ctx.harper = {
|
|
243
269
|
dataRootDir,
|
|
@@ -248,8 +274,9 @@ export async function startHarper(ctx, options) {
|
|
|
248
274
|
httpURL: `http://${loopbackAddress}:${HTTP_PORT}`,
|
|
249
275
|
operationsAPIURL: `http://${loopbackAddress}:${OPERATIONS_API_PORT}`,
|
|
250
276
|
hostname: loopbackAddress,
|
|
251
|
-
process:
|
|
277
|
+
process: result.process,
|
|
252
278
|
logDir,
|
|
279
|
+
startupOutput: { stdout: result.stdout, stderr: result.stderr },
|
|
253
280
|
};
|
|
254
281
|
return ctx;
|
|
255
282
|
}
|
|
@@ -258,6 +285,8 @@ export async function startHarper(ctx, options) {
|
|
|
258
285
|
* @param ctx
|
|
259
286
|
*/
|
|
260
287
|
export async function killHarper(ctx) {
|
|
288
|
+
if (!ctx.harper?.process)
|
|
289
|
+
return;
|
|
261
290
|
await new Promise((resolve) => {
|
|
262
291
|
let timer;
|
|
263
292
|
ctx.harper.process.on('exit', () => {
|
|
@@ -297,6 +326,8 @@ export async function killHarper(ctx) {
|
|
|
297
326
|
* ```
|
|
298
327
|
*/
|
|
299
328
|
export async function teardownHarper(ctx) {
|
|
329
|
+
if (!ctx.harper)
|
|
330
|
+
return;
|
|
300
331
|
await killHarper(ctx);
|
|
301
332
|
await releaseLoopbackAddress(ctx.harper.hostname);
|
|
302
333
|
// a few retries are typically necessary, might take a sec for a process to finish, especially since rocksdb may be flushing
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"harperLifecycle.js","sourceRoot":"","sources":["../src/harperLifecycle.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AACzD,OAAO,EAAE,iBAAiB,EAAE,UAAU,
|
|
1
|
+
{"version":3,"file":"harperLifecycle.js","sourceRoot":"","sources":["../src/harperLifecycle.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
export { startHarper, setupHarperWithFixture, killHarper, teardownHarper, sendOperation, createHarperContext, HarperStartupError, OPERATIONS_API_PORT, DEFAULT_ADMIN_USERNAME, DEFAULT_ADMIN_PASSWORD, DEFAULT_STARTUP_TIMEOUT_MS, HARPER_RUNTIME, 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';
|
package/dist/index.js
CHANGED
|
@@ -1,4 +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";
|
|
1
|
+
export { startHarper, setupHarperWithFixture, killHarper, teardownHarper, sendOperation, createHarperContext, HarperStartupError, OPERATIONS_API_PORT, DEFAULT_ADMIN_USERNAME, DEFAULT_ADMIN_PASSWORD, DEFAULT_STARTUP_TIMEOUT_MS, HARPER_RUNTIME, } from "./harperLifecycle.js";
|
|
2
2
|
export { validateLoopbackAddressPool, getNextAvailableLoopbackAddress, releaseLoopbackAddress, releaseAllLoopbackAddressesForCurrentProcess, } from "./loopbackAddressPool.js";
|
|
3
3
|
export { targz } from "./targz.js";
|
|
4
4
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +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,
|
|
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,kBAAkB,EAClB,mBAAmB,EACnB,sBAAsB,EACtB,sBAAsB,EACtB,0BAA0B,EAC1B,cAAc,GAMd,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>;
|
package/dist/run.d.ts
ADDED