Skip to content

Commit 7c9a602

Browse files
committed
test: use dataclass cases for function discovery regressions
1 parent 54d4dc3 commit 7c9a602

4 files changed

Lines changed: 105 additions & 64 deletions

File tree

‎tests/integration/src/sqlbuild/adapters/snowflake/_test_types.py‎

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,3 +123,10 @@ class SnowflakeCostTestCase:
123123
expected_query_count: int
124124
expected_resource_name: str
125125
expected_minimum_credits: int
126+
127+
128+
@dataclass(frozen=True)
129+
class SnowflakeFunctionDiscoveryTestCase:
130+
description: str
131+
function_name: str
132+
expected_function_count: int
Lines changed: 30 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,51 +1,64 @@
1-
"""Real Snowflake catalog discovery without an implicit connection database."""
1+
"""Real Snowflake function discovery against an explicit catalog."""
22

33
from typing import Any
44

55
import pytest
66

7+
from sqlbuild.adapter.contract.models import FunctionInfo
78
from sqlbuild.adapters.snowflake.classes.snowflake_adapter import SnowflakeAdapter
9+
from tests.integration.src.sqlbuild.adapters.snowflake._test_types import (
10+
SnowflakeFunctionDiscoveryTestCase,
11+
)
812
from tests.integration.src.sqlbuild.adapters.snowflake.helpers import (
913
build_snowflake_connection_config,
1014
)
1115

1216

13-
def test_given_no_current_database_when_discovering_udf_then_finds_explicit_target(
17+
@pytest.mark.parametrize(
18+
"test_case",
19+
[
20+
SnowflakeFunctionDiscoveryTestCase(
21+
description="connection without database finds the explicit target",
22+
function_name="normalize_order",
23+
expected_function_count=1,
24+
)
25+
],
26+
ids=lambda case: case.description,
27+
)
28+
def test_given_connection_without_database_when_discovering_udf_then_finds_explicit_target(
29+
test_case: SnowflakeFunctionDiscoveryTestCase,
1430
adapter: SnowflakeAdapter,
1531
connection: Any,
1632
snowflake_database: str,
1733
snowflake_schema: str,
1834
) -> None:
19-
function_name = "normalize_order"
20-
target = ".".join(
35+
target: str = ".".join(
2136
adapter.render_identifier(part)
22-
for part in (snowflake_database, snowflake_schema, function_name)
37+
for part in (snowflake_database, snowflake_schema, test_case.function_name)
2338
)
2439
adapter.execute(
2540
connection=connection,
26-
sql=f"CREATE FUNCTION {target}(value VARCHAR) RETURNS VARCHAR LANGUAGE SQL AS 'UPPER(value)'",
41+
sql=f"CREATE FUNCTION {target}(value VARCHAR) RETURNS VARCHAR AS 'UPPER(value)'",
2742
)
28-
config = build_snowflake_connection_config()
43+
config: dict[str, object] = build_snowflake_connection_config()
2944
config.pop("database", None)
3045
config.pop("schema", None)
31-
discovery_connection = adapter.connect(config)
46+
discovery_connection: Any = adapter.connect(config)
3247
try:
33-
current = adapter.execute(connection=discovery_connection, sql="SELECT CURRENT_DATABASE()")
34-
if current.fetchone()[0] is not None:
35-
pytest.skip("The test user supplies a default database")
36-
functions = adapter.list_functions(
48+
functions: tuple[FunctionInfo, ...] = adapter.list_functions(
3749
connection=discovery_connection,
3850
database=snowflake_database,
3951
schemas=(snowflake_schema,),
40-
names=(function_name,),
52+
names=(test_case.function_name,),
4153
)
42-
assert len(functions) == 1
43-
assert functions[0].name == function_name
44-
assert functions[0].schema == snowflake_schema.lower()
45-
assert functions[0].database == snowflake_database
4654
finally:
4755
adapter.close(discovery_connection)
4856

57+
assert len(functions) == test_case.expected_function_count
58+
assert functions[0].name == test_case.function_name
59+
assert functions[0].schema == snowflake_schema.lower()
60+
assert functions[0].database == snowflake_database
61+
4962

5063
if __name__ == "__main__":
5164
pytest.main([__file__, "-vv"])

‎tests/unit/src/sqlbuild/adapters/snowflake/_test_types.py‎

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
from dataclasses import dataclass
22
from datetime import datetime
33

4-
from sqlbuild.adapter.contract.models import SchemaDiffResult
4+
from sqlbuild.adapter.contract.models import FunctionInfo, SchemaDiffResult
55
from sqlbuild.compiler.lineage.types import InferredNullability
66
from sqlbuild.cost.types import CostStatus
77

@@ -204,3 +204,15 @@ class SnowflakeRelationAgeMetadataTestCase:
204204
last_altered: datetime | None
205205
expected_created_at: datetime | None
206206
expected_last_altered_at: datetime | None
207+
208+
209+
@dataclass(frozen=True)
210+
class SnowflakeFunctionDiscoveryTestCase:
211+
description: str
212+
database: str | None
213+
schemas: tuple[str, ...] | None
214+
names: tuple[str, ...] | None
215+
rows: tuple[tuple[object, ...], ...]
216+
expected_relation: str
217+
expected_params: tuple[str, ...]
218+
expected_functions: tuple[FunctionInfo, ...]

‎tests/unit/src/sqlbuild/adapters/snowflake/test_function_discovery.py‎

Lines changed: 55 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -6,64 +6,73 @@
66

77
from sqlbuild.adapter.contract.models import FunctionInfo
88
from sqlbuild.adapters.snowflake.classes.snowflake_adapter import SnowflakeAdapter
9+
from tests.unit.src.sqlbuild.adapters.snowflake._test_types import (
10+
SnowflakeFunctionDiscoveryTestCase,
11+
)
912
from tests.unit.src.sqlbuild.adapters.snowflake.helpers import (
1013
FakeSnowflakeMetadataConnection,
1114
FakeSnowflakeMetadataCursor,
1215
)
1316

1417

15-
def test_given_explicit_database_when_listing_functions_then_queries_requested_catalog() -> None:
16-
cursor = FakeSnowflakeMetadataCursor(rows=[("NORMALIZE_ORDER", "PUBLIC", "function")])
17-
connection = FakeSnowflakeMetadataConnection(cursor)
18-
19-
result = SnowflakeAdapter().list_functions(
20-
connection=cast(Any, connection),
21-
database="analytics",
22-
schemas=("public",),
23-
names=("normalize_order",),
24-
)
25-
26-
assert cursor.executed_sql is not None
27-
assert 'FROM "ANALYTICS".information_schema.functions' in cursor.executed_sql
28-
assert cursor.executed_params == ("PUBLIC", "NORMALIZE_ORDER", "analytics")
29-
assert result == (
30-
FunctionInfo(
18+
@pytest.mark.parametrize(
19+
"test_case",
20+
[
21+
SnowflakeFunctionDiscoveryTestCase(
22+
description="explicit database qualifies the information schema",
3123
database="analytics",
32-
schema="public",
33-
name="normalize_order",
34-
function_type="function",
24+
schemas=("public",),
25+
names=("normalize_order",),
26+
rows=(("NORMALIZE_ORDER", "PUBLIC", "function"),),
27+
expected_relation='FROM "ANALYTICS".information_schema.functions',
28+
expected_params=("PUBLIC", "NORMALIZE_ORDER", "analytics"),
29+
expected_functions=(
30+
FunctionInfo(
31+
database="analytics",
32+
schema="public",
33+
name="normalize_order",
34+
function_type="function",
35+
),
36+
),
3537
),
36-
)
37-
assert cursor.closed
38-
39-
40-
def test_given_database_with_quote_when_listing_functions_then_escapes_identifier() -> None:
41-
cursor = FakeSnowflakeMetadataCursor()
42-
43-
SnowflakeAdapter().list_functions(
44-
connection=cast(Any, FakeSnowflakeMetadataConnection(cursor)),
45-
database='order"catalog',
46-
schemas=None,
47-
)
48-
49-
assert cursor.executed_sql is not None
50-
assert 'FROM "ORDER""CATALOG".information_schema.functions' in cursor.executed_sql
51-
assert cursor.closed
52-
53-
54-
def test_given_no_database_when_listing_functions_then_uses_session_catalog() -> None:
55-
cursor = FakeSnowflakeMetadataCursor()
38+
SnowflakeFunctionDiscoveryTestCase(
39+
description="database containing a quote is escaped",
40+
database='order"catalog',
41+
schemas=None,
42+
names=None,
43+
rows=(),
44+
expected_relation='FROM "ORDER""CATALOG".information_schema.functions',
45+
expected_params=('order"catalog',),
46+
expected_functions=(),
47+
),
48+
SnowflakeFunctionDiscoveryTestCase(
49+
description="omitted database uses the session catalog",
50+
database=None,
51+
schemas=None,
52+
names=None,
53+
rows=(),
54+
expected_relation="FROM information_schema.functions",
55+
expected_params=(),
56+
expected_functions=(),
57+
),
58+
],
59+
ids=lambda case: case.description,
60+
)
61+
def test_given_database_when_listing_functions_then_queries_matching_catalog(
62+
test_case: SnowflakeFunctionDiscoveryTestCase,
63+
) -> None:
64+
cursor: FakeSnowflakeMetadataCursor = FakeSnowflakeMetadataCursor(rows=list(test_case.rows))
5665

57-
result = SnowflakeAdapter().list_functions(
66+
result: tuple[FunctionInfo, ...] = SnowflakeAdapter().list_functions(
5867
connection=cast(Any, FakeSnowflakeMetadataConnection(cursor)),
59-
database=None,
60-
schemas=None,
68+
database=test_case.database,
69+
schemas=test_case.schemas,
70+
names=test_case.names,
6171
)
6272

63-
assert cursor.executed_sql is not None
64-
assert "FROM information_schema.functions" in cursor.executed_sql
65-
assert cursor.executed_params == ()
66-
assert result == ()
73+
assert test_case.expected_relation in str(cursor.executed_sql)
74+
assert cursor.executed_params == test_case.expected_params
75+
assert result == test_case.expected_functions
6776
assert cursor.closed
6877

6978

0 commit comments

Comments
 (0)