proximl 0.5.17__py3-none-any.whl → 1.0.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- examples/local_storage.py +0 -2
- proximl/__init__.py +1 -1
- proximl/checkpoints.py +56 -57
- proximl/cli/__init__.py +6 -3
- proximl/cli/checkpoint.py +18 -57
- proximl/cli/dataset.py +17 -57
- proximl/cli/job/__init__.py +89 -67
- proximl/cli/job/create.py +51 -24
- proximl/cli/model.py +14 -56
- proximl/cli/volume.py +18 -57
- proximl/datasets.py +50 -55
- proximl/jobs.py +269 -69
- proximl/models.py +51 -55
- proximl/proximl.py +159 -114
- proximl/utils/__init__.py +1 -0
- proximl/{auth.py → utils/auth.py} +4 -3
- proximl/utils/transfer.py +647 -0
- proximl/volumes.py +48 -53
- {proximl-0.5.17.dist-info → proximl-1.0.1.dist-info}/METADATA +3 -3
- {proximl-0.5.17.dist-info → proximl-1.0.1.dist-info}/RECORD +52 -50
- tests/integration/test_checkpoints_integration.py +4 -3
- tests/integration/test_datasets_integration.py +5 -3
- tests/integration/test_jobs_integration.py +33 -27
- tests/integration/test_models_integration.py +7 -3
- tests/integration/test_volumes_integration.py +2 -2
- tests/unit/cli/test_cli_checkpoint_unit.py +312 -1
- tests/unit/cloudbender/test_nodes_unit.py +112 -0
- tests/unit/cloudbender/test_providers_unit.py +96 -0
- tests/unit/cloudbender/test_regions_unit.py +106 -0
- tests/unit/cloudbender/test_services_unit.py +141 -0
- tests/unit/conftest.py +23 -10
- tests/unit/projects/test_project_data_connectors_unit.py +39 -0
- tests/unit/projects/test_project_datastores_unit.py +37 -0
- tests/unit/projects/test_project_members_unit.py +46 -0
- tests/unit/projects/test_project_services_unit.py +65 -0
- tests/unit/projects/test_projects_unit.py +16 -0
- tests/unit/test_auth_unit.py +17 -2
- tests/unit/test_checkpoints_unit.py +256 -71
- tests/unit/test_datasets_unit.py +218 -68
- tests/unit/test_exceptions.py +133 -0
- tests/unit/test_gpu_types_unit.py +11 -1
- tests/unit/test_jobs_unit.py +1014 -95
- tests/unit/test_main_unit.py +20 -0
- tests/unit/test_models_unit.py +218 -70
- tests/unit/test_proximl_unit.py +627 -3
- tests/unit/test_volumes_unit.py +211 -70
- tests/unit/utils/__init__.py +1 -0
- tests/unit/utils/test_transfer_unit.py +4260 -0
- proximl/cli/connection.py +0 -61
- proximl/connections.py +0 -621
- tests/unit/test_connections_unit.py +0 -182
- {proximl-0.5.17.dist-info → proximl-1.0.1.dist-info}/LICENSE +0 -0
- {proximl-0.5.17.dist-info → proximl-1.0.1.dist-info}/WHEEL +0 -0
- {proximl-0.5.17.dist-info → proximl-1.0.1.dist-info}/entry_points.txt +0 -0
- {proximl-0.5.17.dist-info → proximl-1.0.1.dist-info}/top_level.txt +0 -0
|
@@ -165,3 +165,144 @@ class serviceTests:
|
|
|
165
165
|
)
|
|
166
166
|
assert service.id == "service-id-1"
|
|
167
167
|
assert response.id == "service-id-1"
|
|
168
|
+
|
|
169
|
+
def test_service_status_property(self, service):
|
|
170
|
+
"""Test service status property."""
|
|
171
|
+
service._status = "active"
|
|
172
|
+
assert service.status == "active"
|
|
173
|
+
|
|
174
|
+
def test_service_port_property(self, service):
|
|
175
|
+
"""Test service port property."""
|
|
176
|
+
service._port = "443"
|
|
177
|
+
assert service.port == "443"
|
|
178
|
+
|
|
179
|
+
@mark.asyncio
|
|
180
|
+
async def test_service_wait_for_already_at_status(self, service):
|
|
181
|
+
"""Test wait_for returns immediately if already at target status."""
|
|
182
|
+
service._status = "active"
|
|
183
|
+
result = await service.wait_for("active")
|
|
184
|
+
assert result is None
|
|
185
|
+
|
|
186
|
+
@mark.asyncio
|
|
187
|
+
async def test_service_wait_for_invalid_status(self, service):
|
|
188
|
+
"""Test wait_for raises error for invalid status."""
|
|
189
|
+
with raises(SpecificationError) as exc_info:
|
|
190
|
+
await service.wait_for("invalid_status")
|
|
191
|
+
assert "Invalid wait_for status" in str(exc_info.value.message)
|
|
192
|
+
|
|
193
|
+
@mark.asyncio
|
|
194
|
+
async def test_service_wait_for_timeout_validation(self, service):
|
|
195
|
+
"""Test wait_for validates timeout."""
|
|
196
|
+
with raises(SpecificationError) as exc_info:
|
|
197
|
+
await service.wait_for("active", timeout=25 * 60 * 60)
|
|
198
|
+
assert "timeout must be less than" in str(exc_info.value.message)
|
|
199
|
+
|
|
200
|
+
@mark.asyncio
|
|
201
|
+
async def test_service_wait_for_success(self, service, mock_proximl):
|
|
202
|
+
"""Test wait_for succeeds when status matches."""
|
|
203
|
+
service._status = "new"
|
|
204
|
+
api_response_new = dict(
|
|
205
|
+
provider_uuid="1",
|
|
206
|
+
region_uuid="a",
|
|
207
|
+
service_id="x",
|
|
208
|
+
status="new",
|
|
209
|
+
)
|
|
210
|
+
api_response_active = dict(
|
|
211
|
+
provider_uuid="1",
|
|
212
|
+
region_uuid="a",
|
|
213
|
+
service_id="x",
|
|
214
|
+
status="active",
|
|
215
|
+
)
|
|
216
|
+
mock_proximl._query = AsyncMock(
|
|
217
|
+
side_effect=[api_response_new, api_response_active]
|
|
218
|
+
)
|
|
219
|
+
with patch("proximl.cloudbender.services.asyncio.sleep", new_callable=AsyncMock):
|
|
220
|
+
result = await service.wait_for("active", timeout=10)
|
|
221
|
+
assert result == service
|
|
222
|
+
assert service.status == "active"
|
|
223
|
+
|
|
224
|
+
@mark.asyncio
|
|
225
|
+
async def test_service_wait_for_archived_404(self, service, mock_proximl):
|
|
226
|
+
"""Test wait_for handles 404 for archived status."""
|
|
227
|
+
service._status = "active"
|
|
228
|
+
api_error = ApiError(404, {"errorMessage": "Not found"})
|
|
229
|
+
mock_proximl._query = AsyncMock(side_effect=api_error)
|
|
230
|
+
with patch("proximl.cloudbender.services.asyncio.sleep", new_callable=AsyncMock):
|
|
231
|
+
await service.wait_for("archived", timeout=10)
|
|
232
|
+
|
|
233
|
+
@mark.asyncio
|
|
234
|
+
async def test_service_wait_for_timeout(self, service, mock_proximl):
|
|
235
|
+
"""Test wait_for raises timeout exception."""
|
|
236
|
+
service._status = "new"
|
|
237
|
+
api_response_new = dict(
|
|
238
|
+
provider_uuid="1",
|
|
239
|
+
region_uuid="a",
|
|
240
|
+
service_id="x",
|
|
241
|
+
status="new",
|
|
242
|
+
)
|
|
243
|
+
mock_proximl._query = AsyncMock(return_value=api_response_new)
|
|
244
|
+
with patch("proximl.cloudbender.services.asyncio.sleep", new_callable=AsyncMock):
|
|
245
|
+
with raises(ProxiMLException) as exc_info:
|
|
246
|
+
await service.wait_for("active", timeout=0.1)
|
|
247
|
+
assert "Timeout waiting for" in str(exc_info.value.message)
|
|
248
|
+
|
|
249
|
+
@mark.asyncio
|
|
250
|
+
async def test_service_wait_for_api_error_non_404(self, service, mock_proximl):
|
|
251
|
+
"""Test wait_for raises ApiError when not 404 for archived (line 181)."""
|
|
252
|
+
service._status = "active"
|
|
253
|
+
api_error = ApiError(500, {"errorMessage": "Server Error"})
|
|
254
|
+
mock_proximl._query = AsyncMock(side_effect=api_error)
|
|
255
|
+
with patch("proximl.cloudbender.services.asyncio.sleep", new_callable=AsyncMock):
|
|
256
|
+
with raises(ApiError):
|
|
257
|
+
await service.wait_for("archived", timeout=10)
|
|
258
|
+
|
|
259
|
+
@mark.asyncio
|
|
260
|
+
async def test_service_generate_certificate(self, service, mock_proximl):
|
|
261
|
+
"""Test generate_certificate method."""
|
|
262
|
+
api_response = {
|
|
263
|
+
"provider_uuid": "1",
|
|
264
|
+
"region_uuid": "a",
|
|
265
|
+
"service_id": "x",
|
|
266
|
+
"certificate": "cert-data",
|
|
267
|
+
}
|
|
268
|
+
mock_proximl._query = AsyncMock(return_value=api_response)
|
|
269
|
+
result = await service.generate_certificate()
|
|
270
|
+
mock_proximl._query.assert_called_once_with(
|
|
271
|
+
"/provider/1/region/a/service/x/certificate",
|
|
272
|
+
"POST",
|
|
273
|
+
{},
|
|
274
|
+
dict(algorithm="ed25519"),
|
|
275
|
+
)
|
|
276
|
+
assert result == service
|
|
277
|
+
|
|
278
|
+
@mark.asyncio
|
|
279
|
+
async def test_service_generate_certificate_custom_algorithm(self, service, mock_proximl):
|
|
280
|
+
"""Test generate_certificate with custom algorithm."""
|
|
281
|
+
api_response = {
|
|
282
|
+
"provider_uuid": "1",
|
|
283
|
+
"region_uuid": "a",
|
|
284
|
+
"service_id": "x",
|
|
285
|
+
"certificate": "cert-data",
|
|
286
|
+
}
|
|
287
|
+
mock_proximl._query = AsyncMock(return_value=api_response)
|
|
288
|
+
result = await service.generate_certificate(algorithm="rsa")
|
|
289
|
+
mock_proximl._query.assert_called_once_with(
|
|
290
|
+
"/provider/1/region/a/service/x/certificate",
|
|
291
|
+
"POST",
|
|
292
|
+
{},
|
|
293
|
+
dict(algorithm="rsa"),
|
|
294
|
+
)
|
|
295
|
+
|
|
296
|
+
@mark.asyncio
|
|
297
|
+
async def test_service_sign_client_certificate(self, service, mock_proximl):
|
|
298
|
+
"""Test sign_client_certificate method."""
|
|
299
|
+
api_response = {"certificate": "signed-cert-data"}
|
|
300
|
+
mock_proximl._query = AsyncMock(return_value=api_response)
|
|
301
|
+
result = await service.sign_client_certificate("csr-data")
|
|
302
|
+
mock_proximl._query.assert_called_once_with(
|
|
303
|
+
"/provider/1/region/a/service/x/certificate/sign",
|
|
304
|
+
"POST",
|
|
305
|
+
{},
|
|
306
|
+
dict(csr="csr-data"),
|
|
307
|
+
)
|
|
308
|
+
assert result == api_response
|
tests/unit/conftest.py
CHANGED
|
@@ -4,7 +4,7 @@ from pytest import fixture, mark
|
|
|
4
4
|
from unittest.mock import Mock, AsyncMock, patch, create_autospec
|
|
5
5
|
|
|
6
6
|
from proximl.proximl import ProxiML
|
|
7
|
-
from proximl.auth import Auth
|
|
7
|
+
from proximl.utils.auth import Auth
|
|
8
8
|
from proximl.datasets import Dataset, Datasets
|
|
9
9
|
from proximl.checkpoints import Checkpoint, Checkpoints
|
|
10
10
|
from proximl.volumes import Volume, Volumes
|
|
@@ -12,14 +12,16 @@ from proximl.models import Model, Models
|
|
|
12
12
|
from proximl.gpu_types import GpuType, GpuTypes
|
|
13
13
|
from proximl.environments import Environment, Environments
|
|
14
14
|
from proximl.jobs import Job, Jobs
|
|
15
|
-
from proximl.connections import Connections
|
|
16
15
|
from proximl.projects import (
|
|
17
16
|
Projects,
|
|
18
17
|
Project,
|
|
19
18
|
)
|
|
20
19
|
from proximl.projects.datastores import ProjectDatastores, ProjectDatastore
|
|
21
20
|
from proximl.projects.services import ProjectServices, ProjectService
|
|
22
|
-
from proximl.projects.data_connectors import
|
|
21
|
+
from proximl.projects.data_connectors import (
|
|
22
|
+
ProjectDataConnectors,
|
|
23
|
+
ProjectDataConnector,
|
|
24
|
+
)
|
|
23
25
|
from proximl.projects.credentials import ProjectCredentials, ProjectCredential
|
|
24
26
|
from proximl.projects.secrets import ProjectSecrets, ProjectSecret
|
|
25
27
|
|
|
@@ -1130,12 +1132,13 @@ def mock_proximl(
|
|
|
1130
1132
|
proximl.gpu_types = create_autospec(GpuTypes)
|
|
1131
1133
|
proximl.environments = create_autospec(Environments)
|
|
1132
1134
|
proximl.jobs = create_autospec(Jobs)
|
|
1133
|
-
proximl.connections = create_autospec(Connections)
|
|
1134
1135
|
proximl.projects = create_autospec(Projects)
|
|
1135
1136
|
proximl.datasets.list = AsyncMock(return_value=mock_my_datasets)
|
|
1136
1137
|
proximl.datasets.list_public = AsyncMock(return_value=mock_public_datasets)
|
|
1137
1138
|
proximl.checkpoints.list = AsyncMock(return_value=mock_my_checkpoints)
|
|
1138
|
-
proximl.checkpoints.list_public = AsyncMock(
|
|
1139
|
+
proximl.checkpoints.list_public = AsyncMock(
|
|
1140
|
+
return_value=mock_public_checkpoints
|
|
1141
|
+
)
|
|
1139
1142
|
proximl.models.list = AsyncMock(return_value=mock_models)
|
|
1140
1143
|
proximl.volumes.list = AsyncMock(return_value=mock_my_volumes)
|
|
1141
1144
|
proximl.gpu_types.list = AsyncMock(return_value=mock_gpu_types)
|
|
@@ -1143,17 +1146,25 @@ def mock_proximl(
|
|
|
1143
1146
|
proximl.jobs.list = AsyncMock(return_value=mock_jobs)
|
|
1144
1147
|
proximl.projects.list = AsyncMock(return_value=mock_projects)
|
|
1145
1148
|
proximl.projects.datastores = create_autospec(ProjectDatastores)
|
|
1146
|
-
proximl.projects.datastores.list = AsyncMock(
|
|
1149
|
+
proximl.projects.datastores.list = AsyncMock(
|
|
1150
|
+
return_value=mock_project_datastores
|
|
1151
|
+
)
|
|
1147
1152
|
proximl.projects.services = create_autospec(ProjectServices)
|
|
1148
|
-
proximl.projects.services.list = AsyncMock(
|
|
1153
|
+
proximl.projects.services.list = AsyncMock(
|
|
1154
|
+
return_value=mock_project_services
|
|
1155
|
+
)
|
|
1149
1156
|
proximl.projects.data_connectors = create_autospec(ProjectDataConnectors)
|
|
1150
1157
|
proximl.projects.data_connectors.list = AsyncMock(
|
|
1151
1158
|
return_value=mock_project_data_connectors
|
|
1152
1159
|
)
|
|
1153
1160
|
proximl.projects.credentials = create_autospec(ProjectCredentials)
|
|
1154
|
-
proximl.projects.credentials.list = AsyncMock(
|
|
1161
|
+
proximl.projects.credentials.list = AsyncMock(
|
|
1162
|
+
return_value=mock_project_credentials
|
|
1163
|
+
)
|
|
1155
1164
|
proximl.projects.secrets = create_autospec(ProjectSecrets)
|
|
1156
|
-
proximl.projects.secrets.list = AsyncMock(
|
|
1165
|
+
proximl.projects.secrets.list = AsyncMock(
|
|
1166
|
+
return_value=mock_project_secrets
|
|
1167
|
+
)
|
|
1157
1168
|
|
|
1158
1169
|
proximl.cloudbender = create_autospec(Cloudbender)
|
|
1159
1170
|
|
|
@@ -1166,7 +1177,9 @@ def mock_proximl(
|
|
|
1166
1177
|
proximl.cloudbender.devices = create_autospec(Devices)
|
|
1167
1178
|
proximl.cloudbender.devices.list = AsyncMock(return_value=mock_devices)
|
|
1168
1179
|
proximl.cloudbender.datastores = create_autospec(Datastores)
|
|
1169
|
-
proximl.cloudbender.datastores.list = AsyncMock(
|
|
1180
|
+
proximl.cloudbender.datastores.list = AsyncMock(
|
|
1181
|
+
return_value=mock_datastores
|
|
1182
|
+
)
|
|
1170
1183
|
proximl.cloudbender.services = create_autospec(Services)
|
|
1171
1184
|
proximl.cloudbender.services.list = AsyncMock(return_value=mock_services)
|
|
1172
1185
|
proximl.cloudbender.data_connectors = create_autospec(DataConnectors)
|
|
@@ -33,6 +33,25 @@ def project_data_connector(mock_proximl):
|
|
|
33
33
|
|
|
34
34
|
|
|
35
35
|
class ProjectDataConnectorsTests:
|
|
36
|
+
@mark.asyncio
|
|
37
|
+
async def test_project_data_connectors_get(
|
|
38
|
+
self, project_data_connectors, mock_proximl
|
|
39
|
+
):
|
|
40
|
+
"""Test get method (lines 11-14)."""
|
|
41
|
+
api_response = {
|
|
42
|
+
"project_uuid": "proj-id-1",
|
|
43
|
+
"region_uuid": "reg-id-1",
|
|
44
|
+
"id": "connector-id-1",
|
|
45
|
+
"type": "custom",
|
|
46
|
+
"name": "On-Prem Connection A",
|
|
47
|
+
}
|
|
48
|
+
mock_proximl._query = AsyncMock(return_value=api_response)
|
|
49
|
+
result = await project_data_connectors.get("connector-id-1", param1="value1")
|
|
50
|
+
mock_proximl._query.assert_called_once_with(
|
|
51
|
+
"/project/1/data_connectors/connector-id-1", "GET", dict(param1="value1")
|
|
52
|
+
)
|
|
53
|
+
assert result.id == "connector-id-1"
|
|
54
|
+
|
|
36
55
|
@mark.asyncio
|
|
37
56
|
async def test_project_data_connectors_refresh(
|
|
38
57
|
self, project_data_connectors, mock_proximl
|
|
@@ -100,3 +119,23 @@ class ProjectDataConnectorTests:
|
|
|
100
119
|
empty_project_data_connector = specimen.ProjectDataConnector(mock_proximl)
|
|
101
120
|
assert bool(project_data_connector)
|
|
102
121
|
assert not bool(empty_project_data_connector)
|
|
122
|
+
|
|
123
|
+
@mark.asyncio
|
|
124
|
+
async def test_project_data_connector_enable(self, project_data_connector, mock_proximl):
|
|
125
|
+
"""Test enable method (line 72)."""
|
|
126
|
+
api_response = dict()
|
|
127
|
+
mock_proximl._query = AsyncMock(return_value=api_response)
|
|
128
|
+
await project_data_connector.enable()
|
|
129
|
+
mock_proximl._query.assert_called_once_with(
|
|
130
|
+
"/project/proj-id-1/data_connectors/ds-id-1/enable", "PATCH"
|
|
131
|
+
)
|
|
132
|
+
|
|
133
|
+
@mark.asyncio
|
|
134
|
+
async def test_project_data_connector_disable(self, project_data_connector, mock_proximl):
|
|
135
|
+
"""Test disable method (line 77)."""
|
|
136
|
+
api_response = dict()
|
|
137
|
+
mock_proximl._query = AsyncMock(return_value=api_response)
|
|
138
|
+
await project_data_connector.disable()
|
|
139
|
+
mock_proximl._query.assert_called_once_with(
|
|
140
|
+
"/project/proj-id-1/data_connectors/ds-id-1/disable", "PATCH"
|
|
141
|
+
)
|
|
@@ -33,6 +33,23 @@ def project_datastore(mock_proximl):
|
|
|
33
33
|
|
|
34
34
|
|
|
35
35
|
class ProjectDatastoresTests:
|
|
36
|
+
@mark.asyncio
|
|
37
|
+
async def test_project_datastores_get(self, project_datastores, mock_proximl):
|
|
38
|
+
"""Test get method (lines 11-14)."""
|
|
39
|
+
api_response = {
|
|
40
|
+
"project_uuid": "proj-id-1",
|
|
41
|
+
"region_uuid": "reg-id-1",
|
|
42
|
+
"id": "store-id-1",
|
|
43
|
+
"type": "nfs",
|
|
44
|
+
"name": "On-prem NFS",
|
|
45
|
+
}
|
|
46
|
+
mock_proximl._query = AsyncMock(return_value=api_response)
|
|
47
|
+
result = await project_datastores.get("store-id-1", param1="value1")
|
|
48
|
+
mock_proximl._query.assert_called_once_with(
|
|
49
|
+
"/project/1/datastores/store-id-1", "GET", dict(param1="value1")
|
|
50
|
+
)
|
|
51
|
+
assert result.id == "store-id-1"
|
|
52
|
+
|
|
36
53
|
@mark.asyncio
|
|
37
54
|
async def test_project_datastores_refresh(self, project_datastores, mock_proximl):
|
|
38
55
|
api_response = dict()
|
|
@@ -94,3 +111,23 @@ class ProjectDatastoreTests:
|
|
|
94
111
|
empty_project_datastore = specimen.ProjectDatastore(mock_proximl)
|
|
95
112
|
assert bool(project_datastore)
|
|
96
113
|
assert not bool(empty_project_datastore)
|
|
114
|
+
|
|
115
|
+
@mark.asyncio
|
|
116
|
+
async def test_project_datastore_enable(self, project_datastore, mock_proximl):
|
|
117
|
+
"""Test enable method (line 67)."""
|
|
118
|
+
api_response = dict()
|
|
119
|
+
mock_proximl._query = AsyncMock(return_value=api_response)
|
|
120
|
+
await project_datastore.enable()
|
|
121
|
+
mock_proximl._query.assert_called_once_with(
|
|
122
|
+
"/project/proj-id-1/datastores/ds-id-1/enable", "PATCH"
|
|
123
|
+
)
|
|
124
|
+
|
|
125
|
+
@mark.asyncio
|
|
126
|
+
async def test_project_datastore_disable(self, project_datastore, mock_proximl):
|
|
127
|
+
"""Test disable method (line 72)."""
|
|
128
|
+
api_response = dict()
|
|
129
|
+
mock_proximl._query = AsyncMock(return_value=api_response)
|
|
130
|
+
await project_datastore.disable()
|
|
131
|
+
mock_proximl._query.assert_called_once_with(
|
|
132
|
+
"/project/proj-id-1/datastores/ds-id-1/disable", "PATCH"
|
|
133
|
+
)
|
|
@@ -37,6 +37,42 @@ def project_member(mock_proximl):
|
|
|
37
37
|
|
|
38
38
|
|
|
39
39
|
class ProjectMembersTests:
|
|
40
|
+
@mark.asyncio
|
|
41
|
+
async def test_project_members_add(self, project_members, mock_proximl):
|
|
42
|
+
"""Test add method (lines 18-31)."""
|
|
43
|
+
api_response = {
|
|
44
|
+
"project_uuid": "proj-id-1",
|
|
45
|
+
"email": "newuser@gmail.com",
|
|
46
|
+
"owner": False,
|
|
47
|
+
"job": "all",
|
|
48
|
+
"dataset": "read",
|
|
49
|
+
"model": "all",
|
|
50
|
+
"checkpoint": "read",
|
|
51
|
+
"volume": "all",
|
|
52
|
+
}
|
|
53
|
+
mock_proximl._query = AsyncMock(return_value=api_response)
|
|
54
|
+
result = await project_members.add(
|
|
55
|
+
email="newuser@gmail.com",
|
|
56
|
+
job="all",
|
|
57
|
+
dataset="read",
|
|
58
|
+
model="all",
|
|
59
|
+
checkpoint="read",
|
|
60
|
+
volume="all",
|
|
61
|
+
param1="value1",
|
|
62
|
+
)
|
|
63
|
+
expected_payload = dict(
|
|
64
|
+
email="newuser@gmail.com",
|
|
65
|
+
job="all",
|
|
66
|
+
dataset="read",
|
|
67
|
+
model="all",
|
|
68
|
+
checkpoint="read",
|
|
69
|
+
volume="all",
|
|
70
|
+
)
|
|
71
|
+
mock_proximl._query.assert_called_once_with(
|
|
72
|
+
"/project/1/access", "POST", dict(param1="value1"), expected_payload
|
|
73
|
+
)
|
|
74
|
+
assert result.email == "newuser@gmail.com"
|
|
75
|
+
|
|
40
76
|
@mark.asyncio
|
|
41
77
|
async def test_project_members_list(self, project_members, mock_proximl):
|
|
42
78
|
api_response = [
|
|
@@ -72,6 +108,16 @@ class ProjectMembersTests:
|
|
|
72
108
|
)
|
|
73
109
|
assert len(resp) == 2
|
|
74
110
|
|
|
111
|
+
@mark.asyncio
|
|
112
|
+
async def test_project_members_remove(self, project_members, mock_proximl):
|
|
113
|
+
"""Test remove method (line 35)."""
|
|
114
|
+
api_response = dict()
|
|
115
|
+
mock_proximl._query = AsyncMock(return_value=api_response)
|
|
116
|
+
await project_members.remove("user@gmail.com", param1="value1")
|
|
117
|
+
mock_proximl._query.assert_called_once_with(
|
|
118
|
+
"/project/1/access", "DELETE", dict(param1="value1", email="user@gmail.com")
|
|
119
|
+
)
|
|
120
|
+
|
|
75
121
|
|
|
76
122
|
class ProjectMemberTests:
|
|
77
123
|
def test_project_member_properties(self, project_member):
|
|
@@ -34,6 +34,24 @@ def project_service(mock_proximl):
|
|
|
34
34
|
|
|
35
35
|
|
|
36
36
|
class ProjectServicesTests:
|
|
37
|
+
@mark.asyncio
|
|
38
|
+
async def test_project_services_get(self, project_services, mock_proximl):
|
|
39
|
+
"""Test get method (lines 11-14)."""
|
|
40
|
+
api_response = {
|
|
41
|
+
"project_uuid": "proj-id-1",
|
|
42
|
+
"region_uuid": "reg-id-1",
|
|
43
|
+
"id": "res-id-1",
|
|
44
|
+
"type": "port",
|
|
45
|
+
"name": "On-Prem Service A",
|
|
46
|
+
"hostname": "service-a.local",
|
|
47
|
+
}
|
|
48
|
+
mock_proximl._query = AsyncMock(return_value=api_response)
|
|
49
|
+
result = await project_services.get("res-id-1", param1="value1")
|
|
50
|
+
mock_proximl._query.assert_called_once_with(
|
|
51
|
+
"/project/1/services/res-id-1", "GET", dict(param1="value1")
|
|
52
|
+
)
|
|
53
|
+
assert result.id == "res-id-1"
|
|
54
|
+
|
|
37
55
|
@mark.asyncio
|
|
38
56
|
async def test_project_services_refresh(self, project_services, mock_proximl):
|
|
39
57
|
api_response = dict()
|
|
@@ -100,3 +118,50 @@ class ProjectServiceTests:
|
|
|
100
118
|
empty_project_service = specimen.ProjectService(mock_proximl)
|
|
101
119
|
assert bool(project_service)
|
|
102
120
|
assert not bool(empty_project_service)
|
|
121
|
+
|
|
122
|
+
@mark.asyncio
|
|
123
|
+
async def test_project_service_enable(self, project_service, mock_proximl):
|
|
124
|
+
"""Test enable method (line 72)."""
|
|
125
|
+
api_response = dict()
|
|
126
|
+
mock_proximl._query = AsyncMock(return_value=api_response)
|
|
127
|
+
await project_service.enable()
|
|
128
|
+
mock_proximl._query.assert_called_once_with(
|
|
129
|
+
"/project/proj-id-1/services/res-id-1/enable", "PATCH"
|
|
130
|
+
)
|
|
131
|
+
|
|
132
|
+
@mark.asyncio
|
|
133
|
+
async def test_project_service_disable(self, project_service, mock_proximl):
|
|
134
|
+
"""Test disable method (line 77)."""
|
|
135
|
+
api_response = dict()
|
|
136
|
+
mock_proximl._query = AsyncMock(return_value=api_response)
|
|
137
|
+
await project_service.disable()
|
|
138
|
+
mock_proximl._query.assert_called_once_with(
|
|
139
|
+
"/project/proj-id-1/services/res-id-1/disable", "PATCH"
|
|
140
|
+
)
|
|
141
|
+
|
|
142
|
+
@mark.asyncio
|
|
143
|
+
async def test_project_service_get_service_ca_certificate(self, project_service, mock_proximl):
|
|
144
|
+
"""Test get_service_ca_certificate method (lines 82-87)."""
|
|
145
|
+
api_response = {"certificate": "ca-cert-data"}
|
|
146
|
+
mock_proximl._query = AsyncMock(return_value=api_response)
|
|
147
|
+
result = await project_service.get_service_ca_certificate(param1="value1")
|
|
148
|
+
mock_proximl._query.assert_called_once_with(
|
|
149
|
+
"/project/proj-id-1/services/res-id-1/certificate/ca",
|
|
150
|
+
"GET",
|
|
151
|
+
dict(param1="value1"),
|
|
152
|
+
)
|
|
153
|
+
assert result == api_response
|
|
154
|
+
|
|
155
|
+
@mark.asyncio
|
|
156
|
+
async def test_project_service_sign_client_certificate(self, project_service, mock_proximl):
|
|
157
|
+
"""Test sign_client_certificate method (lines 90-96)."""
|
|
158
|
+
api_response = {"certificate": "signed-cert-data"}
|
|
159
|
+
mock_proximl._query = AsyncMock(return_value=api_response)
|
|
160
|
+
result = await project_service.sign_client_certificate("csr-data", param1="value1")
|
|
161
|
+
mock_proximl._query.assert_called_once_with(
|
|
162
|
+
"/project/proj-id-1/services/res-id-1/certificate/sign",
|
|
163
|
+
"POST",
|
|
164
|
+
dict(param1="value1"),
|
|
165
|
+
dict(csr="csr-data"),
|
|
166
|
+
)
|
|
167
|
+
assert result == api_response
|
|
@@ -48,6 +48,22 @@ class ProjectsTests:
|
|
|
48
48
|
await projects.get("1234")
|
|
49
49
|
mock_proximl._query.assert_called_once_with("/project/1234", "GET", dict())
|
|
50
50
|
|
|
51
|
+
@mark.asyncio
|
|
52
|
+
async def test_get_current_project(self, projects, mock_proximl):
|
|
53
|
+
"""Test get_current method (lines 20-23)."""
|
|
54
|
+
api_response = {
|
|
55
|
+
"id": "project-id-1",
|
|
56
|
+
"name": "current project",
|
|
57
|
+
"owner": True,
|
|
58
|
+
}
|
|
59
|
+
mock_proximl.project = "project-id-1"
|
|
60
|
+
mock_proximl._query = AsyncMock(return_value=api_response)
|
|
61
|
+
result = await projects.get_current(param1="value1")
|
|
62
|
+
mock_proximl._query.assert_called_once_with(
|
|
63
|
+
"/project/project-id-1", "GET", dict(param1="value1")
|
|
64
|
+
)
|
|
65
|
+
assert result.id == "project-id-1"
|
|
66
|
+
|
|
51
67
|
@mark.asyncio
|
|
52
68
|
async def test_list_projects(
|
|
53
69
|
self,
|
tests/unit/test_auth_unit.py
CHANGED
|
@@ -6,7 +6,7 @@ from unittest.mock import AsyncMock, patch, mock_open, MagicMock
|
|
|
6
6
|
from pytest import mark, fixture, raises
|
|
7
7
|
from aiohttp import WSMessage, WSMsgType
|
|
8
8
|
|
|
9
|
-
import proximl.auth as specimen
|
|
9
|
+
import proximl.utils.auth as specimen
|
|
10
10
|
|
|
11
11
|
pytestmark = [mark.sdk, mark.unit]
|
|
12
12
|
|
|
@@ -21,7 +21,22 @@ pytestmark = [mark.sdk, mark.unit]
|
|
|
21
21
|
"PROXIML_POOL_ID": "pool_id",
|
|
22
22
|
},
|
|
23
23
|
)
|
|
24
|
-
|
|
24
|
+
@patch("proximl.utils.auth.boto3.client")
|
|
25
|
+
@patch("proximl.utils.auth.requests.get")
|
|
26
|
+
@patch("builtins.open", side_effect=FileNotFoundError)
|
|
27
|
+
def test_auth_from_envs(mock_open, mock_requests_get, mock_boto3_client):
|
|
28
|
+
# Mock the auth config request
|
|
29
|
+
mock_response = MagicMock()
|
|
30
|
+
mock_response.json.return_value = {
|
|
31
|
+
"region": "us-east-1",
|
|
32
|
+
"userPoolSDKClientId": "default_client_id",
|
|
33
|
+
"userPoolId": "default_pool_id",
|
|
34
|
+
}
|
|
35
|
+
mock_requests_get.return_value = mock_response
|
|
36
|
+
|
|
37
|
+
# Mock boto3 client
|
|
38
|
+
mock_boto3_client.return_value = MagicMock()
|
|
39
|
+
|
|
25
40
|
auth = specimen.Auth(config_dir=os.path.expanduser("~/.proximl"))
|
|
26
41
|
assert auth.__dict__.get("username") == "user-id"
|
|
27
42
|
assert auth.__dict__.get("password") == "key"
|