cumulusci-plus 5.0.0__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.

Files changed (744) hide show
  1. cumulusci/__about__.py +1 -0
  2. cumulusci/__init__.py +22 -0
  3. cumulusci/__main__.py +3 -0
  4. cumulusci/cli/__init__.py +0 -0
  5. cumulusci/cli/cci.py +244 -0
  6. cumulusci/cli/error.py +125 -0
  7. cumulusci/cli/flow.py +185 -0
  8. cumulusci/cli/logger.py +72 -0
  9. cumulusci/cli/org.py +692 -0
  10. cumulusci/cli/plan.py +181 -0
  11. cumulusci/cli/project.py +391 -0
  12. cumulusci/cli/robot.py +116 -0
  13. cumulusci/cli/runtime.py +190 -0
  14. cumulusci/cli/service.py +521 -0
  15. cumulusci/cli/task.py +295 -0
  16. cumulusci/cli/tests/__init__.py +0 -0
  17. cumulusci/cli/tests/test_cci.py +545 -0
  18. cumulusci/cli/tests/test_error.py +170 -0
  19. cumulusci/cli/tests/test_flow.py +276 -0
  20. cumulusci/cli/tests/test_logger.py +25 -0
  21. cumulusci/cli/tests/test_org.py +1438 -0
  22. cumulusci/cli/tests/test_plan.py +245 -0
  23. cumulusci/cli/tests/test_project.py +235 -0
  24. cumulusci/cli/tests/test_robot.py +177 -0
  25. cumulusci/cli/tests/test_runtime.py +197 -0
  26. cumulusci/cli/tests/test_service.py +853 -0
  27. cumulusci/cli/tests/test_task.py +266 -0
  28. cumulusci/cli/tests/test_ui.py +310 -0
  29. cumulusci/cli/tests/test_utils.py +122 -0
  30. cumulusci/cli/tests/utils.py +52 -0
  31. cumulusci/cli/ui.py +234 -0
  32. cumulusci/cli/utils.py +150 -0
  33. cumulusci/conftest.py +181 -0
  34. cumulusci/core/__init__.py +0 -0
  35. cumulusci/core/config/BaseConfig.py +5 -0
  36. cumulusci/core/config/BaseTaskFlowConfig.py +5 -0
  37. cumulusci/core/config/OrgConfig.py +5 -0
  38. cumulusci/core/config/ScratchOrgConfig.py +5 -0
  39. cumulusci/core/config/__init__.py +125 -0
  40. cumulusci/core/config/base_config.py +111 -0
  41. cumulusci/core/config/base_task_flow_config.py +82 -0
  42. cumulusci/core/config/marketing_cloud_service_config.py +83 -0
  43. cumulusci/core/config/oauth2_service_config.py +17 -0
  44. cumulusci/core/config/org_config.py +604 -0
  45. cumulusci/core/config/project_config.py +782 -0
  46. cumulusci/core/config/scratch_org_config.py +251 -0
  47. cumulusci/core/config/sfdx_org_config.py +220 -0
  48. cumulusci/core/config/tests/_test_config_backwards_compatibility.py +33 -0
  49. cumulusci/core/config/tests/test_config.py +1895 -0
  50. cumulusci/core/config/tests/test_config_expensive.py +839 -0
  51. cumulusci/core/config/tests/test_config_util.py +91 -0
  52. cumulusci/core/config/universal_config.py +88 -0
  53. cumulusci/core/config/util.py +18 -0
  54. cumulusci/core/datasets.py +303 -0
  55. cumulusci/core/debug.py +33 -0
  56. cumulusci/core/dependencies/__init__.py +55 -0
  57. cumulusci/core/dependencies/base.py +561 -0
  58. cumulusci/core/dependencies/dependencies.py +273 -0
  59. cumulusci/core/dependencies/github.py +177 -0
  60. cumulusci/core/dependencies/github_resolvers.py +244 -0
  61. cumulusci/core/dependencies/resolvers.py +580 -0
  62. cumulusci/core/dependencies/tests/__init__.py +0 -0
  63. cumulusci/core/dependencies/tests/conftest.py +385 -0
  64. cumulusci/core/dependencies/tests/test_dependencies.py +950 -0
  65. cumulusci/core/dependencies/tests/test_github.py +83 -0
  66. cumulusci/core/dependencies/tests/test_resolvers.py +1027 -0
  67. cumulusci/core/dependencies/utils.py +13 -0
  68. cumulusci/core/enums.py +11 -0
  69. cumulusci/core/exceptions.py +311 -0
  70. cumulusci/core/flowrunner.py +888 -0
  71. cumulusci/core/github.py +665 -0
  72. cumulusci/core/keychain/__init__.py +24 -0
  73. cumulusci/core/keychain/base_project_keychain.py +441 -0
  74. cumulusci/core/keychain/encrypted_file_project_keychain.py +945 -0
  75. cumulusci/core/keychain/environment_project_keychain.py +7 -0
  76. cumulusci/core/keychain/serialization.py +152 -0
  77. cumulusci/core/keychain/subprocess_keychain.py +24 -0
  78. cumulusci/core/keychain/tests/conftest.py +50 -0
  79. cumulusci/core/keychain/tests/test_base_project_keychain.py +299 -0
  80. cumulusci/core/keychain/tests/test_encrypted_file_project_keychain.py +1228 -0
  81. cumulusci/core/metadeploy/__init__.py +0 -0
  82. cumulusci/core/metadeploy/api.py +88 -0
  83. cumulusci/core/metadeploy/plans.py +25 -0
  84. cumulusci/core/metadeploy/tests/test_api.py +276 -0
  85. cumulusci/core/runtime.py +115 -0
  86. cumulusci/core/sfdx.py +162 -0
  87. cumulusci/core/source/__init__.py +16 -0
  88. cumulusci/core/source/github.py +50 -0
  89. cumulusci/core/source/local_folder.py +35 -0
  90. cumulusci/core/source_transforms/__init__.py +0 -0
  91. cumulusci/core/source_transforms/tests/test_transforms.py +1091 -0
  92. cumulusci/core/source_transforms/transforms.py +532 -0
  93. cumulusci/core/tasks.py +404 -0
  94. cumulusci/core/template_utils.py +59 -0
  95. cumulusci/core/tests/__init__.py +0 -0
  96. cumulusci/core/tests/cassettes/TestDatasetsE2E.test_datasets_e2e.yaml +215 -0
  97. cumulusci/core/tests/cassettes/TestDatasetsE2E.test_datasets_extract_standard_objects.yaml +199 -0
  98. cumulusci/core/tests/cassettes/TestDatasetsE2E.test_datasets_read_explicit_extract_declaration.yaml +3 -0
  99. cumulusci/core/tests/fake_remote_repo/cumulusci.yml +32 -0
  100. cumulusci/core/tests/fake_remote_repo/tasks/directory/example_2.py +6 -0
  101. cumulusci/core/tests/fake_remote_repo/tasks/example.py +43 -0
  102. cumulusci/core/tests/fake_remote_repo_2/cumulusci.yml +11 -0
  103. cumulusci/core/tests/fake_remote_repo_2/tasks/example_3.py +6 -0
  104. cumulusci/core/tests/test_datasets_e2e.py +386 -0
  105. cumulusci/core/tests/test_exceptions.py +11 -0
  106. cumulusci/core/tests/test_flowrunner.py +836 -0
  107. cumulusci/core/tests/test_github.py +942 -0
  108. cumulusci/core/tests/test_sfdx.py +138 -0
  109. cumulusci/core/tests/test_source.py +678 -0
  110. cumulusci/core/tests/test_tasks.py +262 -0
  111. cumulusci/core/tests/test_utils.py +141 -0
  112. cumulusci/core/tests/test_utils_merge_config.py +276 -0
  113. cumulusci/core/tests/test_versions.py +76 -0
  114. cumulusci/core/tests/untrusted_repo_child/cumulusci.yml +7 -0
  115. cumulusci/core/tests/untrusted_repo_child/tasks/untrusted_child.py +6 -0
  116. cumulusci/core/tests/untrusted_repo_parent/cumulusci.yml +26 -0
  117. cumulusci/core/tests/untrusted_repo_parent/tasks/untrusted_parent.py +6 -0
  118. cumulusci/core/tests/utils.py +116 -0
  119. cumulusci/core/tests/yaml/global.yaml +0 -0
  120. cumulusci/core/utils.py +402 -0
  121. cumulusci/core/versions.py +149 -0
  122. cumulusci/cumulusci.yml +1621 -0
  123. cumulusci/files/admin_profile.xml +20 -0
  124. cumulusci/files/delete_excludes.txt +424 -0
  125. cumulusci/files/templates/project/README.md +12 -0
  126. cumulusci/files/templates/project/cumulusci.yml +63 -0
  127. cumulusci/files/templates/project/dot-gitignore +60 -0
  128. cumulusci/files/templates/project/mapping.yml +45 -0
  129. cumulusci/files/templates/project/scratch_def.json +25 -0
  130. cumulusci/oauth/__init__.py +0 -0
  131. cumulusci/oauth/client.py +400 -0
  132. cumulusci/oauth/exceptions.py +9 -0
  133. cumulusci/oauth/salesforce.py +95 -0
  134. cumulusci/oauth/tests/__init__.py +0 -0
  135. cumulusci/oauth/tests/cassettes/test_get_device_code.yaml +22 -0
  136. cumulusci/oauth/tests/cassettes/test_get_device_oauth_token.yaml +74 -0
  137. cumulusci/oauth/tests/test_client.py +308 -0
  138. cumulusci/oauth/tests/test_salesforce.py +46 -0
  139. cumulusci/plugins/__init__.py +3 -0
  140. cumulusci/plugins/plugin_base.py +93 -0
  141. cumulusci/plugins/plugin_loader.py +59 -0
  142. cumulusci/robotframework/CumulusCI.py +340 -0
  143. cumulusci/robotframework/CumulusCI.robot +7 -0
  144. cumulusci/robotframework/Performance.py +165 -0
  145. cumulusci/robotframework/Salesforce.py +936 -0
  146. cumulusci/robotframework/Salesforce.robot +192 -0
  147. cumulusci/robotframework/SalesforceAPI.py +416 -0
  148. cumulusci/robotframework/SalesforcePlaywright.py +220 -0
  149. cumulusci/robotframework/SalesforcePlaywright.robot +40 -0
  150. cumulusci/robotframework/__init__.py +2 -0
  151. cumulusci/robotframework/base_library.py +39 -0
  152. cumulusci/robotframework/faker_mixin.py +89 -0
  153. cumulusci/robotframework/form_handlers.py +222 -0
  154. cumulusci/robotframework/javascript/cci_init.js +34 -0
  155. cumulusci/robotframework/javascript/cumulusci.js +4 -0
  156. cumulusci/robotframework/locator_manager.py +197 -0
  157. cumulusci/robotframework/locators_56.py +88 -0
  158. cumulusci/robotframework/locators_57.py +5 -0
  159. cumulusci/robotframework/pageobjects/BasePageObjects.py +433 -0
  160. cumulusci/robotframework/pageobjects/ObjectManagerPageObject.py +246 -0
  161. cumulusci/robotframework/pageobjects/PageObjectLibrary.py +45 -0
  162. cumulusci/robotframework/pageobjects/PageObjects.py +351 -0
  163. cumulusci/robotframework/pageobjects/__init__.py +12 -0
  164. cumulusci/robotframework/pageobjects/baseobjects.py +120 -0
  165. cumulusci/robotframework/perftests/short/collection_perf.robot +105 -0
  166. cumulusci/robotframework/tests/CustomObjectTestPage.py +10 -0
  167. cumulusci/robotframework/tests/FooTestPage.py +8 -0
  168. cumulusci/robotframework/tests/cumulusci/base.robot +40 -0
  169. cumulusci/robotframework/tests/cumulusci/bulkdata.robot +38 -0
  170. cumulusci/robotframework/tests/cumulusci/communities.robot +57 -0
  171. cumulusci/robotframework/tests/cumulusci/datagen.robot +84 -0
  172. cumulusci/robotframework/tests/salesforce/TestLibraryA.py +24 -0
  173. cumulusci/robotframework/tests/salesforce/TestLibraryB.py +20 -0
  174. cumulusci/robotframework/tests/salesforce/TestListener.py +93 -0
  175. cumulusci/robotframework/tests/salesforce/api.robot +178 -0
  176. cumulusci/robotframework/tests/salesforce/browsers.robot +143 -0
  177. cumulusci/robotframework/tests/salesforce/classic.robot +51 -0
  178. cumulusci/robotframework/tests/salesforce/create_contact.robot +59 -0
  179. cumulusci/robotframework/tests/salesforce/faker.robot +68 -0
  180. cumulusci/robotframework/tests/salesforce/forms.robot +172 -0
  181. cumulusci/robotframework/tests/salesforce/label_locator.robot +244 -0
  182. cumulusci/robotframework/tests/salesforce/labels.html +33 -0
  183. cumulusci/robotframework/tests/salesforce/locators.robot +149 -0
  184. cumulusci/robotframework/tests/salesforce/pageobjects/base_pageobjects.robot +100 -0
  185. cumulusci/robotframework/tests/salesforce/pageobjects/example_page_object.py +25 -0
  186. cumulusci/robotframework/tests/salesforce/pageobjects/listing_page.robot +115 -0
  187. cumulusci/robotframework/tests/salesforce/pageobjects/objectmanager.robot +74 -0
  188. cumulusci/robotframework/tests/salesforce/pageobjects/pageobjects.robot +171 -0
  189. cumulusci/robotframework/tests/salesforce/performance.robot +109 -0
  190. cumulusci/robotframework/tests/salesforce/playwright/javascript_keywords.robot +33 -0
  191. cumulusci/robotframework/tests/salesforce/playwright/open_test_browser.robot +48 -0
  192. cumulusci/robotframework/tests/salesforce/playwright/playwright.robot +24 -0
  193. cumulusci/robotframework/tests/salesforce/playwright/ui.robot +32 -0
  194. cumulusci/robotframework/tests/salesforce/populate.robot +89 -0
  195. cumulusci/robotframework/tests/salesforce/test_testlistener.py +37 -0
  196. cumulusci/robotframework/tests/salesforce/ui.robot +361 -0
  197. cumulusci/robotframework/tests/test_cumulusci_library.py +304 -0
  198. cumulusci/robotframework/tests/test_locator_manager.py +158 -0
  199. cumulusci/robotframework/tests/test_pageobjects.py +291 -0
  200. cumulusci/robotframework/tests/test_performance.py +38 -0
  201. cumulusci/robotframework/tests/test_salesforce.py +79 -0
  202. cumulusci/robotframework/tests/test_salesforce_locators.py +73 -0
  203. cumulusci/robotframework/tests/test_template_util.py +53 -0
  204. cumulusci/robotframework/tests/test_utils.py +106 -0
  205. cumulusci/robotframework/utils.py +283 -0
  206. cumulusci/salesforce_api/__init__.py +0 -0
  207. cumulusci/salesforce_api/exceptions.py +23 -0
  208. cumulusci/salesforce_api/filterable_objects.py +96 -0
  209. cumulusci/salesforce_api/mc_soap_envelopes.py +89 -0
  210. cumulusci/salesforce_api/metadata.py +721 -0
  211. cumulusci/salesforce_api/org_schema.py +571 -0
  212. cumulusci/salesforce_api/org_schema_models.py +226 -0
  213. cumulusci/salesforce_api/package_install.py +265 -0
  214. cumulusci/salesforce_api/package_zip.py +301 -0
  215. cumulusci/salesforce_api/rest_deploy.py +148 -0
  216. cumulusci/salesforce_api/retrieve_profile_api.py +301 -0
  217. cumulusci/salesforce_api/soap_envelopes.py +177 -0
  218. cumulusci/salesforce_api/tests/__init__.py +0 -0
  219. cumulusci/salesforce_api/tests/metadata_test_strings.py +24 -0
  220. cumulusci/salesforce_api/tests/test_metadata.py +1015 -0
  221. cumulusci/salesforce_api/tests/test_package_install.py +219 -0
  222. cumulusci/salesforce_api/tests/test_package_zip.py +380 -0
  223. cumulusci/salesforce_api/tests/test_rest_deploy.py +264 -0
  224. cumulusci/salesforce_api/tests/test_retrieve_profile_api.py +337 -0
  225. cumulusci/salesforce_api/tests/test_utils.py +124 -0
  226. cumulusci/salesforce_api/utils.py +51 -0
  227. cumulusci/schema/cumulusci.jsonschema.json +782 -0
  228. cumulusci/tasks/__init__.py +0 -0
  229. cumulusci/tasks/apex/__init__.py +0 -0
  230. cumulusci/tasks/apex/anon.py +157 -0
  231. cumulusci/tasks/apex/batch.py +180 -0
  232. cumulusci/tasks/apex/testrunner.py +835 -0
  233. cumulusci/tasks/apex/tests/cassettes/ManualEditTestApexIntegrationTests.test_run_tests__integration_test.yaml +703 -0
  234. cumulusci/tasks/apex/tests/test_apex_tasks.py +1558 -0
  235. cumulusci/tasks/base_source_control_task.py +17 -0
  236. cumulusci/tasks/bulkdata/__init__.py +15 -0
  237. cumulusci/tasks/bulkdata/base_generate_data_task.py +96 -0
  238. cumulusci/tasks/bulkdata/dates.py +97 -0
  239. cumulusci/tasks/bulkdata/delete.py +156 -0
  240. cumulusci/tasks/bulkdata/extract.py +441 -0
  241. cumulusci/tasks/bulkdata/extract_dataset_utils/calculate_dependencies.py +117 -0
  242. cumulusci/tasks/bulkdata/extract_dataset_utils/extract_yml.py +123 -0
  243. cumulusci/tasks/bulkdata/extract_dataset_utils/hardcoded_default_declarations.py +49 -0
  244. cumulusci/tasks/bulkdata/extract_dataset_utils/synthesize_extract_declarations.py +283 -0
  245. cumulusci/tasks/bulkdata/extract_dataset_utils/tests/test_extract_yml.py +142 -0
  246. cumulusci/tasks/bulkdata/extract_dataset_utils/tests/test_synthesize_extract_declarations.py +575 -0
  247. cumulusci/tasks/bulkdata/factory_utils.py +134 -0
  248. cumulusci/tasks/bulkdata/generate.py +4 -0
  249. cumulusci/tasks/bulkdata/generate_and_load_data.py +232 -0
  250. cumulusci/tasks/bulkdata/generate_and_load_data_from_yaml.py +19 -0
  251. cumulusci/tasks/bulkdata/generate_from_yaml.py +183 -0
  252. cumulusci/tasks/bulkdata/generate_mapping.py +434 -0
  253. cumulusci/tasks/bulkdata/generate_mapping_utils/dependency_map.py +169 -0
  254. cumulusci/tasks/bulkdata/generate_mapping_utils/extract_mapping_file_generator.py +45 -0
  255. cumulusci/tasks/bulkdata/generate_mapping_utils/generate_mapping_from_declarations.py +121 -0
  256. cumulusci/tasks/bulkdata/generate_mapping_utils/load_mapping_file_generator.py +127 -0
  257. cumulusci/tasks/bulkdata/generate_mapping_utils/mapping_generator_post_processes.py +53 -0
  258. cumulusci/tasks/bulkdata/generate_mapping_utils/mapping_transforms.py +139 -0
  259. cumulusci/tasks/bulkdata/generate_mapping_utils/tests/test_generate_extract_mapping_from_declarations.py +135 -0
  260. cumulusci/tasks/bulkdata/generate_mapping_utils/tests/test_generate_load_mapping_from_declarations.py +330 -0
  261. cumulusci/tasks/bulkdata/generate_mapping_utils/tests/test_mapping_generator_post_processes.py +60 -0
  262. cumulusci/tasks/bulkdata/generate_mapping_utils/tests/test_mapping_transforms.py +188 -0
  263. cumulusci/tasks/bulkdata/load.py +1196 -0
  264. cumulusci/tasks/bulkdata/mapping_parser.py +811 -0
  265. cumulusci/tasks/bulkdata/query_transformers.py +264 -0
  266. cumulusci/tasks/bulkdata/select_utils.py +792 -0
  267. cumulusci/tasks/bulkdata/snowfakery.py +753 -0
  268. cumulusci/tasks/bulkdata/snowfakery_utils/queue_manager.py +478 -0
  269. cumulusci/tasks/bulkdata/snowfakery_utils/snowfakery_run_until.py +141 -0
  270. cumulusci/tasks/bulkdata/snowfakery_utils/snowfakery_working_directory.py +53 -0
  271. cumulusci/tasks/bulkdata/snowfakery_utils/subtask_configurator.py +64 -0
  272. cumulusci/tasks/bulkdata/step.py +1242 -0
  273. cumulusci/tasks/bulkdata/tests/__init__.py +0 -0
  274. cumulusci/tasks/bulkdata/tests/cassettes/TestSelect.test_select_random_strategy.yaml +147 -0
  275. cumulusci/tasks/bulkdata/tests/cassettes/TestSelect.test_select_similarity_annoy_strategy.yaml +123 -0
  276. cumulusci/tasks/bulkdata/tests/cassettes/TestSelect.test_select_similarity_select_and_insert_strategy.yaml +313 -0
  277. cumulusci/tasks/bulkdata/tests/cassettes/TestSelect.test_select_similarity_select_and_insert_strategy_bulk.yaml +550 -0
  278. cumulusci/tasks/bulkdata/tests/cassettes/TestSelect.test_select_similarity_strategy.yaml +175 -0
  279. cumulusci/tasks/bulkdata/tests/cassettes/TestSelect.test_select_standard_strategy.yaml +147 -0
  280. cumulusci/tasks/bulkdata/tests/cassettes/TestSnowfakery.test_run_until_records_in_org__multiple_needed.yaml +69 -0
  281. cumulusci/tasks/bulkdata/tests/cassettes/TestSnowfakery.test_run_until_records_in_org__none_needed.yaml +22 -0
  282. cumulusci/tasks/bulkdata/tests/cassettes/TestSnowfakery.test_run_until_records_in_org__one_needed.yaml +24 -0
  283. cumulusci/tasks/bulkdata/tests/cassettes/TestSnowfakery.test_snowfakery_query_salesforce.yaml +25 -0
  284. cumulusci/tasks/bulkdata/tests/cassettes/TestUpdatesIntegrationTests.test_updates_task.yaml +80 -0
  285. cumulusci/tasks/bulkdata/tests/cassettes/TestUpsert.test_simple_upsert__rest.yaml +270 -0
  286. cumulusci/tasks/bulkdata/tests/cassettes/TestUpsert.test_upsert__rest.yaml +267 -0
  287. cumulusci/tasks/bulkdata/tests/cassettes/TestUpsert.test_upsert_complex_external_id_field__rest.yaml +369 -0
  288. cumulusci/tasks/bulkdata/tests/cassettes/TestUpsert.test_upsert_complex_external_id_field_rest__duplicate_error.yaml +204 -0
  289. cumulusci/tasks/bulkdata/tests/cassettes/TestUpsert.test_upsert_complex_fields__bulk.yaml +675 -0
  290. cumulusci/tasks/bulkdata/tests/dummy_data_factory.py +36 -0
  291. cumulusci/tasks/bulkdata/tests/integration_test_utils.py +49 -0
  292. cumulusci/tasks/bulkdata/tests/mapping-oid.yml +87 -0
  293. cumulusci/tasks/bulkdata/tests/mapping_after.yml +38 -0
  294. cumulusci/tasks/bulkdata/tests/mapping_poly.yml +34 -0
  295. cumulusci/tasks/bulkdata/tests/mapping_poly_incomplete.yml +20 -0
  296. cumulusci/tasks/bulkdata/tests/mapping_poly_wrong.yml +21 -0
  297. cumulusci/tasks/bulkdata/tests/mapping_select.yml +20 -0
  298. cumulusci/tasks/bulkdata/tests/mapping_select_invalid_strategy.yml +20 -0
  299. cumulusci/tasks/bulkdata/tests/mapping_select_invalid_threshold__invalid_number.yml +21 -0
  300. cumulusci/tasks/bulkdata/tests/mapping_select_invalid_threshold__invalid_strategy.yml +21 -0
  301. cumulusci/tasks/bulkdata/tests/mapping_select_invalid_threshold__non_float.yml +21 -0
  302. cumulusci/tasks/bulkdata/tests/mapping_select_missing_priority_fields.yml +22 -0
  303. cumulusci/tasks/bulkdata/tests/mapping_select_no_priority_fields.yml +18 -0
  304. cumulusci/tasks/bulkdata/tests/mapping_simple.yml +27 -0
  305. cumulusci/tasks/bulkdata/tests/mapping_v1.yml +28 -0
  306. cumulusci/tasks/bulkdata/tests/mapping_v2.yml +21 -0
  307. cumulusci/tasks/bulkdata/tests/mapping_v3.yml +32 -0
  308. cumulusci/tasks/bulkdata/tests/mapping_vanilla_sf.yml +69 -0
  309. cumulusci/tasks/bulkdata/tests/mock_data_factory_without_mapping.py +12 -0
  310. cumulusci/tasks/bulkdata/tests/person_accounts.yml +23 -0
  311. cumulusci/tasks/bulkdata/tests/person_accounts_minimal.yml +15 -0
  312. cumulusci/tasks/bulkdata/tests/recordtypes.yml +8 -0
  313. cumulusci/tasks/bulkdata/tests/recordtypes_2.yml +6 -0
  314. cumulusci/tasks/bulkdata/tests/recordtypes_with_ispersontype.yml +8 -0
  315. cumulusci/tasks/bulkdata/tests/snowfakery/child/child2.yml +3 -0
  316. cumulusci/tasks/bulkdata/tests/snowfakery/child.yml +4 -0
  317. cumulusci/tasks/bulkdata/tests/snowfakery/gen_npsp_standard_objects.recipe.yml +89 -0
  318. cumulusci/tasks/bulkdata/tests/snowfakery/include_parent.yml +3 -0
  319. cumulusci/tasks/bulkdata/tests/snowfakery/npsp_standard_objects_macros.yml +34 -0
  320. cumulusci/tasks/bulkdata/tests/snowfakery/options.recipe.yml +6 -0
  321. cumulusci/tasks/bulkdata/tests/snowfakery/query_snowfakery.recipe.yml +16 -0
  322. cumulusci/tasks/bulkdata/tests/snowfakery/sf_standard_object_macros.yml +83 -0
  323. cumulusci/tasks/bulkdata/tests/snowfakery/simple_snowfakery.load.yml +2 -0
  324. cumulusci/tasks/bulkdata/tests/snowfakery/simple_snowfakery.recipe.yml +13 -0
  325. cumulusci/tasks/bulkdata/tests/snowfakery/simple_snowfakery_2.load.yml +5 -0
  326. cumulusci/tasks/bulkdata/tests/snowfakery/simple_snowfakery_channels.load.yml +13 -0
  327. cumulusci/tasks/bulkdata/tests/snowfakery/simple_snowfakery_channels.recipe.yml +12 -0
  328. cumulusci/tasks/bulkdata/tests/snowfakery/simple_snowfakery_channels_2.load.yml +13 -0
  329. cumulusci/tasks/bulkdata/tests/snowfakery/unique_values.recipe.yml +4 -0
  330. cumulusci/tasks/bulkdata/tests/snowfakery/upsert.recipe.yml +23 -0
  331. cumulusci/tasks/bulkdata/tests/snowfakery/upsert_2.recipe.yml +29 -0
  332. cumulusci/tasks/bulkdata/tests/snowfakery/upsert_before.yml +10 -0
  333. cumulusci/tasks/bulkdata/tests/test_base_generate_data_tasks.py +61 -0
  334. cumulusci/tasks/bulkdata/tests/test_dates.py +99 -0
  335. cumulusci/tasks/bulkdata/tests/test_delete.py +404 -0
  336. cumulusci/tasks/bulkdata/tests/test_extract.py +1311 -0
  337. cumulusci/tasks/bulkdata/tests/test_factory_utils.py +55 -0
  338. cumulusci/tasks/bulkdata/tests/test_generate_and_load.py +252 -0
  339. cumulusci/tasks/bulkdata/tests/test_generate_from_snowfakery_task.py +343 -0
  340. cumulusci/tasks/bulkdata/tests/test_generatemapping.py +1039 -0
  341. cumulusci/tasks/bulkdata/tests/test_load.py +3175 -0
  342. cumulusci/tasks/bulkdata/tests/test_mapping_parser.py +1658 -0
  343. cumulusci/tasks/bulkdata/tests/test_query_db__joins_self_lookups.yml +12 -0
  344. cumulusci/tasks/bulkdata/tests/test_query_db_joins_lookups.yml +26 -0
  345. cumulusci/tasks/bulkdata/tests/test_query_db_joins_lookups_select.yml +48 -0
  346. cumulusci/tasks/bulkdata/tests/test_select.py +171 -0
  347. cumulusci/tasks/bulkdata/tests/test_select_utils.py +1057 -0
  348. cumulusci/tasks/bulkdata/tests/test_snowfakery.py +1153 -0
  349. cumulusci/tasks/bulkdata/tests/test_step.py +3957 -0
  350. cumulusci/tasks/bulkdata/tests/test_updates.py +513 -0
  351. cumulusci/tasks/bulkdata/tests/test_upsert.py +1015 -0
  352. cumulusci/tasks/bulkdata/tests/test_utils.py +158 -0
  353. cumulusci/tasks/bulkdata/tests/testdata.db +0 -0
  354. cumulusci/tasks/bulkdata/tests/update_describe.py +50 -0
  355. cumulusci/tasks/bulkdata/tests/update_person_accounts.yml +23 -0
  356. cumulusci/tasks/bulkdata/tests/utils.py +114 -0
  357. cumulusci/tasks/bulkdata/update_data.py +260 -0
  358. cumulusci/tasks/bulkdata/upsert_utils.py +130 -0
  359. cumulusci/tasks/bulkdata/utils.py +249 -0
  360. cumulusci/tasks/command.py +178 -0
  361. cumulusci/tasks/connectedapp.py +186 -0
  362. cumulusci/tasks/create_package_version.py +778 -0
  363. cumulusci/tasks/datadictionary.py +745 -0
  364. cumulusci/tasks/dx_convert_from.py +26 -0
  365. cumulusci/tasks/github/__init__.py +17 -0
  366. cumulusci/tasks/github/base.py +16 -0
  367. cumulusci/tasks/github/commit_status.py +13 -0
  368. cumulusci/tasks/github/merge.py +11 -0
  369. cumulusci/tasks/github/publish.py +11 -0
  370. cumulusci/tasks/github/pull_request.py +11 -0
  371. cumulusci/tasks/github/release.py +11 -0
  372. cumulusci/tasks/github/release_report.py +11 -0
  373. cumulusci/tasks/github/tag.py +11 -0
  374. cumulusci/tasks/github/tests/__init__.py +0 -0
  375. cumulusci/tasks/github/tests/test_util.py +202 -0
  376. cumulusci/tasks/github/tests/test_vcs_migration.py +44 -0
  377. cumulusci/tasks/github/tests/util_github_api.py +666 -0
  378. cumulusci/tasks/github/util.py +252 -0
  379. cumulusci/tasks/marketing_cloud/__init__.py +0 -0
  380. cumulusci/tasks/marketing_cloud/api.py +188 -0
  381. cumulusci/tasks/marketing_cloud/base.py +38 -0
  382. cumulusci/tasks/marketing_cloud/deploy.py +345 -0
  383. cumulusci/tasks/marketing_cloud/get_user_info.py +40 -0
  384. cumulusci/tasks/marketing_cloud/mc_constants.py +1 -0
  385. cumulusci/tasks/marketing_cloud/tests/__init__.py +0 -0
  386. cumulusci/tasks/marketing_cloud/tests/conftest.py +46 -0
  387. cumulusci/tasks/marketing_cloud/tests/expected-payload.json +110 -0
  388. cumulusci/tasks/marketing_cloud/tests/test_api.py +97 -0
  389. cumulusci/tasks/marketing_cloud/tests/test_api_soap_envelopes.py +145 -0
  390. cumulusci/tasks/marketing_cloud/tests/test_base.py +14 -0
  391. cumulusci/tasks/marketing_cloud/tests/test_deploy.py +400 -0
  392. cumulusci/tasks/marketing_cloud/tests/test_get_user_info.py +141 -0
  393. cumulusci/tasks/marketing_cloud/tests/validation-response.json +39 -0
  394. cumulusci/tasks/metadata/__init__.py +0 -0
  395. cumulusci/tasks/metadata/ee_src.py +94 -0
  396. cumulusci/tasks/metadata/managed_src.py +100 -0
  397. cumulusci/tasks/metadata/metadata_map.yml +868 -0
  398. cumulusci/tasks/metadata/modify.py +99 -0
  399. cumulusci/tasks/metadata/package.py +684 -0
  400. cumulusci/tasks/metadata/tests/__init__.py +0 -0
  401. cumulusci/tasks/metadata/tests/package_metadata/namespaced_report_folder/.hidden/.keep +0 -0
  402. cumulusci/tasks/metadata/tests/package_metadata/namespaced_report_folder/destructiveChanges.xml +9 -0
  403. cumulusci/tasks/metadata/tests/package_metadata/namespaced_report_folder/package.xml +9 -0
  404. cumulusci/tasks/metadata/tests/package_metadata/namespaced_report_folder/package_install_uninstall.xml +11 -0
  405. cumulusci/tasks/metadata/tests/package_metadata/namespaced_report_folder/reports/namespace__TestFolder/TestReport.report +3 -0
  406. cumulusci/tasks/metadata/tests/sample_package.xml +9 -0
  407. cumulusci/tasks/metadata/tests/test_ee_src.py +112 -0
  408. cumulusci/tasks/metadata/tests/test_managed_src.py +111 -0
  409. cumulusci/tasks/metadata/tests/test_modify.py +123 -0
  410. cumulusci/tasks/metadata/tests/test_package.py +476 -0
  411. cumulusci/tasks/metadata_etl/__init__.py +29 -0
  412. cumulusci/tasks/metadata_etl/base.py +436 -0
  413. cumulusci/tasks/metadata_etl/duplicate_rules.py +24 -0
  414. cumulusci/tasks/metadata_etl/field_sets.py +70 -0
  415. cumulusci/tasks/metadata_etl/help_text.py +92 -0
  416. cumulusci/tasks/metadata_etl/layouts.py +550 -0
  417. cumulusci/tasks/metadata_etl/objects.py +68 -0
  418. cumulusci/tasks/metadata_etl/permissions.py +167 -0
  419. cumulusci/tasks/metadata_etl/picklists.py +221 -0
  420. cumulusci/tasks/metadata_etl/remote_site_settings.py +99 -0
  421. cumulusci/tasks/metadata_etl/sharing.py +138 -0
  422. cumulusci/tasks/metadata_etl/tests/test_base.py +512 -0
  423. cumulusci/tasks/metadata_etl/tests/test_duplicate_rules.py +22 -0
  424. cumulusci/tasks/metadata_etl/tests/test_field_sets.py +156 -0
  425. cumulusci/tasks/metadata_etl/tests/test_help_text.py +387 -0
  426. cumulusci/tasks/metadata_etl/tests/test_ip_ranges.py +85 -0
  427. cumulusci/tasks/metadata_etl/tests/test_layouts.py +858 -0
  428. cumulusci/tasks/metadata_etl/tests/test_objects.py +236 -0
  429. cumulusci/tasks/metadata_etl/tests/test_permissions.py +223 -0
  430. cumulusci/tasks/metadata_etl/tests/test_picklists.py +547 -0
  431. cumulusci/tasks/metadata_etl/tests/test_remote_site_settings.py +46 -0
  432. cumulusci/tasks/metadata_etl/tests/test_sharing.py +333 -0
  433. cumulusci/tasks/metadata_etl/tests/test_value_sets.py +298 -0
  434. cumulusci/tasks/metadata_etl/value_sets.py +106 -0
  435. cumulusci/tasks/metadeploy.py +393 -0
  436. cumulusci/tasks/metaxml.py +88 -0
  437. cumulusci/tasks/preflight/__init__.py +0 -0
  438. cumulusci/tasks/preflight/dataset_load.py +49 -0
  439. cumulusci/tasks/preflight/licenses.py +86 -0
  440. cumulusci/tasks/preflight/packages.py +14 -0
  441. cumulusci/tasks/preflight/permsets.py +23 -0
  442. cumulusci/tasks/preflight/recordtypes.py +16 -0
  443. cumulusci/tasks/preflight/retrieve_tasks.py +30 -0
  444. cumulusci/tasks/preflight/settings.py +77 -0
  445. cumulusci/tasks/preflight/sobjects.py +202 -0
  446. cumulusci/tasks/preflight/tests/test_dataset_load.py +85 -0
  447. cumulusci/tasks/preflight/tests/test_licenses.py +174 -0
  448. cumulusci/tasks/preflight/tests/test_packages.py +14 -0
  449. cumulusci/tasks/preflight/tests/test_permset_preflights.py +51 -0
  450. cumulusci/tasks/preflight/tests/test_recordtypes.py +30 -0
  451. cumulusci/tasks/preflight/tests/test_retrieve_tasks.py +62 -0
  452. cumulusci/tasks/preflight/tests/test_settings.py +130 -0
  453. cumulusci/tasks/preflight/tests/test_sobjects.py +231 -0
  454. cumulusci/tasks/push/README.md +59 -0
  455. cumulusci/tasks/push/__init__.py +0 -0
  456. cumulusci/tasks/push/push_api.py +659 -0
  457. cumulusci/tasks/push/pushfails.py +136 -0
  458. cumulusci/tasks/push/tasks.py +476 -0
  459. cumulusci/tasks/push/tests/conftest.py +263 -0
  460. cumulusci/tasks/push/tests/test_push_api.py +951 -0
  461. cumulusci/tasks/push/tests/test_push_tasks.py +659 -0
  462. cumulusci/tasks/release_notes/README.md +63 -0
  463. cumulusci/tasks/release_notes/__init__.py +0 -0
  464. cumulusci/tasks/release_notes/exceptions.py +5 -0
  465. cumulusci/tasks/release_notes/generator.py +137 -0
  466. cumulusci/tasks/release_notes/parser.py +232 -0
  467. cumulusci/tasks/release_notes/provider.py +44 -0
  468. cumulusci/tasks/release_notes/task.py +300 -0
  469. cumulusci/tasks/release_notes/tests/__init__.py +0 -0
  470. cumulusci/tasks/release_notes/tests/change_notes/full/example1.md +17 -0
  471. cumulusci/tasks/release_notes/tests/change_notes/multi/1.txt +1 -0
  472. cumulusci/tasks/release_notes/tests/change_notes/multi/2.txt +1 -0
  473. cumulusci/tasks/release_notes/tests/change_notes/multi/3.txt +1 -0
  474. cumulusci/tasks/release_notes/tests/change_notes/single/1.txt +1 -0
  475. cumulusci/tasks/release_notes/tests/test_generator.py +582 -0
  476. cumulusci/tasks/release_notes/tests/test_parser.py +867 -0
  477. cumulusci/tasks/release_notes/tests/test_provider.py +512 -0
  478. cumulusci/tasks/release_notes/tests/test_task.py +461 -0
  479. cumulusci/tasks/release_notes/tests/utils.py +153 -0
  480. cumulusci/tasks/robotframework/__init__.py +3 -0
  481. cumulusci/tasks/robotframework/debugger/DebugListener.py +100 -0
  482. cumulusci/tasks/robotframework/debugger/__init__.py +10 -0
  483. cumulusci/tasks/robotframework/debugger/model.py +87 -0
  484. cumulusci/tasks/robotframework/debugger/ui.py +259 -0
  485. cumulusci/tasks/robotframework/libdoc.py +269 -0
  486. cumulusci/tasks/robotframework/robotframework.py +392 -0
  487. cumulusci/tasks/robotframework/stylesheet.css +130 -0
  488. cumulusci/tasks/robotframework/template.html +109 -0
  489. cumulusci/tasks/robotframework/tests/TestLibrary.py +18 -0
  490. cumulusci/tasks/robotframework/tests/TestPageObjects.py +31 -0
  491. cumulusci/tasks/robotframework/tests/TestResource.robot +8 -0
  492. cumulusci/tasks/robotframework/tests/failing_tests.robot +16 -0
  493. cumulusci/tasks/robotframework/tests/performance.robot +23 -0
  494. cumulusci/tasks/robotframework/tests/test_browser_proxies.py +137 -0
  495. cumulusci/tasks/robotframework/tests/test_debugger.py +360 -0
  496. cumulusci/tasks/robotframework/tests/test_robot_parallel.py +141 -0
  497. cumulusci/tasks/robotframework/tests/test_robotframework.py +860 -0
  498. cumulusci/tasks/salesforce/BaseRetrieveMetadata.py +58 -0
  499. cumulusci/tasks/salesforce/BaseSalesforceApiTask.py +45 -0
  500. cumulusci/tasks/salesforce/BaseSalesforceMetadataApiTask.py +18 -0
  501. cumulusci/tasks/salesforce/BaseSalesforceTask.py +4 -0
  502. cumulusci/tasks/salesforce/BaseUninstallMetadata.py +41 -0
  503. cumulusci/tasks/salesforce/CreateCommunity.py +124 -0
  504. cumulusci/tasks/salesforce/CreatePackage.py +29 -0
  505. cumulusci/tasks/salesforce/Deploy.py +240 -0
  506. cumulusci/tasks/salesforce/DeployBundles.py +88 -0
  507. cumulusci/tasks/salesforce/DescribeMetadataTypes.py +26 -0
  508. cumulusci/tasks/salesforce/EnsureRecordTypes.py +202 -0
  509. cumulusci/tasks/salesforce/GetInstalledPackages.py +8 -0
  510. cumulusci/tasks/salesforce/ListCommunities.py +40 -0
  511. cumulusci/tasks/salesforce/ListCommunityTemplates.py +19 -0
  512. cumulusci/tasks/salesforce/PublishCommunity.py +62 -0
  513. cumulusci/tasks/salesforce/RetrievePackaged.py +41 -0
  514. cumulusci/tasks/salesforce/RetrieveReportsAndDashboards.py +82 -0
  515. cumulusci/tasks/salesforce/RetrieveUnpackaged.py +36 -0
  516. cumulusci/tasks/salesforce/SOQLQuery.py +39 -0
  517. cumulusci/tasks/salesforce/UninstallLocal.py +15 -0
  518. cumulusci/tasks/salesforce/UninstallLocalBundles.py +28 -0
  519. cumulusci/tasks/salesforce/UninstallLocalNamespacedBundles.py +58 -0
  520. cumulusci/tasks/salesforce/UninstallPackage.py +32 -0
  521. cumulusci/tasks/salesforce/UninstallPackaged.py +56 -0
  522. cumulusci/tasks/salesforce/UpdateAdminProfile.py +8 -0
  523. cumulusci/tasks/salesforce/__init__.py +79 -0
  524. cumulusci/tasks/salesforce/activate_flow.py +74 -0
  525. cumulusci/tasks/salesforce/check_components.py +324 -0
  526. cumulusci/tasks/salesforce/composite.py +142 -0
  527. cumulusci/tasks/salesforce/create_permission_sets.py +35 -0
  528. cumulusci/tasks/salesforce/custom_settings.py +134 -0
  529. cumulusci/tasks/salesforce/custom_settings_wait.py +132 -0
  530. cumulusci/tasks/salesforce/enable_prediction.py +107 -0
  531. cumulusci/tasks/salesforce/insert_record.py +40 -0
  532. cumulusci/tasks/salesforce/install_package_version.py +242 -0
  533. cumulusci/tasks/salesforce/license_preflights.py +8 -0
  534. cumulusci/tasks/salesforce/network_member_group.py +178 -0
  535. cumulusci/tasks/salesforce/nonsourcetracking.py +228 -0
  536. cumulusci/tasks/salesforce/org_settings.py +193 -0
  537. cumulusci/tasks/salesforce/package_upload.py +328 -0
  538. cumulusci/tasks/salesforce/profiles.py +74 -0
  539. cumulusci/tasks/salesforce/promote_package_version.py +376 -0
  540. cumulusci/tasks/salesforce/retrieve_profile.py +195 -0
  541. cumulusci/tasks/salesforce/salesforce_files.py +244 -0
  542. cumulusci/tasks/salesforce/sourcetracking.py +507 -0
  543. cumulusci/tasks/salesforce/tests/__init__.py +3 -0
  544. cumulusci/tasks/salesforce/tests/test_CreateCommunity.py +278 -0
  545. cumulusci/tasks/salesforce/tests/test_CreatePackage.py +22 -0
  546. cumulusci/tasks/salesforce/tests/test_Deploy.py +470 -0
  547. cumulusci/tasks/salesforce/tests/test_DeployBundles.py +76 -0
  548. cumulusci/tasks/salesforce/tests/test_EnsureRecordTypes.py +345 -0
  549. cumulusci/tasks/salesforce/tests/test_ListCommunities.py +84 -0
  550. cumulusci/tasks/salesforce/tests/test_ListCommunityTemplates.py +49 -0
  551. cumulusci/tasks/salesforce/tests/test_PackageUpload.py +547 -0
  552. cumulusci/tasks/salesforce/tests/test_ProfileGrantAllAccess.py +699 -0
  553. cumulusci/tasks/salesforce/tests/test_PublishCommunity.py +181 -0
  554. cumulusci/tasks/salesforce/tests/test_RetrievePackaged.py +24 -0
  555. cumulusci/tasks/salesforce/tests/test_RetrieveReportsAndDashboards.py +56 -0
  556. cumulusci/tasks/salesforce/tests/test_RetrieveUnpackaged.py +21 -0
  557. cumulusci/tasks/salesforce/tests/test_SOQLQuery.py +30 -0
  558. cumulusci/tasks/salesforce/tests/test_UninstallLocal.py +15 -0
  559. cumulusci/tasks/salesforce/tests/test_UninstallLocalBundles.py +19 -0
  560. cumulusci/tasks/salesforce/tests/test_UninstallLocalNamespacedBundles.py +22 -0
  561. cumulusci/tasks/salesforce/tests/test_UninstallPackage.py +19 -0
  562. cumulusci/tasks/salesforce/tests/test_UninstallPackaged.py +66 -0
  563. cumulusci/tasks/salesforce/tests/test_UninstallPackagedIncremental.py +127 -0
  564. cumulusci/tasks/salesforce/tests/test_activate_flow.py +132 -0
  565. cumulusci/tasks/salesforce/tests/test_base_tasks.py +110 -0
  566. cumulusci/tasks/salesforce/tests/test_check_components.py +445 -0
  567. cumulusci/tasks/salesforce/tests/test_composite.py +250 -0
  568. cumulusci/tasks/salesforce/tests/test_create_permission_sets.py +41 -0
  569. cumulusci/tasks/salesforce/tests/test_custom_settings.py +227 -0
  570. cumulusci/tasks/salesforce/tests/test_custom_settings_wait.py +174 -0
  571. cumulusci/tasks/salesforce/tests/test_describemetadatatypes.py +18 -0
  572. cumulusci/tasks/salesforce/tests/test_enable_prediction.py +240 -0
  573. cumulusci/tasks/salesforce/tests/test_insert_record.py +110 -0
  574. cumulusci/tasks/salesforce/tests/test_install_package_version.py +464 -0
  575. cumulusci/tasks/salesforce/tests/test_network_member_group.py +444 -0
  576. cumulusci/tasks/salesforce/tests/test_nonsourcetracking.py +235 -0
  577. cumulusci/tasks/salesforce/tests/test_org_settings.py +407 -0
  578. cumulusci/tasks/salesforce/tests/test_profiles.py +202 -0
  579. cumulusci/tasks/salesforce/tests/test_retrieve_profile.py +287 -0
  580. cumulusci/tasks/salesforce/tests/test_salesforce_files.py +228 -0
  581. cumulusci/tasks/salesforce/tests/test_sourcetracking.py +350 -0
  582. cumulusci/tasks/salesforce/tests/test_trigger_handlers.py +300 -0
  583. cumulusci/tasks/salesforce/tests/test_update_dependencies.py +509 -0
  584. cumulusci/tasks/salesforce/tests/util.py +79 -0
  585. cumulusci/tasks/salesforce/trigger_handlers.py +119 -0
  586. cumulusci/tasks/salesforce/uninstall_packaged_incremental.py +136 -0
  587. cumulusci/tasks/salesforce/update_dependencies.py +290 -0
  588. cumulusci/tasks/salesforce/update_profile.py +339 -0
  589. cumulusci/tasks/salesforce/users/permsets.py +227 -0
  590. cumulusci/tasks/salesforce/users/photos.py +162 -0
  591. cumulusci/tasks/salesforce/users/tests/photo.mock.txt +1 -0
  592. cumulusci/tasks/salesforce/users/tests/test_permsets.py +950 -0
  593. cumulusci/tasks/salesforce/users/tests/test_photos.py +373 -0
  594. cumulusci/tasks/sample_data/capture_sample_data.py +77 -0
  595. cumulusci/tasks/sample_data/load_sample_data.py +85 -0
  596. cumulusci/tasks/sample_data/test_capture_sample_data.py +117 -0
  597. cumulusci/tasks/sample_data/test_load_sample_data.py +121 -0
  598. cumulusci/tasks/sfdx.py +83 -0
  599. cumulusci/tasks/tests/__init__.py +1 -0
  600. cumulusci/tasks/tests/conftest.py +30 -0
  601. cumulusci/tasks/tests/test_command.py +129 -0
  602. cumulusci/tasks/tests/test_connectedapp.py +236 -0
  603. cumulusci/tasks/tests/test_create_package_version.py +847 -0
  604. cumulusci/tasks/tests/test_datadictionary.py +1575 -0
  605. cumulusci/tasks/tests/test_dx_convert_from.py +60 -0
  606. cumulusci/tasks/tests/test_metadeploy.py +624 -0
  607. cumulusci/tasks/tests/test_metaxml.py +99 -0
  608. cumulusci/tasks/tests/test_promote_package_version.py +488 -0
  609. cumulusci/tasks/tests/test_pushfails.py +96 -0
  610. cumulusci/tasks/tests/test_salesforce.py +72 -0
  611. cumulusci/tasks/tests/test_sfdx.py +105 -0
  612. cumulusci/tasks/tests/test_util.py +207 -0
  613. cumulusci/tasks/util.py +261 -0
  614. cumulusci/tasks/vcs/__init__.py +19 -0
  615. cumulusci/tasks/vcs/commit_status.py +58 -0
  616. cumulusci/tasks/vcs/create_commit_status.py +37 -0
  617. cumulusci/tasks/vcs/download_extract.py +199 -0
  618. cumulusci/tasks/vcs/merge.py +298 -0
  619. cumulusci/tasks/vcs/publish.py +207 -0
  620. cumulusci/tasks/vcs/pull_request.py +9 -0
  621. cumulusci/tasks/vcs/release.py +134 -0
  622. cumulusci/tasks/vcs/release_report.py +105 -0
  623. cumulusci/tasks/vcs/tag.py +31 -0
  624. cumulusci/tasks/vcs/tests/github/test_commit_status.py +196 -0
  625. cumulusci/tasks/vcs/tests/github/test_download_extract.py +896 -0
  626. cumulusci/tasks/vcs/tests/github/test_merge.py +1118 -0
  627. cumulusci/tasks/vcs/tests/github/test_publish.py +823 -0
  628. cumulusci/tasks/vcs/tests/github/test_pull_request.py +29 -0
  629. cumulusci/tasks/vcs/tests/github/test_release.py +390 -0
  630. cumulusci/tasks/vcs/tests/github/test_release_report.py +109 -0
  631. cumulusci/tasks/vcs/tests/github/test_tag.py +90 -0
  632. cumulusci/tasks/vlocity/exceptions.py +2 -0
  633. cumulusci/tasks/vlocity/tests/test_vlocity.py +283 -0
  634. cumulusci/tasks/vlocity/vlocity.py +342 -0
  635. cumulusci/tests/__init__.py +1 -0
  636. cumulusci/tests/cassettes/GET_sobjects_Account_PersonAccount_describe.yaml +18 -0
  637. cumulusci/tests/cassettes/TestIntegrationInfrastructure.test_integration_tests.yaml +19 -0
  638. cumulusci/tests/pytest_plugins/pytest_sf_orgconnect.py +307 -0
  639. cumulusci/tests/pytest_plugins/pytest_sf_vcr.py +275 -0
  640. cumulusci/tests/pytest_plugins/pytest_sf_vcr_serializer.py +160 -0
  641. cumulusci/tests/pytest_plugins/pytest_typeguard.py +5 -0
  642. cumulusci/tests/pytest_plugins/test_vcr_string_compressor.py +49 -0
  643. cumulusci/tests/pytest_plugins/vcr_string_compressor.py +97 -0
  644. cumulusci/tests/shared_cassettes/GET_sobjects_Account_describe.yaml +18 -0
  645. cumulusci/tests/shared_cassettes/GET_sobjects_Case_describe.yaml +18 -0
  646. cumulusci/tests/shared_cassettes/GET_sobjects_Contact_describe.yaml +4838 -0
  647. cumulusci/tests/shared_cassettes/GET_sobjects_Custom__c_describe.yaml +242 -0
  648. cumulusci/tests/shared_cassettes/GET_sobjects_Event_describe.yaml +19 -0
  649. cumulusci/tests/shared_cassettes/GET_sobjects_Global_describe.yaml +1338 -0
  650. cumulusci/tests/shared_cassettes/GET_sobjects_Lead_describe.yaml +18 -0
  651. cumulusci/tests/shared_cassettes/GET_sobjects_OpportunityContactRole_describe.yaml +34 -0
  652. cumulusci/tests/shared_cassettes/GET_sobjects_Opportunity_describe.yaml +1261 -0
  653. cumulusci/tests/shared_cassettes/GET_sobjects_Organization.yaml +49 -0
  654. cumulusci/tests/shared_cassettes/vcr_string_templates/batchInfoList_xml.tpl +15 -0
  655. cumulusci/tests/shared_cassettes/vcr_string_templates/batchInfo_xml.tpl +13 -0
  656. cumulusci/tests/shared_cassettes/vcr_string_templates/jobInfo_insert_xml.tpl +24 -0
  657. cumulusci/tests/shared_cassettes/vcr_string_templates/jobInfo_upsert_xml.tpl +25 -0
  658. cumulusci/tests/test_entry_points.py +20 -0
  659. cumulusci/tests/test_integration_infrastructure.py +131 -0
  660. cumulusci/tests/test_main.py +9 -0
  661. cumulusci/tests/test_schema.py +32 -0
  662. cumulusci/tests/test_utils.py +657 -0
  663. cumulusci/tests/test_vcr_serializer.py +134 -0
  664. cumulusci/tests/uncompressed_cassette.yaml +83 -0
  665. cumulusci/tests/util.py +344 -0
  666. cumulusci/utils/__init__.py +731 -0
  667. cumulusci/utils/classutils.py +9 -0
  668. cumulusci/utils/collections.py +32 -0
  669. cumulusci/utils/deprecation.py +11 -0
  670. cumulusci/utils/encryption.py +31 -0
  671. cumulusci/utils/fileutils.py +295 -0
  672. cumulusci/utils/git.py +142 -0
  673. cumulusci/utils/http/multi_request.py +214 -0
  674. cumulusci/utils/http/requests_utils.py +103 -0
  675. cumulusci/utils/http/tests/cassettes/ManualEditTestCompositeParallelSalesforce.test_http_headers.yaml +32 -0
  676. cumulusci/utils/http/tests/cassettes/TestCompositeParallelSalesforce.test_composite_parallel_salesforce.yaml +65 -0
  677. cumulusci/utils/http/tests/cassettes/TestCompositeParallelSalesforce.test_errors.yaml +24 -0
  678. cumulusci/utils/http/tests/cassettes/TestCompositeParallelSalesforce.test_reference_ids.yaml +49 -0
  679. cumulusci/utils/http/tests/test_multi_request.py +255 -0
  680. cumulusci/utils/iterators.py +21 -0
  681. cumulusci/utils/logging.py +128 -0
  682. cumulusci/utils/metaprogramming.py +10 -0
  683. cumulusci/utils/options.py +138 -0
  684. cumulusci/utils/parallel/queries_in_parallel/run_queries_in_parallel.py +29 -0
  685. cumulusci/utils/parallel/queries_in_parallel/tests/test_run_queries_in_parallel.py +50 -0
  686. cumulusci/utils/parallel/task_worker_queues/parallel_worker.py +238 -0
  687. cumulusci/utils/parallel/task_worker_queues/parallel_worker_queue.py +243 -0
  688. cumulusci/utils/parallel/task_worker_queues/tests/test_parallel_worker.py +353 -0
  689. cumulusci/utils/salesforce/count_sobjects.py +46 -0
  690. cumulusci/utils/salesforce/soql.py +17 -0
  691. cumulusci/utils/salesforce/tests/cassettes/ManualEdit_TestCountSObjects.test_count_sobjects__network_errors.yaml +23 -0
  692. cumulusci/utils/salesforce/tests/cassettes/TestCountSObjects.test_count_sobjects__errors.yaml +33 -0
  693. cumulusci/utils/salesforce/tests/cassettes/TestCountSObjects.test_count_sobjects_simple.yaml +29 -0
  694. cumulusci/utils/salesforce/tests/test_count_sobjects.py +29 -0
  695. cumulusci/utils/salesforce/tests/test_soql.py +30 -0
  696. cumulusci/utils/tests/cassettes/ManualEditTestDescribeOrg.test_minimal_schema.yaml +36 -0
  697. cumulusci/utils/tests/cassettes/ManualEdit_test_describe_to_sql.yaml +191 -0
  698. cumulusci/utils/tests/test_fileutils.py +284 -0
  699. cumulusci/utils/tests/test_git.py +85 -0
  700. cumulusci/utils/tests/test_logging.py +70 -0
  701. cumulusci/utils/tests/test_option_parsing.py +188 -0
  702. cumulusci/utils/tests/test_org_schema.py +691 -0
  703. cumulusci/utils/tests/test_org_schema_models.py +79 -0
  704. cumulusci/utils/tests/test_waiting.py +25 -0
  705. cumulusci/utils/version_strings.py +391 -0
  706. cumulusci/utils/waiting.py +42 -0
  707. cumulusci/utils/xml/__init__.py +91 -0
  708. cumulusci/utils/xml/metadata_tree.py +299 -0
  709. cumulusci/utils/xml/robot_xml.py +114 -0
  710. cumulusci/utils/xml/salesforce_encoding.py +100 -0
  711. cumulusci/utils/xml/test/test_metadata_tree.py +251 -0
  712. cumulusci/utils/xml/test/test_salesforce_encoding.py +173 -0
  713. cumulusci/utils/yaml/cumulusci_yml.py +401 -0
  714. cumulusci/utils/yaml/model_parser.py +156 -0
  715. cumulusci/utils/yaml/safer_loader.py +74 -0
  716. cumulusci/utils/yaml/tests/bad_cci.yml +5 -0
  717. cumulusci/utils/yaml/tests/cassettes/TestCumulusciYml.test_validate_url__with_errors.yaml +20 -0
  718. cumulusci/utils/yaml/tests/test_cumulusci_yml.py +286 -0
  719. cumulusci/utils/yaml/tests/test_model_parser.py +175 -0
  720. cumulusci/utils/yaml/tests/test_safer_loader.py +88 -0
  721. cumulusci/utils/ziputils.py +61 -0
  722. cumulusci/vcs/base.py +143 -0
  723. cumulusci/vcs/bootstrap.py +272 -0
  724. cumulusci/vcs/github/__init__.py +24 -0
  725. cumulusci/vcs/github/adapter.py +689 -0
  726. cumulusci/vcs/github/release_notes/generator.py +219 -0
  727. cumulusci/vcs/github/release_notes/parser.py +151 -0
  728. cumulusci/vcs/github/release_notes/provider.py +143 -0
  729. cumulusci/vcs/github/service.py +569 -0
  730. cumulusci/vcs/github/tests/test_adapter.py +138 -0
  731. cumulusci/vcs/github/tests/test_service.py +408 -0
  732. cumulusci/vcs/models.py +586 -0
  733. cumulusci/vcs/tests/conftest.py +41 -0
  734. cumulusci/vcs/tests/dummy_service.py +241 -0
  735. cumulusci/vcs/tests/test_vcs_base.py +687 -0
  736. cumulusci/vcs/tests/test_vcs_bootstrap.py +727 -0
  737. cumulusci/vcs/utils/__init__.py +31 -0
  738. cumulusci/vcs/vcs_source.py +287 -0
  739. cumulusci_plus-5.0.0.dist-info/METADATA +145 -0
  740. cumulusci_plus-5.0.0.dist-info/RECORD +744 -0
  741. cumulusci_plus-5.0.0.dist-info/WHEEL +4 -0
  742. cumulusci_plus-5.0.0.dist-info/entry_points.txt +3 -0
  743. cumulusci_plus-5.0.0.dist-info/licenses/AUTHORS.rst +41 -0
  744. cumulusci_plus-5.0.0.dist-info/licenses/LICENSE +30 -0
@@ -0,0 +1,835 @@
1
+ """ CumulusCI Tasks for running Apex Tests """
2
+
3
+ import html
4
+ import io
5
+ import json
6
+ import re
7
+
8
+ from cumulusci.core.exceptions import (
9
+ ApexTestException,
10
+ CumulusCIException,
11
+ TaskOptionsError,
12
+ )
13
+ from cumulusci.core.utils import decode_to_unicode, process_bool_arg, process_list_arg, determine_managed_mode
14
+ from cumulusci.tasks.salesforce import BaseSalesforceApiTask
15
+ from cumulusci.utils.http.requests_utils import safe_json_from_response
16
+
17
+ APEX_LIMITS = {
18
+ "Soql": {
19
+ "Label": "TESTING_LIMITS: Number of SOQL queries",
20
+ "SYNC": 100,
21
+ "ASYNC": 200,
22
+ },
23
+ "Email": {
24
+ "Label": "TESTING_LIMITS: Number of Email Invocations",
25
+ "SYNC": 10,
26
+ "ASYNC": 10,
27
+ },
28
+ "AsyncCalls": {
29
+ "Label": "TESTING_LIMITS: Number of future calls",
30
+ "SYNC": 50,
31
+ "ASYNC": 50,
32
+ },
33
+ "DmlRows": {
34
+ "Label": "TESTING_LIMITS: Number of DML rows",
35
+ "SYNC": 10000,
36
+ "ASYNC": 10000,
37
+ },
38
+ "Cpu": {"Label": "TESTING_LIMITS: Maximum CPU time", "SYNC": 10000, "ASYNC": 60000},
39
+ "QueryRows": {
40
+ "Label": "TESTING_LIMITS: Number of query rows",
41
+ "SYNC": 50000,
42
+ "ASYNC": 50000,
43
+ },
44
+ "Dml": {
45
+ "Label": "TESTING_LIMITS: Number of DML statements",
46
+ "SYNC": 150,
47
+ "ASYNC": 150,
48
+ },
49
+ "MobilePush": {
50
+ "Label": "TESTING_LIMITS: Number of Mobile Apex push calls",
51
+ "SYNC": 10,
52
+ "ASYNC": 10,
53
+ },
54
+ "Sosl": {
55
+ "Label": "TESTING_LIMITS: Number of SOSL queries",
56
+ "SYNC": 20,
57
+ "ASYNC": 20,
58
+ },
59
+ "Callouts": {
60
+ "Label": "TESTING_LIMITS: Number of callouts",
61
+ "SYNC": 100,
62
+ "ASYNC": 100,
63
+ },
64
+ }
65
+
66
+
67
+ TEST_RESULT_QUERY = """
68
+ SELECT Id,ApexClassId,TestTimestamp,
69
+ Message,MethodName,Outcome,
70
+ RunTime,StackTrace,
71
+ (SELECT
72
+ Id,Callouts,AsyncCalls,DmlRows,Email,
73
+ LimitContext,LimitExceptions,MobilePush,
74
+ QueryRows,Sosl,Cpu,Dml,Soql
75
+ FROM ApexTestResults)
76
+ FROM ApexTestResult
77
+ WHERE AsyncApexJobId='{}'
78
+ """
79
+
80
+
81
+ class RunApexTests(BaseSalesforceApiTask):
82
+ """Task to run Apex tests with the Tooling API and report results.
83
+
84
+ This task optionally supports retrying unit tests that fail due to
85
+ transitory issues or concurrency-related row locks. To enable retries,
86
+ add ones or more regular expressions to the list option `retry_failures`.
87
+
88
+ When a test run fails, if all of the failures' error messages or stack traces
89
+ match one of these regular expressions, each failed test will be retried by
90
+ itself. This is often useful when running Apex tests in parallel; row locks
91
+ may automatically be retried. Note that retries are supported whether or not
92
+ the org has parallel Apex testing enabled.
93
+
94
+ The ``retry_always`` option modifies this behavior: if a test run fails and
95
+ any (not all) of the failures match the specified regular expressions,
96
+ all of the failed tests will be retried in serial. This is helpful when
97
+ underlying row locking errors are masked by custom exceptions.
98
+
99
+ A useful base configuration for projects wishing to use retries is:
100
+
101
+ .. code-block:: yaml
102
+
103
+ retry_failures:
104
+ - "unable to obtain exclusive access to this record"
105
+ - "UNABLE_TO_LOCK_ROW"
106
+ - "connection was cancelled here"
107
+ retry_always: True
108
+
109
+ Some projects' unit tests produce so many concurrency errors that
110
+ it's faster to execute the entire run in serial mode than to use retries.
111
+ Serial and parallel mode are configured in the scratch org definition file."""
112
+
113
+ api_version = "38.0"
114
+ name = "RunApexTests"
115
+ task_options = {
116
+ "test_name_match": {
117
+ "description": (
118
+ "Pattern to find Apex test classes to run "
119
+ '("%" is wildcard). Defaults to '
120
+ "project__test__name_match from project config. "
121
+ "Comma-separated list for multiple patterns."
122
+ ),
123
+ },
124
+ "test_name_exclude": {
125
+ "description": (
126
+ "Query to find Apex test classes to exclude "
127
+ '("%" is wildcard). Defaults to '
128
+ "project__test__name_exclude from project config. "
129
+ "Comma-separated list for multiple patterns."
130
+ )
131
+ },
132
+ "namespace": {
133
+ "description": (
134
+ "Salesforce project namespace. Defaults to "
135
+ + "project__package__namespace"
136
+ )
137
+ },
138
+ "managed": {
139
+ "description": (
140
+ "If True, search for tests in the namespace "
141
+ + "only. Defaults to False"
142
+ )
143
+ },
144
+ "poll_interval": {
145
+ "description": ("Seconds to wait between polling for Apex test results.")
146
+ },
147
+ "junit_output": {
148
+ "description": "File name for JUnit output. Defaults to test_results.xml"
149
+ },
150
+ "json_output": {
151
+ "description": "File name for json output. Defaults to test_results.json"
152
+ },
153
+ "retry_failures": {
154
+ "description": "A list of regular expression patterns to match against "
155
+ "test failures. If failures match, the failing tests are retried in "
156
+ "serial mode."
157
+ },
158
+ "retry_always": {
159
+ "description": "By default, all failures must match retry_failures to perform "
160
+ "a retry. Set retry_always to True to retry all failed tests if any failure matches."
161
+ },
162
+ "required_org_code_coverage_percent": {
163
+ "description": "Require at least X percent code coverage across the org following the test run.",
164
+ "usage": "--required_org_code_coverage_percent PERCENTAGE",
165
+ },
166
+ "required_per_class_code_coverage_percent": {
167
+ "description": "Require at least X percent code coverage for every class in the org.",
168
+ },
169
+ "verbose": {
170
+ "description": "By default, only failures get detailed output. "
171
+ "Set verbose to True to see all passed test methods."
172
+ },
173
+ "test_suite_names": {
174
+ "description": "Accepts a comma-separated list of test suite names. Only runs test classes that are part of the test suites specified."
175
+ },
176
+ }
177
+
178
+ def _init_options(self, kwargs):
179
+ super(RunApexTests, self)._init_options(kwargs)
180
+
181
+ self.options["test_name_match"] = self.options.get(
182
+ "test_name_match", self.project_config.project__test__name_match
183
+ )
184
+ self.options["test_name_exclude"] = self.options.get(
185
+ "test_name_exclude", self.project_config.project__test__name_exclude
186
+ )
187
+
188
+ self.options["test_suite_names"] = self.options.get(
189
+ "test_suite_names", self.project_config.project__test__suite__names
190
+ )
191
+ if self.options["test_name_match"] is None:
192
+ self.options["test_name_match"] = ""
193
+
194
+ if self.options["test_name_exclude"] is None:
195
+ self.options["test_name_exclude"] = ""
196
+
197
+ self.options["namespace"] = self.options.get(
198
+ "namespace", self.project_config.project__package__namespace
199
+ )
200
+
201
+ self.options["junit_output"] = self.options.get(
202
+ "junit_output", "test_results.xml"
203
+ )
204
+
205
+ self.options["json_output"] = self.options.get(
206
+ "json_output", "test_results.json"
207
+ )
208
+
209
+ self.options["retry_failures"] = process_list_arg(
210
+ self.options.get("retry_failures", [])
211
+ )
212
+ compiled_res = []
213
+ for regex in self.options["retry_failures"]:
214
+ try:
215
+ compiled_res.append(re.compile(regex))
216
+ except re.error as e:
217
+ raise TaskOptionsError(
218
+ "An invalid regular expression ({}) was provided ({})".format(
219
+ regex, e
220
+ )
221
+ )
222
+ self.options["retry_failures"] = compiled_res
223
+ self.options["retry_always"] = process_bool_arg(
224
+ self.options.get("retry_always") or False
225
+ )
226
+
227
+ self.verbose = process_bool_arg(self.options.get("verbose") or False)
228
+
229
+ self.counts = {}
230
+
231
+ if "required_org_code_coverage_percent" in self.options:
232
+ try:
233
+ self.code_coverage_level = int(
234
+ str(self.options["required_org_code_coverage_percent"]).rstrip("%")
235
+ )
236
+ except ValueError:
237
+ raise TaskOptionsError(
238
+ f"Invalid code coverage level {self.options['required_org_code_coverage_percent']}"
239
+ )
240
+ else:
241
+ self.code_coverage_level = 0
242
+
243
+ self.required_per_class_code_coverage_percent = int(
244
+ self.options.get("required_per_class_code_coverage_percent", 0)
245
+ )
246
+ # Raises a TaskOptionsError when the user provides both test_suite_names and test_name_match.
247
+ if (self.options["test_suite_names"]) and (
248
+ self.options["test_name_match"] is not None
249
+ and self.options["test_name_match"] != "%_TEST%"
250
+ ):
251
+ raise TaskOptionsError(
252
+ "Both test_suite_names and test_name_match cannot be passed simultaneously"
253
+ )
254
+
255
+ # pylint: disable=W0201
256
+ def _init_class(self):
257
+ self.classes_by_id = {}
258
+ self.classes_by_name = {}
259
+ self.job_id = None
260
+ self.results_by_class_name = {}
261
+ self.result = None
262
+ self.retry_details = None
263
+
264
+ def _get_namespace_filter(self):
265
+
266
+ if self.options.get("managed"):
267
+
268
+ namespace = self.options.get("namespace")
269
+
270
+ if not namespace:
271
+ raise TaskOptionsError(
272
+ "Running tests in managed mode but no namespace available."
273
+ )
274
+ namespace = "'{}'".format(namespace)
275
+ elif self.org_config.namespace:
276
+ namespace = self.org_config.namespace
277
+ namespace = "'{}'".format(namespace)
278
+ else:
279
+ namespace = "null"
280
+ return namespace
281
+
282
+ def _get_test_class_query(self):
283
+ namespace = self._get_namespace_filter()
284
+ # Split by commas to allow multiple class name matching options
285
+ test_name_match = self.options["test_name_match"]
286
+ included_tests = []
287
+ for pattern in test_name_match.split(","):
288
+ if pattern:
289
+ included_tests.append("Name LIKE '{}'".format(pattern))
290
+ # Add any excludes to the where clause
291
+ test_name_exclude = self.options.get("test_name_exclude", "")
292
+ excluded_tests = []
293
+ for pattern in test_name_exclude.split(","):
294
+ if pattern:
295
+ excluded_tests.append("(NOT Name LIKE '{}')".format(pattern))
296
+ # Get all test classes for namespace
297
+ query = "SELECT Id, Name FROM ApexClass " + "WHERE NamespacePrefix = {}".format(
298
+ namespace
299
+ )
300
+
301
+ if included_tests:
302
+ query += " AND ({})".format(" OR ".join(included_tests))
303
+ if excluded_tests:
304
+ query += " AND {}".format(" AND ".join(excluded_tests))
305
+ return query
306
+
307
+ def _get_test_classes(self):
308
+ # If test_suite_names is provided, execute only tests that are a part of the list of test suites provided.
309
+ if self.options["test_suite_names"]:
310
+ test_classes_from_test_suite_names = (
311
+ self._get_test_classes_from_test_suite_names()
312
+ )
313
+ return test_classes_from_test_suite_names
314
+
315
+ # test_suite_names is not provided. Fetch all the test classes from the org.
316
+ else:
317
+ return self._get_all_test_classes()
318
+
319
+ def _get_all_test_classes(self):
320
+ # Fetches all the test classes from the org.
321
+ query = self._get_test_class_query()
322
+ self.logger.info("Fetching all the test classes...")
323
+ result = self.tooling.query_all(query)
324
+ self.logger.info("Found {} test classes".format(result["totalSize"]))
325
+ return result
326
+
327
+ def _get_comma_separated_string_of_items(self, itemlist):
328
+ # Accepts a list of strings. A formatted string is returned.
329
+ # Example: Input: ['TestSuite1', 'TestSuite2'] Output: ''TestSuite1','TestSuite2''
330
+ return ",".join([f"'{item}'" for item in itemlist])
331
+
332
+ def _get_test_suite_ids_from_test_suite_names_query(self, test_suite_names_arg):
333
+ # Returns a query string which when executed fetches the test suite ids of the list of test suite names.
334
+ test_suite_names = self._get_comma_separated_string_of_items(
335
+ test_suite_names_arg.split(",")
336
+ )
337
+ query1 = f"SELECT Id, TestSuiteName FROM ApexTestSuite WHERE TestSuiteName IN ({test_suite_names})"
338
+ return query1
339
+
340
+ def _get_test_classes_from_test_suite_ids_query(self, testSuiteIds):
341
+ # Returns a query string which when executed fetches Apex test classes for the given list of test suite ids.
342
+ # Apex test classes passed under test_name_exclude are ignored.
343
+ testSuiteIds_formatted = self._get_comma_separated_string_of_items(testSuiteIds)
344
+
345
+ if len(testSuiteIds_formatted) == 0:
346
+ testSuiteIds_formatted = "''"
347
+
348
+ test_name_exclude_arg = self.options["test_name_exclude"]
349
+ condition = ""
350
+
351
+ # Check if test_name_exclude is provided. Append to query string if the former is specified.
352
+ if test_name_exclude_arg:
353
+ test_name_exclude = self._get_comma_separated_string_of_items(
354
+ test_name_exclude_arg.split(",")
355
+ )
356
+ condition = f"AND Name NOT IN ({test_name_exclude})"
357
+
358
+ query = f"SELECT Id, Name FROM ApexClass WHERE Id IN (SELECT ApexClassId FROM TestSuiteMembership WHERE ApexTestSuiteId IN ({testSuiteIds_formatted})) {condition}"
359
+ return query
360
+
361
+ def _get_test_classes_from_test_suite_names(self):
362
+ # Returns a list of Apex test classes that belong to the test suite(s) specified. Test classes specified in test_name_exclude are excluded.
363
+ test_suite_names_arg = self.options["test_suite_names"]
364
+ query1 = self._get_test_suite_ids_from_test_suite_names_query(
365
+ test_suite_names_arg
366
+ )
367
+ self.logger.info("Fetching test suite metadata...")
368
+ result = self.tooling.query_all(query1)
369
+ testSuiteIds = []
370
+
371
+ for record in result["records"]:
372
+ testSuiteIds.append(str(record["Id"]))
373
+
374
+ query2 = self._get_test_classes_from_test_suite_ids_query(testSuiteIds)
375
+ self.logger.info("Fetching test classes belonging to the test suite(s)...")
376
+ result = self.tooling.query_all(query2)
377
+ self.logger.info("Found {} test classes".format(result["totalSize"]))
378
+ return result
379
+
380
+ def _get_test_methods_for_class(self, class_name):
381
+ result = self.tooling.query(
382
+ f"SELECT SymbolTable FROM ApexClass WHERE Name='{class_name}'"
383
+ )
384
+ test_methods = []
385
+
386
+ try:
387
+ methods = result["records"][0]["SymbolTable"]["methods"]
388
+ except (TypeError, IndexError, KeyError):
389
+ raise CumulusCIException(
390
+ f"Unable to acquire symbol table for failed Apex class {class_name}"
391
+ )
392
+ for m in methods:
393
+ for a in m.get("annotations", []):
394
+ if a["name"].lower() in ["istest", "testmethod"]:
395
+ test_methods.append(m["name"])
396
+ break
397
+
398
+ return test_methods
399
+
400
+ def _is_retriable_error_message(self, error_message):
401
+ return any(
402
+ [reg.search(error_message) for reg in self.options["retry_failures"]]
403
+ )
404
+
405
+ def _is_retriable_failure(self, test_result):
406
+ return self._is_retriable_error_message(
407
+ test_result["Message"] or ""
408
+ ) or self._is_retriable_error_message(test_result["StackTrace"] or "")
409
+
410
+ def _get_test_results(self, allow_retries=True):
411
+ # We need to query at both the test result and test queue item level.
412
+ # Some concurrency problems manifest as all or part of the class failing,
413
+ # without leaving behind any visible ApexTestResult records.
414
+ # See https://salesforce.stackexchange.com/questions/262893/any-way-to-get-consistent-test-counts-when-parallel-testing-is-used
415
+
416
+ # First, gather the Ids of failed test classes.
417
+ test_classes = self.tooling.query_all(
418
+ "SELECT Id, Status, ExtendedStatus, ApexClassId FROM ApexTestQueueItem "
419
+ + "WHERE ParentJobId = '{}' AND Status = 'Failed'".format(self.job_id)
420
+ )
421
+ class_level_errors = {
422
+ each_class["ApexClassId"]: each_class["ExtendedStatus"]
423
+ for each_class in test_classes["records"]
424
+ }
425
+
426
+ result = self.tooling.query_all(TEST_RESULT_QUERY.format(self.job_id))
427
+
428
+ if allow_retries:
429
+ self.retry_details = {}
430
+
431
+ for test_result in result["records"]:
432
+ class_name = self.classes_by_id[test_result["ApexClassId"]]
433
+ self.results_by_class_name[class_name][
434
+ test_result["MethodName"]
435
+ ] = test_result
436
+ self.counts[test_result["Outcome"]] += 1
437
+
438
+ # If we have class-level failures that did not come with line-level
439
+ # failure details, report those as well.
440
+ for class_id, error in class_level_errors.items():
441
+ class_name = self.classes_by_id[class_id]
442
+
443
+ self.logger.error(
444
+ f"Class {class_name} failed to run some tests with the message {error}. Applying error to unit test results."
445
+ )
446
+
447
+ # In Spring '20, we cannot get symbol tables for managed classes.
448
+ if self.options.get("managed"):
449
+ self.logger.error(
450
+ f"Cannot access symbol table for managed class {class_name}. Failure will not be retried."
451
+ )
452
+ continue
453
+
454
+ # Get all the method names for this class
455
+ test_methods = self._get_test_methods_for_class(class_name)
456
+ for test_method in test_methods:
457
+ # If this method was not run due to a class-level failure,
458
+ # synthesize a failed result.
459
+ # If we're retrying and fail again, do the same.
460
+ if (
461
+ test_method not in self.results_by_class_name[class_name]
462
+ or self.results_by_class_name[class_name][test_method]["Outcome"]
463
+ == "Fail"
464
+ ):
465
+ self.results_by_class_name[class_name][test_method] = {
466
+ "ApexClassId": class_id,
467
+ "MethodName": test_method,
468
+ "Outcome": "Fail",
469
+ "Message": f"Containing class {class_name} failed with message {error}",
470
+ "StackTrace": "",
471
+ "RunTime": 0,
472
+ }
473
+ self.counts["Fail"] += 1
474
+
475
+ if allow_retries:
476
+ for class_name, results in self.results_by_class_name.items():
477
+ for test_result in results.values():
478
+ # Determine whether this failure is retriable.
479
+ if test_result["Outcome"] == "Fail" and allow_retries:
480
+ can_retry_this_failure = self._is_retriable_failure(test_result)
481
+ if can_retry_this_failure:
482
+ self.counts["Retriable"] += 1
483
+
484
+ # Even if this failure is not retriable per se,
485
+ # persist its details if we might end up retrying
486
+ # all failures.
487
+ if self.options["retry_always"] or can_retry_this_failure:
488
+ self.retry_details.setdefault(
489
+ test_result["ApexClassId"], []
490
+ ).append(test_result["MethodName"])
491
+
492
+ def _process_test_results(self):
493
+ test_results = []
494
+ class_names = list(self.results_by_class_name.keys())
495
+ class_names.sort()
496
+ for class_name in class_names:
497
+ self.retry_details = {}
498
+ method_names = list(self.results_by_class_name[class_name].keys())
499
+ # Added to process for the None methodnames
500
+
501
+ if None in method_names:
502
+ class_id = self.classes_by_name[class_name]
503
+ self.retry_details.setdefault(class_id, []).append(
504
+ self._get_test_methods_for_class(class_name)
505
+ )
506
+ del self.results_by_class_name[class_name][None]
507
+ self.logger.info(
508
+ f"Retrying class with id: {class_id} name:{class_name} due to `None` methodname"
509
+ )
510
+ self.counts["Retriable"] += len(self.retry_details[class_id])
511
+ self._attempt_retries()
512
+
513
+ has_failures = any(
514
+ result["Outcome"] in ["Fail", "CompileFail"]
515
+ for result in self.results_by_class_name[class_name].values()
516
+ )
517
+ if has_failures or self.verbose:
518
+ self.logger.info(f"Class: {class_name}")
519
+ method_names = list(self.results_by_class_name[class_name].keys())
520
+ method_names.sort()
521
+ for method_name in method_names:
522
+ result = self.results_by_class_name[class_name][method_name]
523
+ message = f"\t{result['Outcome']}: {result['MethodName']}"
524
+ duration = result["RunTime"]
525
+ result["stats"] = self._get_stats_from_result(result)
526
+ if duration:
527
+ message += f" ({duration}ms)"
528
+ test_results.append(
529
+ {
530
+ "Children": result.get("children", None),
531
+ "ClassName": decode_to_unicode(class_name),
532
+ "Method": decode_to_unicode(result["MethodName"]),
533
+ "Message": decode_to_unicode(result["Message"]),
534
+ "Outcome": decode_to_unicode(result["Outcome"]),
535
+ "StackTrace": decode_to_unicode(result["StackTrace"]),
536
+ "Stats": result.get("stats", None),
537
+ "TestTimestamp": result.get("TestTimestamp", None),
538
+ }
539
+ )
540
+ if result["Outcome"] in ["Fail", "CompileFail"]:
541
+ self.logger.info(message)
542
+ self.logger.info(f"\tMessage: {result['Message']}")
543
+ self.logger.info(f"\tStackTrace: {result['StackTrace']}")
544
+ elif self.verbose:
545
+ self.logger.info(message)
546
+ self.logger.info("-" * 80)
547
+ self.logger.info(
548
+ "Pass: {} Retried: {} Fail: {} CompileFail: {} Skip: {}".format(
549
+ self.counts["Pass"],
550
+ self.counts["Retriable"],
551
+ self.counts["Fail"],
552
+ self.counts["CompileFail"],
553
+ self.counts["Skip"],
554
+ )
555
+ )
556
+ self.logger.info("-" * 80)
557
+ if self.counts["Fail"] or self.counts["CompileFail"]:
558
+ self.logger.error("-" * 80)
559
+ self.logger.error("Failing Tests")
560
+ self.logger.error("-" * 80)
561
+ counter = 0
562
+ for result in test_results:
563
+ if result["Outcome"] in ["Fail", "CompileFail"]:
564
+ counter += 1
565
+ self.logger.error(
566
+ "{}: {}.{} - {}".format(
567
+ counter,
568
+ result["ClassName"],
569
+ result["Method"],
570
+ result["Outcome"],
571
+ )
572
+ )
573
+ self.logger.error(f"\tMessage: {result['Message']}")
574
+ self.logger.error(f"\tStackTrace: {result['StackTrace']}")
575
+
576
+ return test_results
577
+
578
+ def _get_stats_from_result(self, result):
579
+ stats = {"duration": result["RunTime"]}
580
+
581
+ if result.get("ApexTestResults", None):
582
+ for limit_name, details in APEX_LIMITS.items():
583
+ limit_use = result["ApexTestResults"]["records"][0][limit_name]
584
+ limit_allowed = details[
585
+ result["ApexTestResults"]["records"][0]["LimitContext"]
586
+ ]
587
+ stats[details["Label"]] = {"used": limit_use, "allowed": limit_allowed}
588
+
589
+ return stats
590
+
591
+ def _enqueue_test_run(self, class_ids):
592
+ if isinstance(class_ids, dict):
593
+ body = {
594
+ "tests": [
595
+ {"classId": class_id, "testMethods": class_ids[class_id]}
596
+ for class_id in class_ids
597
+ ]
598
+ }
599
+ else:
600
+ body = {"classids": ",".join(class_ids)}
601
+
602
+ return safe_json_from_response(
603
+ self.tooling._call_salesforce(
604
+ method="POST",
605
+ url=self.tooling.base_url + "runTestsAsynchronous",
606
+ json=body,
607
+ )
608
+ )
609
+
610
+ def _init_task(self):
611
+ super()._init_task()
612
+ self.options["managed"] = determine_managed_mode(
613
+ self.options, self.project_config, self.org_config
614
+ )
615
+
616
+ def _run_task(self):
617
+ result = self._get_test_classes()
618
+ if result["totalSize"] == 0:
619
+ return
620
+ for test_class in result["records"]:
621
+ self.classes_by_id[test_class["Id"]] = test_class["Name"]
622
+ self.classes_by_name[test_class["Name"]] = test_class["Id"]
623
+ self.results_by_class_name[test_class["Name"]] = {}
624
+ self.logger.info("Queuing tests for execution...")
625
+
626
+ self.counts = {
627
+ "Pass": 0,
628
+ "Fail": 0,
629
+ "CompileFail": 0,
630
+ "Skip": 0,
631
+ "Retriable": 0,
632
+ }
633
+ self.job_id = self._enqueue_test_run(
634
+ (str(id) for id in self.classes_by_id.keys())
635
+ )
636
+
637
+ self._wait_for_tests()
638
+ self._get_test_results()
639
+
640
+ # Did we get back retriable test results? Check our retry policy,
641
+ # then enqueue new runs individually, until either (a) all retriable
642
+ # tests succeed or (b) a test fails.
643
+ able_to_retry = (self.counts["Retriable"] and self.options["retry_always"]) or (
644
+ self.counts["Retriable"] and self.counts["Retriable"] == self.counts["Fail"]
645
+ )
646
+ if not able_to_retry:
647
+ self.counts["Retriable"] = 0
648
+ else:
649
+ self._attempt_retries()
650
+
651
+ test_results = self._process_test_results()
652
+ self._write_output(test_results)
653
+
654
+ if self.counts.get("Fail") or self.counts.get("CompileFail"):
655
+ raise ApexTestException(
656
+ "{} tests failed and {} tests failed compilation".format(
657
+ self.counts.get("Fail"), self.counts.get("CompileFail")
658
+ )
659
+ )
660
+
661
+ if self.code_coverage_level or self.required_per_class_code_coverage_percent:
662
+ if self.options.get("namespace") not in self.org_config.installed_packages:
663
+ self._check_code_coverage()
664
+ else:
665
+ self.logger.info(
666
+ "This org contains a managed installation; not checking code coverage."
667
+ )
668
+ else:
669
+ self.logger.info(
670
+ "No code coverage level specified; not checking code coverage."
671
+ )
672
+
673
+ def _check_code_coverage(self):
674
+ self.logger.info("Checking code coverage.")
675
+ class_level_coverage_failures = {}
676
+
677
+ # Query for Class level code coverage using the aggregate
678
+ if self.required_per_class_code_coverage_percent:
679
+ test_classes = self.tooling.query(
680
+ "SELECT ApexClassOrTrigger.Name, ApexClassOrTriggerId, NumLinesCovered, NumLinesUncovered FROM ApexCodeCoverageAggregate ORDER BY ApexClassOrTrigger.Name ASC"
681
+ )["records"]
682
+
683
+ coverage_percentage = 0
684
+ for class_level in test_classes:
685
+ total = (
686
+ class_level["NumLinesCovered"] + class_level["NumLinesUncovered"]
687
+ )
688
+ # prevent division by 0 errors
689
+ if total:
690
+ # calculate coverage percentage
691
+ coverage_percentage = round(
692
+ (class_level["NumLinesCovered"] / total) * 100,
693
+ 2,
694
+ )
695
+
696
+ if coverage_percentage < self.required_per_class_code_coverage_percent:
697
+ class_level_coverage_failures[
698
+ class_level["ApexClassOrTrigger"]["Name"]
699
+ ] = coverage_percentage
700
+
701
+ # Query for OrgWide coverage
702
+ result = self.tooling.query("SELECT PercentCovered FROM ApexOrgWideCoverage")
703
+ coverage = result["records"][0]["PercentCovered"]
704
+
705
+ errors = []
706
+ if self.required_per_class_code_coverage_percent:
707
+ if class_level_coverage_failures:
708
+ for class_name in class_level_coverage_failures.keys():
709
+ errors.append(
710
+ f"{class_name}'s code coverage of {class_level_coverage_failures[class_name]}% is below required level of {self.required_per_class_code_coverage_percent}."
711
+ )
712
+ else:
713
+ self.logger.info(
714
+ f"All classes meet code coverage expectations of {self.required_per_class_code_coverage_percent}% ."
715
+ )
716
+
717
+ if coverage < self.code_coverage_level:
718
+ errors.append(
719
+ f"Organization-wide code coverage of {coverage}% is below required level of {self.code_coverage_level}"
720
+ )
721
+ else:
722
+ self.logger.info(
723
+ f"Organization-wide code coverage of {coverage}% meets expectations."
724
+ )
725
+
726
+ if errors:
727
+ error_message = "\n".join(errors)
728
+ self.logger.info(error_message)
729
+ raise ApexTestException(error_message)
730
+
731
+ def _attempt_retries(self):
732
+ total_method_retries = sum(
733
+ [len(test_list) for test_list in self.retry_details.values()]
734
+ )
735
+ self.logger.warning(
736
+ "Retrying {} failed methods from {} test classes".format(
737
+ total_method_retries, len(self.retry_details)
738
+ )
739
+ )
740
+ self.counts["Fail"] = 0
741
+
742
+ for class_id, test_list in self.retry_details.items():
743
+ for each_test in test_list:
744
+ self.logger.warning(
745
+ "Retrying {}.{}".format(self.classes_by_id[class_id], each_test)
746
+ )
747
+ self.job_id = self._enqueue_test_run({class_id: [each_test]})
748
+ self._wait_for_tests()
749
+ self._get_test_results(allow_retries=False)
750
+
751
+ # If the retry failed, report the remaining failures.
752
+ if self.counts["Fail"]:
753
+ self.logger.error("Test retry failed.")
754
+
755
+ def _wait_for_tests(self):
756
+ self.poll_complete = False
757
+ self.poll_interval_s = int(self.options.get("poll_interval", 1))
758
+ self.poll_count = 0
759
+ self._poll()
760
+
761
+ def _poll_action(self):
762
+ self.result = self.tooling.query_all(
763
+ "SELECT Id, Status, ApexClassId FROM ApexTestQueueItem "
764
+ + "WHERE ParentJobId = '{}'".format(self.job_id)
765
+ )
766
+ counts = {
767
+ "Aborted": 0,
768
+ "Completed": 0,
769
+ "Failed": 0,
770
+ "Holding": 0,
771
+ "Preparing": 0,
772
+ "Processing": 0,
773
+ "Queued": 0,
774
+ }
775
+ processing_class_id = None
776
+ total_test_count = self.result["totalSize"]
777
+ for test_queue_item in self.result["records"]:
778
+ counts[test_queue_item["Status"]] += 1
779
+ if test_queue_item["Status"] == "Processing":
780
+ processing_class_id = test_queue_item["ApexClassId"]
781
+ processing_class = ""
782
+ if counts["Processing"] == 1:
783
+ processing_class = f" ({self.classes_by_id[processing_class_id]})"
784
+ self.logger.info(
785
+ "Completed: {} Processing: {}{} Queued: {}".format(
786
+ counts["Completed"],
787
+ counts["Processing"],
788
+ processing_class,
789
+ counts["Queued"],
790
+ )
791
+ )
792
+ if (
793
+ total_test_count
794
+ == counts["Completed"] + counts["Failed"] + counts["Aborted"]
795
+ ):
796
+ self.logger.info("Apex tests completed")
797
+ self.poll_complete = True
798
+
799
+ def _write_output(self, test_results):
800
+ junit_output = self.options["junit_output"]
801
+ if junit_output:
802
+ with io.open(junit_output, mode="w", encoding="utf-8") as f:
803
+ f.write('<testsuite tests="{}">\n'.format(len(test_results)))
804
+ for result in test_results:
805
+ s = ' <testcase classname="{}" name="{}"'.format(
806
+ result["ClassName"], result["Method"]
807
+ )
808
+ if (
809
+ "Stats" in result
810
+ and result["Stats"]
811
+ and "duration" in result["Stats"]
812
+ ):
813
+ s += ' time="{}"'.format(result["Stats"]["duration"])
814
+ if result["Outcome"] in ["Fail", "CompileFail"]:
815
+ s += ">\n"
816
+ s += ' <failure type="failed" '
817
+ if result["Message"]:
818
+ s += 'message="{}"'.format(html.escape(result["Message"]))
819
+ s += ">"
820
+
821
+ if result["StackTrace"]:
822
+ s += "<![CDATA[{}]]>".format(
823
+ html.escape(result["StackTrace"])
824
+ )
825
+ s += "</failure>\n"
826
+ s += " </testcase>\n"
827
+ else:
828
+ s += " />\n"
829
+ f.write(str(s))
830
+ f.write("</testsuite>")
831
+
832
+ json_output = self.options["json_output"]
833
+ if json_output:
834
+ with io.open(json_output, mode="w", encoding="utf-8") as f:
835
+ f.write(str(json.dumps(test_results, indent=4)))