Skip to content

Commit 539db18

Browse files
committed
test(stack-encrypt): a sealed null only in a termless target, and only through a nullable plan
The two survivors of cargo mutants --in-diff on the null modes (#1298): no test refused "null": "sealed" on an EQL type with a term, and none read a stored null through a plan with no "null", where the field's kind check must refuse it rather than skip. Claude-Session: https://claude.ai/code/session_01XQN1QpaWVDGjHEg39KEJF3
1 parent d5e7bd1 commit 539db18

1 file changed

Lines changed: 56 additions & 0 deletions

File tree

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

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5356,6 +5356,37 @@ mod tests {
53565356
assert_eq!(open(&cipher, with, &plan).await, present);
53575357
}
53585358

5359+
/// A stored null opens only through a plan that says the field may
5360+
/// hold one: read through a plan with no `"null"`, the field's kind
5361+
/// check refuses it, so a nil never reads as a zero value.
5362+
#[tokio::test]
5363+
async fn a_stored_null_is_refused_through_a_plan_that_has_no_null() {
5364+
let cipher = cipher().await;
5365+
let keyset = cipher.default_keyset();
5366+
let sealing = plan(obj(vec![(
5367+
"dob",
5368+
nullable(label("dob"), &["c"], Some("date"), "sealed"),
5369+
)]))
5370+
.expect("parses");
5371+
let reading =
5372+
plan(obj(vec![("dob", typed(label("dob"), &["c"], "date"))])).expect("parses");
5373+
let sealed = seal(&keyset, obj(vec![("dob", Tree::Null)]), &sealing).await;
5374+
let refused = decrypt(Scope::Client(&cipher), sealed, &reading, None)
5375+
.expect("the record fits the plan")
5376+
.await
5377+
.err();
5378+
assert!(
5379+
matches!(&refused, Some(crate::Error::Plan(crate::PlanError::FieldType { field, .. })) if field == "dob"),
5380+
"{refused:?}"
5381+
);
5382+
// And through the plan that says so, a value opens as a value.
5383+
let sealed = seal(&keyset, obj(vec![("dob", date(19_000))]), &sealing).await;
5384+
assert_eq!(
5385+
open(&cipher, sealed, &sealing).await,
5386+
obj(vec![("dob", date(19_000))])
5387+
);
5388+
}
5389+
53595390
/// A null in a typed field with no `"null"` is still refused.
53605391
#[tokio::test]
53615392
async fn a_null_in_a_field_with_no_null_mode_is_refused() {
@@ -6463,6 +6494,31 @@ mod tests {
64636494
assert_eq!(generates(&cipher), 0, "nothing minted");
64646495
}
64656496

6497+
/// A target field stores a null as its plan says: under `"sql"` any
6498+
/// type, but sealed only in a type with no term, since a null has none
6499+
/// and the type's term column would show it (#1298). Refused when the
6500+
/// plan is built, naming the field, before any value crosses.
6501+
#[test]
6502+
fn a_target_field_seals_a_null_only_in_a_type_with_no_term() {
6503+
let with_null = |null: &str| {
6504+
let mut spec = target_spec(label("email"), TEXT_EQ);
6505+
if let Tree::Object(entries) = &mut spec {
6506+
entries.push(("null".to_string(), s(null)));
6507+
}
6508+
obj(vec![("email", spec)])
6509+
};
6510+
let refused = plan_with(with_null("sealed"), &FakeEql).err();
6511+
assert!(
6512+
matches!(
6513+
&refused,
6514+
Some(Error::Plan { field: Some(f), reason: Reason::SealedNullIndex }) if f == "email"
6515+
),
6516+
"{refused:?}"
6517+
);
6518+
let parsed = plan_with(with_null("sql"), &FakeEql).expect("any type stores a SQL null");
6519+
assert_eq!(parsed.fields()[0].null(), Some(NullMode::Sql));
6520+
}
6521+
64666522
/// The resolver opens a target value through the client, which opens
64676523
/// every keyset's values; `confine` is what holds it to the scope's
64686524
/// keyset. A plan of one target field, so no lowered leaf can cause

0 commit comments

Comments
 (0)