@@ -37,17 +37,39 @@ def connection_config(request, database):
3737 )
3838
3939
40+ @pytest .fixture (params = [_SENTINEL ])
41+ def credentials_config (request , database ):
42+ return (
43+ None
44+ if request .param is _SENTINEL # `None` is a valid value to test
45+ else request .param
46+ )
47+
48+
4049@pytest .fixture (params = [_SENTINEL ])
4150def table_name (request ):
4251 # Default table name when not explicitly parametrized
4352 return "test" if request .param is _SENTINEL else request .param
4453
4554
55+ # @pytest.fixture(params=['test'])
56+ # def table_name(request):
57+ # return request.param
58+
59+
4660@pytest .fixture
47- def table_dataset (table_name , database_name , connection_config , load_args , save_args ):
61+ def table_dataset (
62+ database_name ,
63+ credentials_config ,
64+ connection_config ,
65+ load_args ,
66+ save_args ,
67+ table_name ,
68+ ):
4869 return TableDataset (
4970 table_name = table_name ,
5071 database = database_name ,
72+ credentials = credentials_config ,
5173 connection = connection_config ,
5274 load_args = load_args ,
5375 save_args = save_args ,
@@ -238,6 +260,17 @@ def test_describe_includes_backend_mode_and_materialized(self, table_dataset):
238260 assert "database" not in desc ["load_args" ]
239261 assert "database" not in desc ["save_args" ]
240262
263+ @pytest .mark .parametrize (
264+ "save_args" ,
265+ [{"materialized" : "table" }],
266+ indirect = True ,
267+ )
268+ def test_save_empty_dataframe_is_noop (self , table_dataset ):
269+ """Saving an empty DataFrame should be a no-op (no table created)."""
270+ empty_table = ibis .memtable (pd .DataFrame ({"col1" : [], "col2" : [], "col3" : []}))
271+ table_dataset .save (empty_table )
272+ assert not table_dataset .exists ()
273+
241274 @pytest .mark .parametrize ("load_args" , [{"database" : "test" }], indirect = True )
242275 def test_load_extra_params (self , table_dataset , load_args ):
243276 """Test overriding the default load arguments."""
@@ -345,9 +378,100 @@ def test_connection_config(self, mocker, table_dataset, connection_config, key):
345378 table_dataset .load ()
346379 assert ("ibis" , key ) in table_dataset ._connections
347380
381+ @pytest .mark .parametrize (
382+ ("credentials_config" , "key" ),
383+ [
384+ (
385+ "postgres://xxxxxx.postgres.database.azure.com:5432/postgres" ,
386+ (
387+ ("backend" , "postgres" ),
388+ (
389+ "con" ,
390+ "postgres://xxxxxx.postgres.database.azure.com:5432/postgres" ,
391+ ),
392+ ),
393+ ),
394+ (
395+ {
396+ "con" : "postgres://[email protected] :5432/postgres" 397+ },
398+ (
399+ ("backend" , "postgres" ),
400+ (
401+ "con" ,
402+ "postgres://[email protected] :5432/postgres" , 403+ ),
404+ ),
405+ ),
406+ (
407+ {
408+ "backend" : "postgres" ,
409+ "database" : "postgres" ,
410+ "host" : "xxxx.postgres.database.azure.com" ,
411+ },
412+ (
413+ ("backend" , "postgres" ),
414+ ("database" , "postgres" ),
415+ ("host" , "xxxx.postgres.database.azure.com" ),
416+ ),
417+ ),
418+ ],
419+ )
420+ @pytest .mark .parametrize ("connection_config" , [None ], indirect = True )
421+ def test_connection_config_with_credentials (
422+ self , mocker , table_dataset , credentials_config , key
423+ ):
424+ # 1) isolate the cache so parametrized cases don't reuse connections
425+
426+ if isinstance (credentials_config , str ) or "backend" not in credentials_config :
427+ backend = "postgres"
428+ else :
429+ backend = credentials_config ["backend" ]
430+
431+ mocker .patch (f"ibis.{ backend } " )
432+ conn = table_dataset .connection
433+ assert conn is not None
434+ table_dataset .load ()
435+ assert ("ibis" , key ) in table_dataset ._connections
436+
348437 def test_save_data_loaded_using_file_dataset (self , file_dataset , table_dataset ):
349438 """Test interoperability of Ibis datasets sharing a database."""
350439 dummy_table = file_dataset .load ()
351440 assert not table_dataset .exists ()
352441 table_dataset .save (dummy_table )
353442 assert table_dataset .exists ()
443+
444+ # Additional tests for _get_backend_name branch coverage
445+ class TestGetBackendName :
446+ def test_get_backend_name_dict_with_backend (self ):
447+ ds = TableDataset (table_name = "t" )
448+ ds ._credentials = {"backend" : "postgres" , "database" : "db" }
449+ assert ds ._get_backend_name () == "postgres"
450+
451+ def test_get_backend_name_dict_without_backend_or_con (self ):
452+ ds = TableDataset (table_name = "t" )
453+ ds ._credentials = {"user" : "u" , "password" : "p" }
454+ assert ds ._get_backend_name () is None
455+
456+ def test_get_backend_name_string_with_scheme (self ):
457+ ds = TableDataset (table_name = "t" )
458+ ds ._credentials = "mysql://xxxxxx@host:3306/dbname"
459+ assert ds ._get_backend_name () == "mysql"
460+
461+ def test_get_backend_name_string_without_scheme (self ):
462+ ds = TableDataset (table_name = "t" )
463+ ds ._credentials = "not_a_url_string"
464+ assert ds ._get_backend_name () is None
465+
466+ def test_get_backend_name_dict_with_con_and_scheme (self ):
467+ ds = TableDataset (table_name = "t" )
468+ ds ._credentials = {
469+ "con" : "postgres://xxxxxx@host:5432/dbname" ,
470+ "some_other" : "value" ,
471+ }
472+ assert ds ._get_backend_name () == "postgres"
473+
474+ def test_get_backend_name_dict_with_con_without_scheme (self ):
475+ ds = TableDataset (table_name = "t" )
476+ ds ._credentials = {"con" : "sqlite_memory" }
477+ assert ds ._get_backend_name () is None
0 commit comments