Skip to content

Commit 536dfae

Browse files
authored
Improving resiliency of integration tests (#594)
1 parent 9ac6490 commit 536dfae

File tree

11 files changed

+56
-5
lines changed

11 files changed

+56
-5
lines changed

pyproject.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ dependencies = [
3535
test = [
3636
"coverage[toml]>=6.5",
3737
"pytest",
38+
"pytest-xdist",
3839
"pytest-cov>=4.0.0,<5.0.0",
3940
"pytest-mock>=3.0.0,<4.0.0",
4041
]
@@ -65,6 +66,7 @@ dependencies = [
6566

6667
[tool.hatch.envs.integration.scripts]
6768
test = "pytest --cov src tests/integration"
69+
parallel = "pytest -n auto --cov src tests/integration"
6870

6971
[tool.hatch.envs.lint]
7072
detached = true

src/databricks/labs/ucx/mixins/sql.py

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,15 @@
55
from collections.abc import Iterator
66
from datetime import timedelta
77

8+
from databricks.sdk import errors
9+
from databricks.sdk.errors import NotFound
810
from databricks.sdk.service.sql import (
911
ColumnInfoTypeName,
1012
Disposition,
1113
ExecuteStatementResponse,
1214
Format,
1315
ResultData,
16+
ServiceErrorCode,
1417
StatementExecutionAPI,
1518
StatementState,
1619
StatementStatus,
@@ -91,10 +94,28 @@ def __init__(self, api_client):
9194
def _raise_if_needed(status: StatementStatus):
9295
if status.state not in [StatementState.FAILED, StatementState.CANCELED, StatementState.CLOSED]:
9396
return
94-
msg = status.state.value
95-
if status.error is not None:
96-
msg = f"{msg}: {status.error.error_code.value} {status.error.message}"
97-
raise RuntimeError(msg)
97+
if "SCHEMA_NOT_FOUND" in status.error.message:
98+
raise NotFound(status.error.message)
99+
if "TABLE_NOT_FOUND" in status.error.message:
100+
raise NotFound(status.error.message)
101+
mapping = {
102+
ServiceErrorCode.ABORTED: errors.Aborted,
103+
ServiceErrorCode.ALREADY_EXISTS: errors.AlreadyExists,
104+
ServiceErrorCode.BAD_REQUEST: errors.BadRequest,
105+
ServiceErrorCode.CANCELLED: errors.Cancelled,
106+
ServiceErrorCode.DEADLINE_EXCEEDED: errors.DeadlineExceeded,
107+
ServiceErrorCode.INTERNAL_ERROR: errors.InternalError,
108+
ServiceErrorCode.IO_ERROR: errors.InternalError,
109+
ServiceErrorCode.NOT_FOUND: errors.NotFound,
110+
ServiceErrorCode.RESOURCE_EXHAUSTED: errors.ResourceExhausted,
111+
ServiceErrorCode.SERVICE_UNDER_MAINTENANCE: errors.TemporarilyUnavailable,
112+
ServiceErrorCode.TEMPORARILY_UNAVAILABLE: errors.TemporarilyUnavailable,
113+
ServiceErrorCode.UNAUTHENTICATED: errors.Unauthenticated,
114+
ServiceErrorCode.UNKNOWN: errors.Unknown,
115+
ServiceErrorCode.WORKSPACE_TEMPORARILY_UNAVAILABLE: errors.TemporarilyUnavailable,
116+
}
117+
error_class = mapping.get(status.error.error_code, errors.Unknown)
118+
raise error_class(status.error.message)
98119

99120
def execute(
100121
self,

src/databricks/labs/ucx/workspace_access/groups.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -312,6 +312,7 @@ def _get_or_create_backup_group(self, source_group_name: str, source_group: iam.
312312
roles=source_group.roles,
313313
members=source_group.members,
314314
) # TODO: there still could be a corner case, where we get `Group with name db-temp-XXX already exists.`
315+
# TODO: (nfx) except ResourceConflict: ... find group
315316
self._workspace_groups.append(backup_group)
316317
logger.info(f"Backup group {backup_group_name} successfully created")
317318

tests/integration/assessment/test_assessment.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
import logging
2+
from datetime import timedelta
23

4+
from databricks.sdk.errors import NotFound
5+
from databricks.sdk.retries import retried
36
from databricks.sdk.service import compute, jobs
47

58
from databricks.labs.ucx.assessment.crawlers import (
@@ -121,6 +124,7 @@ def test_spn_crawler(ws, inventory_schema, make_job, make_pipeline, sql_backend)
121124
assert results[0].tenant_id == _TEST_TENANT_ID
122125

123126

127+
@retried(on=[NotFound], timeout=timedelta(minutes=5))
124128
def test_spn_crawler_no_config(ws, inventory_schema, make_job, make_pipeline, sql_backend, make_cluster):
125129
make_job()
126130
make_pipeline()
@@ -167,6 +171,7 @@ def test_spn_crawler_deleted_cluster_policy(
167171
assert any(_ for _ in results if _.storage_account == _TEST_STORAGE_ACCOUNT)
168172

169173

174+
@retried(on=[NotFound], timeout=timedelta(minutes=5))
170175
def test_spn_crawler_with_pipeline_unavailable_secret(ws, inventory_schema, make_job, make_pipeline, sql_backend):
171176
make_job(spark_conf=_SPARK_CONF)
172177
make_pipeline(configuration=_PIPELINE_CONF_WITH_SECRET)

tests/integration/framework/test_crawlers.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,4 +8,4 @@ def test_deploys_database(sql_backend, inventory_schema):
88
deployer = SchemaDeployer(sql_backend, inventory_schema, ucx)
99
deployer.deploy_schema()
1010
deployer.deploy_table("grants", Grant)
11-
deployer.deploy_view("grant_detail", "assessment/views/grant_detail.sql")
11+
deployer.deploy_view("grant_detail", "queries/views/grant_detail.sql")

tests/integration/framework/test_fixtures.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
import logging
2+
from datetime import timedelta
23

34
import pytest
45
from _pytest.outcomes import Failed, Skipped
6+
from databricks.sdk.errors import NotFound
7+
from databricks.sdk.retries import retried
58
from databricks.sdk.service.workspace import AclPermission
69

710
from databricks.labs.ucx.mixins.compute import CommandExecutor
@@ -73,6 +76,7 @@ def test_pipeline(make_pipeline):
7376
logger.info(f"created {make_pipeline()}")
7477

7578

79+
@retried(on=[NotFound], timeout=timedelta(minutes=5))
7680
def test_this_wheel_installs(ws, wsfs_wheel):
7781
commands = CommandExecutor(ws)
7882

tests/integration/hive_metastore/test_grants.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,15 @@
11
import logging
2+
from datetime import timedelta
3+
4+
from databricks.sdk.errors import NotFound
5+
from databricks.sdk.retries import retried
26

37
from databricks.labs.ucx.hive_metastore import GrantsCrawler, TablesCrawler
48

59
logger = logging.getLogger(__name__)
610

711

12+
@retried(on=[NotFound], timeout=timedelta(minutes=10))
813
def test_all_grants_in_databases(sql_backend, inventory_schema, make_schema, make_table, make_group):
914
group_a = make_group()
1015
group_b = make_group()
@@ -36,6 +41,7 @@ def test_all_grants_in_databases(sql_backend, inventory_schema, make_schema, mak
3641
all_grants[f"{grant.principal}.{grant.object_key}"] = grant.action_type
3742

3843
assert len(all_grants) >= 8, "must have at least three grants"
44+
# TODO: (nfx) KeyError: 'sdk-xH7i.hive_metastore.default'
3945
assert all_grants[f"{group_a.display_name}.hive_metastore.default"] == "USAGE"
4046
assert all_grants[f"{group_b.display_name}.hive_metastore.default"] == "USAGE"
4147
assert all_grants[f"{group_a.display_name}.{table_a.full_name}"] == "SELECT"

tests/integration/test_installation.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
logger = logging.getLogger(__name__)
1919

2020

21+
@retried(on=[NotFound], timeout=timedelta(minutes=10))
2122
def test_job_failure_propagates_correct_error_message_and_logs(ws, sql_backend, env_or_skip, make_random):
2223
default_cluster_id = env_or_skip("TEST_DEFAULT_CLUSTER_ID")
2324
tacl_cluster_id = env_or_skip("TEST_LEGACY_TABLE_ACL_CLUSTER_ID")

tests/integration/workspace_access/test_groups.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ def test_prepare_environment(ws, make_ucx_group):
5353
continue
5454

5555

56+
@retried(on=[NotFound], timeout=timedelta(minutes=5))
5657
def test_prepare_environment_no_groups_selected(ws, make_ucx_group, make_group, make_acc_group):
5758
make_group()
5859
make_acc_group()
@@ -117,6 +118,8 @@ def test_replace_workspace_groups_with_account_groups(
117118
permission_manager.inventorize_permissions()
118119

119120
dummy_grants = list(permission_manager.load_all_for("TABLE", dummy_table.full_name, Grant))
121+
# TODO: (nfx) where 1 = len([Grant(principal='ucx_BjI1', action_type='MODIFY', catalog='hive_metastore',
122+
# database='ucx_s4ygj', table='ucx_tstdr', view=None, any_file=False, anonymous_function=False)])
120123
assert 2 == len(dummy_grants)
121124

122125
table_permissions = grants.for_table_info(dummy_table)
@@ -285,6 +288,7 @@ def test_migration_state_should_be_saved_without_missing_anything(sql_backend, m
285288
assert len(new_state.groups) == len(state.groups)
286289

287290

291+
@retried(on=[NotFound], timeout=timedelta(minutes=10))
288292
def test_set_owner_permission(
289293
ws,
290294
sql_backend,
@@ -316,6 +320,8 @@ def test_set_owner_permission(
316320
permission_manager.inventorize_permissions()
317321

318322
dummy_grants = list(permission_manager.load_all_for("TABLE", dummy_table.full_name, Grant))
323+
# TODO (nfx) where 1 = len([Grant(principal='ucx_Z1Ga', action_type='MODIFY', catalog='hive_metastore',
324+
# database='ucx_syubc', table='ucx_tiinm', view=None, any_file=False, anonymous_function=False)])
319325
assert 2 == len(dummy_grants)
320326

321327
table_permissions = grants.for_table_info(dummy_table)

tests/integration/workspace_access/test_scim.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
from databricks.labs.ucx.workspace_access.scim import ScimSupport
1111

1212

13+
# TODO: (nfx) databricks.sdk.errors.mapping.ResourceConflict: None Group with name db-temp-ucx_QSFR already exists.
1314
def test_scim(ws: WorkspaceClient, make_ucx_group, sql_backend, inventory_schema):
1415
"""
1516
This test does the following:

0 commit comments

Comments
 (0)