Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
Review the `README.md` and `CONTRIBUTING.md` for all relevant repository information.

## Development Tips
- Ensure you're on at least Node.js v22 or greater when contributing
- Ensure you're on at least Node.js v22.18 or greater when contributing (native TypeScript stripping for `npm test` requires it; see `devEngines`)
- Use `npm install` to install dependencies
- Use `npm run build` to build the project
- Do not run `npm version` or `npm publish`; these commands are for humans only.
Expand All @@ -21,4 +21,5 @@ Review the `README.md` and `CONTRIBUTING.md` for all relevant repository informa
## Testing Tips
- Use `npm link` in this directory and `npm link @harperfast/integration-testing` in other project directories to test out changes locally
- Use `npm run check` to type-check the project without generating a build output
- There are currently no tests for this project
- Use `npm test` to run the tests (`node --test` over `test/**/*.test.ts`). Tests live in `test/`, separate from `src/`, so the build (which emits only `src/`) never includes them.
- To keep them runnable without a real Harper, internal-only helpers are exported from their modules (e.g. `runHarperCommand` in `harperLifecycle.ts`) but intentionally NOT re-exported from `index.ts`, so they stay out of the public API.
5 changes: 4 additions & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,12 @@ There are two `tsconfig` files:
```sh
npm run check # Type-check only (no output)
npm run build # Compile src/ → dist/
npm test # Run the tests (node:test over test/**/*.test.ts)
```

There are no automated tests in this package yet. Validation is type-checking plus manual testing via dependent projects.
Tests live in `test/` (separate from `src/`) and run on the built-in Node.js test runner via `npm test`. They execute the `.ts` files directly using Node's native type stripping, which requires **Node 22.18+** (reflected in `devEngines`). Because they live outside `src/`, the published build (which emits only `src/` → `dist/`) never includes them. `tsconfig.json` type-checks both `src/` and `test/`; `tsconfig.build.json` narrows the build to `src/`. Beyond these tests, validation also includes type-checking and manual testing via dependent projects.

Internal-only helpers may be exported from their modules for testing (e.g. `runHarperCommand` in `harperLifecycle.ts`) but are deliberately **not** re-exported from `src/index.ts`, keeping them out of the public API.

## Releases

Expand Down
25 changes: 20 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,30 +68,45 @@ The Harper binary is resolved in the following order:

```ts
interface StartHarperOptions {
startupTimeoutMs?: number; // Default: 30000 or HARPER_INTEGRATION_TEST_STARTUP_TIMEOUT_MS
startupTimeoutMs?: number; // Idle timeout: max gap between startup output chunks. Default: 60000 or HARPER_INTEGRATION_TEST_STARTUP_TIMEOUT_MS
startupMaxMs?: number; // Absolute startup ceiling regardless of output. Default: 120000 (300000 under CI) or HARPER_INTEGRATION_TEST_STARTUP_MAX_MS
config?: object; // Harper config overrides (passed via HARPER_SET_CONFIG)
env?: object; // Additional environment variables for the Harper process
harperBinPath?: string; // Explicit path to dist/bin/harper.js
}
```

Startup readiness is detected by Harper printing `successfully started`. Rather than a single wall-clock deadline (which makes a slow-but-healthy boot indistinguishable from a hang), the watchdog uses an **idle timeout** that resets on every chunk of output — so the limit is time-since-last-progress — plus a generous absolute ceiling as a backstop. This is why a slow CI boot that keeps logging no longer trips the timeout.

> **Behavior change:** `startupTimeoutMs` (and `HARPER_INTEGRATION_TEST_STARTUP_TIMEOUT_MS`) previously meant an absolute startup deadline. It now means the **idle / no-output window**. If you relied on it as a hard ceiling to fail slow boots quickly, set `startupMaxMs` (or `HARPER_INTEGRATION_TEST_STARTUP_MAX_MS`) instead.

**Environment Variables:**

- `HARPER_INTEGRATION_TEST_STARTUP_TIMEOUT_MS` - Default startup timeout
- `HARPER_INTEGRATION_TEST_STARTUP_TIMEOUT_MS` - Idle startup timeout: max time between chunks of startup output before Harper is treated as hung (resets on output). Default `60000`.
- `HARPER_INTEGRATION_TEST_STARTUP_MAX_MS` - Absolute ceiling on total startup time, regardless of ongoing output. Default `120000` (`300000` under CI).
- `HARPER_INTEGRATION_TEST_INSTALL_PARENT_DIR` - Parent directory for temp Harper install dirs (default: OS tmpdir)
- `HARPER_INTEGRATION_TEST_INSTALL_SCRIPT` - Path to Harper CLI script

### `setupHarperWithFixture(ctx, fixturePath, options?)`

Like `startHarper()`, but copies a component directory into the Harper install before starting, so it's available on first boot without a deploy.

### `killHarper(ctx)`
### `killHarper(ctx, options?)`

Sends SIGTERM to the Harper process and waits for it to exit. Does not release the loopback address or clean up the install directory. Useful for restart scenarios where the test will call `startHarper` again.
Terminates Harper's whole process tree and waits for it to exit. It sends SIGTERM first, giving Harper a grace period to shut down cleanly (flush RocksDB, release ports, reap workers) before escalating to SIGKILL, then waits briefly for the actual exit. Because Harper is spawned as its own process group (`detached` on POSIX), the signal targets the group — parent and any child processes — rather than only the direct child; on Windows it uses `taskkill /T`. A dead process releases its listening sockets, so once `killHarper` returns the fixed ports are free. Does not release the loopback address or clean up the install directory. Useful for restart scenarios where the test will call `startHarper` again.

`options.graceMs` overrides the SIGTERM→SIGKILL grace period (default `5000`, or `HARPER_INTEGRATION_TEST_TEARDOWN_GRACE_MS`).

### `teardownHarper(ctx)`

Kills Harper, releases the loopback address back to the pool, and removes the install directory. Call in a teardown/`after()` hook.
Kills Harper's process tree, releases the loopback address back to the pool, and removes the install directory. Call in a teardown/`after()` hook.

Since `killHarper` waits for the process tree to exit, its fixed ports (Operations API, HTTP/S, MQTT/S) are already released by the time the address is recycled. As a safety assertion, teardown still verifies those ports are free before recycling — the pool only guarantees the *address* is bindable, not that these specific ports are free — and logs a warning if any are somehow still held (a sign a Harper child process escaped the kill). The address is recycled regardless.

**Environment Variables:**

- `HARPER_INTEGRATION_TEST_TEARDOWN_GRACE_MS` - Grace period after SIGTERM before escalating to SIGKILL. Default `5000`.
- `HARPER_INTEGRATION_TEST_PORT_RELEASE_TIMEOUT_MS` - Max time teardown's safety assertion waits for Harper's ports to be free before recycling the loopback address (normally instant, since the process tree is already dead). Default `5000`.

### `sendOperation(context, operation)`

Expand Down
5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,15 +22,16 @@
],
"scripts": {
"check": "tsc",
"build": "tsc -p tsconfig.build.json"
"build": "tsc -p tsconfig.build.json",
"test": "node --test \"test/**/*.test.ts\""
},
"engines": {
"node": ">=20"
},
"devEngines": {
"runtime": {
"name": "node",
"version": ">=22",
"version": ">=22.18.0",
"onFail": "error"
},
"packageManager": {
Expand Down
Loading