@harperfast/harper-pro 5.0.0-beta.2 → 5.0.0-beta.3
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/core/integrationTests/README.md +6 -6
- package/core/integrationTests/deploy/deploy-from-github.test.ts +2 -2
- package/core/integrationTests/deploy/deploy-from-source.test.ts +2 -2
- package/core/integrationTests/server/crash-replay.test.ts +3 -3
- package/core/integrationTests/server/operation-user-rbac.test.ts +2 -2
- package/core/integrationTests/server/operations-server.test.ts +4 -2
- package/core/integrationTests/server/storage-reclamation.test.ts +2 -2
- package/core/integrationTests/server/thread-management.test.ts +2 -2
- package/core/integrationTests/utils/README.md +84 -32
- package/core/integrationTests/utils/harperLifecycle.ts +10 -18
- package/core/package.json +13 -13
- package/core/server/threads/manageThreads.js +3 -1
- package/dist/cloneNode/cloneNode.js +3 -7
- package/dist/cloneNode/cloneNode.js.map +1 -1
- package/dist/core/server/threads/manageThreads.js +3 -1
- package/dist/core/server/threads/manageThreads.js.map +1 -1
- package/npm-shrinkwrap.json +1109 -982
- package/package.json +13 -13
- package/studio/web/assets/{index-CzghSAn2.js → index-C1G-Jo6n.js} +2 -2
- package/studio/web/assets/{index-CzghSAn2.js.map → index-C1G-Jo6n.js.map} +1 -1
- package/studio/web/assets/{index-CWN9Wp5V.js → index-D-CahN0-.js} +2 -2
- package/studio/web/assets/{index-CWN9Wp5V.js.map → index-D-CahN0-.js.map} +1 -1
- package/studio/web/assets/{index-DMDhGP7N.js → index-DxlZI0PX.js} +5 -5
- package/studio/web/assets/{index-DMDhGP7N.js.map → index-DxlZI0PX.js.map} +1 -1
- package/studio/web/assets/{index.lazy-C-yDTGUy.js → index.lazy-BUXDDqq9.js} +2 -2
- package/studio/web/assets/{index.lazy-C-yDTGUy.js.map → index.lazy-BUXDDqq9.js.map} +1 -1
- package/studio/web/assets/{profiler-0fZAOscv.js → profiler-CU93QiSW.js} +2 -2
- package/studio/web/assets/{profiler-0fZAOscv.js.map → profiler-CU93QiSW.js.map} +1 -1
- package/studio/web/assets/{react-redux-BIxqK8O6.js → react-redux-B8k9Ep7e.js} +2 -2
- package/studio/web/assets/{react-redux-BIxqK8O6.js.map → react-redux-B8k9Ep7e.js.map} +1 -1
- package/studio/web/assets/{startRecording-Ca3Gf2MY.js → startRecording-DFeBXGk6.js} +2 -2
- package/studio/web/assets/{startRecording-Ca3Gf2MY.js.map → startRecording-DFeBXGk6.js.map} +1 -1
- package/studio/web/index.html +1 -1
|
@@ -184,7 +184,7 @@ Integration test utilities are located in the [`integrationTests/utils/`](./util
|
|
|
184
184
|
|
|
185
185
|
The most commonly used utilities are:
|
|
186
186
|
|
|
187
|
-
- **`
|
|
187
|
+
- **`startHarper(context)`** - Sets up a complete Harper instance for testing. Use in `before()` hooks.
|
|
188
188
|
- **`teardownHarper(context)`** - Tears down a Harper instance and cleans up resources. Use in `after()` hooks.
|
|
189
189
|
- **`ContextWithHarper`** - TypeScript interface for test context with Harper instance details.
|
|
190
190
|
- **`targz(dirPath)`** - Compresses a directory into a base64-encoded tar.gz string for application deployment.
|
|
@@ -193,11 +193,11 @@ The most commonly used utilities are:
|
|
|
193
193
|
|
|
194
194
|
```ts
|
|
195
195
|
import { suite, test, before, after } from 'node:test';
|
|
196
|
-
import {
|
|
196
|
+
import { startHarper, teardownHarper, type ContextWithHarper } from './utils/harperLifecycle.ts';
|
|
197
197
|
|
|
198
198
|
suite('test suite', (ctx: ContextWithHarper) => {
|
|
199
199
|
before(async () => {
|
|
200
|
-
await
|
|
200
|
+
await startHarper(ctx);
|
|
201
201
|
});
|
|
202
202
|
|
|
203
203
|
after(async () => {
|
|
@@ -227,12 +227,12 @@ Copy and paste the following content to get started:
|
|
|
227
227
|
*/
|
|
228
228
|
import { suite, test, before, after } from 'node:test';
|
|
229
229
|
import { strictEqual } from 'node:assert/strict';
|
|
230
|
-
// Note: adjust the relative path accordingly (e.g., '../utils/harperLifecycle.
|
|
231
|
-
import {
|
|
230
|
+
// Note: adjust the relative path accordingly (e.g., '../utils/harperLifecycle.ts')
|
|
231
|
+
import { startHarper, teardownHarper, type ContextWithHarper } from './utils/harperLifecycle.ts';
|
|
232
232
|
|
|
233
233
|
suite('short description of tests', (ctx: ContextWithHarper) => {
|
|
234
234
|
before(async () => {
|
|
235
|
-
await
|
|
235
|
+
await startHarper(ctx);
|
|
236
236
|
});
|
|
237
237
|
|
|
238
238
|
after(async () => {
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
*/
|
|
6
6
|
import { suite, test, before, after } from 'node:test';
|
|
7
7
|
import { deepStrictEqual, ok, strictEqual } from 'node:assert/strict';
|
|
8
|
-
import {
|
|
8
|
+
import { startHarper, teardownHarper, type ContextWithHarper } from '../utils/harperLifecycle.ts';
|
|
9
9
|
import { join } from 'node:path';
|
|
10
10
|
import { existsSync } from 'node:fs';
|
|
11
11
|
import { readFile } from 'node:fs/promises';
|
|
@@ -13,7 +13,7 @@ import { parse } from 'yaml';
|
|
|
13
13
|
|
|
14
14
|
suite('GitHub application deployment', (ctx: ContextWithHarper) => {
|
|
15
15
|
before(async () => {
|
|
16
|
-
await
|
|
16
|
+
await startHarper(ctx);
|
|
17
17
|
});
|
|
18
18
|
|
|
19
19
|
after(async () => {
|
|
@@ -12,12 +12,12 @@ import { join } from 'node:path';
|
|
|
12
12
|
import { existsSync } from 'node:fs';
|
|
13
13
|
import { setTimeout as sleep } from 'node:timers/promises';
|
|
14
14
|
|
|
15
|
-
import {
|
|
15
|
+
import { startHarper, teardownHarper, type ContextWithHarper } from '../utils/harperLifecycle.ts';
|
|
16
16
|
import { targz } from '../utils/targz.ts';
|
|
17
17
|
|
|
18
18
|
suite('Local application deployment', (ctx: ContextWithHarper) => {
|
|
19
19
|
before(async () => {
|
|
20
|
-
await
|
|
20
|
+
await startHarper(ctx);
|
|
21
21
|
});
|
|
22
22
|
|
|
23
23
|
after(async () => {
|
|
@@ -3,12 +3,12 @@
|
|
|
3
3
|
* database, so replay needs to work for harper to startup.
|
|
4
4
|
*/
|
|
5
5
|
import { suite, test, before, after } from 'node:test';
|
|
6
|
-
import {
|
|
6
|
+
import { startHarper, teardownHarper, type ContextWithHarper } from '../utils/harperLifecycle.ts';
|
|
7
7
|
import { equal } from 'node:assert';
|
|
8
8
|
|
|
9
9
|
suite('Transaction log replay on crash', (ctx: ContextWithHarper) => {
|
|
10
10
|
before(async () => {
|
|
11
|
-
await
|
|
11
|
+
await startHarper(ctx, {
|
|
12
12
|
config: {},
|
|
13
13
|
env: {
|
|
14
14
|
HARPER_NO_FLUSH_ON_EXIT: true, // specifically don't flush, we are testing restart/replay and simulating a crash
|
|
@@ -28,7 +28,7 @@ suite('Transaction log replay on crash', (ctx: ContextWithHarper) => {
|
|
|
28
28
|
await startHarper(ctx);
|
|
29
29
|
let response = await sendOperation(ctx.harper, {
|
|
30
30
|
operation: 'list_roles',
|
|
31
|
-
authorization: ctx.admin,
|
|
31
|
+
authorization: ctx.harper.admin,
|
|
32
32
|
});
|
|
33
33
|
equal(response.length, 1);
|
|
34
34
|
equal(response[0].role, 'super_user');
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
import { suite, test, before, after } from 'node:test';
|
|
12
12
|
import { strictEqual, ok } from 'node:assert/strict';
|
|
13
13
|
|
|
14
|
-
import {
|
|
14
|
+
import { startHarper, teardownHarper, type ContextWithHarper } from '../utils/harperLifecycle.ts';
|
|
15
15
|
|
|
16
16
|
const DATABASE = 'test_db';
|
|
17
17
|
const TABLE = 'dogs';
|
|
@@ -35,7 +35,7 @@ const STANDARD_USER_PASS = 'Test1234!';
|
|
|
35
35
|
|
|
36
36
|
suite('operations RBAC', (ctx: ContextWithHarper) => {
|
|
37
37
|
before(async () => {
|
|
38
|
-
await
|
|
38
|
+
await startHarper(ctx, { config: {}, env: {} });
|
|
39
39
|
|
|
40
40
|
const adminAuth = `Basic ${Buffer.from(`${ctx.harper.admin.username}:${ctx.harper.admin.password}`).toString('base64')}`;
|
|
41
41
|
|
|
@@ -12,11 +12,11 @@ import { ok, strictEqual } from 'node:assert/strict';
|
|
|
12
12
|
import { pack, unpack } from 'msgpackr';
|
|
13
13
|
import { encode, decode } from 'cbor-x';
|
|
14
14
|
|
|
15
|
-
import {
|
|
15
|
+
import { startHarper, teardownHarper, type ContextWithHarper } from '../utils/harperLifecycle.ts';
|
|
16
16
|
|
|
17
17
|
suite('Operations Server', (ctx: ContextWithHarper) => {
|
|
18
18
|
before(async () => {
|
|
19
|
-
await
|
|
19
|
+
await startHarper(ctx, { config: {}, env: {} });
|
|
20
20
|
});
|
|
21
21
|
|
|
22
22
|
after(async () => {
|
|
@@ -95,6 +95,7 @@ suite('Operations Server', (ctx: ContextWithHarper) => {
|
|
|
95
95
|
'Accept': 'application/json',
|
|
96
96
|
'Authorization': `Basic ${Buffer.from(`${ctx.harper.admin.username}:${ctx.harper.admin.password}`).toString('base64')}`,
|
|
97
97
|
},
|
|
98
|
+
// @ts-expect-error - Need to update fetch types to support `Buffer<ArrayBufferLike>`
|
|
98
99
|
body: pack({ operation: 'describe_all' }),
|
|
99
100
|
});
|
|
100
101
|
strictEqual(response.status, 200);
|
|
@@ -140,6 +141,7 @@ suite('Operations Server', (ctx: ContextWithHarper) => {
|
|
|
140
141
|
'Accept': 'application/json',
|
|
141
142
|
'Authorization': `Basic ${Buffer.from(`${ctx.harper.admin.username}:${ctx.harper.admin.password}`).toString('base64')}`,
|
|
142
143
|
},
|
|
144
|
+
// @ts-expect-error - Need to update fetch types to support `Buffer<ArrayBufferLike>`
|
|
143
145
|
body: encode({ operation: 'describe_all' }),
|
|
144
146
|
});
|
|
145
147
|
strictEqual(response.status, 200);
|
|
@@ -20,7 +20,7 @@ import { suite, test, before, after } from 'node:test';
|
|
|
20
20
|
import { ok, strictEqual } from 'node:assert/strict';
|
|
21
21
|
import { setTimeout as sleep } from 'node:timers/promises';
|
|
22
22
|
|
|
23
|
-
import {
|
|
23
|
+
import { startHarper, teardownHarper, type ContextWithHarper } from '../utils/harperLifecycle.ts';
|
|
24
24
|
|
|
25
25
|
const TEST_DATABASE = 'test';
|
|
26
26
|
const TEST_TABLE = 'reclaim';
|
|
@@ -29,7 +29,7 @@ suite('Storage reclamation', (ctx: ContextWithHarper) => {
|
|
|
29
29
|
before(async () => {
|
|
30
30
|
// Set a very high reclamation threshold (99%) so reclamation triggers immediately
|
|
31
31
|
// and a short interval (1 second) for faster test execution
|
|
32
|
-
await
|
|
32
|
+
await startHarper(ctx, {
|
|
33
33
|
config: {
|
|
34
34
|
STORAGE_RECLAMATION_THRESHOLD: 0.99,
|
|
35
35
|
STORAGE_RECLAMATION_INTERVAL: '1s',
|
|
@@ -8,11 +8,11 @@
|
|
|
8
8
|
import { suite, test, before, after } from 'node:test';
|
|
9
9
|
import { strictEqual } from 'node:assert/strict';
|
|
10
10
|
|
|
11
|
-
import {
|
|
11
|
+
import { startHarper, teardownHarper, type ContextWithHarper } from '../utils/harperLifecycle.ts';
|
|
12
12
|
|
|
13
13
|
suite('Thread Management', (ctx: ContextWithHarper) => {
|
|
14
14
|
before(async () => {
|
|
15
|
-
await
|
|
15
|
+
await startHarper(ctx, { config: {}, env: {} });
|
|
16
16
|
});
|
|
17
17
|
|
|
18
18
|
after(async () => {
|
|
@@ -4,26 +4,41 @@ This directory contains utility functions and modules for Harper integration tes
|
|
|
4
4
|
|
|
5
5
|
## Table of Contents
|
|
6
6
|
|
|
7
|
-
- [
|
|
8
|
-
- [
|
|
9
|
-
- [
|
|
7
|
+
- [Integration Test Utilities](#integration-test-utilities)
|
|
8
|
+
- [Table of Contents](#table-of-contents)
|
|
9
|
+
- [Harper Lifecycle Management](#harper-lifecycle-management)
|
|
10
|
+
- [`startHarper(context, options?): Promise<ContextWithHarper>`](#startharpercontext-options-promisecontextwithharper)
|
|
11
|
+
- [`StartHarperOptions`](#startharperoptions)
|
|
12
|
+
- [`killHarper(context): Promise<void>`](#killharpercontext-promisevoid)
|
|
13
|
+
- [`teardownHarper(context): Promise<void>`](#teardownharpercontext-promisevoid)
|
|
14
|
+
- [`ContextWithHarper`](#contextwithharper)
|
|
15
|
+
- [Loopback Address Pool](#loopback-address-pool)
|
|
16
|
+
- [`validateLoopbackAddressPool(): Promise<ValidationResult>`](#validateloopbackaddresspool-promisevalidationresult)
|
|
17
|
+
- [`getNextAvailableLoopbackAddress(): Promise<string>`](#getnextavailableloopbackaddress-promisestring)
|
|
18
|
+
- [`releaseLoopbackAddress(address: string): Promise<void>`](#releaseloopbackaddressaddress-string-promisevoid)
|
|
19
|
+
- [`releaseAllLoopbackAddressesForCurrentProcess(): Promise<void>`](#releaseallloopbackaddressesforcurrentprocess-promisevoid)
|
|
20
|
+
- [Compression Utilities](#compression-utilities)
|
|
21
|
+
- [`targz(dirPath: string): Promise<string>`](#targzdirpath-string-promisestring)
|
|
22
|
+
- [Scripts](#scripts)
|
|
23
|
+
- [`scripts/setup-loopback.sh`](#scriptssetup-loopbacksh)
|
|
24
|
+
- [`scripts/run.mts`](#scriptsrunmts)
|
|
10
25
|
|
|
11
26
|
---
|
|
12
27
|
|
|
13
28
|
## Harper Lifecycle Management
|
|
14
29
|
|
|
15
|
-
**Module:** [`harperLifecycle.
|
|
30
|
+
**Module:** [`harperLifecycle.ts`](./harperLifecycle.ts)
|
|
16
31
|
|
|
17
32
|
Provides functions for managing Harper instances during integration tests, including installation, startup, and teardown.
|
|
18
33
|
|
|
19
|
-
### `
|
|
34
|
+
### `startHarper(context, options?): Promise<ContextWithHarper>`
|
|
20
35
|
|
|
21
36
|
Sets up a complete Harper instance for testing.
|
|
22
37
|
|
|
23
38
|
**Parameters:**
|
|
24
39
|
|
|
25
40
|
- `context` - [`ContextWithHarper`](#contextwithharper) - The test context object
|
|
26
|
-
- `options` - [`
|
|
41
|
+
- `options` - [`StartHarperOptions`](#startharperoptions) (optional) - Configuration options for the setup process
|
|
27
42
|
|
|
28
43
|
**Returns:** `Promise<ContextWithHarper>` - The context with the `harper` property populated
|
|
29
44
|
|
|
@@ -31,8 +46,8 @@ Sets up a complete Harper instance for testing.
|
|
|
31
46
|
|
|
32
47
|
This method should be used in the `before()` lifecycle hook for a test suite. It performs the following steps:
|
|
33
48
|
|
|
34
|
-
1. Creates a Harper instance in a temporary directory
|
|
35
|
-
2. Assigns a unique loopback address from the loopback address pool
|
|
49
|
+
1. Creates a Harper instance in a temporary directory (reuses `ctx.harper.installDir` if already set)
|
|
50
|
+
2. Assigns a unique loopback address from the loopback address pool (reuses `ctx.harper.hostname` if already set)
|
|
36
51
|
3. Starts Harper with test configuration (which self-installs)
|
|
37
52
|
4. Waits for Harper to be fully started, waiting for the startup message to appear in stdout
|
|
38
53
|
5. Populates the `context.harper` object with connection details
|
|
@@ -43,12 +58,12 @@ This method should be used in the `before()` lifecycle hook for a test suite. It
|
|
|
43
58
|
|
|
44
59
|
```ts
|
|
45
60
|
import { suite, test, before, after } from 'node:test';
|
|
46
|
-
import {
|
|
61
|
+
import { startHarper, teardownHarper, type ContextWithHarper } from '../utils/harperLifecycle.ts';
|
|
47
62
|
|
|
48
63
|
// Default setup
|
|
49
64
|
suite('My test suite', (ctx: ContextWithHarper) => {
|
|
50
65
|
before(async () => {
|
|
51
|
-
await
|
|
66
|
+
await startHarper(ctx);
|
|
52
67
|
});
|
|
53
68
|
|
|
54
69
|
after(async () => {
|
|
@@ -64,12 +79,12 @@ suite('My test suite', (ctx: ContextWithHarper) => {
|
|
|
64
79
|
|
|
65
80
|
---
|
|
66
81
|
|
|
67
|
-
### `
|
|
82
|
+
### `StartHarperOptions`
|
|
68
83
|
|
|
69
|
-
Configuration options for `
|
|
84
|
+
Configuration options for `startHarper()`.
|
|
70
85
|
|
|
71
86
|
```typescript
|
|
72
|
-
export interface
|
|
87
|
+
export interface StartHarperOptions {
|
|
73
88
|
startupTimeoutMs?: number;
|
|
74
89
|
config: any;
|
|
75
90
|
env: any;
|
|
@@ -78,13 +93,44 @@ export interface SetupHarperOptions {
|
|
|
78
93
|
|
|
79
94
|
**Properties:**
|
|
80
95
|
|
|
81
|
-
- **`config`** - `object`
|
|
82
|
-
- **`env`** - `object`
|
|
96
|
+
- **`config`** - `object` - Additional configuration options to pass to the Harper CLI.
|
|
97
|
+
- **`env`** - `object` - Additional environment variables to set when starting Harper.
|
|
83
98
|
- **`startupTimeoutMs`** - `number` (optional) - Timeout in milliseconds to wait for Harper to start. Defaults to 30000, or the value of the `HARPER_INTEGRATION_TEST_STARTUP_TIMEOUT_MS` environment variable if set.
|
|
84
99
|
|
|
85
100
|
**Environment Variables:**
|
|
86
101
|
|
|
87
|
-
- `HARPER_INTEGRATION_TEST_STARTUP_TIMEOUT_MS` - Sets the default startup
|
|
102
|
+
- `HARPER_INTEGRATION_TEST_STARTUP_TIMEOUT_MS` - Sets the default startup timeout for all tests when `startupTimeoutMs` is not explicitly provided
|
|
103
|
+
- `HARPER_INTEGRATION_TEST_INSTALL_PARENT_DIR` - Override the parent directory for Harper installation directories (defaults to the OS temp directory)
|
|
104
|
+
- `HARPER_INTEGRATION_TEST_INSTALL_SCRIPT` - Override the path to the Harper CLI script (defaults to `dist/bin/harper.js` relative to the repo root)
|
|
105
|
+
- `HARPER_INTEGRATION_TEST_LOG_DIR` - When set, stdout/stderr logs and Harper's `hdb.log` are written to per-suite subdirectories here; logs are deleted automatically on successful exit and retained on failure
|
|
106
|
+
|
|
107
|
+
---
|
|
108
|
+
|
|
109
|
+
### `killHarper(context): Promise<void>`
|
|
110
|
+
|
|
111
|
+
Kills the running Harper process. Does **not** release the loopback address or remove the installation directory.
|
|
112
|
+
|
|
113
|
+
**Parameters:**
|
|
114
|
+
|
|
115
|
+
- `context` - [`ContextWithHarper`](#contextwithharper) - The test context with a running Harper instance
|
|
116
|
+
|
|
117
|
+
**Returns:** `Promise<void>`
|
|
118
|
+
|
|
119
|
+
**Description:**
|
|
120
|
+
|
|
121
|
+
Sends `SIGTERM` to the Harper process and waits for it to exit. If the process does not exit within 200ms, `SIGKILL` is sent.
|
|
122
|
+
|
|
123
|
+
This is useful for testing Harper restart/crash scenarios. After calling `killHarper()`, call `startHarper()` to restart the instance in the same directory with the same loopback address.
|
|
124
|
+
|
|
125
|
+
**Example:**
|
|
126
|
+
|
|
127
|
+
```ts
|
|
128
|
+
test('recovers after restart', async () => {
|
|
129
|
+
await killHarper(ctx);
|
|
130
|
+
await startHarper(ctx);
|
|
131
|
+
// Harper is running again on the same address
|
|
132
|
+
});
|
|
133
|
+
```
|
|
88
134
|
|
|
89
135
|
---
|
|
90
136
|
|
|
@@ -100,7 +146,7 @@ Tears down a Harper instance and cleans up all resources.
|
|
|
100
146
|
|
|
101
147
|
**Description:**
|
|
102
148
|
|
|
103
|
-
This method should be used in the `after()` lifecycle hook in conjunction with `
|
|
149
|
+
This method should be used in the `after()` lifecycle hook in conjunction with `startHarper()` and `before()`. It performs the following cleanup steps:
|
|
104
150
|
|
|
105
151
|
1. Stops the Harper instance
|
|
106
152
|
2. Releases the loopback address back to the pool
|
|
@@ -111,7 +157,7 @@ This method should be used in the `after()` lifecycle hook in conjunction with `
|
|
|
111
157
|
```ts
|
|
112
158
|
suite('My test suite', (ctx: ContextWithHarper) => {
|
|
113
159
|
before(async () => {
|
|
114
|
-
await
|
|
160
|
+
await startHarper(ctx);
|
|
115
161
|
});
|
|
116
162
|
|
|
117
163
|
after(async () => {
|
|
@@ -131,23 +177,27 @@ TypeScript interface that extends `SuiteContext` and `TestContext` from Node.js
|
|
|
131
177
|
**Interface Definition:**
|
|
132
178
|
|
|
133
179
|
```typescript
|
|
134
|
-
interface
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
password: string;
|
|
140
|
-
};
|
|
141
|
-
httpURL: string;
|
|
142
|
-
operationsAPIURL: string;
|
|
143
|
-
loopbackAddress: string;
|
|
180
|
+
export interface HarperContext {
|
|
181
|
+
installDir: string;
|
|
182
|
+
admin: {
|
|
183
|
+
username: string;
|
|
184
|
+
password: string;
|
|
144
185
|
};
|
|
186
|
+
httpURL: string;
|
|
187
|
+
operationsAPIURL: string;
|
|
188
|
+
hostname: string;
|
|
189
|
+
process: ChildProcess;
|
|
190
|
+
logDir?: string;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
export interface ContextWithHarper extends SuiteContext, TestContext {
|
|
194
|
+
harper: HarperContext;
|
|
145
195
|
}
|
|
146
196
|
```
|
|
147
197
|
|
|
148
198
|
**Properties:**
|
|
149
199
|
|
|
150
|
-
- **`harper`** - `
|
|
200
|
+
- **`harper`** - `HarperContext` - The Harper instance details
|
|
151
201
|
- **`installDir`** - `string` - The absolute path to the Harper installation directory
|
|
152
202
|
- **`admin`** - `object` - Admin credentials
|
|
153
203
|
- **`username`** - `string` - The Harper Admin Username (default: `'admin'`)
|
|
@@ -155,6 +205,8 @@ interface ContextWithHarper extends SuiteContext, TestContext {
|
|
|
155
205
|
- **`httpURL`** - `string` - The HTTP URL for the Harper instance (e.g., `'http://127.0.0.2:9926'`)
|
|
156
206
|
- **`operationsAPIURL`** - `string` - The Operations API URL (e.g., `'http://127.0.0.2:9925'`)
|
|
157
207
|
- **`hostname`** - `string` - The assigned loopback IP address (e.g., `'127.0.0.2'`)
|
|
208
|
+
- **`process`** - `ChildProcess` - The Node.js child process handle for the running Harper instance
|
|
209
|
+
- **`logDir`** - `string | undefined` - Absolute path to the per-suite log directory; only set when `HARPER_INTEGRATION_TEST_LOG_DIR` is configured
|
|
158
210
|
|
|
159
211
|
**Example Usage:**
|
|
160
212
|
|
|
@@ -175,7 +227,7 @@ test('authenticate with admin credentials', async () => {
|
|
|
175
227
|
|
|
176
228
|
## Loopback Address Pool
|
|
177
229
|
|
|
178
|
-
**Module:** [`loopbackAddressPool.
|
|
230
|
+
**Module:** [`loopbackAddressPool.ts`](./loopbackAddressPool.ts)
|
|
179
231
|
|
|
180
232
|
Manages a pool of loopback addresses for concurrent test execution. This allows multiple Harper instances to run simultaneously on different loopback addresses without port conflicts.
|
|
181
233
|
|
|
@@ -227,7 +279,7 @@ If no addresses are available, the function waits and retries until one becomes
|
|
|
227
279
|
**Pool file location:** `${tmpdir()}/harper-integration-test-loopback-pool.json`
|
|
228
280
|
**Lock file location:** `${tmpdir()}/harper-integration-test-loopback-pool.lock`
|
|
229
281
|
|
|
230
|
-
**Note:** This is automatically called by `
|
|
282
|
+
**Note:** This is automatically called by `startHarper()`. You typically don't need to call this directly unless you're implementing custom test infrastructure.
|
|
231
283
|
|
|
232
284
|
---
|
|
233
285
|
|
|
@@ -285,7 +337,7 @@ try {
|
|
|
285
337
|
|
|
286
338
|
## Compression Utilities
|
|
287
339
|
|
|
288
|
-
**Module:** [`targz.
|
|
340
|
+
**Module:** [`targz.ts`](./targz.ts)
|
|
289
341
|
|
|
290
342
|
Provides utilities for compressing directories into tar.gz archives.
|
|
291
343
|
|
|
@@ -18,7 +18,7 @@ const LOG_DIR = process.env.HARPER_INTEGRATION_TEST_LOG_DIR;
|
|
|
18
18
|
/**
|
|
19
19
|
* Options for setting up a Harper instance.
|
|
20
20
|
*/
|
|
21
|
-
export interface
|
|
21
|
+
export interface StartHarperOptions {
|
|
22
22
|
/**
|
|
23
23
|
* Timeout in milliseconds to wait for Harper to start.
|
|
24
24
|
* @default 30000
|
|
@@ -59,8 +59,8 @@ export interface HarperContext {
|
|
|
59
59
|
/**
|
|
60
60
|
* Test context interface with Harper instance details.
|
|
61
61
|
*
|
|
62
|
-
* This interface is populated by `
|
|
63
|
-
* information to interact with the test Harper instance.
|
|
62
|
+
* This interface is populated by `startHarper()` and contains
|
|
63
|
+
* all necessary information to interact with the test Harper instance.
|
|
64
64
|
*/
|
|
65
65
|
export interface ContextWithHarper extends SuiteContext, TestContext {
|
|
66
66
|
harper: HarperContext;
|
|
@@ -184,6 +184,10 @@ function runHarperCommand({ args, env, completionMessage, logDir }: RunHarperCom
|
|
|
184
184
|
* This function performs installation, startup, and waits for Harper to be ready.
|
|
185
185
|
* Always call `teardownHarper()` in the `after()` hook to clean up resources.
|
|
186
186
|
*
|
|
187
|
+
* If `ctx.harper.installDir` or `ctx.harper.hostname` are already set they are
|
|
188
|
+
* reused rather than creating new ones — making this safe to call after
|
|
189
|
+
* `killHarper()` to restart an existing instance.
|
|
190
|
+
*
|
|
187
191
|
* @param ctx - The test context to populate with Harper instance details
|
|
188
192
|
* @param options - Optional configuration for the setup process
|
|
189
193
|
* @returns The context with the `harper` property populated
|
|
@@ -192,7 +196,7 @@ function runHarperCommand({ args, env, completionMessage, logDir }: RunHarperCom
|
|
|
192
196
|
* ```ts
|
|
193
197
|
* suite('My tests', (ctx: ContextWithHarper) => {
|
|
194
198
|
* before(async () => {
|
|
195
|
-
* await
|
|
199
|
+
* await startHarper(ctx);
|
|
196
200
|
* });
|
|
197
201
|
*
|
|
198
202
|
* after(async () => {
|
|
@@ -206,19 +210,7 @@ function runHarperCommand({ args, env, completionMessage, logDir }: RunHarperCom
|
|
|
206
210
|
* });
|
|
207
211
|
* ```
|
|
208
212
|
*/
|
|
209
|
-
export async function
|
|
210
|
-
return startHarper(ctx, options);
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
/**
|
|
214
|
-
* Starts a Harper instance that has been installed.
|
|
215
|
-
*
|
|
216
|
-
* This is a lower-level function called by `setupHarper()`.
|
|
217
|
-
* Most tests should use `setupHarper()` instead.
|
|
218
|
-
*
|
|
219
|
-
* @param ctx - The test context with Harper installation details
|
|
220
|
-
*/
|
|
221
|
-
export async function startHarper(ctx: ContextWithHarper, options?: SetupHarperOptions): Promise<ContextWithHarper> {
|
|
213
|
+
export async function startHarper(ctx: ContextWithHarper, options?: StartHarperOptions): Promise<ContextWithHarper> {
|
|
222
214
|
// Create a directory for this Harper installation
|
|
223
215
|
// Use the system temp directory by default, or a custom parent directory if specified
|
|
224
216
|
const installDirPrefix = join(
|
|
@@ -322,7 +314,7 @@ export async function killHarper(ctx: ContextWithHarper): Promise<void> {
|
|
|
322
314
|
* ```ts
|
|
323
315
|
* suite('My tests', (ctx: ContextWithHarper) => {
|
|
324
316
|
* before(async () => {
|
|
325
|
-
* await
|
|
317
|
+
* await startHarper(ctx);
|
|
326
318
|
* });
|
|
327
319
|
*
|
|
328
320
|
* after(async () => {
|
package/core/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "harper",
|
|
3
3
|
"description": "Harper is an open-source Node.js performance platform that unifies database, cache, application, and messaging layers into one in-memory process.",
|
|
4
|
-
"version": "5.0.0-beta.
|
|
4
|
+
"version": "5.0.0-beta.3",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"homepage": "https://harper.fast",
|
|
7
7
|
"bugs": {
|
|
@@ -133,7 +133,7 @@
|
|
|
133
133
|
"globals": "^16.5.0",
|
|
134
134
|
"intercept-stdout": "0.1.2",
|
|
135
135
|
"mkcert": "^3.2.0",
|
|
136
|
-
"mocha": "^11.7.
|
|
136
|
+
"mocha": "^11.7.5",
|
|
137
137
|
"mqtt": "~4.3.8",
|
|
138
138
|
"oxlint": "^1.31.0",
|
|
139
139
|
"prettier": "~3.7.3",
|
|
@@ -144,18 +144,18 @@
|
|
|
144
144
|
"tsx": "^4.20.6",
|
|
145
145
|
"typescript": "^5.8.2",
|
|
146
146
|
"typescript-eslint": "^8.45.0",
|
|
147
|
-
"undici": "^7.
|
|
147
|
+
"undici": "^7.24.4",
|
|
148
148
|
"why-is-node-still-running": "^1.0.0"
|
|
149
149
|
},
|
|
150
150
|
"dependencies": {
|
|
151
|
-
"@aws-sdk/client-s3": "3.
|
|
151
|
+
"@aws-sdk/client-s3": "^3.1012.0",
|
|
152
152
|
"@aws-sdk/lib-storage": "3.964.0",
|
|
153
153
|
"@datadog/pprof": "^5.11.1",
|
|
154
154
|
"@endo/static-module-record": "^1.1.2",
|
|
155
|
-
"@fastify/autoload": "
|
|
156
|
-
"@fastify/compress": "
|
|
157
|
-
"@fastify/cors": "
|
|
158
|
-
"@fastify/static": "
|
|
155
|
+
"@fastify/autoload": "^6.3.1",
|
|
156
|
+
"@fastify/compress": "^8.3.1",
|
|
157
|
+
"@fastify/cors": "^11.2.0",
|
|
158
|
+
"@fastify/static": "^9.0.0",
|
|
159
159
|
"@harperfast/extended-iterable": "^1.0.1",
|
|
160
160
|
"@harperfast/rocksdb-js": "^0.1.12",
|
|
161
161
|
"@turf/area": "6.5.0",
|
|
@@ -179,8 +179,8 @@
|
|
|
179
179
|
"dotenv": "^16.4.7",
|
|
180
180
|
"easy-ocsp": "1.2.2",
|
|
181
181
|
"fast-glob": "3.3.3",
|
|
182
|
-
"fastify": "
|
|
183
|
-
"fastify-plugin": "
|
|
182
|
+
"fastify": "^5.8.2",
|
|
183
|
+
"fastify-plugin": "^5.1.0",
|
|
184
184
|
"fs-extra": "11.3.3",
|
|
185
185
|
"graphql": "^16.10.0",
|
|
186
186
|
"graphql-http": "^1.22.4",
|
|
@@ -194,7 +194,7 @@
|
|
|
194
194
|
"jsonata": "1.8.7",
|
|
195
195
|
"jsonwebtoken": "9.0.3",
|
|
196
196
|
"lmdb": "3.5.2",
|
|
197
|
-
"lodash": "4.17.
|
|
197
|
+
"lodash": "^4.17.23",
|
|
198
198
|
"mathjs": "11.12.0",
|
|
199
199
|
"micromatch": "^4.0.8",
|
|
200
200
|
"minimist": "1.2.8",
|
|
@@ -222,8 +222,8 @@
|
|
|
222
222
|
"ses": "^1.14.0",
|
|
223
223
|
"stream-chain": "2.2.5",
|
|
224
224
|
"stream-json": "1.9.1",
|
|
225
|
-
"systeminformation": "5.
|
|
226
|
-
"tar-fs": "3.
|
|
225
|
+
"systeminformation": "^5.31.4",
|
|
226
|
+
"tar-fs": "^3.1.2",
|
|
227
227
|
"ulidx": "0.5.0",
|
|
228
228
|
"uuid": "11.1.0",
|
|
229
229
|
"validate.js": "0.13.1",
|
|
@@ -339,7 +339,9 @@ function onMessageByType(type, listener) {
|
|
|
339
339
|
listeners.push(listener);
|
|
340
340
|
if (messagesQueuedByType.has(type)) {
|
|
341
341
|
for (let message of messagesQueuedByType.get(type)) {
|
|
342
|
-
|
|
342
|
+
// enqueue in next event turn; messages always come as events, and trying to do this synchronously can be
|
|
343
|
+
// problematic for getting mixed up with module loading
|
|
344
|
+
setImmediate(() => listener(message));
|
|
343
345
|
}
|
|
344
346
|
messagesQueuedByType.delete(type);
|
|
345
347
|
}
|
|
@@ -214,9 +214,6 @@ async function cloneNode() {
|
|
|
214
214
|
if (freshClone || !systemExists) {
|
|
215
215
|
await installHarper();
|
|
216
216
|
}
|
|
217
|
-
// Start Harper to prepare for clone operations
|
|
218
|
-
const { main } = await import('../core/bin/run.js');
|
|
219
|
-
await main();
|
|
220
217
|
logger.initLogSettings();
|
|
221
218
|
harperLogger = logger.loggerWithTag('cloneNode');
|
|
222
219
|
// Get the config from the leader and write it to the existing local config file, excluding any parameters that should not be cloned
|
|
@@ -241,10 +238,9 @@ async function cloneNode() {
|
|
|
241
238
|
await databases.system.hdb_user.delete({ username: 'clone-temp-admin' });
|
|
242
239
|
}
|
|
243
240
|
}
|
|
244
|
-
//
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
await restartWorkers();
|
|
241
|
+
// Start Harper to prepare for clone operations
|
|
242
|
+
const { main } = await import('../core/bin/run.js');
|
|
243
|
+
await main();
|
|
248
244
|
// Dynamically importing setNode because it was causing early usage of rootpath var install before it was initialized.
|
|
249
245
|
const { setNode } = await import('../replication/setNode.js');
|
|
250
246
|
// Set node will set up replication between this node and the leader,
|