test(stack): EQL v3 integration harness + shared test kit, and two Supabase adapter fixes (PR1 of 4) #1621
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
| name: Test JS | |
| on: | |
| push: | |
| branches: | |
| - 'main' | |
| pull_request: | |
| branches: | |
| - "**" | |
| jobs: | |
| run-tests: | |
| name: Run Tests (Node ${{ matrix.node-version }}) | |
| runs-on: blacksmith-4vcpu-ubuntu-2404 | |
| strategy: | |
| matrix: | |
| node-version: [22, 24] | |
| # Postgres + EQL for the integration tests. Official EQL image — | |
| # PostgreSQL 17 with EQL pre-installed via /docker-entrypoint-initdb.d. | |
| # Pinned to eql-2.3.1 to match the EQL payload format the code emits | |
| # (protect-ffi 0.23.x); bump in lockstep with the protect-ffi upgrade. | |
| services: | |
| postgres: | |
| image: ghcr.io/cipherstash/postgres-eql:17-2.3.1 | |
| env: | |
| POSTGRES_USER: cipherstash | |
| POSTGRES_PASSWORD: password | |
| POSTGRES_DB: cipherstash | |
| ports: | |
| - 5432:5432 | |
| options: >- | |
| --health-cmd "pg_isready -U cipherstash -d cipherstash" | |
| --health-interval 2s | |
| --health-timeout 5s | |
| --health-retries 20 | |
| steps: | |
| - name: Checkout Repo | |
| uses: actions/checkout@v6 | |
| - uses: pnpm/action-setup@v6.0.8 | |
| name: Install pnpm | |
| with: | |
| run_install: false | |
| - name: Install Node.js | |
| uses: actions/setup-node@v6 | |
| with: | |
| node-version: ${{ matrix.node-version }} | |
| cache: 'pnpm' | |
| # node-pty's install hook falls back to `node-gyp rebuild` when no | |
| # linux-x64 prebuild matches. pnpm/action-setup v6 no longer ships | |
| # node-gyp on PATH, so install it explicitly. | |
| - name: Install node-gyp | |
| run: npm install -g node-gyp | |
| - name: Install dependencies | |
| run: pnpm install --frozen-lockfile | |
| # Fail loudly if any live-test credential is missing, so the v3 live | |
| # matrix (and every other `describeLive` suite) can't silently skip. | |
| - name: Require CipherStash secrets | |
| uses: ./.github/actions/require-cs-secrets | |
| with: | |
| workspace-crn: ${{ secrets.CS_WORKSPACE_CRN }} | |
| client-id: ${{ secrets.CS_CLIENT_ID }} | |
| client-key: ${{ secrets.CS_CLIENT_KEY }} | |
| client-access-key: ${{ secrets.CS_CLIENT_ACCESS_KEY }} | |
| - name: Type tests (stack) | |
| run: pnpm --filter @cipherstash/stack run test:types | |
| # The v3 domain catalog lives in the test kit, and its | |
| # `satisfies Record<EqlV3TypeName, DomainSpec>` is what forces a new SDK | |
| # domain to be covered. That check only fires under `tsc`, so without this | |
| # step a domain added to stack would slip through untested. | |
| - name: Type tests (test-kit — enforces v3 domain coverage) | |
| run: pnpm --filter @cipherstash/test-kit run test:types | |
| - name: Lint — no hardcoded package-manager runners | |
| run: pnpm run lint:runners | |
| - name: Test — lint script self-tests | |
| run: pnpm run test:scripts | |
| # The EQL v3 bundle in packages/cli/src/sql is vendored from the pinned | |
| # @cipherstash/eql package (sha256-verified by gen:eql-v3-sql). | |
| # Regenerate and fail on any diff so the pin and the shipped bundle | |
| # cannot drift silently. | |
| - name: Check — vendored EQL v3 SQL bundle is in sync | |
| run: | | |
| pnpm --filter stash run gen:eql-v3-sql | |
| git diff --exit-code -- packages/cli/src/sql/cipherstash-encrypt-v3.sql | |
| - name: Create .env file in ./packages/protect/ | |
| run: | | |
| touch ./packages/protect/.env | |
| echo "CS_WORKSPACE_CRN=${{ secrets.CS_WORKSPACE_CRN }}" >> ./packages/protect/.env | |
| echo "CS_CLIENT_ID=${{ secrets.CS_CLIENT_ID }}" >> ./packages/protect/.env | |
| echo "CS_CLIENT_KEY=${{ secrets.CS_CLIENT_KEY }}" >> ./packages/protect/.env | |
| echo "CS_CLIENT_ACCESS_KEY=${{ secrets.CS_CLIENT_ACCESS_KEY }}" >> ./packages/protect/.env | |
| echo "DATABASE_URL=postgres://cipherstash:password@localhost:5432/cipherstash" >> ./packages/protect/.env | |
| - name: Create .env file in ./packages/stack/ | |
| run: | | |
| touch ./packages/stack/.env | |
| echo "CS_WORKSPACE_CRN=${{ secrets.CS_WORKSPACE_CRN }}" >> ./packages/stack/.env | |
| echo "CS_CLIENT_ID=${{ secrets.CS_CLIENT_ID }}" >> ./packages/stack/.env | |
| echo "CS_CLIENT_KEY=${{ secrets.CS_CLIENT_KEY }}" >> ./packages/stack/.env | |
| echo "CS_CLIENT_ACCESS_KEY=${{ secrets.CS_CLIENT_ACCESS_KEY }}" >> ./packages/stack/.env | |
| echo "DATABASE_URL=postgres://cipherstash:password@localhost:5432/cipherstash" >> ./packages/stack/.env | |
| - name: Create .env file in ./packages/protect-dynamodb/ | |
| run: | | |
| touch ./packages/protect-dynamodb/.env | |
| echo "CS_WORKSPACE_CRN=${{ secrets.CS_WORKSPACE_CRN }}" >> ./packages/protect-dynamodb/.env | |
| echo "CS_CLIENT_ID=${{ secrets.CS_CLIENT_ID }}" >> ./packages/protect-dynamodb/.env | |
| echo "CS_CLIENT_KEY=${{ secrets.CS_CLIENT_KEY }}" >> ./packages/protect-dynamodb/.env | |
| echo "CS_CLIENT_ACCESS_KEY=${{ secrets.CS_CLIENT_ACCESS_KEY }}" >> ./packages/protect-dynamodb/.env | |
| - name: Create .env file in ./packages/drizzle/ | |
| run: | | |
| touch ./packages/drizzle/.env | |
| echo "CS_WORKSPACE_CRN=${{ secrets.CS_WORKSPACE_CRN }}" >> ./packages/drizzle/.env | |
| echo "CS_CLIENT_ID=${{ secrets.CS_CLIENT_ID }}" >> ./packages/drizzle/.env | |
| echo "CS_CLIENT_KEY=${{ secrets.CS_CLIENT_KEY }}" >> ./packages/drizzle/.env | |
| echo "CS_CLIENT_ACCESS_KEY=${{ secrets.CS_CLIENT_ACCESS_KEY }}" >> ./packages/drizzle/.env | |
| echo "DATABASE_URL=postgres://cipherstash:password@localhost:5432/cipherstash" >> ./packages/drizzle/.env | |
| # Run TurboRepo tests | |
| - name: Run tests | |
| run: pnpm run test | |
| # CLI E2E tests drive the built `dist/bin/stash.js` through a real | |
| # pseudo-terminal via node-pty. Run via turbo so the `^build` + `build` | |
| # deps declared on the `test:e2e` task are honored. | |
| - name: Run CLI E2E tests | |
| run: pnpm exec turbo run test:e2e --filter stash | |
| e2e-tests: | |
| name: Run E2E Tests | |
| runs-on: blacksmith-4vcpu-ubuntu-2404 | |
| # Auth-dependent suites in `e2e/` skip themselves unless these env vars | |
| # are set. We expose them at the job level so the wizard subprocess | |
| # picks them up via `process.env`. | |
| env: | |
| CS_WORKSPACE_CRN: ${{ secrets.CS_WORKSPACE_CRN }} | |
| CS_CLIENT_ID: ${{ secrets.CS_CLIENT_ID }} | |
| CS_CLIENT_KEY: ${{ secrets.CS_CLIENT_KEY }} | |
| CS_CLIENT_ACCESS_KEY: ${{ secrets.CS_CLIENT_ACCESS_KEY }} | |
| CS_ZEROKMS_HOST: https://ap-southeast-2.aws.zerokms.cipherstashmanaged.net | |
| CS_CTS_HOST: https://ap-southeast-2.aws.cts.cipherstashmanaged.net | |
| steps: | |
| - name: Checkout Repo | |
| uses: actions/checkout@v6 | |
| - uses: pnpm/action-setup@v6.0.8 | |
| name: Install pnpm | |
| with: | |
| run_install: false | |
| - name: Install Node.js | |
| uses: actions/setup-node@v6 | |
| with: | |
| node-version: 22 | |
| cache: 'pnpm' | |
| # node-pty's install hook falls back to `node-gyp rebuild` when no | |
| # linux-x64 prebuild matches. pnpm/action-setup v6 no longer ships | |
| # node-gyp on PATH, so install it explicitly. | |
| - name: Install node-gyp | |
| run: npm install -g node-gyp | |
| - name: Install dependencies | |
| run: pnpm install --frozen-lockfile | |
| # Auth-dependent `e2e/` suites skip themselves without these vars — a | |
| # silent skip would hide regressions behind a green job. Fail loudly. | |
| - name: Require CipherStash secrets | |
| uses: ./.github/actions/require-cs-secrets | |
| with: | |
| workspace-crn: ${{ secrets.CS_WORKSPACE_CRN }} | |
| client-id: ${{ secrets.CS_CLIENT_ID }} | |
| client-key: ${{ secrets.CS_CLIENT_KEY }} | |
| client-access-key: ${{ secrets.CS_CLIENT_ACCESS_KEY }} | |
| # Run the standalone `e2e/` workspace via turbo so the `^build` | |
| # dep on the `test:e2e` task builds cli + wizard first. CLI's own | |
| # E2E (`packages/cli/tests/e2e/**`) is covered by the `run-tests` | |
| # job above; we filter to the new workspace here to avoid duplication. | |
| - name: Run E2E tests | |
| run: pnpm exec turbo run test:e2e --filter @cipherstash/e2e | |
| # Verifies @cipherstash/stack/wasm-inline works under Deno — i.e. the | |
| # WASM build of protect-ffi 0.26+ and auth 0.40+ can round-trip an | |
| # encryption against ZeroKMS / CTS in a runtime with no native | |
| # bindings available. The deno.json deliberately omits --allow-ffi so | |
| # a silent fallback to the NAPI module is impossible. | |
| wasm-e2e-tests: | |
| name: Run WASM E2E Tests (Deno) | |
| runs-on: blacksmith-4vcpu-ubuntu-2404 | |
| permissions: | |
| contents: read | |
| # CS_WORKSPACE_CRN is the single source of truth for workspace | |
| # identity and region — the stack /wasm-inline config requires it and | |
| # derives the AccessKeyStrategy region from it. | |
| env: | |
| CS_WORKSPACE_CRN: ${{ secrets.CS_WORKSPACE_CRN }} | |
| CS_CLIENT_ID: ${{ secrets.CS_CLIENT_ID }} | |
| CS_CLIENT_KEY: ${{ secrets.CS_CLIENT_KEY }} | |
| CS_CLIENT_ACCESS_KEY: ${{ secrets.CS_CLIENT_ACCESS_KEY }} | |
| steps: | |
| - name: Checkout Repo | |
| uses: actions/checkout@v6 | |
| with: | |
| persist-credentials: false | |
| - uses: pnpm/action-setup@v6.0.8 | |
| name: Install pnpm | |
| with: | |
| run_install: false | |
| - name: Install Node.js | |
| uses: actions/setup-node@v6 | |
| with: | |
| node-version: 22 | |
| cache: 'pnpm' | |
| # node-pty (a dev-dep of @cipherstash/cli used by its E2E PTY | |
| # tests) falls back to `node-gyp rebuild` when no prebuild matches | |
| # the runner, and pnpm/action-setup v6 no longer ships node-gyp on | |
| # PATH. The WASM smoke test itself uses no native modules — this | |
| # install only exists so the workspace-wide `pnpm install` step | |
| # below doesn't fail. | |
| - name: Install node-gyp | |
| run: npm install -g node-gyp | |
| - name: Install Deno | |
| uses: denoland/setup-deno@v2 | |
| with: | |
| deno-version: v2.x | |
| - name: Install dependencies | |
| run: pnpm install --frozen-lockfile | |
| # The Deno smoke test imports the locally-built dist/wasm-inline.js | |
| # via a file URL in e2e/wasm/deno.json — it needs a fresh build. | |
| - name: Build stack | |
| run: pnpm exec turbo run build --filter @cipherstash/stack | |
| # roundtrip.test.ts skips itself (Deno.test.ignore) when any of the four | |
| # CS_* env vars is missing — fine locally, but in CI a silent skip would | |
| # let a rotated / cleared secret hide a real WASM regression behind a | |
| # green job. Fail loudly instead. | |
| - name: Require CipherStash secrets | |
| uses: ./.github/actions/require-cs-secrets | |
| with: | |
| workspace-crn: ${{ secrets.CS_WORKSPACE_CRN }} | |
| client-id: ${{ secrets.CS_CLIENT_ID }} | |
| client-key: ${{ secrets.CS_CLIENT_KEY }} | |
| client-access-key: ${{ secrets.CS_CLIENT_ACCESS_KEY }} | |
| - name: Run Deno WASM smoke test | |
| working-directory: e2e/wasm | |
| run: deno task test | |
| run-tests-bun: | |
| name: Run Tests (Bun) | |
| runs-on: blacksmith-4vcpu-ubuntu-2404 | |
| continue-on-error: true | |
| services: | |
| postgres: | |
| image: ghcr.io/cipherstash/postgres-eql:17-2.3.1 | |
| env: | |
| POSTGRES_USER: cipherstash | |
| POSTGRES_PASSWORD: password | |
| POSTGRES_DB: cipherstash | |
| ports: | |
| - 5432:5432 | |
| options: >- | |
| --health-cmd "pg_isready -U cipherstash -d cipherstash" | |
| --health-interval 2s | |
| --health-timeout 5s | |
| --health-retries 20 | |
| steps: | |
| - name: Checkout Repo | |
| uses: actions/checkout@v6 | |
| - uses: oven-sh/setup-bun@v2 | |
| - uses: pnpm/action-setup@v6.0.8 | |
| name: Install pnpm | |
| with: | |
| run_install: false | |
| - name: Install Node.js | |
| uses: actions/setup-node@v6 | |
| with: | |
| node-version: 22 | |
| cache: 'pnpm' | |
| - name: Install node-gyp | |
| run: npm install -g node-gyp | |
| - name: Install dependencies | |
| run: pnpm install --frozen-lockfile | |
| - name: Create .env file in ./packages/protect/ | |
| run: | | |
| touch ./packages/protect/.env | |
| echo "CS_WORKSPACE_CRN=${{ secrets.CS_WORKSPACE_CRN }}" >> ./packages/protect/.env | |
| echo "CS_CLIENT_ID=${{ secrets.CS_CLIENT_ID }}" >> ./packages/protect/.env | |
| echo "CS_CLIENT_KEY=${{ secrets.CS_CLIENT_KEY }}" >> ./packages/protect/.env | |
| echo "CS_CLIENT_ACCESS_KEY=${{ secrets.CS_CLIENT_ACCESS_KEY }}" >> ./packages/protect/.env | |
| echo "DATABASE_URL=postgres://cipherstash:password@localhost:5432/cipherstash" >> ./packages/protect/.env | |
| - name: Create .env file in ./packages/stack/ | |
| run: | | |
| touch ./packages/stack/.env | |
| echo "CS_WORKSPACE_CRN=${{ secrets.CS_WORKSPACE_CRN }}" >> ./packages/stack/.env | |
| echo "CS_CLIENT_ID=${{ secrets.CS_CLIENT_ID }}" >> ./packages/stack/.env | |
| echo "CS_CLIENT_KEY=${{ secrets.CS_CLIENT_KEY }}" >> ./packages/stack/.env | |
| echo "CS_CLIENT_ACCESS_KEY=${{ secrets.CS_CLIENT_ACCESS_KEY }}" >> ./packages/stack/.env | |
| echo "DATABASE_URL=postgres://cipherstash:password@localhost:5432/cipherstash" >> ./packages/stack/.env | |
| - name: Create .env file in ./packages/protect-dynamodb/ | |
| run: | | |
| touch ./packages/protect-dynamodb/.env | |
| echo "CS_WORKSPACE_CRN=${{ secrets.CS_WORKSPACE_CRN }}" >> ./packages/protect-dynamodb/.env | |
| echo "CS_CLIENT_ID=${{ secrets.CS_CLIENT_ID }}" >> ./packages/protect-dynamodb/.env | |
| echo "CS_CLIENT_KEY=${{ secrets.CS_CLIENT_KEY }}" >> ./packages/protect-dynamodb/.env | |
| echo "CS_CLIENT_ACCESS_KEY=${{ secrets.CS_CLIENT_ACCESS_KEY }}" >> ./packages/protect-dynamodb/.env | |
| - name: Create .env file in ./packages/drizzle/ | |
| run: | | |
| touch ./packages/drizzle/.env | |
| echo "CS_WORKSPACE_CRN=${{ secrets.CS_WORKSPACE_CRN }}" >> ./packages/drizzle/.env | |
| echo "CS_CLIENT_ID=${{ secrets.CS_CLIENT_ID }}" >> ./packages/drizzle/.env | |
| echo "CS_CLIENT_KEY=${{ secrets.CS_CLIENT_KEY }}" >> ./packages/drizzle/.env | |
| echo "CS_CLIENT_ACCESS_KEY=${{ secrets.CS_CLIENT_ACCESS_KEY }}" >> ./packages/drizzle/.env | |
| echo "DATABASE_URL=postgres://cipherstash:password@localhost:5432/cipherstash" >> ./packages/drizzle/.env | |
| # Build with Node (turbo/tsup need Node), then run tests with Bun | |
| - name: Build packages | |
| run: pnpm turbo build --filter './packages/*' | |
| - name: Run tests with Bun | |
| run: | | |
| for dir in packages/schema packages/protect packages/stack packages/protect-dynamodb packages/drizzle packages/stack-forge; do | |
| if [ -f "$dir/vitest.config.ts" ] || [ -f "$dir/package.json" ]; then | |
| echo "--- Testing $dir ---" | |
| (cd "$dir" && bunx --bun vitest run) || true | |
| fi | |
| done |