Skip to content

Commit 25be30c

Browse files
committed
test: add eql_v2.min() and eql_v2.max() aggregate function tests
Port missing min/max aggregate tests from SQL to Rust/SQLx framework. Adds 4 new tests covering NULL handling and correct value identification for both min() and max() functions on encrypted integer columns. - New fixture: aggregate_minmax_data.sql with encrypted test data - Tests verify NULL returns for NULL-only queries - Tests verify correct minimum (plain_int=1) and maximum (plain_int=5) values
1 parent 6535ea8 commit 25be30c

File tree

2 files changed

+125
-1
lines changed

2 files changed

+125
-1
lines changed
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
-- Fixture: aggregate_minmax_data.sql
2+
-- Test data for eql_v2.min() and eql_v2.max() aggregate functions
3+
--
4+
-- Creates table with encrypted integer data including NULL values
5+
-- to test aggregate functions on encrypted columns
6+
7+
-- Create table
8+
CREATE TABLE agg_test
9+
(
10+
plain_int integer,
11+
enc_int eql_v2_encrypted
12+
);
13+
14+
-- Add data. These are encrypted values from the SQL test file.
15+
-- Decrypted `enc_int` value is the same as the `plain_int` value in the same row.
16+
INSERT INTO agg_test (plain_int, enc_int) VALUES
17+
(
18+
NULL,
19+
NULL
20+
),
21+
(
22+
3,
23+
'{"c": "mBbJyWl%QyVQT_N?b~OpQj!$J7B7H2CK@gB#`36H312|)kY;SeM7R*dAl5{R*U)AI+$~k7(JPvj;hmQK^F_}g^7Zs^WuYa^B(7y{V{&<LbY)~;X>N2hzy", "i": {"c": "encrypted_int4", "t": "encrypted"}, "k": "ct", "bf": null, "ob": ["ccccccccb06565ebd23d6a4c3eee512713175e673c6d995ff5d9b1d3492fe8eb289c3eb95029025f5b71fc6e06632b4a1302980e433361c7999724dbdd052739258d9444b0fbd43cc61368e60f4b0d5aeca2aa85c1c89933b53afffcc4eb0632dca75f632bb9bc792d1dbd6bced6253291f0db134552d384e9e378f4f5890c31ca9d115965a0e8fbf13ad8d1d33f88d360d5e2f9680fb158f98158443ffc769cd9aac94380f05e3226b785f58006e5b9da6b8d86a7441a88fd848099a2400ef59b494b0c30013568dc1be9bba560565fccb49309ba2ec3edcff6f9d7a67b519b3754b37b0025dff7592a6117949a04043c100353289628884fe06cb2099e7b4b49abea9797a73ee0b85283a5b6f69bcf45f87e6cd6d45ecfd1633903270781173ed9d31a682bba0e54ff355f456bf0c468e378e41cb54fcc074ad40fb4448f6fec892c1ecda15a5efffb8dde3a3b282865ac436d7e43d48d4327c439956733697d3f5b02ead4805a7f905bdae24c1b35252e34939676a07ddb5454c3580c7d76d792a97988e35142f43667112432623eda5126e9af2592dd"], "v": 1}'::jsonb::eql_v2_encrypted
24+
),
25+
(
26+
5,
27+
'{"c": "mBbKSqWLK6yl>o%G%&x+2$jdg7F`-R(^>R1Q^wGod8-FZ5C$xFI4dN?Ap114=77xPZ9!cKxE}qmyXrhx#K`4ztbUrysQrOFqON6bV{&<LbY)~;X>N2hzy", "i": {"c": "encrypted_int4", "t": "encrypted"}, "k": "ct", "bf": null, "ob": ["ccccccccb065659dd23d6a4c3eee512713175e673c6d995ff5d9b1d3492fe8eb289c3eb95029025f5b71fc6e06632b4a1302980e433361c7999724dbdd052739258d9444b0fbd43cc61368e60f4b0d5aeca2aa85c1c89933b53afffcc4eb0632dca75f632bb9bc792d1dbd6bced6253291f0db134552d384bec7bfb23290d7559fd8637b85ca7510cca465570029734ef0319c77177913ad84f54852bed2e2a67b6dafcab3eb70d3a2592414a43acc03703083cf1fa1984dfc0719337d5de4eefd0d137588641a0d38c771b77ab07ebab3fc9bfd7469c4222e1a8edee71188eeb24bfffcd82f711156381d8068223e3d75f5ba8a958182bc46a0ab58c29872cd17e559ed0b935a445249dbac5b51438cebaf9d28d5c8b67cd99f990d5295c1e37470ce5b33fe01eaf31d84c9a08b267c0e9e1aadfcce7f9e2253ababa71eaf1fec309dc988e454717a3c2e3bffb1c546a7195ecf274eb7d691abcf46a61e34d4c63c45d48831dc23aa11f981de692926cd1d1d77a340c9e54baf62da61d5f88960a93e120d3828f4053577b93b536cc9b05c889dcf171865"], "v": 1}'::jsonb::eql_v2_encrypted
28+
),
29+
(
30+
1,
31+
'{"c": "mBbJSy$p0fHEK%aOAOYi4PTJN7B@a-j{+xl7tffjGTN<-Znt3Zge#lGAX^WHzU`7ml<4vRHLKxoB%}N<H3?J~gR*ISwBlJ)X0By!V{&<LbY)~;X>N2hzy", "i": {"c": "encrypted_int4", "t": "encrypted"}, "k": "ct", "bf": null, "ob": ["ccccccccb0656502d23d6a4c3eee512713175e673c6d995ff5d9b1d3492fe8eb289c3eb95029025f5b71fc6e06632b4a1302980e433361c7999724dbdd052739258d9444b0fbd43cc61368e60f4b0d5aeca2aa85c1c89933b53afffcc4eb0632dca75f632bb9bc792d1dbd6bced6253291f0db134552d384250ca116ef329616ddb341917699b9ea48901124a15a4547be1ff7c672c0c1bc6bb17e2a141f46138fc314f4bf8a55068bf031bc48f038c379e54cfbb1c64eb223c18c87cd68a91fb031905e11d9478f158b561399b527038efc594bfd9fb19c963a2778b75215e1d8933b08df04d1c62742fd48a4de310792031a70ca4b157bc218ab3fbadc6dc14b939422023331c03bcf4b673c5d261a19c3d13155cbaa1b84e9e90e389fa6973dde07fba08c13847006707488e288ce780d59700197452ebc68d22032ab03f7b445e45ed7abb1af34955199440f7db2c969c60b1eb49cdcd75d5e8f7de37848ddebb40df8e14d4b92910e15fedac3f61f22ef430805ba1bbf5fccc9fe792e4c0353beee48ca03ef23c7d3fab19e9aa218aefb44e6c26d70"], "v": 1}'::jsonb::eql_v2_encrypted
32+
),
33+
(
34+
3,
35+
'{"c": "mBbLa7Cm?&jvpfcv1d3hep>s)76qzUbwUky&M&C<M-e2q^@e798gqWcAb{9a>3mjDG_os-_y0MRaMGl@&p#AOuusN|3Lu=mBCcg_V{&<LbY)~;X>N2hzy", "i": {"c": "encrypted_int4", "t": "encrypted"}, "k": "ct", "bf": null, "ob": ["ccccccccb06565ebd23d6a4c3eee512713175e673c6d995ff5d9b1d3492fe8eb289c3eb95029025f5b71fc6e06632b4a1302980e433361c7999724dbdd052739258d9444b0fbd43cc61368e60f4b0d5aeca2aa85c1c89933b53afffcc4eb0632dca75f632bb9bc792d1dbd6bced6253291f0db134552d384e9e378f4f5890c31ca9d115965a0e8fb2c3c60ccce84ffc03bddb22b27a1ce278eec118496fd23f083ebb21bb4b83b89eda8c0bdea50debc5ec4f2b2d91b63a80d39386194ad9d129bee2f5168341cb41ed26dc03466cac5e2dbe7336fdb74c0d37d63b396033ce60002c9950f5ac2970dacf4caace2eef5b81544df88a7ef2a8d69550d25d39c678c8e43a3dcc2857018a2c979b45c6b19dabd28ae7388d62916e6742763d6484d1b45154e6c8e6a66e02b03f64b67ddef24747dded32e226e3a93d5d1a92d11e760403cad04a0dd07c14da336a409739e8bbeb3b3d6b92117fa2d2c941da4996ea61b29ca3fffb4594ddbeab7105a1b4c5e422ec5ab8154db545103d8c2889be2e4591198912446d8b33b8708a4cc959a1e0957dcae6a50c3"], "v": 1}'::jsonb::eql_v2_encrypted
36+
)
37+
;

tests/sqlx/tests/aggregate_tests.rs

Lines changed: 88 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
//! Aggregate function tests
22
//!
3-
//! Tests COUNT, MAX, MIN with encrypted data
3+
//! Tests COUNT, MAX, MIN with encrypted data including eql_v2.min() and eql_v2.max()
44
55
use anyhow::Result;
66
use sqlx::PgPool;
@@ -64,3 +64,90 @@ async fn group_by_with_encrypted_column(pool: PgPool) -> Result<()> {
6464

6565
Ok(())
6666
}
67+
68+
// ========== eql_v2.min() and eql_v2.max() Tests ==========
69+
// Source: src/encrypted/aggregates_test.sql
70+
71+
#[sqlx::test(fixtures(path = "../fixtures", scripts("aggregate_minmax_data")))]
72+
async fn eql_v2_min_with_null_values(pool: PgPool) -> Result<()> {
73+
// Test: eql_v2.min() on NULL encrypted values returns NULL
74+
// Source SQL: ASSERT ((SELECT eql_v2.min(enc_int) FROM agg_test where enc_int IS NULL) IS NULL);
75+
76+
let result: Option<String> =
77+
sqlx::query_scalar("SELECT eql_v2.min(enc_int)::text FROM agg_test WHERE enc_int IS NULL")
78+
.fetch_one(&pool)
79+
.await?;
80+
81+
assert!(
82+
result.is_none(),
83+
"eql_v2.min() should return NULL when querying only NULL values"
84+
);
85+
86+
Ok(())
87+
}
88+
89+
#[sqlx::test(fixtures(path = "../fixtures", scripts("aggregate_minmax_data")))]
90+
async fn eql_v2_min_finds_minimum_encrypted_value(pool: PgPool) -> Result<()> {
91+
// Test: eql_v2.min() finds the minimum encrypted value (plain_int = 1)
92+
// Source SQL: ASSERT ((SELECT enc_int FROM agg_test WHERE plain_int = 1) = (SELECT eql_v2.min(enc_int) FROM agg_test));
93+
94+
// Get the expected minimum value (plain_int = 1)
95+
let expected: String =
96+
sqlx::query_scalar("SELECT enc_int::text FROM agg_test WHERE plain_int = 1")
97+
.fetch_one(&pool)
98+
.await?;
99+
100+
// Get the actual minimum from eql_v2.min()
101+
let actual: String = sqlx::query_scalar("SELECT eql_v2.min(enc_int)::text FROM agg_test")
102+
.fetch_one(&pool)
103+
.await?;
104+
105+
assert_eq!(
106+
actual, expected,
107+
"eql_v2.min() should return the encrypted value where plain_int = 1 (minimum)"
108+
);
109+
110+
Ok(())
111+
}
112+
113+
#[sqlx::test(fixtures(path = "../fixtures", scripts("aggregate_minmax_data")))]
114+
async fn eql_v2_max_with_null_values(pool: PgPool) -> Result<()> {
115+
// Test: eql_v2.max() on NULL encrypted values returns NULL
116+
// Source SQL: ASSERT ((SELECT eql_v2.max(enc_int) FROM agg_test where enc_int IS NULL) IS NULL);
117+
118+
let result: Option<String> =
119+
sqlx::query_scalar("SELECT eql_v2.max(enc_int)::text FROM agg_test WHERE enc_int IS NULL")
120+
.fetch_one(&pool)
121+
.await?;
122+
123+
assert!(
124+
result.is_none(),
125+
"eql_v2.max() should return NULL when querying only NULL values"
126+
);
127+
128+
Ok(())
129+
}
130+
131+
#[sqlx::test(fixtures(path = "../fixtures", scripts("aggregate_minmax_data")))]
132+
async fn eql_v2_max_finds_maximum_encrypted_value(pool: PgPool) -> Result<()> {
133+
// Test: eql_v2.max() finds the maximum encrypted value (plain_int = 5)
134+
// Source SQL: ASSERT ((SELECT enc_int FROM agg_test WHERE plain_int = 5) = (SELECT eql_v2.max(enc_int) FROM agg_test));
135+
136+
// Get the expected maximum value (plain_int = 5)
137+
let expected: String =
138+
sqlx::query_scalar("SELECT enc_int::text FROM agg_test WHERE plain_int = 5")
139+
.fetch_one(&pool)
140+
.await?;
141+
142+
// Get the actual maximum from eql_v2.max()
143+
let actual: String = sqlx::query_scalar("SELECT eql_v2.max(enc_int)::text FROM agg_test")
144+
.fetch_one(&pool)
145+
.await?;
146+
147+
assert_eq!(
148+
actual, expected,
149+
"eql_v2.max() should return the encrypted value where plain_int = 5 (maximum)"
150+
);
151+
152+
Ok(())
153+
}

0 commit comments

Comments
 (0)