Skip to content

Commit 078a0bc

Browse files
added tests for switch
1 parent c49c5b3 commit 078a0bc

File tree

1 file changed

+287
-53
lines changed

1 file changed

+287
-53
lines changed
Lines changed: 287 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -1,90 +1,324 @@
1-
from collections.abc import Sequence
2-
from unittest.mock import Mock, create_autospec
3-
1+
from unittest.mock import create_autospec
2+
from typing import Any, cast
43
import pytest
54

6-
from databricks.labs.blueprint.installation import Installation
5+
from databricks.labs.blueprint.installation import MockInstallation
76
from databricks.labs.blueprint.installer import InstallState
87
from databricks.labs.blueprint.wheels import ProductInfo
8+
from databricks.labs.lakebridge.config import LakebridgeConfiguration
99
from databricks.labs.lakebridge.deployment.job import JobDeployment
1010
from databricks.labs.lakebridge.deployment.switch import SwitchDeployment
11-
from databricks.sdk import WorkspaceClient
12-
from databricks.sdk.errors import NotFound
13-
from databricks.sdk.service.jobs import JobParameterDefinition
11+
from databricks.sdk import WorkspaceClient, JobsExt
12+
from databricks.sdk.errors import NotFound, InvalidParameterValue
13+
from databricks.sdk.service.jobs import CreateResponse
14+
from databricks.sdk.service.iam import User
1415

1516

16-
class FriendOfSwitchDeployment(SwitchDeployment):
17-
"""A friend class to access protected members for testing purposes."""
17+
@pytest.fixture()
18+
def mock_workspace_client() -> WorkspaceClient:
19+
ws: Any = create_autospec(WorkspaceClient, instance=True)
20+
ws.current_user.me.return_value = User(user_name="test_user")
21+
ws.config.host = "https://test.databricks.com"
22+
ws.jobs = cast(Any, create_autospec(JobsExt, instance=True))
23+
return ws
1824

19-
def get_switch_job_parameters(self) -> Sequence[JobParameterDefinition]:
20-
return self._get_switch_job_parameters()
25+
26+
@pytest.fixture()
27+
def installation() -> MockInstallation:
28+
return MockInstallation(is_global=False)
2129

2230

2331
@pytest.fixture()
24-
def workspace_client() -> WorkspaceClient:
25-
ws = create_autospec(WorkspaceClient)
26-
ws.jobs = Mock()
27-
ws.jobs.delete = Mock()
28-
ws.jobs.get = Mock()
29-
ws.jobs.reset = Mock()
30-
ws.jobs.create = Mock()
31-
return ws
32+
def install_state(installation: MockInstallation) -> InstallState:
33+
return InstallState.from_installation(installation)
3234

3335

3436
@pytest.fixture()
35-
def install_state() -> InstallState:
36-
state = create_autospec(InstallState)
37-
state.jobs = {}
38-
state.switch_resources = {}
39-
return state
37+
def product_info() -> ProductInfo:
38+
return ProductInfo.for_testing(LakebridgeConfiguration)
4039

4140

4241
@pytest.fixture()
43-
def switch_deployment(workspace_client: WorkspaceClient, install_state: InstallState) -> SwitchDeployment:
44-
installation = create_autospec(Installation)
45-
product_info = create_autospec(ProductInfo)
46-
job_deployer = create_autospec(JobDeployment)
42+
def job_deployer() -> JobDeployment:
43+
return create_autospec(JobDeployment, instance=True)
44+
45+
46+
@pytest.fixture()
47+
def switch_deployment(
48+
mock_workspace_client: Any,
49+
installation: MockInstallation,
50+
install_state: InstallState,
51+
product_info: ProductInfo,
52+
job_deployer: JobDeployment,
53+
) -> SwitchDeployment:
54+
return SwitchDeployment(mock_workspace_client, installation, install_state, product_info, job_deployer)
55+
56+
57+
def test_install_creates_job_successfully(
58+
switch_deployment: SwitchDeployment, mock_workspace_client: Any, install_state: InstallState
59+
) -> None:
60+
"""Test successful installation creates job and saves state."""
61+
mock_workspace_client.jobs.create.return_value = CreateResponse(job_id=123)
62+
63+
switch_deployment.install()
64+
65+
assert install_state.jobs["Switch"] == "123"
66+
mock_workspace_client.jobs.create.assert_called_once()
67+
68+
69+
def test_install_updates_existing_job(
70+
switch_deployment: SwitchDeployment, mock_workspace_client: Any, install_state: InstallState
71+
) -> None:
72+
"""Test installation updates existing job if found."""
73+
install_state.jobs["Switch"] = "456"
74+
mock_workspace_client.jobs.get.return_value = create_autospec(CreateResponse, instance=True)
75+
76+
switch_deployment.install()
77+
78+
assert install_state.jobs["Switch"] == "456"
79+
mock_workspace_client.jobs.reset.assert_called_once()
80+
mock_workspace_client.jobs.create.assert_not_called()
81+
82+
83+
def test_install_creates_new_job_when_existing_not_found(
84+
switch_deployment: SwitchDeployment, mock_workspace_client: Any, install_state: InstallState
85+
) -> None:
86+
"""Test installation creates new job when existing job is not found."""
87+
install_state.jobs["Switch"] = "789"
88+
mock_workspace_client.jobs.get.side_effect = NotFound("Job not found")
89+
mock_workspace_client.jobs.create.return_value = CreateResponse(job_id=999)
90+
91+
switch_deployment.install()
92+
93+
assert install_state.jobs["Switch"] == "999"
94+
mock_workspace_client.jobs.create.assert_called_once()
95+
96+
97+
def test_install_handles_job_creation_error(
98+
switch_deployment: SwitchDeployment, mock_workspace_client: Any, install_state: InstallState
99+
) -> None:
100+
"""Test installation handles job creation errors gracefully."""
101+
mock_workspace_client.jobs.create.side_effect = RuntimeError("Job creation failed")
102+
103+
switch_deployment.install()
104+
105+
# State should not be updated on error
106+
assert "Switch" not in install_state.jobs
107+
108+
109+
def test_install_handles_invalid_parameter_error(
110+
switch_deployment: SwitchDeployment, mock_workspace_client: Any, install_state: InstallState
111+
) -> None:
112+
"""Test installation handles invalid parameter errors gracefully."""
113+
mock_workspace_client.jobs.create.side_effect = InvalidParameterValue("Invalid parameter")
114+
115+
switch_deployment.install()
116+
117+
assert "Switch" not in install_state.jobs
118+
119+
120+
def test_install_fallback_on_update_failure(
121+
switch_deployment: SwitchDeployment, mock_workspace_client: Any, install_state: InstallState
122+
) -> None:
123+
install_state.jobs["Switch"] = "555"
124+
mock_workspace_client.jobs.get.return_value = create_autospec(CreateResponse, instance=True)
125+
mock_workspace_client.jobs.reset.side_effect = InvalidParameterValue("Update failed")
126+
new_job = CreateResponse(job_id=666)
127+
mock_workspace_client.jobs.create.return_value = new_job
128+
129+
switch_deployment.install()
130+
131+
assert install_state.jobs["Switch"] == "666"
132+
mock_workspace_client.jobs.reset.assert_called_once()
133+
mock_workspace_client.jobs.create.assert_called_once()
134+
135+
136+
def test_install_with_invalid_existing_job_id(
137+
switch_deployment: SwitchDeployment, mock_workspace_client: Any, install_state: InstallState
138+
) -> None:
139+
install_state.jobs["Switch"] = "not_a_number"
140+
mock_workspace_client.jobs.get.side_effect = ValueError("Invalid job ID")
141+
new_job = CreateResponse(job_id=777)
142+
mock_workspace_client.jobs.create.return_value = new_job
143+
144+
switch_deployment.install()
145+
146+
assert install_state.jobs["Switch"] == "777"
147+
mock_workspace_client.jobs.create.assert_called_once()
148+
149+
150+
def test_install_preserves_other_jobs_in_state(
151+
switch_deployment: SwitchDeployment, mock_workspace_client: Any, install_state: InstallState
152+
) -> None:
153+
install_state.jobs["OtherJob"] = "999"
154+
new_job = CreateResponse(job_id=123)
155+
mock_workspace_client.jobs.create.return_value = new_job
47156

48-
return SwitchDeployment(workspace_client, installation, install_state, product_info, job_deployer)
157+
switch_deployment.install()
49158

159+
assert install_state.jobs["Switch"] == "123"
160+
assert install_state.jobs["OtherJob"] == "999"
50161

51-
def test_uninstall_removes_job_and_saves_state(
52-
switch_deployment: SwitchDeployment, install_state, workspace_client
162+
163+
def test_install_configures_job_with_correct_parameters(
164+
switch_deployment: SwitchDeployment, mock_workspace_client: Any
53165
) -> None:
54-
install_state.jobs = {"Switch": "123"}
55-
install_state.save.reset_mock()
166+
"""Test installation configures job with correct parameters."""
167+
new_job = CreateResponse(job_id=123)
168+
mock_workspace_client.jobs.create.return_value = new_job
169+
170+
switch_deployment.install()
171+
172+
# Verify job creation was called with settings
173+
mock_workspace_client.jobs.create.assert_called_once()
174+
call_kwargs = mock_workspace_client.jobs.create.call_args.kwargs
175+
176+
# Verify job name
177+
assert call_kwargs["name"] == "Lakebridge_Switch"
178+
179+
# Verify tags
180+
assert "created_by" in call_kwargs["tags"]
181+
assert call_kwargs["tags"]["created_by"] == "test_user"
182+
assert "switch_version" in call_kwargs["tags"]
183+
184+
# Verify tasks
185+
assert len(call_kwargs["tasks"]) == 1
186+
assert call_kwargs["tasks"][0].task_key == "run_transpilation"
187+
assert call_kwargs["tasks"][0].disable_auto_optimization is True
188+
189+
# Verify parameters
190+
param_names = {param.name for param in call_kwargs["parameters"]}
191+
assert param_names == {"source_tech", "input_dir", "output_dir"}
192+
193+
# Verify max concurrent runs
194+
assert call_kwargs["max_concurrent_runs"] == 100
195+
196+
197+
def test_install_configures_job_with_correct_notebook_path(
198+
switch_deployment: SwitchDeployment, mock_workspace_client: Any, installation: MockInstallation
199+
) -> None:
200+
"""Test installation configures job with correct notebook path."""
201+
new_job = CreateResponse(job_id=123)
202+
mock_workspace_client.jobs.create.return_value = new_job
203+
204+
switch_deployment.install()
205+
206+
call_kwargs = mock_workspace_client.jobs.create.call_args.kwargs
207+
notebook_path = call_kwargs["tasks"][0].notebook_task.notebook_path
56208

57-
workspace_client.jobs.delete.reset_mock()
209+
# Verify notebook path includes switch directory and notebook name
210+
assert "switch" in notebook_path
211+
assert "notebooks" in notebook_path
212+
assert "00_main" in notebook_path
213+
214+
215+
def test_uninstall_removes_job_successfully(
216+
switch_deployment: SwitchDeployment, mock_workspace_client: Any, install_state: InstallState
217+
) -> None:
218+
install_state.jobs["Switch"] = "123"
58219

59220
switch_deployment.uninstall()
60221

61222
assert "Switch" not in install_state.jobs
62-
workspace_client.jobs.delete.assert_called_once_with(123)
63-
install_state.save.assert_called_once()
223+
mock_workspace_client.jobs.delete.assert_called_once_with(123)
64224

65225

66-
def test_uninstall_handles_missing_job(switch_deployment: SwitchDeployment, install_state, workspace_client) -> None:
67-
install_state.jobs = {"Switch": "123"}
68-
workspace_client.jobs.delete.side_effect = NotFound("missing")
226+
def test_uninstall_handles_job_not_found(
227+
switch_deployment: SwitchDeployment, mock_workspace_client: Any, install_state: InstallState
228+
) -> None:
229+
install_state.jobs["Switch"] = "456"
230+
mock_workspace_client.jobs.delete.side_effect = NotFound("Job not found")
69231

70232
switch_deployment.uninstall()
71233

72-
install_state.save.assert_called_once()
234+
assert "Switch" not in install_state.jobs
235+
73236

237+
def test_uninstall_handles_invalid_parameter(
238+
switch_deployment: SwitchDeployment, mock_workspace_client: Any, install_state: InstallState
239+
) -> None:
240+
install_state.jobs["Switch"] = "789"
241+
mock_workspace_client.jobs.delete.side_effect = InvalidParameterValue("Invalid job ID")
242+
243+
switch_deployment.uninstall()
244+
245+
assert "Switch" not in install_state.jobs
74246

75-
def test_get_switch_job_parameters_excludes_wait_for_completion() -> None:
76-
ws = create_autospec(WorkspaceClient)
77-
installation = create_autospec(Installation)
78-
state = create_autospec(InstallState)
79-
state.jobs = {}
80-
product_info = create_autospec(ProductInfo)
81-
job_deployer = create_autospec(JobDeployment)
82247

83-
deployment = FriendOfSwitchDeployment(ws, installation, state, product_info, job_deployer)
248+
def test_uninstall_no_job_in_state(switch_deployment: SwitchDeployment, mock_workspace_client: Any) -> None:
249+
switch_deployment.uninstall()
250+
251+
mock_workspace_client.jobs.delete.assert_not_called()
252+
253+
254+
def test_uninstall_with_invalid_job_id_format(
255+
switch_deployment: SwitchDeployment, mock_workspace_client: Any, install_state: InstallState
256+
) -> None:
257+
install_state.jobs["Switch"] = "not_a_number"
258+
259+
# Should raise ValueError when trying to convert to int
260+
with pytest.raises(ValueError):
261+
switch_deployment.uninstall()
262+
263+
264+
def test_uninstall_preserves_other_jobs_in_state(
265+
switch_deployment: SwitchDeployment, mock_workspace_client: Any, install_state: InstallState
266+
) -> None:
267+
install_state.jobs["Switch"] = "123"
268+
install_state.jobs["OtherJob"] = "999"
269+
270+
switch_deployment.uninstall()
271+
272+
assert "Switch" not in install_state.jobs
273+
assert install_state.jobs["OtherJob"] == "999"
274+
275+
276+
# Parameterized tests
277+
278+
279+
@pytest.mark.parametrize(
280+
"exception",
281+
[
282+
NotFound("Job not found"),
283+
InvalidParameterValue("Invalid parameter"),
284+
],
285+
)
286+
def test_uninstall_handles_exceptions(
287+
switch_deployment: SwitchDeployment,
288+
mock_workspace_client: Any,
289+
install_state: InstallState,
290+
exception,
291+
) -> None:
292+
install_state.jobs["Switch"] = "123"
293+
mock_workspace_client.jobs.delete.side_effect = exception
294+
295+
switch_deployment.uninstall()
296+
297+
assert "Switch" not in install_state.jobs
298+
299+
300+
@pytest.mark.parametrize(
301+
"exception,expected_job_id",
302+
[
303+
(InvalidParameterValue("Update failed"), 888),
304+
(ValueError("Invalid job ID"), 777),
305+
],
306+
)
307+
def test_install_creates_new_job_on_update_failure(
308+
switch_deployment: SwitchDeployment,
309+
mock_workspace_client: Any,
310+
install_state: InstallState,
311+
exception,
312+
expected_job_id,
313+
) -> None:
314+
install_state.jobs["Switch"] = "555"
315+
mock_workspace_client.jobs.get.return_value = create_autospec(CreateResponse, instance=True)
316+
mock_workspace_client.jobs.reset.side_effect = exception
317+
new_job = CreateResponse(job_id=expected_job_id)
318+
mock_workspace_client.jobs.create.return_value = new_job
84319

85-
job_params = deployment.get_switch_job_parameters()
86-
param_names = {param.name for param in job_params}
320+
switch_deployment.install()
87321

88-
assert "source_tech" in param_names
89-
assert "input_dir" in param_names
90-
assert "output_dir" in param_names
322+
assert install_state.jobs["Switch"] == str(expected_job_id)
323+
mock_workspace_client.jobs.reset.assert_called_once()
324+
mock_workspace_client.jobs.create.assert_called_once()

0 commit comments

Comments
 (0)