Skip to content

Commit 67edf5a

Browse files
committed
test(stack-encrypt): a sealed null is as long as a value of every fixed-width kind
The length test covered a date, a float64 and a bool, so half of `ValueKind::sealed_null`'s arms ran in no test and the CRAP gate failed on it (CC 18, 47.6% covered). It now seals a value and a null of every kind the function maps, each integer and float width, a decimal, a date and a timestamp, and checks each pair is the same length and opens back. A decimal given in 64 bits is included: its null is padded to 128 bits, so the value had to be too. It is, and it opens in 128 bits. Refs #1298 Claude-Session: https://claude.ai/code/session_01XQN1QpaWVDGjHEg39KEJF3
1 parent aee4b55 commit 67edf5a

1 file changed

Lines changed: 55 additions & 22 deletions

File tree

‎packages/stack-encrypt/src/dynamic/record.rs‎

Lines changed: 55 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -5341,50 +5341,83 @@ mod tests {
53415341

53425342
/// A null sealed under `"sealed"` is a leaf as long as a value of
53435343
/// the field's type, opens as a null, and costs a data key as a
5344-
/// value does. A boolean's null is the plain null, already as long.
5344+
/// value does, for every kind of fixed width. A boolean's null is
5345+
/// the plain null, already as long. A decimal held in 64 bits is
5346+
/// sealed in 128, as its null is, and opens in 128.
53455347
#[tokio::test]
53465348
async fn a_sealed_null_is_as_long_as_a_value_and_opens_as_a_null() {
5349+
use vitaminc_aead::plaintext::TimeUnit;
53475350
let cipher = cipher().await;
53485351
let keyset = cipher.default_keyset();
5349-
let plan = plan(obj(vec![
5352+
let typed = |logical, value| Tree::Typed(logical, Box::new(value));
5353+
let fields = [
5354+
("flag", "bool", Tree::Bool(true)),
5355+
("i8", "int8", Tree::Int8(-8)),
5356+
("i16", "int16", Tree::Int16(-16)),
5357+
("i32", "int32", Tree::Int32(-32)),
5358+
("i64", "int64", Tree::Int64(-64)),
5359+
("i128", "int128", Tree::Int128(-128)),
5360+
("u8", "uint8", Tree::UInt8(8)),
5361+
("u16", "uint16", Tree::UInt16(16)),
5362+
("u32", "uint32", Tree::UInt32(32)),
5363+
("u64", "uint64", Tree::UInt64(64)),
5364+
("u128", "uint128", Tree::UInt128(128)),
5365+
("f32", "float32", Tree::Float32(1.5)),
5366+
("score", "float64", Tree::Float64(1.5)),
53505367
(
5351-
"dob",
5352-
nullable(label("dob"), &["c"], Some("date"), "sealed"),
5368+
"price",
5369+
"decimal",
5370+
typed(Logical::Decimal { scale: 2 }, Tree::Int128(150)),
53535371
),
53545372
(
5355-
"score",
5356-
nullable(label("score"), &["c"], Some("float64"), "sealed"),
5373+
"price64",
5374+
"decimal",
5375+
typed(Logical::Decimal { scale: 2 }, Tree::Int64(150)),
53575376
),
5377+
("dob", "date", date(19_000)),
53585378
(
5359-
"flag",
5360-
nullable(label("flag"), &["c"], Some("bool"), "sealed"),
5379+
"seen",
5380+
"timestamp",
5381+
typed(Logical::Timestamp(TimeUnit::Nanos), Tree::Int128(1)),
53615382
),
5362-
]))
5383+
];
5384+
let plan = plan(obj(fields
5385+
.iter()
5386+
.map(|(name, kind, _)| (*name, nullable(label(name), &["c"], Some(kind), "sealed")))
5387+
.collect()))
53635388
.expect("parses");
5364-
let present = obj(vec![
5365-
("dob", date(19_000)),
5366-
("score", Tree::Float64(1.5)),
5367-
("flag", Tree::Bool(true)),
5368-
]);
5369-
let absent = obj(vec![
5370-
("dob", Tree::Null),
5371-
("score", Tree::Null),
5372-
("flag", Tree::Null),
5373-
]);
5389+
let present = obj(fields
5390+
.iter()
5391+
.map(|(name, _, value)| (*name, value.clone()))
5392+
.collect());
5393+
let absent = obj(fields
5394+
.iter()
5395+
.map(|(name, _, _)| (*name, Tree::Null))
5396+
.collect());
53745397
check_source(absent.clone(), &plan).expect("a null fits a field that seals one");
53755398
let with = seal(&keyset, present.clone(), &plan).await;
53765399
let without = seal(&keyset, absent.clone(), &plan).await;
5377-
for field in ["dob", "score", "flag"] {
5400+
for (field, kind, _) in &fields {
53785401
assert_eq!(
53795402
sealed_len(&without, field),
53805403
sealed_len(&with, field),
5381-
"{field}: an absent value is as long as a present one"
5404+
"{kind}: an absent value is as long as a present one"
53825405
);
53835406
}
53845407
let again = seal(&keyset, absent.clone(), &plan).await;
53855408
check_record(again, &plan, None).expect("the stored nulls fit the plan");
5409+
let widened = obj(fields
5410+
.iter()
5411+
.map(|(name, _, value)| match *name {
5412+
"price64" => (
5413+
*name,
5414+
typed(Logical::Decimal { scale: 2 }, Tree::Int128(150)),
5415+
),
5416+
_ => (*name, value.clone()),
5417+
})
5418+
.collect());
53865419
assert_eq!(open(&cipher, without, &plan).await, absent);
5387-
assert_eq!(open(&cipher, with, &plan).await, present);
5420+
assert_eq!(open(&cipher, with, &plan).await, widened);
53885421
}
53895422

53905423
/// The term under a stored field's output key, as its bytes.

0 commit comments

Comments
 (0)