Skip to content

Commit fa05297

Browse files
committed
test(encrypt): add runtime conformance
1 parent 53d1948 commit fa05297

13 files changed

Lines changed: 1468 additions & 48 deletions

File tree

‎conformance/stack-encrypt/README.md‎

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,23 @@ It runs native Rust against frozen vectors, Go against the same vectors, then
1919
native Rust against the ciphertexts Go has just produced. It never regenerates
2020
expectations. CI also runs the Go cases on its other supported host platforms.
2121
The same task runs the shared declaration acceptance/rejection corpus in both
22-
languages, with native observations of normalization.
22+
languages, with native observations of normalization. It also runs the shared
23+
runtime cases described below.
2324

2425
The Rust runner is `packages/stack-encrypt/tests/conformance.rs`; the Go runner
2526
is `languages/golang/encrypt/conformance_test.go`. Go calls the record interface
2627
used by generated code. Testing public SDK declarations is a separate layer.
2728
Python and TypeScript runners are pending; their absence is not a passing result.
2829

30+
## `record-runtime-v1` contract
31+
32+
The [runtime contract](runtime-contract.md) defines 37 cases in `v1/runtime.json`:
33+
missing and explicit null values, SQL-null omission, sealed-null storage and
34+
equality, typed batch round trips, write validation and lossless query conversions.
35+
The runners are `packages/stack-encrypt/tests/runtime_conformance.rs` and
36+
`languages/golang/encrypt/runtime_conformance_test.go`. Native Rust also checks
37+
data-key call counts; Go records where its host adapter refuses inputs earlier.
38+
2939
## `record-declarations-v1` contract
3040

3141
The [declaration grammar](declaration-grammar.md) defines the engine's current

‎conformance/stack-encrypt/coverage.md‎

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,12 +23,28 @@ Paths are relative to the repository root. New profile cases run from `v1/`.
2323
Python and future TypeScript runners have no evidence in this checkout. Their
2424
absence is a delivery gap, not a test skip that counts toward certification.
2525

26+
## Runtime checkpoint
27+
28+
`v1/runtime.json` adds 37 shared cases for typed writes and reads, null storage,
29+
batches, equality queries and lossless numeric conversion. See the
30+
[runtime contract](runtime-contract.md) for exact observations and limits.
31+
Native data-key counters cover zero-call preflight failures and single-call
32+
batches; Go provider-call instrumentation remains open.
33+
34+
| Existing evidence | Shared case IDs | Retained assertions |
35+
|---|---|---|
36+
| Native SQL-null record tests | `sql-missing`, `sql-null`, `sql-mixed-batch`, `sql-all-absent` | Wider declarations and malformed stored-record behavior |
37+
| Native sealed-null padding/equality tests | `sealed-mixed-batch`, `sealed-int64`, `sealed-uint64`, `sealed-float64` | Other integer widths, bool ciphertext, dates, timestamps and decimals |
38+
| Go generated `null_test.go` | Same sealed/SQL semantics | Pointer mapping, generated methods, EQL envelopes, reading null into nonpointer fields |
39+
| Native source validation tests | `late-wrong-type`, `late-missing`, `unknown-field` | Repeated keys, unsupported frame shapes and more output kinds |
40+
| Existing shared value-kind/operator fixtures | Runtime query cases cover selected conversion edges | Full type/operator matrices, generated SDK calls and verification |
41+
2642
## Consolidation policy
2743

2844
Shared semantic expectations should be maintained once and executed by each
2945
language runner. Conformance can replace duplicate case tables and fixture
3046
maintenance. It does not eliminate the need to execute the Rust and Go paths.
31-
No existing test has been deleted in these first two slices.
47+
No existing test has been deleted in these first three slices.
3248

3349
| Existing evidence | Relationship to conformance | Consolidation action |
3450
|---|---|---|
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
# Record runtime profile
2+
3+
`record-runtime-v1` describes the behavior exercised by `v1/runtime.json`.
4+
It is an experimental test contract for native Rust and Go WASI, with no EQL
5+
resolver. Python and TypeScript runners remain pending.
6+
7+
Each case uses one field, `value`, with ciphertext (`c`) and equality (`eq`)
8+
outputs, under context `["conformance", "value"]`. `kind` declares its type;
9+
`null` is empty for a required value, `sql`, or `sealed`. Values use the scalar
10+
profile's `{type, text}` representation, extended with `float64` decimal text.
11+
Runners construct typed values before calling the engine.
12+
13+
## Write and read
14+
15+
The `seal` operation receives an ordered `rows` array of field maps. An absent
16+
map key is missing; `{ "type": "null", "text": "" }` is an explicit null.
17+
These are separate inputs, including in languages that offer a nil-like value.
18+
19+
Successful cases specify `expected_rows` and `expected_stored`. The former
20+
describes opened values with their types and order. The latter states whether
21+
each row stores the field at all. Both must have the input row count.
22+
23+
- Required fields reject missing values and explicit nulls.
24+
- SQL-null fields accept both, omit the stored field, and open as explicit null.
25+
- Sealed-null fields require the key to exist. An explicit null stores both
26+
ciphertext and equality, then opens as null.
27+
- Runtime writes require the declared numeric type. A losslessly convertible
28+
value of another width is still refused on this path.
29+
- Unknown fields are refused; they cannot be silently dropped.
30+
- An invalid later row rejects the whole batch before data-key generation.
31+
- An empty batch succeeds with no data-key calls.
32+
33+
Every stored equality term must match a query for its expected opened value.
34+
`equal_lengths` additionally requires all stored ciphertexts in the case to
35+
have equal lengths. Mixed sealed-null/value cases exercise this requirement
36+
for int32, int64, uint64 and float64. This is not a claim about all types.
37+
Boolean equality is not admitted; existing native null tests cover boolean
38+
ciphertext separately.
39+
40+
## Equality queries
41+
42+
The `query` operation receives `input` and either `expected_value` or
43+
`expected_error`. A successful query's term must equal the term stored by
44+
encrypting `expected_value` through the same declaration. That value is
45+
independent expected data, not a conversion computed by the runner.
46+
47+
Cases cover signed narrowing bounds, integral and fractional float inputs,
48+
negative-to-unsigned refusal, and exact versus rounded integer-to-float
49+
conversion around 2^53. Text and boolean values are not parsed as numbers.
50+
A null equality query succeeds only for sealed-null fields.
51+
52+
## Refusals and observations
53+
54+
`expected_error` specifies the engine diagnostic `code`, `reason`, and `field`.
55+
Native Rust must reject before producing a pending operation. Go must expose
56+
`ErrEncoding` and the same diagnostic when the request reaches the guest.
57+
58+
Missing and unknown fields are rejected earlier by Go's record adapter.
59+
Those cases additionally specify `go_message`, an exact adapter regression
60+
observation, and require no engine diagnostic. This prose is not a portable
61+
engine error contract. A future SDK should report equivalent semantics at its
62+
earliest supported stage, without adopting Go's wording.
63+
64+
The native runner instruments data-key generation and retrieval calls. A
65+
successful batch makes one of each when it stores ciphertext, and zero when
66+
every field is omitted. Refused writes and all queries make neither call.
67+
The query oracle's separate encryption is counted separately. These assertions
68+
exclude index-key initialization and do not establish Go provider-call counts.
69+
Go runs the same semantic cases through its deterministic WASI guest; that
70+
guest's lack of network requests is not proof of logical key-call counts.
71+
72+
## Scope and remaining work
73+
74+
The 37 cases cover runtime writes, corresponding reads, and equality queries.
75+
They do not cover malformed stored records, authenticated type mismatches on
76+
read, context composition/preflight, provider failures, or other operators.
77+
Fresh cross-language artifact exchange remains the scalar profile's coverage.
78+
79+
Existing `value_kinds.json` and `query_operators.json` fixtures remain the
80+
shared evidence for their wider type/operator matrices. Existing generated
81+
Go null tests retain pointer mapping and EQL assertions. Native null tests
82+
retain their wider kinds and lower-level padding assertions. No tests are
83+
deleted by this slice; consolidation requires matching those assertions first.

0 commit comments

Comments
 (0)