@@ -490,15 +490,68 @@ def test_against_table_without_a_primary_key(self, sql_magic, ipython_namespace,
490
490
# str_table on sqlite will not have any primary key or any indices at all
491
491
# (all tables in cockroach have an implicit PK, so can't test with it)
492
492
493
- # So the only output should be the dataframe. Not a subsequent HTML blob describing indices.
493
+ # The output should NOT include an HTML blob describing indices.
494
494
sql_magic .execute (r'@sqlite \d str_table' )
495
495
496
- assert len (mock_display .call_args_list ) == 1
496
+ assert len (mock_display .call_args_list ) == 2 # main df, constraints df-as-html
497
497
498
498
df = mock_display .call_args_list [0 ].args [0 ]
499
+ assert isinstance (df , pd .DataFrame )
499
500
assert df .attrs ['noteable' ]['decoration' ]['title' ] == 'Table "str_table" Structure'
500
501
501
- assert isinstance (df , pd .DataFrame )
502
+ # Test test_constraints() will exercise this further. Only mention it here
503
+ # because will be returned and should not be talking about primary key / indices.
504
+ constraint_html = mock_display .call_args_list [1 ].args [0 ].data
505
+ assert constraint_html .startswith (
506
+ '<br />\n <h2>Table <code>str_table</code> Check Constraints</h2>'
507
+ )
508
+
509
+ # All CRDB tables have a primary key, so conditionally expect it to be described.
510
+ @pytest .mark .parametrize (
511
+ 'handle,expected_display_callcount' , [('@cockroach' , 3 ), ('@sqlite' , 2 )]
512
+ )
513
+ def test_constraints (
514
+ self , handle , expected_display_callcount , sql_magic , ipython_namespace , mock_display
515
+ ):
516
+
517
+ sql_magic .execute (rf'{ handle } \d str_table' )
518
+
519
+ assert (
520
+ len (mock_display .call_args_list ) == expected_display_callcount
521
+ ) # main df, maybe index html, constraints df-as-html
522
+
523
+ # The constraints HTML blob will be the final one always.
524
+ constraint_html = mock_display .call_args_list [- 1 ].args [0 ].data
525
+ assert constraint_html .startswith (
526
+ '<br />\n <h2>Table <code>str_table</code> Check Constraints</h2>'
527
+ )
528
+
529
+ # Convert back to dataframe
530
+ constraint_df = pd .read_html (constraint_html )[0 ]
531
+
532
+ assert len (constraint_df ) == 3 # Three check constraints on this table
533
+
534
+ assert constraint_df .columns .tolist () == [
535
+ 'Constraint' ,
536
+ 'Definition' ,
537
+ ]
538
+
539
+ # Should be alpha sorted by constraint name.
540
+ assert constraint_df ['Constraint' ].tolist () == [
541
+ 'never_f_10' ,
542
+ 'only_even_int_col_values' ,
543
+ 'single_char_str_id' ,
544
+ ]
545
+
546
+ # The SQL dialects convert the constraint expressions back to strings with slightly
547
+ # varying spellings (as expected), so can't simply blindly assert all of them.
548
+ constraint_definitions = constraint_df ['Definition' ].tolist ()
549
+
550
+ # This one happens to be regurgitated consistently between sqlite and CRDB.
551
+ assert 'length(str_id) = 1' in constraint_definitions
552
+ # Little gentler substring matching for the other two.
553
+ assert any ("str_id = 'f'" in cd for cd in constraint_definitions )
554
+ assert any ("int_col % 2" in cd for cd in constraint_definitions )
502
555
503
556
def test_no_args_gets_table_list (self , sql_magic , ipython_namespace ):
504
557
sql_magic .execute (r'@sqlite \d' )
0 commit comments