@@ -221,3 +221,172 @@ async fn foreign_key_constraint_with_encrypted(pool: PgPool) -> Result<()> {
221221
222222 Ok ( ( ) )
223223}
224+
225+ // ========================================================================
226+ // EQL-Specific Constraint Tests (from src/encrypted/constraints_test.sql)
227+ // ========================================================================
228+
229+ #[ sqlx:: test( fixtures( path = "../fixtures" , scripts( "encrypted_json" ) ) ) ]
230+ async fn add_encrypted_constraint_prevents_invalid_data ( pool : PgPool ) -> Result < ( ) > {
231+ // Test: eql_v2.add_encrypted_constraint() adds validation to encrypted column
232+ // Source: constraints_test.sql lines 3-21
233+
234+ // First, verify that insert without constraint works (even with invalid empty JSONB)
235+ sqlx:: query ( "INSERT INTO encrypted (e) VALUES ('{}'::jsonb::eql_v2_encrypted)" )
236+ . execute ( & pool)
237+ . await ?;
238+
239+ let count: i64 = sqlx:: query_scalar ( "SELECT COUNT(*) FROM encrypted" )
240+ . fetch_one ( & pool)
241+ . await ?;
242+
243+ assert_eq ! (
244+ count, 4 ,
245+ "Should have 4 records (3 from fixture + 1 invalid)"
246+ ) ;
247+
248+ // Delete the invalid data and reset
249+ sqlx:: query ( "DELETE FROM encrypted WHERE e = '{}'::jsonb::eql_v2_encrypted" )
250+ . execute ( & pool)
251+ . await ?;
252+
253+ // Add the encrypted constraint
254+ sqlx:: query ( "SELECT eql_v2.add_encrypted_constraint('encrypted', 'e')" )
255+ . execute ( & pool)
256+ . await ?;
257+
258+ // Now attempt to insert invalid data - should fail
259+ let result = sqlx:: query ( "INSERT INTO encrypted (e) VALUES ('{}'::jsonb::eql_v2_encrypted)" )
260+ . execute ( & pool)
261+ . await ;
262+
263+ assert ! (
264+ result. is_err( ) ,
265+ "Constraint should prevent insert of invalid eql_v2_encrypted (empty JSONB)"
266+ ) ;
267+
268+ // Verify count unchanged after failed insert
269+ let final_count: i64 = sqlx:: query_scalar ( "SELECT COUNT(*) FROM encrypted" )
270+ . fetch_one ( & pool)
271+ . await ?;
272+
273+ assert_eq ! (
274+ final_count, 3 ,
275+ "Should still have 3 records after constraint prevented invalid insert"
276+ ) ;
277+
278+ Ok ( ( ) )
279+ }
280+
281+ #[ sqlx:: test( fixtures( path = "../fixtures" , scripts( "encrypted_json" ) ) ) ]
282+ async fn remove_encrypted_constraint_allows_invalid_data ( pool : PgPool ) -> Result < ( ) > {
283+ // Test: eql_v2.remove_encrypted_constraint() removes validation from encrypted column
284+ // Source: constraints_test.sql lines 24-43
285+
286+ // Add the encrypted constraint first
287+ sqlx:: query ( "SELECT eql_v2.add_encrypted_constraint('encrypted', 'e')" )
288+ . execute ( & pool)
289+ . await ?;
290+
291+ // Verify constraint is working - invalid data should be rejected
292+ let result = sqlx:: query ( "INSERT INTO encrypted (e) VALUES ('{}'::jsonb::eql_v2_encrypted)" )
293+ . execute ( & pool)
294+ . await ;
295+
296+ assert ! (
297+ result. is_err( ) ,
298+ "Constraint should prevent insert of invalid eql_v2_encrypted"
299+ ) ;
300+
301+ // Remove the constraint
302+ sqlx:: query ( "SELECT eql_v2.remove_encrypted_constraint('encrypted', 'e')" )
303+ . execute ( & pool)
304+ . await ?;
305+
306+ // Now invalid data should be allowed
307+ sqlx:: query ( "INSERT INTO encrypted (e) VALUES ('{}'::jsonb::eql_v2_encrypted)" )
308+ . execute ( & pool)
309+ . await ?;
310+
311+ let count: i64 = sqlx:: query_scalar ( "SELECT COUNT(*) FROM encrypted" )
312+ . fetch_one ( & pool)
313+ . await ?;
314+
315+ assert_eq ! (
316+ count, 4 ,
317+ "Should have 4 records (3 valid + 1 invalid after constraint removed)"
318+ ) ;
319+
320+ Ok ( ( ) )
321+ }
322+
323+ #[ sqlx:: test( fixtures( path = "../fixtures" , scripts( "encrypted_json" ) ) ) ]
324+ async fn version_metadata_validation_on_insert ( pool : PgPool ) -> Result < ( ) > {
325+ // Test: EQL version metadata (v field) is enforced on insert
326+ // Source: constraints_test.sql lines 106-138
327+ //
328+ // Note: The SQL test doesn't explicitly add a constraint, which suggests
329+ // version validation is built into the eql_v2_encrypted type itself or
330+ // is enforced automatically. However, for this test we need to ensure
331+ // the constraint exists to validate version fields.
332+
333+ // Add encrypted constraint to enable version validation
334+ sqlx:: query ( "SELECT eql_v2.add_encrypted_constraint('encrypted', 'e')" )
335+ . execute ( & pool)
336+ . await ?;
337+
338+ // Create a valid encrypted value with version removed
339+ // We'll get a valid encrypted JSON and remove the 'v' field
340+ let encrypted_without_version: String =
341+ sqlx:: query_scalar ( "SELECT (create_encrypted_json(1)::jsonb - 'v')::text" )
342+ . fetch_one ( & pool)
343+ . await ?;
344+
345+ // Attempt to insert without version field - should fail
346+ let result = sqlx:: query ( & format ! (
347+ "INSERT INTO encrypted (e) VALUES ('{}'::jsonb::eql_v2_encrypted)" ,
348+ encrypted_without_version
349+ ) )
350+ . execute ( & pool)
351+ . await ;
352+
353+ assert ! (
354+ result. is_err( ) ,
355+ "Insert should fail when version field is missing"
356+ ) ;
357+
358+ // Create encrypted value with invalid version (v=1 instead of v=2)
359+ let encrypted_invalid_version: String =
360+ sqlx:: query_scalar ( "SELECT (create_encrypted_json(1)::jsonb || '{\" v\" : 1}')::text" )
361+ . fetch_one ( & pool)
362+ . await ?;
363+
364+ // Attempt to insert with invalid version - should fail
365+ let result = sqlx:: query ( & format ! (
366+ "INSERT INTO encrypted (e) VALUES ('{}'::jsonb::eql_v2_encrypted)" ,
367+ encrypted_invalid_version
368+ ) )
369+ . execute ( & pool)
370+ . await ;
371+
372+ assert ! (
373+ result. is_err( ) ,
374+ "Insert should fail when version field is invalid (v=1)"
375+ ) ;
376+
377+ // Insert with valid version (v=2) should succeed
378+ sqlx:: query ( "INSERT INTO encrypted (e) VALUES (create_encrypted_json(1))" )
379+ . execute ( & pool)
380+ . await ?;
381+
382+ let count: i64 = sqlx:: query_scalar ( "SELECT COUNT(*) FROM encrypted" )
383+ . fetch_one ( & pool)
384+ . await ?;
385+
386+ assert_eq ! (
387+ count, 4 ,
388+ "Should have 4 records after successful insert with valid version"
389+ ) ;
390+
391+ Ok ( ( ) )
392+ }
0 commit comments