Skip to content

Commit 4938466

Browse files
committed
test(stack-encrypt): pin every chain's keyset and the duplicate-field rule
cargo-mutants found that dropping a chain's named keyset, or widening the passthrough-and-indexed check, went unnoticed. Each chain kind now asserts the keyset its keys and terms come from, and an indexed or passthrough field named twice without the other is a duplicate field.
1 parent 987d1b8 commit 4938466

1 file changed

Lines changed: 91 additions & 0 deletions

File tree

  • packages/stack-encrypt/tests

‎packages/stack-encrypt/tests/plan.rs‎

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -739,6 +739,28 @@ async fn every_build_error_is_its_own() {
739739
},
740740
"passthrough beside a sealed field with no index is a field named twice"
741741
);
742+
assert_eq!(
743+
build(
744+
fields()
745+
.encrypt_index::<String>("email", Equality)
746+
.encrypt::<String>("email")
747+
),
748+
PlanError::DuplicateField {
749+
field: "email".into()
750+
},
751+
"an indexed field named again without passthrough is a field named twice"
752+
);
753+
assert_eq!(
754+
build(
755+
fields()
756+
.passthrough::<String>("email")
757+
.passthrough::<String>("email")
758+
),
759+
PlanError::DuplicateField {
760+
field: "email".into()
761+
},
762+
"passthrough twice, with no index, is a field named twice"
763+
);
742764
assert_eq!(
743765
build(
744766
fields()
@@ -1259,6 +1281,75 @@ async fn a_keyset_choice_names_what_it_holds() {
12591281
);
12601282
}
12611283

1284+
#[tokio::test]
1285+
async fn every_chain_honours_its_named_keyset() {
1286+
let cipher = stack_cipher().await;
1287+
let tenant = cipher
1288+
.keyset(IdentifiedBy::Name("tenant".to_string().into()))
1289+
.await
1290+
.unwrap();
1291+
let id = tenant.keyset_id();
1292+
assert_ne!(id, cipher.default_keyset().keyset_id());
1293+
let user = user();
1294+
1295+
let indexed = cipher
1296+
.encrypt(&34u32)
1297+
.context("users/age")
1298+
.with(Equality)
1299+
.keyset("tenant")
1300+
.await
1301+
.unwrap();
1302+
assert_eq!(leaf_keyset(&indexed.ciphertext), id);
1303+
1304+
let mut fields = cipher
1305+
.encrypt(&user)
1306+
.context("users")
1307+
.fields()
1308+
.encrypt::<String>("email")
1309+
.encrypt::<u32>("age")
1310+
.encrypt::<String>("notes")
1311+
.passthrough::<u64>("id")
1312+
.keyset("tenant")
1313+
.await
1314+
.unwrap();
1315+
assert_eq!(
1316+
leaf_keyset(&fields.take::<StackCipherText>("notes").unwrap()),
1317+
id
1318+
);
1319+
1320+
let users_plan: Plan<User, _> = users_plan();
1321+
let mut row = cipher
1322+
.encrypt(&user)
1323+
.using(&users_plan)
1324+
.keyset("tenant")
1325+
.await
1326+
.unwrap();
1327+
assert_eq!(
1328+
leaf_keyset(&row.take::<StackCipherText>("notes").unwrap()),
1329+
id
1330+
);
1331+
1332+
// A term is keyed by the keyset's index key: a query under the tenant
1333+
// matches the tenant's term, not the default keyset's.
1334+
let email: Encrypted<EmailTerms> = row.take("email").unwrap();
1335+
let email_plan = users_plan.field("email").unwrap();
1336+
let under_tenant = cipher
1337+
.query("bob@example.com")
1338+
.using(&email_plan)
1339+
.equality()
1340+
.keyset("tenant")
1341+
.await
1342+
.unwrap();
1343+
let under_default = cipher
1344+
.query("bob@example.com")
1345+
.using(&email_plan)
1346+
.equality()
1347+
.await
1348+
.unwrap();
1349+
assert_eq!(under_tenant, email.terms.0);
1350+
assert_ne!(under_default, email.terms.0);
1351+
}
1352+
12621353
// `StackCipher` must stay nameable in this binary's imports.
12631354
#[allow(dead_code)]
12641355
fn _cipher_type(_: &StackCipher<FakeDataKeySource>) {}

0 commit comments

Comments
 (0)