Skip to content

Add integration-tests CI and fix harness CLI resolution#5

Open
BboyAkers wants to merge 3 commits into
mainfrom
verify-harper-v5
Open

Add integration-tests CI and fix harness CLI resolution#5
BboyAkers wants to merge 3 commits into
mainfrom
verify-harper-v5

Conversation

@BboyAkers

Copy link
Copy Markdown
Member

Summary

Verification of the v5 reference repo (Path B). The repo was already correctly on harper@^5.0.11 with @harperfast/integration-testing, an integrationTests/ suite, and a test:integration script. No v5 migration patterns remain (wasLoadedFromSource, blob.save, harperdb imports/globals all absent). However two gaps would prevent the integration tests from ever running, so this PR fills them:

  1. Missing CI workflow — there was no .github/workflows/integration-tests.yml (and no CI runs in the repo at all). Added the standard workflow: Node matrix 22, 24, 26, actions pinned to commit hashes (checkout v6.0.3, setup-node v6.4.0, upload-artifact v7.0.1).

  2. Harness CLI resolution would breakintegrationTests/app.test.ts did not pass harperBinPath. harper's exports map only exposes ".", so the harness's default auto-resolution require.resolve('harper/dist/bin/harper.js') throws ERR_PACKAGE_PATH_NOT_EXPORTED; the cwd-ancestor fallback also finds no dist/bin/harper.js, so getHarperScript() throws "Harper CLI script not found" before Harper ever starts. Verified empirically against harper@5.0.11 + integration-testing@0.3.1. Fixed by resolving the CLI from harper's exported main entry and passing it via the documented harperBinPath escape hatch.

Verification

  • Confirmed no v5 migration footprint remains in source.
  • Ran npm run test:integration locally. With the fix, the harness now gets past CLI resolution (no ERR_PACKAGE_PATH_NOT_EXPORTED, no "CLI script not found") and proceeds to Harper startup, where it fails only at the macOS loopback bind (EADDRNOTAVAIL on 127.0.0.2+). That is a known environmental limitation of this dev machine (loopback aliases not configured, requires interactive sudo) — not a code bug. On ubuntu-latest CI the full 127.0.0.0/8 range is available, so the gate runs without aliasing.
  • Tests are therefore validated-by-CI; the workflow on this PR is the real gate.

Notes

  • PR Updated the Claude Model and Google Vertex Commented Out #4 (env-fix, Claude-model tweak) is unrelated and untouched.
  • Upstream issue worth flagging: harper should export its bin path (or the harness should resolve via the package root) so the harperBinPath workaround isn't needed in every consumer repo.

🤖 Generated with Claude Code

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) <noreply@anthropic.com>

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request updates the integration tests to explicitly resolve the harper binary path and pass it to setupHarperWithFixture, which prevents an ERR_PACKAGE_PATH_NOT_EXPORTED error. Feedback suggests using the native import.meta.resolve instead of creating a CommonJS require helper via createRequire to simplify the ESM resolution.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment thread integrationTests/app.test.ts Outdated
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
integration-tests:
name: Integration Tests (Node ${{ matrix.node-version }})
needs: [generate-node-version-matrix]
runs-on: ubuntu-latest

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing job-level timeout — the integration-tests job has no timeout-minutes. GitHub's default is 6 hours. The startupTimeoutMs: 60000 inside the test guards Harper's startup, but if a test or Harper teardown hangs afterwards the job will burn the full 6-hour budget before failing.

Suggest adding timeout-minutes: 15 (or whatever fits your expected run time) at the job level:

  integration-tests:
    name: Integration Tests (Node ${{ matrix.node-version }})
    needs: [generate-node-version-matrix]
    runs-on: ubuntu-latest
    timeout-minutes: 15

steps:
- name: Checkout code
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No npm cache configured — the setup-node step is missing cache: 'npm'. With three matrix legs (Node 22, 24, 26) each doing a full npm ci, every run re-downloads the full dependency tree. Adding caching typically cuts install time from ~60s to ~5s per leg.

      - name: Set up Node.js
        uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
        with:
          node-version: ${{ matrix.node-version }}
          cache: 'npm'

Comment thread integrationTests/app.test.ts Outdated
// 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');

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Top-level module evaluation: unhandled throw produces no TAP outputimport.meta.resolve('harper') executes at module-load time, before any test runs. If it throws (e.g., harper not installed, or a future harper version that changes its exports map), the entire test file fails to load. The node:test runner reports this as an uncaught exception—not a test failure—so the harper-integration-test-run output is empty/silent, the log artifact has nothing to upload, and the error message is buried in runner stderr rather than the TAP stream.

Moving this into before() (or wrapping in try/catch with an explicit assert.fail()) would make resolution failures surface as a named test failure with a helpful message:

void suite('agent-example-harper loads', (ctx: ContextWithHarper) => {
    let harperBinPath: string;

    before(async () => {
        // Resolve inside before() so a failure surfaces as a named test error, not a silent module crash
        const harperMain = import.meta.resolve('harper');
        harperBinPath = resolve(dirname(fileURLToPath(harperMain)), 'bin/harper.js');
        await setupHarperWithFixture(ctx, FIXTURE_PATH, { startupTimeoutMs: 60000, harperBinPath });
    });

Comment thread integrationTests/app.test.ts Outdated
// 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');

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Path resolution encodes harper's internal layout — the fix derives the CLI path as dirname(fileURLToPath(main)) + 'bin/harper.js'. This works today because harper's main entry (dist/index.js) and its bin (dist/bin/harper.js) both live under dist/. If harper ever separates them (e.g., moves main to dist/lib/index.js), this silently resolves to the wrong path and the harness either crashes or spawns the wrong binary.

A more stable alternative is the HARPER_INTEGRATION_TEST_INSTALL_SCRIPT escape hatch the harness already provides (step 2 in getHarperScript). Setting it in the CI workflow avoids touching the test file at all:

      - name: Run integration tests
        run: npm run test:integration
        env:
          HARPER_INTEGRATION_TEST_INSTALL_SCRIPT: ./node_modules/harper/dist/bin/harper.js
          HARPER_INTEGRATION_TEST_LOG_DIR: /tmp/harper-test-logs
          FORCE_COLOR: '1'

That keeps the escape hatch in CI config rather than test code, and the explicit path makes the dependency on harper's layout visible and easy to update.

- 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) <noreply@anthropic.com>
@BboyAkers

Copy link
Copy Markdown
Member Author

Review follow-up (autonomous agent): Fixed both blocking findings: added timeout-minutes: 15 to the integration-tests job and moved harperBinPath computation inside the before() hook so a resolution failure surfaces as a proper test failure rather than a silent module crash.

@@ -0,0 +1,75 @@
name: Integration Tests

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Low: no explicit permissions block

The workflow doesn't declare permissions:, so the GITHUB_TOKEN inherits whatever the repo/org default grants (which can be read-write). This workflow only checks out code and runs tests — it never comments on PRs or pushes — so it needs nothing beyond read access.

Suggested fix: add near the top of the file:

permissions:
  contents: read


Generated by Barber AI

node-versions: ${{ steps.set-node-versions.outputs.node-versions }}

steps:
- name: Checkout code

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: unneeded checkout in generate-node-version-matrix

This job only derives the node-versions output from the workflow_dispatch input via a shell one-liner — it never reads repository content, so the checkout step does nothing useful.

Suggested fix: drop this step (and the actions/checkout dependency) from the job.


Generated by Barber AI

- name: Set up Node.js
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
with:
node-version: ${{ matrix.node-version }}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Low: no npm cache configured

All three matrix legs (Node 22/24/26) run a full npm ci with no dependency cache, so each leg re-downloads the entire tree from scratch. actions/setup-node supports built-in caching, and the repo already has a package-lock.json for it to key off.

Suggested fix:

Suggested change
node-version: ${{ matrix.node-version }}
node-version: ${{ matrix.node-version }}
cache: 'npm'


Generated by Barber AI

// 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');

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Low: harperBinPath derivation assumes harper's internal dist/ layout

resolve(dirname(fileURLToPath(import.meta.resolve('harper'))), 'bin/harper.js') assumes harper's exported main entry (dist/index.js) and its CLI (dist/bin/harper.js) stay siblings under the same dist/ directory — verified true for harper@5.0.11, but nothing pins that relationship. If a future harper release restructures dist/, this resolves to a path that doesn't exist; the harness's existsSync check in getHarperScript() will catch it and throw a clear error rather than silently running the wrong binary, so the failure mode is safe, but it's still a coupling to an unversioned internal detail.

Suggested fix: no code change needed now, but consider a one-line comment linking to the upstream tracking issue (mentioned in the PR description — harper should export its bin path) so future readers know this is a deliberate, tracked workaround rather than an oversight.


Generated by Barber AI

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants