-
Notifications
You must be signed in to change notification settings - Fork 3
added harper to the dependency graph #4
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
7 commits
Select commit
Hold shift + click to select a range
472e684
added harper to the dependency graph
BboyAkers 9411a42
Add integration testing, CI, and finalize Harper v5 upgrade
BboyAkers 153fd1d
Fix CI shell injection in integration-tests workflow
BboyAkers de2b151
Fix blocking review finding: restore missing npm scripts
BboyAkers 53c1b60
Upgrade harper to 5.2.1
BboyAkers aeb4a89
Regenerate lockfile with npm 12 for Linux CI
BboyAkers 7bd8eff
Fix the dev script, align eslint-config-next, make the tsconfig typec…
BboyAkers File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,82 @@ | ||
| 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 | ||
| env: | ||
| NODE_VER: ${{ github.event.inputs.node-version }} | ||
| run: | | ||
| if [ "$NODE_VER" == "all" ] || [ -z "$NODE_VER" ]; then | ||
| echo "node-versions=[22, 24, 26]" >> $GITHUB_OUTPUT | ||
| else | ||
| echo "node-versions=[$NODE_VER]" >> $GITHUB_OUTPUT | ||
| fi | ||
|
|
||
| integration-tests: | ||
| name: Integration Tests (Node ${{ matrix.node-version }}) | ||
| needs: [generate-node-version-matrix] | ||
| # ubuntu-latest is load-bearing, not just a default. The @harperfast/integration-testing | ||
| # harness binds each suite to its own loopback address (127.0.0.2, 127.0.0.3, ...), and | ||
| # Linux provides the whole 127.0.0.0/8 range out of the box. macOS and Windows runners do | ||
| # not: there the tests fail to bind (EADDRNOTAVAIL) until the aliases are created with | ||
| # `npx harper-integration-test-setup-loopback`, which needs sudo. Add that step first if | ||
| # this job is ever extended to another platform. | ||
| 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: | ||
|
BboyAkers marked this conversation as resolved.
|
||
| 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 | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,117 @@ | ||
| // Integration tests for the Harper Next.js example. | ||
| // | ||
| // Architecture note: this component is served by the `@harperfast/nextjs` plugin, | ||
| // which owns the public HTTP port and routes every request to the Next.js app. | ||
| // The `Dog` table (schema.graphql: `type Dog @table @export`) is therefore NOT | ||
| // reachable as a REST resource over HTTP here — the Next.js server actions | ||
| // (app/actions.js) read and write it through Harper's in-process Resource API | ||
| // (`tables.Dog`). To exercise the same data layer from a test, we drive the | ||
| // Dog table through the Operations API, and separately assert that the Next.js | ||
| // pages render. | ||
| // | ||
| // These tests boot the full component as a fixture under v5 and verify: | ||
| // 1. the component boots and the `Dog` table is defined, | ||
| // 2. create / read-by-id / delete on the `Dog` table (Operations API), | ||
| // 3. the Next.js HTML routes ("/" and "/dogs") render. | ||
|
|
||
| import { suite, test, before, after } from 'node:test'; | ||
| import { strictEqual, ok, deepStrictEqual } from 'node:assert/strict'; | ||
| import { setupHarperWithFixture, teardownHarper, type ContextWithHarper } from '@harperfast/integration-testing'; | ||
| import { fileURLToPath } from 'node:url'; | ||
| import { dirname, resolve } from 'node:path'; | ||
| import { createRequire } from 'node:module'; | ||
|
|
||
| const __dirname = dirname(fileURLToPath(import.meta.url)); | ||
| const FIXTURE_PATH = resolve(__dirname, '..'); | ||
|
|
||
| // The `harper` package's `exports` map only exposes ".", so the harness's | ||
| // auto-resolution of 'harper/dist/bin/harper.js' fails with ERR_PACKAGE_PATH_NOT_EXPORTED. | ||
| // Resolve the CLI from the (exported) main entry and pass it explicitly. | ||
| const require = createRequire(import.meta.url); | ||
| const harperBinPath = resolve(dirname(require.resolve('harper')), 'bin/harper.js'); | ||
|
|
||
| function authHeader(ctx: ContextWithHarper): string { | ||
| const creds = Buffer.from(`${ctx.harper.admin.username}:${ctx.harper.admin.password}`).toString('base64'); | ||
| return `Basic ${creds}`; | ||
| } | ||
|
|
||
| // Drive the Harper data layer (the same layer the Next.js server actions use) | ||
| // through the Operations API, since the HTTP port is owned by the Next.js plugin. | ||
| async function op<T = unknown>(ctx: ContextWithHarper, operation: Record<string, unknown>): Promise<T> { | ||
| const res = await fetch(ctx.harper.operationsAPIURL, { | ||
| method: 'POST', | ||
| headers: { 'Content-Type': 'application/json', 'Authorization': authHeader(ctx) }, | ||
| body: JSON.stringify(operation), | ||
| }); | ||
| ok(res.ok, `operation ${String(operation.operation)} failed with HTTP ${res.status}`); | ||
| return (await res.json()) as T; | ||
| } | ||
|
|
||
| function pageFetch(ctx: ContextWithHarper, path: string) { | ||
| return fetch(`${ctx.harper.httpURL}${path}`, { headers: { Authorization: authHeader(ctx) } }); | ||
| } | ||
|
|
||
| void suite('Harper Next.js example', (ctx: ContextWithHarper) => { | ||
| before(async () => { | ||
| await setupHarperWithFixture(ctx, FIXTURE_PATH, { harperBinPath }); | ||
| }); | ||
|
|
||
| after(async () => { | ||
| await teardownHarper(ctx); | ||
| }); | ||
|
|
||
| void test('component boots and the Dog table is defined', async () => { | ||
| const desc = await op<{ data: { Dog?: { name: string; primary_key: string } } }>(ctx, { | ||
| operation: 'describe_all', | ||
| }); | ||
| ok(desc.data?.Dog, 'expected a Dog table in the "data" database'); | ||
| strictEqual(desc.data.Dog.name, 'Dog'); | ||
| strictEqual(desc.data.Dog.primary_key, 'id'); | ||
| }); | ||
|
|
||
| void test('insert, read-by-id, and delete a Dog (Resource API via Operations API)', async () => { | ||
| await op(ctx, { | ||
| operation: 'insert', | ||
| database: 'data', | ||
| table: 'Dog', | ||
| records: [{ id: 'dog-1', name: 'Buddy', breed: 'Dalmatian', age: 3, color: 'Black and White' }], | ||
| }); | ||
|
|
||
| const got = await op<{ id: string; name: string; breed: string; age: number }[]>(ctx, { | ||
| operation: 'search_by_id', | ||
| database: 'data', | ||
| table: 'Dog', | ||
| ids: ['dog-1'], | ||
| get_attributes: ['id', 'name', 'breed', 'age', 'color'], | ||
| }); | ||
| strictEqual(got.length, 1); | ||
| strictEqual(got[0].name, 'Buddy'); | ||
| strictEqual(got[0].breed, 'Dalmatian'); | ||
| strictEqual(got[0].age, 3); | ||
|
|
||
| await op(ctx, { operation: 'delete', database: 'data', table: 'Dog', ids: ['dog-1'] }); | ||
|
|
||
| const afterDelete = await op<unknown[]>(ctx, { | ||
| operation: 'search_by_id', | ||
| database: 'data', | ||
| table: 'Dog', | ||
| ids: ['dog-1'], | ||
| get_attributes: ['id'], | ||
| }); | ||
| deepStrictEqual(afterDelete, []); | ||
| }); | ||
|
|
||
| void test('Next.js home page renders', async () => { | ||
| const res = await pageFetch(ctx, '/'); | ||
| strictEqual(res.status, 200); | ||
| ok((res.headers.get('content-type') ?? '').includes('text/html'), 'expected an HTML response'); | ||
| const html = await res.text(); | ||
| ok(html.includes('Doggy Management System'), 'expected the home page heading'); | ||
| }); | ||
|
|
||
| void test('Next.js /dogs page renders', async () => { | ||
| const res = await pageFetch(ctx, '/dogs'); | ||
| strictEqual(res.status, 200); | ||
| ok((res.headers.get('content-type') ?? '').includes('text/html'), 'expected an HTML response'); | ||
| }); | ||
| }); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,15 @@ | ||
| { | ||
| "compilerOptions": { | ||
| "target": "ES2022", | ||
| "module": "NodeNext", | ||
|
BboyAkers marked this conversation as resolved.
|
||
| "moduleResolution": "NodeNext", | ||
| "lib": ["ES2022"], | ||
| "types": ["node"], | ||
| "strict": true, | ||
| "esModuleInterop": true, | ||
| "skipLibCheck": true, | ||
| "noEmit": true, | ||
| "verbatimModuleSyntax": true | ||
| }, | ||
| "include": ["**/*.ts"] | ||
| } | ||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.