import { ChildProcess } from 'node:child_process'; import { type SuiteContext, type TestContext } from 'node:test'; /** * Minimal context interface required by startHarper/teardownHarper. * * This is intentionally loose so it can be satisfied by: * - node:test SuiteContext/TestContext objects (via ContextWithHarper) * - Plain objects (e.g. Playwright worker fixtures: `createHarperContext()`) */ export interface HarperTestContext { /** Optional name used for log directory naming (e.g. suite name or Playwright worker index). */ name?: string; /** Populated by startHarper(). May be pre-seeded with dataRootDir/hostname to reuse across restarts. */ harper?: Partial; } /** * A started context — harper is fully populated after startHarper() resolves. */ export interface StartedHarperTestContext extends HarperTestContext { harper: HarperContext; } /** * Creates a plain object satisfying HarperTestContext, for use outside node:test * (e.g. as a Playwright worker fixture). * * @param name Optional name for log directory naming (e.g. Playwright worker index). */ export declare function createHarperContext(name?: string): HarperTestContext; export declare const OPERATIONS_API_PORT = 9925; export declare const DEFAULT_ADMIN_USERNAME = "admin"; export declare const DEFAULT_ADMIN_PASSWORD = "Abc1234!"; /** * Maximum time to wait between chunks of Harper startup output before treating the process * as hung. The startup watchdog resets this window on every chunk of output, so the limit is * time-since-last-progress rather than total boot time: a slow-but-healthy boot that keeps * logging never trips it — only true silence does. Higher under CI, matching * {@link DEFAULT_STARTUP_MAX_MS}'s CI scaling — shared/contended runners can go quiet between * log lines for longer without actually being hung (e.g. native module load/RocksDB open * competing with sibling concurrently-booting Harper instances for CPU). * * Override with `HARPER_INTEGRATION_TEST_STARTUP_TIMEOUT_MS`. Default 60s (150s under CI). */ export declare const DEFAULT_STARTUP_TIMEOUT_MS: number; /** * Absolute ceiling on total startup time, regardless of ongoing output — a generous backstop * so a process that chatters forever without ever reporting ready still fails. Higher under CI, * where shared/contended runners boot more slowly. * * Override with `HARPER_INTEGRATION_TEST_STARTUP_MAX_MS`. Default 120s (300s under CI). */ export declare const DEFAULT_STARTUP_MAX_MS: number; /** * Grace period after SIGTERM before escalating to SIGKILL during teardown, giving Harper time * to shut down cleanly (flush RocksDB, release ports, reap worker children). * * Override with `HARPER_INTEGRATION_TEST_TEARDOWN_GRACE_MS`. Default 5s. */ export declare const DEFAULT_TEARDOWN_GRACE_MS: number; /** * Time teardown's safety assertion waits for Harper's fixed ports to be free before recycling the * loopback address. Killing Harper's process tree (and waiting for exit) should free them * immediately, so this normally returns on the first check; it only matters if a child process * escaped the kill. If the ports are still in use at the deadline, the address is recycled anyway * (no worse than not waiting) and a warning is logged. * * Override with `HARPER_INTEGRATION_TEST_PORT_RELEASE_TIMEOUT_MS`. Default 5s. */ export declare const DEFAULT_PORT_RELEASE_TIMEOUT_MS: number; /** * The runtime to use for running Harper during tests. * Set via the HARPER_RUNTIME environment variable ('node' or 'bun'). * Defaults to 'node'. */ export declare const HARPER_RUNTIME: 'node' | 'bun'; /** * Marker emitted on stdout by startHarper() so the test runner (run.ts) can map * a Harper instance's log directory to the currently executing test file via * the node:test `test:stdout` event. */ export declare const LOG_DIR_MARKER_PREFIX = "[Harper] Logs for this instance will be stored in:"; /** * Options for setting up a Harper instance. */ export interface StartHarperOptions { /** * Maximum time (ms) to wait between chunks of startup output before treating Harper as hung. * Resets on every chunk of output, so it bounds silence, not total boot time. * Falls back to {@link DEFAULT_STARTUP_TIMEOUT_MS} (60s locally, 150s under CI). */ startupTimeoutMs?: number; /** * Absolute ceiling (ms) on total startup time, regardless of ongoing output. * Falls back to {@link DEFAULT_STARTUP_MAX_MS} (120s locally, 300s under CI). */ startupMaxMs?: number; /** * Additional configuration options to pass to the Harper CLI. */ config?: any; /** * Environment variables to set when running Harper. */ env?: any; /** * Explicit path to the Harper CLI script (dist/bin/harper.js). * If not provided, resolution order is: * 1. This option * 2. HARPER_INTEGRATION_TEST_INSTALL_SCRIPT environment variable * 3. Auto-resolved from 'harper' package in node_modules */ harperBinPath?: string; } /** * Build the environment for the spawned Harper child process. Exported for testing. * * `HOME`/`USERPROFILE` are applied LAST so the dataRootDir isolation always takes precedence over * caller-supplied `env` (and over a spread of `process.env`, which contains HOME) — otherwise a caller could * clobber it and re-expose the developer's real home. The isolation keeps Harper's global boot pointer * (`$HOME/.harperdb/hdb_boot_properties.file`) and generated license keys inside the throwaway dataRootDir: * cleaned up with it, never touching the real home, and isolated across concurrent suites. On first start * Harper records its rootPath in that global boot file and — with an explicit `--ROOTPATH` — never overwrites * it again; teardown removes only dataRootDir, so without this isolation the developer's real `~/.harperdb` * would be left pointing at a since-deleted temp install, silently breaking the next `harper dev`/`harper run` * anywhere on the machine. */ export declare function buildHarperChildEnv(dataRootDir: string, config: any, env?: any): Record; export interface HarperContext { /** Absolute path to the Harper installation directory */ dataRootDir: string; /** Admin credentials for the Harper instance */ admin: { /** Admin username (default: 'admin') */ username: string; /** Admin password (default: 'Abc1234!') */ password: string; }; /** HTTP URL for the Harper instance (e.g., 'http://127.0.0.2:9926') */ httpURL: string; /** Operations API URL (e.g., 'http://127.0.0.2:9925') */ operationsAPIURL: string; /** Assigned loopback IP address (e.g., '127.0.0.2') */ hostname: string; /** Child process for the Harper instance */ process: ChildProcess; /** Absolute path to the log directory for this suite (only set when HARPER_INTEGRATION_TEST_LOG_DIR is configured) */ logDir?: string; /** Captured stdout/stderr from Harper startup, up to the point it reported ready. */ startupOutput?: { stdout: string; stderr: string; }; } /** * Test context interface with Harper instance details, for use with node:test. * * This interface is populated by `startHarper()` and contains * all necessary information to interact with the test Harper instance. * * For use outside node:test (e.g. Playwright), use `createHarperContext()` to * create a plain object satisfying `HarperTestContext` instead. */ export interface ContextWithHarper extends SuiteContext, TestContext { harper: HarperContext; } /** * Error thrown when a Harper process fails to start or times out. * Includes captured stdout and stderr for diagnostics. */ export declare class HarperStartupError extends Error { stdout: string; stderr: string; constructor(message: string, stdout: string, stderr: string); } interface RunHarperCommandOptions { args: string[]; env: any; completionMessage?: string; /** When set, stdout and stderr are written to files in this directory */ logDir?: string; harperBinPath?: string; /** Idle timeout (ms): max time between output chunks before treating the process as hung. Resets on output. Falls back to DEFAULT_STARTUP_TIMEOUT_MS. */ timeoutMs?: number; /** Absolute timeout (ms): ceiling on total time regardless of output. Falls back to DEFAULT_STARTUP_MAX_MS. */ maxMs?: number; } interface RunHarperCommandResult { process: ChildProcess; /** Captured stdout up to the point the process was considered ready or exited. */ stdout: string; /** Captured stderr up to the point the process was considered ready or exited. */ stderr: string; } /** * Runs a Harper CLI command and captures output. * * When `logDir` is provided, stdout and stderr are also written to files * (`stdout.log` and `stderr.log`) in that directory. * * @throws {HarperStartupError} If the command times out or exits with a non-zero status code * * Exported for unit testing; not part of the public API (not re-exported from `index.ts`). */ export declare function runHarperCommand({ args, env, completionMessage, logDir, harperBinPath, timeoutMs, maxMs, }: RunHarperCommandOptions): Promise; /** * Sets up a Harper instance with a component pre-installed from a local directory. * * Copies `fixturePath` into `{dataRootDir}/components/{name}` before Harper starts, * so the component is available on the first request without a post-startup deploy. * Use this when tests need a known route available at startup (e.g. mTLS cert tests). * * @param ctx - The test context to populate with Harper instance details * @param fixturePath - Absolute path to the component directory to pre-install * @param options - Optional configuration for the setup process */ export declare function setupHarperWithFixture(ctx: HarperTestContext, fixturePath: string, options?: StartHarperOptions): Promise; /** * Sets up and starts a Harper instance for testing. * * @param ctx - The test context to populate with Harper instance details * @param options - Optional configuration for the setup process * @returns The context with the `harper` property populated * * @example * ```ts * suite('My tests', (ctx: ContextWithHarper) => { * before(async () => { * await startHarper(ctx); * }); * * after(async () => { * await teardownHarper(ctx); * }); * * test('can connect', async () => { * const response = await fetch(ctx.harper.httpURL); * // ... * }); * }); * ``` */ export declare function startHarper(ctx: HarperTestContext, options?: StartHarperOptions): Promise; /** * Kill harper process (can be used for teardown, or killing it before a restart). * * Sends SIGTERM to Harper's whole process tree first and gives it a grace period to shut down * cleanly (flush RocksDB, release ports, reap worker children) before escalating to SIGKILL. * After SIGKILL it waits briefly for the process to actually exit, so callers can rely on it * being gone — and, since a dead process releases its listening sockets, on its ports being free. * * @param ctx * @param options.graceMs Time to wait after SIGTERM before sending SIGKILL. Defaults to * {@link DEFAULT_TEARDOWN_GRACE_MS}. */ export declare function killHarper(ctx: StartedHarperTestContext, options?: { graceMs?: number; }): Promise; /** * Tears down a Harper instance and cleans up all resources. * * This function stops the Harper instance, releases the loopback address, * and removes the installation directory. * @param ctx - The test context with Harper instance details * * @example * ```ts * suite('My tests', (ctx: ContextWithHarper) => { * before(async () => { * await startHarper(ctx); * }); * * after(async () => { * await teardownHarper(ctx); * }); * }); * ``` */ export declare function teardownHarper(ctx: StartedHarperTestContext): Promise; export declare function sendOperation(context: HarperContext, operation: any): Promise; export {};