Skip to content

Commit 142ee12

Browse files
ready for review?
1 parent 1e7d138 commit 142ee12

12 files changed

Lines changed: 700 additions & 175 deletions

File tree

CONTRIBUTING.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ Install dependencies using `npm install`
1515

1616
Build the project using `npm run build` or `npm run build:watch` to automatically rebuild on file changes.
1717

18-
Run integration tests using `npm run test:integration`. Make sure to read the [integration test instructions](./integrationTests/apiTests/README.md) for setup.
18+
Run integration tests using `npm run test:integration`. Make sure to read the [integration test instructions](./integrationTests/README.md) for setup requirements (particularly the loopback address configuration).
1919

2020
Run unit tests using `npm run test:unit <unit-test-file>` or `npm run test:unit:all`, but make sure to build the project first since unit tests depend on the built source files.
2121

integrationTests/README.md

Lines changed: 35 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
We prioritize performance at Harper, and that extends to core developer experience and productivity. Harper is a significantly complex application involving multiple processes, threads, file I/O, database operations, network calls, and so much more. Integration tests differ from unit test as they **must run against the built distribution of Harper as if it was a user or another system** (instead of importing source/built code directly). As a result, integration tests will be as resource intensive as the Harper system is.
44

55
In order to keep things as fast as possible, integration test **files** must be:
6+
67
- **Independent**: Test files do not depend on execution order or state from other test files
78
- **Hermetic**: Test files are self-contained with no external side-effects
89
- **Deterministic**: The same input always produces the same output, no matter how many times its executed
@@ -13,9 +14,9 @@ If we follow these guidelines strictly, we can execute integration tests concurr
1314

1415
This directory contains integration tests migrated from our old repository. These test are incredibly important and are one of the most important ways we've verified the Harper application continues to work throughout the open source transfer. Unfortunately, these tests are very interdependent and cannot be run separately from each other. The setup in early test files is necessary for most other test files to work, and some tests (spread across multiple files) interact with the same resources and data and in some circumstances must be executed in a certain order. This interdependence has made it very difficult, if not impossible, for developers to isolated failing tests during development.
1516

16-
These tests should be generally excluded from our new integration testing guidelines while they are actively ported to new implementations. They are automatically ignored by the `test:integration` script, and can be executed using the `test:integration:apiTests` npm script instead.
17+
These tests should be generally excluded from our new integration testing guidelines while they are actively ported to new implementations. They are automatically ignored by the `test:integration` script, and can be executed using the `test:integration:api-tests` npm script instead.
1718

18-
Complete documentation for configuring and executing these tests is available [here](./apiTests/README.md).
19+
These tests have their own unique configuration and setup requirements that differ from the newer integration test guidelines.
1920

2021
## Running Tests
2122

@@ -24,11 +25,13 @@ Complete documentation for configuring and executing these tests is available [h
2425
>
2526
> Linux Ubuntu systems generally have 127.0.0.1 - 127.255.255.255 enabled by default, but MacOS and Windows does not.
2627
>
27-
> Use the included script `integrationTests/setup-loopback.sh` to quickly enable the required set of loopback addresses.
28+
> Use the included script `integrationTests/utils/scripts/setup-loopback.sh` to quickly enable the required set of loopback addresses.
2829
>
2930
> This script does require `sudo` permissions. We recommend reviewing the source before executing.
31+
>
32+
> The script respects the `HARPER_INTEGRATION_TEST_LOOPBACK_POOL_COUNT` environment variable (defaults to 32) to configure the number of loopback addresses. The integration test runner will automatically validate that the required loopback addresses are available before running tests and will exit with an error if they are not configured.
3033
31-
The Node.js test runner uses process isolation to run test files concurrently _by default_. Meaning, `node --test "integrationTests/*.test.ts"` will run every matched file in its own process. Node.js determines the number of concurrent processes using `os.availableParallelism() - 1`. Since Harper is itself a resource intensive application, this default concurrency causes extreme resource contention and system thrashing as each integration test file is also running at least one more process, the Harper application, plus whatever additional work the Harper application does as part of the tests. Through deep analysis, we have determine a safer default concurrency for our circumstances is slightly more than half of the available parallelism. For more information see [Node.js Test Runner Parallelization Analysis](https://github.com/Ethan-Arrowood/node-test-runner-parallelization-analysis). Thus, using `node --test` to run _all_ integration tests is insufficient. We include a npm script `test:integration` for simplified execution. The `node --test` command can still be used to run an individual test file, or at most a small set of test files, but we recommend using `test:integration` whenever possible.
34+
The Node.js test runner uses process isolation to run test files concurrently _by default_. Meaning, `node --test "integrationTests/*.test.mts"` will run every matched file in its own process. Node.js determines the number of concurrent processes using `os.availableParallelism() - 1`. Since Harper is itself a resource intensive application, this default concurrency causes extreme resource contention and system thrashing as each integration test file is also running at least one more process, the Harper application, plus whatever additional work the Harper application does as part of the tests. Through deep analysis, we have determine a safer default concurrency for our circumstances is slightly more than half of the available parallelism. For more information see [Node.js Test Runner Parallelization Analysis](https://github.com/Ethan-Arrowood/node-test-runner-parallelization-analysis). Thus, using `node --test` to run _all_ integration tests is insufficient. We include a npm script `test:integration` for simplified execution. The `node --test` command can still be used to run an individual test file, or at most a small set of test files, but we recommend using `test:integration` whenever possible.
3235

3336
The `test:integration` script will execute **all integration tests by default** using the safe concurrency settings as described above.
3437

@@ -46,18 +49,19 @@ For example:
4649

4750
```sh
4851
# Supports exact file paths
49-
npm run test:integration -- "integrationTests/install.test.ts"
52+
npm run test:integration -- "integrationTests/deploy/deploy-from-source.test.mts"
5053
# Or glob patterns
51-
npm run test:integration -- "integrationTests/applicationLifecycle/*.test.ts"
54+
npm run test:integration -- "integrationTests/deploy/*.test.mts"
5255
# Or multiple entries
53-
npm run test:integration -- "integrationTests/install.test.ts" "integrationTests/start.test.ts" "integrationTests/stop.test.ts"
56+
npm run test:integration -- "integrationTests/deploy/deploy-from-source.test.mts" "integrationTests/deploy/deploy-from-github.test.mts"
5457
```
5558

5659
### Available options:
5760

5861
All CLI arguments can be overridden using the associative `HARPER_INTEGRATION_TEST_*` environment variable where the `*` is replaced by the capitalized CLI argument name. For example, `--concurrency` is replaced by `HARPER_INTEGRATION_TEST_CONCURRENCY`.
5962

6063
Configuration precedence order is:
64+
6165
1. Environment Variables
6266
2. CLI Argument
6367
3. Default Value
@@ -126,7 +130,7 @@ This option can be overridden using the `HARPER_INTEGRATION_TEST_ONLY` environme
126130

127131
As mentioned in the introduction, integration test **files** should be **independent**, **hermetic**, and **deterministic**.
128132

129-
All files meant to be executed by the test runner should end in `.test.ts`. They can be nested within directories for organization purposes, or be top-level in this `integrationTests` directory. Every test file should begin with a comment block explaining exactly what it is meant to test.
133+
All files meant to be executed by the test runner should end in `.test.mts` (ES module TypeScript). They can be nested within directories for organization purposes, or be top-level in this `integrationTests` directory. Every test file should begin with a comment block explaining exactly what it is meant to test.
130134

131135
Tests must use the Node.js Test Runner API [`node:test`]() for establishing suites (`describe` or `suite`), tests (`it` or `test`), and lifecycle methods (`before`, `beforeEach`, `after`, and `afterEach`).
132136

@@ -144,45 +148,30 @@ Since these tests interact with a running Harper instance directly, they often w
144148
- Network Responses
145149
- File System
146150

147-
Reusable assertion patterns will develop over time. The [utilities](#utilities) section documents various helpers for things such as setting up and tearing down Harper instances, assertion patterns, and more. Familiarize yourself with this section as well as existing tests to best understand general testing patterns.
151+
Reusable assertion patterns will develop over time. Familiarize yourself with existing tests and the [Integration Test Utilities documentation](./utils/README.md) to best understand general testing patterns.
148152

149153
### Utilities
150154

151-
#### `setupHarper(context): Promise<void>`
152-
153-
- **context** - [`SuiteContext`]() | [`TestContext`]() - The context for a set of tests.
154-
155-
This method should be used in the [`before()`]() function for a set of tests. It will create a Harper instance and assign relevant information to the `context.harper` object for use throughout the set of tests. **Do not forget** to call `teardownHarper(ctx)` in the `after()` function or you will have phantom Harper processes after the tests complete.
156-
157-
For example,
158-
159-
```ts
160-
suite('test suite', (ctx) => {
161-
before(async () => {
162-
await setupHarper(ctx);
163-
});
164-
165-
after(async () => {
166-
await teardownHarper(ctx);
167-
});
155+
Integration test utilities are located in the [`integrationTests/utils/`](./utils/) directory and provide essential functionality for test setup, teardown, and common operations.
168156

169-
test('make a request', async () => {
170-
// The `ctx.harper` object will have important information about the relative instance
171-
const response = await fetch(ctx.harper.url);
172-
});
173-
});
174-
```
157+
**Complete utilities documentation is available at [`integrationTests/utils/README.md`](./utils/README.md).**
175158

176-
#### `teardownHarper(context): Promise<void>`
159+
#### Quick Reference
177160

178-
- **context** - [`ContextWithHarper`](#contextwithharper) - A suite or test context object with the `harper` property from [`setupHarper()`]()
161+
The most commonly used utilities are:
179162

180-
This method should be used in the [`after()`]() function in conjunction with the [`setupHarper()`]() and [`before()`]() methods. It will shutdown the relative Harper instance and remove it from the filesystem.
163+
- **`setupHarper(context)`** - Sets up a complete Harper instance for testing. Use in `before()` hooks.
164+
- **`teardownHarper(context)`** - Tears down a Harper instance and cleans up resources. Use in `after()` hooks.
165+
- **`ContextWithHarper`** - TypeScript interface for test context with Harper instance details.
166+
- **`targz(dirPath)`** - Compresses a directory into a base64-encoded tar.gz string for application deployment.
181167

182-
For example,
168+
**Example usage:**
183169

184170
```ts
185-
suite('test suite', (ctx) => {
171+
import { suite, test, before, after } from 'node:test';
172+
import { setupHarper, teardownHarper, type ContextWithHarper } from './utils/harperLifecycle.mts';
173+
174+
suite('test suite', (ctx: ContextWithHarper) => {
186175
before(async () => {
187176
await setupHarper(ctx);
188177
});
@@ -191,23 +180,14 @@ suite('test suite', (ctx) => {
191180
await teardownHarper(ctx);
192181
});
193182

194-
test('make a request', async ( ) => {
195-
// The `ctx.harper` object will have important information about the relative instance
196-
const response = await fetch(ctx.harper.url);
183+
test('make a request', async () => {
184+
const response = await fetch(ctx.harper.httpURL);
185+
// ... assertions
197186
});
198187
});
199188
```
200189

201-
#### `ContextWithHarper`
202-
203-
The interface assigned to a [`SuiteContext`]() or [`TestContext`]() object by [`setupHarper()`]() and necessary for [`teardownHarper()`]().
204-
205-
Properties:
206-
- **url** - [`URL`]() - The URL instance for the Harper instance
207-
- **admin** - `object`
208-
- **username** - `string` - The Harper Admin Username
209-
- **password** - `string` - The Harper Admin Password
210-
- **rootpath** - `string` - The absolute path to the root of the Harper instance
190+
**For detailed documentation** including all available utilities, parameters, return types, configuration options, and advanced usage examples, see the [**Integration Test Utilities Documentation**](./utils/README.md).
211191

212192
### Test File Template
213193

@@ -223,10 +203,10 @@ Copy and paste the following content to get started:
223203
*/
224204
import { suite, test, before, after } from 'node:test';
225205
import { strictEqual } from 'node:assert/strict';
226-
// If this file is nested don't forget to update the path here
227-
import { setupHarper, teardownHarper } from './utils.mts';
206+
// Note: adjust the relative path accordingly (e.g., '../utils/harperLifecycle.mts')
207+
import { setupHarper, teardownHarper, type ContextWithHarper } from './utils/harperLifecycle.mts';
228208

229-
suite('short description of tests', (ctx) => {
209+
suite('short description of tests', (ctx: ContextWithHarper) => {
230210
before(async () => {
231211
await setupHarper(ctx);
232212
});
@@ -237,6 +217,7 @@ suite('short description of tests', (ctx) => {
237217

238218
test('test description', async () => {
239219
// Use `ctx.harper` for access to the instance
220+
// Example: const response = await fetch(ctx.harper.httpURL);
240221
});
241222
});
242223
```
@@ -277,7 +258,7 @@ suite('Concurrency Disabled', () => {
277258
- Suites always run sequentially so the **total run time for the file is ~3 seconds**
278259

279260
```
280-
❯ node --test example.test.ts
261+
❯ node --test example.test.ts
281262
▶ Concurrency Enabled
282263
✔ 1 second (1001.359083ms)
283264
✔ 1 second (1001.860375ms)

integrationTests/deploy/deploy-from-github.test.mts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
/**
22
* Deploy an application from a GitHub repository.
3-
*
3+
*
44
* Verifies application is deployed correctly and is accessible via both API and static site.
55
*/
66
import { suite, test, before, after } from 'node:test';
@@ -43,7 +43,7 @@ suite('GitHub application deployment', (ctx: ContextWithHarper) => {
4343
});
4444
strictEqual(response.status, 200);
4545
const body = await response.json();
46-
deepStrictEqual(body, {"message":"Successfully deployed: test-application, restarting HarperDB"});
46+
deepStrictEqual(body, { message: 'Successfully deployed: test-application, restarting HarperDB' });
4747
await sleep(5000);
4848
ok(existsSync(join(ctx.harper.installDir, 'components', project)));
4949

@@ -63,7 +63,7 @@ suite('GitHub application deployment', (ctx: ContextWithHarper) => {
6363
const response = await fetch(`${ctx.harper.httpURL}/Greeting`);
6464
strictEqual(response.status, 200);
6565
const body = await response.json();
66-
deepStrictEqual(body, { greeting: "Hello, world!" });
66+
deepStrictEqual(body, { greeting: 'Hello, world!' });
6767
});
6868

6969
test('access application static site', async () => {

integrationTests/deploy/deploy-from-source.test.mts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
/**
22
* Local application deployment test.
3-
*
3+
*
44
* Deploys an application from a fixture directory using the `payload` parameter of
55
* the `deploy_component` Operations API call. Verifies that the application is
66
* deployed correctly and can be accessed via HTTP.
7-
*
7+
*
88
*/
99
import { suite, test, before, after } from 'node:test';
1010
import { deepStrictEqual, ok, strictEqual } from 'node:assert/strict';
@@ -46,7 +46,7 @@ suite('Local application deployment', (ctx: ContextWithHarper) => {
4646
});
4747
strictEqual(response.status, 200);
4848
const body = await response.json();
49-
deepStrictEqual(body, {"message":"Successfully deployed: test-application, restarting HarperDB"});
49+
deepStrictEqual(body, { message: 'Successfully deployed: test-application, restarting HarperDB' });
5050
await sleep(5000);
5151
ok(existsSync(join(ctx.harper.installDir, 'components', project)));
5252
ok(existsSync(join(ctx.harper.installDir, 'harper-application-lock.json')));
Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
1-
<!DOCTYPE html>
1+
<!doctype html>
22
<html lang="en">
3-
<head>
4-
<meta charset="UTF-8">
5-
<meta name="viewport" content="width=device-width, initial-scale=1.0">
6-
<title>About Page</title>
7-
</head>
8-
<body>
9-
<h1>About Page</h1>
10-
</body>
11-
</html>
3+
<head>
4+
<meta charset="UTF-8" />
5+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
6+
<title>About Page</title>
7+
</head>
8+
<body>
9+
<h1>About Page</h1>
10+
</body>
11+
</html>

0 commit comments

Comments
 (0)