Skip to content

Commit 3445b33

Browse files
omkar-fossion-elgreco
authored andcommitted
test(python): add tests with mock APIs for Unity Catalog variants
Signed-off-by: Omkar P <[email protected]>
1 parent 66e3c40 commit 3445b33

File tree

6 files changed

+338
-2
lines changed

6 files changed

+338
-2
lines changed
Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
{
2+
"uuid": "0696d6d5-62b6-4a0f-8524-917b0c3848d4",
3+
"lastMigration": 33,
4+
"name": "Unity Catalog (Databricks) Test API",
5+
"endpointPrefix": "",
6+
"latency": 0,
7+
"port": 8080,
8+
"hostname": "",
9+
"folders": [],
10+
"routes": [
11+
{
12+
"uuid": "16d88cdc-abcf-4b0d-a4d6-18a3205a9a6c",
13+
"type": "http",
14+
"documentation": "Temporary Table Credentials API",
15+
"method": "post",
16+
"endpoint": "api/2.1/unity-catalog/temporary-table-credentials",
17+
"responses": [
18+
{
19+
"uuid": "50bba5c1-1e2c-4d5b-a611-ab478320f2b0",
20+
"body": "{\n \"aws_temp_credentials\": {\n \"access_key_id\": \"string\",\n \"secret_access_key\": \"string\",\n \"session_token\": \"string\",\n \"access_point\": \"string\"\n },\n \"azure_user_delegation_sas\": {\n \"sas_token\": \"string\"\n },\n \"r2_temp_credentials\": {\n \"access_key_id\": \"string\",\n \"secret_access_key\": \"string\",\n \"session_token\": \"string\"\n },\n \"expiration_time\": 0,\n \"url\": \"string\"\n}",
21+
"latency": 0,
22+
"statusCode": 200,
23+
"label": "Default response",
24+
"headers": [],
25+
"bodyType": "INLINE",
26+
"filePath": "",
27+
"databucketID": "",
28+
"sendFileAsBody": false,
29+
"rules": [],
30+
"rulesOperator": "OR",
31+
"disableTemplating": false,
32+
"fallbackTo404": false,
33+
"default": true,
34+
"crudKey": "id",
35+
"callbacks": []
36+
}
37+
],
38+
"responseMode": null,
39+
"streamingMode": null,
40+
"streamingInterval": 0
41+
},
42+
{
43+
"uuid": "66f51ad5-2ba3-4cb0-b633-c362a9f75836",
44+
"type": "http",
45+
"documentation": "Get Table Details API",
46+
"method": "get",
47+
"endpoint": "api/2.1/unity-catalog/tables/unity.default.testtable",
48+
"responses": [
49+
{
50+
"uuid": "da280b2c-07b6-45ee-8da7-92c50e4e540e",
51+
"body": "{\n \"name\": \"string\",\n \"catalog_name\": \"string\",\n \"schema_name\": \"string\",\n \"table_type\": \"MANAGED\",\n \"data_source_format\": \"DELTA\",\n \"columns\": [\n {\n \"name\": \"string\",\n \"type_text\": \"string\",\n \"type_name\": \"BOOLEAN\",\n \"position\": 0,\n \"type_precision\": 0,\n \"type_scale\": 0,\n \"type_interval_type\": \"string\",\n \"type_json\": \"string\",\n \"comment\": \"string\",\n \"nullable\": true,\n \"partition_index\": 0,\n \"mask\": {\n \"function_name\": \"string\",\n \"using_column_names\": [\n \"string\"\n ]\n }\n }\n ],\n \"storage_location\": \"../crates/test/tests/data/delta-0.8.0-partitioned\",\n \"view_definition\": \"string\",\n \"view_dependencies\": {\n \"dependencies\": [\n {\n \"table\": {\n \"table_full_name\": \"string\"\n },\n \"function\": {\n \"function_full_name\": \"string\"\n }\n }\n ]\n },\n \"sql_path\": \"string\",\n \"owner\": \"string\",\n \"comment\": \"string\",\n \"properties\": {\n \"property1\": \"string\",\n \"property2\": \"string\"\n },\n \"storage_credential_name\": \"string\",\n \"table_constraints\": [\n {\n \"primary_key_constraint\": {\n \"name\": \"string\",\n \"child_columns\": [\n \"string\"\n ]\n },\n \"foreign_key_constraint\": {\n \"name\": \"string\",\n \"child_columns\": [\n \"string\"\n ],\n \"parent_table\": \"string\",\n \"parent_columns\": [\n \"string\"\n ]\n },\n \"named_table_constraint\": {\n \"name\": \"string\"\n }\n }\n ],\n \"row_filter\": {\n \"function_name\": \"string\",\n \"input_column_names\": [\n \"string\"\n ]\n },\n \"enable_predictive_optimization\": \"DISABLE\",\n \"metastore_id\": \"string\",\n \"full_name\": \"string\",\n \"data_access_configuration_id\": \"string\",\n \"created_at\": 0,\n \"created_by\": \"string\",\n \"updated_at\": 0,\n \"updated_by\": \"string\",\n \"deleted_at\": 0,\n \"table_id\": \"string\",\n \"delta_runtime_properties_kvpairs\": {\n \"delta_runtime_properties\": {\n \"property1\": \"string\",\n \"property2\": \"string\"\n }\n },\n \"effective_predictive_optimization_flag\": {\n \"value\": \"DISABLE\",\n \"inherited_from_type\": \"CATALOG\",\n \"inherited_from_name\": \"string\"\n },\n \"access_point\": \"string\",\n \"pipeline_id\": \"string\",\n \"browse_only\": true\n}",
52+
"latency": 0,
53+
"statusCode": 200,
54+
"label": "Default response",
55+
"headers": [],
56+
"bodyType": "INLINE",
57+
"filePath": "",
58+
"databucketID": "",
59+
"sendFileAsBody": false,
60+
"rules": [],
61+
"rulesOperator": "OR",
62+
"disableTemplating": false,
63+
"fallbackTo404": false,
64+
"default": true,
65+
"crudKey": "id",
66+
"callbacks": []
67+
}
68+
],
69+
"responseMode": null,
70+
"streamingMode": null,
71+
"streamingInterval": 0
72+
}
73+
],
74+
"rootChildren": [
75+
{
76+
"type": "route",
77+
"uuid": "16d88cdc-abcf-4b0d-a4d6-18a3205a9a6c"
78+
},
79+
{
80+
"type": "route",
81+
"uuid": "66f51ad5-2ba3-4cb0-b633-c362a9f75836"
82+
}
83+
],
84+
"proxyMode": false,
85+
"proxyHost": "",
86+
"proxyRemovePrefix": false,
87+
"tlsOptions": {
88+
"enabled": false,
89+
"type": "CERT",
90+
"pfxPath": "",
91+
"certPath": "",
92+
"keyPath": "",
93+
"caPath": "",
94+
"passphrase": ""
95+
},
96+
"cors": true,
97+
"headers": [],
98+
"proxyReqHeaders": [
99+
{
100+
"key": "",
101+
"value": ""
102+
}
103+
],
104+
"proxyResHeaders": [
105+
{
106+
"key": "",
107+
"value": ""
108+
}
109+
],
110+
"data": [],
111+
"callbacks": []
112+
}
Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
{
2+
"uuid": "0696d6d5-62b6-4a0f-8524-917b0c3848d4",
3+
"lastMigration": 33,
4+
"name": "Unity Catalog (OSS) Test API",
5+
"endpointPrefix": "",
6+
"latency": 0,
7+
"port": 8080,
8+
"hostname": "",
9+
"folders": [],
10+
"routes": [
11+
{
12+
"uuid": "16d88cdc-abcf-4b0d-a4d6-18a3205a9a6c",
13+
"type": "http",
14+
"documentation": "Temporary Table Credentials API",
15+
"method": "post",
16+
"endpoint": "api/2.1/unity-catalog/temporary-table-credentials",
17+
"responses": [
18+
{
19+
"uuid": "50bba5c1-1e2c-4d5b-a611-ab478320f2b0",
20+
"body": "{\n \"aws_temp_credentials\": {\n \"access_key_id\": \"string\",\n \"secret_access_key\": \"string\",\n \"session_token\": \"string\"\n },\n \"azure_user_delegation_sas\": {\n \"sas_token\": \"string\"\n },\n \"gcp_oauth_token\": {\n \"oauth_token\": \"string\"\n },\n \"expiration_time\": 0\n}",
21+
"latency": 0,
22+
"statusCode": 200,
23+
"label": "Default response",
24+
"headers": [],
25+
"bodyType": "INLINE",
26+
"filePath": "",
27+
"databucketID": "",
28+
"sendFileAsBody": false,
29+
"rules": [],
30+
"rulesOperator": "OR",
31+
"disableTemplating": false,
32+
"fallbackTo404": false,
33+
"default": true,
34+
"crudKey": "id",
35+
"callbacks": []
36+
}
37+
],
38+
"responseMode": null,
39+
"streamingMode": null,
40+
"streamingInterval": 0
41+
},
42+
{
43+
"uuid": "66f51ad5-2ba3-4cb0-b633-c362a9f75836",
44+
"type": "http",
45+
"documentation": "Get Table Details API",
46+
"method": "get",
47+
"endpoint": "api/2.1/unity-catalog/tables/unity.default.testtable",
48+
"responses": [
49+
{
50+
"uuid": "da280b2c-07b6-45ee-8da7-92c50e4e540e",
51+
"body": "{\n \"name\": \"string\",\n \"catalog_name\": \"string\",\n \"schema_name\": \"string\",\n \"table_type\": \"MANAGED\",\n \"data_source_format\": \"DELTA\",\n \"columns\": [\n {\n \"name\": \"string\",\n \"type_text\": \"string\",\n \"type_json\": \"string\",\n \"type_name\": \"BOOLEAN\",\n \"type_precision\": 0,\n \"type_scale\": 0,\n \"type_interval_type\": \"string\",\n \"position\": 0,\n \"comment\": \"string\",\n \"nullable\": true,\n \"partition_index\": 0\n }\n ],\n \"storage_location\": \"string\",\n \"comment\": \"string\",\n \"properties\": {\n \"additionalProp1\": \"string\",\n \"additionalProp2\": \"string\",\n \"additionalProp3\": \"string\"\n },\n \"owner\": \"string\",\n \"created_at\": 0,\n \"created_by\": \"string\",\n \"updated_at\": 0,\n \"updated_by\": \"string\",\n \"table_id\": \"string\"\n}",
52+
"latency": 0,
53+
"statusCode": 200,
54+
"label": "Default response",
55+
"headers": [],
56+
"bodyType": "INLINE",
57+
"filePath": "",
58+
"databucketID": "",
59+
"sendFileAsBody": false,
60+
"rules": [],
61+
"rulesOperator": "OR",
62+
"disableTemplating": false,
63+
"fallbackTo404": false,
64+
"default": true,
65+
"crudKey": "id",
66+
"callbacks": []
67+
}
68+
],
69+
"responseMode": null,
70+
"streamingMode": null,
71+
"streamingInterval": 0
72+
}
73+
],
74+
"rootChildren": [
75+
{
76+
"type": "route",
77+
"uuid": "16d88cdc-abcf-4b0d-a4d6-18a3205a9a6c"
78+
},
79+
{
80+
"type": "route",
81+
"uuid": "66f51ad5-2ba3-4cb0-b633-c362a9f75836"
82+
}
83+
],
84+
"proxyMode": false,
85+
"proxyHost": "",
86+
"proxyRemovePrefix": false,
87+
"tlsOptions": {
88+
"enabled": false,
89+
"type": "CERT",
90+
"pfxPath": "",
91+
"certPath": "",
92+
"keyPath": "",
93+
"caPath": "",
94+
"passphrase": ""
95+
},
96+
"cors": true,
97+
"headers": [],
98+
"proxyReqHeaders": [
99+
{
100+
"key": "",
101+
"value": ""
102+
}
103+
],
104+
"proxyResHeaders": [
105+
{
106+
"key": "",
107+
"value": ""
108+
}
109+
],
110+
"data": [],
111+
"callbacks": []
112+
}

.github/workflows/python_build.yml

Lines changed: 59 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ jobs:
8484
uv pip uninstall pandas
8585
uv run --no-sync pytest -m "not pandas and not integration and not benchmark"
8686
uv pip install pandas
87-
87+
8888
test-lakefs:
8989
name: Python Build (Python 3.10 LakeFS Integration tests)
9090
runs-on: ubuntu-latest
@@ -110,6 +110,64 @@ jobs:
110110
- name: Run tests
111111
run: uv run --no-sync pytest -m '(lakefs and integration)' --doctest-modules
112112

113+
test-unitycatalog-databricks:
114+
name: Python Build (Python 3.10 Unity Catalog Integration tests)
115+
runs-on: ubuntu-latest
116+
env:
117+
RUSTFLAGS: "-C debuginfo=1"
118+
CARGO_INCREMENTAL: 0
119+
120+
steps:
121+
- uses: actions/checkout@v3
122+
123+
- name: Setup Environment
124+
uses: ./.github/actions/setup-env
125+
126+
- name: Run Mockoon CLI
127+
uses: mockoon/cli-action@v2
128+
with:
129+
version: "latest"
130+
data-file: "./mockoon_data_files/unitycatalog_databricks.json"
131+
port: 8080
132+
133+
- name: Build and install deltalake
134+
run: make develop
135+
136+
- name: Download Data Acceptance Tests (DAT) files
137+
run: make setup-dat
138+
139+
- name: Run tests
140+
run: uv run --no-sync pytest -m '(unitycatalog_databricks and integration)' --doctest-modules
141+
142+
test-unitycatalog-oss:
143+
name: Python Build (Python 3.10 Unity Catalog Integration tests)
144+
runs-on: ubuntu-latest
145+
env:
146+
RUSTFLAGS: "-C debuginfo=1"
147+
CARGO_INCREMENTAL: 0
148+
149+
steps:
150+
- uses: actions/checkout@v3
151+
152+
- name: Setup Environment
153+
uses: ./.github/actions/setup-env
154+
155+
- name: Run Mockoon CLI
156+
uses: mockoon/cli-action@v2
157+
with:
158+
version: "latest"
159+
data-file: "./mockoon_data_files/unitycatalog_oss.json"
160+
port: 8080
161+
162+
- name: Build and install deltalake
163+
run: make develop
164+
165+
- name: Download Data Acceptance Tests (DAT) files
166+
run: make setup-dat
167+
168+
- name: Run tests
169+
run: uv run --no-sync pytest -m '(unitycatalog_oss and integration)' --doctest-modules
170+
113171
test-pyspark:
114172
name: PySpark Integration Tests
115173
runs-on: ubuntu-latest

crates/catalog-unity/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -473,7 +473,7 @@ impl UnityCatalogBuilder {
473473
.await
474474
{
475475
Ok(s) => s,
476-
Err(_e) => panic!("Unable to find the table's storage location."),
476+
Err(err) => panic!("Unable to find the table's storage location. {}", err),
477477
};
478478
let temp_creds_res = unity_catalog
479479
.get_temp_table_credentials(catalog_id, database_name, table_name)

python/pyproject.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,8 @@ markers = [
9797
"pandas: marks tests that require pandas",
9898
"polars: marks tests that require polars",
9999
"lakefs: marks tests that require lakefs",
100+
"unitycatalog_databricks: marks tests that require unitycatalog_databricks",
101+
"unitycatalog_oss: marks tests that require unitycatalog_oss",
100102
"pyspark: marks tests that require pyspark",
101103
]
102104

python/tests/test_unity_catalog.py

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
import os
2+
3+
import pytest
4+
5+
from deltalake import DeltaTable
6+
7+
8+
@pytest.mark.unitycatalog_databricks
9+
@pytest.mark.integration
10+
@pytest.mark.timeout(timeout=10, method="thread")
11+
def test_uc_read_deltatable():
12+
"""Test delta table reads using Unity Catalog URL (uc://)"""
13+
14+
os.environ["DATABRICKS_WORKSPACE_URL"] = "http://localhost:8080"
15+
os.environ["DATABRICKS_ACCESS_TOKEN"] = "123456"
16+
os.environ["UNITY_ALLOW_HTTP_URL"] = "true"
17+
18+
dt = DeltaTable("uc://unity.default.testtable")
19+
20+
assert dt.is_deltatable(dt.table_uri), True
21+
expected = {
22+
"value": ["1", "2", "3", "6", "7", "5", "4"],
23+
"year": ["2020", "2020", "2020", "2021", "2021", "2021", "2021"],
24+
"month": ["1", "2", "2", "12", "12", "12", "4"],
25+
"day": ["1", "3", "5", "20", "20", "4", "5"],
26+
}
27+
assert dt.to_pyarrow_dataset().to_table().to_pydict() == expected
28+
29+
30+
@pytest.mark.unitycatalog_oss
31+
@pytest.mark.integration
32+
@pytest.mark.timeout(timeout=10, method="thread")
33+
def test_uc_read_deltatable_failing():
34+
"""Test delta table reads using Unity Catalog URL (uc://)"""
35+
36+
os.environ["DATABRICKS_WORKSPACE_URL"] = "http://localhost:8080"
37+
os.environ["DATABRICKS_ACCESS_TOKEN"] = "123456"
38+
os.environ["UNITY_ALLOW_HTTP_URL"] = "true"
39+
40+
# @TODO: Currently, this will fail when used with Unity Catalog OSS
41+
# mock APIs. Need to add support for slightly different response payloads
42+
# of Unity Catalog OSS.
43+
with pytest.raises(BaseException):
44+
dt = DeltaTable("uc://unity.default.testtable")
45+
assert dt.is_deltatable(dt.table_uri), True
46+
expected = {
47+
"value": ["1", "2", "3", "6", "7", "5", "4"],
48+
"year": ["2020", "2020", "2020", "2021", "2021", "2021", "2021"],
49+
"month": ["1", "2", "2", "12", "12", "12", "4"],
50+
"day": ["1", "3", "5", "20", "20", "4", "5"],
51+
}
52+
assert dt.to_pyarrow_dataset().to_table().to_pydict() == expected

0 commit comments

Comments
 (0)