@harperfast/harper-pro 5.0.0-beta.1 → 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/.github/workflows/integration-tests.yml +7 -5
- package/core/components/componentLoader.ts +1 -1
- 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 +19 -23
- package/core/package.json +15 -14
- package/core/resources/ResourceInterface.ts +1 -1
- package/core/resources/Table.ts +9 -5
- package/core/resources/search.ts +1 -1
- package/core/security/jsLoader.ts +23 -6
- package/core/security/keys.js +7 -7
- package/core/server/threads/manageThreads.js +3 -1
- package/core/unitTests/components/fixtures/testJSWithDeps/child-dir/typestrip.ts +2 -0
- package/core/unitTests/components/fixtures/testJSWithDeps/resources.js +3 -0
- package/core/unitTests/components/globalIsolation.test.js +1 -0
- package/core/unitTests/resources/query.test.js +16 -1
- package/core/utility/lmdb/commonUtility.js +21 -10
- package/dist/core/components/componentLoader.js +1 -1
- package/dist/core/components/componentLoader.js.map +1 -1
- package/dist/core/resources/Table.js +8 -3
- package/dist/core/resources/Table.js.map +1 -1
- package/dist/core/resources/search.js +1 -1
- package/dist/core/resources/search.js.map +1 -1
- package/dist/core/security/jsLoader.js +22 -7
- package/dist/core/security/jsLoader.js.map +1 -1
- package/dist/core/security/keys.js +7 -7
- package/dist/core/security/keys.js.map +1 -1
- package/dist/core/server/threads/manageThreads.js +3 -1
- package/dist/core/server/threads/manageThreads.js.map +1 -1
- package/dist/core/utility/lmdb/commonUtility.js +20 -13
- package/dist/core/utility/lmdb/commonUtility.js.map +1 -1
- package/dist/licensing/usageLicensing.js.map +1 -1
- package/dist/replication/knownNodes.js +5 -37
- package/dist/replication/knownNodes.js.map +1 -1
- package/dist/replication/nodeIdMapping.js +2 -35
- package/dist/replication/nodeIdMapping.js.map +1 -1
- package/dist/replication/replicationConnection.js +6 -3
- package/dist/replication/replicationConnection.js.map +1 -1
- package/dist/replication/replicator.js +3 -2
- package/dist/replication/replicator.js.map +1 -1
- package/dist/replication/setNode.js +1 -1
- package/dist/replication/setNode.js.map +1 -1
- package/dist/security/certificate.js.map +1 -1
- package/licensing/usageLicensing.ts +3 -2
- package/npm-shrinkwrap.json +1165 -1027
- package/package.json +15 -14
- package/replication/knownNodes.ts +3 -2
- package/replication/nodeIdMapping.ts +1 -1
- package/replication/replicationConnection.ts +17 -5
- package/replication/replicator.ts +7 -2
- package/replication/setNode.ts +1 -1
- package/security/certificate.ts +2 -1
- package/studio/web/assets/{index-C-GXfcup.js → index-C1G-Jo6n.js} +2 -2
- package/studio/web/assets/{index-C-GXfcup.js.map → index-C1G-Jo6n.js.map} +1 -1
- package/studio/web/assets/{index-PFlNdimM.js → index-D-CahN0-.js} +2 -2
- package/studio/web/assets/{index-PFlNdimM.js.map → index-D-CahN0-.js.map} +1 -1
- package/studio/web/assets/{index-BTgXJX9d.js → index-DxlZI0PX.js} +5 -5
- package/studio/web/assets/{index-BTgXJX9d.js.map → index-DxlZI0PX.js.map} +1 -1
- package/studio/web/assets/{index.lazy-C3TJZJ4o.js → index.lazy-BUXDDqq9.js} +2 -2
- package/studio/web/assets/{index.lazy-C3TJZJ4o.js.map → index.lazy-BUXDDqq9.js.map} +1 -1
- package/studio/web/assets/{profiler-DotzgiCJ.js → profiler-CU93QiSW.js} +2 -2
- package/studio/web/assets/{profiler-DotzgiCJ.js.map → profiler-CU93QiSW.js.map} +1 -1
- package/studio/web/assets/{react-redux-VxUEx_mU.js → react-redux-B8k9Ep7e.js} +2 -2
- package/studio/web/assets/{react-redux-VxUEx_mU.js.map → react-redux-B8k9Ep7e.js.map} +1 -1
- package/studio/web/assets/{startRecording-B_9J9Csd.js → startRecording-DFeBXGk6.js} +2 -2
- package/studio/web/assets/{startRecording-B_9J9Csd.js.map → startRecording-DFeBXGk6.js.map} +1 -1
- package/studio/web/index.html +1 -1
- package/core/resources/ResourceInterfaceV2.ts +0 -53
- package/core/resources/ResourceV2.ts +0 -67
- package/core/v1.d.ts +0 -39
- package/core/v1.js +0 -41
- package/core/v2.d.ts +0 -39
- package/core/v2.js +0 -41
- package/dist/core/resources/ResourceInterfaceV2.js +0 -3
- package/dist/core/resources/ResourceInterfaceV2.js.map +0 -1
- package/dist/core/resources/ResourceV2.js +0 -27
- package/dist/core/resources/ResourceV2.js.map +0 -1
|
@@ -112,9 +112,10 @@ jobs:
|
|
|
112
112
|
THREADS_DEBUG: false
|
|
113
113
|
NODE_HOSTNAME: 'localhost'
|
|
114
114
|
run: |
|
|
115
|
-
|
|
115
|
+
mkdir -p /tmp/hdb/log
|
|
116
|
+
node ./dist/bin/harper.js install > /tmp/hdb/log/install-stdout.log 2> /tmp/hdb/log/install-stderr.log
|
|
116
117
|
sleep 10
|
|
117
|
-
node ./dist/bin/harper.js start
|
|
118
|
+
node ./dist/bin/harper.js start > /tmp/hdb/log/start-stdout.log 2> /tmp/hdb/log/start-stderr.log &
|
|
118
119
|
sleep 10
|
|
119
120
|
|
|
120
121
|
- name: Run API Tests
|
|
@@ -125,12 +126,13 @@ jobs:
|
|
|
125
126
|
run: npm run test:integration:api-tests
|
|
126
127
|
|
|
127
128
|
- name: Upload Harper logs
|
|
128
|
-
if:
|
|
129
|
+
if: failure()
|
|
129
130
|
uses: actions/upload-artifact@v7
|
|
130
131
|
with:
|
|
131
132
|
name: harper-integration-api-test-logs-node-${{ matrix.node-version }}
|
|
132
|
-
path: /tmp/hdb/log/
|
|
133
|
-
retention-days:
|
|
133
|
+
path: /tmp/hdb/log/
|
|
134
|
+
retention-days: 3
|
|
135
|
+
if-no-files-found: ignore
|
|
134
136
|
|
|
135
137
|
run-integration-tests:
|
|
136
138
|
name: Integration Tests ${{matrix.shard}}/4 (Node.js v${{ matrix.node-version }})
|
|
@@ -494,7 +494,7 @@ export async function loadComponent(
|
|
|
494
494
|
return loadComponentDirectories(); // return the promise
|
|
495
495
|
});
|
|
496
496
|
}
|
|
497
|
-
if (config.extensionModule || config.pluginModule) {
|
|
497
|
+
if ((config.extensionModule || config.pluginModule) && (!isMainThread || config.runOnMainThread)) {
|
|
498
498
|
const extensionModule = await scopedImport(
|
|
499
499
|
join(componentDirectory, config.extensionModule || config.pluginModule),
|
|
500
500
|
applicationScope
|
|
@@ -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;
|
|
@@ -112,9 +112,13 @@ interface RunHarperCommandOptions {
|
|
|
112
112
|
*/
|
|
113
113
|
function runHarperCommand({ args, env, completionMessage, logDir }: RunHarperCommandOptions): Promise<ChildProcess> {
|
|
114
114
|
const harperScript = getHarperScript();
|
|
115
|
-
const proc = spawn(
|
|
116
|
-
|
|
117
|
-
|
|
115
|
+
const proc = spawn(
|
|
116
|
+
'node',
|
|
117
|
+
['--trace-warnings', '--force-node-api-uncaught-exceptions-policy=true', harperScript, ...args],
|
|
118
|
+
{
|
|
119
|
+
env: { ...process.env, ...env },
|
|
120
|
+
}
|
|
121
|
+
);
|
|
118
122
|
|
|
119
123
|
let stdoutStream: WriteStream | undefined;
|
|
120
124
|
let stderrStream: WriteStream | undefined;
|
|
@@ -156,12 +160,12 @@ function runHarperCommand({ args, env, completionMessage, logDir }: RunHarperCom
|
|
|
156
160
|
proc.on('error', (error) => {
|
|
157
161
|
reject(error);
|
|
158
162
|
});
|
|
159
|
-
proc.on('exit', (statusCode) => {
|
|
163
|
+
proc.on('exit', (statusCode, signal) => {
|
|
160
164
|
clearTimeout(timer);
|
|
161
165
|
if (statusCode === 0) {
|
|
162
166
|
resolve(proc);
|
|
163
167
|
} else {
|
|
164
|
-
let errorMessage = `Harper process failed with exit code ${statusCode}`;
|
|
168
|
+
let errorMessage = `Harper process failed with exit code/signal ${statusCode ?? signal}`;
|
|
165
169
|
stderrStream?.write(errorMessage);
|
|
166
170
|
if (stderr) {
|
|
167
171
|
errorMessage += `\n\nstderr:\n${stderr}`;
|
|
@@ -180,6 +184,10 @@ function runHarperCommand({ args, env, completionMessage, logDir }: RunHarperCom
|
|
|
180
184
|
* This function performs installation, startup, and waits for Harper to be ready.
|
|
181
185
|
* Always call `teardownHarper()` in the `after()` hook to clean up resources.
|
|
182
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
|
+
*
|
|
183
191
|
* @param ctx - The test context to populate with Harper instance details
|
|
184
192
|
* @param options - Optional configuration for the setup process
|
|
185
193
|
* @returns The context with the `harper` property populated
|
|
@@ -188,7 +196,7 @@ function runHarperCommand({ args, env, completionMessage, logDir }: RunHarperCom
|
|
|
188
196
|
* ```ts
|
|
189
197
|
* suite('My tests', (ctx: ContextWithHarper) => {
|
|
190
198
|
* before(async () => {
|
|
191
|
-
* await
|
|
199
|
+
* await startHarper(ctx);
|
|
192
200
|
* });
|
|
193
201
|
*
|
|
194
202
|
* after(async () => {
|
|
@@ -202,19 +210,7 @@ function runHarperCommand({ args, env, completionMessage, logDir }: RunHarperCom
|
|
|
202
210
|
* });
|
|
203
211
|
* ```
|
|
204
212
|
*/
|
|
205
|
-
export async function
|
|
206
|
-
return startHarper(ctx, options);
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
/**
|
|
210
|
-
* Starts a Harper instance that has been installed.
|
|
211
|
-
*
|
|
212
|
-
* This is a lower-level function called by `setupHarper()`.
|
|
213
|
-
* Most tests should use `setupHarper()` instead.
|
|
214
|
-
*
|
|
215
|
-
* @param ctx - The test context with Harper installation details
|
|
216
|
-
*/
|
|
217
|
-
export async function startHarper(ctx: ContextWithHarper, options?: SetupHarperOptions): Promise<ContextWithHarper> {
|
|
213
|
+
export async function startHarper(ctx: ContextWithHarper, options?: StartHarperOptions): Promise<ContextWithHarper> {
|
|
218
214
|
// Create a directory for this Harper installation
|
|
219
215
|
// Use the system temp directory by default, or a custom parent directory if specified
|
|
220
216
|
const installDirPrefix = join(
|
|
@@ -318,7 +314,7 @@ export async function killHarper(ctx: ContextWithHarper): Promise<void> {
|
|
|
318
314
|
* ```ts
|
|
319
315
|
* suite('My tests', (ctx: ContextWithHarper) => {
|
|
320
316
|
* before(async () => {
|
|
321
|
-
* await
|
|
317
|
+
* await startHarper(ctx);
|
|
322
318
|
* });
|
|
323
319
|
*
|
|
324
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,20 +144,20 @@
|
|
|
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
|
-
"@harperfast/rocksdb-js": "^0.1.
|
|
160
|
+
"@harperfast/rocksdb-js": "^0.1.12",
|
|
161
161
|
"@turf/area": "6.5.0",
|
|
162
162
|
"@turf/boolean-contains": "6.5.0",
|
|
163
163
|
"@turf/boolean-disjoint": "6.5.0",
|
|
@@ -168,6 +168,7 @@
|
|
|
168
168
|
"@turf/helpers": "6.5.0",
|
|
169
169
|
"@turf/length": "6.5.0",
|
|
170
170
|
"alasql": "4.6.6",
|
|
171
|
+
"amaro": "^1.1.8",
|
|
171
172
|
"argon2": "0.44.0",
|
|
172
173
|
"asn1js": "3.0.7",
|
|
173
174
|
"cbor-x": "1.6.4",
|
|
@@ -178,8 +179,8 @@
|
|
|
178
179
|
"dotenv": "^16.4.7",
|
|
179
180
|
"easy-ocsp": "1.2.2",
|
|
180
181
|
"fast-glob": "3.3.3",
|
|
181
|
-
"fastify": "
|
|
182
|
-
"fastify-plugin": "
|
|
182
|
+
"fastify": "^5.8.2",
|
|
183
|
+
"fastify-plugin": "^5.1.0",
|
|
183
184
|
"fs-extra": "11.3.3",
|
|
184
185
|
"graphql": "^16.10.0",
|
|
185
186
|
"graphql-http": "^1.22.4",
|
|
@@ -193,7 +194,7 @@
|
|
|
193
194
|
"jsonata": "1.8.7",
|
|
194
195
|
"jsonwebtoken": "9.0.3",
|
|
195
196
|
"lmdb": "3.5.2",
|
|
196
|
-
"lodash": "4.17.
|
|
197
|
+
"lodash": "^4.17.23",
|
|
197
198
|
"mathjs": "11.12.0",
|
|
198
199
|
"micromatch": "^4.0.8",
|
|
199
200
|
"minimist": "1.2.8",
|
|
@@ -221,8 +222,8 @@
|
|
|
221
222
|
"ses": "^1.14.0",
|
|
222
223
|
"stream-chain": "2.2.5",
|
|
223
224
|
"stream-json": "1.9.1",
|
|
224
|
-
"systeminformation": "5.
|
|
225
|
-
"tar-fs": "3.
|
|
225
|
+
"systeminformation": "^5.31.4",
|
|
226
|
+
"tar-fs": "^3.1.2",
|
|
226
227
|
"ulidx": "0.5.0",
|
|
227
228
|
"uuid": "11.1.0",
|
|
228
229
|
"validate.js": "0.13.1",
|
|
@@ -30,7 +30,7 @@ export interface ResourceInterface<Record extends object = any>
|
|
|
30
30
|
allowUpdate(user: User, record: Promise<Record & RecordObject>, context: Context): boolean | Promise<boolean>;
|
|
31
31
|
put?(
|
|
32
32
|
record: Record & RecordObject,
|
|
33
|
-
target
|
|
33
|
+
target?: RequestTargetOrId
|
|
34
34
|
): void | (Record & Partial<RecordObject>) | Promise<void | (Record & Partial<RecordObject>)>;
|
|
35
35
|
patch?(
|
|
36
36
|
record: Partial<Record & RecordObject>,
|