cumulusci-plus 5.0.24__py3-none-any.whl → 5.0.26__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 cumulusci-plus might be problematic. Click here for more details.
- cumulusci/__about__.py +1 -1
- cumulusci/cli/task.py +17 -0
- cumulusci/cli/tests/test_error.py +3 -1
- cumulusci/cli/tests/test_task.py +88 -2
- cumulusci/core/github.py +1 -1
- cumulusci/core/sfdx.py +3 -1
- cumulusci/cumulusci.yml +20 -0
- cumulusci/robotframework/pageobjects/ObjectManagerPageObject.py +1 -1
- cumulusci/salesforce_api/rest_deploy.py +1 -1
- cumulusci/tasks/apex/anon.py +1 -1
- cumulusci/tasks/apex/testrunner.py +6 -1
- cumulusci/tasks/bulkdata/extract.py +0 -1
- cumulusci/tasks/bulkdata/tests/test_load.py +0 -2
- cumulusci/tasks/bulkdata/tests/test_select_utils.py +6 -0
- cumulusci/tasks/metadata_etl/base.py +7 -3
- cumulusci/tasks/push/README.md +15 -17
- cumulusci/tasks/release_notes/README.md +13 -13
- cumulusci/tasks/robotframework/tests/test_robotframework.py +1 -1
- cumulusci/tasks/salesforce/Deploy.py +5 -1
- cumulusci/tasks/salesforce/composite.py +1 -1
- cumulusci/tasks/salesforce/custom_settings_wait.py +1 -1
- cumulusci/tasks/salesforce/enable_prediction.py +5 -1
- cumulusci/tasks/salesforce/sourcetracking.py +1 -1
- cumulusci/tasks/salesforce/tests/test_update_external_credential.py +912 -0
- cumulusci/tasks/salesforce/tests/test_update_named_credential.py +1042 -0
- cumulusci/tasks/salesforce/update_external_credential.py +562 -0
- cumulusci/tasks/salesforce/update_named_credential.py +441 -0
- cumulusci/tasks/salesforce/update_profile.py +17 -13
- cumulusci/tasks/salesforce/users/permsets.py +70 -2
- cumulusci/tasks/salesforce/users/tests/test_permsets.py +184 -0
- cumulusci/tasks/sfdmu/__init__.py +0 -0
- cumulusci/tasks/sfdmu/sfdmu.py +256 -0
- cumulusci/tasks/sfdmu/tests/__init__.py +1 -0
- cumulusci/tasks/sfdmu/tests/test_runner.py +212 -0
- cumulusci/tasks/sfdmu/tests/test_sfdmu.py +443 -0
- cumulusci/tasks/utility/credentialManager.py +256 -0
- cumulusci/tasks/utility/directoryRecreator.py +30 -0
- cumulusci/tasks/utility/secretsToEnv.py +130 -0
- cumulusci/tasks/utility/tests/test_credentialManager.py +564 -0
- cumulusci/tasks/utility/tests/test_directoryRecreator.py +439 -0
- cumulusci/tasks/utility/tests/test_secretsToEnv.py +1091 -0
- cumulusci/utils/__init__.py +23 -1
- cumulusci/utils/http/tests/cassettes/ManualEditTestCompositeParallelSalesforce.test_http_headers.yaml +31 -30
- cumulusci/utils/yaml/tests/test_model_parser.py +2 -2
- {cumulusci_plus-5.0.24.dist-info → cumulusci_plus-5.0.26.dist-info}/METADATA +7 -9
- {cumulusci_plus-5.0.24.dist-info → cumulusci_plus-5.0.26.dist-info}/RECORD +50 -35
- {cumulusci_plus-5.0.24.dist-info → cumulusci_plus-5.0.26.dist-info}/WHEEL +0 -0
- {cumulusci_plus-5.0.24.dist-info → cumulusci_plus-5.0.26.dist-info}/entry_points.txt +0 -0
- {cumulusci_plus-5.0.24.dist-info → cumulusci_plus-5.0.26.dist-info}/licenses/AUTHORS.rst +0 -0
- {cumulusci_plus-5.0.24.dist-info → cumulusci_plus-5.0.26.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,1042 @@
|
|
|
1
|
+
import os
|
|
2
|
+
from unittest import mock
|
|
3
|
+
|
|
4
|
+
import pytest
|
|
5
|
+
import responses
|
|
6
|
+
|
|
7
|
+
from cumulusci.core.exceptions import SalesforceDXException
|
|
8
|
+
from cumulusci.tasks.salesforce.update_named_credential import (
|
|
9
|
+
NamedCredentialCalloutOptions,
|
|
10
|
+
NamedCredentialHttpHeader,
|
|
11
|
+
NamedCredentialParameter,
|
|
12
|
+
TransformNamedCredentialParameter,
|
|
13
|
+
UpdateNamedCredential,
|
|
14
|
+
)
|
|
15
|
+
from cumulusci.tests.util import CURRENT_SF_API_VERSION
|
|
16
|
+
|
|
17
|
+
from .util import create_task
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class TestNamedCredentialHttpHeader:
|
|
21
|
+
"""Test NamedCredentialHttpHeader model"""
|
|
22
|
+
|
|
23
|
+
def test_http_header_defaults(self):
|
|
24
|
+
"""Test default values for http header"""
|
|
25
|
+
header = NamedCredentialHttpHeader(name="test-header", value="test-value")
|
|
26
|
+
assert header.name == "test-header"
|
|
27
|
+
assert header.value == "test-value"
|
|
28
|
+
assert header.secret is False
|
|
29
|
+
assert header.sequence_number is None
|
|
30
|
+
|
|
31
|
+
def test_http_header_with_secret(self):
|
|
32
|
+
"""Test http header with secret flag"""
|
|
33
|
+
header = NamedCredentialHttpHeader(
|
|
34
|
+
name="api-key", value="secret123", secret=True, sequence_number=1
|
|
35
|
+
)
|
|
36
|
+
assert header.name == "api-key"
|
|
37
|
+
assert header.value == "secret123"
|
|
38
|
+
assert header.secret is True
|
|
39
|
+
assert header.sequence_number == 1
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
class TestNamedCredentialParameter:
|
|
43
|
+
"""Test NamedCredentialParameter model"""
|
|
44
|
+
|
|
45
|
+
def test_parameter_with_url(self):
|
|
46
|
+
"""Test parameter with URL"""
|
|
47
|
+
param = NamedCredentialParameter(url="https://example.com")
|
|
48
|
+
assert param.url == "https://example.com"
|
|
49
|
+
assert param.param_type() == "Url"
|
|
50
|
+
assert param.param_value() == "https://example.com"
|
|
51
|
+
|
|
52
|
+
def test_parameter_with_authentication(self):
|
|
53
|
+
"""Test parameter with authentication"""
|
|
54
|
+
param = NamedCredentialParameter(authentication="MyAuth")
|
|
55
|
+
assert param.authentication == "MyAuth"
|
|
56
|
+
assert param.param_type() == "Authentication"
|
|
57
|
+
assert param.param_value() == "MyAuth"
|
|
58
|
+
|
|
59
|
+
def test_parameter_with_certificate(self):
|
|
60
|
+
"""Test parameter with certificate"""
|
|
61
|
+
param = NamedCredentialParameter(certificate="MyCert")
|
|
62
|
+
assert param.certificate == "MyCert"
|
|
63
|
+
assert param.param_type() == "ClientCertificate"
|
|
64
|
+
assert param.param_value() == "MyCert"
|
|
65
|
+
|
|
66
|
+
def test_parameter_with_allowed_namespaces(self):
|
|
67
|
+
"""Test parameter with allowed managed package namespaces"""
|
|
68
|
+
param = NamedCredentialParameter(
|
|
69
|
+
allowed_managed_package_namespaces="namespace1"
|
|
70
|
+
)
|
|
71
|
+
assert param.allowed_managed_package_namespaces == "namespace1"
|
|
72
|
+
assert param.param_type() == "AllowedManagedPackageNamespaces"
|
|
73
|
+
assert param.param_value() == "namespace1"
|
|
74
|
+
|
|
75
|
+
def test_parameter_with_http_headers(self):
|
|
76
|
+
"""Test parameter with HTTP headers"""
|
|
77
|
+
headers = [
|
|
78
|
+
NamedCredentialHttpHeader(name="header1", value="value1"),
|
|
79
|
+
NamedCredentialHttpHeader(
|
|
80
|
+
name="header2", value="value2", sequence_number=1
|
|
81
|
+
),
|
|
82
|
+
]
|
|
83
|
+
param = NamedCredentialParameter(http_header=headers)
|
|
84
|
+
assert param.param_type() == "HttpHeader"
|
|
85
|
+
assert param.param_value(http_header="header1") == "value1"
|
|
86
|
+
assert param.param_value(http_header="header2") == "value2"
|
|
87
|
+
assert param.param_value(http_header="nonexistent") is None
|
|
88
|
+
|
|
89
|
+
def test_parameter_validation_error_multiple_params(self):
|
|
90
|
+
"""Test that only one parameter can be provided"""
|
|
91
|
+
with pytest.raises(ValueError, match="Only one of the parameters is required"):
|
|
92
|
+
NamedCredentialParameter(url="https://example.com", certificate="MyCert")
|
|
93
|
+
|
|
94
|
+
def test_parameter_validation_error_no_params(self):
|
|
95
|
+
"""Test that at least one parameter must be provided"""
|
|
96
|
+
with pytest.raises(ValueError, match="Only one of the parameters is required"):
|
|
97
|
+
NamedCredentialParameter()
|
|
98
|
+
|
|
99
|
+
def test_get_parameter_to_update_url(self):
|
|
100
|
+
"""Test get_parameter_to_update for URL parameter"""
|
|
101
|
+
param = NamedCredentialParameter(url="https://example.com")
|
|
102
|
+
result = param.get_parameter_to_update()
|
|
103
|
+
assert len(result) == 1
|
|
104
|
+
assert result[0]["parameterType"] == "Url"
|
|
105
|
+
assert result[0]["parameterValue"] == "https://example.com"
|
|
106
|
+
|
|
107
|
+
def test_get_parameter_to_update_authentication(self):
|
|
108
|
+
"""Test get_parameter_to_update for authentication parameter"""
|
|
109
|
+
param = NamedCredentialParameter(authentication="MyAuth")
|
|
110
|
+
result = param.get_parameter_to_update()
|
|
111
|
+
assert len(result) == 1
|
|
112
|
+
assert result[0]["parameterType"] == "Authentication"
|
|
113
|
+
assert result[0]["parameterName"] == "ExternalCredential"
|
|
114
|
+
assert result[0]["externalCredential"] == "MyAuth"
|
|
115
|
+
assert result[0]["parameterValue"] == "MyAuth"
|
|
116
|
+
|
|
117
|
+
def test_get_parameter_to_update_certificate(self):
|
|
118
|
+
"""Test get_parameter_to_update for certificate parameter"""
|
|
119
|
+
param = NamedCredentialParameter(certificate="MyCert")
|
|
120
|
+
result = param.get_parameter_to_update()
|
|
121
|
+
assert len(result) == 1
|
|
122
|
+
assert result[0]["parameterType"] == "ClientCertificate"
|
|
123
|
+
assert result[0]["parameterName"] == "ClientCertificate"
|
|
124
|
+
assert result[0]["certificate"] == "MyCert"
|
|
125
|
+
assert result[0]["parameterValue"] == "MyCert"
|
|
126
|
+
|
|
127
|
+
def test_get_parameter_to_update_http_headers(self):
|
|
128
|
+
"""Test get_parameter_to_update for HTTP headers"""
|
|
129
|
+
headers = [
|
|
130
|
+
NamedCredentialHttpHeader(name="header1", value="value1", secret=True),
|
|
131
|
+
NamedCredentialHttpHeader(
|
|
132
|
+
name="header2", value="value2", sequence_number=2
|
|
133
|
+
),
|
|
134
|
+
]
|
|
135
|
+
param = NamedCredentialParameter(http_header=headers)
|
|
136
|
+
result = param.get_parameter_to_update()
|
|
137
|
+
assert len(result) == 2
|
|
138
|
+
assert result[0]["parameterType"] == "HttpHeader"
|
|
139
|
+
assert result[0]["parameterName"] == "header1"
|
|
140
|
+
assert result[0]["parameterValue"] == "value1"
|
|
141
|
+
assert result[0]["secret"] is True
|
|
142
|
+
assert result[1]["parameterName"] == "header2"
|
|
143
|
+
assert result[1]["parameterValue"] == "value2"
|
|
144
|
+
assert result[1]["sequenceNumber"] == 2
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
class TestTransformNamedCredentialParameter:
|
|
148
|
+
"""Test TransformNamedCredentialParameter model"""
|
|
149
|
+
|
|
150
|
+
def test_transform_param_value_url(self):
|
|
151
|
+
"""Test transform parameter value for URL from environment"""
|
|
152
|
+
with mock.patch.dict(os.environ, {"TEST_URL": "https://env-example.com"}):
|
|
153
|
+
param = TransformNamedCredentialParameter(url="TEST_URL")
|
|
154
|
+
assert param.param_value() == "https://env-example.com"
|
|
155
|
+
|
|
156
|
+
def test_transform_param_value_authentication(self):
|
|
157
|
+
"""Test transform parameter value for authentication from environment"""
|
|
158
|
+
with mock.patch.dict(os.environ, {"TEST_AUTH": "EnvAuth"}):
|
|
159
|
+
param = TransformNamedCredentialParameter(authentication="TEST_AUTH")
|
|
160
|
+
assert param.param_value() == "EnvAuth"
|
|
161
|
+
|
|
162
|
+
def test_transform_param_value_certificate(self):
|
|
163
|
+
"""Test transform parameter value for certificate from environment"""
|
|
164
|
+
with mock.patch.dict(os.environ, {"TEST_CERT": "EnvCert"}):
|
|
165
|
+
param = TransformNamedCredentialParameter(certificate="TEST_CERT")
|
|
166
|
+
assert param.param_value() == "EnvCert"
|
|
167
|
+
|
|
168
|
+
def test_transform_param_value_http_header(self):
|
|
169
|
+
"""Test transform parameter value for HTTP header from environment"""
|
|
170
|
+
with mock.patch.dict(os.environ, {"HEADER_VALUE": "env-header-value"}):
|
|
171
|
+
headers = [
|
|
172
|
+
NamedCredentialHttpHeader(name="test-header", value="HEADER_VALUE")
|
|
173
|
+
]
|
|
174
|
+
param = TransformNamedCredentialParameter(http_header=headers)
|
|
175
|
+
assert param.param_value(http_header="test-header") == "env-header-value"
|
|
176
|
+
|
|
177
|
+
def test_transform_param_value_missing_env(self):
|
|
178
|
+
"""Test transform parameter value when environment variable is missing"""
|
|
179
|
+
param = TransformNamedCredentialParameter(url="NONEXISTENT_VAR")
|
|
180
|
+
assert param.param_value() is None
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
class TestNamedCredentialCalloutOptions:
|
|
184
|
+
"""Test NamedCredentialCalloutOptions model"""
|
|
185
|
+
|
|
186
|
+
def test_callout_options_defaults(self):
|
|
187
|
+
"""Test default values for callout options"""
|
|
188
|
+
options = NamedCredentialCalloutOptions()
|
|
189
|
+
assert options.allow_merge_fields_in_body is None
|
|
190
|
+
assert options.allow_merge_fields_in_header is None
|
|
191
|
+
assert options.generate_authorization_header is None
|
|
192
|
+
|
|
193
|
+
def test_callout_options_with_values(self):
|
|
194
|
+
"""Test callout options with values"""
|
|
195
|
+
options = NamedCredentialCalloutOptions(
|
|
196
|
+
allow_merge_fields_in_body=True,
|
|
197
|
+
allow_merge_fields_in_header=True,
|
|
198
|
+
generate_authorization_header=False,
|
|
199
|
+
)
|
|
200
|
+
assert options.allow_merge_fields_in_body is True
|
|
201
|
+
assert options.allow_merge_fields_in_header is True
|
|
202
|
+
assert options.generate_authorization_header is False
|
|
203
|
+
|
|
204
|
+
|
|
205
|
+
class TestUpdateNamedCredential:
|
|
206
|
+
"""Test UpdateNamedCredential task"""
|
|
207
|
+
|
|
208
|
+
@responses.activate
|
|
209
|
+
def test_update_named_credential_success(self):
|
|
210
|
+
"""Test successful update of named credential"""
|
|
211
|
+
task = create_task(
|
|
212
|
+
UpdateNamedCredential,
|
|
213
|
+
{
|
|
214
|
+
"name": "testNc",
|
|
215
|
+
"namespace": "",
|
|
216
|
+
"callout_options": {
|
|
217
|
+
"allow_merge_fields_in_body": True,
|
|
218
|
+
"allow_merge_fields_in_header": True,
|
|
219
|
+
"generate_authorization_header": True,
|
|
220
|
+
},
|
|
221
|
+
"parameters": [{"url": "https://testingAPI.example.com"}],
|
|
222
|
+
},
|
|
223
|
+
)
|
|
224
|
+
|
|
225
|
+
nc_id = "0XA1234567890ABC"
|
|
226
|
+
tooling_url = f"https://test.salesforce.com/services/data/v{CURRENT_SF_API_VERSION}/tooling"
|
|
227
|
+
|
|
228
|
+
# Mock query for named credential ID
|
|
229
|
+
responses.add(
|
|
230
|
+
method="GET",
|
|
231
|
+
url=f"{tooling_url}/query/?q=SELECT+Id+FROM+NamedCredential+WHERE+DeveloperName%3D%27testNc%27+LIMIT+1",
|
|
232
|
+
json={"size": 1, "records": [{"Id": nc_id}]},
|
|
233
|
+
status=200,
|
|
234
|
+
)
|
|
235
|
+
|
|
236
|
+
# Mock get named credential object
|
|
237
|
+
responses.add(
|
|
238
|
+
method="GET",
|
|
239
|
+
url=f"{tooling_url}/sobjects/NamedCredential/{nc_id}",
|
|
240
|
+
json={
|
|
241
|
+
"Metadata": {
|
|
242
|
+
"namedCredentialType": "SecuredEndpoint",
|
|
243
|
+
"namedCredentialParameters": [
|
|
244
|
+
{
|
|
245
|
+
"parameterName": None,
|
|
246
|
+
"parameterType": "Url",
|
|
247
|
+
"parameterValue": "https://old.example.com",
|
|
248
|
+
"certificate": None,
|
|
249
|
+
"description": None,
|
|
250
|
+
"externalCredential": None,
|
|
251
|
+
"sequenceNumber": None,
|
|
252
|
+
}
|
|
253
|
+
],
|
|
254
|
+
}
|
|
255
|
+
},
|
|
256
|
+
status=200,
|
|
257
|
+
)
|
|
258
|
+
|
|
259
|
+
# Mock update named credential
|
|
260
|
+
responses.add(
|
|
261
|
+
method="PATCH",
|
|
262
|
+
url=f"{tooling_url}/sobjects/NamedCredential/{nc_id}",
|
|
263
|
+
json={},
|
|
264
|
+
status=200,
|
|
265
|
+
)
|
|
266
|
+
|
|
267
|
+
task()
|
|
268
|
+
assert len(responses.calls) == 3
|
|
269
|
+
|
|
270
|
+
@responses.activate
|
|
271
|
+
def test_update_named_credential_with_namespace(self):
|
|
272
|
+
"""Test update of named credential with namespace"""
|
|
273
|
+
task = create_task(
|
|
274
|
+
UpdateNamedCredential,
|
|
275
|
+
{
|
|
276
|
+
"name": "testNc",
|
|
277
|
+
"namespace": "th_dev",
|
|
278
|
+
"parameters": [{"url": "https://testingAPI.example.com"}],
|
|
279
|
+
},
|
|
280
|
+
)
|
|
281
|
+
|
|
282
|
+
nc_id = "0XA1234567890ABC"
|
|
283
|
+
tooling_url = f"https://test.salesforce.com/services/data/v{CURRENT_SF_API_VERSION}/tooling"
|
|
284
|
+
|
|
285
|
+
# Mock query for named credential ID with namespace
|
|
286
|
+
responses.add(
|
|
287
|
+
method="GET",
|
|
288
|
+
url=f"{tooling_url}/query/?q=SELECT+Id+FROM+NamedCredential+WHERE+DeveloperName%3D%27testNc%27+AND+NamespacePrefix%3D%27th_dev%27+LIMIT+1",
|
|
289
|
+
json={"size": 1, "records": [{"Id": nc_id}]},
|
|
290
|
+
status=200,
|
|
291
|
+
)
|
|
292
|
+
|
|
293
|
+
# Mock get named credential object
|
|
294
|
+
responses.add(
|
|
295
|
+
method="GET",
|
|
296
|
+
url=f"{tooling_url}/sobjects/NamedCredential/{nc_id}",
|
|
297
|
+
json={
|
|
298
|
+
"Metadata": {
|
|
299
|
+
"namedCredentialType": "SecuredEndpoint",
|
|
300
|
+
"namedCredentialParameters": [
|
|
301
|
+
{
|
|
302
|
+
"parameterName": None,
|
|
303
|
+
"parameterType": "Url",
|
|
304
|
+
"parameterValue": "https://old.example.com",
|
|
305
|
+
}
|
|
306
|
+
],
|
|
307
|
+
}
|
|
308
|
+
},
|
|
309
|
+
status=200,
|
|
310
|
+
)
|
|
311
|
+
|
|
312
|
+
# Mock update named credential
|
|
313
|
+
responses.add(
|
|
314
|
+
method="PATCH",
|
|
315
|
+
url=f"{tooling_url}/sobjects/NamedCredential/{nc_id}",
|
|
316
|
+
json={},
|
|
317
|
+
status=200,
|
|
318
|
+
)
|
|
319
|
+
|
|
320
|
+
task()
|
|
321
|
+
assert len(responses.calls) == 3
|
|
322
|
+
|
|
323
|
+
@responses.activate
|
|
324
|
+
def test_update_named_credential_not_found(self):
|
|
325
|
+
"""Test error when named credential is not found"""
|
|
326
|
+
task = create_task(
|
|
327
|
+
UpdateNamedCredential,
|
|
328
|
+
{
|
|
329
|
+
"name": "nonexistent",
|
|
330
|
+
"parameters": [{"url": "https://testingAPI.example.com"}],
|
|
331
|
+
},
|
|
332
|
+
)
|
|
333
|
+
|
|
334
|
+
tooling_url = f"https://test.salesforce.com/services/data/v{CURRENT_SF_API_VERSION}/tooling"
|
|
335
|
+
|
|
336
|
+
# Mock query for named credential ID - not found
|
|
337
|
+
responses.add(
|
|
338
|
+
method="GET",
|
|
339
|
+
url=f"{tooling_url}/query/?q=SELECT+Id+FROM+NamedCredential+WHERE+DeveloperName%3D%27nonexistent%27+LIMIT+1",
|
|
340
|
+
json={"size": 0, "records": []},
|
|
341
|
+
status=200,
|
|
342
|
+
)
|
|
343
|
+
|
|
344
|
+
with pytest.raises(Exception): # Can be TypeError or SalesforceDXException
|
|
345
|
+
task()
|
|
346
|
+
|
|
347
|
+
@responses.activate
|
|
348
|
+
def test_update_named_credential_query_error(self):
|
|
349
|
+
"""Test error handling when query fails"""
|
|
350
|
+
task = create_task(
|
|
351
|
+
UpdateNamedCredential,
|
|
352
|
+
{
|
|
353
|
+
"name": "testNc",
|
|
354
|
+
"parameters": [{"url": "https://testingAPI.example.com"}],
|
|
355
|
+
},
|
|
356
|
+
)
|
|
357
|
+
|
|
358
|
+
tooling_url = f"https://test.salesforce.com/services/data/v{CURRENT_SF_API_VERSION}/tooling"
|
|
359
|
+
|
|
360
|
+
# Mock query error
|
|
361
|
+
responses.add(
|
|
362
|
+
method="GET",
|
|
363
|
+
url=f"{tooling_url}/query/?q=SELECT+Id+FROM+NamedCredential+WHERE+DeveloperName%3D%27testNc%27+LIMIT+1",
|
|
364
|
+
json={"error": "Query failed"},
|
|
365
|
+
status=500,
|
|
366
|
+
)
|
|
367
|
+
|
|
368
|
+
with pytest.raises(Exception): # Can be TypeError or SalesforceDXException
|
|
369
|
+
task()
|
|
370
|
+
|
|
371
|
+
@responses.activate
|
|
372
|
+
def test_update_named_credential_not_secured_endpoint(self):
|
|
373
|
+
"""Test error when named credential is not a SecuredEndpoint"""
|
|
374
|
+
task = create_task(
|
|
375
|
+
UpdateNamedCredential,
|
|
376
|
+
{
|
|
377
|
+
"name": "testNc",
|
|
378
|
+
"parameters": [{"url": "https://testingAPI.example.com"}],
|
|
379
|
+
},
|
|
380
|
+
)
|
|
381
|
+
|
|
382
|
+
nc_id = "0XA1234567890ABC"
|
|
383
|
+
tooling_url = f"https://test.salesforce.com/services/data/v{CURRENT_SF_API_VERSION}/tooling"
|
|
384
|
+
|
|
385
|
+
# Mock query for named credential ID
|
|
386
|
+
responses.add(
|
|
387
|
+
method="GET",
|
|
388
|
+
url=f"{tooling_url}/query/?q=SELECT+Id+FROM+NamedCredential+WHERE+DeveloperName%3D%27testNc%27+LIMIT+1",
|
|
389
|
+
json={"size": 1, "records": [{"Id": nc_id}]},
|
|
390
|
+
status=200,
|
|
391
|
+
)
|
|
392
|
+
|
|
393
|
+
# Mock get named credential object with wrong type
|
|
394
|
+
responses.add(
|
|
395
|
+
method="GET",
|
|
396
|
+
url=f"{tooling_url}/sobjects/NamedCredential/{nc_id}",
|
|
397
|
+
json={"Metadata": {"namedCredentialType": "Legacy"}},
|
|
398
|
+
status=200,
|
|
399
|
+
)
|
|
400
|
+
|
|
401
|
+
with pytest.raises(
|
|
402
|
+
SalesforceDXException,
|
|
403
|
+
match="Named credential 'testNc' is not a secured endpoint",
|
|
404
|
+
):
|
|
405
|
+
task()
|
|
406
|
+
|
|
407
|
+
@responses.activate
|
|
408
|
+
def test_update_named_credential_get_object_error(self):
|
|
409
|
+
"""Test error when getting named credential object fails"""
|
|
410
|
+
task = create_task(
|
|
411
|
+
UpdateNamedCredential,
|
|
412
|
+
{
|
|
413
|
+
"name": "testNc",
|
|
414
|
+
"parameters": [{"url": "https://testingAPI.example.com"}],
|
|
415
|
+
},
|
|
416
|
+
)
|
|
417
|
+
|
|
418
|
+
nc_id = "0XA1234567890ABC"
|
|
419
|
+
tooling_url = f"https://test.salesforce.com/services/data/v{CURRENT_SF_API_VERSION}/tooling"
|
|
420
|
+
|
|
421
|
+
# Mock query for named credential ID
|
|
422
|
+
responses.add(
|
|
423
|
+
method="GET",
|
|
424
|
+
url=f"{tooling_url}/query/?q=SELECT+Id+FROM+NamedCredential+WHERE+DeveloperName%3D%27testNc%27+LIMIT+1",
|
|
425
|
+
json={"size": 1, "records": [{"Id": nc_id}]},
|
|
426
|
+
status=200,
|
|
427
|
+
)
|
|
428
|
+
|
|
429
|
+
# Mock get named credential object - error
|
|
430
|
+
responses.add(
|
|
431
|
+
method="GET",
|
|
432
|
+
url=f"{tooling_url}/sobjects/NamedCredential/{nc_id}",
|
|
433
|
+
json={"error": "Failed to retrieve"},
|
|
434
|
+
status=404,
|
|
435
|
+
)
|
|
436
|
+
|
|
437
|
+
with pytest.raises(Exception): # Can be TypeError or SalesforceDXException
|
|
438
|
+
task()
|
|
439
|
+
|
|
440
|
+
@responses.activate
|
|
441
|
+
def test_update_named_credential_update_error(self):
|
|
442
|
+
"""Test error when updating named credential fails"""
|
|
443
|
+
task = create_task(
|
|
444
|
+
UpdateNamedCredential,
|
|
445
|
+
{
|
|
446
|
+
"name": "testNc",
|
|
447
|
+
"parameters": [{"url": "https://testingAPI.example.com"}],
|
|
448
|
+
},
|
|
449
|
+
)
|
|
450
|
+
|
|
451
|
+
nc_id = "0XA1234567890ABC"
|
|
452
|
+
tooling_url = f"https://test.salesforce.com/services/data/v{CURRENT_SF_API_VERSION}/tooling"
|
|
453
|
+
|
|
454
|
+
# Mock query for named credential ID
|
|
455
|
+
responses.add(
|
|
456
|
+
method="GET",
|
|
457
|
+
url=f"{tooling_url}/query/?q=SELECT+Id+FROM+NamedCredential+WHERE+DeveloperName%3D%27testNc%27+LIMIT+1",
|
|
458
|
+
json={"size": 1, "records": [{"Id": nc_id}]},
|
|
459
|
+
status=200,
|
|
460
|
+
)
|
|
461
|
+
|
|
462
|
+
# Mock get named credential object
|
|
463
|
+
responses.add(
|
|
464
|
+
method="GET",
|
|
465
|
+
url=f"{tooling_url}/sobjects/NamedCredential/{nc_id}",
|
|
466
|
+
json={
|
|
467
|
+
"Metadata": {
|
|
468
|
+
"namedCredentialType": "SecuredEndpoint",
|
|
469
|
+
"namedCredentialParameters": [
|
|
470
|
+
{
|
|
471
|
+
"parameterName": None,
|
|
472
|
+
"parameterType": "Url",
|
|
473
|
+
"parameterValue": "https://old.example.com",
|
|
474
|
+
}
|
|
475
|
+
],
|
|
476
|
+
}
|
|
477
|
+
},
|
|
478
|
+
status=200,
|
|
479
|
+
)
|
|
480
|
+
|
|
481
|
+
# Mock update named credential - error
|
|
482
|
+
responses.add(
|
|
483
|
+
method="PATCH",
|
|
484
|
+
url=f"{tooling_url}/sobjects/NamedCredential/{nc_id}",
|
|
485
|
+
json={"error": "Update failed"},
|
|
486
|
+
status=400,
|
|
487
|
+
)
|
|
488
|
+
|
|
489
|
+
with pytest.raises(Exception): # Can be TypeError or SalesforceDXException
|
|
490
|
+
task()
|
|
491
|
+
|
|
492
|
+
@responses.activate
|
|
493
|
+
def test_update_named_credential_with_http_headers(self):
|
|
494
|
+
"""Test update of named credential with HTTP headers"""
|
|
495
|
+
task = create_task(
|
|
496
|
+
UpdateNamedCredential,
|
|
497
|
+
{
|
|
498
|
+
"name": "testNc",
|
|
499
|
+
"parameters": [
|
|
500
|
+
{
|
|
501
|
+
"http_header": [
|
|
502
|
+
{"name": "x-api-key", "value": "secret123", "secret": True},
|
|
503
|
+
{
|
|
504
|
+
"name": "x-client-id",
|
|
505
|
+
"value": "client456",
|
|
506
|
+
"sequence_number": 1,
|
|
507
|
+
},
|
|
508
|
+
]
|
|
509
|
+
}
|
|
510
|
+
],
|
|
511
|
+
},
|
|
512
|
+
)
|
|
513
|
+
|
|
514
|
+
nc_id = "0XA1234567890ABC"
|
|
515
|
+
tooling_url = f"https://test.salesforce.com/services/data/v{CURRENT_SF_API_VERSION}/tooling"
|
|
516
|
+
|
|
517
|
+
# Mock query for named credential ID
|
|
518
|
+
responses.add(
|
|
519
|
+
method="GET",
|
|
520
|
+
url=f"{tooling_url}/query/?q=SELECT+Id+FROM+NamedCredential+WHERE+DeveloperName%3D%27testNc%27+LIMIT+1",
|
|
521
|
+
json={"size": 1, "records": [{"Id": nc_id}]},
|
|
522
|
+
status=200,
|
|
523
|
+
)
|
|
524
|
+
|
|
525
|
+
# Mock get named credential object
|
|
526
|
+
responses.add(
|
|
527
|
+
method="GET",
|
|
528
|
+
url=f"{tooling_url}/sobjects/NamedCredential/{nc_id}",
|
|
529
|
+
json={
|
|
530
|
+
"Metadata": {
|
|
531
|
+
"namedCredentialType": "SecuredEndpoint",
|
|
532
|
+
"namedCredentialParameters": [
|
|
533
|
+
{
|
|
534
|
+
"parameterName": None,
|
|
535
|
+
"parameterType": "Url",
|
|
536
|
+
"parameterValue": "https://api.example.com",
|
|
537
|
+
}
|
|
538
|
+
],
|
|
539
|
+
}
|
|
540
|
+
},
|
|
541
|
+
status=200,
|
|
542
|
+
)
|
|
543
|
+
|
|
544
|
+
# Mock update named credential
|
|
545
|
+
responses.add(
|
|
546
|
+
method="PATCH",
|
|
547
|
+
url=f"{tooling_url}/sobjects/NamedCredential/{nc_id}",
|
|
548
|
+
json={},
|
|
549
|
+
status=200,
|
|
550
|
+
)
|
|
551
|
+
|
|
552
|
+
task()
|
|
553
|
+
assert len(responses.calls) == 3
|
|
554
|
+
|
|
555
|
+
@responses.activate
|
|
556
|
+
def test_update_named_credential_with_authentication(self):
|
|
557
|
+
"""Test update of named credential with authentication"""
|
|
558
|
+
task = create_task(
|
|
559
|
+
UpdateNamedCredential,
|
|
560
|
+
{
|
|
561
|
+
"name": "testNc",
|
|
562
|
+
"parameters": [{"authentication": "MyExternalCredential"}],
|
|
563
|
+
},
|
|
564
|
+
)
|
|
565
|
+
|
|
566
|
+
nc_id = "0XA1234567890ABC"
|
|
567
|
+
tooling_url = f"https://test.salesforce.com/services/data/v{CURRENT_SF_API_VERSION}/tooling"
|
|
568
|
+
|
|
569
|
+
# Mock query for named credential ID
|
|
570
|
+
responses.add(
|
|
571
|
+
method="GET",
|
|
572
|
+
url=f"{tooling_url}/query/?q=SELECT+Id+FROM+NamedCredential+WHERE+DeveloperName%3D%27testNc%27+LIMIT+1",
|
|
573
|
+
json={"size": 1, "records": [{"Id": nc_id}]},
|
|
574
|
+
status=200,
|
|
575
|
+
)
|
|
576
|
+
|
|
577
|
+
# Mock get named credential object
|
|
578
|
+
responses.add(
|
|
579
|
+
method="GET",
|
|
580
|
+
url=f"{tooling_url}/sobjects/NamedCredential/{nc_id}",
|
|
581
|
+
json={
|
|
582
|
+
"Metadata": {
|
|
583
|
+
"namedCredentialType": "SecuredEndpoint",
|
|
584
|
+
"namedCredentialParameters": [
|
|
585
|
+
{
|
|
586
|
+
"parameterName": None,
|
|
587
|
+
"parameterType": "Url",
|
|
588
|
+
"parameterValue": "https://api.example.com",
|
|
589
|
+
}
|
|
590
|
+
],
|
|
591
|
+
}
|
|
592
|
+
},
|
|
593
|
+
status=200,
|
|
594
|
+
)
|
|
595
|
+
|
|
596
|
+
# Mock update named credential
|
|
597
|
+
responses.add(
|
|
598
|
+
method="PATCH",
|
|
599
|
+
url=f"{tooling_url}/sobjects/NamedCredential/{nc_id}",
|
|
600
|
+
json={},
|
|
601
|
+
status=200,
|
|
602
|
+
)
|
|
603
|
+
|
|
604
|
+
task()
|
|
605
|
+
assert len(responses.calls) == 3
|
|
606
|
+
|
|
607
|
+
@responses.activate
|
|
608
|
+
def test_update_named_credential_with_certificate(self):
|
|
609
|
+
"""Test update of named credential with certificate"""
|
|
610
|
+
task = create_task(
|
|
611
|
+
UpdateNamedCredential,
|
|
612
|
+
{"name": "testNc", "parameters": [{"certificate": "MyCertificate"}]},
|
|
613
|
+
)
|
|
614
|
+
|
|
615
|
+
nc_id = "0XA1234567890ABC"
|
|
616
|
+
tooling_url = f"https://test.salesforce.com/services/data/v{CURRENT_SF_API_VERSION}/tooling"
|
|
617
|
+
|
|
618
|
+
# Mock query for named credential ID
|
|
619
|
+
responses.add(
|
|
620
|
+
method="GET",
|
|
621
|
+
url=f"{tooling_url}/query/?q=SELECT+Id+FROM+NamedCredential+WHERE+DeveloperName%3D%27testNc%27+LIMIT+1",
|
|
622
|
+
json={"size": 1, "records": [{"Id": nc_id}]},
|
|
623
|
+
status=200,
|
|
624
|
+
)
|
|
625
|
+
|
|
626
|
+
# Mock get named credential object
|
|
627
|
+
responses.add(
|
|
628
|
+
method="GET",
|
|
629
|
+
url=f"{tooling_url}/sobjects/NamedCredential/{nc_id}",
|
|
630
|
+
json={
|
|
631
|
+
"Metadata": {
|
|
632
|
+
"namedCredentialType": "SecuredEndpoint",
|
|
633
|
+
"namedCredentialParameters": [
|
|
634
|
+
{
|
|
635
|
+
"parameterName": None,
|
|
636
|
+
"parameterType": "Url",
|
|
637
|
+
"parameterValue": "https://api.example.com",
|
|
638
|
+
}
|
|
639
|
+
],
|
|
640
|
+
}
|
|
641
|
+
},
|
|
642
|
+
status=200,
|
|
643
|
+
)
|
|
644
|
+
|
|
645
|
+
# Mock update named credential
|
|
646
|
+
responses.add(
|
|
647
|
+
method="PATCH",
|
|
648
|
+
url=f"{tooling_url}/sobjects/NamedCredential/{nc_id}",
|
|
649
|
+
json={},
|
|
650
|
+
status=200,
|
|
651
|
+
)
|
|
652
|
+
|
|
653
|
+
task()
|
|
654
|
+
assert len(responses.calls) == 3
|
|
655
|
+
|
|
656
|
+
@responses.activate
|
|
657
|
+
def test_update_named_credential_with_transform_parameters(self):
|
|
658
|
+
"""Test update of named credential with transform parameters from environment"""
|
|
659
|
+
with mock.patch.dict(
|
|
660
|
+
os.environ,
|
|
661
|
+
{
|
|
662
|
+
"TEST_URL": "https://env.example.com",
|
|
663
|
+
"TEST_AUTH": "EnvAuth",
|
|
664
|
+
"HEADER_VALUE": "env-header-value",
|
|
665
|
+
},
|
|
666
|
+
):
|
|
667
|
+
task = create_task(
|
|
668
|
+
UpdateNamedCredential,
|
|
669
|
+
{
|
|
670
|
+
"name": "testNc",
|
|
671
|
+
"transform_parameters": [
|
|
672
|
+
{"url": "TEST_URL"},
|
|
673
|
+
{"authentication": "TEST_AUTH"},
|
|
674
|
+
{
|
|
675
|
+
"http_header": [
|
|
676
|
+
{
|
|
677
|
+
"name": "x-api-key",
|
|
678
|
+
"value": "HEADER_VALUE",
|
|
679
|
+
"secret": True,
|
|
680
|
+
}
|
|
681
|
+
]
|
|
682
|
+
},
|
|
683
|
+
],
|
|
684
|
+
},
|
|
685
|
+
)
|
|
686
|
+
|
|
687
|
+
nc_id = "0XA1234567890ABC"
|
|
688
|
+
tooling_url = f"https://test.salesforce.com/services/data/v{CURRENT_SF_API_VERSION}/tooling"
|
|
689
|
+
|
|
690
|
+
# Mock query for named credential ID
|
|
691
|
+
responses.add(
|
|
692
|
+
method="GET",
|
|
693
|
+
url=f"{tooling_url}/query/?q=SELECT+Id+FROM+NamedCredential+WHERE+DeveloperName%3D%27testNc%27+LIMIT+1",
|
|
694
|
+
json={"size": 1, "records": [{"Id": nc_id}]},
|
|
695
|
+
status=200,
|
|
696
|
+
)
|
|
697
|
+
|
|
698
|
+
# Mock get named credential object
|
|
699
|
+
responses.add(
|
|
700
|
+
method="GET",
|
|
701
|
+
url=f"{tooling_url}/sobjects/NamedCredential/{nc_id}",
|
|
702
|
+
json={
|
|
703
|
+
"Metadata": {
|
|
704
|
+
"namedCredentialType": "SecuredEndpoint",
|
|
705
|
+
"namedCredentialParameters": [
|
|
706
|
+
{
|
|
707
|
+
"parameterName": None,
|
|
708
|
+
"parameterType": "Url",
|
|
709
|
+
"parameterValue": "https://old.example.com",
|
|
710
|
+
"certificate": None,
|
|
711
|
+
"description": None,
|
|
712
|
+
"externalCredential": None,
|
|
713
|
+
"sequenceNumber": None,
|
|
714
|
+
}
|
|
715
|
+
],
|
|
716
|
+
}
|
|
717
|
+
},
|
|
718
|
+
status=200,
|
|
719
|
+
)
|
|
720
|
+
|
|
721
|
+
# Mock update named credential
|
|
722
|
+
responses.add(
|
|
723
|
+
method="PATCH",
|
|
724
|
+
url=f"{tooling_url}/sobjects/NamedCredential/{nc_id}",
|
|
725
|
+
json={},
|
|
726
|
+
status=200,
|
|
727
|
+
)
|
|
728
|
+
|
|
729
|
+
task()
|
|
730
|
+
assert len(responses.calls) == 3
|
|
731
|
+
|
|
732
|
+
@responses.activate
|
|
733
|
+
def test_update_named_credential_with_callout_options(self):
|
|
734
|
+
"""Test update of named credential with callout options"""
|
|
735
|
+
task = create_task(
|
|
736
|
+
UpdateNamedCredential,
|
|
737
|
+
{
|
|
738
|
+
"name": "testNc",
|
|
739
|
+
"callout_options": {
|
|
740
|
+
"allow_merge_fields_in_body": True,
|
|
741
|
+
"allow_merge_fields_in_header": False,
|
|
742
|
+
"generate_authorization_header": True,
|
|
743
|
+
},
|
|
744
|
+
"parameters": [{"url": "https://api.example.com"}],
|
|
745
|
+
},
|
|
746
|
+
)
|
|
747
|
+
|
|
748
|
+
nc_id = "0XA1234567890ABC"
|
|
749
|
+
tooling_url = f"https://test.salesforce.com/services/data/v{CURRENT_SF_API_VERSION}/tooling"
|
|
750
|
+
|
|
751
|
+
# Mock query for named credential ID
|
|
752
|
+
responses.add(
|
|
753
|
+
method="GET",
|
|
754
|
+
url=f"{tooling_url}/query/?q=SELECT+Id+FROM+NamedCredential+WHERE+DeveloperName%3D%27testNc%27+LIMIT+1",
|
|
755
|
+
json={"size": 1, "records": [{"Id": nc_id}]},
|
|
756
|
+
status=200,
|
|
757
|
+
)
|
|
758
|
+
|
|
759
|
+
# Mock get named credential object
|
|
760
|
+
responses.add(
|
|
761
|
+
method="GET",
|
|
762
|
+
url=f"{tooling_url}/sobjects/NamedCredential/{nc_id}",
|
|
763
|
+
json={
|
|
764
|
+
"Metadata": {
|
|
765
|
+
"namedCredentialType": "SecuredEndpoint",
|
|
766
|
+
"namedCredentialParameters": [
|
|
767
|
+
{
|
|
768
|
+
"parameterName": None,
|
|
769
|
+
"parameterType": "Url",
|
|
770
|
+
"parameterValue": "https://old.example.com",
|
|
771
|
+
}
|
|
772
|
+
],
|
|
773
|
+
}
|
|
774
|
+
},
|
|
775
|
+
status=200,
|
|
776
|
+
)
|
|
777
|
+
|
|
778
|
+
# Mock update named credential
|
|
779
|
+
responses.add(
|
|
780
|
+
method="PATCH",
|
|
781
|
+
url=f"{tooling_url}/sobjects/NamedCredential/{nc_id}",
|
|
782
|
+
json={},
|
|
783
|
+
status=200,
|
|
784
|
+
)
|
|
785
|
+
|
|
786
|
+
task()
|
|
787
|
+
assert len(responses.calls) == 3
|
|
788
|
+
|
|
789
|
+
@responses.activate
|
|
790
|
+
def test_update_named_credential_no_template_parameter(self):
|
|
791
|
+
"""Test update when no template parameter exists"""
|
|
792
|
+
task = create_task(
|
|
793
|
+
UpdateNamedCredential,
|
|
794
|
+
{"name": "testNc", "parameters": [{"url": "https://api.example.com"}]},
|
|
795
|
+
)
|
|
796
|
+
|
|
797
|
+
nc_id = "0XA1234567890ABC"
|
|
798
|
+
tooling_url = f"https://test.salesforce.com/services/data/v{CURRENT_SF_API_VERSION}/tooling"
|
|
799
|
+
|
|
800
|
+
# Mock query for named credential ID
|
|
801
|
+
responses.add(
|
|
802
|
+
method="GET",
|
|
803
|
+
url=f"{tooling_url}/query/?q=SELECT+Id+FROM+NamedCredential+WHERE+DeveloperName%3D%27testNc%27+LIMIT+1",
|
|
804
|
+
json={"size": 1, "records": [{"Id": nc_id}]},
|
|
805
|
+
status=200,
|
|
806
|
+
)
|
|
807
|
+
|
|
808
|
+
# Mock get named credential object without Url parameter
|
|
809
|
+
responses.add(
|
|
810
|
+
method="GET",
|
|
811
|
+
url=f"{tooling_url}/sobjects/NamedCredential/{nc_id}",
|
|
812
|
+
json={
|
|
813
|
+
"Metadata": {
|
|
814
|
+
"namedCredentialType": "SecuredEndpoint",
|
|
815
|
+
"namedCredentialParameters": [],
|
|
816
|
+
}
|
|
817
|
+
},
|
|
818
|
+
status=200,
|
|
819
|
+
)
|
|
820
|
+
|
|
821
|
+
# Mock update named credential
|
|
822
|
+
responses.add(
|
|
823
|
+
method="PATCH",
|
|
824
|
+
url=f"{tooling_url}/sobjects/NamedCredential/{nc_id}",
|
|
825
|
+
json={},
|
|
826
|
+
status=200,
|
|
827
|
+
)
|
|
828
|
+
|
|
829
|
+
task()
|
|
830
|
+
assert len(responses.calls) == 3
|
|
831
|
+
|
|
832
|
+
@responses.activate
|
|
833
|
+
def test_update_named_credential_update_existing_parameter(self):
|
|
834
|
+
"""Test updating an existing parameter"""
|
|
835
|
+
task = create_task(
|
|
836
|
+
UpdateNamedCredential,
|
|
837
|
+
{"name": "testNc", "parameters": [{"url": "https://new.example.com"}]},
|
|
838
|
+
)
|
|
839
|
+
|
|
840
|
+
nc_id = "0XA1234567890ABC"
|
|
841
|
+
tooling_url = f"https://test.salesforce.com/services/data/v{CURRENT_SF_API_VERSION}/tooling"
|
|
842
|
+
|
|
843
|
+
# Mock query for named credential ID
|
|
844
|
+
responses.add(
|
|
845
|
+
method="GET",
|
|
846
|
+
url=f"{tooling_url}/query/?q=SELECT+Id+FROM+NamedCredential+WHERE+DeveloperName%3D%27testNc%27+LIMIT+1",
|
|
847
|
+
json={"size": 1, "records": [{"Id": nc_id}]},
|
|
848
|
+
status=200,
|
|
849
|
+
)
|
|
850
|
+
|
|
851
|
+
# Mock get named credential object with existing Url parameter
|
|
852
|
+
responses.add(
|
|
853
|
+
method="GET",
|
|
854
|
+
url=f"{tooling_url}/sobjects/NamedCredential/{nc_id}",
|
|
855
|
+
json={
|
|
856
|
+
"Metadata": {
|
|
857
|
+
"namedCredentialType": "SecuredEndpoint",
|
|
858
|
+
"namedCredentialParameters": [
|
|
859
|
+
{
|
|
860
|
+
"parameterName": None,
|
|
861
|
+
"parameterType": "Url",
|
|
862
|
+
"parameterValue": "https://old.example.com",
|
|
863
|
+
}
|
|
864
|
+
],
|
|
865
|
+
}
|
|
866
|
+
},
|
|
867
|
+
status=200,
|
|
868
|
+
)
|
|
869
|
+
|
|
870
|
+
# Mock update named credential
|
|
871
|
+
responses.add(
|
|
872
|
+
method="PATCH",
|
|
873
|
+
url=f"{tooling_url}/sobjects/NamedCredential/{nc_id}",
|
|
874
|
+
json={},
|
|
875
|
+
status=200,
|
|
876
|
+
)
|
|
877
|
+
|
|
878
|
+
task()
|
|
879
|
+
assert len(responses.calls) == 3
|
|
880
|
+
|
|
881
|
+
@responses.activate
|
|
882
|
+
def test_update_named_credential_with_allowed_namespaces(self):
|
|
883
|
+
"""Test update of named credential with allowed managed package namespaces"""
|
|
884
|
+
task = create_task(
|
|
885
|
+
UpdateNamedCredential,
|
|
886
|
+
{
|
|
887
|
+
"name": "testNc",
|
|
888
|
+
"parameters": [{"allowed_managed_package_namespaces": "th_dev"}],
|
|
889
|
+
},
|
|
890
|
+
)
|
|
891
|
+
|
|
892
|
+
nc_id = "0XA1234567890ABC"
|
|
893
|
+
tooling_url = f"https://test.salesforce.com/services/data/v{CURRENT_SF_API_VERSION}/tooling"
|
|
894
|
+
|
|
895
|
+
# Mock query for named credential ID
|
|
896
|
+
responses.add(
|
|
897
|
+
method="GET",
|
|
898
|
+
url=f"{tooling_url}/query/?q=SELECT+Id+FROM+NamedCredential+WHERE+DeveloperName%3D%27testNc%27+LIMIT+1",
|
|
899
|
+
json={"size": 1, "records": [{"Id": nc_id}]},
|
|
900
|
+
status=200,
|
|
901
|
+
)
|
|
902
|
+
|
|
903
|
+
# Mock get named credential object
|
|
904
|
+
responses.add(
|
|
905
|
+
method="GET",
|
|
906
|
+
url=f"{tooling_url}/sobjects/NamedCredential/{nc_id}",
|
|
907
|
+
json={
|
|
908
|
+
"Metadata": {
|
|
909
|
+
"namedCredentialType": "SecuredEndpoint",
|
|
910
|
+
"namedCredentialParameters": [
|
|
911
|
+
{
|
|
912
|
+
"parameterName": None,
|
|
913
|
+
"parameterType": "Url",
|
|
914
|
+
"parameterValue": "https://api.example.com",
|
|
915
|
+
}
|
|
916
|
+
],
|
|
917
|
+
}
|
|
918
|
+
},
|
|
919
|
+
status=200,
|
|
920
|
+
)
|
|
921
|
+
|
|
922
|
+
# Mock update named credential
|
|
923
|
+
responses.add(
|
|
924
|
+
method="PATCH",
|
|
925
|
+
url=f"{tooling_url}/sobjects/NamedCredential/{nc_id}",
|
|
926
|
+
json={},
|
|
927
|
+
status=200,
|
|
928
|
+
)
|
|
929
|
+
|
|
930
|
+
task()
|
|
931
|
+
assert len(responses.calls) == 3
|
|
932
|
+
|
|
933
|
+
@responses.activate
|
|
934
|
+
def test_update_named_credential_update_http_header_existing(self):
|
|
935
|
+
"""Test updating existing HTTP header parameter"""
|
|
936
|
+
task = create_task(
|
|
937
|
+
UpdateNamedCredential,
|
|
938
|
+
{
|
|
939
|
+
"name": "testNc",
|
|
940
|
+
"parameters": [
|
|
941
|
+
{
|
|
942
|
+
"http_header": [
|
|
943
|
+
{"name": "x-api-key", "value": "new-value", "secret": True}
|
|
944
|
+
]
|
|
945
|
+
}
|
|
946
|
+
],
|
|
947
|
+
},
|
|
948
|
+
)
|
|
949
|
+
|
|
950
|
+
nc_id = "0XA1234567890ABC"
|
|
951
|
+
tooling_url = f"https://test.salesforce.com/services/data/v{CURRENT_SF_API_VERSION}/tooling"
|
|
952
|
+
|
|
953
|
+
# Mock query for named credential ID
|
|
954
|
+
responses.add(
|
|
955
|
+
method="GET",
|
|
956
|
+
url=f"{tooling_url}/query/?q=SELECT+Id+FROM+NamedCredential+WHERE+DeveloperName%3D%27testNc%27+LIMIT+1",
|
|
957
|
+
json={"size": 1, "records": [{"Id": nc_id}]},
|
|
958
|
+
status=200,
|
|
959
|
+
)
|
|
960
|
+
|
|
961
|
+
# Mock get named credential object with existing HTTP header
|
|
962
|
+
responses.add(
|
|
963
|
+
method="GET",
|
|
964
|
+
url=f"{tooling_url}/sobjects/NamedCredential/{nc_id}",
|
|
965
|
+
json={
|
|
966
|
+
"Metadata": {
|
|
967
|
+
"namedCredentialType": "SecuredEndpoint",
|
|
968
|
+
"namedCredentialParameters": [
|
|
969
|
+
{
|
|
970
|
+
"parameterName": None,
|
|
971
|
+
"parameterType": "Url",
|
|
972
|
+
"parameterValue": "https://api.example.com",
|
|
973
|
+
},
|
|
974
|
+
{
|
|
975
|
+
"parameterName": "x-api-key",
|
|
976
|
+
"parameterType": "HttpHeader",
|
|
977
|
+
"parameterValue": "old-value",
|
|
978
|
+
},
|
|
979
|
+
],
|
|
980
|
+
}
|
|
981
|
+
},
|
|
982
|
+
status=200,
|
|
983
|
+
)
|
|
984
|
+
|
|
985
|
+
# Mock update named credential
|
|
986
|
+
responses.add(
|
|
987
|
+
method="PATCH",
|
|
988
|
+
url=f"{tooling_url}/sobjects/NamedCredential/{nc_id}",
|
|
989
|
+
json={},
|
|
990
|
+
status=200,
|
|
991
|
+
)
|
|
992
|
+
|
|
993
|
+
task()
|
|
994
|
+
assert len(responses.calls) == 3
|
|
995
|
+
|
|
996
|
+
@responses.activate
|
|
997
|
+
def test_update_named_credential_exception_in_update(self):
|
|
998
|
+
"""Test exception handling during update"""
|
|
999
|
+
task = create_task(
|
|
1000
|
+
UpdateNamedCredential,
|
|
1001
|
+
{"name": "testNc", "parameters": [{"url": "https://api.example.com"}]},
|
|
1002
|
+
)
|
|
1003
|
+
|
|
1004
|
+
nc_id = "0XA1234567890ABC"
|
|
1005
|
+
tooling_url = f"https://test.salesforce.com/services/data/v{CURRENT_SF_API_VERSION}/tooling"
|
|
1006
|
+
|
|
1007
|
+
# Mock query for named credential ID
|
|
1008
|
+
responses.add(
|
|
1009
|
+
method="GET",
|
|
1010
|
+
url=f"{tooling_url}/query/?q=SELECT+Id+FROM+NamedCredential+WHERE+DeveloperName%3D%27testNc%27+LIMIT+1",
|
|
1011
|
+
json={"size": 1, "records": [{"Id": nc_id}]},
|
|
1012
|
+
status=200,
|
|
1013
|
+
)
|
|
1014
|
+
|
|
1015
|
+
# Mock get named credential object
|
|
1016
|
+
responses.add(
|
|
1017
|
+
method="GET",
|
|
1018
|
+
url=f"{tooling_url}/sobjects/NamedCredential/{nc_id}",
|
|
1019
|
+
json={
|
|
1020
|
+
"Metadata": {
|
|
1021
|
+
"namedCredentialType": "SecuredEndpoint",
|
|
1022
|
+
"namedCredentialParameters": [
|
|
1023
|
+
{
|
|
1024
|
+
"parameterName": None,
|
|
1025
|
+
"parameterType": "Url",
|
|
1026
|
+
"parameterValue": "https://old.example.com",
|
|
1027
|
+
}
|
|
1028
|
+
],
|
|
1029
|
+
}
|
|
1030
|
+
},
|
|
1031
|
+
status=200,
|
|
1032
|
+
)
|
|
1033
|
+
|
|
1034
|
+
# Mock update named credential - exception
|
|
1035
|
+
responses.add(
|
|
1036
|
+
method="PATCH",
|
|
1037
|
+
url=f"{tooling_url}/sobjects/NamedCredential/{nc_id}",
|
|
1038
|
+
body=Exception("Connection error"),
|
|
1039
|
+
)
|
|
1040
|
+
|
|
1041
|
+
with pytest.raises(Exception): # Can be TypeError or SalesforceDXException
|
|
1042
|
+
task()
|