8000 fix(jobs): keep omitted settings unchanged on jobs/update by avison9 · Pull Request #69 · dmux/minilake · GitHub
[go: up one dir, main page]

Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion src/minilake/services/jobs.py
Original file line number Diff line number Diff line change
Expand Up @@ -201,7 +201,12 @@ async def update_job(req: UpdateJobRequest) -> dict:
job = _state["jobs"][req.job_id]
new_settings = dict(job["settings"])
if req.new_settings is not None:
new_settings.update(req.new_settings.model_dump(exclude_none=True))
# Merge only the top-level fields the request actually sent. A plain dump also
# carries model defaults (name="Untitled"), which would overwrite stored values the
# caller never touched. exclude_unset is not used because it would also strip the
# nested defaults (e.g. a task's run_if) that create_job stores.
sent = req.new_settings.model_fields_set
new_settings.update(req.new_settings.model_dump(exclude_none=True, include=sent))
for field in req.fields_to_remove or []:
new_settings.pop(field, None)
_validate_task_graph(_tasks_from_settings(new_settings))
Expand Down
71 changes: 71 additions & 0 deletions tests/test_jobs.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
from databricks.sdk.errors import DatabricksError
from databricks.sdk.service.compute import ClusterSpec
from databricks.sdk.service.jobs import (
JobEmailNotifications,
JobSettings,
NotebookTask,
RunIf,
Expand Down Expand Up @@ -73,6 +74,76 @@ def test_job_list_includes_created(workspace_client: WorkspaceClient):
print(f"✓ Job {job.job_id} appears in job list")


@pytest.mark.crud
@pytest.mark.parametrize(
"new_settings",
[
JobSettings(tags={"team": "qa"}),
JobSettings(timeout_seconds=600),
JobSettings(),
],
ids=["tags-only", "timeout-only", "empty"],
)
def test_job_partial_update_keeps_omitted_fields(workspace_client: WorkspaceClient, new_settings: JobSettings):
"""Test: jobs/update only changes the fields it sends; the name and tasks survive."""
job = workspace_client.jobs.create(
name="valuable name",
tasks=[Task(task_key="main", notebook_task=NotebookTask(notebook_path="/Shared/nb"))],
)

workspace_client.jobs.update(job_id=job.job_id, new_settings=new_settings)

settings = workspace_client.jobs.get(job_id=job.job_id).settings
assert settings.name == "valuable name"
assert [t.task_key for t in settings.tasks] == ["main"]
if new_settings.tags is not None:
assert settings.tags == new_settings.tags
if new_settings.timeout_seconds is not None:
assert settings.timeout_seconds == new_settings.timeout_seconds

print(f"✓ Partial update kept name and tasks of job {job.job_id}")


@pytest.mark.crud
def test_job_update_with_name_renames(workspace_client: WorkspaceClient):
"""Test: a name sent in jobs/update still replaces the stored one."""
job = workspace_client.jobs.create(name="old name", tasks=[])

workspace_client.jobs.update(job_id=job.job_id, new_settings=JobSettings(name="new name"))

assert workspace_client.jobs.get(job_id=job.job_id).settings.name == "new name"

print(f"✓ Job {job.job_id} renamed via update")


@pytest.mark.crud
def test_job_partial_update_keeps_and_replaces_extra_fields(workspace_client: WorkspaceClient):
"""Test: a setting outside the declared model (email_notifications) is kept when omitted
from jobs/update and replaced when sent, and the name survives both."""
job = workspace_client.jobs.create(
name="valuable name",
email_notifications=JobEmailNotifications(on_failure=["oncall@example.com"]),
tasks=[],
)

workspace_client.jobs.update(job_id=job.job_id, new_settings=JobSettings(tags={"team": "qa"}))
settings = workspace_client.jobs.get(job_id=job.job_id).settings
assert settings.name == "valuable name"
assert settings.email_notifications.on_failure == ["oncall@example.com"]

workspace_client.jobs.update(
job_id=job.job_id,
new_settings=JobSettings(email_notifications=JobEmailNotifications(on_success=["team@example.com"])),
)
settings = workspace_client.jobs.get(job_id=job.job_id).settings
assert settings.name == "valuable name"
assert settings.tags == {"team": "qa"}
assert settings.email_notifications.on_success == ["team@example.com"]
assert not settings.email_notifications.on_failure # a sent top-level field replaces it whole

print(f"✓ Partial updates kept and replaced email_notifications of job {job.job_id}")


@pytest.mark.crud
def test_job_delete(workspace_client: WorkspaceClient):
"""Test: Deleting a job removes it; get then raises NOT_FOUND."""
Expand Down
Loading
0