Skip to content

feat(sdk): migrate unit test from urllib3-mock to MagicMock #405

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 39 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
78e1f96
feat: run sdk unit test for python 3.9-3.12
Apr 22, 2025
ecdd9e2
chore: adjust dependency to support python 3.11++
Apr 22, 2025
cbf6f6e
feat: adjust test_list_jobs
Apr 22, 2025
f35c61a
feat: adjust test_submit_job and create fixture for active project ma…
Apr 22, 2025
440b249
feat: adjust test_fetch_job
Apr 22, 2025
e410c69
feat: adjust test_terminate_job
Apr 22, 2025
bb904e4
feat: adjust test_list_images
Apr 22, 2025
f096772
feat: adjust test_create_image
Apr 22, 2025
7ca5eae
feat: adjust test_list_ensemblers
Apr 23, 2025
24dcaf5
feat: adjust test_create_ensembler
Apr 23, 2025
fa8de42
chore: remove unnecessary context manager
Apr 23, 2025
42f157b
feat: adjust test_update_ensembler
Apr 23, 2025
48e50fd
feat: refactor magic mock for mlflow and gcs to fixtures
Apr 23, 2025
cb5107c
feat: adjust test_update_ensembler_existing_router_version
Apr 23, 2025
9931967
feat: adjust test_update_ensembler_existing_job
Apr 23, 2025
bf3deae
feat: adjust test_delete_ensembler
Apr 23, 2025
176f97c
feat: remove unused fixtures
Apr 23, 2025
bfbeaa6
feat: adjust test_list_projects
Apr 23, 2025
792adf2
feat: adjust test_create_version
Apr 23, 2025
b52f19d
feat: adjust test_list_routers
Apr 23, 2025
4d247ce
feat: adjust test_create_router
Apr 23, 2025
fe40f58
feat: adjust test_delete_router
Apr 23, 2025
2835b08
feat: adjust test_get_router
Apr 23, 2025
5079d50
feat: adjust test_update_router
Apr 23, 2025
5303d6d
feat: adjust test_deploy_router
Apr 23, 2025
af288da
feat: adjust test_undeploy_router
Apr 23, 2025
f401aa1
feat: adjust test_list_versions
Apr 23, 2025
5b4e0d6
feat: adjust test_get_version
Apr 23, 2025
f549dcb
feat: adjust test_get_version_config
Apr 23, 2025
1665090
feat: adjust test_delete_version
Apr 23, 2025
3a2982d
feat: adjust test_deploy_version
Apr 23, 2025
bb86562
feat: adjust test_get_events_list
Apr 23, 2025
69523ff
feat: adjust test_wait_for_status
Apr 23, 2025
6aacdae
feat: adjust test_wait_for_version_status
Apr 23, 2025
30b1e89
chore: try to upgrade setuptools version
Apr 23, 2025
9dd2b4e
feat: bump numpy version to support python 3.9 - 3.12
Apr 23, 2025
1d22d37
chore: install wheel
Apr 23, 2025
7e532aa
chore: adjust numpy version
Apr 23, 2025
01479a7
feat: bump python version used by publish CI
Apr 24, 2025
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
4 changes: 2 additions & 2 deletions .github/workflows/sdk.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ["3.8", "3.9", "3.10"]
python-version: ["3.9", "3.10", "3.11", "3.12"]

defaults:
run:
Expand Down Expand Up @@ -92,7 +92,7 @@ jobs:
- name: Setup Python
uses: actions/setup-python@v5
with:
python-version: 3.8
python-version: 3.11
cache: pip
cache-dependency-path: |
sdk/requirements.txt
Expand Down
3 changes: 2 additions & 1 deletion sdk/Makefile
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
.PHONY: setup
setup:
pip install -r requirements.txt -r requirements.dev.txt
@pip install "setuptools>=64,<75" "wheel"
@pip install -r requirements.txt -r requirements.dev.txt

.PHONY: gen-client
gen-client:
Expand Down
6 changes: 2 additions & 4 deletions sdk/requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,11 @@ deprecation==2.1.0
fire
google-cloud-storage>=1.19.0
mlflow>=1.26.1,<2.0.0
# Numpy >= v1.24.0 is incompatible with our pinned versions of mlflow due to the deprecation of several common numpy
# aliases (see the last bullet point here: https://numpy.org/doc/stable/release/1.24.0-notes.html#expired-deprecations).
numpy<1.24.0
pandas
protobuf>=3.12.0,<5.0.0 # Determined by the mlflow dependency
python_dateutil>=2.5.3
requests
urllib3>=1.25.3
caraml-auth-google==0.0.0.post7
caraml-auth-google==0.0.0.post16.dev0
PyPrind>=2.11.2
numpy>=1.26.0
196 changes: 101 additions & 95 deletions sdk/tests/batch/job_test.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
import json
import os
from unittest.mock import MagicMock, patch

import pytest
from tests import utc_date
import tests
import turing
import turing.batch
import turing.batch.config
from urllib3_mock import Responses

responses = Responses("requests.packages.urllib3")
data_dir = os.path.join(os.path.dirname(__file__), "../testdata/api_responses")

with open(os.path.join(data_dir, "list_jobs_0000.json")) as f:
Expand All @@ -18,13 +20,6 @@
with open(os.path.join(data_dir, "get_job_0000.json")) as f:
get_job_0000 = f.read()


@pytest.fixture(scope="module", name="responses")
def _responses():
return responses


@responses.activate
@pytest.mark.parametrize(
"api_response, expected",
[
Expand Down Expand Up @@ -62,36 +57,37 @@ def _responses():
],
)
def test_list_jobs(
turing_api, active_project, api_response, expected, use_google_oauth
turing_api, project, api_response, expected, use_google_oauth, active_project_magic_mock
):
turing.set_url(turing_api, use_google_oauth)
turing.set_project(active_project.name)

responses.add(
method="GET",
url=f"/v1/projects/{active_project.id}/jobs?"
with patch("urllib3.PoolManager.request") as mock_request:
turing.set_url(turing_api, use_google_oauth)

mock_request.return_value = active_project_magic_mock
turing.set_project(project.name)

mock_response = MagicMock()
mock_response.method = "GET"
mock_response.status = 200
mock_response.path = f"/v1/projects/{project.id}/jobs?"
f"status={turing.batch.EnsemblingJobStatus.PENDING.value}&"
f"status={turing.batch.EnsemblingJobStatus.RUNNING.value}",
body=api_response,
match_querystring=True,
status=200,
content_type="application/json",
)

actual = turing.batch.EnsemblingJob.list(
status=[
turing.batch.EnsemblingJobStatus.PENDING,
turing.batch.EnsemblingJobStatus.RUNNING,
]
)

assert len(actual) == len(expected)
f"status={turing.batch.EnsemblingJobStatus.RUNNING.value}"
mock_response.data = api_response.encode('utf-8')
mock_response.getheader.return_value = 'application/json'

mock_request.return_value = mock_response

for actual, expected in zip(actual, expected):
assert actual == expected
actual = turing.batch.EnsemblingJob.list(
status=[
turing.batch.EnsemblingJobStatus.PENDING,
turing.batch.EnsemblingJobStatus.RUNNING,
]
)

assert len(actual) == len(expected)

@responses.activate
for actual, expected in zip(actual, expected):
assert actual == expected

@pytest.mark.parametrize(
"api_response, expected",
[
Expand All @@ -112,31 +108,34 @@ def test_list_jobs(
)
def test_submit_job(
turing_api,
active_project,
project,
ensembling_job_config,
api_response,
expected,
use_google_oauth,
active_project_magic_mock
):
turing.set_url(turing_api, use_google_oauth)
turing.set_project(active_project.name)
with patch("urllib3.PoolManager.request") as mock_request:
turing.set_url(turing_api, use_google_oauth)

responses.add(
method="POST",
url=f"/v1/projects/{active_project.id}/jobs",
body=api_response,
status=201,
content_type="application/json",
)
mock_request.return_value = active_project_magic_mock
turing.set_project(project.name)

mock_response = MagicMock()
mock_response.method = "POST"
mock_response.status = 201
mock_response.path = f"/v1/projects/{project.id}/jobs"
mock_response.data = api_response.encode('utf-8')
mock_response.getheader.return_value = 'application/json'

actual = turing.batch.job.EnsemblingJob.submit(
ensembler_id=2,
config=ensembling_job_config,
)
assert actual == expected
mock_request.return_value = mock_response

actual = turing.batch.job.EnsemblingJob.submit(
ensembler_id=2,
config=ensembling_job_config,
)
assert actual == expected

@responses.activate
@pytest.mark.parametrize(
"api_response_get, expected, api_response_refresh, updated",
[
Expand Down Expand Up @@ -168,43 +167,46 @@ def test_submit_job(
)
def test_fetch_job(
turing_api,
active_project,
project,
api_response_get,
expected,
api_response_refresh,
updated,
use_google_oauth,
active_project_magic_mock
):
turing.set_url(turing_api, use_google_oauth)
turing.set_project(active_project.name)

responses.add(
method="GET",
url=f"/v1/projects/{active_project.id}/jobs/{expected.id}",
body=api_response_get,
status=200,
content_type="application/json",
)

job = turing.batch.EnsemblingJob.get_by_id(expected.id)
with patch("urllib3.PoolManager.request") as mock_request:
turing.set_url(turing_api, use_google_oauth)

assert job == expected
mock_request.return_value = active_project_magic_mock
turing.set_project(project.name)

responses.reset()
responses.add(
method="GET",
url=f"/v1/projects/{active_project.id}/jobs/{expected.id}",
body=api_response_refresh,
status=200,
content_type="application/json",
)
mock_response = MagicMock()
mock_response.method = "GET"
mock_response.status = 200
mock_response.path = f"/v1/projects/{project.id}/jobs/{expected.id}"
mock_response.data = api_response_get.encode('utf-8')
mock_response.getheader.return_value = 'application/json'

mock_request.return_value = mock_response

job.refresh()
job = turing.batch.EnsemblingJob.get_by_id(expected.id)

assert job == updated
assert job == expected

mock_response = MagicMock()
mock_response.method = "GET"
mock_response.status = 200
mock_response.path = f"/v1/projects/{project.id}/jobs/{expected.id}"
mock_response.data = api_response_refresh.encode('utf-8')
mock_response.getheader.return_value = 'application/json'

mock_request.return_value = mock_response

job.refresh()

@responses.activate
assert job == updated

@pytest.mark.parametrize(
"job, api_response_delete, api_response_get, expected",
[
Expand Down Expand Up @@ -236,34 +238,38 @@ def test_fetch_job(
)
def test_terminate_job(
turing_api,
active_project,
project,
job,
api_response_delete,
api_response_get,
expected,
use_google_oauth,
active_project_magic_mock
):
turing.set_url(turing_api, use_google_oauth)
turing.set_project(active_project.name)
with patch("urllib3.PoolManager.request") as mock_request:
turing.set_url(turing_api, use_google_oauth)

responses.add(
method="DELETE",
url=f"/v1/projects/{active_project.id}/jobs/{job.id}",
body=api_response_delete,
status=201,
content_type="application/json",
)
mock_request.return_value = active_project_magic_mock
turing.set_project(project.name)

responses.add(
method="GET",
url=f"/v1/projects/{active_project.id}/jobs/{job.id}",
body=api_response_get,
status=200,
content_type="application/json",
)
mock_response_1 = MagicMock()
mock_response_1.method = "DELETE"
mock_response_1.status = 201
mock_response_1.path = f"/v1/projects/{project.id}/jobs/{job.id}"
mock_response_1.data = api_response_delete.encode('utf-8')
mock_response_1.getheader.return_value = 'application/json'

mock_response_2 = MagicMock()
mock_response_2.method = "get"
mock_response_2.status = 200
mock_response_2.path = f"/v1/projects/{project.id}/jobs/{job.id}"
mock_response_2.data = api_response_get.encode('utf-8')
mock_response_2.getheader.return_value = 'application/json'

mock_request.side_effect = [mock_response_1, mock_response_2]

assert job != expected
assert job != expected

job.terminate()
job.terminate()

assert job == expected
assert job == expected
Loading
Loading