@@ -1457,19 +1457,29 @@ def test_dlt_pipeline(runner, tmp_path):
14571457 remove (dataset_path )
14581458
14591459
1460- def _stub_ducklake_dlt (monkeypatch , drivername = "sqlite" ):
1460+ def _stub_ducklake_dlt (
1461+ monkeypatch ,
1462+ drivername = "sqlite" ,
1463+ ducklake_name = "mre_ducklake" ,
1464+ metadata_schema = None ,
1465+ override_data_path = False ,
1466+ database = "/tmp/x/mre_ducklake.sqlite" ,
1467+ storage_url = "/tmp/x/mre_ducklake.files" ,
1468+ ):
14611469 """Install a fake `dlt` module exposing a ducklake pipeline. No dlt install needed."""
14621470 import sys
14631471 import types
14641472
1465- catalog = types .SimpleNamespace (drivername = drivername , database = "/tmp/x/mre_ducklake.sqlite" )
1473+ catalog = types .SimpleNamespace (drivername = drivername , database = database )
14661474 credentials = types .SimpleNamespace (
1467- ducklake_name = "mre_ducklake" ,
1468- metadata_schema = None ,
1475+ ducklake_name = ducklake_name ,
1476+ metadata_schema = metadata_schema ,
14691477 catalog = catalog ,
1470- storage_url = "/tmp/x/mre_ducklake.files" ,
1478+ storage_url = storage_url ,
1479+ )
1480+ client_config = types .SimpleNamespace (
1481+ credentials = credentials , override_data_path = override_data_path
14711482 )
1472- client_config = types .SimpleNamespace (credentials = credentials , override_data_path = False )
14731483 pipeline = types .SimpleNamespace (
14741484 destination = types .SimpleNamespace (to_name = lambda dest : "ducklake" ),
14751485 default_schema = types .SimpleNamespace (
@@ -1518,13 +1528,30 @@ def test_dlt_ducklake_pipeline(monkeypatch, drivername):
15181528 pipeline_name = "mre_ducklake" , dialect = "duckdb"
15191529 )
15201530
1521- assert "type: duckdb" in connection_config
1522- assert "type: ducklake" in connection_config
1523- assert "path: /tmp/x/mre_ducklake.sqlite" in connection_config
1524- assert "data_path: /tmp/x/mre_ducklake.files" in connection_config
1531+ # Byte-identical for ordinary paths (no quoting, key order and indent unchanged)
1532+ assert connection_config == (
1533+ " type: duckdb\n "
1534+ " catalogs:\n "
1535+ " mre_ducklake:\n "
1536+ " type: ducklake\n "
1537+ " path: /tmp/x/mre_ducklake.sqlite\n "
1538+ " data_path: /tmp/x/mre_ducklake.files\n "
1539+ " metadata_schema: mre_ducklake"
1540+ )
15251541
1526- # Round-trip: the exact ConfigError from #5914 no longer fires
1542+ # Structural parse instead of substring checks: malformed YAML cannot pass
15271543 parsed = yaml .safe_load ("connection:\n " + connection_config )["connection" ]
1544+ assert parsed ["type" ] == "duckdb"
1545+ assert set (parsed ["catalogs" ]) == {"mre_ducklake" }
1546+ lake = parsed ["catalogs" ]["mre_ducklake" ]
1547+ assert lake == {
1548+ "type" : "ducklake" ,
1549+ "path" : "/tmp/x/mre_ducklake.sqlite" ,
1550+ "data_path" : "/tmp/x/mre_ducklake.files" ,
1551+ "metadata_schema" : "mre_ducklake" ,
1552+ }
1553+
1554+ # Round-trip: the exact ConfigError from #5914 no longer fires
15281555 config = parse_connection_config (parsed )
15291556 assert isinstance (config , DuckDBConnectionConfig )
15301557 attach_sql = next (iter (config .catalogs .values ())).to_sql ("mre_ducklake" )
@@ -1534,14 +1561,136 @@ def test_dlt_ducklake_pipeline(monkeypatch, drivername):
15341561 )
15351562
15361563
1564+ def test_dlt_ducklake_explicit_metadata_schema (monkeypatch ):
1565+ import yaml
1566+
1567+ from sqlmesh .integrations import dlt as dlt_module
1568+
1569+ _stub_ducklake_dlt (monkeypatch , metadata_schema = "custom_meta" )
1570+
1571+ _ , connection_config , _ = dlt_module .generate_dlt_models_and_settings (
1572+ pipeline_name = "mre_ducklake" , dialect = "duckdb"
1573+ )
1574+ parsed = yaml .safe_load ("connection:\n " + connection_config )["connection" ]
1575+ lake = parsed ["catalogs" ]["mre_ducklake" ]
1576+ assert lake ["metadata_schema" ] == "custom_meta"
1577+ assert lake ["path" ] == "/tmp/x/mre_ducklake.sqlite"
1578+
1579+
1580+ def test_dlt_ducklake_override_data_path (monkeypatch ):
1581+ import yaml
1582+
1583+ from sqlmesh .integrations import dlt as dlt_module
1584+
1585+ _stub_ducklake_dlt (monkeypatch , override_data_path = True )
1586+
1587+ _ , connection_config , _ = dlt_module .generate_dlt_models_and_settings (
1588+ pipeline_name = "mre_ducklake" , dialect = "duckdb"
1589+ )
1590+ parsed = yaml .safe_load ("connection:\n " + connection_config )["connection" ]
1591+ lake = parsed ["catalogs" ]["mre_ducklake" ]
1592+ assert lake ["override_data_path" ] is True
1593+
1594+
1595+ def test_dlt_ducklake_custom_name (monkeypatch ):
1596+ import yaml
1597+
1598+ from sqlmesh .integrations import dlt as dlt_module
1599+
1600+ _stub_ducklake_dlt (monkeypatch , ducklake_name = "my_lake" )
1601+
1602+ _ , connection_config , _ = dlt_module .generate_dlt_models_and_settings (
1603+ pipeline_name = "mre_ducklake" , dialect = "duckdb"
1604+ )
1605+ parsed = yaml .safe_load ("connection:\n " + connection_config )["connection" ]
1606+ assert set (parsed ["catalogs" ]) == {"my_lake" }
1607+ lake = parsed ["catalogs" ]["my_lake" ]
1608+ assert lake ["metadata_schema" ] == "my_lake"
1609+
1610+
1611+ def test_dlt_ducklake_yaml_inline_helper ():
1612+ from sqlmesh .integrations .dlt import _yaml_inline
1613+
1614+ # Ordinary values stay byte-identical (no quotes)
1615+ assert _yaml_inline ("/tmp/x/mre_ducklake.sqlite" ) == "/tmp/x/mre_ducklake.sqlite"
1616+ assert _yaml_inline ("mre_ducklake" ) == "mre_ducklake"
1617+ # Pathological values are quoted single-line and round-trip
1618+ import yaml
1619+
1620+ for pathological in (
1621+ "'/tmp/quote/mre_ducklake.sqlite" ,
1622+ "/tmp/new\n line/mre.sqlite" ,
1623+ "a: b # c" ,
1624+ " leading-space" ,
1625+ ):
1626+ emitted = _yaml_inline (pathological )
1627+ assert "\n " not in emitted
1628+ doc = f"connection:\n path: { emitted } \n "
1629+ assert yaml .safe_load (doc )["connection" ]["path" ] == pathological
1630+
1631+
1632+ @pytest .mark .parametrize (
1633+ "pathological" ,
1634+ ["'/tmp/quote/mre_ducklake.sqlite" , "/tmp/new\n line/mre.sqlite" ],
1635+ ids = ["leading-quote" , "newline" ],
1636+ )
1637+ def test_dlt_ducklake_pathological_paths_round_trip (monkeypatch , pathological ):
1638+ import yaml
1639+
1640+ from sqlmesh .core .config .connection import DuckDBConnectionConfig , parse_connection_config
1641+ from sqlmesh .integrations import dlt as dlt_module
1642+
1643+ _stub_ducklake_dlt (monkeypatch , database = pathological )
1644+
1645+ _ , connection_config , _ = dlt_module .generate_dlt_models_and_settings (
1646+ pipeline_name = "mre_ducklake" , dialect = "duckdb"
1647+ )
1648+ # Must not raise ScannerError; values must round-trip exactly
1649+ parsed = yaml .safe_load ("connection:\n " + connection_config )["connection" ]
1650+ assert parsed ["catalogs" ]["mre_ducklake" ]["path" ] == pathological
1651+ config = parse_connection_config (parsed )
1652+ assert isinstance (config , DuckDBConnectionConfig )
1653+
1654+
1655+ def test_dlt_ducklake_block_coexists_with_second_catalog (monkeypatch ):
1656+ import yaml
1657+
1658+ from sqlmesh .core .config .connection import DuckDBConnectionConfig , parse_connection_config
1659+ from sqlmesh .integrations import dlt as dlt_module
1660+
1661+ _stub_ducklake_dlt (monkeypatch )
1662+
1663+ _ , connection_config , _ = dlt_module .generate_dlt_models_and_settings (
1664+ pipeline_name = "mre_ducklake" , dialect = "duckdb"
1665+ )
1666+ parsed = yaml .safe_load ("connection:\n " + connection_config )["connection" ]
1667+ parsed ["catalogs" ]["other" ] = {"type" : "ducklake" , "path" : "/tmp/x/other.sqlite" }
1668+ config = parse_connection_config (parsed )
1669+ assert isinstance (config , DuckDBConnectionConfig )
1670+ assert set (config .catalogs ) == {"mre_ducklake" , "other" }
1671+
1672+
15371673def test_dlt_ducklake_unsupported_catalog (monkeypatch ):
15381674 from sqlmesh .integrations import dlt as dlt_module
15391675
15401676 _stub_ducklake_dlt (monkeypatch , drivername = "postgres" )
15411677
1542- with pytest .raises (ClickException , match = "Unsupported DuckLake catalog" ):
1678+ called = {}
1679+
1680+ orig = dlt_module .format_ducklake_config
1681+
1682+ def _spy (client_config ):
1683+ called ["branch" ] = True
1684+ return orig (client_config )
1685+
1686+ monkeypatch .setattr (dlt_module , "format_ducklake_config" , _spy )
1687+
1688+ with pytest .raises (ClickException , match = "Unsupported DuckLake catalog 'postgres'" ) as exc_info :
15431689 dlt_module .generate_dlt_models_and_settings (pipeline_name = "mre_ducklake" , dialect = "duckdb" )
15441690
1691+ assert called .get ("branch" ) is True
1692+ assert "postgres" in str (exc_info .value )
1693+
15451694
15461695@time_machine .travel (FREEZE_TIME )
15471696def test_environments (runner , tmp_path ):
0 commit comments