ciocore 9.1.0rc2__py2.py3-none-any.whl → 9.1.1b1__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.

Potentially problematic release.


This version of ciocore might be problematic. Click here for more details.

@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: ciocore
3
- Version: 9.1.0rc2
3
+ Version: 9.1.1b1
4
4
  Summary: Core functionality for Conductor's client tools
5
5
  Home-page: https://github.com/ConductorTechnologies/ciocore
6
6
  Author: conductor
@@ -34,8 +34,10 @@ conductor --help
34
34
  Get help for a specific sub command
35
35
  ```bash
36
36
  conductor downloader --help
37
- conductor uploader --help
38
- conductor software --help
37
+ conductor upload --help
38
+ conductor download --help
39
+ conductor packages --help
40
+ conductor docs --help
39
41
  ```
40
42
 
41
43
  Example API usage
@@ -46,14 +48,76 @@ print(api_client.request_software_packages()[0])
46
48
  ```
47
49
  ## Contributing
48
50
 
49
- See [CONTRIBUTING](CONTRIBUTING.md)
51
+ If you are contributing to the Conductor Core project, it's recommended to set up a virtual environment to manage dependencies and isolate your development environment. Follow these steps to get started:
52
+
53
+ ### Setting Up a Virtual Environment
54
+
55
+ 1. **Create a Virtual Environment:**
56
+
57
+ Navigate to the root directory of the project and create a virtual environment:
58
+
59
+ ```bash
60
+ python -m venv venv
61
+ ```
62
+
63
+ 2. **Activate the Virtual Environment:**
64
+
65
+ - On Windows:
66
+
67
+ ```bash
68
+ .\venv\Scripts\activate
69
+ ```
70
+
71
+ - On macOS and Linux:
72
+
73
+ ```bash
74
+ source venv/bin/activate
75
+ ```
76
+
77
+ 3. **Install the Package in Editable Mode:**
78
+
79
+ With the virtual environment activated, install the package in editable mode. This allows you to make changes to the code and test them without reinstalling the package:
80
+
81
+ ```bash
82
+ pip install -r requirements.txt # if you want to publish and so on.
83
+ pip install -e . # To develop and test the package.
84
+ ```
85
+
86
+ ### Running Conductor Commands
87
+
88
+ Once the package is installed, you can run any Conductor command. Here are a few examples:
89
+
90
+ - **Display Help for the Main Command:**
91
+
92
+ ```bash
93
+ conductor --help
94
+ ```
95
+
96
+ - **Get Help for Specific Subcommands:**
97
+
98
+ ```bash
99
+ conductor download --help
100
+ conductor upload --help
101
+ conductor packages --help
102
+ conductor docs
103
+ ```
104
+
105
+ These commands will provide you with detailed information on how to use the Conductor CLI and its subcommands.
106
+
107
+ For more detailed contribution guidelines, please refer to the [CONTRIBUTING](CONTRIBUTING.md) document.
50
108
 
51
109
  ## Changelog
52
110
 
53
- ## Version:9.1.0-rc.2 -- 12 Dec 2024
111
+ ## Version:9.1.1-beta.1 -- 20 Feb 2025
112
+
113
+ * Added contributor guidelines section to README.md
114
+ * Added validation for job IDs and output paths in download command
115
+ * Improved output path handling in BaseDownloader class
116
+
117
+ ## Version:9.1.0 -- 20 Dec 2024
54
118
 
55
- * Use the new required jwt parameters
56
- * Removing py2.7 compatibility
119
+ * Added support for new required JWT authentication parameters
120
+ * Removed Python 2.7 compatibility to focus on modern Python versions
57
121
 
58
122
  ## Version:8.3.3 -- 04 Dec 2024
59
123
 
@@ -1,7 +1,7 @@
1
- ciocore/VERSION,sha256=rK2e-XPKebjaWLwloD4dWey9U5CMdVkZh1nVx-IOI3s,10
1
+ ciocore/VERSION,sha256=FuQPkXEkRVKmKtO24mViA1RsTr3EjFrrRdgT_pg8YAg,12
2
2
  ciocore/__init__.py,sha256=aTP7LeeosQA8BZE67gDV4jgfTK5zxmwZRjiTRu_ZWj0,646
3
3
  ciocore/api_client.py,sha256=KKL7TsYygNcfkFZDPPq1CSJsrVN_QLK4PqP44vXsCQg,33101
4
- ciocore/cli.py,sha256=jZ1lOKQiUcrMhsVmD9SVmPMFwHtgDF4SaoAf2-PBS54,15449
4
+ ciocore/cli.py,sha256=Vzoc_3UBQIQfGaoG-Ef2Klh2IBMj5dJCAEiWvWH6tns,17477
5
5
  ciocore/client_db.py,sha256=tTz3bl2xeDPPcYSDS3g3QgV_xYihJMx0Kj6OeN2klK0,12978
6
6
  ciocore/common.py,sha256=mBIS6KiYoQsjWe6aIFUGRRvCMl8BIN2kmLZ4J8icap8,14982
7
7
  ciocore/conductor_submit.py,sha256=bxvzdyNzscAOOOsqTvAYh5DQsTWyCQJNb16Mf-n_F0M,9702
@@ -25,14 +25,14 @@ ciocore/docsite/index.html,sha256=NEK4HaX2yaetTajVtQuTmS9C5cPnkAtxgeKEj7wQ9t0,20
25
25
  ciocore/docsite/logo.png,sha256=gArgFFWdw8w985-0TkuGIgU_pW9sziEMZdqytXb5WLo,2825
26
26
  ciocore/docsite/objects.inv,sha256=XwmLactPEWWC4fAWqHNPBXGsluRxLLTrwDLQqq51ONY,775
27
27
  ciocore/docsite/sitemap.xml,sha256=M_V85zl0y2adRvzJAnoCxlZH_Hl7TLnIb1A-6l_xGmI,109
28
- ciocore/docsite/sitemap.xml.gz,sha256=5N9BP1xbOEPkbpLHoqTsfzUJD3N4IJB55-_WR8JLfrc,127
29
- ciocore/docsite/apidoc/api_client/index.html,sha256=kkhKzA7OThkpASLveONRPNS7gGppf8Ot2rxek1lZxHc,189722
28
+ ciocore/docsite/sitemap.xml.gz,sha256=frEWw8iRQPdzBRxKi34sMHbxRw6VmtUsfa4Rvqf6Su8,127
29
+ ciocore/docsite/apidoc/api_client/index.html,sha256=6d-3jDp3qOOWjLlOjbJY8SehOb2ZeX3iXPNEynrA3yA,190619
30
30
  ciocore/docsite/apidoc/apidoc/index.html,sha256=GOSvv6KZPOtgekgshRE4j7aDvJkkaiBQLwA_By9J94g,26171
31
- ciocore/docsite/apidoc/config/index.html,sha256=WDqy4MLR3EMp9T_2-Z9Op61rTFkvb0aTWmtjiR8sbjA,72559
32
- ciocore/docsite/apidoc/data/index.html,sha256=8ldmfisgelzdWtTqGY8uebvVRcskhbv-reO3hMcIgGI,51627
33
- ciocore/docsite/apidoc/hardware_set/index.html,sha256=SpYg-lwuCvfLPbNIIM7aQL2jGt-NA5wlVMlIKixGwBo,123042
34
- ciocore/docsite/apidoc/package_environment/index.html,sha256=V6_ah3V1_4_aOwJbEcITCdwuHxe1vGtfn0maRrbflUs,69248
35
- ciocore/docsite/apidoc/package_tree/index.html,sha256=5_4vV8x_GmpsyWHnMP62gBaAcdzfM7L-mP7uQKh_DH4,109054
31
+ ciocore/docsite/apidoc/config/index.html,sha256=NAPA-09l_r0CLYPruJZNeRzX_FTucV_oExMpK83EAcY,72835
32
+ ciocore/docsite/apidoc/data/index.html,sha256=ogsCr6IaLXojBInDdIWh74KAsq7W5AILVsHWJa-3t0A,51926
33
+ ciocore/docsite/apidoc/hardware_set/index.html,sha256=9dT-7TuEu8bknDmL51a3nuwUli1BWw1_nMfJRaqqIc4,123985
34
+ ciocore/docsite/apidoc/package_environment/index.html,sha256=mP0vktOJJxo3etQ4755etLDNiO_LrDXVVcoG0s6lIng,69570
35
+ ciocore/docsite/apidoc/package_tree/index.html,sha256=lsXi0MT4PHABR-uhPKb8Yaq4jWMve4Yxk5V3EgG0zxQ,109859
36
36
  ciocore/docsite/assets/_mkdocstrings.css,sha256=K3bqYEmxlOHQ3-M11JNbBWHCBDBLarkFRm8HuEYrAG4,341
37
37
  ciocore/docsite/assets/images/favicon.png,sha256=AjhUxD_Eslt5XuSVHIAZ494Fk__rb5GLXR8qm0elfP4,1870
38
38
  ciocore/docsite/assets/javascripts/bundle.4e31edb1.min.js,sha256=vMxCR_BtNIcbmbPV6j8Z-YDLQ9ckt4RzvGuzCTg034s,97250
@@ -79,12 +79,12 @@ ciocore/docsite/cmdline/docs/index.html,sha256=-TMLZX8gnXwRcWoaylF8zHRwWAo4BHk6g
79
79
  ciocore/docsite/cmdline/downloader/index.html,sha256=nygj-0GQmpD79B5AxHjwzQxOFv8vxHiu6LP_T2dN_kg,23171
80
80
  ciocore/docsite/cmdline/packages/index.html,sha256=_kXB85PBAgrqW09OerYpxnJuyERHMbcLn6qBGRdyHwk,20923
81
81
  ciocore/docsite/cmdline/uploader/index.html,sha256=vuQ06Gys9Eoxs87PXlqnM5AgB6Ag00BlDIy6oaprjis,25123
82
- ciocore/docsite/how-to-guides/index.html,sha256=KifCHl2S3RfPBZhP1UXwUNWuhcXPlPPqM4Gk6rVGhjQ,20100
82
+ ciocore/docsite/how-to-guides/index.html,sha256=9ZSygr8m3nRpE2JTvifeS9RHleoD3kPjCGZGCs6x-Rk,20192
83
83
  ciocore/docsite/search/search_index.json,sha256=LP1ck_wuTP9A_dWTyaVF8Fugg22q1O0XS6hGqg2zk0M,189860
84
84
  ciocore/docsite/stylesheets/extra.css,sha256=_Cxe9Dhg1BBi6Kqaz_iZD9z9VyqxA9vtONRjP4PVic0,354
85
85
  ciocore/docsite/stylesheets/tables.css,sha256=LE_zwGRxGcdPIy-9QiVPecOzlEBSqZb_WP5vDkFE0ZM,3235
86
86
  ciocore/downloader/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
87
- ciocore/downloader/base_downloader.py,sha256=9PVmP1qy-dop4MbVsmLFvf5W6LKQLUKHPDYE6AEt71g,25784
87
+ ciocore/downloader/base_downloader.py,sha256=yO2gDOeWatJGmHomns9Bvo_dafdtG-_NPWY89-iNCgc,27417
88
88
  ciocore/downloader/download_runner_base.py,sha256=cWTWKhFX5FLG84Wh8_s7xpk3n0nmQ4fDiD2rYV6a3u0,1612
89
89
  ciocore/downloader/job_downloader.py,sha256=HAhr95RyCsj3KTp5W58Znc4JmqHY-mrM5b8vo6kRIJ0,5697
90
90
  ciocore/downloader/legacy_downloader.py,sha256=LIbT3BxuY56lq_UW_io9FSgK_2VIKwFVMENyF-5KllA,51158
@@ -101,10 +101,10 @@ tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
101
101
  tests/instance_type_fixtures.py,sha256=uIzQduqKQVgjllMuyXaYnRC-pwqk5lnTx3NY2M5Nujo,4320
102
102
  tests/package_fixtures.py,sha256=od7ZHofG8ubpQ3PqlUsrcHBcbmD3qVWih9eiIg1WtSQ,5361
103
103
  tests/test_api_client.py,sha256=isntMDs3QTyTWdghBa_LbFAiC7ydZD1RWb655oEZo24,14452
104
- tests/test_base_downloader.py,sha256=SS7tWKv2ZZhpUDk4UCg1TkrNrpntjSewgzLl1mEubSE,3603
104
+ tests/test_base_downloader.py,sha256=Oj50qzt0jNPu34r1tdPRC0crLw1gYeQaDvDNsozYWn4,11804
105
105
  tests/test_cli.py,sha256=_WTs2SWlEgd6wtg1hmOBlFnbWVdFLvqp0KqNhy-y2e8,5532
106
106
  tests/test_common.py,sha256=5N9xeeGVuwN4kcjIg30eBliUD8PePzCNNLuLipGuTgk,410
107
- tests/test_config.py,sha256=iiMPwoVA9C3vvGGR6_gKKMbYkSxheNLdljeOA-iPrJU,13295
107
+ tests/test_config.py,sha256=ahDLB6yVX8pA-B1qvxEpcJ1Wf1HS499rEg7lloeyxfk,11526
108
108
  tests/test_data.py,sha256=o320GdvOJ2TouWtuA8jcs8Cr_gQWlQ6KxWVSWtmARlY,7614
109
109
  tests/test_downloader.py,sha256=hceljsjnuvk5Vk5X4mHgavIEcpbv8ylPwpz7rTwJ-aE,4721
110
110
  tests/test_hardware_set.py,sha256=hW7A_suyYdU7WkB7qoHSBPLxaAP2CKqI0i_ULfO5GeY,4408
@@ -118,8 +118,8 @@ tests/test_uploader.py,sha256=JGp6GEyqRXRtbQSb-IW-cCX_BzNotWdCbnJnLwZvpUM,2869
118
118
  tests/test_validator.py,sha256=2fY66ayNc08PGyj2vTI-V_1yeCWJDngkj2zkUM5TTCI,1526
119
119
  tests/mocks/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
120
120
  tests/mocks/glob.py,sha256=J2MH7nqi6NJOHuGdVWxhfeBd700_Ckj6cLh_8jSNkfg,215
121
- ciocore-9.1.0rc2.dist-info/METADATA,sha256=5ckG7OoC_iQTAE6keOl-Sc3GgfEihq4xc-2BvA10ZnU,18999
122
- ciocore-9.1.0rc2.dist-info/WHEEL,sha256=qUzzGenXXuJTzyjFah76kDVqDvnk-YDzY00svnrl84w,109
123
- ciocore-9.1.0rc2.dist-info/entry_points.txt,sha256=cCqcALMYbC4d8545V9w0Zysfg9MVuKWhzDQ2er4UfGE,47
124
- ciocore-9.1.0rc2.dist-info/top_level.txt,sha256=SvlM5JlqULzAz00JZWfiUhfjhqDzYzSWssA87zdJl0o,14
125
- ciocore-9.1.0rc2.dist-info/RECORD,,
121
+ ciocore-9.1.1b1.dist-info/METADATA,sha256=ffXtlEfKw2KYi6mtX-XRyCAaghtxUUDzVhTNflXV5D4,20826
122
+ ciocore-9.1.1b1.dist-info/WHEEL,sha256=qUzzGenXXuJTzyjFah76kDVqDvnk-YDzY00svnrl84w,109
123
+ ciocore-9.1.1b1.dist-info/entry_points.txt,sha256=cCqcALMYbC4d8545V9w0Zysfg9MVuKWhzDQ2er4UfGE,47
124
+ ciocore-9.1.1b1.dist-info/top_level.txt,sha256=SvlM5JlqULzAz00JZWfiUhfjhqDzYzSWssA87zdJl0o,14
125
+ ciocore-9.1.1b1.dist-info/RECORD,,
@@ -1,21 +1,20 @@
1
+ from concurrent.futures import ThreadPoolExecutor
2
+ from unittest import mock
3
+ from unittest.mock import patch, PropertyMock
4
+ import unittest
5
+
6
+ from ciocore import api_client
1
7
  from ciocore.downloader.base_downloader import (
2
8
  BaseDownloader,
3
- DEFAULT_NUM_THREADS,
4
- DEFAULT_PROGRESS_INTERVAL,
5
- DEFAULT_MAX_ATTEMPTS,
6
9
  DEFAULT_DELAY,
7
10
  DEFAULT_JITTER,
11
+ DEFAULT_MAX_ATTEMPTS,
12
+ DEFAULT_NUM_THREADS,
8
13
  DEFAULT_PAGE_SIZE,
14
+ DEFAULT_PROGRESS_INTERVAL,
15
+ ensure_writable_output_path,
16
+ ensure_path_valid
9
17
  )
10
- import unittest
11
- from unittest import mock
12
-
13
- from ciocore import api_client
14
-
15
- from unittest.mock import patch
16
-
17
- from concurrent.futures import ThreadPoolExecutor
18
-
19
18
 
20
19
  class TestBaseDownloaderInit(unittest.TestCase):
21
20
  def test_default_values(self):
@@ -37,7 +36,6 @@ class TestBaseDownloaderInit(unittest.TestCase):
37
36
  self.assertEqual(downloader.jitter, DEFAULT_JITTER)
38
37
  self.assertIsNone(downloader.regex)
39
38
 
40
-
41
39
  def test_custom_values(self):
42
40
  output_path = "/path/to/destination"
43
41
  num_threads = 4
@@ -102,3 +100,181 @@ class TestBaseDownloaderRun(unittest.TestCase):
102
100
 
103
101
  mock_get_some_tasks.assert_called_with(None)
104
102
  self.downloader.download_tasks.assert_called_with(tasks, my_mock_executor)
103
+
104
+
105
+ class TestEnsureWritableOutputPath(unittest.TestCase):
106
+
107
+ @patch("ciocore.downloader.base_downloader.ensure_path_valid", return_value=True)
108
+ def test_user_specified_path_is_returned_with_job_id_if_valid(
109
+ self, mock_ensure_path_valid
110
+ ):
111
+ file_info = {"job_id": "00123", "task_id": "task1", "relative_path": "file.txt"}
112
+ task_info = {"job_id": "00123"}
113
+ user_specified_path = "/my/new/path"
114
+
115
+ result = ensure_writable_output_path(file_info, task_info, user_specified_path)
116
+ self.assertEqual(result, "/my/new/path/00123")
117
+ mock_ensure_path_valid.assert_called_with("/my/new/path/00123")
118
+
119
+ @patch("ciocore.downloader.base_downloader.ensure_path_valid")
120
+ @patch("ciocore.downloader.base_downloader.get_fallback_path")
121
+ def test_user_specified_path_is_invalid_so_fallback_path_is_returned(
122
+ self, mock_get_fallback_path, mock_ensure_path_valid
123
+ ):
124
+ file_info = {"job_id": "00123", "task_id": "task1", "relative_path": "file.txt"}
125
+ task_info = {"job_id": "00123"}
126
+ user_specified_path = "/invalid/path"
127
+
128
+ # Mock ensure_path_valid to return False for user_specified_path and True for fallback path
129
+ mock_ensure_path_valid.side_effect = lambda path: "invalid" not in path
130
+
131
+ # Mock get_fallback_path to return a specific fallback path
132
+ fallback_path = "/valid/fallback/path/00123"
133
+ mock_get_fallback_path.return_value = fallback_path
134
+
135
+ result = ensure_writable_output_path(file_info, task_info, user_specified_path)
136
+
137
+ # Assert that the fallback path is returned
138
+ self.assertEqual(result, fallback_path)
139
+
140
+ # Ensure that ensure_path_valid was called with the correct paths
141
+ mock_ensure_path_valid.assert_any_call("/invalid/path/00123")
142
+ mock_ensure_path_valid.assert_any_call(fallback_path)
143
+
144
+ # Ensure get_fallback_path was called with the correct job_id
145
+ mock_get_fallback_path.assert_called_with(task_info["job_id"])
146
+
147
+ @patch("ciocore.downloader.base_downloader.ensure_path_valid")
148
+ @patch("ciocore.downloader.base_downloader.get_fallback_path")
149
+ def test_user_specified_path_and_fallback_path_are_invalid_so_none_is_returned(
150
+ self, mock_get_fallback_path, mock_ensure_path_valid
151
+ ):
152
+ file_info = {
153
+ "job_id": "00123",
154
+ "task_id": "task1",
155
+ "relative_path": "file.txt"
156
+ }
157
+ task_info = {"job_id": "00123"}
158
+ user_specified_path = "/invalid/user/path"
159
+
160
+ # Mock ensure_path_valid to return False for all paths
161
+ mock_ensure_path_valid.return_value = False
162
+
163
+ # Mock get_fallback_path to return a specific fallback path
164
+ mock_get_fallback_path.return_value = "/invalid/fallback/path/00123"
165
+
166
+ result = ensure_writable_output_path(file_info, task_info, user_specified_path)
167
+
168
+ # Assert that None is returned when no valid paths are found
169
+ self.assertIsNone(result)
170
+
171
+ # Ensure that ensure_path_valid was called with both paths
172
+ mock_ensure_path_valid.assert_any_call("/invalid/user/path/00123")
173
+ mock_ensure_path_valid.assert_any_call("/invalid/fallback/path/00123")
174
+
175
+ # Ensure get_fallback_path was called with the correct job_id
176
+ mock_get_fallback_path.assert_called_with(task_info["job_id"])
177
+
178
+ @patch("ciocore.downloader.base_downloader.ensure_path_valid", return_value=True)
179
+ def test_output_dir_is_returned_if_valid_and_no_user_specified_path(
180
+ self, mock_ensure_path_valid
181
+ ):
182
+ file_info = {"output_dir": "/valid/output/dir", "job_id": "00123", "task_id": "task1", "relative_path": "file.txt"}
183
+ task_info = {"job_id": "00123"}
184
+
185
+ result = ensure_writable_output_path(file_info, task_info)
186
+
187
+ # Assert that the output directory from file_info is returned
188
+ self.assertEqual(result, file_info["output_dir"])
189
+
190
+ # Ensure that ensure_path_valid was called with the correct path
191
+ mock_ensure_path_valid.assert_called_with(file_info["output_dir"])
192
+
193
+ @patch("ciocore.downloader.base_downloader.ensure_path_valid")
194
+ @patch("ciocore.downloader.base_downloader.get_fallback_path")
195
+ def test_fallback_path_is_returned_if_output_dir_is_invalid_and_no_user_specified_path(
196
+ self, mock_get_fallback_path, mock_ensure_path_valid
197
+ ):
198
+ file_info = {"output_dir": "/invalid/output/dir", "job_id": "00123", "task_id": "task1", "relative_path": "file.txt"}
199
+ task_info = {"job_id": "00123"}
200
+ user_specified_path = None # No user-specified path
201
+ fallback_path = "/valid/fallback/path/00123"
202
+
203
+ # Mock ensure_path_valid to return False for output_dir and True for fallback path
204
+ mock_ensure_path_valid.side_effect = lambda path: path == fallback_path
205
+
206
+ # Mock get_fallback_path to return a specific fallback path
207
+ mock_get_fallback_path.return_value = fallback_path
208
+
209
+ result = ensure_writable_output_path(file_info, task_info, user_specified_path)
210
+
211
+ # Assert that the fallback path is returned
212
+ self.assertEqual(result, fallback_path)
213
+
214
+ # Ensure that ensure_path_valid was called with the correct paths
215
+ mock_ensure_path_valid.assert_any_call(file_info["output_dir"])
216
+ mock_ensure_path_valid.assert_any_call(fallback_path)
217
+
218
+ # Ensure get_fallback_path was called with the correct job_id
219
+ mock_get_fallback_path.assert_called_with(task_info["job_id"])
220
+
221
+ @patch("ciocore.downloader.base_downloader.ensure_path_valid", return_value=False)
222
+ @patch("ciocore.downloader.base_downloader.get_fallback_path")
223
+ def test_none_is_returned_if_output_dir_and_fallback_path_are_invalid(
224
+ self, mock_get_fallback_path, mock_ensure_path_valid
225
+ ):
226
+ file_info = {"output_dir": "/invalid/output/dir", "job_id": "00123", "task_id": "task1", "relative_path": "file.txt"}
227
+ task_info = {"job_id": "00123"}
228
+ user_specified_path = None # No user-specified path
229
+ fallback_path = "/invalid/fallback/path/00123"
230
+
231
+ # Mock get_fallback_path to return a specific fallback path
232
+ mock_get_fallback_path.return_value = fallback_path
233
+
234
+ result = ensure_writable_output_path(file_info, task_info, user_specified_path)
235
+
236
+ # Assert that None is returned when no valid paths are found
237
+ self.assertIsNone(result)
238
+
239
+ # Ensure that ensure_path_valid was called with both paths
240
+ mock_ensure_path_valid.assert_any_call(file_info["output_dir"])
241
+ mock_ensure_path_valid.assert_any_call(fallback_path)
242
+
243
+ # Ensure get_fallback_path was called with the correct job_id
244
+ mock_get_fallback_path.assert_called_with(task_info["job_id"])
245
+
246
+
247
+ class TestEnsurePathValid(unittest.TestCase):
248
+
249
+ @patch("ciocore.downloader.base_downloader.os.makedirs")
250
+ @patch("ciocore.downloader.base_downloader.os")
251
+ def test_drive_letter_path_on_linux_returns_false(self, mock_os, mock_makedirs):
252
+ # Set os.name to 'posix' to simulate a Linux environment
253
+ mock_os.name = 'posix'
254
+
255
+ # Define a path with a drive letter
256
+ path_with_drive_letter = "C:/some/path"
257
+
258
+ # Call ensure_path_valid and assert it returns False
259
+ result = ensure_path_valid(path_with_drive_letter)
260
+ self.assertFalse(result)
261
+
262
+ # Ensure os.makedirs is not called since the path is already invalid
263
+ mock_makedirs.assert_not_called()
264
+
265
+
266
+ @patch("ciocore.downloader.base_downloader.os.makedirs")
267
+ @patch("ciocore.downloader.base_downloader.os")
268
+ def test_drive_letter_path_on_windows_calls_makedirs_and_returns_true(self, mock_os, mock_makedirs):
269
+ # Set os.name to 'nt' to simulate a Windows environment
270
+ mock_os.name = 'nt'
271
+
272
+ # Define a path with a drive letter
273
+ path_with_drive_letter = "C:/some/path"
274
+
275
+ # Call ensure_path_valid and assert it returns True
276
+ result = ensure_path_valid(path_with_drive_letter)
277
+ self.assertTrue(result)
278
+
279
+ # Ensure os.makedirs is called since the path is valid
280
+ mock_makedirs.assert_called_with(path_with_drive_letter, exist_ok=True)
tests/test_config.py CHANGED
@@ -8,7 +8,18 @@ import logging
8
8
 
9
9
  from unittest import mock
10
10
 
11
- APIKEY = '{"auth_provider_x509_cert_url": "https://www.exampleapis.com/oauth2/v1/certs", "auth_uri": "https://accounts.example.com/o/oauth2/auth", "client_email": "account-5641301770895360@eloquent-vector-104019.iam.gserviceaccount.com", "client_id": "106815243682887997903", "client_x509_cert_url": "https://www.exampleapis.com/robot/v1/metadata/x509/account-5641301770895360%40eloquent-vector-104019.iam.gserviceaccount.com", "private_key": "-----BEGIN PRIVATE KEY-----\nMIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDcadqRWyd7VpYN\n804fEn07qlqNXU0ihz6g2dfbj9gzZDhHGVCI5QvPFskAQcV91k8cDdFu380m+sh1\nz9wlcdaM/lksRV/DRJiV76qrqyrNi0gSekZUuYhSsyMWUGvG4aSbf5BzVf1j8W4I\nUArH+ht5pgdSjowNc4zQIqmMH2XY+Ntr+NysBBfIv1PI6GoHFgDYOTSzsvz0qFYS\nWuonYGTjzNz4CY9Yh5ki8iq0/ijKzpUWeRUkpK9uF7WBoxrj3EyFFHejtfhVX2l0\n2KxrIF0kkmy5nmxVUck76FqdQ6vyvaHudREd3z/08hgdYogemQUTKFE/0LQmKuBV\nIJipPvORAgMBAAECggEAZvPMozN8LBikC00XstzMgRePp1MqydN5NeG6+TPlrQ+F\nV/RjkSXHT8oZRdTy3dXB6t0rc4n2xdvC0YCvGBBlwkK1vT+EPO2oBfTF99yCKDME\nDZlui3mDyvkgjPYweVuBKx65Bp5mNo4ZMqnMd18EAVxDM9UgZtIPtlJSdoBd7qtk\nyCGr03l+SV0krmvPV+KS9vyDOg/7Km5gMhTMaIveNyS1pG6AmZ0ggQA5djg/P2iF\nxGwYdvfADY5cBzg0OG5ELv9hvyA4CKN6RLfYv3JJS2gbNaMknjmsjaM/p0LtE2HL\n+uFPL0ZjoMwV3BlEFQIHwhNWS63H43ISBa1/2XvGPwKBgQDw4a4kQ9+Ce3edWonz\n3Fm/3no+HrMSVjbv28qphAHMFrSUdbMejQm4QSbNeOW1pEcVvW21u8tYagrJEsaU\ns4DulFXRep36teVDWpc1FrNowWEPPVeC8CO74VfssK1h2Itqis8JPbzXOcNtSH9+\nAg1EvrB9XnyEvJuM6GOGo3juTwKBgQDqP058+H3iSZe6al4P6Ib3g/82nr2dHeN5\n4xxGu1fzTMNX5lopbNji6tQcsMoMVPdOvCQy5c0PEUbvo7mxfZ8fOZwgBjIcXbYg\nzIJkPTSv7nxSE5M5lW5juzLkdq2k5k0qt9ByWuWEA3PSn/DEANa5888phSCoJSw/\nPjpwHhZoHwKBgQDCoQbMxI6e5lYCrToT8PIPhpptAO8dnM2sxoGcsE2ncp0b63H7\n+GdnGjVZBhtMxdyt4y33DjLCUIRAbUxIsDU4EGC67oEhJsGEx3iva5Uwyjc7UgwY\nfyHQV8ZsN2EQUyBqyJd6VwjzOff+n/prfQrthcoisiqYMbDZjJeGHSXEHwKBgAo4\nBsmG4Z78jOTx/PZ+s1ya4ohUdnsjMahAkxw20ghoIeF0yBwkhnWnvucdg0L0dfF2\nXbHmuoJcw5ZyswgeLdHj5n6zJn58TBS0Nz/+N40xPzUpa3PIpA8vvHGhB8Q408b4\nS9yhQH/40pWuqocybiugijoKd7k+HecIZO49MccLAoGBAPDScJHSxKPW6wJKjxDC\nXXWWQ2flbwv4Sja487QV/trWMSRnHJHnCVHqv/F7ThPaoHM+MJSzrJ7wr/CJhk0H\noEt+0Rn6qPd/A36bSjTfXMFLXWi75ovek+IJGKxr7B46jrcS/oe1XIIOlV1+OvOY\nVoO6vgYkPhpMkth2hyZ/luea\n-----END PRIVATE KEY-----\n", "private_key_id": "3dfe3bdc40d4dc431d283bf22feb113c9b622dd3", "project_id": "eloquent-vector-104019", "token_uri": "https://oauth2.exampleapis.com/token", "type": "service_account"}'
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
+ }'''
12
23
 
13
24
  PY3 = sys.version_info >= (3, 0)
14
25
  BUILTIN_OPEN = "builtins.open" if PY3 else "__builtin__.open"
@@ -264,7 +275,7 @@ class ApiKeyFromVariableTest(unittest.TestCase):
264
275
  self.down()
265
276
 
266
277
  def test_returns_dict_if_key_is_b64encoded(self):
267
- key = APIKEY
278
+ key = MOCK_API_KEY
268
279
  if sys.version_info < (3, 0):
269
280
  self.up(env={"CONDUCTOR_API_KEY": base64.b64encode(key)})
270
281
  else:
@@ -299,7 +310,7 @@ class ApiKeyFromFileTest(unittest.TestCase):
299
310
  self.assertEqual(result, None)
300
311
  self.down()
301
312
 
302
- @mock.patch(BUILTIN_OPEN, new_callable=mock.mock_open, read_data=APIKEY, create=True)
313
+ @mock.patch(BUILTIN_OPEN, new_callable=mock.mock_open, read_data=MOCK_API_KEY, create=True)
303
314
  def test_open_called_with_api_key_path(self, mock_file_open):
304
315
  self.up(env={"CONDUCTOR_API_KEY_PATH": "path/to/api_key"})
305
316
  config.Config.get_api_key_from_file()
@@ -313,7 +324,7 @@ class ApiKeyFromFileTest(unittest.TestCase):
313
324
  config.Config.get_api_key_from_file()
314
325
  self.down()
315
326
 
316
- @mock.patch(BUILTIN_OPEN, new_callable=mock.mock_open, read_data=APIKEY, create=True)
327
+ @mock.patch(BUILTIN_OPEN, new_callable=mock.mock_open, read_data=MOCK_API_KEY, create=True)
317
328
  def test_returns_dict_if_key_is_valid_json(self, mock_file_open):
318
329
  self.up(env={"CONDUCTOR_API_KEY_PATH": "path/to/api_key"})
319
330
  result = config.Config.get_api_key_from_file()