diff --git a/.github/workflows/integration-tests.yml b/.github/workflows/integration-tests.yml index cf0d8fd..3ef9b9a 100644 --- a/.github/workflows/integration-tests.yml +++ b/.github/workflows/integration-tests.yml @@ -4,6 +4,18 @@ on: push: branches: [main] pull_request: + workflow_call: + inputs: + node-version: + description: 'Node.js version' + required: false + type: string + default: 'all' + harper_ref: + description: 'HarperFast/harper ref to test against (branch, SHA, or tag). Empty = use pinned version.' + required: false + type: string + default: '' workflow_dispatch: inputs: node-version: @@ -16,6 +28,11 @@ on: - '20' - '22' - '24' + harper_ref: + description: 'HarperFast/harper ref to test against (branch, SHA, or tag). Empty = use pinned version.' + required: false + type: string + default: '' jobs: generate-node-version-matrix: @@ -31,10 +48,11 @@ jobs: - name: Set Node versions id: set-node-versions run: | - if [ "${{ github.event.inputs.node-version }}" == "all" ] || [ -z "${{ github.event.inputs.node-version }}" ]; then + INPUT="${{ inputs.node-version }}" + if [ "$INPUT" == "all" ] || [ -z "$INPUT" ]; then echo "node-versions=[20, 22, 24]" >> $GITHUB_OUTPUT else - echo "node-versions=[${{ github.event.inputs.node-version }}]" >> $GITHUB_OUTPUT + echo "node-versions=[$INPUT]" >> $GITHUB_OUTPUT fi integration-tests: @@ -59,6 +77,25 @@ jobs: - name: Install dependencies run: npm ci + - name: Checkout harper override + if: ${{ inputs.harper_ref != '' }} + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + repository: HarperFast/harper + ref: ${{ inputs.harper_ref }} + path: harper-override + + - name: Build and install harper override + if: ${{ inputs.harper_ref != '' }} + run: | + cd "$GITHUB_WORKSPACE/harper-override" + npm ci + npm run build + TARBALL=$(npm pack --json | jq -r '.[0].filename') + mv "$TARBALL" /tmp/harper-override.tgz + cd "$GITHUB_WORKSPACE" + npm install --save-dev /tmp/harper-override.tgz + - name: Build run: npm run build @@ -70,7 +107,7 @@ jobs: - name: Run integration tests run: npm run test:integration - timeout-minutes: 5 + timeout-minutes: 15 env: HARPER_INTEGRATION_TEST_LOG_DIR: /tmp/harper-test-logs FORCE_COLOR: '1' diff --git a/fixtures/next-16/.env.secrets b/fixtures/next-16/.env.secrets new file mode 100644 index 0000000..0df3245 --- /dev/null +++ b/fixtures/next-16/.env.secrets @@ -0,0 +1 @@ +MOCK_API_KEY=harper-secret-abc123 diff --git a/fixtures/next-16/app/page.js b/fixtures/next-16/app/page.js index ec82c6e..f74b92d 100644 --- a/fixtures/next-16/app/page.js +++ b/fixtures/next-16/app/page.js @@ -1,7 +1,13 @@ +// force-dynamic so the injected secret is read at request time, proving +// loadEnv ran before Next.js rather than being baked in at build. +export const dynamic = 'force-dynamic'; + export default async function Page() { + const apiKey = process.env.MOCK_API_KEY ?? 'not-set'; return (

Next.js v16

+

{apiKey}

); } diff --git a/fixtures/next-16/config.yaml b/fixtures/next-16/config.yaml index 99f08d1..2f59e0a 100644 --- a/fixtures/next-16/config.yaml +++ b/fixtures/next-16/config.yaml @@ -1,2 +1,13 @@ +rest: true + +# Startup secrets injection: loadEnv runs before @harperfast/nextjs, so +# MOCK_API_KEY is present in process.env before Next.js builds/starts. +loadEnv: + files: .env.secrets + +# A plain Harper REST resource that must coexist with Next.js on the same port. +jsResource: + files: greeting.js + '@harperfast/nextjs': package: '@harperfast/nextjs' diff --git a/fixtures/next-16/greeting.js b/fixtures/next-16/greeting.js new file mode 100644 index 0000000..dc9873b --- /dev/null +++ b/fixtures/next-16/greeting.js @@ -0,0 +1,7 @@ +import { Resource } from 'harper'; + +export class Greeting extends Resource { + static async get() { + return { message: 'hello from harper' }; + } +} diff --git a/integrationTests/next-16.pw.ts b/integrationTests/next-16.pw.ts index d209ec1..06e7ea3 100644 --- a/integrationTests/next-16.pw.ts +++ b/integrationTests/next-16.pw.ts @@ -16,3 +16,17 @@ test('status endpoint returns 200', async ({ request, harper }) => { const response = await request.get(`${harper.operationsAPIURL}/health`); expect(response.status()).toBe(200); }); + +// §5.3(c) — Harper REST API coexists with Next.js on the same port. +test('Harper REST API is reachable on the same port as Next.js', async ({ request, harper }) => { + const response = await request.get(`${harper.httpURL}/Greeting`); + expect(response.status()).toBe(200); + const body = await response.json(); + expect(body.message).toBe('hello from harper'); +}); + +// §5.3(a) — Startup secrets injected via loadEnv before Next.js boots. +test('startup secrets are injected before Next.js boots', async ({ page, harper }) => { + await page.goto(harper.httpURL); + await expect(page.getByTestId('api-key')).toHaveText('harper-secret-abc123'); +});