From f02583444f5abca23fa05e17e4db34e06d597fbc Mon Sep 17 00:00:00 2001 From: Toby Hede Date: Mon, 1 Dec 2025 12:48:44 +1100 Subject: [PATCH 01/18] test: add arrow operator metadata field verification Adds Rust test equivalent for src/operators/->_test.sql lines 106-118. Verifies that e -> 'selector' returns JSONB with 'i' (index) and 'v' (version) fields. Part of legacy SQL test migration to Rust/SQLx framework. --- .../sqlx/tests/jsonb_path_operators_tests.rs | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/tests/sqlx/tests/jsonb_path_operators_tests.rs b/tests/sqlx/tests/jsonb_path_operators_tests.rs index 93c0a761..0763cccd 100644 --- a/tests/sqlx/tests/jsonb_path_operators_tests.rs +++ b/tests/sqlx/tests/jsonb_path_operators_tests.rs @@ -4,6 +4,7 @@ use anyhow::Result; use eql_tests::{QueryAssertion, Selectors}; +use serde_json; use sqlx::{PgPool, Row}; #[sqlx::test(fixtures(path = "../fixtures", scripts("encrypted_json")))] @@ -93,3 +94,32 @@ async fn double_arrow_in_where_clause(pool: PgPool) -> Result<()> { Ok(()) } + +#[sqlx::test(fixtures(path = "../fixtures", scripts("encrypted_json")))] +async fn arrow_operator_returns_metadata_fields(pool: PgPool) -> Result<()> { + // Test: e -> 'selector' returns JSONB with 'i' (index) and 'v' (version) fields + // SQL equivalent: src/operators/->_test.sql lines 106-118 + + let sql = format!( + "SELECT (e -> '{}'::text)::jsonb FROM encrypted LIMIT 1", + Selectors::N + ); + + let result: serde_json::Value = sqlx::query_scalar(&sql).fetch_one(&pool).await?; + + assert!( + result.is_object(), + "-> operator should return JSONB object" + ); + let obj = result.as_object().unwrap(); + assert!( + obj.contains_key("i"), + "Result should contain 'i' (index metadata) field" + ); + assert!( + obj.contains_key("v"), + "Result should contain 'v' (version) field" + ); + + Ok(()) +} From fabb27039eabaeded543f606c000847d01f69bd1 Mon Sep 17 00:00:00 2001 From: Toby Hede Date: Mon, 1 Dec 2025 13:10:28 +1100 Subject: [PATCH 02/18] refactor(test): address code review feedback for metadata test - Enhance test comments to clearly explain what behavior is verified - Add NOTE explaining why raw SQLx is used instead of QueryAssertion - Replace unwrap() with expect() for clearer error context --- tests/sqlx/tests/jsonb_path_operators_tests.rs | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/tests/sqlx/tests/jsonb_path_operators_tests.rs b/tests/sqlx/tests/jsonb_path_operators_tests.rs index 0763cccd..bdd6df1e 100644 --- a/tests/sqlx/tests/jsonb_path_operators_tests.rs +++ b/tests/sqlx/tests/jsonb_path_operators_tests.rs @@ -97,8 +97,14 @@ async fn double_arrow_in_where_clause(pool: PgPool) -> Result<()> { #[sqlx::test(fixtures(path = "../fixtures", scripts("encrypted_json")))] async fn arrow_operator_returns_metadata_fields(pool: PgPool) -> Result<()> { - // Test: e -> 'selector' returns JSONB with 'i' (index) and 'v' (version) fields + // Test: e -> 'selector' returns JSONB with 'i' (index) and 'v' (version) metadata fields. + // This verifies that the arrow operator returns the full encrypted metadata structure, + // not just the value. The metadata includes the index term ('i') and version ('v'). // SQL equivalent: src/operators/->_test.sql lines 106-118 + // + // NOTE: This test uses raw SQLx instead of QueryAssertion because we need to verify + // specific JSONB field presence. QueryAssertion is designed for row count and basic + // value assertions, but doesn't support introspecting JSONB object structure. let sql = format!( "SELECT (e -> '{}'::text)::jsonb FROM encrypted LIMIT 1", @@ -111,7 +117,9 @@ async fn arrow_operator_returns_metadata_fields(pool: PgPool) -> Result<()> { result.is_object(), "-> operator should return JSONB object" ); - let obj = result.as_object().unwrap(); + let obj = result + .as_object() + .expect("Result should be a JSONB object after is_object() check"); assert!( obj.contains_key("i"), "Result should contain 'i' (index metadata) field" From 6535ea85cfe3985d6dc2a02e7d29a6fa751fba73 Mon Sep 17 00:00:00 2001 From: Toby Hede Date: Mon, 1 Dec 2025 13:13:45 +1100 Subject: [PATCH 03/18] style: fix formatting in metadata test --- tests/sqlx/tests/jsonb_path_operators_tests.rs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/tests/sqlx/tests/jsonb_path_operators_tests.rs b/tests/sqlx/tests/jsonb_path_operators_tests.rs index bdd6df1e..43f3b50f 100644 --- a/tests/sqlx/tests/jsonb_path_operators_tests.rs +++ b/tests/sqlx/tests/jsonb_path_operators_tests.rs @@ -113,10 +113,7 @@ async fn arrow_operator_returns_metadata_fields(pool: PgPool) -> Result<()> { let result: serde_json::Value = sqlx::query_scalar(&sql).fetch_one(&pool).await?; - assert!( - result.is_object(), - "-> operator should return JSONB object" - ); + assert!(result.is_object(), "-> operator should return JSONB object"); let obj = result .as_object() .expect("Result should be a JSONB object after is_object() check"); From 25be30c70e08f5fcf606c3410e68e39902d0adde Mon Sep 17 00:00:00 2001 From: Toby Hede Date: Mon, 1 Dec 2025 13:56:12 +1100 Subject: [PATCH 04/18] 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 --- tests/sqlx/fixtures/aggregate_minmax_data.sql | 37 ++++++++ tests/sqlx/tests/aggregate_tests.rs | 89 ++++++++++++++++++- 2 files changed, 125 insertions(+), 1 deletion(-) create mode 100644 tests/sqlx/fixtures/aggregate_minmax_data.sql diff --git a/tests/sqlx/fixtures/aggregate_minmax_data.sql b/tests/sqlx/fixtures/aggregate_minmax_data.sql new file mode 100644 index 00000000..d9cf7163 --- /dev/null +++ b/tests/sqlx/fixtures/aggregate_minmax_data.sql @@ -0,0 +1,37 @@ +-- Fixture: aggregate_minmax_data.sql +-- Test data for eql_v2.min() and eql_v2.max() aggregate functions +-- +-- Creates table with encrypted integer data including NULL values +-- to test aggregate functions on encrypted columns + +-- Create table +CREATE TABLE agg_test +( + plain_int integer, + enc_int eql_v2_encrypted +); + +-- Add data. These are encrypted values from the SQL test file. +-- Decrypted `enc_int` value is the same as the `plain_int` value in the same row. +INSERT INTO agg_test (plain_int, enc_int) VALUES +( + NULL, + NULL +), +( + 3, + '{"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{&N2hzy", "i": {"c": "encrypted_int4", "t": "encrypted"}, "k": "ct", "bf": null, "ob": ["ccccccccb06565ebd23d6a4c3eee512713175e673c6d995ff5d9b1d3492fe8eb289c3eb95029025f5b71fc6e06632b4a1302980e433361c7999724dbdd052739258d9444b0fbd43cc61368e60f4b0d5aeca2aa85c1c89933b53afffcc4eb0632dca75f632bb9bc792d1dbd6bced6253291f0db134552d384e9e378f4f5890c31ca9d115965a0e8fbf13ad8d1d33f88d360d5e2f9680fb158f98158443ffc769cd9aac94380f05e3226b785f58006e5b9da6b8d86a7441a88fd848099a2400ef59b494b0c30013568dc1be9bba560565fccb49309ba2ec3edcff6f9d7a67b519b3754b37b0025dff7592a6117949a04043c100353289628884fe06cb2099e7b4b49abea9797a73ee0b85283a5b6f69bcf45f87e6cd6d45ecfd1633903270781173ed9d31a682bba0e54ff355f456bf0c468e378e41cb54fcc074ad40fb4448f6fec892c1ecda15a5efffb8dde3a3b282865ac436d7e43d48d4327c439956733697d3f5b02ead4805a7f905bdae24c1b35252e34939676a07ddb5454c3580c7d76d792a97988e35142f43667112432623eda5126e9af2592dd"], "v": 1}'::jsonb::eql_v2_encrypted +), +( + 5, + '{"c": "mBbKSqWLK6yl>o%G%&x+2$jdg7F`-R(^>R1Q^wGod8-FZ5C$xFI4dN?Ap114=77xPZ9!cKxE}qmyXrhx#K`4ztbUrysQrOFqON6bV{&N2hzy", "i": {"c": "encrypted_int4", "t": "encrypted"}, "k": "ct", "bf": null, "ob": ["ccccccccb065659dd23d6a4c3eee512713175e673c6d995ff5d9b1d3492fe8eb289c3eb95029025f5b71fc6e06632b4a1302980e433361c7999724dbdd052739258d9444b0fbd43cc61368e60f4b0d5aeca2aa85c1c89933b53afffcc4eb0632dca75f632bb9bc792d1dbd6bced6253291f0db134552d384bec7bfb23290d7559fd8637b85ca7510cca465570029734ef0319c77177913ad84f54852bed2e2a67b6dafcab3eb70d3a2592414a43acc03703083cf1fa1984dfc0719337d5de4eefd0d137588641a0d38c771b77ab07ebab3fc9bfd7469c4222e1a8edee71188eeb24bfffcd82f711156381d8068223e3d75f5ba8a958182bc46a0ab58c29872cd17e559ed0b935a445249dbac5b51438cebaf9d28d5c8b67cd99f990d5295c1e37470ce5b33fe01eaf31d84c9a08b267c0e9e1aadfcce7f9e2253ababa71eaf1fec309dc988e454717a3c2e3bffb1c546a7195ecf274eb7d691abcf46a61e34d4c63c45d48831dc23aa11f981de692926cd1d1d77a340c9e54baf62da61d5f88960a93e120d3828f4053577b93b536cc9b05c889dcf171865"], "v": 1}'::jsonb::eql_v2_encrypted +), +( + 1, + '{"c": "mBbJSy$p0fHEK%aOAOYi4PTJN7B@a-j{+xl7tffjGTN<-Znt3Zge#lGAX^WHzU`7ml<4vRHLKxoB%}NN2hzy", "i": {"c": "encrypted_int4", "t": "encrypted"}, "k": "ct", "bf": null, "ob": ["ccccccccb0656502d23d6a4c3eee512713175e673c6d995ff5d9b1d3492fe8eb289c3eb95029025f5b71fc6e06632b4a1302980e433361c7999724dbdd052739258d9444b0fbd43cc61368e60f4b0d5aeca2aa85c1c89933b53afffcc4eb0632dca75f632bb9bc792d1dbd6bced6253291f0db134552d384250ca116ef329616ddb341917699b9ea48901124a15a4547be1ff7c672c0c1bc6bb17e2a141f46138fc314f4bf8a55068bf031bc48f038c379e54cfbb1c64eb223c18c87cd68a91fb031905e11d9478f158b561399b527038efc594bfd9fb19c963a2778b75215e1d8933b08df04d1c62742fd48a4de310792031a70ca4b157bc218ab3fbadc6dc14b939422023331c03bcf4b673c5d261a19c3d13155cbaa1b84e9e90e389fa6973dde07fba08c13847006707488e288ce780d59700197452ebc68d22032ab03f7b445e45ed7abb1af34955199440f7db2c969c60b1eb49cdcd75d5e8f7de37848ddebb40df8e14d4b92910e15fedac3f61f22ef430805ba1bbf5fccc9fe792e4c0353beee48ca03ef23c7d3fab19e9aa218aefb44e6c26d70"], "v": 1}'::jsonb::eql_v2_encrypted +), +( + 3, + '{"c": "mBbLa7Cm?&jvpfcv1d3hep>s)76qzUbwUky&M&C3mjDG_os-_y0MRaMGl@&p#AOuusN|3Lu=mBCcg_V{&N2hzy", "i": {"c": "encrypted_int4", "t": "encrypted"}, "k": "ct", "bf": null, "ob": ["ccccccccb06565ebd23d6a4c3eee512713175e673c6d995ff5d9b1d3492fe8eb289c3eb95029025f5b71fc6e06632b4a1302980e433361c7999724dbdd052739258d9444b0fbd43cc61368e60f4b0d5aeca2aa85c1c89933b53afffcc4eb0632dca75f632bb9bc792d1dbd6bced6253291f0db134552d384e9e378f4f5890c31ca9d115965a0e8fb2c3c60ccce84ffc03bddb22b27a1ce278eec118496fd23f083ebb21bb4b83b89eda8c0bdea50debc5ec4f2b2d91b63a80d39386194ad9d129bee2f5168341cb41ed26dc03466cac5e2dbe7336fdb74c0d37d63b396033ce60002c9950f5ac2970dacf4caace2eef5b81544df88a7ef2a8d69550d25d39c678c8e43a3dcc2857018a2c979b45c6b19dabd28ae7388d62916e6742763d6484d1b45154e6c8e6a66e02b03f64b67ddef24747dded32e226e3a93d5d1a92d11e760403cad04a0dd07c14da336a409739e8bbeb3b3d6b92117fa2d2c941da4996ea61b29ca3fffb4594ddbeab7105a1b4c5e422ec5ab8154db545103d8c2889be2e4591198912446d8b33b8708a4cc959a1e0957dcae6a50c3"], "v": 1}'::jsonb::eql_v2_encrypted +) +; diff --git a/tests/sqlx/tests/aggregate_tests.rs b/tests/sqlx/tests/aggregate_tests.rs index 624d0915..10decc1d 100644 --- a/tests/sqlx/tests/aggregate_tests.rs +++ b/tests/sqlx/tests/aggregate_tests.rs @@ -1,6 +1,6 @@ //! Aggregate function tests //! -//! Tests COUNT, MAX, MIN with encrypted data +//! Tests COUNT, MAX, MIN with encrypted data including eql_v2.min() and eql_v2.max() use anyhow::Result; use sqlx::PgPool; @@ -64,3 +64,90 @@ async fn group_by_with_encrypted_column(pool: PgPool) -> Result<()> { Ok(()) } + +// ========== eql_v2.min() and eql_v2.max() Tests ========== +// Source: src/encrypted/aggregates_test.sql + +#[sqlx::test(fixtures(path = "../fixtures", scripts("aggregate_minmax_data")))] +async fn eql_v2_min_with_null_values(pool: PgPool) -> Result<()> { + // Test: eql_v2.min() on NULL encrypted values returns NULL + // Source SQL: ASSERT ((SELECT eql_v2.min(enc_int) FROM agg_test where enc_int IS NULL) IS NULL); + + let result: Option = + sqlx::query_scalar("SELECT eql_v2.min(enc_int)::text FROM agg_test WHERE enc_int IS NULL") + .fetch_one(&pool) + .await?; + + assert!( + result.is_none(), + "eql_v2.min() should return NULL when querying only NULL values" + ); + + Ok(()) +} + +#[sqlx::test(fixtures(path = "../fixtures", scripts("aggregate_minmax_data")))] +async fn eql_v2_min_finds_minimum_encrypted_value(pool: PgPool) -> Result<()> { + // Test: eql_v2.min() finds the minimum encrypted value (plain_int = 1) + // Source SQL: ASSERT ((SELECT enc_int FROM agg_test WHERE plain_int = 1) = (SELECT eql_v2.min(enc_int) FROM agg_test)); + + // Get the expected minimum value (plain_int = 1) + let expected: String = + sqlx::query_scalar("SELECT enc_int::text FROM agg_test WHERE plain_int = 1") + .fetch_one(&pool) + .await?; + + // Get the actual minimum from eql_v2.min() + let actual: String = sqlx::query_scalar("SELECT eql_v2.min(enc_int)::text FROM agg_test") + .fetch_one(&pool) + .await?; + + assert_eq!( + actual, expected, + "eql_v2.min() should return the encrypted value where plain_int = 1 (minimum)" + ); + + Ok(()) +} + +#[sqlx::test(fixtures(path = "../fixtures", scripts("aggregate_minmax_data")))] +async fn eql_v2_max_with_null_values(pool: PgPool) -> Result<()> { + // Test: eql_v2.max() on NULL encrypted values returns NULL + // Source SQL: ASSERT ((SELECT eql_v2.max(enc_int) FROM agg_test where enc_int IS NULL) IS NULL); + + let result: Option = + sqlx::query_scalar("SELECT eql_v2.max(enc_int)::text FROM agg_test WHERE enc_int IS NULL") + .fetch_one(&pool) + .await?; + + assert!( + result.is_none(), + "eql_v2.max() should return NULL when querying only NULL values" + ); + + Ok(()) +} + +#[sqlx::test(fixtures(path = "../fixtures", scripts("aggregate_minmax_data")))] +async fn eql_v2_max_finds_maximum_encrypted_value(pool: PgPool) -> Result<()> { + // Test: eql_v2.max() finds the maximum encrypted value (plain_int = 5) + // Source SQL: ASSERT ((SELECT enc_int FROM agg_test WHERE plain_int = 5) = (SELECT eql_v2.max(enc_int) FROM agg_test)); + + // Get the expected maximum value (plain_int = 5) + let expected: String = + sqlx::query_scalar("SELECT enc_int::text FROM agg_test WHERE plain_int = 5") + .fetch_one(&pool) + .await?; + + // Get the actual maximum from eql_v2.max() + let actual: String = sqlx::query_scalar("SELECT eql_v2.max(enc_int)::text FROM agg_test") + .fetch_one(&pool) + .await?; + + assert_eq!( + actual, expected, + "eql_v2.max() should return the encrypted value where plain_int = 5 (maximum)" + ); + + Ok(()) +} From fa9265b1aefdd52a370bcc0af7e5b248840903ef Mon Sep 17 00:00:00 2001 From: Toby Hede Date: Mon, 1 Dec 2025 14:07:33 +1100 Subject: [PATCH 05/18] test(sqlx): add encrypted column constraint validation tests Add three Rust/SQLx test cases for eql_v2 encrypted column constraint functions: - add_encrypted_constraint_prevents_invalid_data: validates constraint blocks invalid JSONB - remove_encrypted_constraint_allows_invalid_data: validates constraint removal allows invalid data - version_metadata_validation_on_insert: validates v=2 version field enforcement These tests verify the constraint management functions work correctly to prevent insertion of malformed eql_v2_encrypted values. --- tests/sqlx/tests/constraint_tests.rs | 169 +++++++++++++++++++++++++++ 1 file changed, 169 insertions(+) diff --git a/tests/sqlx/tests/constraint_tests.rs b/tests/sqlx/tests/constraint_tests.rs index 15428677..92245a8a 100644 --- a/tests/sqlx/tests/constraint_tests.rs +++ b/tests/sqlx/tests/constraint_tests.rs @@ -221,3 +221,172 @@ async fn foreign_key_constraint_with_encrypted(pool: PgPool) -> Result<()> { Ok(()) } + +// ======================================================================== +// EQL-Specific Constraint Tests (from src/encrypted/constraints_test.sql) +// ======================================================================== + +#[sqlx::test(fixtures(path = "../fixtures", scripts("encrypted_json")))] +async fn add_encrypted_constraint_prevents_invalid_data(pool: PgPool) -> Result<()> { + // Test: eql_v2.add_encrypted_constraint() adds validation to encrypted column + // Source: constraints_test.sql lines 3-21 + + // First, verify that insert without constraint works (even with invalid empty JSONB) + sqlx::query("INSERT INTO encrypted (e) VALUES ('{}'::jsonb::eql_v2_encrypted)") + .execute(&pool) + .await?; + + let count: i64 = sqlx::query_scalar("SELECT COUNT(*) FROM encrypted") + .fetch_one(&pool) + .await?; + + assert_eq!( + count, 4, + "Should have 4 records (3 from fixture + 1 invalid)" + ); + + // Delete the invalid data and reset + sqlx::query("DELETE FROM encrypted WHERE e = '{}'::jsonb::eql_v2_encrypted") + .execute(&pool) + .await?; + + // Add the encrypted constraint + sqlx::query("SELECT eql_v2.add_encrypted_constraint('encrypted', 'e')") + .execute(&pool) + .await?; + + // Now attempt to insert invalid data - should fail + let result = sqlx::query("INSERT INTO encrypted (e) VALUES ('{}'::jsonb::eql_v2_encrypted)") + .execute(&pool) + .await; + + assert!( + result.is_err(), + "Constraint should prevent insert of invalid eql_v2_encrypted (empty JSONB)" + ); + + // Verify count unchanged after failed insert + let final_count: i64 = sqlx::query_scalar("SELECT COUNT(*) FROM encrypted") + .fetch_one(&pool) + .await?; + + assert_eq!( + final_count, 3, + "Should still have 3 records after constraint prevented invalid insert" + ); + + Ok(()) +} + +#[sqlx::test(fixtures(path = "../fixtures", scripts("encrypted_json")))] +async fn remove_encrypted_constraint_allows_invalid_data(pool: PgPool) -> Result<()> { + // Test: eql_v2.remove_encrypted_constraint() removes validation from encrypted column + // Source: constraints_test.sql lines 24-43 + + // Add the encrypted constraint first + sqlx::query("SELECT eql_v2.add_encrypted_constraint('encrypted', 'e')") + .execute(&pool) + .await?; + + // Verify constraint is working - invalid data should be rejected + let result = sqlx::query("INSERT INTO encrypted (e) VALUES ('{}'::jsonb::eql_v2_encrypted)") + .execute(&pool) + .await; + + assert!( + result.is_err(), + "Constraint should prevent insert of invalid eql_v2_encrypted" + ); + + // Remove the constraint + sqlx::query("SELECT eql_v2.remove_encrypted_constraint('encrypted', 'e')") + .execute(&pool) + .await?; + + // Now invalid data should be allowed + sqlx::query("INSERT INTO encrypted (e) VALUES ('{}'::jsonb::eql_v2_encrypted)") + .execute(&pool) + .await?; + + let count: i64 = sqlx::query_scalar("SELECT COUNT(*) FROM encrypted") + .fetch_one(&pool) + .await?; + + assert_eq!( + count, 4, + "Should have 4 records (3 valid + 1 invalid after constraint removed)" + ); + + Ok(()) +} + +#[sqlx::test(fixtures(path = "../fixtures", scripts("encrypted_json")))] +async fn version_metadata_validation_on_insert(pool: PgPool) -> Result<()> { + // Test: EQL version metadata (v field) is enforced on insert + // Source: constraints_test.sql lines 106-138 + // + // Note: The SQL test doesn't explicitly add a constraint, which suggests + // version validation is built into the eql_v2_encrypted type itself or + // is enforced automatically. However, for this test we need to ensure + // the constraint exists to validate version fields. + + // Add encrypted constraint to enable version validation + sqlx::query("SELECT eql_v2.add_encrypted_constraint('encrypted', 'e')") + .execute(&pool) + .await?; + + // Create a valid encrypted value with version removed + // We'll get a valid encrypted JSON and remove the 'v' field + let encrypted_without_version: String = + sqlx::query_scalar("SELECT (create_encrypted_json(1)::jsonb - 'v')::text") + .fetch_one(&pool) + .await?; + + // Attempt to insert without version field - should fail + let result = sqlx::query(&format!( + "INSERT INTO encrypted (e) VALUES ('{}'::jsonb::eql_v2_encrypted)", + encrypted_without_version + )) + .execute(&pool) + .await; + + assert!( + result.is_err(), + "Insert should fail when version field is missing" + ); + + // Create encrypted value with invalid version (v=1 instead of v=2) + let encrypted_invalid_version: String = + sqlx::query_scalar("SELECT (create_encrypted_json(1)::jsonb || '{\"v\": 1}')::text") + .fetch_one(&pool) + .await?; + + // Attempt to insert with invalid version - should fail + let result = sqlx::query(&format!( + "INSERT INTO encrypted (e) VALUES ('{}'::jsonb::eql_v2_encrypted)", + encrypted_invalid_version + )) + .execute(&pool) + .await; + + assert!( + result.is_err(), + "Insert should fail when version field is invalid (v=1)" + ); + + // Insert with valid version (v=2) should succeed + sqlx::query("INSERT INTO encrypted (e) VALUES (create_encrypted_json(1))") + .execute(&pool) + .await?; + + let count: i64 = sqlx::query_scalar("SELECT COUNT(*) FROM encrypted") + .fetch_one(&pool) + .await?; + + assert_eq!( + count, 4, + "Should have 4 records after successful insert with valid version" + ); + + Ok(()) +} From 07e44b1ee00b7ecb0e895355de3eb961ce550fed Mon Sep 17 00:00:00 2001 From: Toby Hede Date: Mon, 1 Dec 2025 14:07:53 +1100 Subject: [PATCH 06/18] test(sqlx): add selector-based ORE comparison tests Add 13 Rust/SQLx test cases for selector-based ORE comparisons with index fallback: - Tests for <, >, <=, >= operators with e->'selector' syntax - Coverage for ore_cllw_u64_8 index type ($.n numeric selector) - Coverage for ore_cllw_var_8 index type ($.hello string selector) - Fallback behavior when requested index type missing Includes new helper function get_ste_vec_selector_term() to extract selector terms using SQL helper functions, matching the pattern from legacy SQL tests. These tests verify that ORE comparisons work correctly with selector extraction and gracefully fall back to JSONB literal comparison when the expected index type is not present. --- tests/sqlx/src/helpers.rs | 85 ++++++ tests/sqlx/src/lib.rs | 5 +- tests/sqlx/tests/comparison_tests.rs | 407 ++++++++++++++++++++++++++- 3 files changed, 495 insertions(+), 2 deletions(-) diff --git a/tests/sqlx/src/helpers.rs b/tests/sqlx/src/helpers.rs index 6335ef73..381ce8f1 100644 --- a/tests/sqlx/src/helpers.rs +++ b/tests/sqlx/src/helpers.rs @@ -82,3 +82,88 @@ pub async fn get_ore_encrypted_as_jsonb(pool: &PgPool, id: i32) -> Result Result { + let sql = format!("SELECT e::text FROM ste_vec WHERE id = {}", id); + let row = sqlx::query(&sql) + .fetch_one(pool) + .await + .with_context(|| format!("fetching ste_vec encrypted value for id={}", id))?; + + let result: Option = row + .try_get(0) + .with_context(|| format!("extracting text column for id={}", id))?; + + result.with_context(|| format!("ste_vec table returned NULL for id={}", id)) +} + +/// Extract selector term using SQL helper functions +/// +/// Uses the get_numeric_ste_vec_*() helper functions to extract a selector term. +/// This matches the SQL test pattern exactly. +/// +/// # Arguments +/// * `pool` - Database connection pool +/// * `value` - Which STE vec value to use (10, 20, 30, or 42) +/// * `selector` - Selector hash to extract (e.g., Selectors::N, Selectors::HELLO) +/// +/// # Example +/// ```ignore +/// // Extract $.n selector from n=30 test data +/// let term = get_ste_vec_selector_term(&pool, 30, Selectors::N).await?; +/// ``` +pub async fn get_ste_vec_selector_term( + pool: &PgPool, + value: i32, + selector: &str, +) -> Result { + // Call the appropriate get_numeric_ste_vec_*() function + let func_name = match value { + 10 => "get_numeric_ste_vec_10", + 20 => "get_numeric_ste_vec_20", + 30 => "get_numeric_ste_vec_30", + 42 => "get_numeric_ste_vec_42", + _ => { + return Err(anyhow::anyhow!( + "Invalid value: {}. Must be 10, 20, 30, or 42", + value + )) + } + }; + + // SQL equivalent: sv := get_numeric_ste_vec_30()::eql_v2_encrypted; + // term := sv->'2517068c0d1f9d4d41d2c666211f785e'::text; + let sql = format!( + "SELECT ({}()::eql_v2_encrypted -> '{}'::text)::text", + func_name, selector + ); + + let row = sqlx::query(&sql).fetch_one(pool).await.with_context(|| { + format!( + "extracting selector '{}' from ste_vec value={}", + selector, value + ) + })?; + + let result: Option = row.try_get(0).with_context(|| { + format!( + "getting text column for selector '{}' from value={}", + selector, value + ) + })?; + + result.with_context(|| { + format!( + "selector extraction returned NULL for selector='{}', value={}", + selector, value + ) + }) +} diff --git a/tests/sqlx/src/lib.rs b/tests/sqlx/src/lib.rs index 6ea784dd..7c81cdd3 100644 --- a/tests/sqlx/src/lib.rs +++ b/tests/sqlx/src/lib.rs @@ -10,7 +10,10 @@ pub mod index_types; pub mod selectors; pub use assertions::QueryAssertion; -pub use helpers::{get_encrypted_term, get_ore_encrypted, get_ore_encrypted_as_jsonb}; +pub use helpers::{ + get_encrypted_term, get_ore_encrypted, get_ore_encrypted_as_jsonb, get_ste_vec_encrypted, + get_ste_vec_selector_term, +}; pub use index_types as IndexTypes; pub use selectors::Selectors; diff --git a/tests/sqlx/tests/comparison_tests.rs b/tests/sqlx/tests/comparison_tests.rs index 12ba9c0e..63add45b 100644 --- a/tests/sqlx/tests/comparison_tests.rs +++ b/tests/sqlx/tests/comparison_tests.rs @@ -3,7 +3,10 @@ //! Tests EQL comparison operators with ORE (Order-Revealing Encryption) use anyhow::{Context, Result}; -use eql_tests::{get_ore_encrypted, get_ore_encrypted_as_jsonb, QueryAssertion}; +use eql_tests::{ + get_ore_encrypted, get_ore_encrypted_as_jsonb, get_ste_vec_selector_term, QueryAssertion, + Selectors, +}; use sqlx::{PgPool, Row}; /// Helper to execute create_encrypted_json SQL function @@ -308,3 +311,405 @@ async fn greater_than_or_equal_jsonb_gte_encrypted(pool: PgPool) -> Result<()> { Ok(()) } + +// ============================================================================ +// Selector-based Comparison Tests +// ============================================================================ +// Tests for extracting subterms with e->'selector' and comparing them +// Covers ore_cllw_u64_8 and ore_cllw_var_8 index types with fallback behavior + +#[sqlx::test] +async fn selector_less_than_with_ore_cllw_u64_8(pool: PgPool) -> Result<()> { + // Test: e->'selector' < term with ore_cllw_u64_8 index + // Source SQL: src/operators/<_test.sql (lines 20-50) + // + // Uses test data created by seed_encrypted_json() helper which creates: + // - Three records with n=10, n=20, n=30 + // - ore_cllw_u64_8 index on $.n selector + + // Create table and seed with test data + sqlx::query("SELECT create_table_with_encrypted()") + .execute(&pool) + .await?; + + sqlx::query("SELECT seed_encrypted_json()") + .execute(&pool) + .await?; + + // Extract $.n selector term from n=30 test data + let term = get_ste_vec_selector_term(&pool, 30, Selectors::N).await?; + + // Query: e->'$.n' < term(30) + // Should return 2 records (n=10 and n=20) + let sql = format!( + "SELECT e FROM encrypted WHERE e->'{}'::text < '{}'::eql_v2_encrypted", + Selectors::N, + term + ); + + QueryAssertion::new(&pool, &sql).count(2).await; + + Ok(()) +} + +#[sqlx::test] +async fn selector_less_than_with_ore_cllw_u64_8_fallback(pool: PgPool) -> Result<()> { + // Test: e->'selector' < term fallback when index missing + // Source SQL: src/operators/<_test.sql (lines 42-48) + // + // Tests that comparison falls back to JSONB literal comparison + // when the requested index type is not present on the selector + + sqlx::query("SELECT create_table_with_encrypted()") + .execute(&pool) + .await?; + + sqlx::query("SELECT seed_encrypted_json()") + .execute(&pool) + .await?; + + // Extract $.n selector term from n=30 test data + let term = get_ste_vec_selector_term(&pool, 30, Selectors::N).await?; + + // Query with $.hello selector (which has ore_cllw_var_8, not ore_cllw_u64_8) + // Falls back to JSONB literal comparison - should still return results + let sql = format!( + "SELECT e FROM encrypted WHERE e->'{}'::text < '{}'::eql_v2_encrypted", + Selectors::HELLO, + term + ); + + QueryAssertion::new(&pool, &sql).returns_rows().await; + + Ok(()) +} + +#[sqlx::test] +async fn selector_less_than_with_ore_cllw_var_8(pool: PgPool) -> Result<()> { + // Test: e->'selector' < term with ore_cllw_var_8 index + // Source SQL: src/operators/<_test.sql (lines 66-95) + // + // STE vec test data has ore_cllw_var_8 on $.hello selector (a7cea93975ed8c01f861ccb6bd082784) + // Extract $.hello from ste_vec id=3 and compare + + sqlx::query("SELECT create_table_with_encrypted()") + .execute(&pool) + .await?; + + sqlx::query("SELECT seed_encrypted_json()") + .execute(&pool) + .await?; + + // Extract $.hello selector term from n=30 test data (corresponds to "three") + let term = get_ste_vec_selector_term(&pool, 30, Selectors::HELLO).await?; + + // Query: e->'$.hello' < term(from ste_vec 3) + // Should return 1 record (ste_vec id=1, since "world 1" < "world 3") + let sql = format!( + "SELECT e FROM encrypted WHERE e->'{}'::text < '{}'::eql_v2_encrypted", + Selectors::HELLO, + term + ); + + QueryAssertion::new(&pool, &sql).count(1).await; + + Ok(()) +} + +#[sqlx::test] +async fn selector_greater_than_with_ore_cllw_u64_8(pool: PgPool) -> Result<()> { + // Test: e->'selector' > term with ore_cllw_u64_8 index + // Source SQL: src/operators/>_test.sql (lines 20-50) + // + // Extract $.n from ste_vec id=2 (n=20 value) and find records > 20 + + sqlx::query("SELECT create_table_with_encrypted()") + .execute(&pool) + .await?; + + sqlx::query("SELECT seed_encrypted_json()") + .execute(&pool) + .await?; + + // Extract $.n selector term from n=20 test data + let term = get_ste_vec_selector_term(&pool, 20, Selectors::N).await?; + + // Query: e->'$.n' > term(20) + // Should return 1 record (n=30) + let sql = format!( + "SELECT e FROM encrypted WHERE e->'{}'::text > '{}'::eql_v2_encrypted", + Selectors::N, + term + ); + + QueryAssertion::new(&pool, &sql).count(1).await; + + Ok(()) +} + +#[sqlx::test] +async fn selector_greater_than_with_ore_cllw_u64_8_fallback(pool: PgPool) -> Result<()> { + // Test: e->'selector' > term fallback when index missing + // Source SQL: src/operators/>_test.sql (lines 42-48) + + sqlx::query("SELECT create_table_with_encrypted()") + .execute(&pool) + .await?; + + sqlx::query("SELECT seed_encrypted_json()") + .execute(&pool) + .await?; + + // Extract $.n selector term from n=20 test data + let term = get_ste_vec_selector_term(&pool, 20, Selectors::N).await?; + + // Query with $.hello selector (falls back to JSONB comparison) + let sql = format!( + "SELECT e FROM encrypted WHERE e->'{}'::text > '{}'::eql_v2_encrypted", + Selectors::HELLO, + term + ); + + QueryAssertion::new(&pool, &sql).returns_rows().await; + + Ok(()) +} + +#[sqlx::test] +async fn selector_greater_than_with_ore_cllw_var_8(pool: PgPool) -> Result<()> { + // Test: e->'selector' > term with ore_cllw_var_8 index + // Source SQL: src/operators/>_test.sql (lines 66-96) + + sqlx::query("SELECT create_table_with_encrypted()") + .execute(&pool) + .await?; + + sqlx::query("SELECT seed_encrypted_json()") + .execute(&pool) + .await?; + + // Extract $.hello selector term from n=30 test data (corresponds to "three") + let term = get_ste_vec_selector_term(&pool, 30, Selectors::HELLO).await?; + + // Query: e->'$.hello' > term + // Should return 1 record + let sql = format!( + "SELECT e FROM encrypted WHERE e->'{}'::text > '{}'::eql_v2_encrypted", + Selectors::HELLO, + term + ); + + QueryAssertion::new(&pool, &sql).count(1).await; + + Ok(()) +} + +#[sqlx::test] +async fn selector_greater_than_with_ore_cllw_var_8_fallback(pool: PgPool) -> Result<()> { + // Test: e->'selector' > term fallback to JSONB comparison + // Source SQL: src/operators/>_test.sql (lines 88-94) + // + // Tests fallback when selector doesn't have ore_cllw_var_8 + + sqlx::query("SELECT create_table_with_encrypted()") + .execute(&pool) + .await?; + + sqlx::query("SELECT seed_encrypted_json()") + .execute(&pool) + .await?; + + // Extract $.hello selector term from n=30 test data + let term = get_ste_vec_selector_term(&pool, 30, Selectors::HELLO).await?; + + // Query with $.n selector (which has ore_cllw_u64_8, not ore_cllw_var_8) + // Falls back to JSONB literal comparison - should not return results + // because the extracted term is a string selector and $.n expects numeric + let sql = format!( + "SELECT e FROM encrypted WHERE e->'{}'::text > '{}'::eql_v2_encrypted", + Selectors::N, + term + ); + + // The fallback query succeeds but returns no results due to type mismatch + QueryAssertion::new(&pool, &sql).count(0).await; + + Ok(()) +} + +#[sqlx::test] +async fn selector_less_than_or_equal_with_ore_cllw_u64_8(pool: PgPool) -> Result<()> { + // Test: e->'selector' <= term with ore_cllw_u64_8 index + // Source SQL: src/operators/<=_ore_cllw_u64_8_test.sql (lines 23-53) + // + // Extract $.n from ste_vec id=2 (n=20) and find records <= 20 + + sqlx::query("SELECT create_table_with_encrypted()") + .execute(&pool) + .await?; + + sqlx::query("SELECT seed_encrypted_json()") + .execute(&pool) + .await?; + + // Extract $.n selector term from n=20 test data + let term = get_ste_vec_selector_term(&pool, 20, Selectors::N).await?; + + // Query: e->'$.n' <= term(20) + // Should return 2 records (n=10 and n=20) + let sql = format!( + "SELECT e FROM encrypted WHERE e->'{}'::text <= '{}'::eql_v2_encrypted", + Selectors::N, + term + ); + + QueryAssertion::new(&pool, &sql).count(2).await; + + Ok(()) +} + +#[sqlx::test] +async fn selector_less_than_or_equal_with_ore_cllw_u64_8_fallback(pool: PgPool) -> Result<()> { + // Test: e->'selector' <= term fallback when index missing + // Source SQL: src/operators/<=_ore_cllw_u64_8_test.sql (lines 45-51) + + sqlx::query("SELECT create_table_with_encrypted()") + .execute(&pool) + .await?; + + sqlx::query("SELECT seed_encrypted_json()") + .execute(&pool) + .await?; + + // Extract $.n selector term from n=20 test data + let term = get_ste_vec_selector_term(&pool, 20, Selectors::N).await?; + + // Query with $.hello selector (falls back to JSONB comparison) + // The extracted term is numeric but $.hello selector expects string + let sql = format!( + "SELECT e FROM encrypted WHERE e->'{}'::text <= '{}'::eql_v2_encrypted", + Selectors::HELLO, + term + ); + + // SQL test behavior: fallback succeeds but returns no results due to type mismatch + QueryAssertion::new(&pool, &sql).count(0).await; + + Ok(()) +} + +#[sqlx::test] +async fn selector_greater_than_or_equal_with_ore_cllw_u64_8(pool: PgPool) -> Result<()> { + // Test: e->'selector' >= term with ore_cllw_u64_8 index + // Source SQL: src/operators/>=_test.sql (lines 20-50) + // + // Extract $.n from ste_vec id=1 (n=10) and find records >= 10 + + sqlx::query("SELECT create_table_with_encrypted()") + .execute(&pool) + .await?; + + sqlx::query("SELECT seed_encrypted_json()") + .execute(&pool) + .await?; + + // Extract $.n selector term from n=10 test data + let term = get_ste_vec_selector_term(&pool, 10, Selectors::N).await?; + + // Query: e->'$.n' >= term(10) + // Should return 3 records (n=10, n=20, n=30) + let sql = format!( + "SELECT e FROM encrypted WHERE e->'{}'::text >= '{}'::eql_v2_encrypted", + Selectors::N, + term + ); + + QueryAssertion::new(&pool, &sql).count(3).await; + + Ok(()) +} + +#[sqlx::test] +async fn selector_greater_than_or_equal_with_ore_cllw_u64_8_fallback(pool: PgPool) -> Result<()> { + // Test: e->'selector' >= term fallback when index missing + // Source SQL: src/operators/>=_test.sql (lines 42-48) + + sqlx::query("SELECT create_table_with_encrypted()") + .execute(&pool) + .await?; + + sqlx::query("SELECT seed_encrypted_json()") + .execute(&pool) + .await?; + + // Extract $.n selector term from n=10 test data + let term = get_ste_vec_selector_term(&pool, 10, Selectors::N).await?; + + // Query with $.hello selector (falls back to JSONB comparison) + let sql = format!( + "SELECT e FROM encrypted WHERE e->'{}'::text >= '{}'::eql_v2_encrypted", + Selectors::HELLO, + term + ); + + QueryAssertion::new(&pool, &sql).returns_rows().await; + + Ok(()) +} + +#[sqlx::test] +async fn selector_greater_than_or_equal_with_ore_cllw_var_8(pool: PgPool) -> Result<()> { + // Test: e->'selector' >= term with ore_cllw_var_8 index + // Source SQL: src/operators/>=_test.sql (lines 66-96) + + sqlx::query("SELECT create_table_with_encrypted()") + .execute(&pool) + .await?; + + sqlx::query("SELECT seed_encrypted_json()") + .execute(&pool) + .await?; + + // Extract $.hello selector term from n=10 test data (corresponds to "one") + let term = get_ste_vec_selector_term(&pool, 10, Selectors::HELLO).await?; + + // Query: e->'$.hello' >= term + // Should return 3 records (all have "world X" >= "world 1") + let sql = format!( + "SELECT e FROM encrypted WHERE e->'{}'::text >= '{}'::eql_v2_encrypted", + Selectors::HELLO, + term + ); + + QueryAssertion::new(&pool, &sql).count(3).await; + + Ok(()) +} + +#[sqlx::test] +async fn selector_greater_than_or_equal_with_ore_cllw_var_8_fallback(pool: PgPool) -> Result<()> { + // Test: e->'selector' >= term fallback to JSONB comparison + // Source SQL: src/operators/>=_test.sql (lines 88-94) + + sqlx::query("SELECT create_table_with_encrypted()") + .execute(&pool) + .await?; + + sqlx::query("SELECT seed_encrypted_json()") + .execute(&pool) + .await?; + + // Extract $.hello selector term from n=10 test data + let term = get_ste_vec_selector_term(&pool, 10, Selectors::HELLO).await?; + + // Query with $.n selector (falls back to JSONB comparison) + let sql = format!( + "SELECT e FROM encrypted WHERE e->'{}'::text >= '{}'::eql_v2_encrypted", + Selectors::N, + term + ); + + QueryAssertion::new(&pool, &sql).returns_rows().await; + + Ok(()) +} From 9ec9f2de7a5a199d0d1c16a5281a16491be6a935 Mon Sep 17 00:00:00 2001 From: Toby Hede Date: Mon, 1 Dec 2025 14:16:27 +1100 Subject: [PATCH 07/18] test(sqlx): add ciphertext extraction and encrypted selector tests Add test coverage for JSONB path operators with encrypted selectors: - eql_v2.ciphertext() extraction from arrow operator results - -> and ->> operators with eql_v2_encrypted selectors Tests verify ciphertext extraction functionality and encrypted selector handling across both single-row and multi-row scenarios. --- .../sqlx/tests/jsonb_path_operators_tests.rs | 106 ++++++++++++++++++ 1 file changed, 106 insertions(+) diff --git a/tests/sqlx/tests/jsonb_path_operators_tests.rs b/tests/sqlx/tests/jsonb_path_operators_tests.rs index 43f3b50f..c20eaf34 100644 --- a/tests/sqlx/tests/jsonb_path_operators_tests.rs +++ b/tests/sqlx/tests/jsonb_path_operators_tests.rs @@ -128,3 +128,109 @@ async fn arrow_operator_returns_metadata_fields(pool: PgPool) -> Result<()> { Ok(()) } + +#[sqlx::test(fixtures(path = "../fixtures", scripts("encrypted_json")))] +async fn ciphertext_function_extracts_from_arrow_result(pool: PgPool) -> Result<()> { + // Test: eql_v2.ciphertext(e -> 'selector') extracts ciphertext value + // SQL equivalent: src/operators/->_test.sql lines 60-75 + // + // The ciphertext() function extracts the 'c' field from the encrypted JSONB structure. + // When combined with the -> operator, it allows extracting ciphertext from nested paths. + + let sql = format!( + "SELECT eql_v2.ciphertext(e -> '{}'::text) FROM encrypted LIMIT 1", + Selectors::N + ); + + // Should return ciphertext value (a text string) + QueryAssertion::new(&pool, &sql).returns_rows().await; + + Ok(()) +} + +#[sqlx::test(fixtures(path = "../fixtures", scripts("encrypted_json")))] +async fn ciphertext_function_returns_all_rows(pool: PgPool) -> Result<()> { + // Test: eql_v2.ciphertext() returns ciphertext for all encrypted rows + // SQL equivalent: src/operators/->_test.sql lines 70-73 + + let sql = format!( + "SELECT eql_v2.ciphertext(e -> '{}'::text) FROM encrypted", + Selectors::N + ); + + // All 3 records have $.n path, should return 3 ciphertext values + QueryAssertion::new(&pool, &sql).count(3).await; + + Ok(()) +} + +#[sqlx::test(fixtures(path = "../fixtures", scripts("encrypted_json")))] +async fn arrow_operator_with_encrypted_selector(pool: PgPool) -> Result<()> { + // Test: e -> eql_v2_encrypted selector (encrypted selector) + // SQL equivalent: src/operators/->_test.sql lines 39-56 + // + // The -> operator can accept an eql_v2_encrypted value as the selector. + // The selector is created from JSONB with structure: {"s": "selector_hash"} + + let encrypted_selector = Selectors::as_encrypted(Selectors::ROOT); + let sql = format!( + "SELECT e -> '{}'::jsonb::eql_v2_encrypted FROM encrypted LIMIT 1", + encrypted_selector + ); + + QueryAssertion::new(&pool, &sql).returns_rows().await; + + Ok(()) +} + +#[sqlx::test(fixtures(path = "../fixtures", scripts("encrypted_json")))] +async fn arrow_operator_with_encrypted_selector_all_rows(pool: PgPool) -> Result<()> { + // Test: e -> eql_v2_encrypted selector returns all matching rows + // SQL equivalent: src/operators/->_test.sql lines 51-54 + + let encrypted_selector = Selectors::as_encrypted(Selectors::ROOT); + let sql = format!( + "SELECT e -> '{}'::jsonb::eql_v2_encrypted FROM encrypted", + encrypted_selector + ); + + // All 3 records should have the root selector + QueryAssertion::new(&pool, &sql).count(3).await; + + Ok(()) +} + +#[sqlx::test(fixtures(path = "../fixtures", scripts("encrypted_json")))] +async fn double_arrow_operator_with_encrypted_selector(pool: PgPool) -> Result<()> { + // Test: e ->> eql_v2_encrypted selector (encrypted selector) + // SQL equivalent: src/operators/->>_test.sql lines 50-67 + // + // The ->> operator can also accept an eql_v2_encrypted value as the selector. + + let encrypted_selector = Selectors::as_encrypted(Selectors::ROOT); + let sql = format!( + "SELECT e ->> '{}'::jsonb::eql_v2_encrypted FROM encrypted LIMIT 1", + encrypted_selector + ); + + QueryAssertion::new(&pool, &sql).returns_rows().await; + + Ok(()) +} + +#[sqlx::test(fixtures(path = "../fixtures", scripts("encrypted_json")))] +async fn double_arrow_operator_with_encrypted_selector_all_rows(pool: PgPool) -> Result<()> { + // Test: e ->> eql_v2_encrypted selector returns all matching rows + // SQL equivalent: src/operators/->>_test.sql lines 62-65 + + let encrypted_selector = Selectors::as_encrypted(Selectors::ROOT); + let sql = format!( + "SELECT e ->> '{}'::jsonb::eql_v2_encrypted FROM encrypted", + encrypted_selector + ); + + // All 3 records should have the root selector + QueryAssertion::new(&pool, &sql).count(3).await; + + Ok(()) +} From f889aeaa0c883279ab7c94c947767700b0b41f7e Mon Sep 17 00:00:00 2001 From: Toby Hede Date: Mon, 1 Dec 2025 14:16:40 +1100 Subject: [PATCH 08/18] test(sqlx): add ORDER BY NULL handling and helper function tests Add test coverage for ORDER BY edge cases and helper function: - NULLS FIRST/LAST behavior in ASC and DESC ordering - eql_v2.order_by() helper function with filtered result sets Tests verify correct NULL positioning in ordered results and proper functioning of the order_by helper across different sort directions. --- tests/sqlx/tests/order_by_tests.rs | 269 +++++++++++++++++++++++++++++ 1 file changed, 269 insertions(+) diff --git a/tests/sqlx/tests/order_by_tests.rs b/tests/sqlx/tests/order_by_tests.rs index d27b1828..1bc594ba 100644 --- a/tests/sqlx/tests/order_by_tests.rs +++ b/tests/sqlx/tests/order_by_tests.rs @@ -118,3 +118,272 @@ async fn order_by_asc_with_greater_than_returns_lowest(pool: PgPool) -> Result<( Ok(()) } + +// NULL ordering tests - replicating SQL test cases from order_by_test.sql lines 93-116 + +#[sqlx::test] +async fn order_by_asc_nulls_first_returns_null_record_first(pool: PgPool) -> Result<()> { + // Test: ORDER BY e ASC NULLS FIRST returns NULL values first + // Setup: Create table with NULLs and encrypted values + // - ID=1: NULL + // - ID=2: 42 + // - ID=3: 3 + // - ID=4: NULL + // Expected: ORDER BY e ASC NULLS FIRST returns id=1 first + + // Create test table + sqlx::query("CREATE TABLE encrypted(id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY, e eql_v2_encrypted)") + .execute(&pool) + .await?; + + // Insert NULL + sqlx::query("INSERT INTO encrypted(e) VALUES (NULL::jsonb::eql_v2_encrypted)") + .execute(&pool) + .await?; + + // Insert id=42 from ore table + let ore_42 = get_ore_encrypted(&pool, 42).await?; + sqlx::query(&format!( + "INSERT INTO encrypted(e) VALUES ('{}'::eql_v2_encrypted)", + ore_42 + )) + .execute(&pool) + .await?; + + // Insert id=3 from ore table + let ore_3 = get_ore_encrypted(&pool, 3).await?; + sqlx::query(&format!( + "INSERT INTO encrypted(e) VALUES ('{}'::eql_v2_encrypted)", + ore_3 + )) + .execute(&pool) + .await?; + + // Insert another NULL + sqlx::query("INSERT INTO encrypted(e) VALUES (NULL::jsonb::eql_v2_encrypted)") + .execute(&pool) + .await?; + + // Test: NULLS FIRST should return id=1 + let sql = "SELECT id FROM encrypted ORDER BY e ASC NULLS FIRST"; + let row = sqlx::query(sql).fetch_one(&pool).await?; + let first_id: i64 = row.try_get(0)?; + assert_eq!( + first_id, 1, + "ORDER BY e ASC NULLS FIRST should return NULL value (id=1) first" + ); + + Ok(()) +} + +#[sqlx::test] +async fn order_by_asc_nulls_last_returns_smallest_value_first(pool: PgPool) -> Result<()> { + // Test: ORDER BY e ASC NULLS LAST returns smallest non-NULL value first + // Setup: Same as previous test + // Expected: ORDER BY e ASC NULLS LAST returns id=3 (value=3) first + + // Create test table + sqlx::query("CREATE TABLE encrypted(id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY, e eql_v2_encrypted)") + .execute(&pool) + .await?; + + // Insert NULL + sqlx::query("INSERT INTO encrypted(e) VALUES (NULL::jsonb::eql_v2_encrypted)") + .execute(&pool) + .await?; + + // Insert id=42 from ore table + let ore_42 = get_ore_encrypted(&pool, 42).await?; + sqlx::query(&format!( + "INSERT INTO encrypted(e) VALUES ('{}'::eql_v2_encrypted)", + ore_42 + )) + .execute(&pool) + .await?; + + // Insert id=3 from ore table + let ore_3 = get_ore_encrypted(&pool, 3).await?; + sqlx::query(&format!( + "INSERT INTO encrypted(e) VALUES ('{}'::eql_v2_encrypted)", + ore_3 + )) + .execute(&pool) + .await?; + + // Insert another NULL + sqlx::query("INSERT INTO encrypted(e) VALUES (NULL::jsonb::eql_v2_encrypted)") + .execute(&pool) + .await?; + + // Test: NULLS LAST should return id=3 (smallest value) + let sql = "SELECT id FROM encrypted ORDER BY e ASC NULLS LAST"; + let row = sqlx::query(sql).fetch_one(&pool).await?; + let first_id: i64 = row.try_get(0)?; + assert_eq!( + first_id, 3, + "ORDER BY e ASC NULLS LAST should return smallest non-NULL value (id=3) first" + ); + + Ok(()) +} + +#[sqlx::test] +async fn order_by_desc_nulls_first_returns_null_value_first(pool: PgPool) -> Result<()> { + // Test: ORDER BY e DESC NULLS FIRST returns NULL values first + // Expected: ORDER BY e DESC NULLS FIRST returns id=1 first + + // Create test table + sqlx::query("CREATE TABLE encrypted(id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY, e eql_v2_encrypted)") + .execute(&pool) + .await?; + + // Insert NULL + sqlx::query("INSERT INTO encrypted(e) VALUES (NULL::jsonb::eql_v2_encrypted)") + .execute(&pool) + .await?; + + // Insert id=42 from ore table + let ore_42 = get_ore_encrypted(&pool, 42).await?; + sqlx::query(&format!( + "INSERT INTO encrypted(e) VALUES ('{}'::eql_v2_encrypted)", + ore_42 + )) + .execute(&pool) + .await?; + + // Insert id=3 from ore table + let ore_3 = get_ore_encrypted(&pool, 3).await?; + sqlx::query(&format!( + "INSERT INTO encrypted(e) VALUES ('{}'::eql_v2_encrypted)", + ore_3 + )) + .execute(&pool) + .await?; + + // Insert another NULL + sqlx::query("INSERT INTO encrypted(e) VALUES (NULL::jsonb::eql_v2_encrypted)") + .execute(&pool) + .await?; + + // Test: DESC NULLS FIRST should return id=1 + let sql = "SELECT id FROM encrypted ORDER BY e DESC NULLS FIRST"; + let row = sqlx::query(sql).fetch_one(&pool).await?; + let first_id: i64 = row.try_get(0)?; + assert_eq!( + first_id, 1, + "ORDER BY e DESC NULLS FIRST should return NULL value (id=1) first" + ); + + Ok(()) +} + +#[sqlx::test] +async fn order_by_desc_nulls_last_returns_largest_value_first(pool: PgPool) -> Result<()> { + // Test: ORDER BY e DESC NULLS LAST returns largest non-NULL value first + // Expected: ORDER BY e DESC NULLS LAST returns id=2 (value=42) first + + // Create test table + sqlx::query("CREATE TABLE encrypted(id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY, e eql_v2_encrypted)") + .execute(&pool) + .await?; + + // Insert NULL + sqlx::query("INSERT INTO encrypted(e) VALUES (NULL::jsonb::eql_v2_encrypted)") + .execute(&pool) + .await?; + + // Insert id=42 from ore table + let ore_42 = get_ore_encrypted(&pool, 42).await?; + sqlx::query(&format!( + "INSERT INTO encrypted(e) VALUES ('{}'::eql_v2_encrypted)", + ore_42 + )) + .execute(&pool) + .await?; + + // Insert id=3 from ore table + let ore_3 = get_ore_encrypted(&pool, 3).await?; + sqlx::query(&format!( + "INSERT INTO encrypted(e) VALUES ('{}'::eql_v2_encrypted)", + ore_3 + )) + .execute(&pool) + .await?; + + // Insert another NULL + sqlx::query("INSERT INTO encrypted(e) VALUES (NULL::jsonb::eql_v2_encrypted)") + .execute(&pool) + .await?; + + // Test: DESC NULLS LAST should return id=2 (largest value) + let sql = "SELECT id FROM encrypted ORDER BY e DESC NULLS LAST"; + let row = sqlx::query(sql).fetch_one(&pool).await?; + let first_id: i64 = row.try_get(0)?; + assert_eq!( + first_id, 2, + "ORDER BY e DESC NULLS LAST should return largest non-NULL value (id=2) first" + ); + + Ok(()) +} + +// eql_v2.order_by() helper function tests - from order_by_test.sql lines 133-144 + +#[sqlx::test] +async fn order_by_helper_function_desc_returns_correct_count(pool: PgPool) -> Result<()> { + // Test: ORDER BY eql_v2.order_by(e) DESC with WHERE e < 42 + // Expected: Returns 41 records + + let ore_term = get_ore_encrypted(&pool, 42).await?; + + let sql = format!( + "SELECT id FROM ore WHERE e < '{}'::eql_v2_encrypted ORDER BY eql_v2.order_by(e) DESC", + ore_term + ); + + QueryAssertion::new(&pool, &sql).count(41).await; + + Ok(()) +} + +#[sqlx::test] +async fn order_by_helper_function_desc_returns_highest_value_first(pool: PgPool) -> Result<()> { + // Test: ORDER BY eql_v2.order_by(e) DESC LIMIT 1 returns id=41 + + let ore_term = get_ore_encrypted(&pool, 42).await?; + + let sql = format!( + "SELECT id FROM ore WHERE e < '{}'::eql_v2_encrypted ORDER BY eql_v2.order_by(e) DESC LIMIT 1", + ore_term + ); + + let row = sqlx::query(&sql).fetch_one(&pool).await?; + let id: i64 = row.try_get(0)?; + assert_eq!( + id, 41, + "ORDER BY eql_v2.order_by(e) DESC should return id=41 (highest value < 42) first" + ); + + Ok(()) +} + +#[sqlx::test] +async fn order_by_helper_function_asc_returns_lowest_value_first(pool: PgPool) -> Result<()> { + // Test: ORDER BY eql_v2.order_by(e) ASC LIMIT 1 returns id=1 + + let ore_term = get_ore_encrypted(&pool, 42).await?; + + let sql = format!( + "SELECT id FROM ore WHERE e < '{}'::eql_v2_encrypted ORDER BY eql_v2.order_by(e) ASC LIMIT 1", + ore_term + ); + + let row = sqlx::query(&sql).fetch_one(&pool).await?; + let id: i64 = row.try_get(0)?; + assert_eq!( + id, 1, + "ORDER BY eql_v2.order_by(e) ASC should return id=1 (lowest value < 42) first" + ); + + Ok(()) +} From ffb06a85499357d6ae3257cb07277d2d2f4717c4 Mon Sep 17 00:00:00 2001 From: Toby Hede Date: Mon, 1 Dec 2025 14:29:44 +1100 Subject: [PATCH 09/18] fix(test): make aggregate fixture idempotent Add DROP TABLE IF EXISTS before CREATE TABLE to allow fixture to be executed multiple times without 'relation already exists' error. --- tests/sqlx/fixtures/aggregate_minmax_data.sql | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/sqlx/fixtures/aggregate_minmax_data.sql b/tests/sqlx/fixtures/aggregate_minmax_data.sql index d9cf7163..c635f88d 100644 --- a/tests/sqlx/fixtures/aggregate_minmax_data.sql +++ b/tests/sqlx/fixtures/aggregate_minmax_data.sql @@ -4,7 +4,8 @@ -- Creates table with encrypted integer data including NULL values -- to test aggregate functions on encrypted columns --- Create table +-- Create table (drop first for idempotency) +DROP TABLE IF EXISTS agg_test; CREATE TABLE agg_test ( plain_int integer, From d29f44ebd201c02d70bd611f3dddd0f69a073f2a Mon Sep 17 00:00:00 2001 From: Toby Hede Date: Mon, 1 Dec 2025 14:37:56 +1100 Subject: [PATCH 10/18] fix(test): add tie-breaker to NULL ordering tests Add secondary sort by id to ensure deterministic ordering among NULL rows. SQL standard only guarantees NULLs precede/follow non-NULLs, not ordering among equal sort keys. Without tie-breaker, tests could flake depending on planner behavior. --- tests/sqlx/tests/order_by_tests.rs | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/tests/sqlx/tests/order_by_tests.rs b/tests/sqlx/tests/order_by_tests.rs index 1bc594ba..664ad285 100644 --- a/tests/sqlx/tests/order_by_tests.rs +++ b/tests/sqlx/tests/order_by_tests.rs @@ -164,13 +164,14 @@ async fn order_by_asc_nulls_first_returns_null_record_first(pool: PgPool) -> Res .execute(&pool) .await?; - // Test: NULLS FIRST should return id=1 - let sql = "SELECT id FROM encrypted ORDER BY e ASC NULLS FIRST"; + // Test: NULLS FIRST should return a NULL row first + // Use tie-breaker (id) to ensure deterministic ordering among NULL rows + let sql = "SELECT id FROM encrypted ORDER BY e ASC NULLS FIRST, id"; let row = sqlx::query(sql).fetch_one(&pool).await?; let first_id: i64 = row.try_get(0)?; assert_eq!( first_id, 1, - "ORDER BY e ASC NULLS FIRST should return NULL value (id=1) first" + "ORDER BY e ASC NULLS FIRST, id should return NULL value with lowest id (id=1) first" ); Ok(()) @@ -265,13 +266,14 @@ async fn order_by_desc_nulls_first_returns_null_value_first(pool: PgPool) -> Res .execute(&pool) .await?; - // Test: DESC NULLS FIRST should return id=1 - let sql = "SELECT id FROM encrypted ORDER BY e DESC NULLS FIRST"; + // Test: DESC NULLS FIRST should return a NULL row first + // Use tie-breaker (id) to ensure deterministic ordering among NULL rows + let sql = "SELECT id FROM encrypted ORDER BY e DESC NULLS FIRST, id"; let row = sqlx::query(sql).fetch_one(&pool).await?; let first_id: i64 = row.try_get(0)?; assert_eq!( first_id, 1, - "ORDER BY e DESC NULLS FIRST should return NULL value (id=1) first" + "ORDER BY e DESC NULLS FIRST, id should return NULL value with lowest id (id=1) first" ); Ok(()) From c8b4f36c94b6cd53c772bbffed3e6a9ceec6c140 Mon Sep 17 00:00:00 2001 From: Toby Hede Date: Mon, 1 Dec 2025 15:20:21 +1100 Subject: [PATCH 11/18] chore: remove legacy SQL test files Remove 38 inline SQL test files from src/ directory. These tests have been migrated to the SQLx Rust test framework in tests/sqlx/. --- src/blake3/compare_test.sql | 26 -- src/bloom_filter/functions_test.sql | 14 - src/config/config_test.sql | 349 --------------------- src/encrypted/aggregates_test.sql | 50 --- src/encrypted/constraints_test.sql | 139 -------- src/encryptindex/functions_test.sql | 290 ----------------- src/hmac_256/compare_test.sql | 26 -- src/hmac_256/functions_test.sql | 26 -- src/jsonb/functions_test.sql | 338 -------------------- src/operators/->>_test.sql | 68 ---- src/operators/->_test.sql | 118 ------- src/operators/<=_ore_cllw_u64_8_test.sql | 57 ---- src/operators/<=_ore_cllw_var_8_test.sql | 53 ---- src/operators/<=_test.sql | 84 ----- src/operators/<>_ore_cllw_u64_8_test.sql | 57 ---- src/operators/<>_ore_cllw_var_8_test.sql | 56 ---- src/operators/<>_ore_test.sql | 87 ----- src/operators/<>_test.sql | 165 ---------- src/operators/<@_test.sql | 44 --- src/operators/<_test.sql | 159 ---------- src/operators/=_ore_cllw_u64_8_test.sql | 56 ---- src/operators/=_ore_cllw_var_8_test.sql | 53 ---- src/operators/=_ore_test.sql | 87 ----- src/operators/=_test.sql | 196 ------------ src/operators/>=_test.sql | 175 ----------- src/operators/>_test.sql | 159 ---------- src/operators/@>_test.sql | 94 ------ src/operators/compare_test.sql | 207 ------------ src/operators/operator_class_test.sql | 239 -------------- src/operators/order_by_test.sql | 149 --------- src/operators/~~_test.sql | 108 ------- src/ore_block_u64_8_256/compare_test.sql | 27 -- src/ore_block_u64_8_256/functions_test.sql | 58 ---- src/ore_cllw_u64_8/compare_test.sql | 29 -- src/ore_cllw_var_8/compare_test.sql | 29 -- src/ore_cllw_var_8/functions_test.sql | 0 src/ste_vec/functions_test.sql | 132 -------- src/version_test.sql | 9 - 38 files changed, 4013 deletions(-) delete mode 100644 src/blake3/compare_test.sql delete mode 100644 src/bloom_filter/functions_test.sql delete mode 100644 src/config/config_test.sql delete mode 100644 src/encrypted/aggregates_test.sql delete mode 100644 src/encrypted/constraints_test.sql delete mode 100644 src/encryptindex/functions_test.sql delete mode 100644 src/hmac_256/compare_test.sql delete mode 100644 src/hmac_256/functions_test.sql delete mode 100644 src/jsonb/functions_test.sql delete mode 100644 src/operators/->>_test.sql delete mode 100644 src/operators/->_test.sql delete mode 100644 src/operators/<=_ore_cllw_u64_8_test.sql delete mode 100644 src/operators/<=_ore_cllw_var_8_test.sql delete mode 100644 src/operators/<=_test.sql delete mode 100644 src/operators/<>_ore_cllw_u64_8_test.sql delete mode 100644 src/operators/<>_ore_cllw_var_8_test.sql delete mode 100644 src/operators/<>_ore_test.sql delete mode 100644 src/operators/<>_test.sql delete mode 100644 src/operators/<@_test.sql delete mode 100644 src/operators/<_test.sql delete mode 100644 src/operators/=_ore_cllw_u64_8_test.sql delete mode 100644 src/operators/=_ore_cllw_var_8_test.sql delete mode 100644 src/operators/=_ore_test.sql delete mode 100644 src/operators/=_test.sql delete mode 100644 src/operators/>=_test.sql delete mode 100644 src/operators/>_test.sql delete mode 100644 src/operators/@>_test.sql delete mode 100644 src/operators/compare_test.sql delete mode 100644 src/operators/operator_class_test.sql delete mode 100644 src/operators/order_by_test.sql delete mode 100644 src/operators/~~_test.sql delete mode 100644 src/ore_block_u64_8_256/compare_test.sql delete mode 100644 src/ore_block_u64_8_256/functions_test.sql delete mode 100644 src/ore_cllw_u64_8/compare_test.sql delete mode 100644 src/ore_cllw_var_8/compare_test.sql delete mode 100644 src/ore_cllw_var_8/functions_test.sql delete mode 100644 src/ste_vec/functions_test.sql delete mode 100644 src/version_test.sql diff --git a/src/blake3/compare_test.sql b/src/blake3/compare_test.sql deleted file mode 100644 index b6a49de7..00000000 --- a/src/blake3/compare_test.sql +++ /dev/null @@ -1,26 +0,0 @@ -\set ON_ERROR_STOP on - -DO $$ - DECLARE - a eql_v2_encrypted; - b eql_v2_encrypted; - c eql_v2_encrypted; - BEGIN - a := create_encrypted_json(1, 'b3'); - b := create_encrypted_json(2, 'b3'); - c := create_encrypted_json(3, 'b3'); - - ASSERT eql_v2.compare_blake3(a, a) = 0; - ASSERT eql_v2.compare_blake3(a, b) = -1; - ASSERT eql_v2.compare_blake3(a, c) = -1; - - ASSERT eql_v2.compare_blake3(b, b) = 0; - ASSERT eql_v2.compare_blake3(b, a) = 1; - ASSERT eql_v2.compare_blake3(b, c) = -1; - - ASSERT eql_v2.compare_blake3(c, c) = 0; - ASSERT eql_v2.compare_blake3(c, b) = 1; - ASSERT eql_v2.compare_blake3(c, a) = 1; - END; -$$ LANGUAGE plpgsql; - diff --git a/src/bloom_filter/functions_test.sql b/src/bloom_filter/functions_test.sql deleted file mode 100644 index 87010453..00000000 --- a/src/bloom_filter/functions_test.sql +++ /dev/null @@ -1,14 +0,0 @@ -\set ON_ERROR_STOP on - -DO $$ - BEGIN - PERFORM assert_result( - 'Extract match index term from encrypted', - 'SELECT eql_v2.bloom_filter(''{"bf": []}''::jsonb)'); - - PERFORM assert_exception( - 'Missing match index term in encrypted raises exception', - 'SELECT eql_v2.bloom_filter(''{}''::jsonb)'); - - END; -$$ LANGUAGE plpgsql; diff --git a/src/config/config_test.sql b/src/config/config_test.sql deleted file mode 100644 index e67b4840..00000000 --- a/src/config/config_test.sql +++ /dev/null @@ -1,349 +0,0 @@ -\set ON_ERROR_STOP on - - --- Create tables for adding configuration -DROP TABLE IF EXISTS users; -CREATE TABLE users -( - id bigint GENERATED ALWAYS AS IDENTITY, - name eql_v2_encrypted, - PRIMARY KEY(id) -); - -DROP TABLE IF EXISTS blah; -CREATE TABLE blah -( - id bigint GENERATED ALWAYS AS IDENTITY, - vtha eql_v2_encrypted, - PRIMARY KEY(id) -); - - --- --- Helper function for assertions --- -DROP FUNCTION IF EXISTS _search_config_exists(text, text, text, text); -CREATE FUNCTION _search_config_exists(table_name text, column_name text, index_name text, state text DEFAULT 'pending') - RETURNS boolean -LANGUAGE sql STRICT PARALLEL SAFE -BEGIN ATOMIC - SELECT EXISTS (SELECT id FROM eql_v2_configuration c - WHERE c.state = state AND - c.data #> array['tables', table_name, column_name, 'indexes'] ? index_name); -END; - - --- ----------------------------------------------- --- Add and remove multiple indexes --- --- ----------------------------------------------- -TRUNCATE TABLE eql_v2_configuration; - -DO $$ - BEGIN - - -- Add indexes - PERFORM eql_v2.add_search_config('users', 'name', 'match', migrating => true); - ASSERT (SELECT _search_config_exists('users', 'name', 'match')); - - -- Add index with cast - PERFORM eql_v2.add_search_config('users', 'name', 'unique', 'int', migrating => true); - ASSERT (SELECT _search_config_exists('users', 'name', 'unique')); - - ASSERT (SELECT EXISTS (SELECT id FROM eql_v2_configuration c - WHERE c.state = 'pending' AND - c.data #> array['tables', 'users', 'name'] ? 'cast_as')); - - -- Match index removed - PERFORM eql_v2.remove_search_config('users', 'name', 'match', migrating => true); - ASSERT NOT (SELECT _search_config_exists('users', 'name', 'match')); - - -- All indexes removed, but column config preserved - PERFORM eql_v2.remove_search_config('users', 'name', 'unique', migrating => true); - ASSERT (SELECT EXISTS (SELECT FROM eql_v2_configuration c WHERE c.state = 'pending')); - ASSERT (SELECT data #> array['tables', 'users', 'name', 'indexes'] = '{}' FROM eql_v2_configuration c WHERE c.state = 'pending'); - - END; -$$ LANGUAGE plpgsql; - - - --- ----------------------------------------------- --- Add and remove multiple indexes from multiple tables --- --- ----------------------------------------------- -TRUNCATE TABLE eql_v2_configuration; - - -DO $$ - BEGIN - - -- Add indexes - PERFORM eql_v2.add_search_config('users', 'name', 'match', migrating => true); - ASSERT (SELECT _search_config_exists('users', 'name', 'match')); - - ASSERT (SELECT EXISTS (SELECT id FROM eql_v2_configuration c - WHERE c.state = 'pending' AND - c.data #> array['tables', 'users', 'name', 'indexes'] ? 'match')); - - -- Add index with cast - PERFORM eql_v2.add_search_config('blah', 'vtha', 'unique', 'int', migrating => true); - ASSERT (SELECT _search_config_exists('blah', 'vtha', 'unique')); - - ASSERT (SELECT EXISTS (SELECT id FROM eql_v2_configuration c - WHERE c.state = 'pending' AND - c.data #> array['tables', 'users', 'name', 'indexes'] ? 'match')); - - - ASSERT (SELECT EXISTS (SELECT id FROM eql_v2_configuration c - WHERE c.state = 'pending' AND - c.data #> array['tables', 'blah', 'vtha', 'indexes'] ? 'unique')); - - - -- Match index removed - PERFORM eql_v2.remove_search_config('users', 'name', 'match', migrating => true); - ASSERT NOT (SELECT _search_config_exists('users', 'name', 'match')); - - -- Match index removed - PERFORM eql_v2.remove_search_config('blah', 'vtha', 'unique', migrating => true); - ASSERT NOT (SELECT _search_config_exists('users', 'vtha', 'unique')); - - -- All indexes removed, but column config preserved - ASSERT (SELECT EXISTS (SELECT FROM eql_v2_configuration c WHERE c.state = 'pending')); - ASSERT (SELECT data #> array['tables', 'blah', 'vtha', 'indexes'] = '{}' FROM eql_v2_configuration c WHERE c.state = 'pending'); - - END; -$$ LANGUAGE plpgsql; - --- SELECT FROM eql_v2_configuration c WHERE c.state = 'pending'; - - --- ----------------------------------------------- --- Add & modify index --- Pending configuration created and contains the path `user/name.match.option` --- ----------------------------------------------- --- TRUNCATE TABLE eql_v2_configuration; - - -DO $$ - BEGIN - PERFORM eql_v2.add_search_config('users', 'name', 'match', migrating => true); - ASSERT (SELECT _search_config_exists('users', 'name', 'match')); - - -- Pending configuration contains the path `user/name.match.option` - PERFORM eql_v2.modify_search_config('users', 'name', 'match', 'int', '{"option": "value"}'::jsonb, migrating => true); - ASSERT (SELECT _search_config_exists('users', 'name', 'match')); - - ASSERT (SELECT EXISTS (SELECT id FROM eql_v2_configuration c - WHERE c.state = 'pending' AND - c.data #> array['tables', 'users', 'name', 'indexes', 'match'] ? 'option')); - - ASSERT (SELECT EXISTS (SELECT id FROM eql_v2_configuration c - WHERE c.state = 'pending' AND - c.data #> array['tables', 'users', 'name'] ? 'cast_as')); - - -- All indexes removed, but column config preserved - PERFORM eql_v2.remove_search_config('users', 'name', 'match', migrating => true); - ASSERT (SELECT EXISTS (SELECT FROM eql_v2_configuration c WHERE c.state = 'pending')); - ASSERT (SELECT data #> array['tables', 'users', 'name', 'indexes'] = '{}' FROM eql_v2_configuration c WHERE c.state = 'pending'); - END; -$$ LANGUAGE plpgsql; - - --- -- ----------------------------------------------- --- -- With existing active config --- -- Adding an index creates a new pending configuration --- -- ----------------------------------------------- -TRUNCATE TABLE eql_v2_configuration; - --- create an active configuration -INSERT INTO eql_v2_configuration (state, data) VALUES ( - 'active', - '{ - "v": 1, - "tables": { - "users": { - "blah": { - "cast_as": "text", - "indexes": { - "match": {} - } - }, - "vtha": { - "cast_as": "text", - "indexes": {} - } - } - } - }'::jsonb -); - --- An encrypting config should exist -DO $$ - BEGIN - ASSERT (SELECT _search_config_exists('users', 'blah', 'match', 'active')); - - PERFORM eql_v2.add_search_config('users', 'name', 'match', migrating => true); - - -- index added to name - ASSERT (SELECT _search_config_exists('users', 'name', 'match' )); - - -- pending is a copy of the active config - -- and the active index still exists - ASSERT (SELECT _search_config_exists('users', 'blah', 'match')); - - END; -$$ LANGUAGE plpgsql; - - --- -- ----------------------------------------------- --- -- Add and remove column --- -- --- -- ----------------------------------------------- -TRUNCATE TABLE eql_v2_configuration; -DO $$ - BEGIN - - PERFORM assert_exception( - 'Cannot add index to column that does not exist', - 'SELECT eql_v2.add_column(''user'', ''name'')'); - - PERFORM assert_no_result( - 'No configuration was created', - 'SELECT * FROM eql_v2_configuration'); - END; -$$ LANGUAGE plpgsql; - - - --- -- ----------------------------------------------- --- -- Add and remove column --- -- --- -- ----------------------------------------------- -TRUNCATE TABLE eql_v2_configuration; -DO $$ - BEGIN - -- reset the table - PERFORM create_table_with_encrypted(); - - PERFORM eql_v2.add_column('encrypted', 'e', migrating => true); - - PERFORM assert_count( - 'Pending configuration was created', - 'SELECT * FROM eql_v2_configuration c WHERE c.state = ''pending''', - 1); - - - PERFORM eql_v2.remove_column('encrypted', 'e', migrating => true); - - PERFORM assert_count( - 'Pending configuration exists but is empty', - 'SELECT * FROM eql_v2_configuration c WHERE c.state = ''pending''', - 1); - - -- Verify the config is empty - ASSERT (SELECT data #> array['tables'] = '{}' FROM eql_v2_configuration c WHERE c.state = 'pending'); - - END; -$$ LANGUAGE plpgsql; - - --- ----------------------------------------------- ---- --- eql_v2_configuration tyoe --- Validate configuration schema --- Try and insert many invalid configurations --- None should exist --- --- ----------------------------------------------- -TRUNCATE TABLE eql_v2_configuration; - -\set ON_ERROR_STOP off -\set ON_ERROR_ROLLBACK on - -DO $$ - BEGIN - RAISE NOTICE '------------------------------------------------------'; - RAISE NOTICE 'eql_v2_configuration constraint tests: 4 errors follow'; - END; -$$ LANGUAGE plpgsql; --- --- No schema version -INSERT INTO eql_v2_configuration (data) VALUES ( - '{ - "tables": { - "users": { - "blah": { - "cast_as": "text", - "indexes": {} - } - } - } - }'::jsonb -); - --- --- Empty tables -INSERT INTO eql_v2_configuration (data) VALUES ( - '{ - "v": 1, - "tables": {} - }'::jsonb -); - - --- --- invalid cast -INSERT INTO eql_v2_configuration (data) VALUES ( - '{ - "v": 1, - "tables": { - "users": { - "blah": { - "cast_as": "regex" - } - } - } - }'::jsonb -); - --- --- invalid index -INSERT INTO eql_v2_configuration (data) VALUES ( - '{ - "v": 1, - "tables": { - "users": { - "blah": { - "cast_as": "text", - "indexes": { - "blah": {} - } - } - } - } - }'::jsonb -); - - --- Pending configuration should not be created; -DO $$ - BEGIN - ASSERT (SELECT NOT EXISTS (SELECT FROM eql_v2_configuration c WHERE c.state = 'pending')); - END; -$$ LANGUAGE plpgsql; - -DO $$ - BEGIN - RAISE NOTICE 'eql_v2_configuration constraint tests: OK'; - RAISE NOTICE '------------------------------------------------------'; - END; -$$ LANGUAGE plpgsql; - - -\set ON_ERROR_STOP on -\set ON_ERROR_ROLLBACK off - - - - diff --git a/src/encrypted/aggregates_test.sql b/src/encrypted/aggregates_test.sql deleted file mode 100644 index 68bd4ea5..00000000 --- a/src/encrypted/aggregates_test.sql +++ /dev/null @@ -1,50 +0,0 @@ -\set ON_ERROR_STOP on - --- create table -CREATE TABLE agg_test -( - plain_int integer, - enc_int eql_v2_encrypted -); - --- Add data. These are saved from the psql query output connected to Proxy. --- Decrypted `enc_int` value is the same as the `plain_int` value in the same row. -INSERT INTO agg_test (plain_int, enc_int) VALUES -( - NULL, - NULL -), -( - 3, - '{"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{&N2hzy", "i": {"c": "encrypted_int4", "t": "encrypted"}, "k": "ct", "bf": null, "ob": ["ccccccccb06565ebd23d6a4c3eee512713175e673c6d995ff5d9b1d3492fe8eb289c3eb95029025f5b71fc6e06632b4a1302980e433361c7999724dbdd052739258d9444b0fbd43cc61368e60f4b0d5aeca2aa85c1c89933b53afffcc4eb0632dca75f632bb9bc792d1dbd6bced6253291f0db134552d384e9e378f4f5890c31ca9d115965a0e8fbf13ad8d1d33f88d360d5e2f9680fb158f98158443ffc769cd9aac94380f05e3226b785f58006e5b9da6b8d86a7441a88fd848099a2400ef59b494b0c30013568dc1be9bba560565fccb49309ba2ec3edcff6f9d7a67b519b3754b37b0025dff7592a6117949a04043c100353289628884fe06cb2099e7b4b49abea9797a73ee0b85283a5b6f69bcf45f87e6cd6d45ecfd1633903270781173ed9d31a682bba0e54ff355f456bf0c468e378e41cb54fcc074ad40fb4448f6fec892c1ecda15a5efffb8dde3a3b282865ac436d7e43d48d4327c439956733697d3f5b02ead4805a7f905bdae24c1b35252e34939676a07ddb5454c3580c7d76d792a97988e35142f43667112432623eda5126e9af2592dd"], "v": 1}'::jsonb::eql_v2_encrypted -), -( - 5, - '{"c": "mBbKSqWLK6yl>o%G%&x+2$jdg7F`-R(^>R1Q^wGod8-FZ5C$xFI4dN?Ap114=77xPZ9!cKxE}qmyXrhx#K`4ztbUrysQrOFqON6bV{&N2hzy", "i": {"c": "encrypted_int4", "t": "encrypted"}, "k": "ct", "bf": null, "ob": ["ccccccccb065659dd23d6a4c3eee512713175e673c6d995ff5d9b1d3492fe8eb289c3eb95029025f5b71fc6e06632b4a1302980e433361c7999724dbdd052739258d9444b0fbd43cc61368e60f4b0d5aeca2aa85c1c89933b53afffcc4eb0632dca75f632bb9bc792d1dbd6bced6253291f0db134552d384bec7bfb23290d7559fd8637b85ca7510cca465570029734ef0319c77177913ad84f54852bed2e2a67b6dafcab3eb70d3a2592414a43acc03703083cf1fa1984dfc0719337d5de4eefd0d137588641a0d38c771b77ab07ebab3fc9bfd7469c4222e1a8edee71188eeb24bfffcd82f711156381d8068223e3d75f5ba8a958182bc46a0ab58c29872cd17e559ed0b935a445249dbac5b51438cebaf9d28d5c8b67cd99f990d5295c1e37470ce5b33fe01eaf31d84c9a08b267c0e9e1aadfcce7f9e2253ababa71eaf1fec309dc988e454717a3c2e3bffb1c546a7195ecf274eb7d691abcf46a61e34d4c63c45d48831dc23aa11f981de692926cd1d1d77a340c9e54baf62da61d5f88960a93e120d3828f4053577b93b536cc9b05c889dcf171865"], "v": 1}'::jsonb::eql_v2_encrypted -), -( - 1, - '{"c": "mBbJSy$p0fHEK%aOAOYi4PTJN7B@a-j{+xl7tffjGTN<-Znt3Zge#lGAX^WHzU`7ml<4vRHLKxoB%}NN2hzy", "i": {"c": "encrypted_int4", "t": "encrypted"}, "k": "ct", "bf": null, "ob": ["ccccccccb0656502d23d6a4c3eee512713175e673c6d995ff5d9b1d3492fe8eb289c3eb95029025f5b71fc6e06632b4a1302980e433361c7999724dbdd052739258d9444b0fbd43cc61368e60f4b0d5aeca2aa85c1c89933b53afffcc4eb0632dca75f632bb9bc792d1dbd6bced6253291f0db134552d384250ca116ef329616ddb341917699b9ea48901124a15a4547be1ff7c672c0c1bc6bb17e2a141f46138fc314f4bf8a55068bf031bc48f038c379e54cfbb1c64eb223c18c87cd68a91fb031905e11d9478f158b561399b527038efc594bfd9fb19c963a2778b75215e1d8933b08df04d1c62742fd48a4de310792031a70ca4b157bc218ab3fbadc6dc14b939422023331c03bcf4b673c5d261a19c3d13155cbaa1b84e9e90e389fa6973dde07fba08c13847006707488e288ce780d59700197452ebc68d22032ab03f7b445e45ed7abb1af34955199440f7db2c969c60b1eb49cdcd75d5e8f7de37848ddebb40df8e14d4b92910e15fedac3f61f22ef430805ba1bbf5fccc9fe792e4c0353beee48ca03ef23c7d3fab19e9aa218aefb44e6c26d70"], "v": 1}'::jsonb::eql_v2_encrypted -), -( - 3, - '{"c": "mBbLa7Cm?&jvpfcv1d3hep>s)76qzUbwUky&M&C3mjDG_os-_y0MRaMGl@&p#AOuusN|3Lu=mBCcg_V{&N2hzy", "i": {"c": "encrypted_int4", "t": "encrypted"}, "k": "ct", "bf": null, "ob": ["ccccccccb06565ebd23d6a4c3eee512713175e673c6d995ff5d9b1d3492fe8eb289c3eb95029025f5b71fc6e06632b4a1302980e433361c7999724dbdd052739258d9444b0fbd43cc61368e60f4b0d5aeca2aa85c1c89933b53afffcc4eb0632dca75f632bb9bc792d1dbd6bced6253291f0db134552d384e9e378f4f5890c31ca9d115965a0e8fb2c3c60ccce84ffc03bddb22b27a1ce278eec118496fd23f083ebb21bb4b83b89eda8c0bdea50debc5ec4f2b2d91b63a80d39386194ad9d129bee2f5168341cb41ed26dc03466cac5e2dbe7336fdb74c0d37d63b396033ce60002c9950f5ac2970dacf4caace2eef5b81544df88a7ef2a8d69550d25d39c678c8e43a3dcc2857018a2c979b45c6b19dabd28ae7388d62916e6742763d6484d1b45154e6c8e6a66e02b03f64b67ddef24747dded32e226e3a93d5d1a92d11e760403cad04a0dd07c14da336a409739e8bbeb3b3d6b92117fa2d2c941da4996ea61b29ca3fffb4594ddbeab7105a1b4c5e422ec5ab8154db545103d8c2889be2e4591198912446d8b33b8708a4cc959a1e0957dcae6a50c3"], "v": 1}'::jsonb::eql_v2_encrypted -) -; - --- run normal cases -DO $$ - BEGIN - -- min null finds null - ASSERT ((SELECT eql_v2.min(enc_int) FROM agg_test where enc_int IS NULL) IS NULL); - - -- min enc_int finds the minimum (1) - ASSERT ((SELECT enc_int FROM agg_test WHERE plain_int = 1) = (SELECT eql_v2.min(enc_int) FROM agg_test)); - - -- max null finds null - ASSERT ((SELECT eql_v2.max(enc_int) FROM agg_test where enc_int IS NULL) IS NULL); - - -- max enc_int finds the maximum (5) - ASSERT ((SELECT enc_int FROM agg_test WHERE plain_int = 5) = (SELECT eql_v2.max(enc_int) FROM agg_test)); - END; -$$ LANGUAGE plpgsql; diff --git a/src/encrypted/constraints_test.sql b/src/encrypted/constraints_test.sql deleted file mode 100644 index df85ef1f..00000000 --- a/src/encrypted/constraints_test.sql +++ /dev/null @@ -1,139 +0,0 @@ -\set ON_ERROR_STOP on - -SELECT create_table_with_encrypted(); - -DO $$ - BEGIN - -- insert without constraint works - INSERT INTO encrypted(e) VALUES ('{}'::jsonb::eql_v2_encrypted); - - -- delete the data - PERFORM create_table_with_encrypted(); - - -- add constraint - PERFORM eql_v2.add_encrypted_constraint('encrypted', 'e'); - - PERFORM assert_exception( - 'Constraint catches invalid eql_v2_encrypted', - 'INSERT INTO encrypted (e) VALUES (''{}''::jsonb::eql_v2_encrypted)'); - - END; -$$ LANGUAGE plpgsql; - - -DO $$ - BEGIN - -- reset the table - PERFORM create_table_with_encrypted(); - - -- add constraint - PERFORM eql_v2.add_encrypted_constraint('encrypted', 'e'); - - PERFORM assert_exception( - 'Constraint catches invalid eql_v2_encrypted', - 'INSERT INTO encrypted (e) VALUES (''{}''::jsonb::eql_v2_encrypted)'); - - PERFORM eql_v2.remove_encrypted_constraint('encrypted', 'e'); - - PERFORM assert_result( - 'Insert invalid data without constraint', - 'INSERT INTO encrypted (e) VALUES (''{}''::jsonb::eql_v2_encrypted) RETURNING id'); - - END; -$$ LANGUAGE plpgsql; - - --- ----------------------------------------------- --- Adding search config adds the constraint --- --- ----------------------------------------------- -TRUNCATE TABLE eql_v2_configuration; - -DO $$ - BEGIN - -- reset the table - PERFORM create_table_with_encrypted(); - - PERFORM eql_v2.add_search_config('encrypted', 'e', 'match'); - - PERFORM assert_exception( - 'Constraint catches invalid eql_v2_encrypted', - 'INSERT INTO encrypted (e) VALUES (''{}''::jsonb::eql_v2_encrypted)'); - - -- add constraint without error - PERFORM eql_v2.add_encrypted_constraint('encrypted', 'e'); - - PERFORM eql_v2.remove_encrypted_constraint('encrypted', 'e'); - - PERFORM assert_result( - 'Insert invalid data without constraint', - 'INSERT INTO encrypted (e) VALUES (''{}''::jsonb::eql_v2_encrypted) RETURNING id'); - - END; -$$ LANGUAGE plpgsql; - - --- ----------------------------------------------- --- Adding column adds the constraint --- --- ----------------------------------------------- -TRUNCATE TABLE eql_v2_configuration; - -DO $$ - BEGIN - -- reset the table - PERFORM create_table_with_encrypted(); - - PERFORM eql_v2.add_column('encrypted', 'e'); - - PERFORM assert_exception( - 'Constraint catches invalid eql_v2_encrypted', - 'INSERT INTO encrypted (e) VALUES (''{}''::jsonb::eql_v2_encrypted)'); - - -- add constraint without error - PERFORM eql_v2.add_encrypted_constraint('encrypted', 'e'); - - PERFORM eql_v2.remove_encrypted_constraint('encrypted', 'e'); - - PERFORM assert_result( - 'Insert invalid data without constraint', - 'INSERT INTO encrypted (e) VALUES (''{}''::jsonb::eql_v2_encrypted) RETURNING id'); - - END; -$$ LANGUAGE plpgsql; - - --- EQL version is enforced -DO $$ - DECLARE - e eql_v2_encrypted; - BEGIN - - -- reset data - PERFORM create_table_with_encrypted(); - - -- remove the version field - e := create_encrypted_json(1)::jsonb-'v'; - - PERFORM assert_exception( - 'Insert with missing version fails', - format('INSERT INTO encrypted (e) VALUES (%s::jsonb::eql_v2_encrypted) RETURNING id', e)); - - -- set version to 1 - e := create_encrypted_json(1)::jsonb || '{"v": 1}'; - - PERFORM assert_exception( - 'Insert with invalid version fails', - format('INSERT INTO encrypted (e) VALUES (%s::jsonb::eql_v2_encrypted) RETURNING id', e)); - - -- set version to 1 - e := create_encrypted_json(1); - - PERFORM assert_result( - 'Insert with valid version is ok', - format('INSERT INTO encrypted (e) VALUES (%L) RETURNING id', e)); - - - END; -$$ LANGUAGE plpgsql; - diff --git a/src/encryptindex/functions_test.sql b/src/encryptindex/functions_test.sql deleted file mode 100644 index 1044e10a..00000000 --- a/src/encryptindex/functions_test.sql +++ /dev/null @@ -1,290 +0,0 @@ -\set ON_ERROR_STOP on - --- ----------------------------------------------- --- --- Alter table from config --- --- ----------------------------------------------- -TRUNCATE TABLE eql_v2_configuration; - --- Create a table with a plaintext column -DROP TABLE IF EXISTS users; -CREATE TABLE users -( - id bigint GENERATED ALWAYS AS IDENTITY, - name TEXT, - PRIMARY KEY(id) -); - -INSERT INTO eql_v2_configuration (data) VALUES ( - '{ - "v": 1, - "tables": { - "users": { - "name": { - "cast_as": "text", - "indexes": { - "ore": {} - } - } - } - } - }'::jsonb -); - -DO $$ - BEGIN - - -- the column is pending encryptindexing - ASSERT (SELECT EXISTS (SELECT * FROM eql_v2.select_pending_columns() AS c WHERE c.column_name = 'name')); - - -- the target column does not exist - ASSERT (SELECT EXISTS (SELECT * FROM eql_v2.select_target_columns() AS c WHERE c.target_column IS NULL)); - - -- Add the vtha_encrypted column to the table - PERFORM eql_v2.create_encrypted_columns(); - - ASSERT (SELECT EXISTS (SELECT * FROM information_schema.columns s WHERE s.column_name = 'name_encrypted')); - - -- -- rename columns - PERFORM eql_v2.rename_encrypted_columns(); - - ASSERT (SELECT EXISTS (SELECT * FROM information_schema.columns s WHERE s.column_name = 'name_plaintext')); - ASSERT (SELECT EXISTS (SELECT * FROM information_schema.columns s WHERE s.column_name = 'name' and s.udt_name = 'eql_v2_encrypted')); - ASSERT (SELECT NOT EXISTS (SELECT * FROM information_schema.columns s WHERE s.column_name = 'name_encrypted')); - END; -$$ LANGUAGE plpgsql; - - --- ----------------------------------------------- --- Create multiple columns --- --- ----------------------------------------------- -TRUNCATE TABLE eql_v2_configuration; - --- Create a table with multiple plaintext columns -DROP TABLE IF EXISTS users; -CREATE TABLE users -( - id bigint GENERATED ALWAYS AS IDENTITY, - name TEXT, - email INT, - PRIMARY KEY(id) -); - -INSERT INTO eql_v2_configuration (data) VALUES ( - '{ - "v": 1, - "tables": { - "users": { - "name": { - "cast_as": "text", - "indexes": { - "ore": {}, - "unique": {} - } - }, - "email": { - "cast_as": "text", - "indexes": { - "match": {} - } - } - } - } - }'::jsonb -); - -DO $$ - BEGIN - - -- the column is pending encryptindexing - ASSERT (SELECT EXISTS (SELECT * FROM eql_v2.select_pending_columns() AS c WHERE c.column_name = 'name')); - - -- the target column does not exisgt - ASSERT (SELECT EXISTS (SELECT * FROM eql_v2.select_target_columns() AS c WHERE c.target_column IS NULL)); - - -- create column - PERFORM eql_v2.create_encrypted_columns(); - - ASSERT (SELECT EXISTS (SELECT * FROM information_schema.columns s WHERE s.column_name = 'name_encrypted')); - ASSERT (SELECT EXISTS (SELECT * FROM information_schema.columns s WHERE s.column_name = 'email_encrypted')); - END; -$$ LANGUAGE plpgsql; - - --- ----------------------------------------------- --- With existing active config --- and an updated schema --- Start encryptindexing --- The active config is unchanged --- The pending config should now be encrypting --- ----------------------------------------------- -TRUNCATE TABLE eql_v2_configuration; - --- create an active configuration -INSERT INTO eql_v2_configuration (state, data) VALUES ( - 'active', - '{ - "v": 1, - "tables": { - "users": { - "name": { - "cast_as": "text", - "indexes": { - "unique": {} - } - } - } - } - }'::jsonb -); - --- Create a table with plaintext and encrypted columns -DROP TABLE IF EXISTS users; -CREATE TABLE users -( - id bigint GENERATED ALWAYS AS IDENTITY, - name TEXT, - name_encrypted eql_v2_encrypted, - PRIMARY KEY(id) -); - - --- An encrypting config should exist -DO $$ - BEGIN - PERFORM eql_v2.add_search_config('users', 'name_encrypted', 'match', migrating => true); - PERFORM eql_v2.migrate_config(); - ASSERT (SELECT EXISTS (SELECT FROM eql_v2_configuration c WHERE c.state = 'active')); - ASSERT (SELECT EXISTS (SELECT FROM eql_v2_configuration c WHERE c.state = 'encrypting')); - ASSERT (SELECT NOT EXISTS (SELECT FROM eql_v2_configuration c WHERE c.state = 'pending')); - END; -$$ LANGUAGE plpgsql; - - --- Encrypting config without `migrating = true` is immediately active -DO $$ - BEGIN - TRUNCATE TABLE eql_v2_configuration; - PERFORM eql_v2.add_search_config('users', 'name_encrypted', 'match'); - ASSERT (SELECT EXISTS (SELECT FROM eql_v2_configuration c WHERE c.state = 'active')); - END; -$$ LANGUAGE plpgsql; - - --- migrate_config() should raise an exception when no pending configuration exists -DO $$ - BEGIN - TRUNCATE TABLE eql_v2_configuration; - PERFORM eql_v2.add_search_config('users', 'name_encrypted', 'match'); - - PERFORM assert_exception( - 'eql_v2.migrate_config() should raise an exception when no pending configuration exists', - 'SELECT eql_v2.migrate_config()' - ); - END; -$$ LANGUAGE plpgsql; - --- ----------------------------------------------- --- With existing active config and an updated schema using a raw JSONB column --- Start encryptindexing --- The active config is unchanged --- The pending config should now be encrypting --- ----------------------------------------------- -TRUNCATE TABLE eql_v2_configuration; - --- create an active configuration -INSERT INTO eql_v2_configuration (state, data) VALUES ( - 'active', - '{ - "v": 1, - "tables": { - "users": { - "name": { - "cast_as": "text", - "indexes": { - "unique": {} - } - } - } - } - }'::jsonb -); - --- Create a table with plaintext and jsonb column -DROP TABLE IF EXISTS users; -CREATE TABLE users -( - id bigint GENERATED ALWAYS AS IDENTITY, - name TEXT, - name_encrypted eql_v2_encrypted, - PRIMARY KEY(id) -); - - --- An encrypting config should exist -DO $$ - BEGIN - PERFORM eql_v2.add_search_config('users', 'name_encrypted', 'match', migrating => true); - PERFORM eql_v2.migrate_config(); - - ASSERT (SELECT EXISTS (SELECT FROM eql_v2_configuration c WHERE c.state = 'active')); - ASSERT (SELECT EXISTS (SELECT FROM eql_v2_configuration c WHERE c.state = 'encrypting')); - ASSERT (SELECT NOT EXISTS (SELECT FROM eql_v2_configuration c WHERE c.state = 'pending')); - END; -$$ LANGUAGE plpgsql; - - --- ----------------------------------------------- --- With existing active config --- Activate encrypting config --- The active config is now inactive --- The encrypting config should now be active --- ----------------------------------------------- -TRUNCATE TABLE eql_v2_configuration; - --- create an active configuration -INSERT INTO eql_v2_configuration (state, data) VALUES ( - 'active', - '{ - "v": 1, - "tables": { - "users": { - "name": { - "cast_as": "text", - "indexes": { - "unique": {} - } - } - } - } - }'::jsonb -); - - --- Create a table with multiple plaintext columns -DROP TABLE IF EXISTS users; -CREATE TABLE users -( - id bigint GENERATED ALWAYS AS IDENTITY, - name TEXT, - name_encrypted eql_v2_encrypted, - PRIMARY KEY(id) -); - --- An encrypting config should exist -DO $$ - BEGIN - PERFORM eql_v2.add_search_config('users', 'name_encrypted', 'match', migrating => true); - - PERFORM eql_v2.migrate_config(); -- need to encrypt first - PERFORM eql_v2.activate_config(); - - ASSERT (SELECT EXISTS (SELECT FROM eql_v2_configuration c WHERE c.state = 'active')); - ASSERT (SELECT EXISTS (SELECT FROM eql_v2_configuration c WHERE c.state = 'inactive')); - ASSERT (SELECT NOT EXISTS (SELECT FROM eql_v2_configuration c WHERE c.state = 'encrypting')); - ASSERT (SELECT NOT EXISTS (SELECT FROM eql_v2_configuration c WHERE c.state = 'pending')); - - END; -$$ LANGUAGE plpgsql; diff --git a/src/hmac_256/compare_test.sql b/src/hmac_256/compare_test.sql deleted file mode 100644 index 3529bfef..00000000 --- a/src/hmac_256/compare_test.sql +++ /dev/null @@ -1,26 +0,0 @@ -\set ON_ERROR_STOP on - -DO $$ - DECLARE - a eql_v2_encrypted; - b eql_v2_encrypted; - c eql_v2_encrypted; - BEGIN - a := create_encrypted_json(1, 'hm'); - b := create_encrypted_json(2, 'hm'); - c := create_encrypted_json(3, 'hm'); - - ASSERT eql_v2.compare_hmac_256(a, a) = 0; - ASSERT eql_v2.compare_hmac_256(a, b) = -1; - ASSERT eql_v2.compare_hmac_256(a, c) = -1; - - ASSERT eql_v2.compare_hmac_256(b, b) = 0; - ASSERT eql_v2.compare_hmac_256(b, a) = 1; - ASSERT eql_v2.compare_hmac_256(b, c) = -1; - - ASSERT eql_v2.compare_hmac_256(c, c) = 0; - ASSERT eql_v2.compare_hmac_256(c, b) = 1; - ASSERT eql_v2.compare_hmac_256(c, a) = 1; - END; -$$ LANGUAGE plpgsql; - diff --git a/src/hmac_256/functions_test.sql b/src/hmac_256/functions_test.sql deleted file mode 100644 index b4ccfae8..00000000 --- a/src/hmac_256/functions_test.sql +++ /dev/null @@ -1,26 +0,0 @@ -\set ON_ERROR_STOP on - -DO $$ - BEGIN - PERFORM assert_result( - 'Extract hmac_256 index term from encrypted', - 'SELECT eql_v2.hmac_256(''{"hm": "u"}''::jsonb)'); - - PERFORM assert_exception( - 'Missing hmac_256 index term in encrypted raises exception', - 'SELECT eql_v2.hmac_256(''{}''::jsonb)'); - - END; -$$ LANGUAGE plpgsql; - - -DO $$ - DECLARE - e eql_v2_encrypted; - BEGIN - e := create_encrypted_json(1, 'hm'); - - ASSERT eql_v2.has_hmac_256(e); - END; -$$ LANGUAGE plpgsql; - diff --git a/src/jsonb/functions_test.sql b/src/jsonb/functions_test.sql deleted file mode 100644 index 861d4435..00000000 --- a/src/jsonb/functions_test.sql +++ /dev/null @@ -1,338 +0,0 @@ -\set ON_ERROR_STOP on - - -SELECT create_table_with_encrypted(); -SELECT seed_encrypted_json(); - - --- ======================================================================== --- --- Selector &.a[*] --- -> 33743aed3ae636f6bf05cff11ac4b519 --- -DO $$ - BEGIN - - PERFORM seed_encrypted_json(); - PERFORM seed_encrypted(get_array_ste_vec()::eql_v2_encrypted); - - PERFORM assert_result( - 'jsonb_array_elements returns array elements from jsonb_path_query result', - 'SELECT eql_v2.jsonb_array_elements(eql_v2.jsonb_path_query(e, ''f510853730e1c3dbd31b86963f029dd5'')) as e FROM encrypted;'); - - PERFORM assert_count( - 'jsonb_array_elements returns the correct number of array elements from jsonb_path_query result', - 'SELECT eql_v2.jsonb_array_elements(eql_v2.jsonb_path_query(e, ''f510853730e1c3dbd31b86963f029dd5'')) as e FROM encrypted;', - 5); - - PERFORM assert_exception( - 'jsonb_array_elements exception if input is not an array', - 'SELECT eql_v2.jsonb_array_elements(eql_v2.jsonb_path_query(e, ''33743aed3ae636f6bf05cff11ac4b519'')) as e FROM encrypted LIMIT 1;'); - - END; -$$ LANGUAGE plpgsql; - --- --- Selector &.a[*] as eql_v2_encrypted --- -> 33743aed3ae636f6bf05cff11ac4b519 --- -DO $$ -DECLARE - selector eql_v2_encrypted; - - BEGIN - - PERFORM seed_encrypted_json(); - PERFORM seed_encrypted(get_array_ste_vec()::eql_v2_encrypted); - - selector := '{"s": "f510853730e1c3dbd31b86963f029dd5"}'::jsonb::eql_v2_encrypted; - - PERFORM assert_result( - 'jsonb_array_elements returns array elements from jsonb_path_query result using eql_v2_encrypted selector', - format('SELECT eql_v2.jsonb_array_elements_text(eql_v2.jsonb_path_query(e, %L::eql_v2_encrypted)) as e FROM encrypted;', selector)); - - PERFORM assert_count( - 'jsonb_array_elements returns the correct number of array elements from jsonb_path_query result', - format('SELECT eql_v2.jsonb_array_elements_text(eql_v2.jsonb_path_query(e, %L::eql_v2_encrypted)) as e FROM encrypted;', selector), - 5); - - selector := '{"s": "33743aed3ae636f6bf05cff11ac4b519"}'::jsonb::eql_v2_encrypted; - - PERFORM assert_exception( - 'jsonb_array_elements exception if input is not an array', - format('SELECT eql_v2.jsonb_array_elements_text(eql_v2.jsonb_path_query(e, %L::eql_v2_encrypted)) as e FROM encrypted;', selector)); - - END; -$$ LANGUAGE plpgsql; - - --- -- ======================================================================== --- -- --- -- Selector &.a[*] --- -- -> 33743aed3ae636f6bf05cff11ac4b519 --- -- -DO $$ -DECLARE - sv eql_v2_encrypted; - results eql_v2_encrypted[]; - BEGIN - - PERFORM seed_encrypted_json(); - PERFORM seed_encrypted(get_array_ste_vec()::eql_v2_encrypted); - - PERFORM assert_result( - 'jsonb_array_elements_text returns array elements from jsonb_path_query result', - 'SELECT eql_v2.jsonb_array_elements_text(eql_v2.jsonb_path_query(e, ''f510853730e1c3dbd31b86963f029dd5'')) as e FROM encrypted;'); - - PERFORM assert_count( - 'jsonb_array_elements_text returns the correct number of array elements from jsonb_path_query result', - 'SELECT eql_v2.jsonb_array_elements_text(eql_v2.jsonb_path_query(e, ''f510853730e1c3dbd31b86963f029dd5'')) as e FROM encrypted;', - 5); - - PERFORM assert_exception( - 'jsonb_array_elements_text exception if input is not an array', - 'SELECT eql_v2.jsonb_array_elements_text(eql_v2.jsonb_path_query(e, ''33743aed3ae636f6bf05cff11ac4b519'')) as e FROM encrypted LIMIT 1;'); - - END; -$$ LANGUAGE plpgsql; - - --- ======================================================================== --- --- Selector &.a[*] --- -> 33743aed3ae636f6bf05cff11ac4b519 --- -DO $$ -DECLARE - sv eql_v2_encrypted; - results eql_v2_encrypted[]; - BEGIN - - PERFORM seed_encrypted_json(); - PERFORM seed_encrypted(get_array_ste_vec()::eql_v2_encrypted); - - PERFORM assert_result( - 'jsonb_array_length returns array length of jsonb_path_query result', - 'SELECT eql_v2.jsonb_array_length(eql_v2.jsonb_path_query(e, ''f510853730e1c3dbd31b86963f029dd5'')) as e FROM encrypted LIMIT 1;', - '5'); - - PERFORM assert_exception( - 'jsonb_array_length exception if input is not an array', - 'SELECT eql_v2.jsonb_array_length(eql_v2.jsonb_path_query(e, ''33743aed3ae636f6bf05cff11ac4b519'')) as e FROM encrypted LIMIT 1;'); - - END; -$$ LANGUAGE plpgsql; - - - --- -- ======================================================================== --- --- -- "{\"hello\": \"four\", \"n\": 20, \"a\": [1, 2, 3, 4, 5] }", --- --- Selector &.a[*] --- -> 33743aed3ae636f6bf05cff11ac4b519 --- -DO $$ -DECLARE - sv eql_v2_encrypted; - results eql_v2_encrypted[]; - BEGIN - - PERFORM seed_encrypted_json(); - - -- Insert a row with array selector - sv := get_array_ste_vec()::eql_v2_encrypted; - PERFORM seed_encrypted(sv); - - PERFORM assert_count( - 'jsonb_path_query with array selector returns count', - 'SELECT eql_v2.jsonb_path_query_first(e, ''33743aed3ae636f6bf05cff11ac4b519'') as e FROM encrypted;', - 4 - ); - - PERFORM assert_count( - 'jsonb_path_query with array selector returns count', - 'SELECT eql_v2.jsonb_path_query_first(e, ''33743aed3ae636f6bf05cff11ac4b519'') as e FROM encrypted WHERE eql_v2.jsonb_path_query_first(e, ''33743aed3ae636f6bf05cff11ac4b519'') IS NOT NULL;', - 1 - ); - - END; -$$ LANGUAGE plpgsql; - - --- ------------------------------------------------------------------------ - - - --- ------------------------------------------------------------------------ --- --- jsonb_path_query --- - --- Paths --- $ -> bca213de9ccce676fa849ff9c4807963 --- $.hello -> a7cea93975ed8c01f861ccb6bd082784 --- $.n -> 2517068c0d1f9d4d41d2c666211f785e --- --- -DO $$ - BEGIN - PERFORM seed_encrypted_json(); - - PERFORM assert_result( - 'jsonb_path_query', - 'SELECT eql_v2.jsonb_path_query(e, ''2517068c0d1f9d4d41d2c666211f785e'') FROM encrypted LIMIT 1;'); - - PERFORM assert_count( - 'jsonb_path_query returns count', - 'SELECT eql_v2.jsonb_path_query(e, ''2517068c0d1f9d4d41d2c666211f785e'') FROM encrypted;', - 3); - - END; -$$ LANGUAGE plpgsql; - - -DO $$ - DECLARE - result jsonb; - BEGIN - PERFORM seed_encrypted_json(); - - SELECT eql_v2.jsonb_path_query(e, '2517068c0d1f9d4d41d2c666211f785e')::jsonb FROM encrypted LIMIT 1 INTO result; - - ASSERT result ? 'i'; - ASSERT result ? 'v'; - - END; -$$ LANGUAGE plpgsql; - - - -DO $$ - DECLARE - result jsonb; - BEGIN - PERFORM seed_encrypted_json(); - PERFORM seed_encrypted(get_array_ste_vec()::eql_v2_encrypted); - - SELECT eql_v2.jsonb_array_elements(eql_v2.jsonb_path_query(e, 'f510853730e1c3dbd31b86963f029dd5')::jsonb) FROM encrypted INTO result; - - ASSERT result ? 'i'; - ASSERT result ? 'v'; - END; -$$ LANGUAGE plpgsql; - - -DO $$ - BEGIN - - PERFORM seed_encrypted_json(); - - PERFORM assert_result( - 'jsonb_path_exists returns true', - 'SELECT eql_v2.jsonb_path_exists(e, ''2517068c0d1f9d4d41d2c666211f785e'') FROM encrypted LIMIT 1;', - 'true'); - - PERFORM assert_result( - 'jsonb_path_exists returns false', - 'SELECT eql_v2.jsonb_path_exists(e, ''blahvtha'') FROM encrypted LIMIT 1;', - 'false'); - - PERFORM assert_count( - 'jsonb_path_exists returns count', - 'SELECT eql_v2.jsonb_path_exists(e, ''2517068c0d1f9d4d41d2c666211f785e'') FROM encrypted;', - 3); - END; -$$ LANGUAGE plpgsql; - - - --- -- --- -- Selector &.a[*] --- -- -> 33743aed3ae636f6bf05cff11ac4b519 --- -- -DO $$ -DECLARE - sv eql_v2_encrypted; - results eql_v2_encrypted[]; - BEGIN - - -- Insert a row with array selector - sv := get_array_ste_vec()::eql_v2_encrypted; - PERFORM seed_encrypted(sv); - - PERFORM assert_result( - 'jsonb_path_query with array selector', - 'SELECT eql_v2.jsonb_path_query(e, ''f510853730e1c3dbd31b86963f029dd5'') FROM encrypted;'); - - -- An array should be wrapped and returned as a single element - PERFORM assert_count( - 'jsonb_path_query with array selector returns one result', - 'SELECT eql_v2.jsonb_path_query(e, ''f510853730e1c3dbd31b86963f029dd5'') FROM encrypted;', - 1); - END; -$$ LANGUAGE plpgsql; - - - --- -- --- -- Selector &.a[*] --- -- -> 33743aed3ae636f6bf05cff11ac4b519 --- -- -DO $$ - DECLARE - sv eql_v2_encrypted; - results eql_v2_encrypted[]; - BEGIN - - PERFORM seed_encrypted_json(); - - -- Insert a row with array selector - sv := get_array_ste_vec()::eql_v2_encrypted; - PERFORM seed_encrypted(sv); - - PERFORM assert_result( - 'jsonb_path_exists with array selector', - 'SELECT eql_v2.jsonb_path_exists(e, ''f510853730e1c3dbd31b86963f029dd5'') FROM encrypted;'); - - PERFORM assert_count( - 'jsonb_path_exists with array selector returns correct number of records', - 'SELECT eql_v2.jsonb_path_exists(e, ''f510853730e1c3dbd31b86963f029dd5'') FROM encrypted;', - 4); - END; -$$ LANGUAGE plpgsql; - - - --- -- --- -- Selector &.a[*] --- -- -> 33743aed3ae636f6bf05cff11ac4b519 --- -- -DO $$ -DECLARE - sv eql_v2_encrypted; - results eql_v2_encrypted[]; - BEGIN - - PERFORM seed_encrypted_json(); - - -- Insert a row with array selector - sv := get_array_ste_vec()::eql_v2_encrypted; - PERFORM seed_encrypted(sv); - - PERFORM assert_count( - 'jsonb_path_query with array selector returns count', - 'SELECT eql_v2.jsonb_path_query_first(e, ''33743aed3ae636f6bf05cff11ac4b519'') as e FROM encrypted;', - 4 - ); - - PERFORM assert_count( - 'jsonb_path_query with array selector returns count', - 'SELECT eql_v2.jsonb_path_query_first(e, ''33743aed3ae636f6bf05cff11ac4b519'') as e FROM encrypted WHERE eql_v2.jsonb_path_query_first(e, ''33743aed3ae636f6bf05cff11ac4b519'') IS NOT NULL;', - 1 - ); - - END; -$$ LANGUAGE plpgsql; - - diff --git a/src/operators/->>_test.sql b/src/operators/->>_test.sql deleted file mode 100644 index d7b4c889..00000000 --- a/src/operators/->>_test.sql +++ /dev/null @@ -1,68 +0,0 @@ -\set ON_ERROR_STOP on - -SELECT create_table_with_encrypted(); -SELECT seed_encrypted_json(); - - --- --- The ->> operator returns ciphertext matching the selector -DO $$ - BEGIN - PERFORM assert_result( - 'Selector ->> returns at least one eql_v2_encrypted', - 'SELECT e->>''bca213de9ccce676fa849ff9c4807963''::text FROM encrypted;'); - - PERFORM assert_count( - 'Selector ->> returns all eql_v2_encrypted', - 'SELECT e->>''bca213de9ccce676fa849ff9c4807963''::text FROM encrypted;', - 3); - END; -$$ LANGUAGE plpgsql; - - --- --- The ->> operator returns NULL if no matching selector -DO $$ - BEGIN - PERFORM assert_no_result( - 'Unknown selector -> returns null', - 'SELECT e->>''blahvtha''::text FROM encrypted;'); - - END; -$$ LANGUAGE plpgsql; - - --- --- The ->> operator returns ciphertext matching the selector --- NOTE: DEPRECATED BEHAVIOUR --- DO $$ --- BEGIN - --- PERFORM assert_result( --- 'Selector ->> returns all eql_v2_encrypted', --- 'SELECT e->>''bca213de9ccce676fa849ff9c4807963''::text FROM encrypted LIMIT 1;', --- 'mBbLGB9xHAGzLvUj-`@Wmf=IhD87n7r3ir3n!Sk6AKir_YawR=0c>pk(OydB;ntIEXK~c>V&4>)rNkf> operator accepts an eql_v2_encrypted as the selector --- -DO $$ - DECLARE - term text; - BEGIN - term := '{"s": "bca213de9ccce676fa849ff9c4807963"}'; - - PERFORM assert_result( - 'Selector ->> returns at least one eql_v2_encrypted', - format('SELECT e->>%L::jsonb::eql_v2_encrypted FROM encrypted;', term)); - - PERFORM assert_count( - 'Selector ->> returns all eql_v2_encrypted', - format('SELECT e->>%L::jsonb::eql_v2_encrypted FROM encrypted;', term), - 3); - END; -$$ LANGUAGE plpgsql; - diff --git a/src/operators/->_test.sql b/src/operators/->_test.sql deleted file mode 100644 index f741e00c..00000000 --- a/src/operators/->_test.sql +++ /dev/null @@ -1,118 +0,0 @@ -\set ON_ERROR_STOP on - - -SELECT create_table_with_encrypted(); -SELECT seed_encrypted_json(); - - - --- --- The -> operator returns an encrypted matching the selector -DO $$ - BEGIN - PERFORM assert_result( - 'Selector -> returns at least one eql_v2_encrypted', - 'SELECT e->''bca213de9ccce676fa849ff9c4807963''::text FROM encrypted;'); - - PERFORM assert_count( - 'Selector -> returns all eql_v2_encrypted', - 'SELECT e->''bca213de9ccce676fa849ff9c4807963''::text FROM encrypted;', - 3); - END; -$$ LANGUAGE plpgsql; - - --- --- The -> operator returns NULL if no matching selector -DO $$ - BEGIN - PERFORM assert_no_result( - 'Unknown selector -> returns null', - 'SELECT e->''blahvtha''::text FROM encrypted;'); - - END; -$$ LANGUAGE plpgsql; - - - --- --- The -> operator accepts an eql_v2_encrypted as the selector --- -DO $$ - DECLARE - term text; - BEGIN - term := '{"s": "bca213de9ccce676fa849ff9c4807963"}'; - - PERFORM assert_result( - 'Selector -> returns at least one eql_v2_encrypted', - format('SELECT e->%L::jsonb::eql_v2_encrypted FROM encrypted;', term)); - - PERFORM assert_count( - 'Selector -> returns all eql_v2_encrypted', - format('SELECT e->%L::jsonb::eql_v2_encrypted FROM encrypted;', term), - 3); - END; -$$ LANGUAGE plpgsql; - - --- --- encrypted returned from -> operator expression called via eql_v2.ciphertext --- -DO $$ - DECLARE - result eql_v2_encrypted; - BEGIN - PERFORM assert_result( - 'Fetch ciphertext via selector', - 'SELECT eql_v2.ciphertext(e->''2517068c0d1f9d4d41d2c666211f785e''::text) FROM encrypted;'); - - PERFORM assert_count( - 'Fetch ciphertext via selector returns all eql_v2_encrypted', - 'SELECT eql_v2.ciphertext(e->''2517068c0d1f9d4d41d2c666211f785e''::text) FROM encrypted;', - 3); - END; -$$ LANGUAGE plpgsql; - - - --- --- encrypted returned from -> operator expression called via eql_v2.ciphertext --- -DO $$ - DECLARE - result eql_v2_encrypted; - BEGIN - - PERFORM truncate_table_with_encrypted(); - PERFORM seed_encrypted(get_array_ste_vec()::eql_v2_encrypted); - - PERFORM assert_result( - '-> operator with integer returns array', - 'SELECT eql_v2.jsonb_path_query_first(e, ''f510853730e1c3dbd31b86963f029dd5'')->0 as e FROM encrypted'); - - PERFORM assert_count( - '-> operator with integer returns array', - 'SELECT eql_v2.jsonb_path_query_first(e, ''f510853730e1c3dbd31b86963f029dd5'')->0 as e FROM encrypted', - 1); - - END; -$$ LANGUAGE plpgsql; - - --- --- Field accessor returns column metadata --- -DO $$ - DECLARE - result jsonb; - BEGIN - PERFORM seed_encrypted_json(); - - SELECT e->'2517068c0d1f9d4d41d2c666211f785e'::text FROM encrypted LIMIT 1 INTO result; - - ASSERT result ? 'i'; - ASSERT result ? 'v'; - - END; -$$ LANGUAGE plpgsql; diff --git a/src/operators/<=_ore_cllw_u64_8_test.sql b/src/operators/<=_ore_cllw_u64_8_test.sql deleted file mode 100644 index 0db355dc..00000000 --- a/src/operators/<=_ore_cllw_u64_8_test.sql +++ /dev/null @@ -1,57 +0,0 @@ -\set ON_ERROR_STOP on - -SELECT create_table_with_encrypted(); -SELECT seed_encrypted_json(); - - --- ======================================================================== - - --- ------------------------------------------------------------------------ --- ------------------------------------------------------------------------ --- --- ore_cllw_u64_8 less than or equal to <= --- --- Test data is in form '{"hello": "{one | two | three}", "n": {10 | 20 | 30} }' --- --- Paths --- $ -> bca213de9ccce676fa849ff9c4807963 --- $.hello -> a7cea93975ed8c01f861ccb6bd082784 --- $.n -> 2517068c0d1f9d4d41d2c666211f785e --- --- -DO $$ -DECLARE - sv eql_v2_encrypted; - term eql_v2_encrypted; - BEGIN - - -- This extracts the data associated with the field from the test eql_v2_encrypted - -- json n: 10 - sv := get_numeric_ste_vec_20()::eql_v2_encrypted; - -- extract the term at $.n returned as eql_v2_encrypted - term := sv->'2517068c0d1f9d4d41d2c666211f785e'::text; - - -- -- -- -- $.n - PERFORM assert_result( - format('eql_v2_encrypted <= eql_v2_encrypted with ore_cllw_u64_8 index term'), - format('SELECT e FROM encrypted WHERE (e->''2517068c0d1f9d4d41d2c666211f785e''::text) <= %L::eql_v2_encrypted', term)); - - PERFORM assert_count( - format('eql_v2_encrypted <= eql_v2_encrypted with ore index term'), - format('SELECT e FROM encrypted WHERE e->''2517068c0d1f9d4d41d2c666211f785e''::text <= %L::eql_v2_encrypted', term), - 2); - - -- Check the $.hello path - -- Returned encrypted does not have ore_cllw_u64_8 - -- Falls back to jsonb literal comparison - PERFORM assert_no_result( - format('eql_v2_encrypted <= eql_v2_encrypted with ore index term'), - format('SELECT e FROM encrypted WHERE e->''a7cea93975ed8c01f861ccb6bd082784''::text <= %L::eql_v2_encrypted', term)); - - END; -$$ LANGUAGE plpgsql; - - - -SELECT drop_table_with_encrypted(); \ No newline at end of file diff --git a/src/operators/<=_ore_cllw_var_8_test.sql b/src/operators/<=_ore_cllw_var_8_test.sql deleted file mode 100644 index b8255ae8..00000000 --- a/src/operators/<=_ore_cllw_var_8_test.sql +++ /dev/null @@ -1,53 +0,0 @@ -\set ON_ERROR_STOP on - -SELECT create_table_with_encrypted(); -SELECT seed_encrypted_json(); - - --- ------------------------------------------------------------------------ --- ------------------------------------------------------------------------ --- --- ore_cllw_u64_8 less than or equal to <= --- --- Test data is in form '{"hello": "{one | two | three}", "n": {10 | 20 | 30} }' --- --- Paths --- $ -> bca213de9ccce676fa849ff9c4807963 --- $.hello -> a7cea93975ed8c01f861ccb6bd082784 --- $.n -> 2517068c0d1f9d4d41d2c666211f785e --- --- -DO $$ -DECLARE - sv eql_v2_encrypted; - term eql_v2_encrypted; - BEGIN - - -- This extracts the data associated with the field from the test eql_v2_encrypted - -- json n: 10 - sv := get_numeric_ste_vec_20()::eql_v2_encrypted; - -- extract the term at $.n returned as eql_v2_encrypted - term := sv->'a7cea93975ed8c01f861ccb6bd082784'::text; - - -- -- -- -- $.n - PERFORM assert_result( - format('eql_v2_encrypted <= eql_v2_encrypted with ore_cllw_u64_8 index term'), - format('SELECT e FROM encrypted WHERE (e->''a7cea93975ed8c01f861ccb6bd082784''::text) <= %L::eql_v2_encrypted', term)); - - PERFORM assert_count( - format('eql_v2_encrypted <= eql_v2_encrypted with ore index term'), - format('SELECT e FROM encrypted WHERE e->''a7cea93975ed8c01f861ccb6bd082784''::text <= %L::eql_v2_encrypted', term), - 3); - - -- -- Check the $.n path - -- -- Returned encrypted does not have ore_cllw_u64_8 - PERFORM assert_result( - format('eql_v2_encrypted <= eql_v2_encrypted with ore index term'), - format('SELECT e FROM encrypted WHERE e->''2517068c0d1f9d4d41d2c666211f785e''::text <= %L::eql_v2_encrypted', term)); - - END; -$$ LANGUAGE plpgsql; - - - -SELECT drop_table_with_encrypted(); \ No newline at end of file diff --git a/src/operators/<=_test.sql b/src/operators/<=_test.sql deleted file mode 100644 index d0ac226e..00000000 --- a/src/operators/<=_test.sql +++ /dev/null @@ -1,84 +0,0 @@ -\set ON_ERROR_STOP on - -SELECT create_table_with_encrypted(); -SELECT seed_encrypted_json(); - - --- --- ORE - eql_v2_encrypted <= eql_v2_encrypted --- -DO $$ -DECLARE - e eql_v2_encrypted; - ore_term jsonb; - BEGIN - - -- Record with a Numeric ORE term of 42 - e := create_encrypted_ore_json(42); - PERFORM seed_encrypted(e); - - PERFORM assert_result( - 'eql_v2_encrypted <= eql_v2_encrypted', - format('SELECT e FROM encrypted WHERE e <= %L::eql_v2_encrypted', e)); - - PERFORM assert_count( - format('eql_v2_encrypted <= eql_v2_encrypted'), - format('SELECT e FROM encrypted WHERE e <= %L;', e), - 4); - - e := create_encrypted_ore_json(20); - - PERFORM assert_result( - 'eql_v2_encrypted <= eql_v2_encrypted', - format('SELECT e FROM encrypted WHERE e <= %L::eql_v2_encrypted', e)); - - PERFORM assert_count( - format('eql_v2_encrypted <= eql_v2_encrypted'), - format('SELECT e FROM encrypted WHERE e <= %L;', e), - 2); - END; -$$ LANGUAGE plpgsql; - - --- --- ORE - eql_v2.lte(a eql_v2_encrypted, b eql_v2_encrypted) --- -DO $$ -DECLARE - e eql_v2_encrypted; - ore_term jsonb; - BEGIN - -- Reset data - PERFORM seed_encrypted_json(); - - -- Record with a Numeric ORE term of 42 - e := create_encrypted_ore_json(42); - PERFORM seed_encrypted(e); - - PERFORM assert_result( - 'eql_v2.lte(a eql_v2_encrypted, b eql_v2_encrypted)', - format('SELECT e FROM encrypted WHERE eql_v2.lte(e, %L)', e)); - - -- include - PERFORM assert_count( - 'eql_v2.lte(a eql_v2_encrypted, b eql_v2_encrypted)', - format('SELECT e FROM encrypted WHERE eql_v2.lte(e, %L)', e), - 4); - - -- Record with a Numeric ORE term of 30 - e := create_encrypted_ore_json(30); - - PERFORM assert_result( - 'eql_v2.get(a eql_v2_encrypted, b eql_v2_encrypted)', - format('SELECT e FROM encrypted WHERE eql_v2.lte(e, %L)', e)); - - PERFORM assert_count( - 'eql_v2.get(a eql_v2_encrypted, b eql_v2_encrypted)', - format('SELECT e FROM encrypted WHERE eql_v2.lte(e, %L)', e), - 3); - END; -$$ LANGUAGE plpgsql; - - - -SELECT drop_table_with_encrypted(); \ No newline at end of file diff --git a/src/operators/<>_ore_cllw_u64_8_test.sql b/src/operators/<>_ore_cllw_u64_8_test.sql deleted file mode 100644 index 17881b0e..00000000 --- a/src/operators/<>_ore_cllw_u64_8_test.sql +++ /dev/null @@ -1,57 +0,0 @@ -\set ON_ERROR_STOP on - -SELECT create_table_with_encrypted(); -SELECT seed_encrypted_json(); - - --- ======================================================================== - - --- ------------------------------------------------------------------------ --- ------------------------------------------------------------------------ --- --- ore_cllw_u64_8 equality --- --- Test data is '{"hello": "world", "n": 42}' - --- Paths --- $ -> bca213de9ccce676fa849ff9c4807963 --- $.hello -> a7cea93975ed8c01f861ccb6bd082784 --- $.n -> 2517068c0d1f9d4d41d2c666211f785e --- --- - -DO $$ -DECLARE - sv eql_v2_encrypted; - term eql_v2_encrypted; - BEGIN - - -- This extracts the data associated with the field from the test eql_v2_encrypted - -- json n: 10 - sv := get_numeric_ste_vec_10()::eql_v2_encrypted; - -- extract the term at $.n returned as eql_v2_encrypted - term := sv->'2517068c0d1f9d4d41d2c666211f785e'::text; - - -- -- -- -- $.n - PERFORM assert_result( - format('eql_v2_encrypted <> eql_v2_encrypted with ore_cllw_u64_8 index term'), - format('SELECT e FROM encrypted WHERE (e->''2517068c0d1f9d4d41d2c666211f785e''::text) <> %L::eql_v2_encrypted', term)); - - PERFORM assert_count( - format('eql_v2_encrypted <> eql_v2_encrypted with ore index term'), - format('SELECT e FROM encrypted WHERE e->''2517068c0d1f9d4d41d2c666211f785e''::text <> %L::eql_v2_encrypted', term), - 2); - - -- -- Check the $.hello path - -- -- Returned encrypted does not have ore_cllw_u64_8 - PERFORM assert_result( - format('eql_v2_encrypted <> eql_v2_encrypted with ore index term'), - format('SELECT e FROM encrypted WHERE e->''a7cea93975ed8c01f861ccb6bd082784''::text <> %L::eql_v2_encrypted', term)); - - END; -$$ LANGUAGE plpgsql; - - - -SELECT drop_table_with_encrypted(); \ No newline at end of file diff --git a/src/operators/<>_ore_cllw_var_8_test.sql b/src/operators/<>_ore_cllw_var_8_test.sql deleted file mode 100644 index 13d3b659..00000000 --- a/src/operators/<>_ore_cllw_var_8_test.sql +++ /dev/null @@ -1,56 +0,0 @@ -\set ON_ERROR_STOP on - -SELECT create_table_with_encrypted(); -SELECT seed_encrypted_json(); - - --- ======================================================================== - - --- ------------------------------------------------------------------------ --- ------------------------------------------------------------------------ --- --- ore_cllw_u64_8 equality --- --- Test data is '{"hello": "world", "n": 42}' - --- Paths --- $ -> bca213de9ccce676fa849ff9c4807963 --- $.hello -> a7cea93975ed8c01f861ccb6bd082784 --- $.n -> 2517068c0d1f9d4d41d2c666211f785e --- --- -DO $$ -DECLARE - sv eql_v2_encrypted; - term eql_v2_encrypted; - BEGIN - - -- This extracts the data associated with the field from the test eql_v2_encrypted - -- json n: 10 - sv := get_numeric_ste_vec_10()::eql_v2_encrypted; - -- extract the term at $.n returned as eql_v2_encrypted - term := sv->'a7cea93975ed8c01f861ccb6bd082784'::text; - - -- -- -- -- $.n - PERFORM assert_result( - format('eql_v2_encrypted <> eql_v2_encrypted with ore_cllw_var_8 index term'), - format('SELECT e FROM encrypted WHERE (e->''a7cea93975ed8c01f861ccb6bd082784''::text) <> %L::eql_v2_encrypted', term)); - - PERFORM assert_count( - format('eql_v2_encrypted <> eql_v2_encrypted with ore_cllw_var_8 index term'), - format('SELECT e FROM encrypted WHERE e->''a7cea93975ed8c01f861ccb6bd082784''::text <> %L::eql_v2_encrypted', term), - 2); - - -- -- Check the $.n path - -- -- Returned encrypted does not have ore_cllw_var_8 - PERFORM assert_result( - format('eql_v2_encrypted <> eql_v2_encrypted with ore_cllw_var_8 index term'), - format('SELECT e FROM encrypted WHERE e->''2517068c0d1f9d4d41d2c666211f785e''::text <> %L::eql_v2_encrypted', term)); - - END; -$$ LANGUAGE plpgsql; - - - -SELECT drop_table_with_encrypted(); \ No newline at end of file diff --git a/src/operators/<>_ore_test.sql b/src/operators/<>_ore_test.sql deleted file mode 100644 index df68fedf..00000000 --- a/src/operators/<>_ore_test.sql +++ /dev/null @@ -1,87 +0,0 @@ -\set ON_ERROR_STOP on - -SELECT create_table_with_encrypted(); -SELECT seed_encrypted_json(); - - --- --- ORE - eql_v2_encrypted <> eql_v2_encrypted --- -DO $$ -DECLARE - e eql_v2_encrypted; - ore_term jsonb; - BEGIN - - -- Record with a Numeric ORE term of 42 - e := create_encrypted_ore_json(42); - PERFORM seed_encrypted(e); - - PERFORM assert_result( - 'eql_v2_encrypted <> eql_v2_encrypted', - format('SELECT e FROM encrypted WHERE e <> %L::eql_v2_encrypted', e)); - - PERFORM assert_count( - format('eql_v2_encrypted <> eql_v2_encrypted'), - format('SELECT e FROM encrypted WHERE e <> %L;', e), - 3); - - e := create_encrypted_ore_json(20); - - PERFORM assert_result( - 'eql_v2_encrypted <> eql_v2_encrypted', - format('SELECT e FROM encrypted WHERE e <> %L::eql_v2_encrypted', e)); - - PERFORM assert_count( - format('eql_v2_encrypted <> eql_v2_encrypted'), - format('SELECT e FROM encrypted WHERE e <> %L;', e), - 3); - END; -$$ LANGUAGE plpgsql; - - --- --- ORE - eql_v2.gte(a eql_v2_encrypted, b eql_v2_encrypted) --- -DO $$ -DECLARE - e eql_v2_encrypted; - ore_term jsonb; - BEGIN - -- Reset data - PERFORM seed_encrypted_json(); - - -- Record with a Numeric ORE term of 20 - e := create_encrypted_ore_json(20); - PERFORM seed_encrypted(e); - - PERFORM assert_result( - 'eql_v2.neq(a eql_v2_encrypted, b eql_v2_encrypted)', - format('SELECT e FROM encrypted WHERE eql_v2.neq(e, %L)', e)); - - -- include - PERFORM assert_count( - 'eql_v2.neq(a eql_v2_encrypted, b eql_v2_encrypted)', - format('SELECT e FROM encrypted WHERE eql_v2.neq(e, %L)', e), - 2); - - -- Record with a Numeric ORE term of 30 - e := create_encrypted_ore_json(30); - - PERFORM assert_result( - 'eql_v2.get(a eql_v2_encrypted, b eql_v2_encrypted)', - format('SELECT e FROM encrypted WHERE eql_v2.neq(e, %L)', e)); - - PERFORM assert_count( - 'eql_v2.get(a eql_v2_encrypted, b eql_v2_encrypted)', - format('SELECT e FROM encrypted WHERE eql_v2.neq(e, %L)', e), - 3); - END; -$$ LANGUAGE plpgsql; - - - --- ======================================================================== - - -SELECT drop_table_with_encrypted(); \ No newline at end of file diff --git a/src/operators/<>_test.sql b/src/operators/<>_test.sql deleted file mode 100644 index 076f50d0..00000000 --- a/src/operators/<>_test.sql +++ /dev/null @@ -1,165 +0,0 @@ -\set ON_ERROR_STOP on - -SELECT create_table_with_encrypted(); -SELECT seed_encrypted_json(); - - --- --- Unique equality - eql_v2_encrypted <> eql_v2_encrypted --- -DO $$ -DECLARE - e eql_v2_encrypted; - BEGIN - - for i in 1..3 loop - e := create_encrypted_json(i, 'hm'); - - PERFORM assert_count( - format('eql_v2_encrypted <> eql_v2_encrypted with unique index term %s of 3', i), - format('SELECT e FROM encrypted WHERE e <> %L;', e), - 2); - - end loop; - - -- record not in database - e := create_encrypted_json(91347, 'hm'); - - PERFORM assert_no_result( - 'eql_v2_encrypted <> eql_v2_encrypted with no matching record', - format('SELECT e FROM encrypted WHERE e <> %L;', e)); - - END; -$$ LANGUAGE plpgsql; - - --- ------------------------------------------------------------------------ --- ------------------------------------------------------------------------ --- --- Unique equality - eql_v2.neq(eql_v2_encrypted, eql_v2_encrypted) --- -DO $$ -DECLARE - e eql_v2_encrypted; - BEGIN - - for i in 1..3 loop - e := create_encrypted_json(i, 'hm'); - - PERFORM assert_count( - format('eql_v2.neq(eql_v2_encrypted, eql_v2_encrypted) with unique index term %s of 3', i), - format('SELECT e FROM encrypted WHERE eql_v2.neq(e, %L);', e), - 2); - end loop; - - -- record not in database - e := create_encrypted_json(91347, 'hm'); - - PERFORM assert_no_result( - 'eql_v2_encrypted <> eql_v2_encrypted with no matching record', - format('SELECT e FROM encrypted WHERE e <> %L;', e)); - - END; -$$ LANGUAGE plpgsql; - - - --- ======================================================================== - --- ------------------------------------------------------------------------ --- ------------------------------------------------------------------------ --- --- Blake equality - eql_v2_encrypted <> eql_v2_encrypted --- -DO $$ -DECLARE - e eql_v2_encrypted; - BEGIN - - for i in 1..3 loop - e := create_encrypted_json(i, 'b3'); - - PERFORM assert_result( - format('eql_v2_encrypted <> eql_v2_encrypted with blake3 index term %s of 3', i), - format('SELECT e FROM encrypted WHERE e <> %L;', e)); - - end loop; - - -- remove the ore index term - e := create_encrypted_json(91347, 'b3'); - - PERFORM assert_no_result( - 'eql_v2_encrypted <> eql_v2_encrypted with no matching record', - format('SELECT e FROM encrypted WHERE e <> %L;', e)); - - END; -$$ LANGUAGE plpgsql; - - --- ------------------------------------------------------------------------ --- ------------------------------------------------------------------------ --- --- Blake3 equality - eql_v2.neq(eql_v2_encrypted, eql_v2_encrypted) --- -DO $$ -DECLARE - e eql_v2_encrypted; - BEGIN - - for i in 1..3 loop - e := create_encrypted_json(i, 'b3'); - - PERFORM assert_result( - format('eql_v2.neq(eql_v2_encrypted, eql_v2_encrypted) with unique index term %s of 3', i), - format('SELECT e FROM encrypted WHERE eql_v2.neq(e, %L);', e)); - end loop; - - -- remove the ore index term - e := create_encrypted_json(91347, 'b3'); - - PERFORM assert_no_result( - 'eql_v2.neq(eql_v2_encrypted, eql_v2_encrypted) with no matching record', - format('SELECT e FROM encrypted WHERE eql_v2.neq(e, %L);', e)); - - END; -$$ LANGUAGE plpgsql; - - --- ------------------------------------------------------------------------ --- ------------------------------------------------------------------------ --- --- Blake3 equality - eql_v2_encrypted = jsonb --- -DO $$ -DECLARE - e jsonb; - BEGIN - for i in 1..3 loop - - -- remove the default - e := create_encrypted_json(i, 'b3'); - - PERFORM assert_result( - format('eql_v2_encrypted = jsonb with unique index term %s of 3', i), - format('SELECT e FROM encrypted WHERE e <> %L::jsonb;', e)); - - PERFORM assert_result( - format('jsonb = eql_v2_encrypted with unique index term %s of 3', i), - format('SELECT e FROM encrypted WHERE %L::jsonb = e', e)); - end loop; - - e := create_encrypted_json(91347, 'b3'); - - PERFORM assert_no_result( - 'eql_v2_encrypted = jsonb with no matching record', - format('SELECT e FROM encrypted WHERE e <> %L::jsonb', e)); - - PERFORM assert_no_result( - 'jsonb = eql_v2_encrypted with no matching record', - format('SELECT e FROM encrypted WHERE %L::jsonb = e', e)); - - END; -$$ LANGUAGE plpgsql; - - -SELECT drop_table_with_encrypted(); \ No newline at end of file diff --git a/src/operators/<@_test.sql b/src/operators/<@_test.sql deleted file mode 100644 index 0f500099..00000000 --- a/src/operators/<@_test.sql +++ /dev/null @@ -1,44 +0,0 @@ -\set ON_ERROR_STOP on - -SELECT create_table_with_encrypted(); -SELECT seed_encrypted_json(); - --- ------------------------------------------------------------------------ --- ------------------------------------------------------------------------ --- --- ore_cllw_u64_8 equality --- --- Test data is in form '{"hello": "{one | two | three}", "n": {10 | 20 | 30} }' --- --- Paths --- $ -> bca213de9ccce676fa849ff9c4807963 --- $.hello -> a7cea93975ed8c01f861ccb6bd082784 --- $.n -> 2517068c0d1f9d4d41d2c666211f785e --- --- -DO $$ -DECLARE - sv eql_v2_encrypted; - term eql_v2_encrypted; - BEGIN - - -- This extracts the data associated with the field from the test eql_v2_encrypted - sv := get_numeric_ste_vec_10()::eql_v2_encrypted; - -- extract the term at $.n returned as eql_v2_encrypted - term := sv->'a7cea93975ed8c01f861ccb6bd082784'::text; - - -- -- -- -- $.n - PERFORM assert_result( - format('eql_v2_encrypted = eql_v2_encrypted with ore_cllw_u64_8 index term'), - format('SELECT e FROM encrypted WHERE %L::eql_v2_encrypted <@ e', term)); - - PERFORM assert_count( - format('eql_v2_encrypted = eql_v2_encrypted with ore index term'), - format('SELECT e FROM encrypted WHERE %L::eql_v2_encrypted <@ e', term), - 1); - - END; -$$ LANGUAGE plpgsql; - - -SELECT drop_table_with_encrypted(); \ No newline at end of file diff --git a/src/operators/<_test.sql b/src/operators/<_test.sql deleted file mode 100644 index a872cc59..00000000 --- a/src/operators/<_test.sql +++ /dev/null @@ -1,159 +0,0 @@ -\set ON_ERROR_STOP on - -SELECT create_table_with_encrypted(); -SELECT seed_encrypted_json(); - - --- ------------------------------------------------------------------------ --- ------------------------------------------------------------------------ --- --- eql_v2_encrypted < eql_v2_encrypted with ore_cllw_u64_8 index --- --- Test data is in form '{"hello": "{one | two | three}", "n": {10 | 20 | 30} }' --- --- Paths --- $ -> bca213de9ccce676fa849ff9c4807963 --- $.hello -> a7cea93975ed8c01f861ccb6bd082784 --- $.n -> 2517068c0d1f9d4d41d2c666211f785e --- --- -- -DO $$ -DECLARE - sv eql_v2_encrypted; - term eql_v2_encrypted; - BEGIN - - -- This extracts the data associated with the field from the test eql_v2_encrypted - -- json n: 30 - sv := get_numeric_ste_vec_30()::eql_v2_encrypted; - -- extract the term at $.n returned as eql_v2_encrypted - term := sv->'2517068c0d1f9d4d41d2c666211f785e'::text; - - -- -- -- -- $.n - PERFORM assert_result( - format('eql_v2_encrypted < eql_v2_encrypted with ore_cllw_u64_8 index term'), - format('SELECT e FROM encrypted WHERE e->''2517068c0d1f9d4d41d2c666211f785e''::text < %L::eql_v2_encrypted', term)); - - PERFORM assert_count( - format('eql_v2_encrypted < eql_v2_encrypted with ore index term'), - format('SELECT e FROM encrypted WHERE e->''2517068c0d1f9d4d41d2c666211f785e''::text < %L::eql_v2_encrypted', term), - 2); - - -- -- Check the $.hello path - -- -- Returned encrypted does not have ore_cllw_u64_8 - -- Falls back to jsonb literal comparison - PERFORM assert_result( - format('eql_v2_encrypted < eql_v2_encrypted with ore index term'), - format('SELECT e FROM encrypted WHERE e->''a7cea93975ed8c01f861ccb6bd082784''::text < %L::eql_v2_encrypted', term)); - - END; -$$ LANGUAGE plpgsql; - - --- ------------------------------------------------------------------------ --- ------------------------------------------------------------------------ --- --- eql_v2_encrypted < eql_v2_encrypted with ore_cllw_var_8 index --- --- Test data is in form '{"hello": "{one | two | three}", "n": {10 | 20 | 30} }' --- --- Paths --- $ -> bca213de9ccce676fa849ff9c4807963 --- $.hello -> a7cea93975ed8c01f861ccb6bd082784 --- $.n -> 2517068c0d1f9d4d41d2c666211f785e --- --- -- -DO $$ -DECLARE - sv eql_v2_encrypted; - term eql_v2_encrypted; - BEGIN - - -- This extracts the data associated with the field from the test eql_v2_encrypted - sv := get_numeric_ste_vec_30()::eql_v2_encrypted; - -- extract the term at $.n returned as eql_v2_encrypted - term := sv->'a7cea93975ed8c01f861ccb6bd082784'::text; - - -- -- -- -- $.n - PERFORM assert_result( - format('eql_v2_encrypted < eql_v2_encrypted with ore_cllw_var_8 index term'), - format('SELECT e FROM encrypted WHERE e->''a7cea93975ed8c01f861ccb6bd082784''::text < %L::eql_v2_encrypted', term)); - - PERFORM assert_count( - format('eql_v2_encrypted < eql_v2_encrypted with ore_cllw_var_8 index term'), - format('SELECT e FROM encrypted WHERE e->''a7cea93975ed8c01f861ccb6bd082784''::text < %L::eql_v2_encrypted', term), - 1); - - -- -- Check the $.n path - -- -- Returned encrypted does not have ore_cllw_var_8 - -- Falls back to jsonb literal comparison - PERFORM assert_result( - format('eql_v2_encrypted < eql_v2_encrypted with ore_cllw_var_8 index term'), - format('SELECT e FROM encrypted WHERE e->''2517068c0d1f9d4d41d2c666211f785e''::text < %L::eql_v2_encrypted', term)); - - END; -$$ LANGUAGE plpgsql; - - - --- --- ORE - eql_v2_encrypted < eql_v2_encrypted --- -DO $$ -DECLARE - e eql_v2_encrypted; - ore_term eql_v2_encrypted; - BEGIN - SELECT ore.e FROM ore WHERE id = 42 INTO ore_term; - - PERFORM assert_count( - 'eql_v2_encrypted < eql_v2_encrypted', - format('SELECT id FROM ore WHERE e < %L ORDER BY e DESC', ore_term), - 41); - - -- Record with a Numeric ORE term of 1 - e := create_encrypted_ore_json(1); - - PERFORM assert_no_result( - format('eql_v2_encrypted < eql_v2_encrypted'), - format('SELECT e FROM encrypted WHERE e < %L;', e)); - - -- Record with a Numeric ORE term of 42 - e := create_encrypted_ore_json(42); - - PERFORM assert_result( - 'eql_v2_encrypted < eql_v2_encrypted', - format('SELECT e FROM encrypted WHERE e < %L', e)); - - PERFORM assert_count( - 'eql_v2_encrypted < eql_v2_encrypted', - format('SELECT e FROM encrypted WHERE e < %L', e), - 3); - END; -$$ LANGUAGE plpgsql; - - --- -- --- -- ORE - eql_v2.lt(a eql_v2_encrypted, b eql_v2_encrypted) --- -- -DO $$ -DECLARE - e eql_v2_encrypted; - ore_term jsonb; - BEGIN - -- Record with a Numeric ORE term of 42 - e := create_encrypted_ore_json(42); - - PERFORM assert_result( - 'eql_v2.lt(a eql_v2_encrypted, b eql_v2_encrypted)', - format('SELECT e FROM encrypted WHERE eql_v2.lt(e, %L)', e)); - - PERFORM assert_count( - 'eql_v2.lt(a eql_v2_encrypted, b eql_v2_encrypted)', - format('SELECT e FROM encrypted WHERE eql_v2.lt(e, %L)', e), - 3); - END; -$$ LANGUAGE plpgsql; - - -SELECT drop_table_with_encrypted(); \ No newline at end of file diff --git a/src/operators/=_ore_cllw_u64_8_test.sql b/src/operators/=_ore_cllw_u64_8_test.sql deleted file mode 100644 index 6498214a..00000000 --- a/src/operators/=_ore_cllw_u64_8_test.sql +++ /dev/null @@ -1,56 +0,0 @@ -\set ON_ERROR_STOP on - -SELECT create_table_with_encrypted(); -SELECT seed_encrypted_json(); - - --- ======================================================================== - - --- ------------------------------------------------------------------------ --- ------------------------------------------------------------------------ --- --- ore_cllw_u64_8 equality --- --- Test data is in form '{"hello": "{one | two | three}", "n": {10 | 20 | 30} }' --- --- Paths --- $ -> bca213de9ccce676fa849ff9c4807963 --- $.hello -> a7cea93975ed8c01f861ccb6bd082784 --- $.n -> 2517068c0d1f9d4d41d2c666211f785e --- --- -DO $$ -DECLARE - sv eql_v2_encrypted; - term eql_v2_encrypted; - BEGIN - - -- This extracts the data associated with the field from the test eql_v2_encrypted - -- json n: 10 - sv := get_numeric_ste_vec_10()::eql_v2_encrypted; - -- extract the term at $.n returned as eql_v2_encrypted - term := sv->'2517068c0d1f9d4d41d2c666211f785e'::text; - - -- -- -- -- $.n - PERFORM assert_result( - format('eql_v2_encrypted = eql_v2_encrypted with ore_cllw_u64_8 index term'), - format('SELECT e FROM encrypted WHERE (e->''2517068c0d1f9d4d41d2c666211f785e''::text) = %L::eql_v2_encrypted', term)); - - PERFORM assert_count( - format('eql_v2_encrypted = eql_v2_encrypted with ore index term'), - format('SELECT e FROM encrypted WHERE e->''2517068c0d1f9d4d41d2c666211f785e''::text = %L::eql_v2_encrypted', term), - 1); - - -- -- Check the $.hello path - -- -- Returned encrypted does not have ore_cllw_u64_8 - PERFORM assert_no_result( - format('eql_v2_encrypted = eql_v2_encrypted with ore index term'), - format('SELECT e FROM encrypted WHERE e->''a7cea93975ed8c01f861ccb6bd082784''::text = %L::eql_v2_encrypted', term)); - - END; -$$ LANGUAGE plpgsql; - - - -SELECT drop_table_with_encrypted(); \ No newline at end of file diff --git a/src/operators/=_ore_cllw_var_8_test.sql b/src/operators/=_ore_cllw_var_8_test.sql deleted file mode 100644 index 1eb7ee8b..00000000 --- a/src/operators/=_ore_cllw_var_8_test.sql +++ /dev/null @@ -1,53 +0,0 @@ -\set ON_ERROR_STOP on - -SELECT create_table_with_encrypted(); -SELECT seed_encrypted_json(); - - --- ------------------------------------------------------------------------ --- ------------------------------------------------------------------------ --- --- ore_cllw_u64_8 equality --- --- Test data is in form '{"hello": "{one | two | three}", "n": {10 | 20 | 30} }' --- --- Paths --- $ -> bca213de9ccce676fa849ff9c4807963 --- $.hello -> a7cea93975ed8c01f861ccb6bd082784 --- $.n -> 2517068c0d1f9d4d41d2c666211f785e --- --- -DO $$ -DECLARE - sv eql_v2_encrypted; - term eql_v2_encrypted; - BEGIN - - -- This extracts the data associated with the field from the test eql_v2_encrypted - -- json n: 10 - sv := get_numeric_ste_vec_10()::eql_v2_encrypted; - -- extract the term at $.n returned as eql_v2_encrypted - term := sv->'a7cea93975ed8c01f861ccb6bd082784'::text; - - -- -- -- -- $.n - PERFORM assert_result( - format('eql_v2_encrypted = eql_v2_encrypted with ore_cllw_u64_8 index term'), - format('SELECT e FROM encrypted WHERE (e->''a7cea93975ed8c01f861ccb6bd082784''::text) = %L::eql_v2_encrypted', term)); - - PERFORM assert_count( - format('eql_v2_encrypted = eql_v2_encrypted with ore index term'), - format('SELECT e FROM encrypted WHERE e->''a7cea93975ed8c01f861ccb6bd082784''::text = %L::eql_v2_encrypted', term), - 1); - - -- -- Check the $.n path - -- -- Returned encrypted does not have ore_cllw_u64_8 - PERFORM assert_no_result( - format('eql_v2_encrypted = eql_v2_encrypted with ore index term'), - format('SELECT e FROM encrypted WHERE e->''2517068c0d1f9d4d41d2c666211f785e''::text = %L::eql_v2_encrypted', term)); - - END; -$$ LANGUAGE plpgsql; - - - -SELECT drop_table_with_encrypted(); \ No newline at end of file diff --git a/src/operators/=_ore_test.sql b/src/operators/=_ore_test.sql deleted file mode 100644 index 2f75a8d0..00000000 --- a/src/operators/=_ore_test.sql +++ /dev/null @@ -1,87 +0,0 @@ -\set ON_ERROR_STOP on - -SELECT create_table_with_encrypted(); -SELECT seed_encrypted_json(); - - --- --- ORE - eql_v2_encrypted = eql_v2_encrypted --- -DO $$ -DECLARE - e eql_v2_encrypted; - ore_term jsonb; - BEGIN - - -- Record with a Numeric ORE term of 42 - e := create_encrypted_ore_json(42); - PERFORM seed_encrypted(e); - - PERFORM assert_result( - 'eql_v2_encrypted = eql_v2_encrypted', - format('SELECT e FROM encrypted WHERE e = %L::eql_v2_encrypted', e)); - - PERFORM assert_count( - format('eql_v2_encrypted = eql_v2_encrypted'), - format('SELECT e FROM encrypted WHERE e = %L;', e), - 1); - - e := create_encrypted_ore_json(20); - - PERFORM assert_result( - 'eql_v2_encrypted = eql_v2_encrypted', - format('SELECT e FROM encrypted WHERE e = %L::eql_v2_encrypted', e)); - - PERFORM assert_count( - format('eql_v2_encrypted = eql_v2_encrypted'), - format('SELECT e FROM encrypted WHERE e = %L;', e), - 1); - END; -$$ LANGUAGE plpgsql; - - --- --- ORE - eql_v2.gte(a eql_v2_encrypted, b eql_v2_encrypted) --- -DO $$ -DECLARE - e eql_v2_encrypted; - ore_term jsonb; - BEGIN - -- Reset data - PERFORM seed_encrypted_json(); - - -- Record with a Numeric ORE term of 20 - e := create_encrypted_ore_json(20); - PERFORM seed_encrypted(e); - - PERFORM assert_result( - 'eql_v2.eq(a eql_v2_encrypted, b eql_v2_encrypted)', - format('SELECT e FROM encrypted WHERE eql_v2.eq(e, %L)', e)); - - -- include - PERFORM assert_count( - 'eql_v2.eq(a eql_v2_encrypted, b eql_v2_encrypted)', - format('SELECT e FROM encrypted WHERE eql_v2.eq(e, %L)', e), - 2); - - -- Record with a Numeric ORE term of 30 - e := create_encrypted_ore_json(30); - - PERFORM assert_result( - 'eql_v2.get(a eql_v2_encrypted, b eql_v2_encrypted)', - format('SELECT e FROM encrypted WHERE eql_v2.eq(e, %L)', e)); - - PERFORM assert_count( - 'eql_v2.get(a eql_v2_encrypted, b eql_v2_encrypted)', - format('SELECT e FROM encrypted WHERE eql_v2.eq(e, %L)', e), - 1); - END; -$$ LANGUAGE plpgsql; - - - --- ======================================================================== - - -SELECT drop_table_with_encrypted(); \ No newline at end of file diff --git a/src/operators/=_test.sql b/src/operators/=_test.sql deleted file mode 100644 index 1f39e717..00000000 --- a/src/operators/=_test.sql +++ /dev/null @@ -1,196 +0,0 @@ -\set ON_ERROR_STOP on - -SELECT create_table_with_encrypted(); -SELECT seed_encrypted_json(); - - --- --- Unique equality - eql_v2_encrypted = eql_v2_encrypted --- -DO $$ -DECLARE - e eql_v2_encrypted; - BEGIN - - for i in 1..3 loop - e := create_encrypted_json(i, 'hm'); - - PERFORM assert_result( - format('eql_v2_encrypted = eql_v2_encrypted with unique index term %s of 3', i), - format('SELECT e FROM encrypted WHERE e = %L;', e)); - - end loop; - - -- remove the ore index term - e := create_encrypted_json(91347, 'hm'); - - PERFORM assert_no_result( - 'eql_v2_encrypted = eql_v2_encrypted with no matching record', - format('SELECT e FROM encrypted WHERE e = %L;', e)); - - END; -$$ LANGUAGE plpgsql; - - --- --- Unique equality - eql_v2.eq(eql_v2_encrypted, eql_v2_encrypted) --- -DO $$ -DECLARE - e eql_v2_encrypted; - BEGIN - - for i in 1..3 loop - e := create_encrypted_json(i)::jsonb-'ob'; - - PERFORM assert_result( - format('eql_v2.eq(eql_v2_encrypted, eql_v2_encrypted) with unique index term %s of 3', i), - format('SELECT e FROM encrypted WHERE eql_v2.eq(e, %L);', e)); - end loop; - - -- remove the ore index term - e := create_encrypted_json(91347)::jsonb-'ob'; - - PERFORM assert_no_result( - 'eql_v2.eq(eql_v2_encrypted, eql_v2_encrypted) with no matching record', - format('SELECT e FROM encrypted WHERE eql_v2.eq(e, %L);', e)); - - END; -$$ LANGUAGE plpgsql; - - --- --- Unique equality - eql_v2_encrypted = jsonb --- -DO $$ -DECLARE - e jsonb; - BEGIN - for i in 1..3 loop - - -- remove the default - e := create_encrypted_json(i)::jsonb-'ob'; - - PERFORM assert_result( - format('eql_v2_encrypted = jsonb with unique index term %s of 3', i), - format('SELECT e FROM encrypted WHERE e = %L::jsonb;', e)); - - PERFORM assert_result( - format('jsonb = eql_v2_encrypted with unique index term %s of 3', i), - format('SELECT e FROM encrypted WHERE %L::jsonb = e', e)); - end loop; - - e := create_encrypted_json(91347)::jsonb-'ob'; - - PERFORM assert_no_result( - 'eql_v2_encrypted = jsonb with no matching record', - format('SELECT e FROM encrypted WHERE e = %L::jsonb', e)); - - PERFORM assert_no_result( - 'jsonb = eql_v2_encrypted with no matching record', - format('SELECT e FROM encrypted WHERE %L::jsonb = e', e)); - - END; -$$ LANGUAGE plpgsql; - - - --- ======================================================================== - --- ------------------------------------------------------------------------ --- ------------------------------------------------------------------------ --- --- Blake equality - eql_v2_encrypted = eql_v2_encrypted --- -DO $$ -DECLARE - e eql_v2_encrypted; - BEGIN - - for i in 1..3 loop - e := create_encrypted_json(i, 'b3'); - - PERFORM assert_result( - format('eql_v2_encrypted = eql_v2_encrypted with unique index term %s of 3', i), - format('SELECT e FROM encrypted WHERE e = %L;', e)); - - end loop; - - -- remove the ore index term - e := create_encrypted_json(91347, 'b3'); - - PERFORM assert_no_result( - 'eql_v2_encrypted = eql_v2_encrypted with no matching record', - format('SELECT e FROM encrypted WHERE e = %L;', e)); - - END; -$$ LANGUAGE plpgsql; - - --- ------------------------------------------------------------------------ --- ------------------------------------------------------------------------ --- --- Blake3 equality - eql_v2.eq(eql_v2_encrypted, eql_v2_encrypted) --- -DO $$ -DECLARE - e eql_v2_encrypted; - BEGIN - - for i in 1..3 loop - e := create_encrypted_json(i, 'b3'); - - PERFORM assert_result( - format('eql_v2.eq(eql_v2_encrypted, eql_v2_encrypted) with unique index term %s of 3', i), - format('SELECT e FROM encrypted WHERE eql_v2.eq(e, %L);', e)); - end loop; - - -- remove the ore index term - e := create_encrypted_json(91347, 'b3'); - - PERFORM assert_no_result( - 'eql_v2.eq(eql_v2_encrypted, eql_v2_encrypted) with no matching record', - format('SELECT e FROM encrypted WHERE eql_v2.eq(e, %L);', e)); - - END; -$$ LANGUAGE plpgsql; - - --- ------------------------------------------------------------------------ --- ------------------------------------------------------------------------ --- --- Blake3 equality - eql_v2_encrypted = jsonb --- -DO $$ -DECLARE - e jsonb; - BEGIN - for i in 1..3 loop - - -- remove the default - e := create_encrypted_json(i, 'b3'); - - PERFORM assert_result( - format('eql_v2_encrypted = jsonb with unique index term %s of 3', i), - format('SELECT e FROM encrypted WHERE e = %L::jsonb;', e)); - - PERFORM assert_result( - format('jsonb = eql_v2_encrypted with unique index term %s of 3', i), - format('SELECT e FROM encrypted WHERE %L::jsonb = e', e)); - end loop; - - e := create_encrypted_json(91347, 'b3'); - - PERFORM assert_no_result( - 'eql_v2_encrypted = jsonb with no matching record', - format('SELECT e FROM encrypted WHERE e = %L::jsonb', e)); - - PERFORM assert_no_result( - 'jsonb = eql_v2_encrypted with no matching record', - format('SELECT e FROM encrypted WHERE %L::jsonb = e', e)); - - END; -$$ LANGUAGE plpgsql; - - -SELECT drop_table_with_encrypted(); \ No newline at end of file diff --git a/src/operators/>=_test.sql b/src/operators/>=_test.sql deleted file mode 100644 index 82e22e8e..00000000 --- a/src/operators/>=_test.sql +++ /dev/null @@ -1,175 +0,0 @@ -\set ON_ERROR_STOP on - -SELECT create_table_with_encrypted(); -SELECT seed_encrypted_json(); - - --- ------------------------------------------------------------------------ --- ------------------------------------------------------------------------ --- --- eql_v2_encrypted < eql_v2_encrypted with ore_cllw_var_8 index --- --- Test data is in form '{"hello": "{one | two | three}", "n": {10 | 20 | 30} }' --- --- Paths --- $ -> bca213de9ccce676fa849ff9c4807963 --- $.hello -> a7cea93975ed8c01f861ccb6bd082784 --- $.n -> 2517068c0d1f9d4d41d2c666211f785e --- --- -- -DO $$ -DECLARE - sv eql_v2_encrypted; - term eql_v2_encrypted; - BEGIN - - -- This extracts the data associated with the field from the test eql_v2_encrypted - -- json n: 30 - sv := get_numeric_ste_vec_10()::eql_v2_encrypted; - -- extract the term at $.n returned as eql_v2_encrypted - term := sv->'2517068c0d1f9d4d41d2c666211f785e'::text; - - -- -- -- -- $.n - PERFORM assert_result( - format('eql_v2_encrypted >= eql_v2_encrypted with ore_cllw_u64_8 index term'), - format('SELECT e FROM encrypted WHERE e->''2517068c0d1f9d4d41d2c666211f785e''::text >= %L::eql_v2_encrypted', term)); - - PERFORM assert_count( - format('eql_v2_encrypted >= eql_v2_encrypted with ore index term'), - format('SELECT e FROM encrypted WHERE e->''2517068c0d1f9d4d41d2c666211f785e''::text >= %L::eql_v2_encrypted', term), - 3); - - -- -- Check the $.hello path - -- -- Returned encrypted does not have ore_cllw_u64_8 - -- Falls back to jsonb literal comparison - PERFORM assert_result( - format('eql_v2_encrypted >= eql_v2_encrypted with ore index term'), - format('SELECT e FROM encrypted WHERE e->''a7cea93975ed8c01f861ccb6bd082784''::text >= %L::eql_v2_encrypted', term)); - - END; -$$ LANGUAGE plpgsql; - - --- ------------------------------------------------------------------------ --- ------------------------------------------------------------------------ --- --- eql_v2_encrypted < eql_v2_encrypted with ore_cllw_var_8 index --- --- Test data is in form '{"hello": "{one | two | three}", "n": {10 | 20 | 30} }' --- --- Paths --- $ -> bca213de9ccce676fa849ff9c4807963 --- $.hello -> a7cea93975ed8c01f861ccb6bd082784 --- $.n -> 2517068c0d1f9d4d41d2c666211f785e --- --- -- -DO $$ -DECLARE - sv eql_v2_encrypted; - term eql_v2_encrypted; - BEGIN - - -- This extracts the data associated with the field from the test eql_v2_encrypted - -- json n: 30 - sv := get_numeric_ste_vec_10()::eql_v2_encrypted; - -- extract the term at $.n returned as eql_v2_encrypted - term := sv->'a7cea93975ed8c01f861ccb6bd082784'::text; - - -- -- -- -- $.n - PERFORM assert_result( - format('eql_v2_encrypted >= eql_v2_encrypted with ore_cllw_u64_8 index term'), - format('SELECT e FROM encrypted WHERE e->''a7cea93975ed8c01f861ccb6bd082784''::text >= %L::eql_v2_encrypted', term)); - - PERFORM assert_count( - format('eql_v2_encrypted >= eql_v2_encrypted with ore index term'), - format('SELECT e FROM encrypted WHERE e->''a7cea93975ed8c01f861ccb6bd082784''::text >= %L::eql_v2_encrypted', term), - 3); - - -- -- Check the $.hello path - -- -- Returned encrypted does not have ore_cllw_u64_8 - -- Falls back to jsonb literal comparison - PERFORM assert_result( - format('eql_v2_encrypted >= eql_v2_encrypted with ore index term'), - format('SELECT e FROM encrypted WHERE e->''2517068c0d1f9d4d41d2c666211f785e''::text >= %L::eql_v2_encrypted', term)); - - END; -$$ LANGUAGE plpgsql; - - --- --- ORE - eql_v2_encrypted >= eql_v2_encrypted --- -DO $$ -DECLARE - e eql_v2_encrypted; - ore_term jsonb; - BEGIN - - -- Record with a Numeric ORE term of 12 - e := create_encrypted_ore_json(12); - PERFORM seed_encrypted(e); - - PERFORM assert_result( - 'eql_v2_encrypted >= eql_v2_encrypted', - format('SELECT e FROM encrypted WHERE e >= %L', e)); - - PERFORM assert_count( - format('eql_v2_encrypted >= eql_v2_encrypted'), - format('SELECT e FROM encrypted WHERE e >= %L;', e), - 3); - - e := create_encrypted_ore_json(20); - - PERFORM assert_result( - 'eql_v2_encrypted >= eql_v2_encrypted', - format('SELECT e FROM encrypted WHERE e >= %L::eql_v2_encrypted', e)); - - PERFORM assert_count( - format('eql_v2_encrypted >= eql_v2_encrypted'), - format('SELECT e FROM encrypted WHERE e >= %L;', e), - 2); - END; -$$ LANGUAGE plpgsql; - - --- --- ORE - eql_v2.gte(a eql_v2_encrypted, b eql_v2_encrypted) --- -DO $$ -DECLARE - e eql_v2_encrypted; - ore_term jsonb; - BEGIN - -- Reset data - PERFORM seed_encrypted_json(); - - -- Record with a Numeric ORE term of 42 - e := create_encrypted_ore_json(42); - PERFORM seed_encrypted(e); - - PERFORM assert_result( - 'eql_v2.gte(a eql_v2_encrypted, b eql_v2_encrypted)', - format('SELECT e FROM encrypted WHERE eql_v2.gte(e, %L)', e)); - - -- include - PERFORM assert_count( - 'eql_v2.gte(a eql_v2_encrypted, b eql_v2_encrypted)', - format('SELECT e FROM encrypted WHERE eql_v2.gte(e, %L)', e), - 1); - - -- Record with a Numeric ORE term of 30 - e := create_encrypted_ore_json(30); - - PERFORM assert_result( - 'eql_v2.get(a eql_v2_encrypted, b eql_v2_encrypted)', - format('SELECT e FROM encrypted WHERE eql_v2.gte(e, %L)', e)); - - PERFORM assert_count( - 'eql_v2.get(a eql_v2_encrypted, b eql_v2_encrypted)', - format('SELECT e FROM encrypted WHERE eql_v2.gte(e, %L)', e), - 2); - END; -$$ LANGUAGE plpgsql; - - -SELECT drop_table_with_encrypted(); \ No newline at end of file diff --git a/src/operators/>_test.sql b/src/operators/>_test.sql deleted file mode 100644 index d2429509..00000000 --- a/src/operators/>_test.sql +++ /dev/null @@ -1,159 +0,0 @@ -\set ON_ERROR_STOP on - -SELECT create_table_with_encrypted(); -SELECT seed_encrypted_json(); - - --- ------------------------------------------------------------------------ --- ------------------------------------------------------------------------ --- --- eql_v2_encrypted < eql_v2_encrypted with ore_cllw_u64_8 index --- --- Test data is in form '{"hello": "{one | two | three}", "n": {10 | 20 | 30} }' --- --- Paths --- $ -> bca213de9ccce676fa849ff9c4807963 --- $.hello -> a7cea93975ed8c01f861ccb6bd082784 --- $.n -> 2517068c0d1f9d4d41d2c666211f785e --- --- -- -DO $$ -DECLARE - sv eql_v2_encrypted; - term eql_v2_encrypted; - BEGIN - - -- This extracts the data associated with the field from the test eql_v2_encrypted - -- json n: 30 - sv := get_numeric_ste_vec_20()::eql_v2_encrypted; - -- extract the term at $.n returned as eql_v2_encrypted - term := sv->'2517068c0d1f9d4d41d2c666211f785e'::text; - - -- -- -- -- $.n - PERFORM assert_result( - format('eql_v2_encrypted > eql_v2_encrypted with ore_cllw_u64_8 index term'), - format('SELECT e FROM encrypted WHERE e->''2517068c0d1f9d4d41d2c666211f785e''::text > %L::eql_v2_encrypted', term)); - - PERFORM assert_count( - format('eql_v2_encrypted > eql_v2_encrypted with ore index term'), - format('SELECT e FROM encrypted WHERE e->''2517068c0d1f9d4d41d2c666211f785e''::text > %L::eql_v2_encrypted', term), - 1); - - -- Check the $.hello path - -- Returned encrypted does not have ore_cllw_u64_8 - -- Falls back to jsonb literal comparison - PERFORM assert_result( - format('eql_v2_encrypted > eql_v2_encrypted with ore index term'), - format('SELECT e FROM encrypted WHERE e->''a7cea93975ed8c01f861ccb6bd082784''::text > %L::eql_v2_encrypted', term)); - - END; -$$ LANGUAGE plpgsql; - - --- ------------------------------------------------------------------------ --- ------------------------------------------------------------------------ --- --- eql_v2_encrypted < eql_v2_encrypted with ore_cllw_var_8 index --- --- Test data is in form '{"hello": "{one | two | three}", "n": {10 | 20 | 30} }' --- --- Paths --- $ -> bca213de9ccce676fa849ff9c4807963 --- $.hello -> a7cea93975ed8c01f861ccb6bd082784 --- $.n -> 2517068c0d1f9d4d41d2c666211f785e --- --- -- -DO $$ -DECLARE - sv eql_v2_encrypted; - term eql_v2_encrypted; - BEGIN - - -- This extracts the data associated with the field from the test eql_v2_encrypted - -- json n: 30 - sv := get_numeric_ste_vec_30()::eql_v2_encrypted; - -- extract the term at $.n returned as eql_v2_encrypted - term := sv->'a7cea93975ed8c01f861ccb6bd082784'::text; - - -- -- -- -- $.n - PERFORM assert_result( - format('eql_v2_encrypted > eql_v2_encrypted with ore_cllw_u64_8 index term'), - format('SELECT e FROM encrypted WHERE e->''a7cea93975ed8c01f861ccb6bd082784''::text > %L::eql_v2_encrypted', term)); - - PERFORM assert_count( - format('eql_v2_encrypted > eql_v2_encrypted with ore index term'), - format('SELECT e FROM encrypted WHERE e->''a7cea93975ed8c01f861ccb6bd082784''::text > %L::eql_v2_encrypted', term), - 1); - - -- Check the $.hello path - -- Returned encrypted does not have ore_cllw_var_8 - -- Falls back to jsonb literal comparison - PERFORM assert_no_result( - format('eql_v2_encrypted > eql_v2_encrypted with ore index term'), - format('SELECT e FROM encrypted WHERE e->''2517068c0d1f9d4d41d2c666211f785e''::text > %L::eql_v2_encrypted', term)); - - END; -$$ LANGUAGE plpgsql; - - --- --- ORE - eql_v2_encrypted > eql_v2_encrypted --- -DO $$ -DECLARE - e eql_v2_encrypted; - ore_term eql_v2_encrypted; - BEGIN - SELECT ore.e FROM ore WHERE id = 42 INTO ore_term; - - PERFORM assert_count( - 'eql_v2_encrypted > eql_v2_encrypted', - format('SELECT id FROM ore WHERE e > %L ORDER BY e DESC', ore_term), - 57); - - -- Record with a Numeric ORE term of 1 - e := create_encrypted_ore_json(99); - - PERFORM assert_no_result( - format('eql_v2_encrypted > eql_v2_encrypted'), - format('SELECT e FROM encrypted WHERE e > %L;', e)); - - -- Record with a Numeric ORE term of 42 - e := create_encrypted_ore_json(17); - - PERFORM assert_result( - 'eql_v2_encrypted > eql_v2_encrypted', - format('SELECT e FROM encrypted WHERE e > %L', e)); - - PERFORM assert_count( - 'eql_v2_encrypted > eql_v2_encrypted', - format('SELECT e FROM encrypted WHERE e > %L', e), - 2); - END; -$$ LANGUAGE plpgsql; - - --- -- --- -- ORE - eql_v2.lt(a eql_v2_encrypted, b eql_v2_encrypted) --- -- -DO $$ -DECLARE - e eql_v2_encrypted; - ore_term jsonb; - BEGIN - -- Record with a Numeric ORE term of 42 - e := create_encrypted_ore_json(21); - - PERFORM assert_result( - 'eql_v2.lt(a eql_v2_encrypted, b eql_v2_encrypted)', - format('SELECT e FROM encrypted WHERE eql_v2.gt(e, %L)', e)); - - PERFORM assert_count( - 'eql_v2.lt(a eql_v2_encrypted, b eql_v2_encrypted)', - format('SELECT e FROM encrypted WHERE eql_v2.gt(e, %L)', e), - 1); - END; -$$ LANGUAGE plpgsql; - - -SELECT drop_table_with_encrypted(); \ No newline at end of file diff --git a/src/operators/@>_test.sql b/src/operators/@>_test.sql deleted file mode 100644 index 82494fec..00000000 --- a/src/operators/@>_test.sql +++ /dev/null @@ -1,94 +0,0 @@ -\set ON_ERROR_STOP on - -SELECT create_table_with_encrypted(); -SELECT seed_encrypted_json(); - - --- ------------------------------------------------------------------------ --- ------------------------------------------------------------------------ --- --- eql_v2_encrypted contains itself --- --- -DO $$ - DECLARE - a eql_v2_encrypted; - b eql_v2_encrypted; - BEGIN - - a := get_numeric_ste_vec_10()::eql_v2_encrypted; - b := get_numeric_ste_vec_10()::eql_v2_encrypted; - - ASSERT a @> b; - ASSERT b @> a; - END; -$$ LANGUAGE plpgsql; - - --- ------------------------------------------------------------------------ --- ------------------------------------------------------------------------ --- --- eql_v2_encrypted contains a term --- --- -DO $$ - DECLARE - a eql_v2_encrypted; - b eql_v2_encrypted; - term eql_v2_encrypted; - BEGIN - - a := get_numeric_ste_vec_10()::eql_v2_encrypted; - b := get_numeric_ste_vec_10()::eql_v2_encrypted; - - -- $.n - term := b->'2517068c0d1f9d4d41d2c666211f785e'::text; - - ASSERT a @> term; - - ASSERT NOT term @> a; - END; -$$ LANGUAGE plpgsql; - - - --- ------------------------------------------------------------------------ --- ------------------------------------------------------------------------ --- --- ore_cllw_u64_8 equality --- --- Test data is in form '{"hello": "{one | two | three}", "n": {10 | 20 | 30} }' --- --- Paths --- $ -> bca213de9ccce676fa849ff9c4807963 --- $.hello -> a7cea93975ed8c01f861ccb6bd082784 --- $.n -> 2517068c0d1f9d4d41d2c666211f785e --- --- -DO $$ -DECLARE - sv eql_v2_encrypted; - term eql_v2_encrypted; - BEGIN - - -- This extracts the data associated with the field from the test eql_v2_encrypted - sv := get_numeric_ste_vec_10()::eql_v2_encrypted; - -- extract the term at $.n returned as eql_v2_encrypted - term := sv->'a7cea93975ed8c01f861ccb6bd082784'::text; - - -- -- -- -- $.n - PERFORM assert_result( - format('eql_v2_encrypted = eql_v2_encrypted with ore_cllw_u64_8 index term'), - format('SELECT e FROM encrypted WHERE e @> %L::eql_v2_encrypted', term)); - - PERFORM assert_count( - format('eql_v2_encrypted = eql_v2_encrypted with ore index term'), - format('SELECT e FROM encrypted WHERE e @> %L::eql_v2_encrypted', term), - 1); - - END; -$$ LANGUAGE plpgsql; - - - -SELECT drop_table_with_encrypted(); \ No newline at end of file diff --git a/src/operators/compare_test.sql b/src/operators/compare_test.sql deleted file mode 100644 index c55c1b31..00000000 --- a/src/operators/compare_test.sql +++ /dev/null @@ -1,207 +0,0 @@ -\set ON_ERROR_STOP on - - --- Compare compare_ore_cllw_var_8 -DO $$ - DECLARE - a eql_v2_encrypted; - b eql_v2_encrypted; - c eql_v2_encrypted; - BEGIN - - -- {"hello": "world{N}"} - -- $.hello: d90b97b5207d30fe867ca816ed0fe4a7 - a := eql_v2.jsonb_path_query(create_encrypted_ste_vec_json(1), 'd90b97b5207d30fe867ca816ed0fe4a7'); - b := eql_v2.jsonb_path_query(create_encrypted_ste_vec_json(2), 'd90b97b5207d30fe867ca816ed0fe4a7'); - c := eql_v2.jsonb_path_query(create_encrypted_ste_vec_json(3), 'd90b97b5207d30fe867ca816ed0fe4a7'); - - ASSERT eql_v2.compare(a, a) = 0; - ASSERT eql_v2.compare(a, b) = -1; - ASSERT eql_v2.compare(a, c) = -1; - - ASSERT eql_v2.compare(b, b) = 0; - ASSERT eql_v2.compare(b, a) = 1; - ASSERT eql_v2.compare(b, c) = -1; - - ASSERT eql_v2.compare(c, c) = 0; - ASSERT eql_v2.compare(c, b) = 1; - ASSERT eql_v2.compare(c, a) = 1; - END; -$$ LANGUAGE plpgsql; - - --- Compare compare_ore_cllw_var_8 -DO $$ - DECLARE - a eql_v2_encrypted; - b eql_v2_encrypted; - c eql_v2_encrypted; - BEGIN - - -- {"number": {N}} - -- $.number: 3dba004f4d7823446e7cb71f6681b344 - a := eql_v2.jsonb_path_query(create_encrypted_ste_vec_json(1), '3dba004f4d7823446e7cb71f6681b344'); - b := eql_v2.jsonb_path_query(create_encrypted_ste_vec_json(5), '3dba004f4d7823446e7cb71f6681b344'); - c := eql_v2.jsonb_path_query(create_encrypted_ste_vec_json(10), '3dba004f4d7823446e7cb71f6681b344'); - - ASSERT eql_v2.compare(a, a) = 0; - ASSERT eql_v2.compare(a, b) = -1; - ASSERT eql_v2.compare(a, c) = -1; - - ASSERT eql_v2.compare(b, b) = 0; - ASSERT eql_v2.compare(b, a) = 1; - ASSERT eql_v2.compare(b, c) = -1; - - ASSERT eql_v2.compare(c, c) = 0; - ASSERT eql_v2.compare(c, b) = 1; - ASSERT eql_v2.compare(c, a) = 1; - END; -$$ LANGUAGE plpgsql; - - --- Compare ore_block_u64_8_256 -DO $$ - DECLARE - a eql_v2_encrypted; - b eql_v2_encrypted; - c eql_v2_encrypted; - BEGIN - - a := create_encrypted_ore_json(1); - b := create_encrypted_ore_json(21); - c := create_encrypted_ore_json(42); - - ASSERT eql_v2.compare(a, a) = 0; - ASSERT eql_v2.compare(a, b) = -1; - ASSERT eql_v2.compare(a, c) = -1; - - ASSERT eql_v2.compare(b, b) = 0; - ASSERT eql_v2.compare(b, a) = 1; - ASSERT eql_v2.compare(b, c) = -1; - - ASSERT eql_v2.compare(c, c) = 0; - ASSERT eql_v2.compare(c, b) = 1; - ASSERT eql_v2.compare(c, a) = 1; - END; -$$ LANGUAGE plpgsql; - - --- Compare blake3 -DO $$ - DECLARE - a eql_v2_encrypted; - b eql_v2_encrypted; - c eql_v2_encrypted; - BEGIN - a := create_encrypted_json(1, 'b3'); - b := create_encrypted_json(2, 'b3'); - c := create_encrypted_json(3, 'b3'); - - ASSERT eql_v2.compare(a, a) = 0; - ASSERT eql_v2.compare(a, b) = -1; - ASSERT eql_v2.compare(a, c) = -1; - - ASSERT eql_v2.compare(b, b) = 0; - ASSERT eql_v2.compare(b, a) = 1; - ASSERT eql_v2.compare(b, c) = -1; - - ASSERT eql_v2.compare(c, c) = 0; - ASSERT eql_v2.compare(c, b) = 1; - ASSERT eql_v2.compare(c, a) = 1; - END; -$$ LANGUAGE plpgsql; - - --- Compare hmac_256 -DO $$ - DECLARE - a eql_v2_encrypted; - b eql_v2_encrypted; - c eql_v2_encrypted; - BEGIN - a := create_encrypted_json(1, 'hm'); - b := create_encrypted_json(2, 'hm'); - c := create_encrypted_json(3, 'hm'); - - ASSERT eql_v2.compare(a, a) = 0; - ASSERT eql_v2.compare(a, b) = -1; - ASSERT eql_v2.compare(a, c) = -1; - - ASSERT eql_v2.compare(b, b) = 0; - ASSERT eql_v2.compare(b, a) = 1; - ASSERT eql_v2.compare(b, c) = -1; - - ASSERT eql_v2.compare(c, c) = 0; - ASSERT eql_v2.compare(c, b) = 1; - ASSERT eql_v2.compare(c, a) = 1; - END; -$$ LANGUAGE plpgsql; - - - --- Compare with no index terms --- This is a fallback to literal comparison of the encrypted data -DO $$ - DECLARE - a eql_v2_encrypted; - b eql_v2_encrypted; - c eql_v2_encrypted; - BEGIN - a := '{"a": 1}'::jsonb::eql_v2_encrypted; - b := '{"b": 2}'::jsonb::eql_v2_encrypted; - c := '{"c": 3}'::jsonb::eql_v2_encrypted; - - ASSERT eql_v2.compare(a, a) = 0; - ASSERT eql_v2.compare(a, b) = -1; - ASSERT eql_v2.compare(a, c) = -1; - - ASSERT eql_v2.compare(b, b) = 0; - ASSERT eql_v2.compare(b, a) = 1; - ASSERT eql_v2.compare(b, c) = -1; - - ASSERT eql_v2.compare(c, c) = 0; - ASSERT eql_v2.compare(c, b) = 1; - ASSERT eql_v2.compare(c, a) = 1; - END; -$$ LANGUAGE plpgsql; - - --- --- Compare hmac_256 when record has a `null` index of higher precedence --- TEST COVERAGE FOR BUG FIX --- --- ORE Block indexes `ob` are used in compare before hmac_256 indexes. --- If the index term is null `{"ob": null}` it should not be used --- Comparing two `null` values is evaluated as equality and hilarity ensues --- - -DO $$ - DECLARE - a eql_v2_encrypted; - b eql_v2_encrypted; - c eql_v2_encrypted; - BEGIN - -- generate with `hm` index - a := create_encrypted_json(1, 'hm'); - -- append `null` index - a := '{"ob": null}'::jsonb || a::jsonb; - - b := create_encrypted_json(2, 'hm'); - b := '{"ob": null}'::jsonb || b::jsonb; - - c := create_encrypted_json(3, 'hm'); - c := '{"ob": null}'::jsonb || c::jsonb; - - ASSERT eql_v2.compare(a, a) = 0; - ASSERT eql_v2.compare(a, b) = -1; - ASSERT eql_v2.compare(a, c) = -1; - - ASSERT eql_v2.compare(b, b) = 0; - ASSERT eql_v2.compare(b, a) = 1; - ASSERT eql_v2.compare(b, c) = -1; - - ASSERT eql_v2.compare(c, c) = 0; - ASSERT eql_v2.compare(c, b) = 1; - ASSERT eql_v2.compare(c, a) = 1; - END; -$$ LANGUAGE plpgsql; diff --git a/src/operators/operator_class_test.sql b/src/operators/operator_class_test.sql deleted file mode 100644 index efb836cb..00000000 --- a/src/operators/operator_class_test.sql +++ /dev/null @@ -1,239 +0,0 @@ -\set ON_ERROR_STOP on - --- --- ORE GROUP BY --- -DO $$ - BEGIN - - PERFORM create_table_with_encrypted(); - - -- Copy ORE data into encrypted - INSERT INTO encrypted(e) SELECT e FROM ore WHERE ore.id=42; - INSERT INTO encrypted(e) SELECT e FROM ore WHERE ore.id=42; - INSERT INTO encrypted(e) SELECT e FROM ore WHERE ore.id=42; - INSERT INTO encrypted(e) SELECT e FROM ore WHERE ore.id=42; - INSERT INTO encrypted(e) SELECT e FROM ore WHERE ore.id=99; - INSERT INTO encrypted(e) SELECT e FROM ore WHERE ore.id=99; - - -- Should be the rows with value of 42 - PERFORM assert_id( - 'GROUP BY eql_v2_encrypted', - 'SELECT count(id) FROM encrypted GROUP BY e ORDER BY count(id) DESC', - 4); - END; -$$ LANGUAGE plpgsql; - --- --- Confirm index used correctly --- -DO $$ - DECLARE - ore_term eql_v2_encrypted; - result text; - BEGIN - - PERFORM create_table_with_encrypted(); - - EXECUTE 'EXPLAIN ANALYZE SELECT e::jsonb FROM encrypted WHERE e = ''("{\"ob\": \"abc\"}")'';' into result; - - -- PERFORM eql_v2.log('', result); - - IF position('Bitmap Heap Scan on encrypted' in result) > 0 THEN - RAISE EXCEPTION 'Unexpected Bitmap Heap Scan: %', result; - ELSE - ASSERT true; - END IF; - - -- Add index - CREATE INDEX ON encrypted (e eql_v2.encrypted_operator_class); - - SELECT ore.e FROM ore WHERE id = 42 INTO ore_term; - EXECUTE format('EXPLAIN ANALYZE SELECT e::jsonb FROM encrypted WHERE e = %L::eql_v2_encrypted;', ore_term) into result; - - IF position('Bitmap Heap Scan on encrypted' in result) > 0 THEN - ASSERT true; - ELSE - RAISE EXCEPTION 'Expected Bitmap Heap Scan: %', result; - END IF; - - -- INDEX WILL BE USED - EXECUTE 'EXPLAIN ANALYZE SELECT e::jsonb FROM encrypted WHERE e = ''("{\"hm\": \"abc\"}")'';' into result; - - IF position('Bitmap Heap Scan on encrypted' in result) > 0 THEN - ASSERT true; - ELSE - RAISE EXCEPTION 'Expected Bitmap Heap Scan: %', result; - END IF; - - --- - EXECUTE 'EXPLAIN ANALYZE SELECT e::jsonb FROM encrypted WHERE e = ''("{\"blah\": \"vtha\"}")'';' into result; - - IF position('Bitmap Heap Scan on encrypted' in result) > 0 THEN - ASSERT true; - ELSE - RAISE EXCEPTION 'Expected Bitmap Heap Scan: %', result; - END IF; - - END; -$$ LANGUAGE plpgsql; - - - --- --- Adding index to table where values do not have an appropriate search term --- -DO $$ - DECLARE - ore_term eql_v2_encrypted; - result text; - BEGIN - - PERFORM create_table_with_encrypted(); - - INSERT INTO encrypted (e) VALUES ('("{\"bf\": \"[1, 2, 3]\"}")'); - - -- Add index - CREATE INDEX encrypted_index ON encrypted (e eql_v2.encrypted_operator_class); - - ANALYZE encrypted; - - EXECUTE 'EXPLAIN ANALYZE SELECT e::jsonb FROM encrypted WHERE e = ''("{\"bf\": \"[1,2,3]\"}")'';' into result; - - IF position('Seq Scan on encrypted' in result) > 0 THEN - ASSERT true; - ELSE - RAISE EXCEPTION 'Unexpected Seq Scan: %', result; - END IF; - - -- NO INDEX WILL BE USED - EXECUTE 'EXPLAIN ANALYZE SELECT e::jsonb FROM encrypted WHERE e = ''("{\"hm\": \"abc\"}")'';' into result; - - IF position('Seq Scan on encrypted' in result) > 0 THEN - ASSERT true; - ELSE - RAISE EXCEPTION 'Unexpected Seq Scan: %', result; - END IF; - - INSERT INTO encrypted (e) VALUES ('("{\"hm\": \"abc\"}")'); - INSERT INTO encrypted (e) VALUES ('("{\"hm\": \"def\"}")'); - INSERT INTO encrypted (e) VALUES ('("{\"hm\": \"ghi\"}")'); - - ANALYZE encrypted; - - -- STILL NO INDEX WILL BE USED - EXECUTE 'EXPLAIN ANALYZE SELECT e::jsonb FROM encrypted WHERE e = ''("{\"hm\": \"abc\"}")'';' into result; - - IF position('Seq Scan on encrypted' in result) > 0 THEN - ASSERT true; - ELSE - RAISE EXCEPTION 'Unexpected Seq Scan: %', result; - END IF; - - DROP INDEX encrypted_index; - CREATE INDEX encrypted_index ON encrypted (e eql_v2.encrypted_operator_class); - - ANALYZE encrypted; - - EXECUTE 'EXPLAIN ANALYZE SELECT e::jsonb FROM encrypted WHERE e = ''("{\"hm\": \"abc\"}")'';' into result; - - -- -- AND STILL NOPE - IF position('Seq Scan on encrypted' in result) > 0 THEN - ASSERT true; - ELSE - RAISE EXCEPTION 'Unexpected Seq Scan: %', result; - END IF; - - - TRUNCATE encrypted; - DROP INDEX encrypted_index; - CREATE INDEX encrypted_index ON encrypted (e eql_v2.encrypted_operator_class); - - INSERT INTO encrypted (e) VALUES ('("{\"hm\": \"abc\"}")'); - INSERT INTO encrypted (e) VALUES ('("{\"hm\": \"def\"}")'); - INSERT INTO encrypted (e) VALUES ('("{\"hm\": \"ghi\"}")'); - INSERT INTO encrypted (e) VALUES ('("{\"hm\": \"jkl\"}")'); - INSERT INTO encrypted (e) VALUES ('("{\"hm\": \"mno\"}")'); - - -- Literal row type type thing - EXECUTE 'EXPLAIN ANALYZE SELECT e::jsonb FROM encrypted WHERE e = ''("{\"hm\": \"abc\"}")'';' into result; - - IF position('Index Only Scan using encrypted' in result) > 0 THEN - ASSERT true; - ELSE - RAISE EXCEPTION 'Expected Index Only Scan: %', result; - END IF; - - -- Cast to jsonb - EXECUTE 'EXPLAIN ANALYZE SELECT e::jsonb FROM encrypted WHERE e = ''{"hm": "abc"}''::jsonb;' into result; - - -- INDEX IS NOT USED - IF position('Seq Scan on encrypted' in result) > 0 THEN - ASSERT true; - ELSE - RAISE EXCEPTION 'Unexpected Seq Scan: %', result; - END IF; - - -- Cast to jsonb to eql_v2_encrypted - EXECUTE 'EXPLAIN ANALYZE SELECT e::jsonb FROM encrypted WHERE e = ''{"hm": "abc"}''::jsonb::eql_v2_encrypted;' into result; - - IF position('Index Only Scan using encrypted' in result) > 0 THEN - ASSERT true; - ELSE - RAISE EXCEPTION 'Expected Index Only Scan: %', result; - END IF; - - -- Cast to text to eql_v2_encrypted - EXECUTE 'EXPLAIN ANALYZE SELECT e::jsonb FROM encrypted WHERE e = ''{"hm": "abc"}''::text::eql_v2_encrypted;' into result; - - IF position('Index Only Scan using encrypted' in result) > 0 THEN - ASSERT true; - ELSE - RAISE EXCEPTION 'Expected Index Only Scan: %', result; - END IF; - - -- Use to_encrypted with jsonb - EXECUTE 'EXPLAIN ANALYZE SELECT e::jsonb FROM encrypted WHERE e = eql_v2.to_encrypted(''{"hm": "abc"}''::jsonb);' into result; - - IF position('Index Only Scan using encrypted' in result) > 0 THEN - ASSERT true; - ELSE - RAISE EXCEPTION 'Expected Index Only Scan: %', result; - END IF; - - -- Use to_encrypted with text - EXECUTE 'EXPLAIN ANALYZE SELECT e::jsonb FROM encrypted WHERE e = eql_v2.to_encrypted(''{"hm": "abc"}'');' into result; - - - IF position('Index Only Scan using encrypted' in result) > 0 THEN - ASSERT true; - ELSE - RAISE EXCEPTION 'Expected Index Only Scan: %', result; - END IF; - - -- - SELECT ore.e FROM ore WHERE id = 42 INTO ore_term; - EXECUTE format('EXPLAIN ANALYZE SELECT e::jsonb FROM encrypted WHERE e = %L::eql_v2_encrypted;', ore_term) into result; - - IF position('Index Only Scan using encrypted' in result) > 0 THEN - ASSERT true; - ELSE - RAISE EXCEPTION 'Expected Index Only Scan: %', result; - END IF; - - - TRUNCATE encrypted; - PERFORM seed_encrypted_json(); - - EXECUTE 'EXPLAIN ANALYZE SELECT e::jsonb FROM encrypted WHERE e = ''("{\"blah\": \"vtha\"}")'';' into result; - - IF position('Index Only Scan using encrypted' in result) > 0 THEN - ASSERT true; - ELSE - RAISE EXCEPTION 'Expected Index Only Scan: %', result; - END IF; - - END; -$$ LANGUAGE plpgsql; - - diff --git a/src/operators/order_by_test.sql b/src/operators/order_by_test.sql deleted file mode 100644 index 35e7207f..00000000 --- a/src/operators/order_by_test.sql +++ /dev/null @@ -1,149 +0,0 @@ -\set ON_ERROR_STOP on - - - --- --- ORE - ORDER BY without function --- -DO $$ -DECLARE - e eql_v2_encrypted; - ore_term eql_v2_encrypted; - BEGIN - -- Pull a record from the ore table with value of "42" - SELECT ore.e FROM ore WHERE id = 42 INTO ore_term; - - -- lt - PERFORM assert_count( - 'ORDER BY eql_v2.order_by(e) DESC', - format('SELECT id FROM ore WHERE e < %L ORDER BY e DESC', ore_term), - 41); - - PERFORM assert_result( - 'ORDER BY e DESC returns correct record', - format('SELECT id FROM ore WHERE e < %L ORDER BY e DESC LIMIT 1', ore_term), - '41'); - - PERFORM assert_result( - 'ORDER BY e ASC', - format('SELECT id FROM ore WHERE e < %L ORDER BY e ASC LIMIT 1', ore_term), - '1'); - - -- gt - PERFORM assert_count( - 'ORDER BY eql_v2.order_by(e) DESC', - format('SELECT id FROM ore WHERE e > %L ORDER BY e ASC', ore_term), - 57); - - PERFORM assert_result( - 'ORDER BY e DESC returns correct record', - format('SELECT id FROM ore WHERE e > %L ORDER BY e DESC LIMIT 1', ore_term), - '99'); - - PERFORM assert_result( - 'ORDER BY e ASC', - format('SELECT id FROM ore WHERE e > %L ORDER BY e ASC LIMIT 1', ore_term), - '43'); - - END; -$$ LANGUAGE plpgsql; - - -SELECT create_table_with_encrypted(); - --- --- ORE - ORDER BY NULL handling --- -DO $$ -DECLARE - e eql_v2_encrypted; - ore_term eql_v2_encrypted; - BEGIN - - -- Insert records with NULL values - -- record with ID=1 and e=NULL - INSERT INTO encrypted(e) VALUES (NULL::jsonb::eql_v2_encrypted); - - - -- Pull records from the ore table and insert - SELECT ore.e FROM ore WHERE id = 42 INTO ore_term; - -- record with ID=2 and e=42 - INSERT INTO encrypted(e) VALUES (ore_term); - - - SELECT ore.e FROM ore WHERE id = 3 INTO ore_term; - -- record with ID=3 and e=3 - INSERT INTO encrypted(e) VALUES (ore_term); - - - -- record with ID=4 and e=NULL - INSERT INTO encrypted(e) VALUES (NULL::jsonb::eql_v2_encrypted); - - PERFORM assert_result( - 'ORDER BY encrypted', - format('SELECT id FROM encrypted ORDER BY e'), - '3'); - - PERFORM assert_result( - 'ORDER BY encrypted ASC', - format('SELECT id FROM encrypted ORDER BY e ASC'), - '3'); - - PERFORM assert_result( - 'ORDER BY eql_v2.order_by(e) ASC NULLS FIRST', - format('SELECT id FROM encrypted ORDER BY e ASC NULLS FIRST'), - '1'); - - PERFORM assert_result( - 'ORDER BY eql_v2.order_by(e) ASC NULLS LAST', - format('SELECT id FROM encrypted ORDER BY e ASC NULLS LAST'), - '3'); - - -- NULLS FIRST when DESC - PERFORM assert_result( - 'ORDER BY encrypted DESC', - format('SELECT id FROM encrypted ORDER BY e DESC'), - '1'); - - PERFORM assert_result( - 'ORDER BY eql_v2.order_by(e) DESC NULLS FIRST', - format('SELECT id FROM encrypted ORDER BY e DESC NULLS FIRST'), - '1'); - - PERFORM assert_result( - 'ORDER BY eql_v2.order_by(e) DESC NULLS LAST', - format('SELECT id FROM encrypted ORDER BY e DESC NULLS LAST'), - '2'); - - END; -$$ LANGUAGE plpgsql; - - --- --- ORE - ORDER BY ore_block_u64_8_256(eql_v2_encrypted) - -DO $$ -DECLARE - e eql_v2_encrypted; - ore_term eql_v2_encrypted; - BEGIN - SELECT ore.e FROM ore WHERE id = 42 INTO ore_term; - - PERFORM assert_count( - 'ORDER BY eql_v2.order_by(e) DESC', - format('SELECT id FROM ore WHERE e < %L ORDER BY eql_v2.order_by(e) DESC', ore_term), - 41); - - PERFORM assert_result( - 'ORDER BY eql_v2.order_by(e) DESC returns correct record', - format('SELECT id FROM ore WHERE e < %L ORDER BY eql_v2.order_by(e) DESC LIMIT 1', ore_term), - '41'); - - PERFORM assert_result( - 'ORDER BY eql_v2.order_by(e) ASC', - format('SELECT id FROM ore WHERE e < %L ORDER BY eql_v2.order_by(e) ASC LIMIT 1', ore_term), - '1'); - END; -$$ LANGUAGE plpgsql; - -SELECT drop_table_with_encrypted(); \ No newline at end of file diff --git a/src/operators/~~_test.sql b/src/operators/~~_test.sql deleted file mode 100644 index 6bb05461..00000000 --- a/src/operators/~~_test.sql +++ /dev/null @@ -1,108 +0,0 @@ -\set ON_ERROR_STOP on - -SELECT create_table_with_encrypted(); -SELECT seed_encrypted_json(); - --- --- Match - eql_v2_encrypted ~~ eql_v2_encrypted --- -DO $$ -DECLARE - e eql_v2_encrypted; - BEGIN - - for i in 1..3 loop - e := create_encrypted_json(i); - - PERFORM assert_result( - format('eql_v2_encrypted ~~ eql_v2_encrypted %s of 3', i), - format('SELECT e FROM encrypted WHERE e ~~ %L;', e)); - - PERFORM assert_result( - format('eql_v2_encrypted LIKE eql_v2_encrypted %s of 3', i), - format('SELECT e FROM encrypted WHERE e LIKE %L;', e)); - - end loop; - - -- Partial match - e := create_encrypted_json('bf')::jsonb || '{"bf": [10, 11]}'; - - PERFORM assert_result( - 'eql_v2_encrypted ~~ eql_v2_encrypted with partial match', - format('SELECT e FROM encrypted WHERE e ~~ %L;', e)); - - PERFORM assert_result( - 'eql_v2_encrypted LIKE eql_v2_encrypted with partial match', - format('SELECT e FROM encrypted WHERE e LIKE %L;', e)); - - END; -$$ LANGUAGE plpgsql; - - --- --- Match - eql_v2_encrypted ~~* eql_v2_encrypted --- -DO $$ -DECLARE - e eql_v2_encrypted; - BEGIN - - for i in 1..3 loop - e := create_encrypted_json(i, 'bf'); - - PERFORM assert_result( - format('eql_v2_encrypted ~~* eql_v2_encrypted %s of 3', i), - format('SELECT e FROM encrypted WHERE e ~~* %L;', e)); - - PERFORM assert_result( - format('eql_v2_encrypted LIKE eql_v2_encrypted %s of 3', i), - format('SELECT e FROM encrypted WHERE e ILIKE %L;', e)); - - end loop; - - -- Partial match - e := create_encrypted_json('bf')::jsonb || '{"bf": [10, 11]}'; - - PERFORM assert_result( - 'eql_v2_encrypted ~~* eql_v2_encrypted with partial match', - format('SELECT e FROM encrypted WHERE e ~~* %L;', e)); - - PERFORM assert_result( - 'eql_v2_encrypted LIKE eql_v2_encrypted with partial match', - format('SELECT e FROM encrypted WHERE e ILIKE %L;', e)); - - END; -$$ LANGUAGE plpgsql; - - --- --- Match - eql_v2.bloom_filter(eql_v2_encrypted, eql_v2_encrypted) --- -DO $$ -DECLARE - e eql_v2_encrypted; - BEGIN - - for i in 1..3 loop - e := create_encrypted_json(i, 'bf'); - - PERFORM assert_result( - format('eql_v2.like(eql_v2_encrypted, eql_v2_encrypted)', i), - format('SELECT e FROM encrypted WHERE eql_v2.like(e, %L);', e)); - - end loop; - - -- Partial match - e := create_encrypted_json('bf')::jsonb || '{"bf": [10, 11]}'; - - PERFORM assert_result( - 'eql_v2.like(eql_v2_encrypted, eql_v2_encrypted)', - format('SELECT e FROM encrypted WHERE eql_v2.like(e, %L);', e)); - - END; -$$ LANGUAGE plpgsql; - - - - -SELECT drop_table_with_encrypted(); \ No newline at end of file diff --git a/src/ore_block_u64_8_256/compare_test.sql b/src/ore_block_u64_8_256/compare_test.sql deleted file mode 100644 index 83c90939..00000000 --- a/src/ore_block_u64_8_256/compare_test.sql +++ /dev/null @@ -1,27 +0,0 @@ -\set ON_ERROR_STOP on - -DO $$ - DECLARE - a eql_v2_encrypted; - b eql_v2_encrypted; - c eql_v2_encrypted; - BEGIN - - a := create_encrypted_ore_json(1); - b := create_encrypted_ore_json(21); - c := create_encrypted_ore_json(42); - - ASSERT eql_v2.compare_ore_block_u64_8_256(a, a) = 0; - ASSERT eql_v2.compare_ore_block_u64_8_256(a, b) = -1; - ASSERT eql_v2.compare_ore_block_u64_8_256(a, c) = -1; - - ASSERT eql_v2.compare_ore_block_u64_8_256(b, b) = 0; - ASSERT eql_v2.compare_ore_block_u64_8_256(b, a) = 1; - ASSERT eql_v2.compare_ore_block_u64_8_256(b, c) = -1; - - ASSERT eql_v2.compare_ore_block_u64_8_256(c, c) = 0; - ASSERT eql_v2.compare_ore_block_u64_8_256(c, b) = 1; - ASSERT eql_v2.compare_ore_block_u64_8_256(c, a) = 1; - END; -$$ LANGUAGE plpgsql; - diff --git a/src/ore_block_u64_8_256/functions_test.sql b/src/ore_block_u64_8_256/functions_test.sql deleted file mode 100644 index 0ecfaa22..00000000 --- a/src/ore_block_u64_8_256/functions_test.sql +++ /dev/null @@ -1,58 +0,0 @@ -\set ON_ERROR_STOP on - -DO $$ - BEGIN - PERFORM assert_result( - 'Extract ore index term from encrypted', - 'SELECT eql_v2.ore_block_u64_8_256(''{"ob": []}''::jsonb)'); - - PERFORM assert_exception( - 'Missing ore index term in encrypted raises exception', - 'SELECT eql_v2.ore_block_u64_8_256(''{}''::jsonb)'); - END; -$$ LANGUAGE plpgsql; - - - -DO $$ - DECLARE - ore_term eql_v2_encrypted; - BEGIN - SELECT ore.e FROM ore WHERE id = 42 INTO ore_term; - - ASSERT eql_v2.has_ore_block_u64_8_256(ore_term); - - END; -$$ LANGUAGE plpgsql; - - - --- --- ORE - ORDER BY ore_block_u64_8_256(eql_v2_encrypted) --- SKIP THIS TEST --- Individual term operators are currently disabled because we may not need them anymore - - --- DO $$ --- DECLARE --- e eql_v2_encrypted; --- ore_term eql_v2_encrypted; --- BEGIN --- SELECT ore.e FROM ore WHERE id = 42 INTO ore_term; - --- PERFORM assert_count( --- 'ORDER BY eql_v2.ore_block_u64_8_256(e) DESC', --- format('SELECT id FROM ore WHERE e < %L ORDER BY eql_v2.ore_block_u64_8_256(e) DESC', ore_term), --- 41); - --- PERFORM assert_result( --- 'ORDER BY eql_v2.ore_block_u64_8_256(e) DESC returns correct record', --- format('SELECT id FROM ore WHERE e < %L ORDER BY eql_v2.ore_block_u64_8_256(e) DESC LIMIT 1', ore_term), --- '41'); - --- PERFORM assert_result( --- 'ORDER BY eql_v2.ore_block_u64_8_256(e) ASC', --- format('SELECT id FROM ore WHERE e < %L ORDER BY eql_v2.ore_block_u64_8_256(e) ASC LIMIT 1', ore_term), --- '1'); --- END; --- $$ LANGUAGE plpgsql; diff --git a/src/ore_cllw_u64_8/compare_test.sql b/src/ore_cllw_u64_8/compare_test.sql deleted file mode 100644 index d9219b9f..00000000 --- a/src/ore_cllw_u64_8/compare_test.sql +++ /dev/null @@ -1,29 +0,0 @@ -\set ON_ERROR_STOP on - -DO $$ - DECLARE - a eql_v2_encrypted; - b eql_v2_encrypted; - c eql_v2_encrypted; - BEGIN - - -- {"number": {N}} - -- $.number: 3dba004f4d7823446e7cb71f6681b344 - a := eql_v2.jsonb_path_query(create_encrypted_ste_vec_json(1), '3dba004f4d7823446e7cb71f6681b344'); - b := eql_v2.jsonb_path_query(create_encrypted_ste_vec_json(5), '3dba004f4d7823446e7cb71f6681b344'); - c := eql_v2.jsonb_path_query(create_encrypted_ste_vec_json(10), '3dba004f4d7823446e7cb71f6681b344'); - - ASSERT eql_v2.compare_ore_cllw_u64_8(a, a) = 0; - ASSERT eql_v2.compare_ore_cllw_u64_8(a, b) = -1; - ASSERT eql_v2.compare_ore_cllw_u64_8(a, c) = -1; - - ASSERT eql_v2.compare_ore_cllw_u64_8(b, b) = 0; - ASSERT eql_v2.compare_ore_cllw_u64_8(b, a) = 1; - ASSERT eql_v2.compare_ore_cllw_u64_8(b, c) = -1; - - ASSERT eql_v2.compare_ore_cllw_u64_8(c, c) = 0; - ASSERT eql_v2.compare_ore_cllw_u64_8(c, b) = 1; - ASSERT eql_v2.compare_ore_cllw_u64_8(c, a) = 1; - END; -$$ LANGUAGE plpgsql; - diff --git a/src/ore_cllw_var_8/compare_test.sql b/src/ore_cllw_var_8/compare_test.sql deleted file mode 100644 index fa9af427..00000000 --- a/src/ore_cllw_var_8/compare_test.sql +++ /dev/null @@ -1,29 +0,0 @@ -\set ON_ERROR_STOP on - -DO $$ - DECLARE - a eql_v2_encrypted; - b eql_v2_encrypted; - c eql_v2_encrypted; - BEGIN - - -- {"hello": "world{N}"} - -- $.hello: d90b97b5207d30fe867ca816ed0fe4a7 - a := eql_v2.jsonb_path_query(create_encrypted_ste_vec_json(1), 'd90b97b5207d30fe867ca816ed0fe4a7'); - b := eql_v2.jsonb_path_query(create_encrypted_ste_vec_json(2), 'd90b97b5207d30fe867ca816ed0fe4a7'); - c := eql_v2.jsonb_path_query(create_encrypted_ste_vec_json(3), 'd90b97b5207d30fe867ca816ed0fe4a7'); - - ASSERT eql_v2.compare_ore_cllw_var_8(a, a) = 0; - ASSERT eql_v2.compare_ore_cllw_var_8(a, b) = -1; - ASSERT eql_v2.compare_ore_cllw_var_8(a, c) = -1; - - ASSERT eql_v2.compare_ore_cllw_var_8(b, b) = 0; - ASSERT eql_v2.compare_ore_cllw_var_8(b, a) = 1; - ASSERT eql_v2.compare_ore_cllw_var_8(b, c) = -1; - - ASSERT eql_v2.compare_ore_cllw_var_8(c, c) = 0; - ASSERT eql_v2.compare_ore_cllw_var_8(c, b) = 1; - ASSERT eql_v2.compare_ore_cllw_var_8(c, a) = 1; - END; -$$ LANGUAGE plpgsql; - diff --git a/src/ore_cllw_var_8/functions_test.sql b/src/ore_cllw_var_8/functions_test.sql deleted file mode 100644 index e69de29b..00000000 diff --git a/src/ste_vec/functions_test.sql b/src/ste_vec/functions_test.sql deleted file mode 100644 index 620262b5..00000000 --- a/src/ste_vec/functions_test.sql +++ /dev/null @@ -1,132 +0,0 @@ -\set ON_ERROR_STOP on - -SELECT create_table_with_encrypted(); -SELECT seed_encrypted_json(); - - -DO $$ - DECLARE - e eql_v2_encrypted; - sv eql_v2_encrypted[]; - BEGIN - - SELECT encrypted.e FROM encrypted LIMIT 1 INTO e; - - sv := eql_v2.ste_vec(e); - ASSERT array_length(sv, 1) = 3; - - -- eql_v2_encrypted that IS a ste_vec element - e := get_numeric_ste_vec_10()::eql_v2_encrypted; - - sv := eql_v2.ste_vec(e); - ASSERT array_length(sv, 1) = 3; - - END; -$$ LANGUAGE plpgsql; - - -DO $$ - DECLARE - e eql_v2_encrypted; - BEGIN - e := '{ "a": 1 }'::jsonb::eql_v2_encrypted; - ASSERT eql_v2.is_ste_vec_array(e); - - e := '{ "a": 0 }'::jsonb::eql_v2_encrypted; - ASSERT NOT eql_v2.is_ste_vec_array(e); - - e := '{ }'::jsonb::eql_v2_encrypted; - ASSERT NOT eql_v2.is_ste_vec_array(e); - END; -$$ LANGUAGE plpgsql; - - -DO $$ - DECLARE - e jsonb; - BEGIN - -- extract the ste_vec array item as an eeql_v2_encrypted - e := eql_v2.to_ste_vec_value('{ "i": "i", "v": 2, "sv": [ { "ocf": "ocf" }] }'::jsonb); - - ASSERT e ? 'i'; - ASSERT e ? 'v'; - ASSERT e ? 'ocf'; - - -- Returns the original if not an stevec value - e := eql_v2.to_ste_vec_value('{ "i": "i", "v": 2, "b3": "b3" }'::jsonb); - - ASSERT e ? 'i'; - ASSERT e ? 'v'; - ASSERT e ? 'b3'; - - END; -$$ LANGUAGE plpgsql; - - - -DO $$ - DECLARE - e eql_v2_encrypted; - sv eql_v2_encrypted[]; - BEGIN - e := '{ "sv": [1] }'::jsonb::eql_v2_encrypted; - ASSERT eql_v2.is_ste_vec_value(e); - - e := '{ "sv": [] }'::jsonb::eql_v2_encrypted; - ASSERT NOT eql_v2.is_ste_vec_value(e); - - e := '{ }'::jsonb::eql_v2_encrypted; - ASSERT NOT eql_v2.is_ste_vec_value(e); - - END; -$$ LANGUAGE plpgsql; - - --- ------------------------------------------------------------------------ --- ------------------------------------------------------------------------ --- --- eql_v2_encrypted contains itself --- --- -DO $$ - DECLARE - a eql_v2_encrypted; - b eql_v2_encrypted; - BEGIN - - a := get_numeric_ste_vec_10()::eql_v2_encrypted; - b := get_numeric_ste_vec_10()::eql_v2_encrypted; - - ASSERT eql_v2.ste_vec_contains(a, b); - ASSERT eql_v2.ste_vec_contains(b, a); - - END; -$$ LANGUAGE plpgsql; - - --- ------------------------------------------------------------------------ --- ------------------------------------------------------------------------ --- --- eql_v2_encrypted contains a term --- --- -DO $$ - DECLARE - a eql_v2_encrypted; - b eql_v2_encrypted; - term eql_v2_encrypted; - BEGIN - - a := get_numeric_ste_vec_10()::eql_v2_encrypted; - b := get_numeric_ste_vec_10()::eql_v2_encrypted; - - -- $.n - - term := b->('2517068c0d1f9d4d41d2c666211f785e'::text); - - ASSERT eql_v2.ste_vec_contains(a, term); - - ASSERT NOT eql_v2.ste_vec_contains(term, a); - END; -$$ LANGUAGE plpgsql; - diff --git a/src/version_test.sql b/src/version_test.sql deleted file mode 100644 index cb717454..00000000 --- a/src/version_test.sql +++ /dev/null @@ -1,9 +0,0 @@ -\set ON_ERROR_STOP on - -DO $$ - BEGIN - PERFORM assert_result( - 'eql_v2.version()', - 'SELECT true WHERE eql_v2.version() = ''DEV'''); - END; -$$ LANGUAGE plpgsql; From 41d41a3e035da4a46a14340aa169d4771033eb53 Mon Sep 17 00:00:00 2001 From: Toby Hede Date: Mon, 1 Dec 2025 15:20:36 +1100 Subject: [PATCH 12/18] chore: remove legacy test runner script Remove tasks/test/legacy.sh - no longer needed after removal of inline SQL tests. --- tasks/test/legacy.sh | 61 -------------------------------------------- 1 file changed, 61 deletions(-) delete mode 100755 tasks/test/legacy.sh diff --git a/tasks/test/legacy.sh b/tasks/test/legacy.sh deleted file mode 100755 index 4c087b3b..00000000 --- a/tasks/test/legacy.sh +++ /dev/null @@ -1,61 +0,0 @@ -#!/usr/bin/env bash -#MISE description="Run legacy SQL tests (inline test files)" -#USAGE flag "--test " help="Specific test file pattern to run" default="false" -#USAGE flag "--postgres " help="PostgreSQL version to test against" default="17" { -#USAGE choices "14" "15" "16" "17" -#USAGE } - -set -euo pipefail - -POSTGRES_VERSION=${usage_postgres} - -connection_url=postgresql://${POSTGRES_USER:-$USER}:${POSTGRES_PASSWORD}@${POSTGRES_HOST}:${POSTGRES_PORT}/${POSTGRES_DB} -container_name=postgres-${POSTGRES_VERSION} - -# Check postgres is running (script will exit if not) -source "$(dirname "$0")/../postgres/check_container.sh" ${POSTGRES_VERSION} - -run_test () { - echo - echo '###############################################' - echo "# Running Test: ${1}" - echo '###############################################' - echo - - cat $1 | docker exec -i ${container_name} psql --variable ON_ERROR_STOP=1 $connection_url -f- -} - -# Reset database -mise run reset --force --postgres ${POSTGRES_VERSION} - -echo -echo '###############################################' -echo '# Installing release/cipherstash-encrypt.sql' -echo '###############################################' -echo - -# Install -cat release/cipherstash-encrypt.sql | docker exec -i ${container_name} psql ${connection_url} -f- - - -cat tests/test_helpers.sql | docker exec -i ${container_name} psql ${connection_url} -f- -cat tests/ore.sql | docker exec -i ${container_name} psql ${connection_url} -f- -cat tests/ste_vec.sql | docker exec -i ${container_name} psql ${connection_url} -f- - - -if [ $usage_test = "false" ]; then - find src -type f -path "*_test.sql" | while read -r sql_file; do - echo $sql_file - run_test $sql_file - done -else - find src -type f -path "*$usage_test*" | while read -r sql_file; do - run_test $sql_file - done -fi - -echo -echo '###############################################' -echo "# ✅ALL TESTS PASSED " -echo '###############################################' -echo From f9c1676ee8848448da70139b23663257062962bc Mon Sep 17 00:00:00 2001 From: Toby Hede Date: Mon, 1 Dec 2025 15:20:58 +1100 Subject: [PATCH 13/18] refactor: update test runner to use only SQLx tests Remove legacy SQL test invocation from main test runner. Tests now run in 2 phases: 1. SQLx Rust lint checks 2. SQLx Rust tests --- tasks/test.sh | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-) diff --git a/tasks/test.sh b/tasks/test.sh index 415b2428..dc9f43ec 100755 --- a/tasks/test.sh +++ b/tasks/test.sh @@ -1,6 +1,5 @@ #!/usr/bin/env bash -#MISE description="Run all tests (legacy SQL + SQLx Rust)" -#USAGE flag "--test " help="Test to run" default="false" +#MISE description="Run all tests (SQLx Rust)" #USAGE flag "--postgres " help="PostgreSQL version to test against" default="17" { #USAGE choices "14" "15" "16" "17" #USAGE } @@ -11,7 +10,7 @@ set -euo pipefail POSTGRES_VERSION=${usage_postgres} echo "==========================================" -echo "Running Complete EQL Test Suite" +echo "Running EQL Test Suite" echo "PostgreSQL Version: $POSTGRES_VERSION" echo "==========================================" echo "" @@ -26,21 +25,14 @@ mise run --output prefix build --force # Run lints on sqlx tests echo "" echo "==============================================" -echo "1/3: Running linting checks on SQLx Rust tests" +echo "1/2: Running linting checks on SQLx Rust tests" echo "==============================================" mise run --output prefix test:lint -# Run legacy SQL tests -echo "" -echo "==============================================" -echo "2/3: Running Legacy SQL Tests" -echo "==============================================" -mise run --output prefix test:legacy --postgres ${POSTGRES_VERSION} - # Run SQLx Rust tests echo "" echo "==============================================" -echo "3/3: Running SQLx Rust Tests" +echo "2/2: Running SQLx Rust Tests" echo "==============================================" mise run --output prefix test:sqlx @@ -51,6 +43,5 @@ echo "==============================================" echo "" echo "Summary:" echo " ✓ SQLx Rust lint checks" -echo " ✓ Legacy SQL tests" echo " ✓ SQLx Rust tests" echo "" From f034f9ad23881869d18e96340b68be4f7ad417f1 Mon Sep 17 00:00:00 2001 From: Toby Hede Date: Mon, 1 Dec 2025 15:24:44 +1100 Subject: [PATCH 14/18] docs: remove legacy test migration documentation Remove files that tracked SQL-to-SQLx test migration: - test-migration-guide.md - test-inventory.md - assertion-counts.md Migration is complete - these files are no longer needed. --- docs/assertion-counts.md | 56 ------------- docs/test-inventory.md | 59 -------------- docs/test-migration-guide.md | 151 ----------------------------------- 3 files changed, 266 deletions(-) delete mode 100644 docs/assertion-counts.md delete mode 100644 docs/test-inventory.md delete mode 100644 docs/test-migration-guide.md diff --git a/docs/assertion-counts.md b/docs/assertion-counts.md deleted file mode 100644 index 0762bf18..00000000 --- a/docs/assertion-counts.md +++ /dev/null @@ -1,56 +0,0 @@ -# Assertion Count Report - -Generated: 2025-10-27 - -# SQL Test Assertions - -| File | ASSERT | PERFORM assert_* | SELECT checks | Total | -|------|--------|------------------|---------------|-------| -| `src/blake3/compare_test.sql` | 9 | 0 | 0 | 9 | -| `src/bloom_filter/functions_test.sql` | 0 | 2 | 0 | 2 | -| `src/config/config_test.sql` | 26 | 4 | 11 | 41 | -| `src/encrypted/aggregates_test.sql` | 4 | 0 | 2 | 6 | -| `src/encrypted/constraints_test.sql` | 0 | 6 | 0 | 6 | -| `src/encryptindex/functions_test.sql` | 21 | 1 | 19 | 41 | -| `src/hmac_256/compare_test.sql` | 9 | 0 | 0 | 9 | -| `src/hmac_256/functions_test.sql` | 1 | 2 | 0 | 3 | -| `src/jsonb/functions_test.sql` | 4 | 24 | 0 | 28 | -| `src/operators/->>_test.sql` | 0 | 6 | 0 | 6 | -| `src/operators/->_test.sql` | 2 | 9 | 0 | 11 | -| `src/operators/<=_ore_cllw_u64_8_test.sql` | 0 | 3 | 3 | 6 | -| `src/operators/<=_ore_cllw_var_8_test.sql` | 0 | 3 | 3 | 6 | -| `src/operators/<=_test.sql` | 0 | 8 | 4 | 12 | -| `src/operators/<>_ore_cllw_u64_8_test.sql` | 0 | 3 | 0 | 3 | -| `src/operators/<>_ore_cllw_var_8_test.sql` | 0 | 3 | 0 | 3 | -| `src/operators/<>_ore_test.sql` | 0 | 8 | 0 | 8 | -| `src/operators/<>_test.sql` | 0 | 12 | 2 | 14 | -| `src/operators/<@_test.sql` | 0 | 2 | 0 | 2 | -| `src/operators/<_test.sql` | 0 | 12 | 1 | 13 | -| `src/operators/=_ore_cllw_u64_8_test.sql` | 0 | 3 | 3 | 6 | -| `src/operators/=_ore_cllw_var_8_test.sql` | 0 | 3 | 3 | 6 | -| `src/operators/=_ore_test.sql` | 0 | 8 | 4 | 12 | -| `src/operators/=_test.sql` | 0 | 16 | 12 | 28 | -| `src/operators/>=_test.sql` | 0 | 14 | 10 | 24 | -| `src/operators/>_test.sql` | 0 | 12 | 1 | 13 | -| `src/operators/@>_test.sql` | 4 | 2 | 0 | 6 | -| `src/operators/compare_test.sql` | 63 | 0 | 0 | 63 | -| `src/operators/operator_class_test.sql` | 16 | 1 | 24 | 41 | -| `src/operators/order_by_test.sql` | 0 | 16 | 4 | 20 | -| `src/operators/~~_test.sql` | 0 | 10 | 0 | 10 | -| `src/ore_block_u64_8_256/compare_test.sql` | 9 | 0 | 0 | 9 | -| `src/ore_block_u64_8_256/functions_test.sql` | 1 | 5 | 2 | 8 | -| `src/ore_cllw_u64_8/compare_test.sql` | 9 | 0 | 0 | 9 | -| `src/ore_cllw_var_8/compare_test.sql` | 9 | 0 | 0 | 9 | -| `src/ore_cllw_var_8/functions_test.sql` | 0 | 0 | 0 | 0 | -| `src/ste_vec/functions_test.sql` | 18 | 0 | 0 | 18 | -| `src/version_test.sql` | 0 | 1 | 1 | 2 | - -**Total SQL assertions:** 513 across 38 files - -# Rust Test Assertions - -| File | assert* | expect* | is_err/is_ok | Total | -|------|---------|---------|--------------|-------| -| *(no Rust tests yet)* | 0 | 0 | 0 | 0 | - -**Total Rust assertions:** 0 across 0 files diff --git a/docs/test-inventory.md b/docs/test-inventory.md deleted file mode 100644 index a555dc87..00000000 --- a/docs/test-inventory.md +++ /dev/null @@ -1,59 +0,0 @@ -# Test Inventory - SQL to SQLx Migration - -Generated: 2025-10-27 - -| # | SQL Test File | Test Cases | Lines | Status | Rust Test File | Notes | -|---|---------------|------------|-------|--------|----------------|-------| -| 1 | `src/blake3/compare_test.sql` | 1 | 26 | ❌ TODO | `*TBD*` | | -| 2 | `src/bloom_filter/functions_test.sql` | 1 | 14 | ❌ TODO | `*TBD*` | | -| 3 | `src/config/config_test.sql` | 9 | 331 | ❌ TODO | `*TBD*` | | -| 4 | `src/encrypted/aggregates_test.sql` | 1 | 50 | ❌ TODO | `*TBD*` | | -| 5 | `src/encrypted/constraints_test.sql` | 3 | 79 | ❌ TODO | `*TBD*` | | -| 6 | `src/encryptindex/functions_test.sql` | 7 | 290 | ❌ TODO | `*TBD*` | | -| 7 | `src/hmac_256/compare_test.sql` | 1 | 26 | ❌ TODO | `*TBD*` | | -| 8 | `src/hmac_256/functions_test.sql` | 2 | 26 | ❌ TODO | `*TBD*` | | -| 9 | `src/jsonb/functions_test.sql` | 12 | 338 | ❌ TODO | `*TBD*` | | -| 10 | `src/operators/->>_test.sql` | 4 | 68 | ❌ TODO | `*TBD*` | | -| 11 | `src/operators/->_test.sql` | 6 | 118 | ❌ TODO | `*TBD*` | | -| 12 | `src/operators/<=_ore_cllw_u64_8_test.sql` | 1 | 56 | ❌ TODO | `*TBD*` | | -| 13 | `src/operators/<=_ore_cllw_var_8_test.sql` | 1 | 52 | ❌ TODO | `*TBD*` | | -| 14 | `src/operators/<=_test.sql` | 2 | 83 | ❌ TODO | `*TBD*` | | -| 15 | `src/operators/<>_ore_cllw_u64_8_test.sql` | 1 | 56 | ❌ TODO | `*TBD*` | | -| 16 | `src/operators/<>_ore_cllw_var_8_test.sql` | 1 | 55 | ❌ TODO | `*TBD*` | | -| 17 | `src/operators/<>_ore_test.sql` | 2 | 86 | ❌ TODO | `*TBD*` | | -| 18 | `src/operators/<>_test.sql` | 5 | 164 | ❌ TODO | `*TBD*` | | -| 19 | `src/operators/<@_test.sql` | 1 | 43 | ❌ TODO | `*TBD*` | | -| 20 | `src/operators/<_test.sql` | 4 | 158 | ❌ TODO | `*TBD*` | | -| 21 | `src/operators/=_ore_cllw_u64_8_test.sql` | 1 | 55 | ❌ TODO | `*TBD*` | | -| 22 | `src/operators/=_ore_cllw_var_8_test.sql` | 1 | 52 | ❌ TODO | `*TBD*` | | -| 23 | `src/operators/=_ore_test.sql` | 2 | 86 | ❌ TODO | `*TBD*` | | -| 24 | `src/operators/=_test.sql` | 6 | 195 | ❌ TODO | `*TBD*` | | -| 25 | `src/operators/>=_test.sql` | 4 | 174 | ❌ TODO | `*TBD*` | | -| 26 | `src/operators/>_test.sql` | 4 | 158 | ❌ TODO | `*TBD*` | | -| 27 | `src/operators/@>_test.sql` | 3 | 93 | ❌ TODO | `*TBD*` | | -| 28 | `src/operators/compare_test.sql` | 7 | 207 | ❌ TODO | `*TBD*` | | -| 29 | `src/operators/operator_class_test.sql` | 3 | 239 | ❌ TODO | `*TBD*` | | -| 30 | `src/operators/order_by_test.sql` | 3 | 148 | ❌ TODO | `*TBD*` | | -| 31 | `src/operators/~~_test.sql` | 3 | 107 | ❌ TODO | `*TBD*` | | -| 32 | `src/ore_block_u64_8_256/compare_test.sql` | 1 | 27 | ❌ TODO | `*TBD*` | | -| 33 | `src/ore_block_u64_8_256/functions_test.sql` | 3 | 58 | ❌ TODO | `*TBD*` | | -| 34 | `src/ore_cllw_u64_8/compare_test.sql` | 1 | 29 | ❌ TODO | `*TBD*` | | -| 35 | `src/ore_cllw_var_8/compare_test.sql` | 1 | 29 | ❌ TODO | `*TBD*` | | -| 36 | `src/ore_cllw_var_8/functions_test.sql` | 0 | 0 | ❌ TODO | `*TBD*` | | -| 37 | `src/ste_vec/functions_test.sql` | 6 | 132 | ❌ TODO | `*TBD*` | | -| 38 | `src/version_test.sql` | 1 | 9 | ❌ TODO | `*TBD*` | | - -## Summary - -- **Total SQL Test Files:** 38 -- **Total Test Cases:** 115 -- **Total Lines:** 3917 - -## Usage - -Update this inventory as you port tests: -1. Mark status ✅ when Rust test passes -2. Add Rust test file path -3. Add notes for any deviations - -Regenerate: `./tools/generate-test-inventory.sh` diff --git a/docs/test-migration-guide.md b/docs/test-migration-guide.md deleted file mode 100644 index f3e4453d..00000000 --- a/docs/test-migration-guide.md +++ /dev/null @@ -1,151 +0,0 @@ -# SQL to SQLx Test Migration Guide - -This guide explains how to port SQL tests to Rust/SQLx while ensuring complete coverage. - -## Overview - -We have three tools to track coverage during migration: - -1. **Test Inventory** - Which tests have been ported -2. **Assertion Counts** - How many test assertions ported -3. **Function Call Tracking** - Which SQL functions are tested - -## Workflow - -### Before Starting Migration - -1. **Generate baseline coverage:** - - ```bash - # Enable function tracking - psql postgres://cipherstash:password@localhost:7432/postgres -c "ALTER SYSTEM SET track_functions = 'all';" - psql postgres://cipherstash:password@localhost:7432/postgres -c "SELECT pg_reload_conf();" - - # Reset stats and run SQL tests - psql postgres://cipherstash:password@localhost:7432/postgres -c "SELECT pg_stat_reset();" - mise run test - - # Capture baseline - TEST_TYPE=sql ./tools/track-function-calls.sh sql-function-calls.json - ./tools/count-assertions.sh - ./tools/generate-test-inventory.sh - ``` - -2. **Review baseline metrics:** - - 38 SQL test files - - ~3,917 lines of tests - - ~513 assertions - - Function call coverage saved in `sql-function-calls.json` - -### During Migration - -For each SQL test file you port: - -1. **Pick a test from inventory** (`docs/test-inventory.md`) - -2. **Read the SQL test**, understand what it tests - -3. **Write equivalent Rust test** in `rust-tests/tests/` - -4. **Run the Rust test:** - ```bash - cd rust-tests - cargo test -- --nocapture - ``` - -5. **Verify function coverage:** - ```bash - psql postgres://cipherstash:password@localhost:7432/postgres -c "SELECT pg_stat_reset();" - cd rust-tests && cargo test - TEST_TYPE=rust ./tools/track-function-calls.sh rust-function-calls.json - ./tools/compare-function-calls.sh sql-function-calls.json rust-function-calls.json - ``` - -6. **Update test inventory:** - ```bash - ./tools/generate-test-inventory.sh - ``` - - Manually edit `docs/test-inventory.md` to mark test as ✅ Ported. - -7. **Check assertion coverage:** - ```bash - ./tools/compare-assertions.sh - ``` - -8. **Commit** when test passes and coverage verified: - ```bash - git add rust-tests/tests/.rs docs/test-inventory.md - git commit -m "test: port tests from SQL to SQLx" - ``` - -### After Migration Complete - -1. **Verify 100% coverage:** - ```bash - ./tools/check-test-coverage.sh - ``` - -2. **Ensure all checks pass:** - - ✅ All 38 tests marked as ported - - ✅ Rust assertion count ≥ SQL assertion count - - ✅ Rust function calls cover same functions as SQL - -3. **Delete SQL tests** (only after verification): - ```bash - # DO NOT DO THIS UNTIL MIGRATION COMPLETE - git rm src/**/*_test.sql - ``` - -## Coverage Metrics Explained - -### Test Inventory - -Shows 1:1 mapping of SQL test files to Rust test files: - -- **Status ❌ TODO** - Not yet ported -- **Status ✅ Ported** - Rust equivalent exists and passes - -### Assertion Counts - -Tracks test thoroughness: - -- **SQL:** ASSERT statements, PERFORM assert_*, SELECT checks -- **Rust:** assert!(), assert_eq!(), .expect() - -Goal: Rust count ≥ SQL count - -### Function Call Tracking - -Ensures same code paths exercised: - -- Uses PostgreSQL `pg_stat_user_functions` -- Compares which `eql_v2.*` functions called in SQL vs Rust tests -- Identifies gaps: functions only in SQL tests = missing coverage - -## Troubleshooting - -**Q: Rust test passes but function coverage shows missing functions?** - -A: Your test might not be exercising the same code paths. Review the SQL test to see which functions it calls. - -**Q: Assertion count much lower in Rust?** - -A: You may be using fewer, but more comprehensive assertions. That's OK if function coverage matches. Document in test inventory notes. - -**Q: pg_stat_user_functions not tracking?** - -A: Verify `track_functions = 'all'` in postgresql.conf and PostgreSQL reloaded. - -## Tips - -- **Port tests in related groups** (e.g., all operator tests together) -- **Keep both test suites running** until migration complete -- **Update inventory frequently** to track progress -- **Compare function coverage often** to catch gaps early - -## See Also - -- `tools/check-test-coverage.sh` - Run all coverage checks -- `docs/test-inventory.md` - Current migration status -- `docs/assertion-counts.md` - Detailed assertion breakdown From 1b6f46003fea31b4f72a00cd97c37cf63d1919cb Mon Sep 17 00:00:00 2001 From: Toby Hede Date: Mon, 1 Dec 2025 15:25:42 +1100 Subject: [PATCH 15/18] docs: update DEVELOPMENT.md for SQLx-only testing - Update file structure description to reference tests/sqlx/ - Simplify testing section to focus on SQLx framework - Remove outdated mermaid workflow diagram - Fix formatting of IMPORTANT block --- DEVELOPMENT.md | 76 ++++---------------------------------------------- 1 file changed, 5 insertions(+), 71 deletions(-) diff --git a/DEVELOPMENT.md b/DEVELOPMENT.md index d65cf843..3f0f0faa 100644 --- a/DEVELOPMENT.md +++ b/DEVELOPMENT.md @@ -59,7 +59,7 @@ These are the important files in the repo: └── playground/ <-- Playground enviroment for experimenting with EQL and CipherStash Proxy ``` -Tests live alongside the individual SQL files, with a filename ending with `_test.sql` +Tests are in the `tests/sqlx/` directory using Rust and the SQLx framework. We break SQL into small modules named after what they do. @@ -144,85 +144,19 @@ There are tests for checking EQL against PostgreSQL versions 14–17, that verif - Validating schemas for EQL configuration, encrypted data, and encrypted indexes - Using PostgreSQL operators on encrypted data and indexes (`=`, `<>`, `@>`) +Tests are written in Rust using the SQLx framework and live in `tests/sqlx/`. + The easiest way to run the tests [is in GitHub Actions](./.github/workflows/test-eql.yml): - Automatically whenever there are changes in the `sql/`, `tests/`, or `tasks/` directories - By manually running [the workflow](https://github.com/cipherstash/encrypt-query-language/actions/workflows/test-eql.yml) -This is how the `test-eql.yml` workflow functions: - -```mermaid ---- -title: Testing EQL ---- -stateDiagram-v2 - direction LR - classDef code font-family:monospace; - - - state "🧍 Human makes changes to EQL sources" as changes - state sources_fork <> - state sources_join <> - state "src/*.sql" as source_sql - state "tasks/**/*" as source_tasks - state "tests/**/*" as source_tests - state sources_changed <> - - state "🛠️ Trigger GitHub Actions workflow test-eql.yml" as build_triggered - state "Matrix: Test EQL SQL components" as matrix - state "Test with Postgres 14" as pg14 - state "Test with Postgres 15" as pg15 - state "Test with Postgres 16" as pg16 - state "Test with Postgres 17" as pg17 - state "Check build results" as check - state if_state <> - - changes --> sources_fork - sources_fork --> source_sql:::code - sources_fork --> source_tests:::code - sources_fork --> source_tasks:::code - source_sql --> sources_join - source_tests --> sources_join - source_tasks --> sources_join - sources_join --> source_changed_check - source_changed_check --> sources_changed - sources_changed --> build_triggered : Some changes - sources_changed --> [*]: No changes - - state "Check source changes" as source_changed_check - - [*] --> changes - - build_triggered --> matrix - - state fork_state <> - matrix --> fork_state - fork_state --> pg14 - fork_state --> pg15 - fork_state --> pg16 - fork_state --> pg17 - - state join_state <> - pg14 --> join_state - pg15 --> join_state - pg16 --> join_state - pg17 --> join_state - - state "✅ Pass build" as build_pass - state "❌ Fail build" as build_fail - join_state --> check - check --> if_state - if_state --> build_pass: All success - if_state --> build_fail : Any failures - build_pass --> [*] - build_fail --> [*] -``` - You can also [run the tests locally](#running-tests-locally) when doing local development. ### Running tests locally -> [!IMPORTANT] > **Before you run the tests locally** you need to [set up a local dev environment](#set-up-a-local-development-environment). +> [!IMPORTANT] +> **Before you run the tests locally** you need to [set up a local dev environment](#set-up-a-local-development-environment). To run tests locally with PostgreSQL 17: From bd68344650592344b1b4f6a124705e2b886b4e80 Mon Sep 17 00:00:00 2001 From: Toby Hede Date: Mon, 1 Dec 2025 15:30:53 +1100 Subject: [PATCH 16/18] docs: update CLAUDE.md for SQLx-only testing Update testing documentation to reflect removal of legacy SQL tests and use of SQLx Rust framework only. --- CLAUDE.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index d72cf372..320fd20e 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -26,9 +26,9 @@ This project uses `mise` for task management. Common commands: ### Testing - Run all tests: `mise run test` -- Run specific test: `mise run test --test ` -- Run tests against specific PostgreSQL version: `mise run test --postgres 14|15|16|17` -- Tests are located in `*_test.sql` files alongside source code +- Run SQLx tests directly: `mise run test:sqlx` +- Run SQLx tests in watch mode: `mise run test:sqlx:watch` +- Tests are located in `tests/sqlx/` using Rust and SQLx framework ### Build System - Dependencies are resolved using `-- REQUIRE:` comments in SQL files @@ -54,7 +54,7 @@ This is the **Encrypt Query Language (EQL)** - a PostgreSQL extension for search - `src/config/` - Configuration management functions - `src/blake3/`, `src/hmac_256/`, `src/bloom_filter/`, `src/ore_*` - Index implementations - `tasks/` - mise task scripts -- `tests/` - Test files (PostgreSQL 14-17 support) +- `tests/sqlx/` - Rust/SQLx test framework (PostgreSQL 14-17 support) - `release/` - Generated SQL installation files ### Key Concepts @@ -65,11 +65,12 @@ This is the **Encrypt Query Language (EQL)** - a PostgreSQL extension for search - **CipherStash Proxy**: Required for encryption/decryption operations ### Testing Infrastructure +- Tests are written in Rust using SQLx, located in `tests/sqlx/` - Tests run against PostgreSQL 14, 15, 16, 17 using Docker containers +- Use `mise run test --postgres 14|15|16|17` to test against a specific version - Container configuration in `tests/docker-compose.yml` -- Test helpers in `tests/test_helpers.sql` +- SQL test fixtures and helpers in `tests/test_helpers.sql` - Database connection: `localhost:7432` (cipherstash/password) -- **Rust/SQLx Tests**: Modern test framework in `tests/sqlx/` (see README there) ## Project Learning & Retrospectives @@ -163,7 +164,6 @@ HTML output is also generated in `docs/api/html/` for local preview only. - SQL files are modular - put operator wrappers in `operators.sql`, implementation in `functions.sql` - All SQL files must have `-- REQUIRE:` dependency declarations -- Test files end with `_test.sql` and live alongside source files - Build system uses `tsort` to resolve dependency order - Supabase build excludes operator classes (not supported) - **Documentation**: All functions/types must have Doxygen comments (see Documentation Standards above) From 95ce76ec23a32a39d158d82652df8cea029b2cb3 Mon Sep 17 00:00:00 2001 From: Toby Hede Date: Mon, 1 Dec 2025 15:58:09 +1100 Subject: [PATCH 17/18] fix(test): correct aggregate tests to use encrypted column MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The aggregate tests (count, max, min) were incorrectly testing the plain `id` column instead of the encrypted `e` column: - count_aggregate_on_encrypted_column: COUNT(*) → COUNT(e) - max_aggregate_on_encrypted_column: MAX(id) → eql_v2.max(e) - min_aggregate_on_encrypted_column: MIN(id) → eql_v2.min(e) These tests now properly verify that aggregate functions work on encrypted data, not just plain integers. --- tests/sqlx/tests/aggregate_tests.rs | 40 ++++++++++++++++++++++------- 1 file changed, 31 insertions(+), 9 deletions(-) diff --git a/tests/sqlx/tests/aggregate_tests.rs b/tests/sqlx/tests/aggregate_tests.rs index 10decc1d..fe137e96 100644 --- a/tests/sqlx/tests/aggregate_tests.rs +++ b/tests/sqlx/tests/aggregate_tests.rs @@ -7,39 +7,61 @@ use sqlx::PgPool; #[sqlx::test] async fn count_aggregate_on_encrypted_column(pool: PgPool) -> Result<()> { - // Test: COUNT works with encrypted columns + // Test: COUNT works on encrypted columns (counts non-NULL encrypted values) - let count: i64 = sqlx::query_scalar("SELECT COUNT(*) FROM ore") + let count: i64 = sqlx::query_scalar("SELECT COUNT(e) FROM ore") .fetch_one(&pool) .await?; - assert_eq!(count, 99, "should count all ORE records"); + assert_eq!(count, 99, "should count all non-NULL encrypted values"); Ok(()) } #[sqlx::test] async fn max_aggregate_on_encrypted_column(pool: PgPool) -> Result<()> { - // Test: MAX returns highest value with ORE + // Test: eql_v2.max() returns highest encrypted value + // The ore table has id and e columns where e is the encrypted version of id + // So eql_v2.max(e) should return the encrypted value corresponding to id=99 - let max_id: i64 = sqlx::query_scalar("SELECT MAX(id) FROM ore WHERE id <= 50") + // Get the expected max value (encrypted value where id = 99) + let expected: String = sqlx::query_scalar("SELECT e::text FROM ore WHERE id = 99") .fetch_one(&pool) .await?; - assert_eq!(max_id, 50, "MAX should return 50"); + // Get the actual max from eql_v2.max() + let actual: String = sqlx::query_scalar("SELECT eql_v2.max(e)::text FROM ore") + .fetch_one(&pool) + .await?; + + assert_eq!( + actual, expected, + "eql_v2.max(e) should return the encrypted value where id = 99 (maximum)" + ); Ok(()) } #[sqlx::test] async fn min_aggregate_on_encrypted_column(pool: PgPool) -> Result<()> { - // Test: MIN returns lowest value with ORE + // Test: eql_v2.min() returns lowest encrypted value + // The ore table has id and e columns where e is the encrypted version of id + // So eql_v2.min(e) should return the encrypted value corresponding to id=1 - let min_id: i64 = sqlx::query_scalar("SELECT MIN(id) FROM ore WHERE id >= 10") + // Get the expected min value (encrypted value where id = 1) + let expected: String = sqlx::query_scalar("SELECT e::text FROM ore WHERE id = 1") .fetch_one(&pool) .await?; - assert_eq!(min_id, 10, "MIN should return 10"); + // Get the actual min from eql_v2.min() + let actual: String = sqlx::query_scalar("SELECT eql_v2.min(e)::text FROM ore") + .fetch_one(&pool) + .await?; + + assert_eq!( + actual, expected, + "eql_v2.min(e) should return the encrypted value where id = 1 (minimum)" + ); Ok(()) } From 57d7635de82ac0a11ce0f2baa633ea06b647a23e Mon Sep 17 00:00:00 2001 From: Lindsay Holmwood Date: Tue, 2 Dec 2025 01:34:16 +1100 Subject: [PATCH 18/18] chore: remove toilet paper on shoe Claude chattily records the minutiae of transforming text from one file to another. That's not helpful to future maintainers, and will confuse newcomers to the code base. --- tests/sqlx/tests/aggregate_tests.rs | 1 - tests/sqlx/tests/comparison_tests.rs | 13 ------------- tests/sqlx/tests/constraint_tests.rs | 5 +---- tests/sqlx/tests/jsonb_path_operators_tests.rs | 7 ------- tests/sqlx/tests/order_by_tests.rs | 4 ++-- 5 files changed, 3 insertions(+), 27 deletions(-) diff --git a/tests/sqlx/tests/aggregate_tests.rs b/tests/sqlx/tests/aggregate_tests.rs index fe137e96..9d7b5245 100644 --- a/tests/sqlx/tests/aggregate_tests.rs +++ b/tests/sqlx/tests/aggregate_tests.rs @@ -88,7 +88,6 @@ async fn group_by_with_encrypted_column(pool: PgPool) -> Result<()> { } // ========== eql_v2.min() and eql_v2.max() Tests ========== -// Source: src/encrypted/aggregates_test.sql #[sqlx::test(fixtures(path = "../fixtures", scripts("aggregate_minmax_data")))] async fn eql_v2_min_with_null_values(pool: PgPool) -> Result<()> { diff --git a/tests/sqlx/tests/comparison_tests.rs b/tests/sqlx/tests/comparison_tests.rs index 63add45b..6fbe8212 100644 --- a/tests/sqlx/tests/comparison_tests.rs +++ b/tests/sqlx/tests/comparison_tests.rs @@ -321,7 +321,6 @@ async fn greater_than_or_equal_jsonb_gte_encrypted(pool: PgPool) -> Result<()> { #[sqlx::test] async fn selector_less_than_with_ore_cllw_u64_8(pool: PgPool) -> Result<()> { // Test: e->'selector' < term with ore_cllw_u64_8 index - // Source SQL: src/operators/<_test.sql (lines 20-50) // // Uses test data created by seed_encrypted_json() helper which creates: // - Three records with n=10, n=20, n=30 @@ -355,7 +354,6 @@ async fn selector_less_than_with_ore_cllw_u64_8(pool: PgPool) -> Result<()> { #[sqlx::test] async fn selector_less_than_with_ore_cllw_u64_8_fallback(pool: PgPool) -> Result<()> { // Test: e->'selector' < term fallback when index missing - // Source SQL: src/operators/<_test.sql (lines 42-48) // // Tests that comparison falls back to JSONB literal comparison // when the requested index type is not present on the selector @@ -387,7 +385,6 @@ async fn selector_less_than_with_ore_cllw_u64_8_fallback(pool: PgPool) -> Result #[sqlx::test] async fn selector_less_than_with_ore_cllw_var_8(pool: PgPool) -> Result<()> { // Test: e->'selector' < term with ore_cllw_var_8 index - // Source SQL: src/operators/<_test.sql (lines 66-95) // // STE vec test data has ore_cllw_var_8 on $.hello selector (a7cea93975ed8c01f861ccb6bd082784) // Extract $.hello from ste_vec id=3 and compare @@ -419,7 +416,6 @@ async fn selector_less_than_with_ore_cllw_var_8(pool: PgPool) -> Result<()> { #[sqlx::test] async fn selector_greater_than_with_ore_cllw_u64_8(pool: PgPool) -> Result<()> { // Test: e->'selector' > term with ore_cllw_u64_8 index - // Source SQL: src/operators/>_test.sql (lines 20-50) // // Extract $.n from ste_vec id=2 (n=20 value) and find records > 20 @@ -450,7 +446,6 @@ async fn selector_greater_than_with_ore_cllw_u64_8(pool: PgPool) -> Result<()> { #[sqlx::test] async fn selector_greater_than_with_ore_cllw_u64_8_fallback(pool: PgPool) -> Result<()> { // Test: e->'selector' > term fallback when index missing - // Source SQL: src/operators/>_test.sql (lines 42-48) sqlx::query("SELECT create_table_with_encrypted()") .execute(&pool) @@ -478,7 +473,6 @@ async fn selector_greater_than_with_ore_cllw_u64_8_fallback(pool: PgPool) -> Res #[sqlx::test] async fn selector_greater_than_with_ore_cllw_var_8(pool: PgPool) -> Result<()> { // Test: e->'selector' > term with ore_cllw_var_8 index - // Source SQL: src/operators/>_test.sql (lines 66-96) sqlx::query("SELECT create_table_with_encrypted()") .execute(&pool) @@ -507,7 +501,6 @@ async fn selector_greater_than_with_ore_cllw_var_8(pool: PgPool) -> Result<()> { #[sqlx::test] async fn selector_greater_than_with_ore_cllw_var_8_fallback(pool: PgPool) -> Result<()> { // Test: e->'selector' > term fallback to JSONB comparison - // Source SQL: src/operators/>_test.sql (lines 88-94) // // Tests fallback when selector doesn't have ore_cllw_var_8 @@ -540,7 +533,6 @@ async fn selector_greater_than_with_ore_cllw_var_8_fallback(pool: PgPool) -> Res #[sqlx::test] async fn selector_less_than_or_equal_with_ore_cllw_u64_8(pool: PgPool) -> Result<()> { // Test: e->'selector' <= term with ore_cllw_u64_8 index - // Source SQL: src/operators/<=_ore_cllw_u64_8_test.sql (lines 23-53) // // Extract $.n from ste_vec id=2 (n=20) and find records <= 20 @@ -571,7 +563,6 @@ async fn selector_less_than_or_equal_with_ore_cllw_u64_8(pool: PgPool) -> Result #[sqlx::test] async fn selector_less_than_or_equal_with_ore_cllw_u64_8_fallback(pool: PgPool) -> Result<()> { // Test: e->'selector' <= term fallback when index missing - // Source SQL: src/operators/<=_ore_cllw_u64_8_test.sql (lines 45-51) sqlx::query("SELECT create_table_with_encrypted()") .execute(&pool) @@ -601,7 +592,6 @@ async fn selector_less_than_or_equal_with_ore_cllw_u64_8_fallback(pool: PgPool) #[sqlx::test] async fn selector_greater_than_or_equal_with_ore_cllw_u64_8(pool: PgPool) -> Result<()> { // Test: e->'selector' >= term with ore_cllw_u64_8 index - // Source SQL: src/operators/>=_test.sql (lines 20-50) // // Extract $.n from ste_vec id=1 (n=10) and find records >= 10 @@ -632,7 +622,6 @@ async fn selector_greater_than_or_equal_with_ore_cllw_u64_8(pool: PgPool) -> Res #[sqlx::test] async fn selector_greater_than_or_equal_with_ore_cllw_u64_8_fallback(pool: PgPool) -> Result<()> { // Test: e->'selector' >= term fallback when index missing - // Source SQL: src/operators/>=_test.sql (lines 42-48) sqlx::query("SELECT create_table_with_encrypted()") .execute(&pool) @@ -660,7 +649,6 @@ async fn selector_greater_than_or_equal_with_ore_cllw_u64_8_fallback(pool: PgPoo #[sqlx::test] async fn selector_greater_than_or_equal_with_ore_cllw_var_8(pool: PgPool) -> Result<()> { // Test: e->'selector' >= term with ore_cllw_var_8 index - // Source SQL: src/operators/>=_test.sql (lines 66-96) sqlx::query("SELECT create_table_with_encrypted()") .execute(&pool) @@ -689,7 +677,6 @@ async fn selector_greater_than_or_equal_with_ore_cllw_var_8(pool: PgPool) -> Res #[sqlx::test] async fn selector_greater_than_or_equal_with_ore_cllw_var_8_fallback(pool: PgPool) -> Result<()> { // Test: e->'selector' >= term fallback to JSONB comparison - // Source SQL: src/operators/>=_test.sql (lines 88-94) sqlx::query("SELECT create_table_with_encrypted()") .execute(&pool) diff --git a/tests/sqlx/tests/constraint_tests.rs b/tests/sqlx/tests/constraint_tests.rs index 92245a8a..3f6f7f20 100644 --- a/tests/sqlx/tests/constraint_tests.rs +++ b/tests/sqlx/tests/constraint_tests.rs @@ -223,13 +223,12 @@ async fn foreign_key_constraint_with_encrypted(pool: PgPool) -> Result<()> { } // ======================================================================== -// EQL-Specific Constraint Tests (from src/encrypted/constraints_test.sql) +// EQL-Specific Constraint Tests // ======================================================================== #[sqlx::test(fixtures(path = "../fixtures", scripts("encrypted_json")))] async fn add_encrypted_constraint_prevents_invalid_data(pool: PgPool) -> Result<()> { // Test: eql_v2.add_encrypted_constraint() adds validation to encrypted column - // Source: constraints_test.sql lines 3-21 // First, verify that insert without constraint works (even with invalid empty JSONB) sqlx::query("INSERT INTO encrypted (e) VALUES ('{}'::jsonb::eql_v2_encrypted)") @@ -281,7 +280,6 @@ async fn add_encrypted_constraint_prevents_invalid_data(pool: PgPool) -> Result< #[sqlx::test(fixtures(path = "../fixtures", scripts("encrypted_json")))] async fn remove_encrypted_constraint_allows_invalid_data(pool: PgPool) -> Result<()> { // Test: eql_v2.remove_encrypted_constraint() removes validation from encrypted column - // Source: constraints_test.sql lines 24-43 // Add the encrypted constraint first sqlx::query("SELECT eql_v2.add_encrypted_constraint('encrypted', 'e')") @@ -323,7 +321,6 @@ async fn remove_encrypted_constraint_allows_invalid_data(pool: PgPool) -> Result #[sqlx::test(fixtures(path = "../fixtures", scripts("encrypted_json")))] async fn version_metadata_validation_on_insert(pool: PgPool) -> Result<()> { // Test: EQL version metadata (v field) is enforced on insert - // Source: constraints_test.sql lines 106-138 // // Note: The SQL test doesn't explicitly add a constraint, which suggests // version validation is built into the eql_v2_encrypted type itself or diff --git a/tests/sqlx/tests/jsonb_path_operators_tests.rs b/tests/sqlx/tests/jsonb_path_operators_tests.rs index c20eaf34..e8875cf3 100644 --- a/tests/sqlx/tests/jsonb_path_operators_tests.rs +++ b/tests/sqlx/tests/jsonb_path_operators_tests.rs @@ -100,7 +100,6 @@ async fn arrow_operator_returns_metadata_fields(pool: PgPool) -> Result<()> { // Test: e -> 'selector' returns JSONB with 'i' (index) and 'v' (version) metadata fields. // This verifies that the arrow operator returns the full encrypted metadata structure, // not just the value. The metadata includes the index term ('i') and version ('v'). - // SQL equivalent: src/operators/->_test.sql lines 106-118 // // NOTE: This test uses raw SQLx instead of QueryAssertion because we need to verify // specific JSONB field presence. QueryAssertion is designed for row count and basic @@ -132,7 +131,6 @@ async fn arrow_operator_returns_metadata_fields(pool: PgPool) -> Result<()> { #[sqlx::test(fixtures(path = "../fixtures", scripts("encrypted_json")))] async fn ciphertext_function_extracts_from_arrow_result(pool: PgPool) -> Result<()> { // Test: eql_v2.ciphertext(e -> 'selector') extracts ciphertext value - // SQL equivalent: src/operators/->_test.sql lines 60-75 // // The ciphertext() function extracts the 'c' field from the encrypted JSONB structure. // When combined with the -> operator, it allows extracting ciphertext from nested paths. @@ -151,7 +149,6 @@ async fn ciphertext_function_extracts_from_arrow_result(pool: PgPool) -> Result< #[sqlx::test(fixtures(path = "../fixtures", scripts("encrypted_json")))] async fn ciphertext_function_returns_all_rows(pool: PgPool) -> Result<()> { // Test: eql_v2.ciphertext() returns ciphertext for all encrypted rows - // SQL equivalent: src/operators/->_test.sql lines 70-73 let sql = format!( "SELECT eql_v2.ciphertext(e -> '{}'::text) FROM encrypted", @@ -167,7 +164,6 @@ async fn ciphertext_function_returns_all_rows(pool: PgPool) -> Result<()> { #[sqlx::test(fixtures(path = "../fixtures", scripts("encrypted_json")))] async fn arrow_operator_with_encrypted_selector(pool: PgPool) -> Result<()> { // Test: e -> eql_v2_encrypted selector (encrypted selector) - // SQL equivalent: src/operators/->_test.sql lines 39-56 // // The -> operator can accept an eql_v2_encrypted value as the selector. // The selector is created from JSONB with structure: {"s": "selector_hash"} @@ -186,7 +182,6 @@ async fn arrow_operator_with_encrypted_selector(pool: PgPool) -> Result<()> { #[sqlx::test(fixtures(path = "../fixtures", scripts("encrypted_json")))] async fn arrow_operator_with_encrypted_selector_all_rows(pool: PgPool) -> Result<()> { // Test: e -> eql_v2_encrypted selector returns all matching rows - // SQL equivalent: src/operators/->_test.sql lines 51-54 let encrypted_selector = Selectors::as_encrypted(Selectors::ROOT); let sql = format!( @@ -203,7 +198,6 @@ async fn arrow_operator_with_encrypted_selector_all_rows(pool: PgPool) -> Result #[sqlx::test(fixtures(path = "../fixtures", scripts("encrypted_json")))] async fn double_arrow_operator_with_encrypted_selector(pool: PgPool) -> Result<()> { // Test: e ->> eql_v2_encrypted selector (encrypted selector) - // SQL equivalent: src/operators/->>_test.sql lines 50-67 // // The ->> operator can also accept an eql_v2_encrypted value as the selector. @@ -221,7 +215,6 @@ async fn double_arrow_operator_with_encrypted_selector(pool: PgPool) -> Result<( #[sqlx::test(fixtures(path = "../fixtures", scripts("encrypted_json")))] async fn double_arrow_operator_with_encrypted_selector_all_rows(pool: PgPool) -> Result<()> { // Test: e ->> eql_v2_encrypted selector returns all matching rows - // SQL equivalent: src/operators/->>_test.sql lines 62-65 let encrypted_selector = Selectors::as_encrypted(Selectors::ROOT); let sql = format!( diff --git a/tests/sqlx/tests/order_by_tests.rs b/tests/sqlx/tests/order_by_tests.rs index 664ad285..f90ec559 100644 --- a/tests/sqlx/tests/order_by_tests.rs +++ b/tests/sqlx/tests/order_by_tests.rs @@ -119,7 +119,7 @@ async fn order_by_asc_with_greater_than_returns_lowest(pool: PgPool) -> Result<( Ok(()) } -// NULL ordering tests - replicating SQL test cases from order_by_test.sql lines 93-116 +// NULL ordering tests #[sqlx::test] async fn order_by_asc_nulls_first_returns_null_record_first(pool: PgPool) -> Result<()> { @@ -329,7 +329,7 @@ async fn order_by_desc_nulls_last_returns_largest_value_first(pool: PgPool) -> R Ok(()) } -// eql_v2.order_by() helper function tests - from order_by_test.sql lines 133-144 +// eql_v2.order_by() helper function tests #[sqlx::test] async fn order_by_helper_function_desc_returns_correct_count(pool: PgPool) -> Result<()> {