Skip to content

Commit 83611df

Browse files
committed
test(proxy): add backwards compatibility tests for canonical config migration
Add 9 unit tests covering ColumnType-to-Postgres type mapping and CanonicalEncryptionConfig parsing to establish a safety net before further refactoring: - column.rs: 4 tests for column_type_to_postgres_type (text, json, json accessor, exhaustive variant coverage) - config.rs: 5 tests for config map construction (identifier bridging, multi-table configs, invalid index validation, realistic EQL schema fixture, malformed JSON error handling)
1 parent 27fa59b commit 83611df

2 files changed

Lines changed: 207 additions & 0 deletions

File tree

packages/cipherstash-proxy/src/postgresql/context/column.rs

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,3 +82,55 @@ fn column_type_to_postgres_type(
8282
(ColumnType::Json, _) => postgres_types::Type::JSONB,
8383
}
8484
}
85+
86+
#[cfg(test)]
87+
mod tests {
88+
use super::*;
89+
use eql_mapper::EqlTermVariant;
90+
91+
#[test]
92+
fn text_column_maps_to_postgres_text() {
93+
assert_eq!(
94+
column_type_to_postgres_type(&ColumnType::Text, EqlTermVariant::Full),
95+
postgres_types::Type::TEXT
96+
);
97+
}
98+
99+
#[test]
100+
fn json_column_maps_to_postgres_jsonb() {
101+
assert_eq!(
102+
column_type_to_postgres_type(&ColumnType::Json, EqlTermVariant::Full),
103+
postgres_types::Type::JSONB
104+
);
105+
}
106+
107+
#[test]
108+
fn json_accessor_maps_to_postgres_text() {
109+
assert_eq!(
110+
column_type_to_postgres_type(&ColumnType::Json, EqlTermVariant::JsonAccessor),
111+
postgres_types::Type::TEXT
112+
);
113+
}
114+
115+
#[test]
116+
fn all_column_types_have_postgres_mapping() {
117+
let types = vec![
118+
ColumnType::Boolean,
119+
ColumnType::BigInt,
120+
ColumnType::BigUInt,
121+
ColumnType::Date,
122+
ColumnType::Decimal,
123+
ColumnType::Float,
124+
ColumnType::Int,
125+
ColumnType::SmallInt,
126+
ColumnType::Timestamp,
127+
ColumnType::Text,
128+
ColumnType::Json,
129+
];
130+
131+
for ct in types {
132+
// Should not panic
133+
let _ = column_type_to_postgres_type(&ct, EqlTermVariant::Full);
134+
}
135+
}
136+
}

packages/cipherstash-proxy/src/proxy/encrypt_config/config.rs

Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -285,4 +285,159 @@ mod tests {
285285
},
286286
);
287287
}
288+
289+
#[test]
290+
fn config_map_preserves_table_and_column_names() {
291+
let json = json!({
292+
"v": 1,
293+
"tables": {
294+
"my_schema.users": {
295+
"email_address": {
296+
"cast_as": "text",
297+
"indexes": { "unique": {} }
298+
}
299+
}
300+
}
301+
});
302+
303+
let config = parse(json);
304+
305+
let ident = Identifier::new("my_schema.users", "email_address");
306+
let column = config.get(&ident).expect("column exists");
307+
assert_eq!(column.name, "email_address");
308+
assert_eq!(column.cast_type, ColumnType::Text);
309+
}
310+
311+
#[test]
312+
fn config_map_handles_multiple_tables() {
313+
let json = json!({
314+
"v": 1,
315+
"tables": {
316+
"users": {
317+
"email": { "cast_as": "text" }
318+
},
319+
"orders": {
320+
"total": { "cast_as": "int" }
321+
}
322+
}
323+
});
324+
325+
let config = parse(json);
326+
327+
assert_eq!(config.len(), 2);
328+
329+
let email = config.get(&Identifier::new("users", "email")).expect("users.email exists");
330+
assert_eq!(email.cast_type, ColumnType::Text);
331+
332+
let total = config.get(&Identifier::new("orders", "total")).expect("orders.total exists");
333+
assert_eq!(total.cast_type, ColumnType::Int);
334+
}
335+
336+
#[test]
337+
fn invalid_config_returns_error() {
338+
let json = json!({
339+
"v": 1,
340+
"tables": {
341+
"users": {
342+
"email": {
343+
"cast_as": "text",
344+
"indexes": {
345+
"ste_vec": { "prefix": "test" }
346+
}
347+
}
348+
}
349+
}
350+
});
351+
352+
let config: CanonicalEncryptionConfig = serde_json::from_value(json).unwrap();
353+
let result = config.into_config_map();
354+
assert!(result.is_err(), "ste_vec on text column should fail validation");
355+
}
356+
357+
#[test]
358+
fn real_eql_config_produces_correct_encrypt_config() {
359+
let json = json!({
360+
"v": 1,
361+
"tables": {
362+
"encrypted": {
363+
"encrypted_text": {
364+
"cast_as": "text",
365+
"indexes": { "unique": {}, "match": {}, "ore": {} }
366+
},
367+
"encrypted_bool": {
368+
"cast_as": "boolean",
369+
"indexes": { "unique": {}, "ore": {} }
370+
},
371+
"encrypted_int2": {
372+
"cast_as": "small_int",
373+
"indexes": { "unique": {}, "ore": {} }
374+
},
375+
"encrypted_int4": {
376+
"cast_as": "int",
377+
"indexes": { "unique": {}, "ore": {} }
378+
},
379+
"encrypted_int8": {
380+
"cast_as": "big_int",
381+
"indexes": { "unique": {}, "ore": {} }
382+
},
383+
"encrypted_float8": {
384+
"cast_as": "double",
385+
"indexes": { "unique": {}, "ore": {} }
386+
},
387+
"encrypted_date": {
388+
"cast_as": "date",
389+
"indexes": { "unique": {}, "ore": {} }
390+
},
391+
"encrypted_jsonb": {
392+
"cast_as": "jsonb",
393+
"indexes": {
394+
"ste_vec": { "prefix": "encrypted/encrypted_jsonb" }
395+
}
396+
},
397+
"encrypted_jsonb_filtered": {
398+
"cast_as": "jsonb",
399+
"indexes": {
400+
"ste_vec": {
401+
"prefix": "encrypted/encrypted_jsonb_filtered",
402+
"term_filters": [{ "kind": "downcase" }]
403+
}
404+
}
405+
}
406+
}
407+
}
408+
});
409+
410+
let config = parse(json);
411+
412+
// All 9 columns present with correct Identifiers
413+
assert_eq!(config.len(), 9);
414+
415+
// Verify legacy type aliases map correctly
416+
let float_col = config.get(&Identifier::new("encrypted", "encrypted_float8")).unwrap();
417+
assert_eq!(float_col.cast_type, ColumnType::Float);
418+
419+
let jsonb_col = config.get(&Identifier::new("encrypted", "encrypted_jsonb")).unwrap();
420+
assert_eq!(jsonb_col.cast_type, ColumnType::Json);
421+
422+
// Verify index counts
423+
let text_col = config.get(&Identifier::new("encrypted", "encrypted_text")).unwrap();
424+
assert_eq!(text_col.indexes.len(), 3);
425+
426+
let bool_col = config.get(&Identifier::new("encrypted", "encrypted_bool")).unwrap();
427+
assert_eq!(bool_col.indexes.len(), 2);
428+
429+
let jsonb_filtered = config.get(&Identifier::new("encrypted", "encrypted_jsonb_filtered")).unwrap();
430+
assert_eq!(jsonb_filtered.indexes.len(), 1);
431+
}
432+
433+
#[test]
434+
fn malformed_json_returns_parse_error() {
435+
let json = json!({
436+
"v": 1,
437+
"tables": "not a map"
438+
});
439+
440+
let result = serde_json::from_value::<CanonicalEncryptionConfig>(json);
441+
assert!(result.is_err());
442+
}
288443
}

0 commit comments

Comments
 (0)