cumulusci-plus 5.0.21__py3-none-any.whl → 5.0.43__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 (135) hide show
  1. cumulusci/__about__.py +1 -1
  2. cumulusci/cli/logger.py +2 -2
  3. cumulusci/cli/service.py +20 -0
  4. cumulusci/cli/task.py +19 -3
  5. cumulusci/cli/tests/test_error.py +3 -1
  6. cumulusci/cli/tests/test_flow.py +279 -2
  7. cumulusci/cli/tests/test_org.py +5 -0
  8. cumulusci/cli/tests/test_service.py +15 -12
  9. cumulusci/cli/tests/test_task.py +122 -2
  10. cumulusci/cli/tests/utils.py +1 -4
  11. cumulusci/core/config/__init__.py +1 -0
  12. cumulusci/core/config/base_task_flow_config.py +26 -1
  13. cumulusci/core/config/org_config.py +2 -1
  14. cumulusci/core/config/project_config.py +14 -20
  15. cumulusci/core/config/scratch_org_config.py +12 -0
  16. cumulusci/core/config/tests/test_config.py +1 -0
  17. cumulusci/core/config/tests/test_config_expensive.py +9 -3
  18. cumulusci/core/config/universal_config.py +3 -4
  19. cumulusci/core/dependencies/base.py +5 -1
  20. cumulusci/core/dependencies/dependencies.py +1 -1
  21. cumulusci/core/dependencies/github.py +1 -2
  22. cumulusci/core/dependencies/resolvers.py +1 -1
  23. cumulusci/core/dependencies/tests/test_dependencies.py +1 -1
  24. cumulusci/core/dependencies/tests/test_resolvers.py +1 -1
  25. cumulusci/core/flowrunner.py +90 -6
  26. cumulusci/core/github.py +1 -1
  27. cumulusci/core/sfdx.py +3 -1
  28. cumulusci/core/source_transforms/tests/test_transforms.py +1 -1
  29. cumulusci/core/source_transforms/transforms.py +1 -1
  30. cumulusci/core/tasks.py +13 -2
  31. cumulusci/core/tests/test_flowrunner.py +100 -0
  32. cumulusci/core/tests/test_tasks.py +65 -0
  33. cumulusci/core/utils.py +3 -1
  34. cumulusci/core/versions.py +1 -1
  35. cumulusci/cumulusci.yml +73 -1
  36. cumulusci/oauth/client.py +1 -1
  37. cumulusci/plugins/plugin_base.py +5 -3
  38. cumulusci/robotframework/pageobjects/ObjectManagerPageObject.py +1 -1
  39. cumulusci/salesforce_api/rest_deploy.py +1 -1
  40. cumulusci/schema/cumulusci.jsonschema.json +69 -0
  41. cumulusci/tasks/apex/anon.py +1 -1
  42. cumulusci/tasks/apex/testrunner.py +421 -144
  43. cumulusci/tasks/apex/tests/test_apex_tasks.py +917 -1
  44. cumulusci/tasks/bulkdata/extract.py +0 -1
  45. cumulusci/tasks/bulkdata/extract_dataset_utils/extract_yml.py +1 -1
  46. cumulusci/tasks/bulkdata/extract_dataset_utils/synthesize_extract_declarations.py +1 -1
  47. cumulusci/tasks/bulkdata/extract_dataset_utils/tests/test_extract_yml.py +1 -1
  48. cumulusci/tasks/bulkdata/generate_and_load_data.py +136 -12
  49. cumulusci/tasks/bulkdata/mapping_parser.py +139 -44
  50. cumulusci/tasks/bulkdata/select_utils.py +1 -1
  51. cumulusci/tasks/bulkdata/snowfakery.py +100 -25
  52. cumulusci/tasks/bulkdata/tests/test_generate_and_load.py +159 -0
  53. cumulusci/tasks/bulkdata/tests/test_load.py +0 -2
  54. cumulusci/tasks/bulkdata/tests/test_mapping_parser.py +763 -1
  55. cumulusci/tasks/bulkdata/tests/test_select_utils.py +46 -0
  56. cumulusci/tasks/bulkdata/tests/test_snowfakery.py +133 -0
  57. cumulusci/tasks/create_package_version.py +190 -16
  58. cumulusci/tasks/datadictionary.py +1 -1
  59. cumulusci/tasks/metadata_etl/__init__.py +2 -0
  60. cumulusci/tasks/metadata_etl/applications.py +256 -0
  61. cumulusci/tasks/metadata_etl/base.py +7 -3
  62. cumulusci/tasks/metadata_etl/layouts.py +1 -1
  63. cumulusci/tasks/metadata_etl/permissions.py +1 -1
  64. cumulusci/tasks/metadata_etl/remote_site_settings.py +2 -2
  65. cumulusci/tasks/metadata_etl/tests/test_applications.py +710 -0
  66. cumulusci/tasks/push/README.md +15 -17
  67. cumulusci/tasks/release_notes/README.md +13 -13
  68. cumulusci/tasks/release_notes/generator.py +13 -8
  69. cumulusci/tasks/robotframework/tests/test_robotframework.py +6 -1
  70. cumulusci/tasks/salesforce/Deploy.py +53 -2
  71. cumulusci/tasks/salesforce/SfPackageCommands.py +363 -0
  72. cumulusci/tasks/salesforce/__init__.py +1 -0
  73. cumulusci/tasks/salesforce/assign_ps_psg.py +448 -0
  74. cumulusci/tasks/salesforce/composite.py +1 -1
  75. cumulusci/tasks/salesforce/custom_settings_wait.py +1 -1
  76. cumulusci/tasks/salesforce/enable_prediction.py +5 -1
  77. cumulusci/tasks/salesforce/getPackageVersion.py +89 -0
  78. cumulusci/tasks/salesforce/insert_record.py +18 -19
  79. cumulusci/tasks/salesforce/sourcetracking.py +1 -1
  80. cumulusci/tasks/salesforce/tests/test_Deploy.py +316 -1
  81. cumulusci/tasks/salesforce/tests/test_SfPackageCommands.py +554 -0
  82. cumulusci/tasks/salesforce/tests/test_assign_ps_psg.py +1055 -0
  83. cumulusci/tasks/salesforce/tests/test_enable_prediction.py +4 -2
  84. cumulusci/tasks/salesforce/tests/test_getPackageVersion.py +651 -0
  85. cumulusci/tasks/salesforce/tests/test_update_dependencies.py +1 -1
  86. cumulusci/tasks/salesforce/tests/test_update_external_auth_identity_provider.py +927 -0
  87. cumulusci/tasks/salesforce/tests/test_update_external_credential.py +1427 -0
  88. cumulusci/tasks/salesforce/tests/test_update_named_credential.py +1042 -0
  89. cumulusci/tasks/salesforce/tests/test_update_record.py +512 -0
  90. cumulusci/tasks/salesforce/update_dependencies.py +2 -2
  91. cumulusci/tasks/salesforce/update_external_auth_identity_provider.py +551 -0
  92. cumulusci/tasks/salesforce/update_external_credential.py +647 -0
  93. cumulusci/tasks/salesforce/update_named_credential.py +441 -0
  94. cumulusci/tasks/salesforce/update_profile.py +17 -13
  95. cumulusci/tasks/salesforce/update_record.py +217 -0
  96. cumulusci/tasks/salesforce/users/permsets.py +62 -5
  97. cumulusci/tasks/salesforce/users/tests/test_permsets.py +237 -11
  98. cumulusci/tasks/sfdmu/__init__.py +0 -0
  99. cumulusci/tasks/sfdmu/sfdmu.py +376 -0
  100. cumulusci/tasks/sfdmu/tests/__init__.py +1 -0
  101. cumulusci/tasks/sfdmu/tests/test_runner.py +212 -0
  102. cumulusci/tasks/sfdmu/tests/test_sfdmu.py +1012 -0
  103. cumulusci/tasks/tests/test_create_package_version.py +716 -1
  104. cumulusci/tasks/tests/test_util.py +42 -0
  105. cumulusci/tasks/util.py +37 -1
  106. cumulusci/tasks/utility/copyContents.py +402 -0
  107. cumulusci/tasks/utility/credentialManager.py +302 -0
  108. cumulusci/tasks/utility/directoryRecreator.py +30 -0
  109. cumulusci/tasks/utility/env_management.py +1 -1
  110. cumulusci/tasks/utility/secretsToEnv.py +135 -0
  111. cumulusci/tasks/utility/tests/test_copyContents.py +1719 -0
  112. cumulusci/tasks/utility/tests/test_credentialManager.py +1150 -0
  113. cumulusci/tasks/utility/tests/test_directoryRecreator.py +439 -0
  114. cumulusci/tasks/utility/tests/test_secretsToEnv.py +1118 -0
  115. cumulusci/tests/test_integration_infrastructure.py +3 -1
  116. cumulusci/tests/test_utils.py +70 -6
  117. cumulusci/utils/__init__.py +54 -9
  118. cumulusci/utils/classutils.py +5 -2
  119. cumulusci/utils/http/tests/cassettes/ManualEditTestCompositeParallelSalesforce.test_http_headers.yaml +31 -30
  120. cumulusci/utils/options.py +23 -1
  121. cumulusci/utils/parallel/task_worker_queues/parallel_worker.py +1 -1
  122. cumulusci/utils/yaml/cumulusci_yml.py +8 -3
  123. cumulusci/utils/yaml/model_parser.py +2 -2
  124. cumulusci/utils/yaml/tests/test_cumulusci_yml.py +1 -1
  125. cumulusci/utils/yaml/tests/test_model_parser.py +3 -3
  126. cumulusci/vcs/base.py +23 -15
  127. cumulusci/vcs/bootstrap.py +5 -4
  128. cumulusci/vcs/utils/list_modified_files.py +189 -0
  129. cumulusci/vcs/utils/tests/test_list_modified_files.py +588 -0
  130. {cumulusci_plus-5.0.21.dist-info → cumulusci_plus-5.0.43.dist-info}/METADATA +11 -10
  131. {cumulusci_plus-5.0.21.dist-info → cumulusci_plus-5.0.43.dist-info}/RECORD +135 -104
  132. {cumulusci_plus-5.0.21.dist-info → cumulusci_plus-5.0.43.dist-info}/WHEEL +1 -1
  133. {cumulusci_plus-5.0.21.dist-info → cumulusci_plus-5.0.43.dist-info}/entry_points.txt +0 -0
  134. {cumulusci_plus-5.0.21.dist-info → cumulusci_plus-5.0.43.dist-info}/licenses/AUTHORS.rst +0 -0
  135. {cumulusci_plus-5.0.21.dist-info → cumulusci_plus-5.0.43.dist-info}/licenses/LICENSE +0 -0
@@ -10,7 +10,7 @@ from unittest import mock
10
10
  import pytest
11
11
  import responses
12
12
  import yaml
13
- from pydantic import ValidationError
13
+ from pydantic.v1 import ValidationError
14
14
 
15
15
  from cumulusci.core.config import BaseProjectConfig, TaskConfig, UniversalConfig
16
16
  from cumulusci.core.dependencies.dependencies import (
@@ -845,3 +845,718 @@ class TestCreatePackageVersion:
845
845
  assert task._prepare_cci_dependencies(
846
846
  {"ids": [{"subscriberPackageVersionId": "04t000000000000"}]}
847
847
  ) == [{"version_id": "04t000000000000"}]
848
+
849
+
850
+ class TestPackageConfigNewFeatures:
851
+ """Tests for new PackageConfig validation features."""
852
+
853
+ def test_validate_apex_test_access(self):
854
+ with pytest.raises(ValidationError, match="Only managed packages"):
855
+ PackageConfig(
856
+ package_type=PackageTypeEnum.unlocked,
857
+ apex_test_access={"permission_set_names": ["TestPermSet"]},
858
+ ) # type: ignore
859
+
860
+ def test_validate_package_metadata_access(self):
861
+ with pytest.raises(ValidationError, match="Only managed packages"):
862
+ PackageConfig(
863
+ package_type=PackageTypeEnum.unlocked,
864
+ package_metadata_access={"permission_set_names": ["TestPermSet"]},
865
+ ) # type: ignore
866
+
867
+
868
+ class TestCreatePackageVersionNewFeatures:
869
+ """Tests for new CreatePackageVersion functionality."""
870
+
871
+ devhub_base_url = (
872
+ f"https://devhub.my.salesforce.com/services/data/v{CURRENT_SF_API_VERSION}"
873
+ )
874
+
875
+ @responses.activate
876
+ def test_version_number_option(self, get_task, devhub_config):
877
+ """Test that version_number option is used instead of version_base/version_type"""
878
+ task = get_task(
879
+ {
880
+ "package_type": "Managed",
881
+ "package_name": "Test Package",
882
+ "version_number": "2.5.3.10",
883
+ }
884
+ )
885
+
886
+ responses.add(
887
+ "GET",
888
+ f"{self.devhub_base_url}/tooling/query/",
889
+ json={"size": 0, "records": []},
890
+ )
891
+ responses.add(
892
+ "POST",
893
+ f"{self.devhub_base_url}/tooling/sobjects/Package2/",
894
+ json={"id": "0Ho6g000000fy4ZCAQ"},
895
+ )
896
+ responses.add(
897
+ "GET",
898
+ f"{self.devhub_base_url}/tooling/query/",
899
+ json={"size": 0, "records": []},
900
+ )
901
+
902
+ package_id = task._get_or_create_package(task.package_config)
903
+ builder = BasePackageZipBuilder()
904
+
905
+ with mock.patch.object(
906
+ task, "_get_base_version_number"
907
+ ) as _, mock.patch.object(
908
+ builder, "as_hash", return_value="testhash"
909
+ ), mock.patch.object(
910
+ builder, "as_bytes", return_value=b"testbytes"
911
+ ), mock.patch.object(
912
+ task, "_get_tooling_object"
913
+ ) as mock_tooling:
914
+ mock_tooling_obj = mock.Mock()
915
+
916
+ def capture_create(request):
917
+ import base64
918
+
919
+ version_info_b64 = request["VersionInfo"]
920
+ version_info_bytes = base64.b64decode(version_info_b64)
921
+ version_info_zip = zipfile.ZipFile(io.BytesIO(version_info_bytes), "r")
922
+ descriptor_json = version_info_zip.read("package2-descriptor.json")
923
+ descriptor = json.loads(descriptor_json.decode("utf-8"))
924
+ # Verify version_number was used instead of version_base/version_type
925
+ assert descriptor["versionNumber"] == "2.5.3.10"
926
+ return {"id": "08c000000000002AAA"}
927
+
928
+ mock_tooling_obj.create.side_effect = capture_create
929
+ mock_tooling.return_value = mock_tooling_obj
930
+
931
+ task._create_version_request(
932
+ package_id, task.package_config, builder, skip_validation=True
933
+ )
934
+ # Verify version_number was parsed correctly
935
+ assert task.options["version_number"] is not None
936
+ assert task.options["version_number"].format() == "2.5.3.10"
937
+
938
+ @responses.activate
939
+ def test_dependencies_option(self, get_task, devhub_config):
940
+ """Test that explicit dependencies option is used"""
941
+ task = get_task(
942
+ {
943
+ "package_type": "Managed",
944
+ "package_name": "Test Package",
945
+ "dependencies": "04t000000000001,04t000000000002",
946
+ }
947
+ )
948
+
949
+ responses.add(
950
+ "GET",
951
+ f"{self.devhub_base_url}/tooling/query/",
952
+ json={"size": 0, "records": []},
953
+ )
954
+ responses.add(
955
+ "POST",
956
+ f"{self.devhub_base_url}/tooling/sobjects/Package2/",
957
+ json={"id": "0Ho6g000000fy4ZCAQ"},
958
+ )
959
+ responses.add(
960
+ "GET",
961
+ f"{self.devhub_base_url}/tooling/query/",
962
+ json={"size": 0, "records": []},
963
+ )
964
+
965
+ package_id = task._get_or_create_package(task.package_config)
966
+ builder = BasePackageZipBuilder()
967
+
968
+ with mock.patch.object(
969
+ task, "_get_dependencies"
970
+ ) as mock_get_deps, mock.patch.object(
971
+ task, "_get_base_version_number"
972
+ ) as mock_version, mock.patch.object(
973
+ builder, "as_hash", return_value="testhash"
974
+ ), mock.patch.object(
975
+ builder, "as_bytes", return_value=b"testbytes"
976
+ ), mock.patch.object(
977
+ task, "_get_tooling_object"
978
+ ) as mock_tooling:
979
+ mock_version.return_value.increment.return_value.format.return_value = (
980
+ "1.0.0.1"
981
+ )
982
+ mock_tooling_obj = mock.Mock()
983
+
984
+ def capture_create(request):
985
+ import base64
986
+
987
+ version_info_b64 = request["VersionInfo"]
988
+ version_info_bytes = base64.b64decode(version_info_b64)
989
+ version_info_zip = zipfile.ZipFile(io.BytesIO(version_info_bytes), "r")
990
+ descriptor_json = version_info_zip.read("package2-descriptor.json")
991
+ descriptor = json.loads(descriptor_json.decode("utf-8"))
992
+ # Verify dependencies were set correctly
993
+ assert "dependencies" in descriptor
994
+ assert descriptor["dependencies"] == [
995
+ {"subscriberPackageVersionId": "04t000000000001"},
996
+ {"subscriberPackageVersionId": "04t000000000002"},
997
+ ]
998
+ # _get_dependencies should not be called when dependencies option is set
999
+ mock_get_deps.assert_not_called()
1000
+ return {"id": "08c000000000002AAA"}
1001
+
1002
+ mock_tooling_obj.create.side_effect = capture_create
1003
+ mock_tooling.return_value = mock_tooling_obj
1004
+
1005
+ task._create_version_request(
1006
+ package_id,
1007
+ task.package_config,
1008
+ builder,
1009
+ skip_validation=False,
1010
+ )
1011
+ # Verify dependencies were parsed correctly
1012
+ assert task.options["dependencies"] == [
1013
+ {"subscriberPackageVersionId": "04t000000000001"},
1014
+ {"subscriberPackageVersionId": "04t000000000002"},
1015
+ ]
1016
+
1017
+ @responses.activate
1018
+ def test_apex_test_access_string_format(self, get_task, devhub_config):
1019
+ """Test apex_test_access with string format for permission sets"""
1020
+ task = get_task(
1021
+ {
1022
+ "package_type": "Managed",
1023
+ "package_name": "Test Package",
1024
+ }
1025
+ )
1026
+ task.package_config.apex_test_access = {
1027
+ "permission_set_names": "PermSet1, PermSet2",
1028
+ "permission_set_license_names": "License1, License2",
1029
+ }
1030
+
1031
+ builder = BasePackageZipBuilder()
1032
+
1033
+ # Mock the query for existing Package2VersionCreateRequest
1034
+ responses.add(
1035
+ "GET",
1036
+ f"{self.devhub_base_url}/tooling/query/",
1037
+ json={"size": 0, "records": []},
1038
+ )
1039
+
1040
+ with mock.patch.object(
1041
+ task, "_get_base_version_number"
1042
+ ) as mock_version, mock.patch.object(
1043
+ builder, "as_hash", return_value="testhash"
1044
+ ), mock.patch.object(
1045
+ builder, "as_bytes", return_value=b"testbytes"
1046
+ ), mock.patch.object(
1047
+ task, "_get_tooling_object"
1048
+ ) as mock_tooling:
1049
+ mock_version.return_value.increment.return_value.format.return_value = (
1050
+ "1.0.0.1"
1051
+ )
1052
+ mock_tooling_obj = mock.Mock()
1053
+
1054
+ def capture_create(request):
1055
+ import base64
1056
+
1057
+ version_info_b64 = request["VersionInfo"]
1058
+ version_info_bytes = base64.b64decode(version_info_b64)
1059
+ version_info_zip = zipfile.ZipFile(io.BytesIO(version_info_bytes), "r")
1060
+ descriptor_json = version_info_zip.read("package2-descriptor.json")
1061
+ descriptor = json.loads(descriptor_json.decode("utf-8"))
1062
+ assert "permissionSetNames" in descriptor
1063
+ assert descriptor["permissionSetNames"] == ["PermSet1", "PermSet2"]
1064
+ assert "permissionSetLicenseDeveloperNames" in descriptor
1065
+ assert descriptor["permissionSetLicenseDeveloperNames"] == [
1066
+ "License1",
1067
+ "License2",
1068
+ ]
1069
+ return {"id": "08c000000000002AAA"}
1070
+
1071
+ mock_tooling_obj.create.side_effect = capture_create
1072
+ mock_tooling.return_value = mock_tooling_obj
1073
+
1074
+ task._create_version_request(
1075
+ "0Ho6g000000fy4ZCAQ",
1076
+ task.package_config,
1077
+ builder,
1078
+ skip_validation=True,
1079
+ )
1080
+
1081
+ @responses.activate
1082
+ def test_apex_test_access_list_format(self, get_task, devhub_config):
1083
+ """Test apex_test_access with list format for permission sets"""
1084
+ task = get_task(
1085
+ {
1086
+ "package_type": "Managed",
1087
+ "package_name": "Test Package",
1088
+ }
1089
+ )
1090
+ task.package_config.apex_test_access = {
1091
+ "permission_set_names": ["PermSet1", "PermSet2"],
1092
+ "permission_set_license_names": ["License1", "License2"],
1093
+ }
1094
+
1095
+ builder = BasePackageZipBuilder()
1096
+
1097
+ # Mock the query for existing Package2VersionCreateRequest
1098
+ responses.add(
1099
+ "GET",
1100
+ f"{self.devhub_base_url}/tooling/query/",
1101
+ json={"size": 0, "records": []},
1102
+ )
1103
+
1104
+ with mock.patch.object(
1105
+ task, "_get_base_version_number"
1106
+ ) as mock_version, mock.patch.object(
1107
+ builder, "as_hash", return_value="testhash"
1108
+ ), mock.patch.object(
1109
+ builder, "as_bytes", return_value=b"testbytes"
1110
+ ), mock.patch.object(
1111
+ task, "_get_tooling_object"
1112
+ ) as mock_tooling:
1113
+ mock_version.return_value.increment.return_value.format.return_value = (
1114
+ "1.0.0.1"
1115
+ )
1116
+ mock_tooling_obj = mock.Mock()
1117
+
1118
+ def capture_create(request):
1119
+ import base64
1120
+
1121
+ version_info_b64 = request["VersionInfo"]
1122
+ version_info_bytes = base64.b64decode(version_info_b64)
1123
+ version_info_zip = zipfile.ZipFile(io.BytesIO(version_info_bytes), "r")
1124
+ descriptor_json = version_info_zip.read("package2-descriptor.json")
1125
+ descriptor = json.loads(descriptor_json.decode("utf-8"))
1126
+ assert "permissionSetNames" in descriptor
1127
+ assert descriptor["permissionSetNames"] == ["PermSet1", "PermSet2"]
1128
+ assert "permissionSetLicenseDeveloperNames" in descriptor
1129
+ assert descriptor["permissionSetLicenseDeveloperNames"] == [
1130
+ "License1",
1131
+ "License2",
1132
+ ]
1133
+ return {"id": "08c000000000002AAA"}
1134
+
1135
+ mock_tooling_obj.create.side_effect = capture_create
1136
+ mock_tooling.return_value = mock_tooling_obj
1137
+
1138
+ task._create_version_request(
1139
+ "0Ho6g000000fy4ZCAQ",
1140
+ task.package_config,
1141
+ builder,
1142
+ skip_validation=True,
1143
+ )
1144
+
1145
+ @responses.activate
1146
+ def test_package_metadata_access_string_format(self, get_task, devhub_config):
1147
+ """Test package_metadata_access with string format"""
1148
+ task = get_task(
1149
+ {
1150
+ "package_type": "Managed",
1151
+ "package_name": "Test Package",
1152
+ }
1153
+ )
1154
+ task.package_config.package_metadata_access = {
1155
+ "permission_set_names": "MetaPermSet1, MetaPermSet2",
1156
+ "permission_set_license_names": "MetaLicense1, MetaLicense2",
1157
+ }
1158
+
1159
+ builder = BasePackageZipBuilder()
1160
+
1161
+ # Mock the query for existing Package2VersionCreateRequest
1162
+ responses.add(
1163
+ "GET",
1164
+ f"{self.devhub_base_url}/tooling/query/",
1165
+ json={"size": 0, "records": []},
1166
+ )
1167
+
1168
+ with mock.patch.object(
1169
+ task, "_get_base_version_number"
1170
+ ) as mock_version, mock.patch.object(
1171
+ builder, "as_hash", return_value="testhash"
1172
+ ), mock.patch.object(
1173
+ builder, "as_bytes", return_value=b"testbytes"
1174
+ ), mock.patch.object(
1175
+ task, "_get_tooling_object"
1176
+ ) as mock_tooling:
1177
+ mock_version.return_value.increment.return_value.format.return_value = (
1178
+ "1.0.0.1"
1179
+ )
1180
+ mock_tooling_obj = mock.Mock()
1181
+
1182
+ def capture_create(request):
1183
+ import base64
1184
+
1185
+ version_info_b64 = request["VersionInfo"]
1186
+ version_info_bytes = base64.b64decode(version_info_b64)
1187
+ version_info_zip = zipfile.ZipFile(io.BytesIO(version_info_bytes), "r")
1188
+ descriptor_json = version_info_zip.read("package2-descriptor.json")
1189
+ descriptor = json.loads(descriptor_json.decode("utf-8"))
1190
+ assert "packageMetadataPermissionSetNames" in descriptor
1191
+ assert descriptor["packageMetadataPermissionSetNames"] == [
1192
+ "MetaPermSet1",
1193
+ "MetaPermSet2",
1194
+ ]
1195
+ assert "packageMetadataPermissionSetLicenseNames" in descriptor
1196
+ assert descriptor["packageMetadataPermissionSetLicenseNames"] == [
1197
+ "MetaLicense1",
1198
+ "MetaLicense2",
1199
+ ]
1200
+ return {"id": "08c000000000002AAA"}
1201
+
1202
+ mock_tooling_obj.create.side_effect = capture_create
1203
+ mock_tooling.return_value = mock_tooling_obj
1204
+
1205
+ task._create_version_request(
1206
+ "0Ho6g000000fy4ZCAQ",
1207
+ task.package_config,
1208
+ builder,
1209
+ skip_validation=True,
1210
+ )
1211
+
1212
+ @responses.activate
1213
+ def test_package_metadata_access_list_format(self, get_task, devhub_config):
1214
+ """Test package_metadata_access with list format"""
1215
+ task = get_task(
1216
+ {
1217
+ "package_type": "Managed",
1218
+ "package_name": "Test Package",
1219
+ }
1220
+ )
1221
+ task.package_config.package_metadata_access = {
1222
+ "permission_set_names": ["MetaPermSet1", "MetaPermSet2"],
1223
+ "permission_set_license_names": ["MetaLicense1", "MetaLicense2"],
1224
+ }
1225
+
1226
+ builder = BasePackageZipBuilder()
1227
+
1228
+ # Mock the query for existing Package2VersionCreateRequest
1229
+ responses.add(
1230
+ "GET",
1231
+ f"{self.devhub_base_url}/tooling/query/",
1232
+ json={"size": 0, "records": []},
1233
+ )
1234
+
1235
+ with mock.patch.object(
1236
+ task, "_get_base_version_number"
1237
+ ) as mock_version, mock.patch.object(
1238
+ builder, "as_hash", return_value="testhash"
1239
+ ), mock.patch.object(
1240
+ builder, "as_bytes", return_value=b"testbytes"
1241
+ ), mock.patch.object(
1242
+ task, "_get_tooling_object"
1243
+ ) as mock_tooling:
1244
+ mock_version.return_value.increment.return_value.format.return_value = (
1245
+ "1.0.0.1"
1246
+ )
1247
+ mock_tooling_obj = mock.Mock()
1248
+
1249
+ def capture_create(request):
1250
+ import base64
1251
+
1252
+ version_info_b64 = request["VersionInfo"]
1253
+ version_info_bytes = base64.b64decode(version_info_b64)
1254
+ version_info_zip = zipfile.ZipFile(io.BytesIO(version_info_bytes), "r")
1255
+ descriptor_json = version_info_zip.read("package2-descriptor.json")
1256
+ descriptor = json.loads(descriptor_json.decode("utf-8"))
1257
+ assert "packageMetadataPermissionSetNames" in descriptor
1258
+ assert descriptor["packageMetadataPermissionSetNames"] == [
1259
+ "MetaPermSet1",
1260
+ "MetaPermSet2",
1261
+ ]
1262
+ assert "packageMetadataPermissionSetLicenseNames" in descriptor
1263
+ assert descriptor["packageMetadataPermissionSetLicenseNames"] == [
1264
+ "MetaLicense1",
1265
+ "MetaLicense2",
1266
+ ]
1267
+ return {"id": "08c000000000002AAA"}
1268
+
1269
+ mock_tooling_obj.create.side_effect = capture_create
1270
+ mock_tooling.return_value = mock_tooling_obj
1271
+
1272
+ task._create_version_request(
1273
+ "0Ho6g000000fy4ZCAQ",
1274
+ task.package_config,
1275
+ builder,
1276
+ skip_validation=True,
1277
+ )
1278
+
1279
+ @responses.activate
1280
+ @mock.patch("cumulusci.tasks.create_package_version.consolidate_metadata")
1281
+ @mock.patch("cumulusci.tasks.create_package_version.convert_sfdx_source")
1282
+ @mock.patch("cumulusci.tasks.create_package_version.MetadataPackageZipBuilder")
1283
+ @mock.patch("cumulusci.tasks.create_package_version.clean_temp_directory")
1284
+ def test_get_unpackaged_metadata_path_string(
1285
+ self,
1286
+ mock_clean_temp,
1287
+ mock_zip_builder,
1288
+ mock_convert_sfdx,
1289
+ mock_consolidate,
1290
+ task,
1291
+ ):
1292
+ """Test _get_unpackaged_metadata_path with string path"""
1293
+ import tempfile
1294
+
1295
+ temp_path = tempfile.mkdtemp()
1296
+ mock_consolidate.return_value = temp_path, 1
1297
+ mock_convert_sfdx.return_value.__enter__.return_value = temp_path
1298
+
1299
+ mock_builder_instance = mock.Mock()
1300
+ mock_builder_instance.as_bytes.return_value = b"testzipbytes"
1301
+ mock_zip_builder.return_value = mock_builder_instance
1302
+
1303
+ version_bytes = io.BytesIO()
1304
+ version_info = zipfile.ZipFile(version_bytes, "w", zipfile.ZIP_DEFLATED)
1305
+
1306
+ result = task._get_unpackaged_metadata_path("unpackaged/pre", version_info)
1307
+
1308
+ assert result == version_info
1309
+ mock_consolidate.assert_called_once_with(
1310
+ "unpackaged/pre", task.project_config.repo_root, logger=task.logger
1311
+ )
1312
+ mock_convert_sfdx.assert_called_once()
1313
+ mock_zip_builder.assert_called_once()
1314
+ mock_builder_instance.as_bytes.assert_called_once()
1315
+ mock_clean_temp.assert_called_once_with(temp_path)
1316
+ assert "unpackaged-metadata-package.zip" in [
1317
+ name for name in version_info.namelist()
1318
+ ]
1319
+
1320
+ version_info.close()
1321
+
1322
+ @responses.activate
1323
+ @mock.patch("cumulusci.tasks.create_package_version.consolidate_metadata")
1324
+ @mock.patch("cumulusci.tasks.create_package_version.convert_sfdx_source")
1325
+ @mock.patch("cumulusci.tasks.create_package_version.MetadataPackageZipBuilder")
1326
+ @mock.patch("cumulusci.tasks.create_package_version.clean_temp_directory")
1327
+ def test_get_unpackaged_metadata_path_list(
1328
+ self,
1329
+ mock_clean_temp,
1330
+ mock_zip_builder,
1331
+ mock_convert_sfdx,
1332
+ mock_consolidate,
1333
+ task,
1334
+ ):
1335
+ """Test _get_unpackaged_metadata_path with list of paths"""
1336
+ import tempfile
1337
+
1338
+ temp_path = tempfile.mkdtemp()
1339
+ mock_consolidate.return_value = temp_path, 1
1340
+ mock_convert_sfdx.return_value.__enter__.return_value = temp_path
1341
+
1342
+ mock_builder_instance = mock.Mock()
1343
+ mock_builder_instance.as_bytes.return_value = b"testzipbytes"
1344
+ mock_zip_builder.return_value = mock_builder_instance
1345
+
1346
+ version_bytes = io.BytesIO()
1347
+ version_info = zipfile.ZipFile(version_bytes, "w", zipfile.ZIP_DEFLATED)
1348
+
1349
+ metadata_paths = ["unpackaged/pre", "unpackaged/post"]
1350
+ result = task._get_unpackaged_metadata_path(metadata_paths, version_info)
1351
+
1352
+ assert result == version_info
1353
+ mock_consolidate.assert_called_once_with(
1354
+ metadata_paths, task.project_config.repo_root, logger=task.logger
1355
+ )
1356
+ version_info.close()
1357
+
1358
+ @responses.activate
1359
+ @mock.patch("cumulusci.tasks.create_package_version.consolidate_metadata")
1360
+ @mock.patch("cumulusci.tasks.create_package_version.convert_sfdx_source")
1361
+ @mock.patch("cumulusci.tasks.create_package_version.MetadataPackageZipBuilder")
1362
+ @mock.patch("cumulusci.tasks.create_package_version.clean_temp_directory")
1363
+ def test_get_unpackaged_metadata_path_dict(
1364
+ self,
1365
+ mock_clean_temp,
1366
+ mock_zip_builder,
1367
+ mock_convert_sfdx,
1368
+ mock_consolidate,
1369
+ task,
1370
+ ):
1371
+ """Test _get_unpackaged_metadata_path with dict format"""
1372
+ import tempfile
1373
+
1374
+ temp_path = tempfile.mkdtemp()
1375
+ mock_consolidate.return_value = temp_path, 1
1376
+ mock_convert_sfdx.return_value.__enter__.return_value = temp_path
1377
+
1378
+ mock_builder_instance = mock.Mock()
1379
+ mock_builder_instance.as_bytes.return_value = b"testzipbytes"
1380
+ mock_zip_builder.return_value = mock_builder_instance
1381
+
1382
+ version_bytes = io.BytesIO()
1383
+ version_info = zipfile.ZipFile(version_bytes, "w", zipfile.ZIP_DEFLATED)
1384
+
1385
+ metadata_paths = {"unpackaged/pre": "*.*", "unpackaged/post": "test.xml"}
1386
+ result = task._get_unpackaged_metadata_path(metadata_paths, version_info)
1387
+
1388
+ assert result == version_info
1389
+ mock_consolidate.assert_called_once_with(
1390
+ metadata_paths, task.project_config.repo_root, logger=task.logger
1391
+ )
1392
+ version_info.close()
1393
+
1394
+ @responses.activate
1395
+ def test_create_version_request_with_unpackaged_metadata(
1396
+ self, get_task, devhub_config
1397
+ ):
1398
+ """Test _create_version_request includes unpackaged metadata when configured"""
1399
+ task = get_task(
1400
+ {
1401
+ "package_type": "Managed",
1402
+ "package_name": "Test Package",
1403
+ }
1404
+ )
1405
+ task.package_config.unpackaged_metadata_path = "unpackaged/pre"
1406
+
1407
+ builder = BasePackageZipBuilder()
1408
+
1409
+ # Mock the query for existing Package2VersionCreateRequest
1410
+ responses.add(
1411
+ "GET",
1412
+ f"{self.devhub_base_url}/tooling/query/",
1413
+ json={"size": 0, "records": []},
1414
+ )
1415
+
1416
+ with mock.patch.object(
1417
+ task, "_get_unpackaged_metadata_path"
1418
+ ) as mock_unpackaged, mock.patch.object(
1419
+ task, "_get_base_version_number"
1420
+ ) as mock_version, mock.patch.object(
1421
+ builder, "as_hash", return_value="testhash"
1422
+ ), mock.patch.object(
1423
+ builder, "as_bytes", return_value=b"testbytes"
1424
+ ), mock.patch.object(
1425
+ task, "_get_tooling_object"
1426
+ ) as mock_tooling:
1427
+ mock_version.return_value.increment.return_value.format.return_value = (
1428
+ "1.0.0.1"
1429
+ )
1430
+ version_bytes = io.BytesIO()
1431
+ version_info = zipfile.ZipFile(version_bytes, "w", zipfile.ZIP_DEFLATED)
1432
+ mock_unpackaged.return_value = version_info
1433
+
1434
+ mock_tooling_obj = mock.Mock()
1435
+ mock_tooling_obj.create.return_value = {"id": "08c000000000002AAA"}
1436
+ mock_tooling.return_value = mock_tooling_obj
1437
+
1438
+ task._create_version_request(
1439
+ "0Ho6g000000fy4ZCAQ",
1440
+ task.package_config,
1441
+ builder,
1442
+ skip_validation=True,
1443
+ )
1444
+
1445
+ mock_unpackaged.assert_called_once_with("unpackaged/pre", mock.ANY)
1446
+ version_info.close()
1447
+
1448
+ @responses.activate
1449
+ def test_apex_test_access_partial_config(self, get_task, devhub_config):
1450
+ """Test apex_test_access with only permission_set_names"""
1451
+ task = get_task(
1452
+ {
1453
+ "package_type": "Managed",
1454
+ "package_name": "Test Package",
1455
+ }
1456
+ )
1457
+ task.package_config.apex_test_access = {
1458
+ "permission_set_names": ["PermSet1"],
1459
+ }
1460
+
1461
+ builder = BasePackageZipBuilder()
1462
+
1463
+ # Mock the query for existing Package2VersionCreateRequest
1464
+ responses.add(
1465
+ "GET",
1466
+ f"{self.devhub_base_url}/tooling/query/",
1467
+ json={"size": 0, "records": []},
1468
+ )
1469
+
1470
+ with mock.patch.object(
1471
+ task, "_get_base_version_number"
1472
+ ) as mock_version, mock.patch.object(
1473
+ builder, "as_hash", return_value="testhash"
1474
+ ), mock.patch.object(
1475
+ builder, "as_bytes", return_value=b"testbytes"
1476
+ ), mock.patch.object(
1477
+ task, "_get_tooling_object"
1478
+ ) as mock_tooling:
1479
+ mock_version.return_value.increment.return_value.format.return_value = (
1480
+ "1.0.0.1"
1481
+ )
1482
+ mock_tooling_obj = mock.Mock()
1483
+
1484
+ def capture_create(request):
1485
+ import base64
1486
+
1487
+ version_info_b64 = request["VersionInfo"]
1488
+ version_info_bytes = base64.b64decode(version_info_b64)
1489
+ version_info_zip = zipfile.ZipFile(io.BytesIO(version_info_bytes), "r")
1490
+ descriptor_json = version_info_zip.read("package2-descriptor.json")
1491
+ descriptor = json.loads(descriptor_json.decode("utf-8"))
1492
+ assert "permissionSetNames" in descriptor
1493
+ assert "permissionSetLicenseDeveloperNames" not in descriptor
1494
+ return {"id": "08c000000000002AAA"}
1495
+
1496
+ mock_tooling_obj.create.side_effect = capture_create
1497
+ mock_tooling.return_value = mock_tooling_obj
1498
+
1499
+ task._create_version_request(
1500
+ "0Ho6g000000fy4ZCAQ",
1501
+ task.package_config,
1502
+ builder,
1503
+ skip_validation=True,
1504
+ )
1505
+
1506
+ @responses.activate
1507
+ def test_package_metadata_access_partial_config(self, get_task, devhub_config):
1508
+ """Test package_metadata_access with only permission_set_license_names"""
1509
+ task = get_task(
1510
+ {
1511
+ "package_type": "Managed",
1512
+ "package_name": "Test Package",
1513
+ }
1514
+ )
1515
+ task.package_config.package_metadata_access = {
1516
+ "permission_set_license_names": ["MetaLicense1"],
1517
+ }
1518
+
1519
+ builder = BasePackageZipBuilder()
1520
+
1521
+ # Mock the query for existing Package2VersionCreateRequest
1522
+ responses.add(
1523
+ "GET",
1524
+ f"{self.devhub_base_url}/tooling/query/",
1525
+ json={"size": 0, "records": []},
1526
+ )
1527
+
1528
+ with mock.patch.object(
1529
+ task, "_get_base_version_number"
1530
+ ) as mock_version, mock.patch.object(
1531
+ builder, "as_hash", return_value="testhash"
1532
+ ), mock.patch.object(
1533
+ builder, "as_bytes", return_value=b"testbytes"
1534
+ ), mock.patch.object(
1535
+ task, "_get_tooling_object"
1536
+ ) as mock_tooling:
1537
+ mock_version.return_value.increment.return_value.format.return_value = (
1538
+ "1.0.0.1"
1539
+ )
1540
+ mock_tooling_obj = mock.Mock()
1541
+
1542
+ def capture_create(request):
1543
+ import base64
1544
+
1545
+ version_info_b64 = request["VersionInfo"]
1546
+ version_info_bytes = base64.b64decode(version_info_b64)
1547
+ version_info_zip = zipfile.ZipFile(io.BytesIO(version_info_bytes), "r")
1548
+ descriptor_json = version_info_zip.read("package2-descriptor.json")
1549
+ descriptor = json.loads(descriptor_json.decode("utf-8"))
1550
+ assert "packageMetadataPermissionSetLicenseNames" in descriptor
1551
+ assert "packageMetadataPermissionSetNames" not in descriptor
1552
+ return {"id": "08c000000000002AAA"}
1553
+
1554
+ mock_tooling_obj.create.side_effect = capture_create
1555
+ mock_tooling.return_value = mock_tooling_obj
1556
+
1557
+ task._create_version_request(
1558
+ "0Ho6g000000fy4ZCAQ",
1559
+ task.package_config,
1560
+ builder,
1561
+ skip_validation=True,
1562
+ )