cumulusci-plus 5.0.35__py3-none-any.whl → 5.0.45__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.
Files changed (39) hide show
  1. cumulusci/__about__.py +1 -1
  2. cumulusci/cli/cci.py +3 -2
  3. cumulusci/cli/task.py +9 -10
  4. cumulusci/cli/tests/test_org.py +5 -0
  5. cumulusci/cli/tests/test_task.py +34 -0
  6. cumulusci/core/config/__init__.py +1 -0
  7. cumulusci/core/config/org_config.py +2 -1
  8. cumulusci/core/config/project_config.py +12 -0
  9. cumulusci/core/config/scratch_org_config.py +12 -0
  10. cumulusci/core/config/sfdx_org_config.py +4 -1
  11. cumulusci/core/config/tests/test_config.py +1 -0
  12. cumulusci/core/dependencies/base.py +4 -0
  13. cumulusci/cumulusci.yml +18 -1
  14. cumulusci/schema/cumulusci.jsonschema.json +5 -0
  15. cumulusci/tasks/apex/testrunner.py +7 -4
  16. cumulusci/tasks/bulkdata/tests/test_select_utils.py +20 -0
  17. cumulusci/tasks/metadata_etl/__init__.py +2 -0
  18. cumulusci/tasks/metadata_etl/applications.py +256 -0
  19. cumulusci/tasks/metadata_etl/tests/test_applications.py +710 -0
  20. cumulusci/tasks/salesforce/insert_record.py +18 -19
  21. cumulusci/tasks/salesforce/tests/test_enable_prediction.py +4 -2
  22. cumulusci/tasks/salesforce/tests/test_update_external_auth_identity_provider.py +927 -0
  23. cumulusci/tasks/salesforce/tests/test_update_external_credential.py +523 -8
  24. cumulusci/tasks/salesforce/tests/test_update_record.py +512 -0
  25. cumulusci/tasks/salesforce/update_external_auth_identity_provider.py +551 -0
  26. cumulusci/tasks/salesforce/update_external_credential.py +89 -4
  27. cumulusci/tasks/salesforce/update_record.py +217 -0
  28. cumulusci/tasks/sfdmu/sfdmu.py +14 -1
  29. cumulusci/tasks/utility/credentialManager.py +58 -12
  30. cumulusci/tasks/utility/secretsToEnv.py +42 -11
  31. cumulusci/tasks/utility/tests/test_credentialManager.py +586 -0
  32. cumulusci/tasks/utility/tests/test_secretsToEnv.py +1240 -62
  33. cumulusci/utils/yaml/cumulusci_yml.py +1 -0
  34. {cumulusci_plus-5.0.35.dist-info → cumulusci_plus-5.0.45.dist-info}/METADATA +5 -7
  35. {cumulusci_plus-5.0.35.dist-info → cumulusci_plus-5.0.45.dist-info}/RECORD +39 -33
  36. {cumulusci_plus-5.0.35.dist-info → cumulusci_plus-5.0.45.dist-info}/WHEEL +1 -1
  37. {cumulusci_plus-5.0.35.dist-info → cumulusci_plus-5.0.45.dist-info}/entry_points.txt +0 -0
  38. {cumulusci_plus-5.0.35.dist-info → cumulusci_plus-5.0.45.dist-info}/licenses/AUTHORS.rst +0 -0
  39. {cumulusci_plus-5.0.35.dist-info → cumulusci_plus-5.0.45.dist-info}/licenses/LICENSE +0 -0
@@ -109,7 +109,7 @@ class TestExternalCredentialParameter:
109
109
  assert param.auth_provider == "MyAuthProvider"
110
110
  result = param.get_external_credential_parameter()
111
111
  assert result["parameterType"] == "AuthProvider"
112
- assert result["parameterValue"] == "MyAuthProvider"
112
+ assert result["authProvider"] == "MyAuthProvider"
113
113
  assert result["parameterName"] == "AuthProvider"
114
114
 
115
115
  def test_parameter_with_auth_provider_url(self):
@@ -231,7 +231,9 @@ class TestTransformExternalCredentialParameter:
231
231
  auth_provider="MY_AUTH_PROVIDER"
232
232
  )
233
233
  result = param.get_external_credential_parameter()
234
- assert result["parameterValue"] == "EnvAuthProvider"
234
+ assert (
235
+ result["authProvider"] == "MY_AUTH_PROVIDER"
236
+ ) # Transform doesn't apply to authProvider field
235
237
 
236
238
  def test_transform_parameter_missing_env(self):
237
239
  """Test parameter transformation with missing environment variable"""
@@ -507,6 +509,23 @@ class TestUpdateExternalCredential:
507
509
  @responses.activate
508
510
  def test_update_external_credential_with_namespace(self):
509
511
  """Test update of external credential with namespace"""
512
+ # Mock SF API version discovery
513
+ responses.add(
514
+ method="GET",
515
+ url="https://test.salesforce.com/services/data",
516
+ json=[{"version": CURRENT_SF_API_VERSION}],
517
+ status=200,
518
+ )
519
+
520
+ # Mock installed packages query
521
+ tooling_url = f"https://test.salesforce.com/services/data/v{CURRENT_SF_API_VERSION}/tooling"
522
+ responses.add(
523
+ method="GET",
524
+ url=f"{tooling_url}/query/?q=SELECT+SubscriberPackage.Id%2C+SubscriberPackage.Name%2C+SubscriberPackage.NamespacePrefix%2C+SubscriberPackageVersionId+FROM+InstalledSubscriberPackage",
525
+ json={"size": 0, "records": []},
526
+ status=200,
527
+ )
528
+
510
529
  task = create_task(
511
530
  UpdateExternalCredential,
512
531
  {
@@ -517,7 +536,6 @@ class TestUpdateExternalCredential:
517
536
  )
518
537
 
519
538
  ext_cred_id = "0XE1234567890ABC"
520
- tooling_url = f"https://test.salesforce.com/services/data/v{CURRENT_SF_API_VERSION}/tooling"
521
539
 
522
540
  # Mock query for external credential ID
523
541
  responses.add(
@@ -556,7 +574,7 @@ class TestUpdateExternalCredential:
556
574
  )
557
575
 
558
576
  task()
559
- assert len(responses.calls) == 3
577
+ assert len(responses.calls) == 5 # 2 setup + 3 task calls
560
578
 
561
579
  @responses.activate
562
580
  def test_update_external_credential_add_new_parameter(self):
@@ -758,6 +776,23 @@ class TestUpdateExternalCredential:
758
776
  @responses.activate
759
777
  def test_update_external_credential_no_existing_parameters(self):
760
778
  """Test update when external credential has no existing parameters"""
779
+ # Mock SF API version discovery
780
+ responses.add(
781
+ method="GET",
782
+ url="https://test.salesforce.com/services/data",
783
+ json=[{"version": CURRENT_SF_API_VERSION}],
784
+ status=200,
785
+ )
786
+
787
+ # Mock installed packages query
788
+ tooling_url = f"https://test.salesforce.com/services/data/v{CURRENT_SF_API_VERSION}/tooling"
789
+ responses.add(
790
+ method="GET",
791
+ url=f"{tooling_url}/query/?q=SELECT+SubscriberPackage.Id%2C+SubscriberPackage.Name%2C+SubscriberPackage.NamespacePrefix%2C+SubscriberPackageVersionId+FROM+InstalledSubscriberPackage",
792
+ json={"size": 0, "records": []},
793
+ status=200,
794
+ )
795
+
761
796
  task = create_task(
762
797
  UpdateExternalCredential,
763
798
  {
@@ -767,7 +802,6 @@ class TestUpdateExternalCredential:
767
802
  )
768
803
 
769
804
  ext_cred_id = "0XE1234567890ABC"
770
- tooling_url = f"https://test.salesforce.com/services/data/v{CURRENT_SF_API_VERSION}/tooling"
771
805
 
772
806
  # Mock query for external credential ID
773
807
  responses.add(
@@ -794,11 +828,28 @@ class TestUpdateExternalCredential:
794
828
  )
795
829
 
796
830
  task()
797
- assert len(responses.calls) == 3
831
+ assert len(responses.calls) == 5 # 2 setup + 3 task calls
798
832
 
799
833
  @responses.activate
800
834
  def test_update_external_credential_with_multiple_parameters(self):
801
835
  """Test update with multiple parameters"""
836
+ # Mock SF API version discovery
837
+ responses.add(
838
+ method="GET",
839
+ url="https://test.salesforce.com/services/data",
840
+ json=[{"version": CURRENT_SF_API_VERSION}],
841
+ status=200,
842
+ )
843
+
844
+ # Mock installed packages query
845
+ tooling_url = f"https://test.salesforce.com/services/data/v{CURRENT_SF_API_VERSION}/tooling"
846
+ responses.add(
847
+ method="GET",
848
+ url=f"{tooling_url}/query/?q=SELECT+SubscriberPackage.Id%2C+SubscriberPackage.Name%2C+SubscriberPackage.NamespacePrefix%2C+SubscriberPackageVersionId+FROM+InstalledSubscriberPackage",
849
+ json={"size": 0, "records": []},
850
+ status=200,
851
+ )
852
+
802
853
  auth_header = HttpHeader(name="Authorization", value="Bearer token123")
803
854
  jwt_claim = ExtParameter(name="sub", value='{"sub":"user"}')
804
855
  task = create_task(
@@ -814,7 +865,6 @@ class TestUpdateExternalCredential:
814
865
  )
815
866
 
816
867
  ext_cred_id = "0XE1234567890ABC"
817
- tooling_url = f"https://test.salesforce.com/services/data/v{CURRENT_SF_API_VERSION}/tooling"
818
868
 
819
869
  # Mock query for external credential ID
820
870
  responses.add(
@@ -853,7 +903,7 @@ class TestUpdateExternalCredential:
853
903
  )
854
904
 
855
905
  task()
856
- assert len(responses.calls) == 3
906
+ assert len(responses.calls) == 5 # 2 setup + 3 task calls
857
907
 
858
908
  @responses.activate
859
909
  def test_update_external_credential_with_sequence_number(self):
@@ -910,3 +960,468 @@ class TestUpdateExternalCredential:
910
960
 
911
961
  task()
912
962
  assert len(responses.calls) == 3
963
+
964
+ @responses.activate
965
+ def test_auth_provider_removes_external_auth_identity_provider(self):
966
+ """Test that adding AuthProvider removes ExternalAuthIdentityProvider"""
967
+ # Mock SF API version discovery
968
+ responses.add(
969
+ method="GET",
970
+ url="https://test.salesforce.com/services/data",
971
+ json=[{"version": CURRENT_SF_API_VERSION}],
972
+ status=200,
973
+ )
974
+
975
+ # Mock installed packages query
976
+ tooling_url = f"https://test.salesforce.com/services/data/v{CURRENT_SF_API_VERSION}/tooling"
977
+ responses.add(
978
+ method="GET",
979
+ url=f"{tooling_url}/query/?q=SELECT+SubscriberPackage.Id%2C+SubscriberPackage.Name%2C+SubscriberPackage.NamespacePrefix%2C+SubscriberPackageVersionId+FROM+InstalledSubscriberPackage",
980
+ json={"size": 0, "records": []},
981
+ status=200,
982
+ )
983
+
984
+ task = create_task(
985
+ UpdateExternalCredential,
986
+ {
987
+ "name": "testExtCred",
988
+ "parameters": [{"auth_provider": "MyAuthProvider"}],
989
+ },
990
+ )
991
+
992
+ ext_cred_id = "0XE1234567890ABC"
993
+
994
+ # Mock query for external credential ID
995
+ responses.add(
996
+ method="GET",
997
+ url=f"{tooling_url}/query/?q=SELECT+Id+FROM+ExternalCredential+WHERE+DeveloperName%3D%27testExtCred%27+LIMIT+1",
998
+ json={"size": 1, "records": [{"Id": ext_cred_id}]},
999
+ status=200,
1000
+ )
1001
+
1002
+ # Mock get external credential object with ExternalAuthIdentityProvider
1003
+ responses.add(
1004
+ method="GET",
1005
+ url=f"{tooling_url}/sobjects/ExternalCredential/{ext_cred_id}",
1006
+ json={
1007
+ "Metadata": {
1008
+ "externalCredentialParameters": [
1009
+ {
1010
+ "parameterType": "ExternalAuthIdentityProvider",
1011
+ "parameterName": "ExternalAuthIdentityProvider",
1012
+ "externalAuthIdentityProvider": "OldProvider",
1013
+ "authProvider": None,
1014
+ "certificate": None,
1015
+ "description": None,
1016
+ "parameterGroup": "DefaultGroup",
1017
+ "parameterValue": None,
1018
+ "sequenceNumber": None,
1019
+ },
1020
+ {
1021
+ "parameterType": "AuthParameter",
1022
+ "parameterName": "Scope",
1023
+ "parameterValue": "scope",
1024
+ "authProvider": None,
1025
+ "certificate": None,
1026
+ "description": None,
1027
+ "externalAuthIdentityProvider": None,
1028
+ "parameterGroup": "DefaultGroup",
1029
+ "sequenceNumber": None,
1030
+ },
1031
+ ],
1032
+ }
1033
+ },
1034
+ status=200,
1035
+ )
1036
+
1037
+ # Mock update external credential
1038
+ responses.add(
1039
+ method="PATCH",
1040
+ url=f"{tooling_url}/sobjects/ExternalCredential/{ext_cred_id}",
1041
+ json={},
1042
+ status=200,
1043
+ )
1044
+
1045
+ task()
1046
+ assert len(responses.calls) == 5 # 2 setup + 3 task calls
1047
+
1048
+ # Verify the PATCH request removed ExternalAuthIdentityProvider
1049
+ patch_call = responses.calls[4] # Last call is the PATCH
1050
+ updated_params = patch_call.request.body
1051
+ import json
1052
+
1053
+ body = json.loads(updated_params)
1054
+ params = body["Metadata"]["externalCredentialParameters"]
1055
+
1056
+ # Should not contain ExternalAuthIdentityProvider
1057
+ assert not any(
1058
+ p.get("parameterType") == "ExternalAuthIdentityProvider" for p in params
1059
+ )
1060
+ # Should contain AuthProvider
1061
+ assert any(p.get("parameterType") == "AuthProvider" for p in params)
1062
+ # Should still contain AuthParameter
1063
+ assert any(p.get("parameterType") == "AuthParameter" for p in params)
1064
+
1065
+ @responses.activate
1066
+ def test_external_auth_identity_provider_removes_auth_provider(self):
1067
+ """Test that adding ExternalAuthIdentityProvider removes AuthProvider"""
1068
+ # Mock SF API version discovery
1069
+ responses.add(
1070
+ method="GET",
1071
+ url="https://test.salesforce.com/services/data",
1072
+ json=[{"version": CURRENT_SF_API_VERSION}],
1073
+ status=200,
1074
+ )
1075
+
1076
+ # Mock installed packages query
1077
+ tooling_url = f"https://test.salesforce.com/services/data/v{CURRENT_SF_API_VERSION}/tooling"
1078
+ responses.add(
1079
+ method="GET",
1080
+ url=f"{tooling_url}/query/?q=SELECT+SubscriberPackage.Id%2C+SubscriberPackage.Name%2C+SubscriberPackage.NamespacePrefix%2C+SubscriberPackageVersionId+FROM+InstalledSubscriberPackage",
1081
+ json={"size": 0, "records": []},
1082
+ status=200,
1083
+ )
1084
+
1085
+ task = create_task(
1086
+ UpdateExternalCredential,
1087
+ {
1088
+ "name": "testExtCred",
1089
+ "parameters": [
1090
+ {"external_auth_identity_provider": "MyExternalAuthProvider"}
1091
+ ],
1092
+ },
1093
+ )
1094
+
1095
+ ext_cred_id = "0XE1234567890ABC"
1096
+
1097
+ # Mock query for external credential ID
1098
+ responses.add(
1099
+ method="GET",
1100
+ url=f"{tooling_url}/query/?q=SELECT+Id+FROM+ExternalCredential+WHERE+DeveloperName%3D%27testExtCred%27+LIMIT+1",
1101
+ json={"size": 1, "records": [{"Id": ext_cred_id}]},
1102
+ status=200,
1103
+ )
1104
+
1105
+ # Mock get external credential object with AuthProvider
1106
+ responses.add(
1107
+ method="GET",
1108
+ url=f"{tooling_url}/sobjects/ExternalCredential/{ext_cred_id}",
1109
+ json={
1110
+ "Metadata": {
1111
+ "externalCredentialParameters": [
1112
+ {
1113
+ "parameterType": "AuthProvider",
1114
+ "parameterName": "AuthProvider",
1115
+ "authProvider": "OldAuthProvider",
1116
+ "certificate": None,
1117
+ "description": None,
1118
+ "externalAuthIdentityProvider": None,
1119
+ "parameterGroup": "DefaultGroup",
1120
+ "parameterValue": "OldAuthProvider",
1121
+ "sequenceNumber": None,
1122
+ },
1123
+ {
1124
+ "parameterType": "AuthParameter",
1125
+ "parameterName": "Scope",
1126
+ "parameterValue": "scope",
1127
+ "authProvider": None,
1128
+ "certificate": None,
1129
+ "description": None,
1130
+ "externalAuthIdentityProvider": None,
1131
+ "parameterGroup": "DefaultGroup",
1132
+ "sequenceNumber": None,
1133
+ },
1134
+ ],
1135
+ }
1136
+ },
1137
+ status=200,
1138
+ )
1139
+
1140
+ # Mock update external credential
1141
+ responses.add(
1142
+ method="PATCH",
1143
+ url=f"{tooling_url}/sobjects/ExternalCredential/{ext_cred_id}",
1144
+ json={},
1145
+ status=200,
1146
+ )
1147
+
1148
+ task()
1149
+ assert len(responses.calls) == 5 # 2 setup + 3 task calls
1150
+
1151
+ # Verify the PATCH request removed AuthProvider
1152
+ patch_call = responses.calls[4] # Last call is the PATCH
1153
+ updated_params = patch_call.request.body
1154
+ import json
1155
+
1156
+ body = json.loads(updated_params)
1157
+ params = body["Metadata"]["externalCredentialParameters"]
1158
+
1159
+ # Should not contain AuthProvider
1160
+ assert not any(p.get("parameterType") == "AuthProvider" for p in params)
1161
+ # Should contain ExternalAuthIdentityProvider
1162
+ assert any(
1163
+ p.get("parameterType") == "ExternalAuthIdentityProvider" for p in params
1164
+ )
1165
+ # Should still contain AuthParameter
1166
+ assert any(p.get("parameterType") == "AuthParameter" for p in params)
1167
+
1168
+ @responses.activate
1169
+ def test_multiple_auth_providers_removed(self):
1170
+ """Test that multiple conflicting parameters are removed"""
1171
+ # Mock SF API version discovery
1172
+ responses.add(
1173
+ method="GET",
1174
+ url="https://test.salesforce.com/services/data",
1175
+ json=[{"version": CURRENT_SF_API_VERSION}],
1176
+ status=200,
1177
+ )
1178
+
1179
+ # Mock installed packages query
1180
+ tooling_url = f"https://test.salesforce.com/services/data/v{CURRENT_SF_API_VERSION}/tooling"
1181
+ responses.add(
1182
+ method="GET",
1183
+ url=f"{tooling_url}/query/?q=SELECT+SubscriberPackage.Id%2C+SubscriberPackage.Name%2C+SubscriberPackage.NamespacePrefix%2C+SubscriberPackageVersionId+FROM+InstalledSubscriberPackage",
1184
+ json={"size": 0, "records": []},
1185
+ status=200,
1186
+ )
1187
+
1188
+ task = create_task(
1189
+ UpdateExternalCredential,
1190
+ {
1191
+ "name": "testExtCred",
1192
+ "parameters": [
1193
+ {"external_auth_identity_provider": "MyExternalAuthProvider"}
1194
+ ],
1195
+ },
1196
+ )
1197
+
1198
+ ext_cred_id = "0XE1234567890ABC"
1199
+
1200
+ # Mock query for external credential ID
1201
+ responses.add(
1202
+ method="GET",
1203
+ url=f"{tooling_url}/query/?q=SELECT+Id+FROM+ExternalCredential+WHERE+DeveloperName%3D%27testExtCred%27+LIMIT+1",
1204
+ json={"size": 1, "records": [{"Id": ext_cred_id}]},
1205
+ status=200,
1206
+ )
1207
+
1208
+ # Mock get external credential object with multiple AuthProvider parameters
1209
+ responses.add(
1210
+ method="GET",
1211
+ url=f"{tooling_url}/sobjects/ExternalCredential/{ext_cred_id}",
1212
+ json={
1213
+ "Metadata": {
1214
+ "externalCredentialParameters": [
1215
+ {
1216
+ "parameterType": "AuthProvider",
1217
+ "parameterName": "AuthProvider",
1218
+ "authProvider": "Provider1",
1219
+ "certificate": None,
1220
+ "description": None,
1221
+ "externalAuthIdentityProvider": None,
1222
+ "parameterGroup": "Group1",
1223
+ "parameterValue": "Provider1",
1224
+ "sequenceNumber": None,
1225
+ },
1226
+ {
1227
+ "parameterType": "AuthProvider",
1228
+ "parameterName": "AuthProvider",
1229
+ "authProvider": "Provider2",
1230
+ "certificate": None,
1231
+ "description": None,
1232
+ "externalAuthIdentityProvider": None,
1233
+ "parameterGroup": "Group2",
1234
+ "parameterValue": "Provider2",
1235
+ "sequenceNumber": None,
1236
+ },
1237
+ ],
1238
+ }
1239
+ },
1240
+ status=200,
1241
+ )
1242
+
1243
+ # Mock update external credential
1244
+ responses.add(
1245
+ method="PATCH",
1246
+ url=f"{tooling_url}/sobjects/ExternalCredential/{ext_cred_id}",
1247
+ json={},
1248
+ status=200,
1249
+ )
1250
+
1251
+ task()
1252
+ assert len(responses.calls) == 5 # 2 setup + 3 task calls
1253
+
1254
+ # Verify all AuthProvider parameters were removed
1255
+ patch_call = responses.calls[4] # Last call is the PATCH
1256
+ updated_params = patch_call.request.body
1257
+ import json
1258
+
1259
+ body = json.loads(updated_params)
1260
+ params = body["Metadata"]["externalCredentialParameters"]
1261
+
1262
+ # Should not contain any AuthProvider
1263
+ auth_providers = [p for p in params if p.get("parameterType") == "AuthProvider"]
1264
+ assert len(auth_providers) == 0
1265
+
1266
+ @responses.activate
1267
+ def test_no_conflict_when_no_existing_parameters(self):
1268
+ """Test that no error occurs when there are no existing conflicting parameters"""
1269
+ # Mock SF API version discovery
1270
+ responses.add(
1271
+ method="GET",
1272
+ url="https://test.salesforce.com/services/data",
1273
+ json=[{"version": CURRENT_SF_API_VERSION}],
1274
+ status=200,
1275
+ )
1276
+
1277
+ # Mock installed packages query
1278
+ tooling_url = f"https://test.salesforce.com/services/data/v{CURRENT_SF_API_VERSION}/tooling"
1279
+ responses.add(
1280
+ method="GET",
1281
+ url=f"{tooling_url}/query/?q=SELECT+SubscriberPackage.Id%2C+SubscriberPackage.Name%2C+SubscriberPackage.NamespacePrefix%2C+SubscriberPackageVersionId+FROM+InstalledSubscriberPackage",
1282
+ json={"size": 0, "records": []},
1283
+ status=200,
1284
+ )
1285
+
1286
+ task = create_task(
1287
+ UpdateExternalCredential,
1288
+ {
1289
+ "name": "testExtCred",
1290
+ "parameters": [{"auth_provider": "MyAuthProvider"}],
1291
+ },
1292
+ )
1293
+
1294
+ ext_cred_id = "0XE1234567890ABC"
1295
+
1296
+ # Mock query for external credential ID
1297
+ responses.add(
1298
+ method="GET",
1299
+ url=f"{tooling_url}/query/?q=SELECT+Id+FROM+ExternalCredential+WHERE+DeveloperName%3D%27testExtCred%27+LIMIT+1",
1300
+ json={"size": 1, "records": [{"Id": ext_cred_id}]},
1301
+ status=200,
1302
+ )
1303
+
1304
+ # Mock get external credential object with no parameters
1305
+ responses.add(
1306
+ method="GET",
1307
+ url=f"{tooling_url}/sobjects/ExternalCredential/{ext_cred_id}",
1308
+ json={
1309
+ "Metadata": {
1310
+ "externalCredentialParameters": [],
1311
+ }
1312
+ },
1313
+ status=200,
1314
+ )
1315
+
1316
+ # Mock update external credential
1317
+ responses.add(
1318
+ method="PATCH",
1319
+ url=f"{tooling_url}/sobjects/ExternalCredential/{ext_cred_id}",
1320
+ json={},
1321
+ status=200,
1322
+ )
1323
+
1324
+ task()
1325
+ assert len(responses.calls) == 5 # 2 setup + 3 task calls
1326
+
1327
+ @responses.activate
1328
+ def test_update_existing_auth_provider_removes_external_auth(self):
1329
+ """Test updating existing AuthProvider also removes ExternalAuthIdentityProvider"""
1330
+ # Mock SF API version discovery
1331
+ responses.add(
1332
+ method="GET",
1333
+ url="https://test.salesforce.com/services/data",
1334
+ json=[{"version": CURRENT_SF_API_VERSION}],
1335
+ status=200,
1336
+ )
1337
+
1338
+ # Mock installed packages query
1339
+ tooling_url = f"https://test.salesforce.com/services/data/v{CURRENT_SF_API_VERSION}/tooling"
1340
+ responses.add(
1341
+ method="GET",
1342
+ url=f"{tooling_url}/query/?q=SELECT+SubscriberPackage.Id%2C+SubscriberPackage.Name%2C+SubscriberPackage.NamespacePrefix%2C+SubscriberPackageVersionId+FROM+InstalledSubscriberPackage",
1343
+ json={"size": 0, "records": []},
1344
+ status=200,
1345
+ )
1346
+
1347
+ task = create_task(
1348
+ UpdateExternalCredential,
1349
+ {
1350
+ "name": "testExtCred",
1351
+ "parameters": [{"auth_provider": "UpdatedAuthProvider"}],
1352
+ },
1353
+ )
1354
+
1355
+ ext_cred_id = "0XE1234567890ABC"
1356
+
1357
+ # Mock query for external credential ID
1358
+ responses.add(
1359
+ method="GET",
1360
+ url=f"{tooling_url}/query/?q=SELECT+Id+FROM+ExternalCredential+WHERE+DeveloperName%3D%27testExtCred%27+LIMIT+1",
1361
+ json={"size": 1, "records": [{"Id": ext_cred_id}]},
1362
+ status=200,
1363
+ )
1364
+
1365
+ # Mock get external credential object with both AuthProvider and ExternalAuthIdentityProvider
1366
+ responses.add(
1367
+ method="GET",
1368
+ url=f"{tooling_url}/sobjects/ExternalCredential/{ext_cred_id}",
1369
+ json={
1370
+ "Metadata": {
1371
+ "externalCredentialParameters": [
1372
+ {
1373
+ "parameterType": "AuthProvider",
1374
+ "parameterName": "AuthProvider",
1375
+ "authProvider": "OldAuthProvider",
1376
+ "certificate": None,
1377
+ "description": None,
1378
+ "externalAuthIdentityProvider": None,
1379
+ "parameterGroup": "DefaultGroup",
1380
+ "parameterValue": "OldAuthProvider",
1381
+ "sequenceNumber": None,
1382
+ },
1383
+ {
1384
+ "parameterType": "ExternalAuthIdentityProvider",
1385
+ "parameterName": "ExternalAuthIdentityProvider",
1386
+ "externalAuthIdentityProvider": "SomeProvider",
1387
+ "authProvider": None,
1388
+ "certificate": None,
1389
+ "description": None,
1390
+ "parameterGroup": "DefaultGroup",
1391
+ "parameterValue": None,
1392
+ "sequenceNumber": None,
1393
+ },
1394
+ ],
1395
+ }
1396
+ },
1397
+ status=200,
1398
+ )
1399
+
1400
+ # Mock update external credential
1401
+ responses.add(
1402
+ method="PATCH",
1403
+ url=f"{tooling_url}/sobjects/ExternalCredential/{ext_cred_id}",
1404
+ json={},
1405
+ status=200,
1406
+ )
1407
+
1408
+ task()
1409
+ assert len(responses.calls) == 5 # 2 setup + 3 task calls
1410
+
1411
+ # Verify ExternalAuthIdentityProvider was removed
1412
+ patch_call = responses.calls[4] # Last call is the PATCH
1413
+ updated_params = patch_call.request.body
1414
+ import json
1415
+
1416
+ body = json.loads(updated_params)
1417
+ params = body["Metadata"]["externalCredentialParameters"]
1418
+
1419
+ # Should have 2 AuthProviders (old one updated + new one added) and no ExternalAuthIdentityProvider
1420
+ # Actually, since authProvider value is different, it adds a new AuthProvider
1421
+ # So we have old AuthProvider updated to new value, no new one is added
1422
+ auth_providers = [p for p in params if p.get("parameterType") == "AuthProvider"]
1423
+ assert len(auth_providers) >= 1
1424
+ # Should not contain ExternalAuthIdentityProvider
1425
+ assert not any(
1426
+ p.get("parameterType") == "ExternalAuthIdentityProvider" for p in params
1427
+ )