From e1217f83c89482ea023e57a1f4917237ba552598 Mon Sep 17 00:00:00 2001 From: Austin Akers Date: Fri, 5 Jun 2026 15:38:49 -0400 Subject: [PATCH 1/3] Add integration-tests CI and fix harness CLI resolution The reference repo was already on harper@^5 with @harperfast/integration-testing and an integrationTests/ suite, but two gaps would prevent its tests from ever running: - No .github/workflows/integration-tests.yml existed, so integration tests had no CI gate. Added the standard workflow (Node 22/24/26, actions pinned to commit hashes). - The test did not pass `harperBinPath`. harper's `exports` map only exposes ".", so the harness's default `require.resolve('harper/dist/bin/harper.js')` throws ERR_PACKAGE_PATH_NOT_EXPORTED and the cwd-ancestor fallback finds no dist/bin/harper.js, so getHarperScript() throws "Harper CLI script not found" before Harper ever starts. Resolve the CLI from harper's exported main entry and pass it via the documented `harperBinPath` escape hatch. Co-Authored-By: Claude Opus 4.8 (1M context) --- .github/workflows/integration-tests.yml | 74 +++++++++++++++++++++++++ integrationTests/app.test.ts | 10 +++- 2 files changed, 83 insertions(+), 1 deletion(-) create mode 100644 .github/workflows/integration-tests.yml diff --git a/.github/workflows/integration-tests.yml b/.github/workflows/integration-tests.yml new file mode 100644 index 0000000..e4b2d7a --- /dev/null +++ b/.github/workflows/integration-tests.yml @@ -0,0 +1,74 @@ +name: Integration Tests + +on: + push: + branches: [main] + pull_request: + workflow_dispatch: + inputs: + node-version: + description: 'Node.js version' + required: true + type: choice + default: 'all' + options: + - 'all' + - '22' + - '24' + - '26' + +jobs: + generate-node-version-matrix: + name: Generate Node Version Matrix + runs-on: ubuntu-latest + outputs: + node-versions: ${{ steps.set-node-versions.outputs.node-versions }} + + steps: + - name: Checkout code + uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 + + - name: Set Node versions + id: set-node-versions + run: | + if [ "${{ github.event.inputs.node-version }}" == "all" ] || [ -z "${{ github.event.inputs.node-version }}" ]; then + echo "node-versions=[22, 24, 26]" >> $GITHUB_OUTPUT + else + echo "node-versions=[${{ github.event.inputs.node-version }}]" >> $GITHUB_OUTPUT + fi + + integration-tests: + name: Integration Tests (Node ${{ matrix.node-version }}) + needs: [generate-node-version-matrix] + runs-on: ubuntu-latest + + strategy: + fail-fast: false + matrix: + node-version: ${{ fromJSON(needs.generate-node-version-matrix.outputs.node-versions) }} + + steps: + - name: Checkout code + uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 + + - name: Set up Node.js + uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 + with: + node-version: ${{ matrix.node-version }} + + - name: Install dependencies + run: npm ci + + - name: Run integration tests + run: npm run test:integration + env: + HARPER_INTEGRATION_TEST_LOG_DIR: /tmp/harper-test-logs + FORCE_COLOR: '1' + + - name: Upload Harper logs on failure + if: failure() + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: harper-logs-node-${{ matrix.node-version }} + path: /tmp/harper-test-logs/ + retention-days: 7 diff --git a/integrationTests/app.test.ts b/integrationTests/app.test.ts index 301e89d..5d5ffa3 100644 --- a/integrationTests/app.test.ts +++ b/integrationTests/app.test.ts @@ -8,6 +8,14 @@ import { suite, test, before, after } from 'node:test'; import { strictEqual, ok } from 'node:assert/strict'; import { setupHarperWithFixture, teardownHarper, type ContextWithHarper } from '@harperfast/integration-testing'; import { fileURLToPath } from 'node:url'; +import { createRequire } from 'node:module'; +import { dirname, resolve } from 'node:path'; + +const require = createRequire(import.meta.url); +// harper's `exports` map only exposes ".", so `require.resolve('harper/dist/bin/harper.js')` +// (the harness's default auto-resolution) throws ERR_PACKAGE_PATH_NOT_EXPORTED. Resolve the CLI +// from harper's exported main entry and pass it explicitly via the harness escape hatch. +const harperBinPath = resolve(dirname(require.resolve('harper')), 'bin/harper.js'); const FIXTURE_PATH = fileURLToPath(new URL('../', import.meta.url)); @@ -26,7 +34,7 @@ function authFetch( void suite('agent-example-harper loads', (ctx: ContextWithHarper) => { before(async () => { - await setupHarperWithFixture(ctx, FIXTURE_PATH, { startupTimeoutMs: 60000 }); + await setupHarperWithFixture(ctx, FIXTURE_PATH, { startupTimeoutMs: 60000, harperBinPath }); }); after(async () => { From e02789da945fb722927171345e69d5f10f1e89c2 Mon Sep 17 00:00:00 2001 From: Austin Akers Date: Tue, 9 Jun 2026 10:53:45 -0400 Subject: [PATCH 2/3] Update integrationTests/app.test.ts Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> --- integrationTests/app.test.ts | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/integrationTests/app.test.ts b/integrationTests/app.test.ts index 5d5ffa3..a3ca20c 100644 --- a/integrationTests/app.test.ts +++ b/integrationTests/app.test.ts @@ -8,14 +8,12 @@ import { suite, test, before, after } from 'node:test'; import { strictEqual, ok } from 'node:assert/strict'; import { setupHarperWithFixture, teardownHarper, type ContextWithHarper } from '@harperfast/integration-testing'; import { fileURLToPath } from 'node:url'; -import { createRequire } from 'node:module'; import { dirname, resolve } from 'node:path'; -const require = createRequire(import.meta.url); -// harper's `exports` map only exposes ".", so `require.resolve('harper/dist/bin/harper.js')` +// harper's exports map only exposes ".", so resolving 'harper/dist/bin/harper.js' // (the harness's default auto-resolution) throws ERR_PACKAGE_PATH_NOT_EXPORTED. Resolve the CLI // from harper's exported main entry and pass it explicitly via the harness escape hatch. -const harperBinPath = resolve(dirname(require.resolve('harper')), 'bin/harper.js'); +const harperBinPath = resolve(dirname(fileURLToPath(import.meta.resolve('harper'))), 'bin/harper.js'); const FIXTURE_PATH = fileURLToPath(new URL('../', import.meta.url)); From 8560a744db5240d942c1b8e9b91745fc57f386f8 Mon Sep 17 00:00:00 2001 From: Austin Akers Date: Thu, 11 Jun 2026 13:04:25 -0400 Subject: [PATCH 3/3] Fix blocking review findings - Add timeout-minutes: 15 to integration-tests job (prevents 6-hour default CI burn if a test or teardown hangs) - Move harperBinPath computation inside before() hook (a top-level throw causes a silent module crash with no TAP output; inside the hook it surfaces as a proper test failure) Co-Authored-By: Claude Sonnet 4.6 (1M context) --- .github/workflows/integration-tests.yml | 1 + integrationTests/app.test.ts | 9 ++++----- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/integration-tests.yml b/.github/workflows/integration-tests.yml index e4b2d7a..cb312d5 100644 --- a/.github/workflows/integration-tests.yml +++ b/.github/workflows/integration-tests.yml @@ -41,6 +41,7 @@ jobs: name: Integration Tests (Node ${{ matrix.node-version }}) needs: [generate-node-version-matrix] runs-on: ubuntu-latest + timeout-minutes: 15 strategy: fail-fast: false diff --git a/integrationTests/app.test.ts b/integrationTests/app.test.ts index a3ca20c..e633a87 100644 --- a/integrationTests/app.test.ts +++ b/integrationTests/app.test.ts @@ -10,11 +10,6 @@ import { setupHarperWithFixture, teardownHarper, type ContextWithHarper } from ' import { fileURLToPath } from 'node:url'; import { dirname, resolve } from 'node:path'; -// harper's exports map only exposes ".", so resolving 'harper/dist/bin/harper.js' -// (the harness's default auto-resolution) throws ERR_PACKAGE_PATH_NOT_EXPORTED. Resolve the CLI -// from harper's exported main entry and pass it explicitly via the harness escape hatch. -const harperBinPath = resolve(dirname(fileURLToPath(import.meta.resolve('harper'))), 'bin/harper.js'); - const FIXTURE_PATH = fileURLToPath(new URL('../', import.meta.url)); function authFetch( @@ -32,6 +27,10 @@ function authFetch( void suite('agent-example-harper loads', (ctx: ContextWithHarper) => { before(async () => { + // harper's exports map only exposes ".", so resolving 'harper/dist/bin/harper.js' + // (the harness's default auto-resolution) throws ERR_PACKAGE_PATH_NOT_EXPORTED. Resolve the CLI + // from harper's exported main entry and pass it explicitly via the harness escape hatch. + const harperBinPath = resolve(dirname(fileURLToPath(import.meta.resolve('harper'))), 'bin/harper.js'); await setupHarperWithFixture(ctx, FIXTURE_PATH, { startupTimeoutMs: 60000, harperBinPath }); });