Skip to content

chore(protect-ffi): add integration suite to pnpm workspace - #948

Open
tobyhede wants to merge 1 commit into
mainfrom
chore/protect-ffi-absorb-integration-tests
Open

chore(protect-ffi): add integration suite to pnpm workspace#948
tobyhede wants to merge 1 commit into
mainfrom
chore/protect-ffi-absorb-integration-tests

Conversation

@tobyhede

@tobyhede tobyhede commented Aug 26, 2026

Copy link
Copy Markdown
Contributor

Summary

CipherStash Stack encrypts data in the database. Two pieces have to agree for that to work: the Rust that produces an encrypted value, and the SQL (called EQL — Encrypt Query Language) that stores and searches it. They ship as a matched pair, at one version, because a mismatch is not a compile error — it is a query that returns wrong results, or nothing, at runtime in a customer's database.

Both pieces now live in this repository, so they can be built from the same commit and cannot disagree. One directory was still opting out: packages/protect-ffi/integration-tests, the test suite that runs real encryption against a real Postgres. It sat outside the repo's package manager with a lockfile of its own, downloading a published copy of the EQL SQL from the npm registry while testing Rust built from this repo. That directory is also the only place the EQL SQL is exercised at all.

So the one suite whose job is to catch that mismatch was the one place it could still happen. This PR brings the directory into the workspace, and the mismatch is now unrepresentable.

It was not a free move, which is why it was always planned as its own PR. Joining the workspace also means taking the repository's shared dependency versions, and one of those — the authentication library — had made breaking API changes across the versions being skipped. Those broke the suite, and are fixed here.

Changes

Joining the workspace

  • packages/protect-ffi/integration-tests is listed in pnpm-workspace.yaml; its package-lock.json is deleted and the npm ci step is gone.
  • The EQL SQL package and the Rust bindings package now resolve to the copies in this repo rather than to downloads.
  • Shared versions (@cipherstash/auth, vitest, typescript) come from the repo's catalog.
  • The setup task builds the in-repo EQL package before reading its SQL — that was free when a prebuilt download was unpacked, and is a build step now.

Fixing what the version bump broke

  • @cipherstash/auth ^0.39.0 → 0.42.0 changed three functions to return a success/failure wrapper object instead of the value directly, and changed one function's arguments. Four call sites updated across three test files and a test fixture.

Making the same class of break visible next time

  • The suite gained a typecheck script, run on every pull request. Its type-check previously ran nowhere in CI, which is why a breaking API change could land silently.
  • That check includes the suite's two .cjs fixtures. One is a real call site of the changed API, and the default file pattern would have skipped it.

Keeping the ordinary test command usable

  • Joining the workspace puts this directory in reach of pnpm test, which every contributor runs. If one of its scripts were named after a task the build tool knows, pnpm test would start demanding Docker, a database and live credentials for everyone. Its runners are named vitest:live / vitest:live:coverage instead, and a test derives the forbidden names from turbo.json rather than listing them.

Guards and docs

  • The lint rule that forbids downloading EQL instead of building it in-repo had one written exemption, for this directory. It is deleted; the list is now empty, and the rule's own staleness check is what required that in the same change.
  • A new check asserts vitest and its coverage plugin stay on the same version — they refuse to run against each other otherwise.
  • AGENTS.md, the package README, and both absorption planning documents updated.

No changeset: no published package changes behaviour or public API here.

Verification

Run against Docker and live CipherStash credentials, with both EQL versions installed:

  • mise run test:integration:all20 files, 232 tests, 0 skipped. Includes the identity-bound encryption tests, both EQL v1-era and current SQL suites, and the WebAssembly round trip. Both shapes of the changed auth API are covered by a live test each.
  • pnpm test — 16/16 tasks. The live suite is correctly not among them.
  • pnpm test:scripts — 818 passing, stable across five runs.
  • End-to-end supply-chain suite — 48 passing.
  • All four lint scripts, and the release gate.
  • pnpm install --frozen-lockfile clean, with a single shared @cipherstash/auth instance across stack, cli, wizard and this suite — the arrangement whose absence caused the "Failed to load native binding" failures in 1.0.0-rc.2.

Honest gaps:

  • The credentialed CI job has now run on this PR and passed (2m 1s, 20 files / 20 passed). Every step succeeded — including the secrets pre-flight, so it was not a credential skip — and npm ci appears nowhere in its log. It has not run on the default branch yet; that only happens after merge.
  • pnpm test:scripts failed once (817/818) mid-work and passed on every run since. That run overlapped a concurrent build rewriting a file one of the tests reads, which I believe was the cause — but I did not capture the test name before it went green, so I cannot state that as fact.
  • pnpm run code:check cannot run repo-wide on my machine: stale agent worktrees under .claude/ carry their own Biome config and abort it. Biome was run scoped to the changed paths instead — clean, warnings only.

Related

Refs #885 — the EQL absorption this completes. That PR moved the SQL in-repo; this removes the last place a downloaded copy was still used.

Review notes

Start with packages/protect-ffi/integration-tests/package.json and pnpm-workspace.yaml — everything else follows from those two.

The auth API fixes are the part that could be wrong in a way tests would not catch, since they are test code themselves. tests/js-strategy.test.ts is worth a close look: the API accepts two return shapes and the suite deliberately covers one per test. Before this change that split was accidental — the old library version returned the plain shape and the test forwarded it. Preserving the split took an explicit unwrap, and without it both tests would have exercised the same shape while the other, which is what hand-written integrations return, went untested.

Deliberately kept: @vitest/coverage-v8 brings roughly fifteen transitive dev dependencies into the shared lockfile. It was already installed in the old isolated lockfile, where nothing monitored it — the absorption plan lists that as a problem this change resolves. Bringing it under the repo's dependency monitoring is the point, not a side effect.

Not done, and I would rather it were raised than assumed: the .cjs fixtures needed one type annotation each to pass the new type-check. That is a real constraint of checking plain JavaScript, not a workaround, and there is a comment saying so.

https://claude.ai/code/session_01FGhktoHnwEuibNQF1MYexw

`packages/protect-ffi/integration-tests` sat outside the pnpm workspace with
its own `package-lock.json`, installed by `npm ci`. That made it the last place
in the tree where `@cipherstash/eql` was pinned to a published version — and it
is the suite that owns the repo's only EQL v2 *and* v3 SQL coverage. So its
`postgres-v3.test.ts` queried SQL installed from the npm bundle while the
payloads under test were emitted by the in-tree `eql-bindings`: the two halves
of EQL could disagree, and would have disagreed in a database rather than in
CI. The directory is now a workspace member, `@cipherstash/eql` resolves
`workspace:^`, and the registry-pin guard's exemption list is empty.

Moving the pins was not neutral, which is why this was always meant to be its
own change. `@cipherstash/auth` `^0.39.0 -> 0.42.0` is a breaking API move:
`AccessKeyStrategy.create`, `OidcFederationStrategy.create` and `getToken` all
return `@byteslice/result` Results now, and `OidcFederationStrategy.create`
takes a whole CRN instead of `(region, workspaceId, getJwt)`. Four call sites
needed unwrapping, and nothing would have caught them — the suite's `tsc` rode
on its own `npm test` while CI invoked vitest directly, so it compiled nowhere.
It has a `typecheck` script run from `tests.yml` now, with `checkJs` on so the
two `.cjs` fixtures (one of them a real strategy call site) are covered too.

Workspace membership has a cost worth naming: root `pnpm test` is
`turbo test --filter './packages/**'`, which now reaches this package. The
suite is kept out by naming no live script after a turbo task — its runners are
`vitest:live` and `vitest:live:coverage`. `test` is the obvious trap and
`test:integration` the less obvious one, being a real turbo task that four
integration workflows invoke. `integrationSuiteCi.test.ts` derives the
forbidden set from `turbo.json` rather than listing it, and also asserts
`tasks.toml` still installs with pnpm, reading past comments so a sentence
about `npm ci` cannot stand in for the command.

No changeset: nothing in a published package's behaviour or surface changes.

Verified with a credentialed local run against Docker and live ZeroKMS --
`mise run test:integration:all`, 20 files / 232 tests, zero skips, EQL v2 and
v3 both installed. Also `pnpm test` (16/16 tasks, suite correctly not among
them), `pnpm test:scripts` (818, stable over three runs), the e2e supply-chain
suite, all four lint scripts, the release gate, and
`pnpm install --frozen-lockfile` clean with one shared `@cipherstash/auth`
instance across stack, cli, wizard and the suite.

Refs #885

Claude-Session: https://claude.ai/code/session_01FGhktoHnwEuibNQF1MYexw
@changeset-bot

changeset-bot Bot commented Aug 26, 2026

Copy link
Copy Markdown

⚠️ No Changeset found

Latest commit: bd97382

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@tobyhede
tobyhede marked this pull request as ready for review August 26, 2026 05:57
@tobyhede
tobyhede requested a review from a team as a code owner August 26, 2026 05:57
@tobyhede
tobyhede requested a review from freshtonic August 26, 2026 05:58
@tobyhede tobyhede changed the title chore(protect-ffi): absorb the integration suite into the pnpm workspace chore(protect-ffi): add integration suite to pnpm workspace Aug 26, 2026
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.

1 participant