ciocore 5.1.1__py2.py3-none-any.whl → 10.0.0b3__py2.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.
- ciocore/VERSION +1 -1
- ciocore/__init__.py +23 -1
- ciocore/api_client.py +655 -160
- ciocore/auth/__init__.py +5 -3
- ciocore/cli.py +501 -0
- ciocore/common.py +15 -13
- ciocore/conductor_submit.py +77 -60
- ciocore/config.py +127 -13
- ciocore/data.py +162 -77
- ciocore/docsite/404.html +746 -0
- ciocore/docsite/apidoc/api_client/index.html +3605 -0
- ciocore/docsite/apidoc/apidoc/index.html +909 -0
- ciocore/docsite/apidoc/config/index.html +1652 -0
- ciocore/docsite/apidoc/data/index.html +1553 -0
- ciocore/docsite/apidoc/hardware_set/index.html +2460 -0
- ciocore/docsite/apidoc/package_environment/index.html +1507 -0
- ciocore/docsite/apidoc/package_tree/index.html +2386 -0
- ciocore/docsite/assets/_mkdocstrings.css +16 -0
- ciocore/docsite/assets/images/favicon.png +0 -0
- ciocore/docsite/assets/javascripts/bundle.471ce7a9.min.js +29 -0
- ciocore/docsite/assets/javascripts/bundle.471ce7a9.min.js.map +7 -0
- ciocore/docsite/assets/javascripts/lunr/min/lunr.ar.min.js +1 -0
- ciocore/docsite/assets/javascripts/lunr/min/lunr.da.min.js +18 -0
- ciocore/docsite/assets/javascripts/lunr/min/lunr.de.min.js +18 -0
- ciocore/docsite/assets/javascripts/lunr/min/lunr.du.min.js +18 -0
- ciocore/docsite/assets/javascripts/lunr/min/lunr.el.min.js +1 -0
- ciocore/docsite/assets/javascripts/lunr/min/lunr.es.min.js +18 -0
- ciocore/docsite/assets/javascripts/lunr/min/lunr.fi.min.js +18 -0
- ciocore/docsite/assets/javascripts/lunr/min/lunr.fr.min.js +18 -0
- ciocore/docsite/assets/javascripts/lunr/min/lunr.he.min.js +1 -0
- ciocore/docsite/assets/javascripts/lunr/min/lunr.hi.min.js +1 -0
- ciocore/docsite/assets/javascripts/lunr/min/lunr.hu.min.js +18 -0
- ciocore/docsite/assets/javascripts/lunr/min/lunr.hy.min.js +1 -0
- ciocore/docsite/assets/javascripts/lunr/min/lunr.it.min.js +18 -0
- ciocore/docsite/assets/javascripts/lunr/min/lunr.ja.min.js +1 -0
- ciocore/docsite/assets/javascripts/lunr/min/lunr.jp.min.js +1 -0
- ciocore/docsite/assets/javascripts/lunr/min/lunr.kn.min.js +1 -0
- ciocore/docsite/assets/javascripts/lunr/min/lunr.ko.min.js +1 -0
- ciocore/docsite/assets/javascripts/lunr/min/lunr.multi.min.js +1 -0
- ciocore/docsite/assets/javascripts/lunr/min/lunr.nl.min.js +18 -0
- ciocore/docsite/assets/javascripts/lunr/min/lunr.no.min.js +18 -0
- ciocore/docsite/assets/javascripts/lunr/min/lunr.pt.min.js +18 -0
- ciocore/docsite/assets/javascripts/lunr/min/lunr.ro.min.js +18 -0
- ciocore/docsite/assets/javascripts/lunr/min/lunr.ru.min.js +18 -0
- ciocore/docsite/assets/javascripts/lunr/min/lunr.sa.min.js +1 -0
- ciocore/docsite/assets/javascripts/lunr/min/lunr.stemmer.support.min.js +1 -0
- ciocore/docsite/assets/javascripts/lunr/min/lunr.sv.min.js +18 -0
- ciocore/docsite/assets/javascripts/lunr/min/lunr.ta.min.js +1 -0
- ciocore/docsite/assets/javascripts/lunr/min/lunr.te.min.js +1 -0
- ciocore/docsite/assets/javascripts/lunr/min/lunr.th.min.js +1 -0
- ciocore/docsite/assets/javascripts/lunr/min/lunr.tr.min.js +18 -0
- ciocore/docsite/assets/javascripts/lunr/min/lunr.vi.min.js +1 -0
- ciocore/docsite/assets/javascripts/lunr/min/lunr.zh.min.js +1 -0
- ciocore/docsite/assets/javascripts/lunr/tinyseg.js +206 -0
- ciocore/docsite/assets/javascripts/lunr/wordcut.js +6708 -0
- ciocore/docsite/assets/javascripts/workers/search.b8dbb3d2.min.js +42 -0
- ciocore/docsite/assets/javascripts/workers/search.b8dbb3d2.min.js.map +7 -0
- ciocore/docsite/assets/stylesheets/main.3cba04c6.min.css +1 -0
- ciocore/docsite/assets/stylesheets/main.3cba04c6.min.css.map +1 -0
- ciocore/docsite/assets/stylesheets/palette.06af60db.min.css +1 -0
- ciocore/docsite/assets/stylesheets/palette.06af60db.min.css.map +1 -0
- ciocore/docsite/cmdline/docs/index.html +871 -0
- ciocore/docsite/cmdline/downloader/index.html +934 -0
- ciocore/docsite/cmdline/packages/index.html +878 -0
- ciocore/docsite/cmdline/uploader/index.html +995 -0
- ciocore/docsite/how-to-guides/index.html +869 -0
- ciocore/docsite/index.html +895 -0
- ciocore/docsite/logo.png +0 -0
- ciocore/docsite/objects.inv +0 -0
- ciocore/docsite/search/search_index.json +1 -0
- ciocore/docsite/sitemap.xml +3 -0
- ciocore/docsite/sitemap.xml.gz +0 -0
- ciocore/docsite/stylesheets/extra.css +26 -0
- ciocore/docsite/stylesheets/tables.css +167 -0
- ciocore/downloader/base_downloader.py +644 -0
- ciocore/downloader/download_runner_base.py +47 -0
- ciocore/downloader/job_downloader.py +119 -0
- ciocore/{downloader.py → downloader/legacy_downloader.py} +12 -9
- ciocore/downloader/log.py +73 -0
- ciocore/downloader/logging_download_runner.py +87 -0
- ciocore/downloader/perpetual_downloader.py +63 -0
- ciocore/downloader/registry.py +97 -0
- ciocore/downloader/reporter.py +135 -0
- ciocore/exceptions.py +8 -2
- ciocore/file_utils.py +51 -50
- ciocore/hardware_set.py +449 -0
- ciocore/loggeria.py +89 -20
- ciocore/package_environment.py +110 -48
- ciocore/package_query.py +182 -0
- ciocore/package_tree.py +319 -258
- ciocore/retry.py +0 -0
- ciocore/uploader/_uploader.py +547 -364
- ciocore/uploader/thread_queue_job.py +176 -0
- ciocore/uploader/upload_stats/__init__.py +3 -4
- ciocore/uploader/upload_stats/stats_formats.py +10 -4
- ciocore/validator.py +34 -2
- ciocore/worker.py +174 -151
- ciocore-10.0.0b3.dist-info/METADATA +928 -0
- ciocore-10.0.0b3.dist-info/RECORD +128 -0
- {ciocore-5.1.1.dist-info → ciocore-10.0.0b3.dist-info}/WHEEL +1 -1
- ciocore-10.0.0b3.dist-info/entry_points.txt +2 -0
- tests/instance_type_fixtures.py +175 -0
- tests/package_fixtures.py +205 -0
- tests/test_api_client.py +297 -12
- tests/test_base_downloader.py +104 -0
- tests/test_cli.py +149 -0
- tests/test_common.py +1 -7
- tests/test_config.py +40 -18
- tests/test_data.py +162 -173
- tests/test_downloader.py +118 -0
- tests/test_hardware_set.py +139 -0
- tests/test_job_downloader.py +213 -0
- tests/test_package_query.py +38 -0
- tests/test_package_tree.py +91 -291
- tests/test_submit.py +44 -18
- tests/test_uploader.py +1 -4
- ciocore/__about__.py +0 -10
- ciocore/cli/conductor.py +0 -191
- ciocore/compat.py +0 -15
- ciocore-5.1.1.data/scripts/conductor +0 -19
- ciocore-5.1.1.data/scripts/conductor.bat +0 -13
- ciocore-5.1.1.dist-info/METADATA +0 -408
- ciocore-5.1.1.dist-info/RECORD +0 -47
- tests/mocks/api_client_mock.py +0 -51
- /ciocore/{cli → downloader}/__init__.py +0 -0
- {ciocore-5.1.1.dist-info → ciocore-10.0.0b3.dist-info}/top_level.txt +0 -0
tests/test_config.py
CHANGED
|
@@ -6,12 +6,20 @@ import sys
|
|
|
6
6
|
import os
|
|
7
7
|
import logging
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
9
|
+
from unittest import mock
|
|
10
|
+
|
|
11
|
+
MOCK_API_KEY = '''{
|
|
12
|
+
"type": "service_account",
|
|
13
|
+
"project_id": "mock-project",
|
|
14
|
+
"private_key_id": "mock-key-id",
|
|
15
|
+
"private_key": "-----BEGIN PRIVATE KEY-----\\nMOCKKEY\\n-----END PRIVATE KEY-----\\n",
|
|
16
|
+
"client_email": "mock@example.com",
|
|
17
|
+
"client_id": "123456789",
|
|
18
|
+
"auth_uri": "https://accounts.example.com/oauth2/auth",
|
|
19
|
+
"token_uri": "https://oauth2.example.com/token",
|
|
20
|
+
"auth_provider_x509_cert_url": "https://www.example.com/oauth2/v1/certs",
|
|
21
|
+
"client_x509_cert_url": "https://www.example.com/robot/v1/metadata/x509/mock%40example.com"
|
|
22
|
+
}'''
|
|
15
23
|
|
|
16
24
|
PY3 = sys.version_info >= (3, 0)
|
|
17
25
|
BUILTIN_OPEN = "builtins.open" if PY3 else "__builtin__.open"
|
|
@@ -29,7 +37,7 @@ class ConfigInitTest(unittest.TestCase):
|
|
|
29
37
|
It Mocks the 2 api_key functions, the multiprocessing package, and the os environment.
|
|
30
38
|
"""
|
|
31
39
|
self.cpu_count_patcher = mock.patch(
|
|
32
|
-
"
|
|
40
|
+
"os.cpu_count", return_value=overrides.get("cpu_count", 4)
|
|
33
41
|
)
|
|
34
42
|
self.mock_cpu_count = self.cpu_count_patcher.start()
|
|
35
43
|
if overrides.get("cpu_count_raise", False):
|
|
@@ -64,23 +72,37 @@ class ConfigInitTest(unittest.TestCase):
|
|
|
64
72
|
cfg = config.Config()
|
|
65
73
|
self.assertEqual(cfg.__class__.__name__, "Config")
|
|
66
74
|
|
|
75
|
+
# DOWNLOADER PAGE SIZE
|
|
76
|
+
def test_downloader_page_size_default_to_50(self):
|
|
77
|
+
self.up()
|
|
78
|
+
cfg = config.Config()
|
|
79
|
+
self.assertEqual(cfg.config["downloader_page_size"], 50)
|
|
80
|
+
self.down()
|
|
81
|
+
|
|
82
|
+
def test_downloader_page_size_variable_overrides_default(self):
|
|
83
|
+
self.up(env={"CONDUCTOR_DOWNLOADER_PAGE_SIZE": "10"})
|
|
84
|
+
cfg = config.Config()
|
|
85
|
+
self.assertEqual(cfg.config["downloader_page_size"], 10)
|
|
86
|
+
self.down()
|
|
87
|
+
|
|
88
|
+
|
|
67
89
|
# THREAD COUNT
|
|
68
|
-
def
|
|
69
|
-
self.up(cpu_count=
|
|
90
|
+
def test_thread_count_is_cpu_count_minus_one(self):
|
|
91
|
+
self.up(cpu_count=4)
|
|
70
92
|
cfg = config.Config()
|
|
71
|
-
self.assertEqual(cfg.config["thread_count"],
|
|
93
|
+
self.assertEqual(cfg.config["thread_count"], 3)
|
|
72
94
|
self.down()
|
|
73
95
|
|
|
74
|
-
def
|
|
75
|
-
self.up(cpu_count=
|
|
96
|
+
def test_thread_count_is_max_15(self):
|
|
97
|
+
self.up(cpu_count=20)
|
|
76
98
|
cfg = config.Config()
|
|
77
|
-
self.assertEqual(cfg.config["thread_count"],
|
|
99
|
+
self.assertEqual(cfg.config["thread_count"], 15)
|
|
78
100
|
self.down()
|
|
79
101
|
|
|
80
|
-
def
|
|
102
|
+
def test_thread_count_is_15_if_an_exception_is_thrown(self):
|
|
81
103
|
self.up(cpu_count_raise=True)
|
|
82
104
|
cfg = config.Config()
|
|
83
|
-
self.assertEqual(cfg.config["thread_count"],
|
|
105
|
+
self.assertEqual(cfg.config["thread_count"], 15)
|
|
84
106
|
self.down()
|
|
85
107
|
|
|
86
108
|
def test_thread_count_variable_overrides_default(self):
|
|
@@ -253,7 +275,7 @@ class ApiKeyFromVariableTest(unittest.TestCase):
|
|
|
253
275
|
self.down()
|
|
254
276
|
|
|
255
277
|
def test_returns_dict_if_key_is_b64encoded(self):
|
|
256
|
-
key =
|
|
278
|
+
key = MOCK_API_KEY
|
|
257
279
|
if sys.version_info < (3, 0):
|
|
258
280
|
self.up(env={"CONDUCTOR_API_KEY": base64.b64encode(key)})
|
|
259
281
|
else:
|
|
@@ -288,7 +310,7 @@ class ApiKeyFromFileTest(unittest.TestCase):
|
|
|
288
310
|
self.assertEqual(result, None)
|
|
289
311
|
self.down()
|
|
290
312
|
|
|
291
|
-
@mock.patch(BUILTIN_OPEN, new_callable=mock.mock_open, read_data=
|
|
313
|
+
@mock.patch(BUILTIN_OPEN, new_callable=mock.mock_open, read_data=MOCK_API_KEY, create=True)
|
|
292
314
|
def test_open_called_with_api_key_path(self, mock_file_open):
|
|
293
315
|
self.up(env={"CONDUCTOR_API_KEY_PATH": "path/to/api_key"})
|
|
294
316
|
config.Config.get_api_key_from_file()
|
|
@@ -302,7 +324,7 @@ class ApiKeyFromFileTest(unittest.TestCase):
|
|
|
302
324
|
config.Config.get_api_key_from_file()
|
|
303
325
|
self.down()
|
|
304
326
|
|
|
305
|
-
@mock.patch(BUILTIN_OPEN, new_callable=mock.mock_open, read_data=
|
|
327
|
+
@mock.patch(BUILTIN_OPEN, new_callable=mock.mock_open, read_data=MOCK_API_KEY, create=True)
|
|
306
328
|
def test_returns_dict_if_key_is_valid_json(self, mock_file_open):
|
|
307
329
|
self.up(env={"CONDUCTOR_API_KEY_PATH": "path/to/api_key"})
|
|
308
330
|
result = config.Config.get_api_key_from_file()
|
tests/test_data.py
CHANGED
|
@@ -1,184 +1,173 @@
|
|
|
1
|
-
""" test data
|
|
2
|
-
|
|
3
|
-
isort:skip_file
|
|
4
|
-
"""
|
|
5
|
-
import json
|
|
6
|
-
import os
|
|
7
|
-
import sys
|
|
8
1
|
import unittest
|
|
9
|
-
from mock import patch
|
|
10
|
-
from ciocore import data
|
|
11
|
-
|
|
12
|
-
PROJECTS = [
|
|
13
|
-
"Deadpool",
|
|
14
|
-
"Harry Potter & the chamber of secrets",
|
|
15
|
-
"Captain Corelli's Mandolin",
|
|
16
|
-
"Gone with the Wind"
|
|
17
|
-
]
|
|
18
|
-
|
|
19
|
-
LIN_INSTANCE_TYPES = [
|
|
20
|
-
{
|
|
21
|
-
"cores": 8,
|
|
22
|
-
"memory": 30.0,
|
|
23
|
-
"description": "8 core, 30GB Mem",
|
|
24
|
-
"name": "n1-standard-8",
|
|
25
|
-
"operating_system": "linux"
|
|
26
|
-
},
|
|
27
|
-
{
|
|
28
|
-
"cores": 64,
|
|
29
|
-
"memory": 416.0,
|
|
30
|
-
"description": "64 core, 416GB Mem",
|
|
31
|
-
"name": "n1-highmem-64",
|
|
32
|
-
"operating_system": "linux"
|
|
33
|
-
},
|
|
34
|
-
{
|
|
35
|
-
"cores": 4,
|
|
36
|
-
"memory": 26.0,
|
|
37
|
-
"description": "4 core, 26GB Mem",
|
|
38
|
-
"name": "n1-highmem-4",
|
|
39
|
-
"operating_system": "linux"
|
|
40
|
-
},
|
|
41
|
-
{
|
|
42
|
-
"cores": 32,
|
|
43
|
-
"memory": 208.0,
|
|
44
|
-
"description": "32 core, 208GB Mem",
|
|
45
|
-
"name": "n1-highmem-32",
|
|
46
|
-
"operating_system": "linux"
|
|
47
|
-
}
|
|
48
|
-
]
|
|
49
|
-
|
|
50
|
-
WIN_INSTANCE_TYPES = [
|
|
51
|
-
{
|
|
52
|
-
"cores": 64,
|
|
53
|
-
"memory": 416.0,
|
|
54
|
-
"description": "64 core, 416GB Mem",
|
|
55
|
-
"name": "n1-highmem-64-w",
|
|
56
|
-
"operating_system": "windows"
|
|
57
|
-
},
|
|
58
|
-
{
|
|
59
|
-
"cores": 4,
|
|
60
|
-
"memory": 26.0,
|
|
61
|
-
"description": "4 core, 26GB Mem",
|
|
62
|
-
"name": "n1-highmem-4-w",
|
|
63
|
-
"operating_system": "windows"
|
|
64
|
-
},
|
|
65
|
-
{
|
|
66
|
-
"cores": 32,
|
|
67
|
-
"memory": 208.0,
|
|
68
|
-
"description": "32 core, 208GB Mem",
|
|
69
|
-
"name": "n1-highmem-32-w",
|
|
70
|
-
"operating_system": "windows"
|
|
71
|
-
}
|
|
72
|
-
]
|
|
73
|
-
|
|
74
|
-
ALL_INSTANCE_TYPES = WIN_INSTANCE_TYPES+LIN_INSTANCE_TYPES
|
|
75
|
-
|
|
76
|
-
with open(os.path.join(os.path.dirname(__file__), "fixtures", "sw_packages.json"), 'r') as content:
|
|
77
|
-
SOFTWARE = json.load(content)["data"]
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
class TestDataSingleton(unittest.TestCase):
|
|
81
|
-
|
|
82
|
-
def setUp(self):
|
|
83
|
-
projects_patcher = patch('ciocore.api_client.request_projects', return_value=PROJECTS)
|
|
84
|
-
software_packages_patcher = patch('ciocore.api_client.request_software_packages', return_value=SOFTWARE)
|
|
85
|
-
|
|
86
|
-
self.mock_projects = projects_patcher.start()
|
|
87
|
-
self.mock_software_packages = software_packages_patcher.start()
|
|
88
|
-
|
|
89
|
-
self.addCleanup(projects_patcher.stop)
|
|
90
|
-
self.addCleanup(software_packages_patcher.stop)
|
|
91
|
-
data.__data__ = {}
|
|
92
|
-
data.__product__ = None
|
|
93
|
-
|
|
94
|
-
class TestDataAllInstanceTypes(TestDataSingleton):
|
|
2
|
+
from unittest.mock import patch
|
|
3
|
+
from ciocore import data as coredata
|
|
95
4
|
|
|
5
|
+
class TestCoreData(unittest.TestCase):
|
|
96
6
|
def setUp(self):
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
7
|
+
"""Set up test fixtures before each test method."""
|
|
8
|
+
self.mock_api_responses = {
|
|
9
|
+
'projects': ['ProjectA', 'ProjectB'],
|
|
10
|
+
'instance_types': [
|
|
11
|
+
{'operating_system': 'windows', 'name': 'win_machine', 'cores': 1, 'memory': '1024'},
|
|
12
|
+
{'operating_system': 'linux', 'name': 'linux_machine', 'cores': 1, 'memory': '1024'}
|
|
13
|
+
],
|
|
14
|
+
'software': [ # Added package_id to each package
|
|
15
|
+
{
|
|
16
|
+
'name': 'maya-io',
|
|
17
|
+
'version': '2022',
|
|
18
|
+
'platform': 'windows',
|
|
19
|
+
'product': 'maya-io',
|
|
20
|
+
'package_id': '123456789'
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
'name': 'maya-io',
|
|
24
|
+
'version': '2022',
|
|
25
|
+
'platform': 'linux',
|
|
26
|
+
'product': 'maya-io',
|
|
27
|
+
'package_id': '123456780'
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
'name': 'nuke',
|
|
31
|
+
'version': '13.0',
|
|
32
|
+
'platform': 'linux',
|
|
33
|
+
'product': 'nuke',
|
|
34
|
+
'package_id': '123456781'
|
|
35
|
+
}
|
|
36
|
+
],
|
|
37
|
+
'extra_env': ['PATH=/custom/path', 'LICENSE_SERVER=1234']
|
|
38
|
+
}
|
|
39
|
+
# Clear data before each test
|
|
40
|
+
coredata.clear()
|
|
41
|
+
|
|
42
|
+
def tearDown(self):
|
|
43
|
+
"""Clean up after each test method."""
|
|
44
|
+
coredata.clear()
|
|
45
|
+
|
|
46
|
+
def test_init_with_single_product(self):
|
|
47
|
+
"""Test initializing with a single product"""
|
|
48
|
+
coredata.init("maya-io")
|
|
49
|
+
self.assertEqual(coredata.products(), ["maya-io"])
|
|
50
|
+
self.assertEqual(coredata.platforms(), set(["windows", "linux"]))
|
|
51
|
+
|
|
52
|
+
def test_init_with_multiple_products(self):
|
|
53
|
+
"""Test initializing with multiple products"""
|
|
54
|
+
coredata.init("maya-io", "nuke")
|
|
55
|
+
self.assertEqual(coredata.products(), ["maya-io", "nuke"])
|
|
56
|
+
|
|
57
|
+
def test_init_with_deprecated_product(self):
|
|
58
|
+
"""Test initializing with deprecated product kwarg"""
|
|
59
|
+
coredata.init(product="maya-io")
|
|
60
|
+
self.assertEqual(coredata.products(), ["maya-io"])
|
|
61
|
+
|
|
62
|
+
def test_init_with_all_products(self):
|
|
63
|
+
"""Test initializing with 'all' products"""
|
|
64
|
+
coredata.init(product="all")
|
|
65
|
+
self.assertEqual(coredata.products(), [])
|
|
66
|
+
|
|
67
|
+
def test_init_conflict_raises_error(self):
|
|
68
|
+
"""Test that using both products arg and product kwarg raises error"""
|
|
109
69
|
with self.assertRaises(ValueError):
|
|
110
|
-
|
|
70
|
+
coredata.init("maya-io", product="nuke")
|
|
111
71
|
|
|
112
|
-
def
|
|
72
|
+
def test_data_requires_init(self):
|
|
73
|
+
"""Test that data() raises error if not initialized"""
|
|
113
74
|
with self.assertRaises(ValueError):
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
data
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
data
|
|
127
|
-
self.assertEqual(data.valid(), False)
|
|
128
|
-
|
|
129
|
-
def test_does_not_refresh_if_not_force(self):
|
|
130
|
-
data.init("all")
|
|
131
|
-
p1 = data.data()["projects"]
|
|
132
|
-
self.assertEqual(len(p1), 4)
|
|
133
|
-
self.mock_projects.return_value = ["a", "b"]
|
|
134
|
-
p2 = data.data()["projects"]
|
|
135
|
-
self.assertEqual(p2, p1)
|
|
136
|
-
|
|
137
|
-
def test_does_refresh_if_force_all(self):
|
|
138
|
-
data.init("all")
|
|
139
|
-
p1 = data.data()["projects"]
|
|
140
|
-
self.assertEqual(len(p1), 4)
|
|
141
|
-
self.mock_projects.return_value = ["a", "b"]
|
|
142
|
-
p2 = data.data(force=True)["projects"]
|
|
143
|
-
self.assertNotEqual(p2, p1)
|
|
144
|
-
self.assertEqual(len(p2), 2)
|
|
75
|
+
coredata.data()
|
|
76
|
+
|
|
77
|
+
@patch('ciocore.data.api_client')
|
|
78
|
+
def test_data_fetching(self, mock_client):
|
|
79
|
+
"""Test fetching data through the data() function"""
|
|
80
|
+
# Setup mock responses
|
|
81
|
+
mock_client.request_projects.return_value = self.mock_api_responses['projects']
|
|
82
|
+
mock_client.request_instance_types.return_value = self.mock_api_responses['instance_types']
|
|
83
|
+
mock_client.request_software_packages.return_value = self.mock_api_responses['software']
|
|
84
|
+
mock_client.request_extra_environment.return_value = self.mock_api_responses['extra_env']
|
|
85
|
+
|
|
86
|
+
coredata.init("maya-io")
|
|
87
|
+
result = coredata.data()
|
|
145
88
|
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
89
|
+
self.assertIn("projects", result)
|
|
90
|
+
self.assertIn("instance_types", result)
|
|
91
|
+
self.assertIn("software", result)
|
|
92
|
+
self.assertIn("extra_environment", result)
|
|
93
|
+
|
|
94
|
+
self.assertEqual(result["projects"], self.mock_api_responses["projects"])
|
|
95
|
+
self.assertIsInstance(result["instance_types"], coredata.HardwareSet)
|
|
96
|
+
self.assertIsInstance(result["software"], coredata.PackageTree)
|
|
97
|
+
|
|
98
|
+
@patch('ciocore.data.api_client')
|
|
99
|
+
def test_force_refresh(self, mock_client):
|
|
100
|
+
"""Test forcing data refresh"""
|
|
101
|
+
# Setup mock responses
|
|
102
|
+
mock_client.request_projects.return_value = self.mock_api_responses['projects']
|
|
103
|
+
mock_client.request_instance_types.return_value = self.mock_api_responses['instance_types']
|
|
104
|
+
mock_client.request_software_packages.return_value = self.mock_api_responses['software']
|
|
105
|
+
mock_client.request_extra_environment.return_value = self.mock_api_responses['extra_env']
|
|
106
|
+
|
|
107
|
+
coredata.init("maya-io")
|
|
108
|
+
first_data = coredata.data()
|
|
109
|
+
|
|
110
|
+
# Second call without force should not make new API calls
|
|
111
|
+
mock_client.request_projects.reset_mock()
|
|
112
|
+
second_data = coredata.data()
|
|
113
|
+
mock_client.request_projects.assert_not_called()
|
|
114
|
+
|
|
115
|
+
# Call with force=True should make new API calls
|
|
116
|
+
third_data = coredata.data(force=True)
|
|
117
|
+
mock_client.request_projects.assert_called_once()
|
|
160
118
|
|
|
161
|
-
def
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
data.
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
119
|
+
def test_valid_state(self):
|
|
120
|
+
"""Test valid() function behavior"""
|
|
121
|
+
self.assertFalse(coredata.valid()) # Should be invalid before initialization
|
|
122
|
+
|
|
123
|
+
coredata.init("maya-io")
|
|
124
|
+
self.assertFalse(coredata.valid()) # Should be invalid before data is fetched
|
|
125
|
+
|
|
126
|
+
with patch('ciocore.data.api_client') as mock_client:
|
|
127
|
+
# Setup mock responses
|
|
128
|
+
mock_client.request_projects.return_value = self.mock_api_responses['projects']
|
|
129
|
+
mock_client.request_instance_types.return_value = self.mock_api_responses['instance_types']
|
|
130
|
+
mock_client.request_software_packages.return_value = self.mock_api_responses['software']
|
|
131
|
+
mock_client.request_extra_environment.return_value = self.mock_api_responses['extra_env']
|
|
132
|
+
|
|
133
|
+
coredata.data() # Fetch data
|
|
134
|
+
self.assertTrue(coredata.valid()) # Should be valid after data is fetched
|
|
135
|
+
|
|
136
|
+
coredata.clear()
|
|
137
|
+
self.assertFalse(coredata.valid()) # Should be invalid after clear
|
|
138
|
+
|
|
139
|
+
@patch('ciocore.data.api_client')
|
|
140
|
+
def test_platforms_filtering(self, mock_client):
|
|
141
|
+
"""Test platform filtering based on instance types and software"""
|
|
142
|
+
# Setup mock responses
|
|
143
|
+
mock_client.request_projects.return_value = self.mock_api_responses['projects']
|
|
144
|
+
mock_client.request_instance_types.return_value = self.mock_api_responses['instance_types']
|
|
145
|
+
mock_client.request_software_packages.return_value = self.mock_api_responses['software']
|
|
146
|
+
mock_client.request_extra_environment.return_value = self.mock_api_responses['extra_env']
|
|
147
|
+
|
|
148
|
+
coredata.init("nuke") # nuke only supports linux
|
|
149
|
+
coredata.data()
|
|
150
|
+
|
|
151
|
+
# Should only have linux instance types since nuke only supports linux
|
|
152
|
+
self.assertEqual(coredata.platforms(), {"linux"})
|
|
153
|
+
|
|
154
|
+
@patch('ciocore.data.api_client')
|
|
155
|
+
def test_instances_filter(self, mock_client):
|
|
156
|
+
"""Testing that instance_types is passed correctly to api_client"""
|
|
157
|
+
# Set up mock response
|
|
158
|
+
mock_client.request_instance_types.return_value = self.mock_api_responses['instance_types']
|
|
159
|
+
coredata.init("maya-io")
|
|
160
|
+
|
|
161
|
+
# Test with no filter
|
|
162
|
+
coredata.data()
|
|
163
|
+
mock_client.request_instance_types.assert_called_with(filter_param="")
|
|
182
164
|
|
|
165
|
+
# Test with filter and force=True
|
|
166
|
+
mock_client.request_instance_types.reset_mock()
|
|
183
167
|
|
|
168
|
+
instances_filter = "gpu.gpu_count=gte:1:int"
|
|
169
|
+
coredata.data(force=True, instances_filter=instances_filter)
|
|
170
|
+
mock_client.request_instance_types.assert_called_with(filter_param=instances_filter)
|
|
184
171
|
|
|
172
|
+
if __name__ == '__main__':
|
|
173
|
+
unittest.main()
|
tests/test_downloader.py
ADDED
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
|
|
2
|
+
from ciocore.downloader.job_downloader import JobDownloader, flatten
|
|
3
|
+
import unittest
|
|
4
|
+
from unittest import mock
|
|
5
|
+
from ciocore.downloader.base_downloader import temp_file
|
|
6
|
+
import tempfile
|
|
7
|
+
import shutil
|
|
8
|
+
import os
|
|
9
|
+
import stat
|
|
10
|
+
import contextlib
|
|
11
|
+
|
|
12
|
+
class TempFileTests(unittest.TestCase):
|
|
13
|
+
|
|
14
|
+
def test_temp_file_creation(self):
|
|
15
|
+
# Create a temporary file and test if it exists
|
|
16
|
+
with temp_file("tests/files/file.txt") as temp_path:
|
|
17
|
+
self.assertTrue(os.path.exists(temp_path))
|
|
18
|
+
self.assertTrue(os.access(temp_path, os.W_OK & os.R_OK))
|
|
19
|
+
|
|
20
|
+
def test_temp_file_overwrite_existing(self):
|
|
21
|
+
# Test if existing file is overwritten by the temporary file
|
|
22
|
+
# Create a dummy file at the specified path
|
|
23
|
+
os.makedirs("tests/files", exist_ok=True)
|
|
24
|
+
with open("tests/files/file.txt", "w") as f:
|
|
25
|
+
f.write("dummy content")
|
|
26
|
+
|
|
27
|
+
# Replace the file with a temporary file and check the content
|
|
28
|
+
with temp_file("tests/files/file.txt") as temp_path:
|
|
29
|
+
with open(temp_path, "r") as tf:
|
|
30
|
+
content = tf.read()
|
|
31
|
+
self.assertEqual(content, "")
|
|
32
|
+
|
|
33
|
+
def test_temp_file_cleanup(self):
|
|
34
|
+
with temp_file("tests/files/file.txt") as temp_path:
|
|
35
|
+
pass # Do nothing
|
|
36
|
+
self.assertFalse(os.path.exists(temp_path))
|
|
37
|
+
|
|
38
|
+
class TestJobDownloaderFlatten(unittest.TestCase):
|
|
39
|
+
|
|
40
|
+
def test_pad_job_id(self):
|
|
41
|
+
input = ("1234",)
|
|
42
|
+
result = flatten(input)
|
|
43
|
+
self.assertEqual(result, [{"job_id": "01234", "task_ids":None}])
|
|
44
|
+
|
|
45
|
+
def test_several_job_ids(self):
|
|
46
|
+
input = ("1234","1235","1236")
|
|
47
|
+
result = flatten(input)
|
|
48
|
+
self.assertEqual(result, [
|
|
49
|
+
{"job_id": "01234", "task_ids":None},
|
|
50
|
+
{"job_id": "01235", "task_ids":None},
|
|
51
|
+
{"job_id": "01236", "task_ids":None}
|
|
52
|
+
])
|
|
53
|
+
|
|
54
|
+
def test_job_and_tasks(self):
|
|
55
|
+
input = ("1234:1-7x2,10",)
|
|
56
|
+
result = flatten(input)
|
|
57
|
+
self.assertEqual(result, [{"job_id": "01234", "task_ids":["001","003","005","007","010"]}])
|
|
58
|
+
|
|
59
|
+
def test_several_job_and_tasks(self):
|
|
60
|
+
input = ("1234:1-7x2,10","1235:12-15")
|
|
61
|
+
result = flatten(input)
|
|
62
|
+
self.assertEqual(result, [
|
|
63
|
+
{"job_id": "01234", "task_ids":["001","003","005","007","010"]},
|
|
64
|
+
{"job_id": "01235", "task_ids":["012","013","014","015"]}
|
|
65
|
+
])
|
|
66
|
+
|
|
67
|
+
def test_mix_job_and_job_with_tasks(self):
|
|
68
|
+
input = ("1234","1235:12-15")
|
|
69
|
+
result = flatten(input)
|
|
70
|
+
self.assertEqual(result, [
|
|
71
|
+
{"job_id": "01234", "task_ids":None},
|
|
72
|
+
{"job_id": "01235", "task_ids":["012","013","014","015"]}
|
|
73
|
+
])
|
|
74
|
+
|
|
75
|
+
def test_invalid_range_downloads_whole_job(self):
|
|
76
|
+
# Someone might have a bunch of stuff queued up and made a mistake and left for the night.
|
|
77
|
+
# We should download the whole job in this case so they don't have to restart the dl in the
|
|
78
|
+
# morning.
|
|
79
|
+
input = ("1234:badrange",)
|
|
80
|
+
result = flatten(input)
|
|
81
|
+
self.assertEqual(result, [
|
|
82
|
+
{"job_id": "01234", "task_ids":None}
|
|
83
|
+
])
|
|
84
|
+
|
|
85
|
+
class TestJobDownloaderGetSomeTasks(unittest.TestCase):
|
|
86
|
+
def setUp(self):
|
|
87
|
+
# Create a mock instance of JobDownloader
|
|
88
|
+
self.obj = JobDownloader(jobs=["00000"])
|
|
89
|
+
|
|
90
|
+
@mock.patch("ciocore.downloader.job_downloader.logger.error")
|
|
91
|
+
def test_get_some_tasks_error(self, mock_error):
|
|
92
|
+
# Mock the api client to return an error
|
|
93
|
+
mock_api_client = mock.Mock()
|
|
94
|
+
response = '{"error": "some error message"}'
|
|
95
|
+
code = 404
|
|
96
|
+
mock_api_client.make_request.return_value = (response, code)
|
|
97
|
+
self.obj.client = mock_api_client
|
|
98
|
+
# call get_some_tasks
|
|
99
|
+
tasks, locator = self.obj.get_some_tasks({})
|
|
100
|
+
|
|
101
|
+
self.assertEqual(tasks, [])
|
|
102
|
+
self.assertIsNone(locator)
|
|
103
|
+
mock_error.assert_called_with(
|
|
104
|
+
'Error fetching download info for job ID: %s : %s : %s', '00000', '/jobs/00000/downloads', mock.ANY)
|
|
105
|
+
|
|
106
|
+
def test_get_some_tasks_success(self):
|
|
107
|
+
mock_api_client = mock.Mock()
|
|
108
|
+
# Mock the api client to return a batch of tasks and a next cursor
|
|
109
|
+
response = '{"downloads": [{"id": 1, "name": "task1"}, {"id": 2, "name": "task2"}], "next_cursor": "1234"}'
|
|
110
|
+
code = 201
|
|
111
|
+
mock_api_client.make_request.return_value = (response, code)
|
|
112
|
+
self.obj.client = mock_api_client
|
|
113
|
+
self.obj.jobs = [{"job_id": "00000", "task_ids": [1, 2]}]
|
|
114
|
+
# call get_some_tasks with a locator
|
|
115
|
+
tasks, locator = self.obj.get_some_tasks({"job_index": 0, "cursor": None})
|
|
116
|
+
|
|
117
|
+
self.assertEqual(tasks, [{"id": 1, "name": "task1"}, {"id": 2, "name": "task2"}])
|
|
118
|
+
self.assertEqual(locator, {"job_index": 0, "cursor": "1234"})
|