Skip to content

Commit d5e7bd1

Browse files
committed
test(eql): PostgreSQL stores a sealed null in a storage-only column
The domain check of eql_v4_date takes a value whose c is a typed null, as it takes a date, and it reads back and opens as None (#1298). Runs in mise run test:encryption:postgres with the file's other database tests. Claude-Session: https://claude.ai/code/session_01XQN1QpaWVDGjHEg39KEJF3
1 parent ac0a38f commit d5e7bd1

1 file changed

Lines changed: 47 additions & 0 deletions

File tree

‎packages/eql/tests/encryption/tests/v4_date_time_numeric.rs‎

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -987,3 +987,50 @@ fn refused_null(
987987
let null = ValueFrame::from_bytes(&[0x00]).unwrap();
988988
refused(targets::encrypt(name, keyset, &label(), null))
989989
}
990+
991+
/// Needs an EQL v4 installation. A storage-only column stores an absent
992+
/// value sealed as a typed null (#1298): the domain's check takes it, as
993+
/// it takes a value, and it reads back and opens as `None`.
994+
#[test]
995+
#[ignore = "requires EQL_TEST_DATABASE_URL pointing to an EQL v4 database"]
996+
fn postgres_stores_a_sealed_null_in_a_storage_only_column() {
997+
let runtime = tokio::runtime::Builder::new_current_thread()
998+
.enable_all()
999+
.build()
1000+
.unwrap();
1001+
let (cipher, _) = runtime.block_on(common::cipher());
1002+
let keyset = cipher.default_keyset();
1003+
let mut db = database();
1004+
db.batch_execute(
1005+
"CREATE TEMP TABLE v4_optional (id int PRIMARY KEY, entry public.eql_v4_date);",
1006+
)
1007+
.unwrap();
1008+
let (absent, present): (date::Date, date::Date) = runtime.block_on(async {
1009+
(
1010+
keyset.encrypt_as(&None::<Date>, column()).await.unwrap(),
1011+
keyset
1012+
.encrypt_as(&Date::from(day("2024-02-29")), column())
1013+
.await
1014+
.unwrap(),
1015+
)
1016+
});
1017+
for (id, value) in [(1, &absent), (2, &present)] {
1018+
db.execute(
1019+
"INSERT INTO v4_optional (id, entry) VALUES ($1, $2::text::jsonb::public.eql_v4_date)",
1020+
&[&id, &json(value)],
1021+
)
1022+
.unwrap();
1023+
}
1024+
let stored: String = db
1025+
.query_one(
1026+
"SELECT entry::jsonb::text FROM v4_optional WHERE id = 1",
1027+
&[],
1028+
)
1029+
.unwrap()
1030+
.get(0);
1031+
let stored: date::Date = serde_json::from_str(&stored).unwrap();
1032+
let opened: Option<Date> = runtime
1033+
.block_on(async { cipher.decrypt_as(stored, column().into()).await })
1034+
.unwrap();
1035+
assert!(opened.is_none(), "the stored null opens as None");
1036+
}

0 commit comments

Comments
 (0)