Skip to content

feat(eql-bindings): produce Smallint and SmallintEq from i16 - #1267

Merged
coderdan merged 2 commits into
nextfrom
feat/eql-v4-smallint
Oct 19, 2026
Merged

coderdan merged 2 commits into
nextfrom
feat/eql-v4-smallint

Conversation

@coderdan

@coderdan coderdan commented Oct 18, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Go developers can now store a 16-bit integer in an EQL column, and search it by equality: Smallint (stored and read only) and SmallintEq (=). EQL is the SQL library we install into a customer's PostgreSQL database to store and search encrypted values. Stack Encrypt is our Rust encryption engine, which the Go SDK runs as a WebAssembly guest. The engine and the Go SDK already had the 16-bit integer kind. The only thing missing was eql-bindings naming i16 as the family's plaintext.

This also fixes a stale message. stashgen (the Go code generator) told users that the date, timestamp and numeric types wait for the engine to gain their kinds. The engine has them now. What those types actually wait for is a choice of Rust plaintext type, and the message now says that.

Based on next; independent of the other open EQL v4 PRs.

Changes

eql-codegen and eql-bindings

Tests (packages/eql/tests/encryption)

  • Round trips through the typed and by-name entry points, at both extremes of the width.
  • SmallintEq(42) has the same equality term as IntegerEq(42) under one column. That's ADR-0002's one integer equality domain.
  • A frame of another width is refused by name. An i32 ciphertext that fits opens as an i16, and one that doesn't fit is refused.
  • PostgreSQL (postgres_stores_and_finds_smallint_rows):
    • = on eql_v4_smallint_eq finds exactly the expected rows, including i16::MIN and i16::MAX;
    • the storage-only column refuses =;
    • a stored value reads back and opens.
  • v4_queries.json gains a SmallintEq query (key tally).

Go

  • A Tally test model covers int16 → Smallint, int16 → SmallintEq and int32 → SmallintEq:

    • the values seal and open back;
    • an int32 outside the range, above or below, is refused with ErrEncoding naming the field. That holds on the codec write, the single-field Encrypt and the Eq query;
    • Count.Eq matches the Rust fixture byte for byte.
  • TestV4SmallintRowsMatchTheCommittedFixture writes v4_smallint_rows.json. Rust opens those Go-sealed rows at 16 bits, and a PostgreSQL test stores them and checks = with the fixture's query.

    Tally is a model of its own because Profile's rows are frozen in stored_v4_rows_v1.json, and a new Profile field would change them.

  • The guest's and the SDK's catalog tests list the two new types; SmallintEq answers eq.

  • Updated the README, doc.go, the stashgen README and the CHANGELOG.

Verification

  • packages/eql: mise run build, test:codegen, test:crates, test:encryption (420 passed), types:check (on the commit) and codegen:parity all passed.
  • mise run test:encryption:postgres passed against a disposable PostgreSQL 17.5 (initdb cluster) with the v4 bundle built from the tree: all 6 tests, including the two new smallint ones.
  • Clippy with -D warnings on eql-bindings, eql-codegen and eql-encryption-tests (all features, all targets) was clean, and so was eql-bindings rustdoc with -D warnings.
  • All five guests rebuilt. mise run wasm:guest:test passed.
  • In languages/golang: mise run go:test passed, go vet ./... was clean and mise run go:lint reported 0 issues. go generate ./... leaves no diff.
  • Stack Encrypt is unchanged, so there are no mutants to run on it.

Related

Refs #1130: the smallint storage and equality types.

Review notes

  • Small diff. The engine change is two lines in encryption/targets.rs and two entries in bindings.rs. The rest is generated or tests.
  • Follow-ups:
  • Review follow-ups (force-pushed): a Go-sealed rows fixture that Rust and PostgreSQL read, both range bounds on every refusal path, a separate changeset, and two stale comments. Each fix went into the commit that owns it.
  • Expected rebase: this and feat(eql-bindings): produce the OPE-ordered EQL v4 types #1266 both add rows to ENCRYPTION_DOMAINS, the generated targets table and the same catalog test lists. Whichever lands second regenerates and merges those lists.

@changeset-bot

changeset-bot Bot commented Oct 18, 2026 •

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 337e882

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 12 packages
Name Type
@cipherstash/eql Minor
stash Patch
@cipherstash/stack-prisma Patch
@cipherstash/basic-example Patch
@cipherstash/e2e Patch
@cipherstash/prisma-example Patch
@cipherstash/stack-drizzle Patch
@cipherstash/stack-supabase Patch
@cipherstash/stack Patch
@cipherstash/wizard Patch
@cipherstash/bench Patch
@cipherstash/test-kit Patch

Not sure what this means? Click here to learn what changesets are.

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

@coderabbitai

coderabbitai Bot commented Oct 18, 2026 •

Copy link
Copy Markdown
Contributor

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration
  • Configuration used: Organization UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: dc0a8c36-d8c3-406e-80c1-6a1d2e5bc3f2

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Comment @coderabbitai help to get the list of available commands.

@coderdan coderdan left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Review at 48c44de. No blocking problems. The range checks, the equality term and the SQL shapes are right. There are two test gaps, a changeset conflict that will certainly happen, and two stale comments.

Findings (most severe first)

  1. Medium: no row the Go SDK sealed as Smallint or SmallintEq reaches Rust or PostgreSQL, and nothing freezes the smallint stored format (tally.go, inline). fixtures/README.md says v4_rows.json and stored_v4_rows_v1.json hold "one value of every producible EQL v4 type". That is no longer true. Here is what goes wrong: a later change to how the guest seals a smallint leaf passes the Go round trip and the tally query fixture. The equality term is the same at every integer width by design, so the byte match on the term can't see the width. The frozen-rows guard was built to catch exactly that kind of change, and it doesn't cover these two types. #1266 handles the same problem for Ranking by adding a Go-written v4_ordered_rows.json that Rust opens and inserts into PostgreSQL. Do the same for Tally, and either reword the README line or freeze a smallint row set.

  2. Low: the changeset will conflict with #1266 (inline). Both PRs rewrite the same single-line paragraph of .changeset/eql-v4-bindings.md from the same base blob (88887e454), so whichever merges second gets a textual conflict. #1266 targets feat/stack-encrypt-order-keys, so the conflict arrives when it rebases onto next. A separate .changeset/eql-v4-smallint.md ('@cipherstash/eql': minor, one sentence) avoids it. It would also clear the changeset-bot "No Changeset found" warning on this PR, which appears because the PR only edits an existing file. The package choice is right: @cipherstash/eql is the lockstep vehicle for the eql-bindings crate, types:check shows the TypeScript output is unchanged, and the Go module isn't released through Changesets (its CHANGELOG is updated).

  3. Low: the Go test covers only one of the refusal paths (inline). It checks a codec write above the range. It doesn't check the lower bound (MinInt16 - 1), an out-of-range Wide.Eq query, the single-field Wide.Encrypt, or that the error names the field. I checked these by hand with a throwaway test against the deterministic EQL guest, and all of them behave correctly:

    • Wide.Eq(MaxInt16+1) gives gensupport: wide: cipherstash: malformed input: a value does not fit the field's kind int16.
    • EncryptTally with Wide: MinInt16-1 gives ... field "wide": ... does not fit the field's kind int16.

    widened_test.go pins both bounds and the query path for IntegerEq. Mirroring it here would keep the smallint path pinned if gensupport changes.

  4. Nit: two leftovers of the stale premise this PR corrects (inline). The doc comment on plaintext_of (bindings.rs:366) still says "None for a family the engine's value enum has no kind for". The test the_producible_types_are_the_unordered_ones_the_value_enum_carries (tests/targets.rs:567) is named after the same premise.

What I checked and found correct

  • Range enforcement from Go. stashgen declares every built-in integer field stored as Smallint/SmallintEq with kind gensupport.Int16 (see tally_stash.go, where the int32 field wide is declared as Int16). asKind and integerAs range-check with MinInt16/MaxInt16 on the codec write, on Field.Encrypt and on Field.Term/Range. A value outside the range is refused with ErrEncoding and the field's name; it is never truncated. Opening widens back into the field's Go type through Get[int32]. The guest's by-name dispatch also refuses any frame that isn't exactly int16, so the host can't send a wider leaf.
  • Equality term. SmallintEq(v) produces the same hm as IntegerEq(v) and BigintEq(v) under one column. That is ADR-0002's single integer equality domain, and the column identifier keeps terms from different columns apart. With a throwaway local assertion I also confirmed the terms agree across widths for negative values and the extremes (-1, -300, i16::MIN, i16::MAX), so there is no sign-extension mismatch, and that SmallintEq(-1) differs from IntegerEq(65535).
  • SQL. public.eql_v4_smallint requires v/i/c and _eq adds hm, with v = 4. eql_v4.eq_term compares hm. The generated structs and the envelope assertions (c,i,v and c,hm,i,v) match. I didn't run the ignored PostgreSQL test myself; the PR description says it passed against PostgreSQL 17.5.
  • The new stashgen message is accurate. Stack Encrypt's ValueKind has Date, Timestamp and Decimal, and the Go SDK has record.Date, Timestamp and Decimal. So "the engine and the Go SDK carry the kind" is true, and #1130 is the umbrella issue for these types. json is caught by an earlier branch, so only date, timestamp and numeric reach the new wording, and the codegen test pins all three across ""/eq/ord.
  • Generated code drift. None. types:check regenerated everything and left the tree clean, and so did go generate ./....

Checks run (no credentials needed)

  • packages/eql:
    • mise run test:codegen and mise run types:check (clean tree afterwards);
    • cargo test --locked -p eql-encryption-tests: all passed, PostgreSQL tests ignored;
    • cargo clippy --locked -p eql-bindings -p eql-encryption-tests -p eql-codegen --all-features --all-targets -D warnings: clean.
  • All five guests built (including wasm:guest:build:eql:deterministic). Then, in languages/golang:
    • go generate ./...: no diff;
    • go vet ./...: clean;
    • go test ./...: all packages pass, including TestV4SmallintFieldsSealAndOpenBack.

Comment thread languages/golang/encrypt/internal/testusers/tally.go
Comment thread .changeset/eql-v4-bindings.md Outdated
Comment thread languages/golang/encrypt/eql_v4_smallint_test.go Outdated
Comment thread packages/eql/crates/eql-codegen/src/bindings.rs
The engine and the Go SDK both carry the int16 kind now, so the smallint
family's storage and equality types need only eql-bindings to name a
plaintext: i16 joins the sealed Plaintext set and plaintext_of, and the
two domains join ENCRYPTION_DOMAINS. SmallintEq's term is the integer
equality term, one per value whatever the width, so a smallint shares it
with an integer of the same value under the same column.

The date, timestamp and numeric families no longer say the engine has
no kind for them, which stopped being true when the kinds landed. What
they wait for is a choice of Rust plaintext type, and the reason says so.

Tests: round trips through both entry points, the shared equality term,
a value of another width refused by name and an i32 that does not fit
refused on open, and in PostgreSQL `=` on SmallintEq (including both
extremes) with the storage-only column blocked. v4_queries.json gains a
SmallintEq query for the Go SDK.

@coderdan coderdan left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Incremental re-review of 48c44de..1838018: the amended d5184c2 plus the new 1838018.

Prior findings (review)

  1. Medium, no Go-sealed smallint row reaches Rust or PostgreSQL, and the README overclaims: fixed as asked. v4_smallint_rows.json is Go-sealed. smallint_rows_the_go_sdk_sealed_open_in_rust opens all three fields at i16, including wide, the int32 field. postgres_stores_and_queries_smallint_rows_the_go_sdk_sealed puts the rows into eql_v4_smallint / eql_v4_smallint_eq columns, and = with the tally query returns rows 1 and 3, not row 2. The README now says these rows are not frozen yet, so it no longer claims they are. The ignored test lives in v4_types, which test:encryption:postgres already loops over, so test-eql.yml runs it. One gap is left in how CI hands rows from Go to Rust (inline below).
  2. Low, changeset conflict with #1266: fixed. The entry moved to .changeset/eql-v4-smallint.md. A trial git merge-tree against #1266's head (82ab168) shows no conflict in .changeset/. fixtures/README.md and tests/v4_types.rs merge cleanly too. The conflicts left are in files both PRs regenerate or edit on purpose: eql_gen.go, v4/targets.rs, eql-codegen, the guest's targets.rs, native_ops.rs, and the Go and eql-bindings docs. Those are expected for two PRs that both make types producible.
  3. Low, refusal coverage: fixed. Both bounds now go through EncryptTally, Wide.Encrypt and Wide.Eq, and the bounds themselves are accepted. I printed the errors. All three name the field (field "wide": ... does not fit the field's kind int16), so the strings.Contains(..., "wide") check is not passing by accident.
  4. Nit, wording that says the engine lacks a kind: fixed. The plaintext_of doc and the targets.rs test name are reworded, and grep finds no other copies in the touched files.

New findings

  • Low: the smallint rows have no hand-off of today's rows from Go to Rust, unlike v4_rows.json. On top of that, the existing CI step's cargo filter now also selects the smallint test, which only reads the committed file, so the step looks like it covers smallint when it does not. Details inline.
  • Nit: once #1266 merges, the README's "Types made producible since (Smallint, SmallintEq)" will be incomplete. Inline.

Verified sound

  • The drift check (smallintWithoutCiphertexts) compares everything except c, including hm and the key sets. It passes against a freshly built deterministic EQL guest, so the committed fixture matches today's build.
  • The Rust Fixture and Row shapes match what Go writes. The query plaintext (-12) splits the rows, as the test asserts.
  • tally.go keeps Profile and the frozen v1 rows untouched. Its generated tally_stash.go matches go generate.

Checks run (no credentials)

  • Built all five guests: base, deterministic, EQL, EQL deterministic, auth.
  • go generate ./... in languages/golang: no diff, nothing untracked.
  • go vet ./encrypt/....
  • go test -run 'TestV4Smallint|TestV4RowsMatch' ./encrypt: pass, not skipped.
  • cargo test --locked -p eql-encryption-tests --test v4_types --test targets: 30 passed, 5 ignored.
  • mise run test:encryption:postgres against a scratch local PostgreSQL 15: all 5 ignored PostgreSQL tests in v4_types pass, including the new one, and the text_eq one passes too.
  • git merge-tree against #1266's head.

Comment thread packages/eql/tests/encryption/tests/v4_types.rs
// TestV4RowsMatchTheCommittedFixture does for v4_rows.json: everything but
// each `c` must match, and the committed `c` values must open. The Rust
// suite opens the same rows and stores them in PostgreSQL, so a change to
// how the guest seals a smallint leaf shows there too. EQL_UPDATE_FIXTURES

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

This sentence, like the commit message's "a change to how the guest seals a smallint leaf shows in Rust and PostgreSQL", is true only for rows already in the committed file. See the comment on go_tally::fixture() in v4_types.rs: without a hand-off of today's rows, Rust and PostgreSQL see a guest change only after the file is regenerated.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fixed in 337e882. The doc comment no longer says a guest change shows in Rust and PostgreSQL. It now says EQL_UPDATE_FIXTURES leaves the committed file alone when only c moves, and that EQL_V4_SMALLINT_ROWS_OUT writes today's rows for CI to hand to Rust (EQL_V4_SMALLINT_ROWS). The commit message is reworded the same way. A guest change that moves only a smallint ciphertext reaches Rust's 16-bit open through the hand-off. PostgreSQL stores and queries the committed rows only.

Comment thread packages/eql/tests/encryption/fixtures/README.md
@coderdan
coderdan marked this pull request as ready for review October 19, 2026 01:28
@coderdan
coderdan requested a review from a team as a code owner October 19, 2026 01:28
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 19, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-19T01:32:01.469030Z 1838018 Draft marked ready
🔒 Security Review ✅ Completed 2026-10-19T01:32:13.981824Z 1838018 Draft marked ready
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@cipherstash-agent cipherstash-agent left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Review attestation

Source: automated /code-review medium + /cross-check-review-findings pass by Claude Code on head 1838018. It read the full non-generated diff for eql-bindings, eql-codegen, the Go Tally model and its tests, the fixtures, and the changeset. I did not run the build or test suites. CI and the PR's reported local verification cover those.

Critical / blocking: none.

Findings: none survived cross-check.

Checked and found consistent:

  • i16 is added to the sealed Plaintext set and mapped to Int16 in targets.rs. encrypt_named, decrypt_named and query_named each gain Smallint / SmallintEq arms. The Smallint ord families stay non-producible with accurate reasons.
  • plaintext_of, ENCRYPTION_DOMAINS and the regenerated v4/targets.rs and eql_gen.go agree.
  • The date, timestamp and numeric gap text is updated in the codegen, the generated table and the tests.
  • Range handling is covered on both bounds for every refusal path, and the int32 → SmallintEq equality term shares the integer one.
  • The changeset (@cipherstash/eql minor) is present. No skill under skills/ names these types, so I flagged none.

Minor, non-blocking note: PR #1266 will conflict on ENCRYPTION_DOMAINS, the generated targets table and the catalog test lists, as the description already says. Whichever lands second should regenerate.


Generated by Claude Code

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 1838018cb0

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread packages/eql/crates/eql-bindings/src/encryption/targets.rs
…o's rows in PostgreSQL

A Tally test model, under Profile's table so its query shares
v4_queries.json without touching Profile's frozen rows, holds an int16
Smallint, an int16 SmallintEq and an int32 SmallintEq. The rows seal to
their EQL types and open back; an int32 outside the smallint range, above
or below, is refused with ErrEncoding naming the field on the codec
write, the single-field Encrypt and the Eq query; and Count.Eq derives
the Rust fixture's query byte for byte.

TestV4SmallintRowsMatchTheCommittedFixture checks v4_smallint_rows.json,
which the Rust suite opens at 16 bits and inserts into eql_v4 columns,
where = with the fixture's query finds the rows holding its value. The
file is rewritten only when something other than `c` moves, so on its own
it carries the ciphertexts of the last regeneration, not today's.

EQL_V4_SMALLINT_ROWS_OUT and EQL_V4_SMALLINT_ROWS hand today's rows from
Go to Rust, as EQL_V4_ROWS_OUT and EQL_V4_ROWS do for v4_rows.json, and
the Go workflow's hand-off step now passes both pairs. So a guest change
that moves only a smallint ciphertext, which Go's own round trip cannot
see because the equality term is the same at every width, fails Rust's
16-bit open in the pull request that makes it. CI runs only the two open
tests on the handed-off rows; PostgreSQL stores and queries the committed
rows. The step's cargo filter is now --exact, so it names only the tests
that read what it hands them.
@coderdan
coderdan force-pushed the feat/eql-v4-smallint branch from 1838018 to 337e882 Compare October 19, 2026 01:57

@coderdan coderdan left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Incremental re-review of 1838018..337e882. Commit 1 (d5184c2) is unchanged, and the merge-base with next is still c612ed4. Only commit 2 moved.

Prior items (review)

  1. Low, no hand-off of today's smallint rows from Go to Rust, with a substring filter that only looked like it covered them: fixed. Go writes EQL_V4_SMALLINT_ROWS_OUT before it compares with the committed file. go_tally::fixture() reads EQL_V4_SMALLINT_ROWS and falls back to the committed file. The CI step sets both pairs and now names its two tests with --exact. The CI log for this head (run 39187725154, job "WASI check, guests and Go (Linux)") shows smallint_rows_the_go_sdk_sealed_open_in_rust ... ok and rows_the_go_sdk_sealed_open_in_rust ... ok, with 2 passed; 17 filtered out.
  2. Nit, the README list would leave out #1266's types: fixed. The README now says "Types made producible since are sealed into their own regenerable row fixtures (see the table above)". That stays true whichever PR merges second.

The Codex P1 on targets.rs:490 (the claim that eql-bindings depends on a published stack-encrypt 0.3.0 that lacks Int16): I agree with the author's reply. crates.io lists only stack-encrypt 0.1.0 and 0.2.0. The version = "0.3.0" requirement and ValueKind::Int16 both already exist on next, and this PR doesn't touch stack-encrypt or eql-bindings' Cargo.toml.

New findings (both small, both inline, both in the hand-off step)

  • Low: if either test is later renamed, the --exact filter silently stops running it, and the step still passes. libtest exits 0 with 1 passed; 18 filtered out. I checked this locally with one misspelled name.
  • Nit: test -s "$rows" && test -s "$smallint_rows" doesn't stop the step when the first file is missing. The step still fails, but later and with a misleading message.

What I checked and found correct

  • -- --exact a b. libtest treats the two names as two filters ORed together, and with --exact each one has to match a full test name. Both tests are top-level in tests/v4_types.rs, so the names match. Locally the command ran exactly those two (2 passed; 17 filtered out), the same as CI.
  • The Go -run '^(TestV4RowsMatchTheCommittedFixture|TestV4SmallintRowsMatchTheCommittedFixture)$'. It contains no /, so Go doesn't split it into subtest levels. With -v both tests ran and passed, and both files were written.
  • Quoting. The regex is single-quoted, so the shell doesn't touch (, | or $. Every variable is double-quoted.
  • The hand-off is real. I pointed EQL_V4_SMALLINT_ROWS at a file that doesn't exist, and the smallint test failed with NotFound, so it reads the path the variable gives it. The file Go wrote today differs from the committed one only in c: it is byte-identical once each c is removed, and it differs with c included. So Rust does open fresh ciphertexts.
  • Go writes the file before it compares. The write comes before the committed-file comparison and before EQL_UPDATE_FIXTURES, and a failed write is t.Fatal. So CI gets today's rows even when the drift check then fails, and that failure still fails the step. This matches TestV4RowsMatchTheCommittedFixture.
  • The override doesn't reach the PostgreSQL suite. test-eql.yml sets neither variable, so postgres_stores_and_queries_smallint_rows_the_go_sdk_sealed keeps using the committed rows, as the commit message says. frozen() still can't be redirected.

Checks run (no credentials)

  • Built wasm:guest:build:eql:deterministic.
  • Ran the Go half of the step with -v: both tests passed, not skipped, and both files were non-empty.
  • Ran the Rust half exactly as written in the workflow, against those files: 2 passed.
  • Ran it again with a missing EQL_V4_SMALLINT_ROWS file, and again with a misspelled exact name. Those runs give the two inline findings.
  • cargo test --locked -p eql-encryption-tests in packages/eql: all passed, 6 ignored.
  • vitest run --config scripts/vitest.config.mjs: 75 of 77 files passed, including every workflow-scanning test (workflow-run-fail-fast, eql-suite-ci, crates-ci, workflow-working-directory, and so on). The two failures come from my environment: this worktree has no node_modules of its own, so it lacks @changesets/parse and the SQL bundle for @cipherstash/eql@3.0.4. Neither touches a file in this PR.
  • gh pr checks 1267: all green. review was skipped.

smallint_rows="$RUNNER_TEMP/v4_smallint_rows.json"
(cd languages/golang && CGO_ENABLED=0 EQL_V4_ROWS_OUT="$rows" EQL_V4_SMALLINT_ROWS_OUT="$smallint_rows" go test -count=1 -run '^(TestV4RowsMatchTheCommittedFixture|TestV4SmallintRowsMatchTheCommittedFixture)$' ./encrypt)
test -s "$rows" && test -s "$smallint_rows"
EQL_V4_ROWS="$rows" EQL_V4_SMALLINT_ROWS="$smallint_rows" cargo test --locked --manifest-path packages/eql/Cargo.toml -p eql-encryption-tests --test v4_types -- --exact rows_the_go_sdk_sealed_open_in_rust smallint_rows_the_go_sdk_sealed_open_in_rust

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Low: --exact makes this step stricter about which tests run, but it doesn't check that they ran. If either test is renamed (say smallint_rows_the_go_sdk_sealed_open_in_rust becomes ..._open_at_16_bits), libtest matches nothing for that name, runs the other test alone, and exits 0. I checked this locally with one name misspelled: 1 passed; 0 failed; 18 filtered out, cargo exit 0. The hand-off this PR adds would then be gone, and CI would stay green. The old substring filter had the same weakness, but --exact makes it break on any rename.

One way to make it fail loudly is to count before running:

filters=(--exact rows_the_go_sdk_sealed_open_in_rust smallint_rows_the_go_sdk_sealed_open_in_rust)
test "$(cargo test --locked --manifest-path packages/eql/Cargo.toml -p eql-encryption-tests --test v4_types -- "${filters[@]}" --list --format terse | grep -c ': test$')" -eq 2
EQL_V4_ROWS="$rows" EQL_V4_SMALLINT_ROWS="$smallint_rows" cargo test ... -- "${filters[@]}"

A small test under scripts/__tests__/ that checks each name in this step against a fn in tests/v4_types.rs would also do it, and wouldn't touch the workflow.

EQL_V4_ROWS="$rows" cargo test --locked --manifest-path packages/eql/Cargo.toml -p eql-encryption-tests --test v4_types rows_the_go_sdk_sealed_open_in_rust
smallint_rows="$RUNNER_TEMP/v4_smallint_rows.json"
(cd languages/golang && CGO_ENABLED=0 EQL_V4_ROWS_OUT="$rows" EQL_V4_SMALLINT_ROWS_OUT="$smallint_rows" go test -count=1 -run '^(TestV4RowsMatchTheCommittedFixture|TestV4SmallintRowsMatchTheCommittedFixture)$' ./encrypt)
test -s "$rows" && test -s "$smallint_rows"

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Nit: this line only enforces its second half. shell: bash runs as bash -eo pipefail, and errexit ignores a failing command that isn't the last one in an && list. So if $rows is missing or empty, && stops before the second test, the list fails, and the step keeps going to cargo.

The realistic way to get there is the deterministic EQL guest not being built. deterministicEQLClient then calls t.Skip in both Go tests, go test prints ok, and neither file gets written. That case still fails loudly, but in the wrong place. cargo panics in both tests with fixtures/v4_rows.json is written by the Go SDK's TestV4RowsMatchTheCommittedFixture: ... NotFound (I reproduced the smallint version by pointing the variable at a path that doesn't exist). The message names the committed fixture, which is present, not the $RUNNER_TEMP file that is missing. That sends whoever reads the log to the wrong file.

Splitting it into two lines makes both checks stop the step:

test -s "$rows"
test -s "$smallint_rows"

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