Skip to content

Commit 65110d6

Browse files
KrisWuli006XiaJunjie2020
authored andcommitted
test(datasource): cover Hive partition field discovery
Add eight regression cases for partition metadata, duplicate fields, blank names, valid hash-prefixed columns, empty results, and extra driver columns. Execute the production get_fields and ColumnSchema definitions with a mocked Hive cursor. Validation: python -m pytest backend/tests -q (11 passed).
1 parent 67601e6 commit 65110d6

1 file changed

Lines changed: 124 additions & 0 deletions

File tree

backend/tests/test_hive_fields.py

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
"""Regression tests for Hive DESCRIBE field discovery (issue #1250)."""
2+
3+
import ast
4+
import json
5+
from contextlib import nullcontext
6+
from pathlib import Path
7+
from types import SimpleNamespace
8+
from unittest.mock import Mock
9+
10+
import pytest
11+
12+
BACKEND_DIR = Path(__file__).resolve().parents[1]
13+
14+
15+
def _load_symbol(relative_path, name, namespace):
16+
# As in test_connection_pool_manager, avoid importing unrelated DB drivers.
17+
path = BACKEND_DIR / relative_path
18+
tree = ast.parse(path.read_text(encoding="utf-8"))
19+
node = next(node for node in tree.body if getattr(node, "name", None) == name)
20+
module = ast.Module(body=[node], type_ignores=[])
21+
exec(compile(module, str(path), "exec"), namespace)
22+
return namespace[name]
23+
24+
25+
@pytest.fixture
26+
def get_hive_fields():
27+
cursor = Mock()
28+
connection = SimpleNamespace(cursor=lambda: nullcontext(cursor))
29+
pool = SimpleNamespace(connection=lambda: nullcontext(connection))
30+
namespace = {
31+
"json": json,
32+
"CoreDatasource": SimpleNamespace,
33+
"DatasourceConf": SimpleNamespace,
34+
"aes_decrypt": lambda value: value,
35+
"DB": SimpleNamespace(
36+
get_db=lambda _: SimpleNamespace(connect_type="driver")
37+
),
38+
"ConnectType": SimpleNamespace(sqlalchemy="sqlalchemy"),
39+
"get_driver_pool": lambda _: pool,
40+
"get_field_sql": lambda *args: ("DESCRIBE sample", None, None),
41+
}
42+
_load_symbol("common/utils/utils.py", "equals_ignore_case", namespace)
43+
_load_symbol("apps/datasource/models/datasource.py", "ColumnSchema", namespace)
44+
get_fields = _load_symbol("apps/db/db.py", "get_fields", namespace)
45+
46+
def discover(rows):
47+
cursor.fetchall.return_value = rows
48+
fields = get_fields(SimpleNamespace(type="hive", configuration="{}"), "sample")
49+
return [(field.fieldName, field.fieldType, field.fieldComment) for field in fields]
50+
51+
return discover
52+
53+
54+
@pytest.mark.parametrize("repeat_partition", [False, True])
55+
def test_partition_fields_are_retained_once_without_metadata(get_hive_fields, repeat_partition):
56+
rows = [("id", "int", "identifier")]
57+
if repeat_partition:
58+
rows.append(("dt", "string", "partition date"))
59+
rows.extend([
60+
("", "", ""),
61+
("# Partition Information", "", ""),
62+
("# col_name", "data_type", "comment"),
63+
("dt", "string", "partition date"),
64+
])
65+
66+
assert get_hive_fields(rows) == [
67+
("id", "int", "identifier"),
68+
("dt", "string", "partition date"),
69+
]
70+
71+
72+
def test_duplicate_fields_preserve_first_occurrence_and_order(get_hive_fields):
73+
assert get_hive_fields([
74+
("id", "int", "identifier"),
75+
("dt", "string", "first comment"),
76+
("region", "string", "region"),
77+
("dt", "string", "repeated comment"),
78+
]) == [
79+
("id", "int", "identifier"),
80+
("dt", "string", "first comment"),
81+
("region", "string", "region"),
82+
]
83+
84+
85+
def test_blank_names_and_padded_metadata_are_ignored(get_hive_fields):
86+
assert get_hive_fields([
87+
(None, None, None),
88+
(" ", "", ""),
89+
(" # Partition Information ", None, None),
90+
(" # col_name ", "data_type ", "comment"),
91+
("dt", "string", None),
92+
]) == [("dt", "string", None)]
93+
94+
95+
def test_regular_columns_preserve_names_types_and_comments(get_hive_fields):
96+
assert get_hive_fields([
97+
("id", "bigint", None),
98+
("amount", "decimal(10,2)", b"amount"),
99+
("#tag", "string", "tag"),
100+
("# Partition Information", "string", "a real column"),
101+
(" spaced name ", "string", "keep the original name"),
102+
]) == [
103+
("id", "bigint", None),
104+
("amount", "decimal(10,2)", "amount"),
105+
("#tag", "string", "tag"),
106+
("# Partition Information", "string", "a real column"),
107+
(" spaced name ", "string", "keep the original name"),
108+
]
109+
110+
111+
def test_extra_driver_columns_remain_supported(get_hive_fields):
112+
assert get_hive_fields([
113+
("id", "int", "identifier", "extra", 1, None),
114+
("# Partition Information", None, None, "extra", 2, None),
115+
("dt", "string", "partition date", "extra", 3, None),
116+
]) == [
117+
("id", "int", "identifier"),
118+
("dt", "string", "partition date"),
119+
]
120+
121+
122+
@pytest.mark.parametrize("rows", [[], [("", "", ""), ("# col_name", "data_type", "comment")]])
123+
def test_no_real_columns_returns_empty_list(get_hive_fields, rows):
124+
assert get_hive_fields(rows) == []

0 commit comments

Comments
 (0)