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.
- cumulusci/__about__.py +1 -0
- cumulusci/__init__.py +22 -0
- cumulusci/__main__.py +3 -0
- cumulusci/cli/__init__.py +0 -0
- cumulusci/cli/cci.py +244 -0
- cumulusci/cli/error.py +125 -0
- cumulusci/cli/flow.py +185 -0
- cumulusci/cli/logger.py +72 -0
- cumulusci/cli/org.py +692 -0
- cumulusci/cli/plan.py +181 -0
- cumulusci/cli/project.py +391 -0
- cumulusci/cli/robot.py +116 -0
- cumulusci/cli/runtime.py +190 -0
- cumulusci/cli/service.py +521 -0
- cumulusci/cli/task.py +295 -0
- cumulusci/cli/tests/__init__.py +0 -0
- cumulusci/cli/tests/test_cci.py +545 -0
- cumulusci/cli/tests/test_error.py +170 -0
- cumulusci/cli/tests/test_flow.py +276 -0
- cumulusci/cli/tests/test_logger.py +25 -0
- cumulusci/cli/tests/test_org.py +1438 -0
- cumulusci/cli/tests/test_plan.py +245 -0
- cumulusci/cli/tests/test_project.py +235 -0
- cumulusci/cli/tests/test_robot.py +177 -0
- cumulusci/cli/tests/test_runtime.py +197 -0
- cumulusci/cli/tests/test_service.py +853 -0
- cumulusci/cli/tests/test_task.py +266 -0
- cumulusci/cli/tests/test_ui.py +310 -0
- cumulusci/cli/tests/test_utils.py +122 -0
- cumulusci/cli/tests/utils.py +52 -0
- cumulusci/cli/ui.py +234 -0
- cumulusci/cli/utils.py +150 -0
- cumulusci/conftest.py +181 -0
- cumulusci/core/__init__.py +0 -0
- cumulusci/core/config/BaseConfig.py +5 -0
- cumulusci/core/config/BaseTaskFlowConfig.py +5 -0
- cumulusci/core/config/OrgConfig.py +5 -0
- cumulusci/core/config/ScratchOrgConfig.py +5 -0
- cumulusci/core/config/__init__.py +125 -0
- cumulusci/core/config/base_config.py +111 -0
- cumulusci/core/config/base_task_flow_config.py +82 -0
- cumulusci/core/config/marketing_cloud_service_config.py +83 -0
- cumulusci/core/config/oauth2_service_config.py +17 -0
- cumulusci/core/config/org_config.py +604 -0
- cumulusci/core/config/project_config.py +782 -0
- cumulusci/core/config/scratch_org_config.py +251 -0
- cumulusci/core/config/sfdx_org_config.py +220 -0
- cumulusci/core/config/tests/_test_config_backwards_compatibility.py +33 -0
- cumulusci/core/config/tests/test_config.py +1895 -0
- cumulusci/core/config/tests/test_config_expensive.py +839 -0
- cumulusci/core/config/tests/test_config_util.py +91 -0
- cumulusci/core/config/universal_config.py +88 -0
- cumulusci/core/config/util.py +18 -0
- cumulusci/core/datasets.py +303 -0
- cumulusci/core/debug.py +33 -0
- cumulusci/core/dependencies/__init__.py +55 -0
- cumulusci/core/dependencies/base.py +561 -0
- cumulusci/core/dependencies/dependencies.py +273 -0
- cumulusci/core/dependencies/github.py +177 -0
- cumulusci/core/dependencies/github_resolvers.py +244 -0
- cumulusci/core/dependencies/resolvers.py +580 -0
- cumulusci/core/dependencies/tests/__init__.py +0 -0
- cumulusci/core/dependencies/tests/conftest.py +385 -0
- cumulusci/core/dependencies/tests/test_dependencies.py +950 -0
- cumulusci/core/dependencies/tests/test_github.py +83 -0
- cumulusci/core/dependencies/tests/test_resolvers.py +1027 -0
- cumulusci/core/dependencies/utils.py +13 -0
- cumulusci/core/enums.py +11 -0
- cumulusci/core/exceptions.py +311 -0
- cumulusci/core/flowrunner.py +888 -0
- cumulusci/core/github.py +665 -0
- cumulusci/core/keychain/__init__.py +24 -0
- cumulusci/core/keychain/base_project_keychain.py +441 -0
- cumulusci/core/keychain/encrypted_file_project_keychain.py +945 -0
- cumulusci/core/keychain/environment_project_keychain.py +7 -0
- cumulusci/core/keychain/serialization.py +152 -0
- cumulusci/core/keychain/subprocess_keychain.py +24 -0
- cumulusci/core/keychain/tests/conftest.py +50 -0
- cumulusci/core/keychain/tests/test_base_project_keychain.py +299 -0
- cumulusci/core/keychain/tests/test_encrypted_file_project_keychain.py +1228 -0
- cumulusci/core/metadeploy/__init__.py +0 -0
- cumulusci/core/metadeploy/api.py +88 -0
- cumulusci/core/metadeploy/plans.py +25 -0
- cumulusci/core/metadeploy/tests/test_api.py +276 -0
- cumulusci/core/runtime.py +115 -0
- cumulusci/core/sfdx.py +162 -0
- cumulusci/core/source/__init__.py +16 -0
- cumulusci/core/source/github.py +50 -0
- cumulusci/core/source/local_folder.py +35 -0
- cumulusci/core/source_transforms/__init__.py +0 -0
- cumulusci/core/source_transforms/tests/test_transforms.py +1091 -0
- cumulusci/core/source_transforms/transforms.py +532 -0
- cumulusci/core/tasks.py +404 -0
- cumulusci/core/template_utils.py +59 -0
- cumulusci/core/tests/__init__.py +0 -0
- cumulusci/core/tests/cassettes/TestDatasetsE2E.test_datasets_e2e.yaml +215 -0
- cumulusci/core/tests/cassettes/TestDatasetsE2E.test_datasets_extract_standard_objects.yaml +199 -0
- cumulusci/core/tests/cassettes/TestDatasetsE2E.test_datasets_read_explicit_extract_declaration.yaml +3 -0
- cumulusci/core/tests/fake_remote_repo/cumulusci.yml +32 -0
- cumulusci/core/tests/fake_remote_repo/tasks/directory/example_2.py +6 -0
- cumulusci/core/tests/fake_remote_repo/tasks/example.py +43 -0
- cumulusci/core/tests/fake_remote_repo_2/cumulusci.yml +11 -0
- cumulusci/core/tests/fake_remote_repo_2/tasks/example_3.py +6 -0
- cumulusci/core/tests/test_datasets_e2e.py +386 -0
- cumulusci/core/tests/test_exceptions.py +11 -0
- cumulusci/core/tests/test_flowrunner.py +836 -0
- cumulusci/core/tests/test_github.py +942 -0
- cumulusci/core/tests/test_sfdx.py +138 -0
- cumulusci/core/tests/test_source.py +678 -0
- cumulusci/core/tests/test_tasks.py +262 -0
- cumulusci/core/tests/test_utils.py +141 -0
- cumulusci/core/tests/test_utils_merge_config.py +276 -0
- cumulusci/core/tests/test_versions.py +76 -0
- cumulusci/core/tests/untrusted_repo_child/cumulusci.yml +7 -0
- cumulusci/core/tests/untrusted_repo_child/tasks/untrusted_child.py +6 -0
- cumulusci/core/tests/untrusted_repo_parent/cumulusci.yml +26 -0
- cumulusci/core/tests/untrusted_repo_parent/tasks/untrusted_parent.py +6 -0
- cumulusci/core/tests/utils.py +116 -0
- cumulusci/core/tests/yaml/global.yaml +0 -0
- cumulusci/core/utils.py +402 -0
- cumulusci/core/versions.py +149 -0
- cumulusci/cumulusci.yml +1621 -0
- cumulusci/files/admin_profile.xml +20 -0
- cumulusci/files/delete_excludes.txt +424 -0
- cumulusci/files/templates/project/README.md +12 -0
- cumulusci/files/templates/project/cumulusci.yml +63 -0
- cumulusci/files/templates/project/dot-gitignore +60 -0
- cumulusci/files/templates/project/mapping.yml +45 -0
- cumulusci/files/templates/project/scratch_def.json +25 -0
- cumulusci/oauth/__init__.py +0 -0
- cumulusci/oauth/client.py +400 -0
- cumulusci/oauth/exceptions.py +9 -0
- cumulusci/oauth/salesforce.py +95 -0
- cumulusci/oauth/tests/__init__.py +0 -0
- cumulusci/oauth/tests/cassettes/test_get_device_code.yaml +22 -0
- cumulusci/oauth/tests/cassettes/test_get_device_oauth_token.yaml +74 -0
- cumulusci/oauth/tests/test_client.py +308 -0
- cumulusci/oauth/tests/test_salesforce.py +46 -0
- cumulusci/plugins/__init__.py +3 -0
- cumulusci/plugins/plugin_base.py +93 -0
- cumulusci/plugins/plugin_loader.py +59 -0
- cumulusci/robotframework/CumulusCI.py +340 -0
- cumulusci/robotframework/CumulusCI.robot +7 -0
- cumulusci/robotframework/Performance.py +165 -0
- cumulusci/robotframework/Salesforce.py +936 -0
- cumulusci/robotframework/Salesforce.robot +192 -0
- cumulusci/robotframework/SalesforceAPI.py +416 -0
- cumulusci/robotframework/SalesforcePlaywright.py +220 -0
- cumulusci/robotframework/SalesforcePlaywright.robot +40 -0
- cumulusci/robotframework/__init__.py +2 -0
- cumulusci/robotframework/base_library.py +39 -0
- cumulusci/robotframework/faker_mixin.py +89 -0
- cumulusci/robotframework/form_handlers.py +222 -0
- cumulusci/robotframework/javascript/cci_init.js +34 -0
- cumulusci/robotframework/javascript/cumulusci.js +4 -0
- cumulusci/robotframework/locator_manager.py +197 -0
- cumulusci/robotframework/locators_56.py +88 -0
- cumulusci/robotframework/locators_57.py +5 -0
- cumulusci/robotframework/pageobjects/BasePageObjects.py +433 -0
- cumulusci/robotframework/pageobjects/ObjectManagerPageObject.py +246 -0
- cumulusci/robotframework/pageobjects/PageObjectLibrary.py +45 -0
- cumulusci/robotframework/pageobjects/PageObjects.py +351 -0
- cumulusci/robotframework/pageobjects/__init__.py +12 -0
- cumulusci/robotframework/pageobjects/baseobjects.py +120 -0
- cumulusci/robotframework/perftests/short/collection_perf.robot +105 -0
- cumulusci/robotframework/tests/CustomObjectTestPage.py +10 -0
- cumulusci/robotframework/tests/FooTestPage.py +8 -0
- cumulusci/robotframework/tests/cumulusci/base.robot +40 -0
- cumulusci/robotframework/tests/cumulusci/bulkdata.robot +38 -0
- cumulusci/robotframework/tests/cumulusci/communities.robot +57 -0
- cumulusci/robotframework/tests/cumulusci/datagen.robot +84 -0
- cumulusci/robotframework/tests/salesforce/TestLibraryA.py +24 -0
- cumulusci/robotframework/tests/salesforce/TestLibraryB.py +20 -0
- cumulusci/robotframework/tests/salesforce/TestListener.py +93 -0
- cumulusci/robotframework/tests/salesforce/api.robot +178 -0
- cumulusci/robotframework/tests/salesforce/browsers.robot +143 -0
- cumulusci/robotframework/tests/salesforce/classic.robot +51 -0
- cumulusci/robotframework/tests/salesforce/create_contact.robot +59 -0
- cumulusci/robotframework/tests/salesforce/faker.robot +68 -0
- cumulusci/robotframework/tests/salesforce/forms.robot +172 -0
- cumulusci/robotframework/tests/salesforce/label_locator.robot +244 -0
- cumulusci/robotframework/tests/salesforce/labels.html +33 -0
- cumulusci/robotframework/tests/salesforce/locators.robot +149 -0
- cumulusci/robotframework/tests/salesforce/pageobjects/base_pageobjects.robot +100 -0
- cumulusci/robotframework/tests/salesforce/pageobjects/example_page_object.py +25 -0
- cumulusci/robotframework/tests/salesforce/pageobjects/listing_page.robot +115 -0
- cumulusci/robotframework/tests/salesforce/pageobjects/objectmanager.robot +74 -0
- cumulusci/robotframework/tests/salesforce/pageobjects/pageobjects.robot +171 -0
- cumulusci/robotframework/tests/salesforce/performance.robot +109 -0
- cumulusci/robotframework/tests/salesforce/playwright/javascript_keywords.robot +33 -0
- cumulusci/robotframework/tests/salesforce/playwright/open_test_browser.robot +48 -0
- cumulusci/robotframework/tests/salesforce/playwright/playwright.robot +24 -0
- cumulusci/robotframework/tests/salesforce/playwright/ui.robot +32 -0
- cumulusci/robotframework/tests/salesforce/populate.robot +89 -0
- cumulusci/robotframework/tests/salesforce/test_testlistener.py +37 -0
- cumulusci/robotframework/tests/salesforce/ui.robot +361 -0
- cumulusci/robotframework/tests/test_cumulusci_library.py +304 -0
- cumulusci/robotframework/tests/test_locator_manager.py +158 -0
- cumulusci/robotframework/tests/test_pageobjects.py +291 -0
- cumulusci/robotframework/tests/test_performance.py +38 -0
- cumulusci/robotframework/tests/test_salesforce.py +79 -0
- cumulusci/robotframework/tests/test_salesforce_locators.py +73 -0
- cumulusci/robotframework/tests/test_template_util.py +53 -0
- cumulusci/robotframework/tests/test_utils.py +106 -0
- cumulusci/robotframework/utils.py +283 -0
- cumulusci/salesforce_api/__init__.py +0 -0
- cumulusci/salesforce_api/exceptions.py +23 -0
- cumulusci/salesforce_api/filterable_objects.py +96 -0
- cumulusci/salesforce_api/mc_soap_envelopes.py +89 -0
- cumulusci/salesforce_api/metadata.py +721 -0
- cumulusci/salesforce_api/org_schema.py +571 -0
- cumulusci/salesforce_api/org_schema_models.py +226 -0
- cumulusci/salesforce_api/package_install.py +265 -0
- cumulusci/salesforce_api/package_zip.py +301 -0
- cumulusci/salesforce_api/rest_deploy.py +148 -0
- cumulusci/salesforce_api/retrieve_profile_api.py +301 -0
- cumulusci/salesforce_api/soap_envelopes.py +177 -0
- cumulusci/salesforce_api/tests/__init__.py +0 -0
- cumulusci/salesforce_api/tests/metadata_test_strings.py +24 -0
- cumulusci/salesforce_api/tests/test_metadata.py +1015 -0
- cumulusci/salesforce_api/tests/test_package_install.py +219 -0
- cumulusci/salesforce_api/tests/test_package_zip.py +380 -0
- cumulusci/salesforce_api/tests/test_rest_deploy.py +264 -0
- cumulusci/salesforce_api/tests/test_retrieve_profile_api.py +337 -0
- cumulusci/salesforce_api/tests/test_utils.py +124 -0
- cumulusci/salesforce_api/utils.py +51 -0
- cumulusci/schema/cumulusci.jsonschema.json +782 -0
- cumulusci/tasks/__init__.py +0 -0
- cumulusci/tasks/apex/__init__.py +0 -0
- cumulusci/tasks/apex/anon.py +157 -0
- cumulusci/tasks/apex/batch.py +180 -0
- cumulusci/tasks/apex/testrunner.py +835 -0
- cumulusci/tasks/apex/tests/cassettes/ManualEditTestApexIntegrationTests.test_run_tests__integration_test.yaml +703 -0
- cumulusci/tasks/apex/tests/test_apex_tasks.py +1558 -0
- cumulusci/tasks/base_source_control_task.py +17 -0
- cumulusci/tasks/bulkdata/__init__.py +15 -0
- cumulusci/tasks/bulkdata/base_generate_data_task.py +96 -0
- cumulusci/tasks/bulkdata/dates.py +97 -0
- cumulusci/tasks/bulkdata/delete.py +156 -0
- cumulusci/tasks/bulkdata/extract.py +441 -0
- cumulusci/tasks/bulkdata/extract_dataset_utils/calculate_dependencies.py +117 -0
- cumulusci/tasks/bulkdata/extract_dataset_utils/extract_yml.py +123 -0
- cumulusci/tasks/bulkdata/extract_dataset_utils/hardcoded_default_declarations.py +49 -0
- cumulusci/tasks/bulkdata/extract_dataset_utils/synthesize_extract_declarations.py +283 -0
- cumulusci/tasks/bulkdata/extract_dataset_utils/tests/test_extract_yml.py +142 -0
- cumulusci/tasks/bulkdata/extract_dataset_utils/tests/test_synthesize_extract_declarations.py +575 -0
- cumulusci/tasks/bulkdata/factory_utils.py +134 -0
- cumulusci/tasks/bulkdata/generate.py +4 -0
- cumulusci/tasks/bulkdata/generate_and_load_data.py +232 -0
- cumulusci/tasks/bulkdata/generate_and_load_data_from_yaml.py +19 -0
- cumulusci/tasks/bulkdata/generate_from_yaml.py +183 -0
- cumulusci/tasks/bulkdata/generate_mapping.py +434 -0
- cumulusci/tasks/bulkdata/generate_mapping_utils/dependency_map.py +169 -0
- cumulusci/tasks/bulkdata/generate_mapping_utils/extract_mapping_file_generator.py +45 -0
- cumulusci/tasks/bulkdata/generate_mapping_utils/generate_mapping_from_declarations.py +121 -0
- cumulusci/tasks/bulkdata/generate_mapping_utils/load_mapping_file_generator.py +127 -0
- cumulusci/tasks/bulkdata/generate_mapping_utils/mapping_generator_post_processes.py +53 -0
- cumulusci/tasks/bulkdata/generate_mapping_utils/mapping_transforms.py +139 -0
- cumulusci/tasks/bulkdata/generate_mapping_utils/tests/test_generate_extract_mapping_from_declarations.py +135 -0
- cumulusci/tasks/bulkdata/generate_mapping_utils/tests/test_generate_load_mapping_from_declarations.py +330 -0
- cumulusci/tasks/bulkdata/generate_mapping_utils/tests/test_mapping_generator_post_processes.py +60 -0
- cumulusci/tasks/bulkdata/generate_mapping_utils/tests/test_mapping_transforms.py +188 -0
- cumulusci/tasks/bulkdata/load.py +1196 -0
- cumulusci/tasks/bulkdata/mapping_parser.py +811 -0
- cumulusci/tasks/bulkdata/query_transformers.py +264 -0
- cumulusci/tasks/bulkdata/select_utils.py +792 -0
- cumulusci/tasks/bulkdata/snowfakery.py +753 -0
- cumulusci/tasks/bulkdata/snowfakery_utils/queue_manager.py +478 -0
- cumulusci/tasks/bulkdata/snowfakery_utils/snowfakery_run_until.py +141 -0
- cumulusci/tasks/bulkdata/snowfakery_utils/snowfakery_working_directory.py +53 -0
- cumulusci/tasks/bulkdata/snowfakery_utils/subtask_configurator.py +64 -0
- cumulusci/tasks/bulkdata/step.py +1242 -0
- cumulusci/tasks/bulkdata/tests/__init__.py +0 -0
- cumulusci/tasks/bulkdata/tests/cassettes/TestSelect.test_select_random_strategy.yaml +147 -0
- cumulusci/tasks/bulkdata/tests/cassettes/TestSelect.test_select_similarity_annoy_strategy.yaml +123 -0
- cumulusci/tasks/bulkdata/tests/cassettes/TestSelect.test_select_similarity_select_and_insert_strategy.yaml +313 -0
- cumulusci/tasks/bulkdata/tests/cassettes/TestSelect.test_select_similarity_select_and_insert_strategy_bulk.yaml +550 -0
- cumulusci/tasks/bulkdata/tests/cassettes/TestSelect.test_select_similarity_strategy.yaml +175 -0
- cumulusci/tasks/bulkdata/tests/cassettes/TestSelect.test_select_standard_strategy.yaml +147 -0
- cumulusci/tasks/bulkdata/tests/cassettes/TestSnowfakery.test_run_until_records_in_org__multiple_needed.yaml +69 -0
- cumulusci/tasks/bulkdata/tests/cassettes/TestSnowfakery.test_run_until_records_in_org__none_needed.yaml +22 -0
- cumulusci/tasks/bulkdata/tests/cassettes/TestSnowfakery.test_run_until_records_in_org__one_needed.yaml +24 -0
- cumulusci/tasks/bulkdata/tests/cassettes/TestSnowfakery.test_snowfakery_query_salesforce.yaml +25 -0
- cumulusci/tasks/bulkdata/tests/cassettes/TestUpdatesIntegrationTests.test_updates_task.yaml +80 -0
- cumulusci/tasks/bulkdata/tests/cassettes/TestUpsert.test_simple_upsert__rest.yaml +270 -0
- cumulusci/tasks/bulkdata/tests/cassettes/TestUpsert.test_upsert__rest.yaml +267 -0
- cumulusci/tasks/bulkdata/tests/cassettes/TestUpsert.test_upsert_complex_external_id_field__rest.yaml +369 -0
- cumulusci/tasks/bulkdata/tests/cassettes/TestUpsert.test_upsert_complex_external_id_field_rest__duplicate_error.yaml +204 -0
- cumulusci/tasks/bulkdata/tests/cassettes/TestUpsert.test_upsert_complex_fields__bulk.yaml +675 -0
- cumulusci/tasks/bulkdata/tests/dummy_data_factory.py +36 -0
- cumulusci/tasks/bulkdata/tests/integration_test_utils.py +49 -0
- cumulusci/tasks/bulkdata/tests/mapping-oid.yml +87 -0
- cumulusci/tasks/bulkdata/tests/mapping_after.yml +38 -0
- cumulusci/tasks/bulkdata/tests/mapping_poly.yml +34 -0
- cumulusci/tasks/bulkdata/tests/mapping_poly_incomplete.yml +20 -0
- cumulusci/tasks/bulkdata/tests/mapping_poly_wrong.yml +21 -0
- cumulusci/tasks/bulkdata/tests/mapping_select.yml +20 -0
- cumulusci/tasks/bulkdata/tests/mapping_select_invalid_strategy.yml +20 -0
- cumulusci/tasks/bulkdata/tests/mapping_select_invalid_threshold__invalid_number.yml +21 -0
- cumulusci/tasks/bulkdata/tests/mapping_select_invalid_threshold__invalid_strategy.yml +21 -0
- cumulusci/tasks/bulkdata/tests/mapping_select_invalid_threshold__non_float.yml +21 -0
- cumulusci/tasks/bulkdata/tests/mapping_select_missing_priority_fields.yml +22 -0
- cumulusci/tasks/bulkdata/tests/mapping_select_no_priority_fields.yml +18 -0
- cumulusci/tasks/bulkdata/tests/mapping_simple.yml +27 -0
- cumulusci/tasks/bulkdata/tests/mapping_v1.yml +28 -0
- cumulusci/tasks/bulkdata/tests/mapping_v2.yml +21 -0
- cumulusci/tasks/bulkdata/tests/mapping_v3.yml +32 -0
- cumulusci/tasks/bulkdata/tests/mapping_vanilla_sf.yml +69 -0
- cumulusci/tasks/bulkdata/tests/mock_data_factory_without_mapping.py +12 -0
- cumulusci/tasks/bulkdata/tests/person_accounts.yml +23 -0
- cumulusci/tasks/bulkdata/tests/person_accounts_minimal.yml +15 -0
- cumulusci/tasks/bulkdata/tests/recordtypes.yml +8 -0
- cumulusci/tasks/bulkdata/tests/recordtypes_2.yml +6 -0
- cumulusci/tasks/bulkdata/tests/recordtypes_with_ispersontype.yml +8 -0
- cumulusci/tasks/bulkdata/tests/snowfakery/child/child2.yml +3 -0
- cumulusci/tasks/bulkdata/tests/snowfakery/child.yml +4 -0
- cumulusci/tasks/bulkdata/tests/snowfakery/gen_npsp_standard_objects.recipe.yml +89 -0
- cumulusci/tasks/bulkdata/tests/snowfakery/include_parent.yml +3 -0
- cumulusci/tasks/bulkdata/tests/snowfakery/npsp_standard_objects_macros.yml +34 -0
- cumulusci/tasks/bulkdata/tests/snowfakery/options.recipe.yml +6 -0
- cumulusci/tasks/bulkdata/tests/snowfakery/query_snowfakery.recipe.yml +16 -0
- cumulusci/tasks/bulkdata/tests/snowfakery/sf_standard_object_macros.yml +83 -0
- cumulusci/tasks/bulkdata/tests/snowfakery/simple_snowfakery.load.yml +2 -0
- cumulusci/tasks/bulkdata/tests/snowfakery/simple_snowfakery.recipe.yml +13 -0
- cumulusci/tasks/bulkdata/tests/snowfakery/simple_snowfakery_2.load.yml +5 -0
- cumulusci/tasks/bulkdata/tests/snowfakery/simple_snowfakery_channels.load.yml +13 -0
- cumulusci/tasks/bulkdata/tests/snowfakery/simple_snowfakery_channels.recipe.yml +12 -0
- cumulusci/tasks/bulkdata/tests/snowfakery/simple_snowfakery_channels_2.load.yml +13 -0
- cumulusci/tasks/bulkdata/tests/snowfakery/unique_values.recipe.yml +4 -0
- cumulusci/tasks/bulkdata/tests/snowfakery/upsert.recipe.yml +23 -0
- cumulusci/tasks/bulkdata/tests/snowfakery/upsert_2.recipe.yml +29 -0
- cumulusci/tasks/bulkdata/tests/snowfakery/upsert_before.yml +10 -0
- cumulusci/tasks/bulkdata/tests/test_base_generate_data_tasks.py +61 -0
- cumulusci/tasks/bulkdata/tests/test_dates.py +99 -0
- cumulusci/tasks/bulkdata/tests/test_delete.py +404 -0
- cumulusci/tasks/bulkdata/tests/test_extract.py +1311 -0
- cumulusci/tasks/bulkdata/tests/test_factory_utils.py +55 -0
- cumulusci/tasks/bulkdata/tests/test_generate_and_load.py +252 -0
- cumulusci/tasks/bulkdata/tests/test_generate_from_snowfakery_task.py +343 -0
- cumulusci/tasks/bulkdata/tests/test_generatemapping.py +1039 -0
- cumulusci/tasks/bulkdata/tests/test_load.py +3175 -0
- cumulusci/tasks/bulkdata/tests/test_mapping_parser.py +1658 -0
- cumulusci/tasks/bulkdata/tests/test_query_db__joins_self_lookups.yml +12 -0
- cumulusci/tasks/bulkdata/tests/test_query_db_joins_lookups.yml +26 -0
- cumulusci/tasks/bulkdata/tests/test_query_db_joins_lookups_select.yml +48 -0
- cumulusci/tasks/bulkdata/tests/test_select.py +171 -0
- cumulusci/tasks/bulkdata/tests/test_select_utils.py +1057 -0
- cumulusci/tasks/bulkdata/tests/test_snowfakery.py +1153 -0
- cumulusci/tasks/bulkdata/tests/test_step.py +3957 -0
- cumulusci/tasks/bulkdata/tests/test_updates.py +513 -0
- cumulusci/tasks/bulkdata/tests/test_upsert.py +1015 -0
- cumulusci/tasks/bulkdata/tests/test_utils.py +158 -0
- cumulusci/tasks/bulkdata/tests/testdata.db +0 -0
- cumulusci/tasks/bulkdata/tests/update_describe.py +50 -0
- cumulusci/tasks/bulkdata/tests/update_person_accounts.yml +23 -0
- cumulusci/tasks/bulkdata/tests/utils.py +114 -0
- cumulusci/tasks/bulkdata/update_data.py +260 -0
- cumulusci/tasks/bulkdata/upsert_utils.py +130 -0
- cumulusci/tasks/bulkdata/utils.py +249 -0
- cumulusci/tasks/command.py +178 -0
- cumulusci/tasks/connectedapp.py +186 -0
- cumulusci/tasks/create_package_version.py +778 -0
- cumulusci/tasks/datadictionary.py +745 -0
- cumulusci/tasks/dx_convert_from.py +26 -0
- cumulusci/tasks/github/__init__.py +17 -0
- cumulusci/tasks/github/base.py +16 -0
- cumulusci/tasks/github/commit_status.py +13 -0
- cumulusci/tasks/github/merge.py +11 -0
- cumulusci/tasks/github/publish.py +11 -0
- cumulusci/tasks/github/pull_request.py +11 -0
- cumulusci/tasks/github/release.py +11 -0
- cumulusci/tasks/github/release_report.py +11 -0
- cumulusci/tasks/github/tag.py +11 -0
- cumulusci/tasks/github/tests/__init__.py +0 -0
- cumulusci/tasks/github/tests/test_util.py +202 -0
- cumulusci/tasks/github/tests/test_vcs_migration.py +44 -0
- cumulusci/tasks/github/tests/util_github_api.py +666 -0
- cumulusci/tasks/github/util.py +252 -0
- cumulusci/tasks/marketing_cloud/__init__.py +0 -0
- cumulusci/tasks/marketing_cloud/api.py +188 -0
- cumulusci/tasks/marketing_cloud/base.py +38 -0
- cumulusci/tasks/marketing_cloud/deploy.py +345 -0
- cumulusci/tasks/marketing_cloud/get_user_info.py +40 -0
- cumulusci/tasks/marketing_cloud/mc_constants.py +1 -0
- cumulusci/tasks/marketing_cloud/tests/__init__.py +0 -0
- cumulusci/tasks/marketing_cloud/tests/conftest.py +46 -0
- cumulusci/tasks/marketing_cloud/tests/expected-payload.json +110 -0
- cumulusci/tasks/marketing_cloud/tests/test_api.py +97 -0
- cumulusci/tasks/marketing_cloud/tests/test_api_soap_envelopes.py +145 -0
- cumulusci/tasks/marketing_cloud/tests/test_base.py +14 -0
- cumulusci/tasks/marketing_cloud/tests/test_deploy.py +400 -0
- cumulusci/tasks/marketing_cloud/tests/test_get_user_info.py +141 -0
- cumulusci/tasks/marketing_cloud/tests/validation-response.json +39 -0
- cumulusci/tasks/metadata/__init__.py +0 -0
- cumulusci/tasks/metadata/ee_src.py +94 -0
- cumulusci/tasks/metadata/managed_src.py +100 -0
- cumulusci/tasks/metadata/metadata_map.yml +868 -0
- cumulusci/tasks/metadata/modify.py +99 -0
- cumulusci/tasks/metadata/package.py +684 -0
- cumulusci/tasks/metadata/tests/__init__.py +0 -0
- cumulusci/tasks/metadata/tests/package_metadata/namespaced_report_folder/.hidden/.keep +0 -0
- cumulusci/tasks/metadata/tests/package_metadata/namespaced_report_folder/destructiveChanges.xml +9 -0
- cumulusci/tasks/metadata/tests/package_metadata/namespaced_report_folder/package.xml +9 -0
- cumulusci/tasks/metadata/tests/package_metadata/namespaced_report_folder/package_install_uninstall.xml +11 -0
- cumulusci/tasks/metadata/tests/package_metadata/namespaced_report_folder/reports/namespace__TestFolder/TestReport.report +3 -0
- cumulusci/tasks/metadata/tests/sample_package.xml +9 -0
- cumulusci/tasks/metadata/tests/test_ee_src.py +112 -0
- cumulusci/tasks/metadata/tests/test_managed_src.py +111 -0
- cumulusci/tasks/metadata/tests/test_modify.py +123 -0
- cumulusci/tasks/metadata/tests/test_package.py +476 -0
- cumulusci/tasks/metadata_etl/__init__.py +29 -0
- cumulusci/tasks/metadata_etl/base.py +436 -0
- cumulusci/tasks/metadata_etl/duplicate_rules.py +24 -0
- cumulusci/tasks/metadata_etl/field_sets.py +70 -0
- cumulusci/tasks/metadata_etl/help_text.py +92 -0
- cumulusci/tasks/metadata_etl/layouts.py +550 -0
- cumulusci/tasks/metadata_etl/objects.py +68 -0
- cumulusci/tasks/metadata_etl/permissions.py +167 -0
- cumulusci/tasks/metadata_etl/picklists.py +221 -0
- cumulusci/tasks/metadata_etl/remote_site_settings.py +99 -0
- cumulusci/tasks/metadata_etl/sharing.py +138 -0
- cumulusci/tasks/metadata_etl/tests/test_base.py +512 -0
- cumulusci/tasks/metadata_etl/tests/test_duplicate_rules.py +22 -0
- cumulusci/tasks/metadata_etl/tests/test_field_sets.py +156 -0
- cumulusci/tasks/metadata_etl/tests/test_help_text.py +387 -0
- cumulusci/tasks/metadata_etl/tests/test_ip_ranges.py +85 -0
- cumulusci/tasks/metadata_etl/tests/test_layouts.py +858 -0
- cumulusci/tasks/metadata_etl/tests/test_objects.py +236 -0
- cumulusci/tasks/metadata_etl/tests/test_permissions.py +223 -0
- cumulusci/tasks/metadata_etl/tests/test_picklists.py +547 -0
- cumulusci/tasks/metadata_etl/tests/test_remote_site_settings.py +46 -0
- cumulusci/tasks/metadata_etl/tests/test_sharing.py +333 -0
- cumulusci/tasks/metadata_etl/tests/test_value_sets.py +298 -0
- cumulusci/tasks/metadata_etl/value_sets.py +106 -0
- cumulusci/tasks/metadeploy.py +393 -0
- cumulusci/tasks/metaxml.py +88 -0
- cumulusci/tasks/preflight/__init__.py +0 -0
- cumulusci/tasks/preflight/dataset_load.py +49 -0
- cumulusci/tasks/preflight/licenses.py +86 -0
- cumulusci/tasks/preflight/packages.py +14 -0
- cumulusci/tasks/preflight/permsets.py +23 -0
- cumulusci/tasks/preflight/recordtypes.py +16 -0
- cumulusci/tasks/preflight/retrieve_tasks.py +30 -0
- cumulusci/tasks/preflight/settings.py +77 -0
- cumulusci/tasks/preflight/sobjects.py +202 -0
- cumulusci/tasks/preflight/tests/test_dataset_load.py +85 -0
- cumulusci/tasks/preflight/tests/test_licenses.py +174 -0
- cumulusci/tasks/preflight/tests/test_packages.py +14 -0
- cumulusci/tasks/preflight/tests/test_permset_preflights.py +51 -0
- cumulusci/tasks/preflight/tests/test_recordtypes.py +30 -0
- cumulusci/tasks/preflight/tests/test_retrieve_tasks.py +62 -0
- cumulusci/tasks/preflight/tests/test_settings.py +130 -0
- cumulusci/tasks/preflight/tests/test_sobjects.py +231 -0
- cumulusci/tasks/push/README.md +59 -0
- cumulusci/tasks/push/__init__.py +0 -0
- cumulusci/tasks/push/push_api.py +659 -0
- cumulusci/tasks/push/pushfails.py +136 -0
- cumulusci/tasks/push/tasks.py +476 -0
- cumulusci/tasks/push/tests/conftest.py +263 -0
- cumulusci/tasks/push/tests/test_push_api.py +951 -0
- cumulusci/tasks/push/tests/test_push_tasks.py +659 -0
- cumulusci/tasks/release_notes/README.md +63 -0
- cumulusci/tasks/release_notes/__init__.py +0 -0
- cumulusci/tasks/release_notes/exceptions.py +5 -0
- cumulusci/tasks/release_notes/generator.py +137 -0
- cumulusci/tasks/release_notes/parser.py +232 -0
- cumulusci/tasks/release_notes/provider.py +44 -0
- cumulusci/tasks/release_notes/task.py +300 -0
- cumulusci/tasks/release_notes/tests/__init__.py +0 -0
- cumulusci/tasks/release_notes/tests/change_notes/full/example1.md +17 -0
- cumulusci/tasks/release_notes/tests/change_notes/multi/1.txt +1 -0
- cumulusci/tasks/release_notes/tests/change_notes/multi/2.txt +1 -0
- cumulusci/tasks/release_notes/tests/change_notes/multi/3.txt +1 -0
- cumulusci/tasks/release_notes/tests/change_notes/single/1.txt +1 -0
- cumulusci/tasks/release_notes/tests/test_generator.py +582 -0
- cumulusci/tasks/release_notes/tests/test_parser.py +867 -0
- cumulusci/tasks/release_notes/tests/test_provider.py +512 -0
- cumulusci/tasks/release_notes/tests/test_task.py +461 -0
- cumulusci/tasks/release_notes/tests/utils.py +153 -0
- cumulusci/tasks/robotframework/__init__.py +3 -0
- cumulusci/tasks/robotframework/debugger/DebugListener.py +100 -0
- cumulusci/tasks/robotframework/debugger/__init__.py +10 -0
- cumulusci/tasks/robotframework/debugger/model.py +87 -0
- cumulusci/tasks/robotframework/debugger/ui.py +259 -0
- cumulusci/tasks/robotframework/libdoc.py +269 -0
- cumulusci/tasks/robotframework/robotframework.py +392 -0
- cumulusci/tasks/robotframework/stylesheet.css +130 -0
- cumulusci/tasks/robotframework/template.html +109 -0
- cumulusci/tasks/robotframework/tests/TestLibrary.py +18 -0
- cumulusci/tasks/robotframework/tests/TestPageObjects.py +31 -0
- cumulusci/tasks/robotframework/tests/TestResource.robot +8 -0
- cumulusci/tasks/robotframework/tests/failing_tests.robot +16 -0
- cumulusci/tasks/robotframework/tests/performance.robot +23 -0
- cumulusci/tasks/robotframework/tests/test_browser_proxies.py +137 -0
- cumulusci/tasks/robotframework/tests/test_debugger.py +360 -0
- cumulusci/tasks/robotframework/tests/test_robot_parallel.py +141 -0
- cumulusci/tasks/robotframework/tests/test_robotframework.py +860 -0
- cumulusci/tasks/salesforce/BaseRetrieveMetadata.py +58 -0
- cumulusci/tasks/salesforce/BaseSalesforceApiTask.py +45 -0
- cumulusci/tasks/salesforce/BaseSalesforceMetadataApiTask.py +18 -0
- cumulusci/tasks/salesforce/BaseSalesforceTask.py +4 -0
- cumulusci/tasks/salesforce/BaseUninstallMetadata.py +41 -0
- cumulusci/tasks/salesforce/CreateCommunity.py +124 -0
- cumulusci/tasks/salesforce/CreatePackage.py +29 -0
- cumulusci/tasks/salesforce/Deploy.py +240 -0
- cumulusci/tasks/salesforce/DeployBundles.py +88 -0
- cumulusci/tasks/salesforce/DescribeMetadataTypes.py +26 -0
- cumulusci/tasks/salesforce/EnsureRecordTypes.py +202 -0
- cumulusci/tasks/salesforce/GetInstalledPackages.py +8 -0
- cumulusci/tasks/salesforce/ListCommunities.py +40 -0
- cumulusci/tasks/salesforce/ListCommunityTemplates.py +19 -0
- cumulusci/tasks/salesforce/PublishCommunity.py +62 -0
- cumulusci/tasks/salesforce/RetrievePackaged.py +41 -0
- cumulusci/tasks/salesforce/RetrieveReportsAndDashboards.py +82 -0
- cumulusci/tasks/salesforce/RetrieveUnpackaged.py +36 -0
- cumulusci/tasks/salesforce/SOQLQuery.py +39 -0
- cumulusci/tasks/salesforce/UninstallLocal.py +15 -0
- cumulusci/tasks/salesforce/UninstallLocalBundles.py +28 -0
- cumulusci/tasks/salesforce/UninstallLocalNamespacedBundles.py +58 -0
- cumulusci/tasks/salesforce/UninstallPackage.py +32 -0
- cumulusci/tasks/salesforce/UninstallPackaged.py +56 -0
- cumulusci/tasks/salesforce/UpdateAdminProfile.py +8 -0
- cumulusci/tasks/salesforce/__init__.py +79 -0
- cumulusci/tasks/salesforce/activate_flow.py +74 -0
- cumulusci/tasks/salesforce/check_components.py +324 -0
- cumulusci/tasks/salesforce/composite.py +142 -0
- cumulusci/tasks/salesforce/create_permission_sets.py +35 -0
- cumulusci/tasks/salesforce/custom_settings.py +134 -0
- cumulusci/tasks/salesforce/custom_settings_wait.py +132 -0
- cumulusci/tasks/salesforce/enable_prediction.py +107 -0
- cumulusci/tasks/salesforce/insert_record.py +40 -0
- cumulusci/tasks/salesforce/install_package_version.py +242 -0
- cumulusci/tasks/salesforce/license_preflights.py +8 -0
- cumulusci/tasks/salesforce/network_member_group.py +178 -0
- cumulusci/tasks/salesforce/nonsourcetracking.py +228 -0
- cumulusci/tasks/salesforce/org_settings.py +193 -0
- cumulusci/tasks/salesforce/package_upload.py +328 -0
- cumulusci/tasks/salesforce/profiles.py +74 -0
- cumulusci/tasks/salesforce/promote_package_version.py +376 -0
- cumulusci/tasks/salesforce/retrieve_profile.py +195 -0
- cumulusci/tasks/salesforce/salesforce_files.py +244 -0
- cumulusci/tasks/salesforce/sourcetracking.py +507 -0
- cumulusci/tasks/salesforce/tests/__init__.py +3 -0
- cumulusci/tasks/salesforce/tests/test_CreateCommunity.py +278 -0
- cumulusci/tasks/salesforce/tests/test_CreatePackage.py +22 -0
- cumulusci/tasks/salesforce/tests/test_Deploy.py +470 -0
- cumulusci/tasks/salesforce/tests/test_DeployBundles.py +76 -0
- cumulusci/tasks/salesforce/tests/test_EnsureRecordTypes.py +345 -0
- cumulusci/tasks/salesforce/tests/test_ListCommunities.py +84 -0
- cumulusci/tasks/salesforce/tests/test_ListCommunityTemplates.py +49 -0
- cumulusci/tasks/salesforce/tests/test_PackageUpload.py +547 -0
- cumulusci/tasks/salesforce/tests/test_ProfileGrantAllAccess.py +699 -0
- cumulusci/tasks/salesforce/tests/test_PublishCommunity.py +181 -0
- cumulusci/tasks/salesforce/tests/test_RetrievePackaged.py +24 -0
- cumulusci/tasks/salesforce/tests/test_RetrieveReportsAndDashboards.py +56 -0
- cumulusci/tasks/salesforce/tests/test_RetrieveUnpackaged.py +21 -0
- cumulusci/tasks/salesforce/tests/test_SOQLQuery.py +30 -0
- cumulusci/tasks/salesforce/tests/test_UninstallLocal.py +15 -0
- cumulusci/tasks/salesforce/tests/test_UninstallLocalBundles.py +19 -0
- cumulusci/tasks/salesforce/tests/test_UninstallLocalNamespacedBundles.py +22 -0
- cumulusci/tasks/salesforce/tests/test_UninstallPackage.py +19 -0
- cumulusci/tasks/salesforce/tests/test_UninstallPackaged.py +66 -0
- cumulusci/tasks/salesforce/tests/test_UninstallPackagedIncremental.py +127 -0
- cumulusci/tasks/salesforce/tests/test_activate_flow.py +132 -0
- cumulusci/tasks/salesforce/tests/test_base_tasks.py +110 -0
- cumulusci/tasks/salesforce/tests/test_check_components.py +445 -0
- cumulusci/tasks/salesforce/tests/test_composite.py +250 -0
- cumulusci/tasks/salesforce/tests/test_create_permission_sets.py +41 -0
- cumulusci/tasks/salesforce/tests/test_custom_settings.py +227 -0
- cumulusci/tasks/salesforce/tests/test_custom_settings_wait.py +174 -0
- cumulusci/tasks/salesforce/tests/test_describemetadatatypes.py +18 -0
- cumulusci/tasks/salesforce/tests/test_enable_prediction.py +240 -0
- cumulusci/tasks/salesforce/tests/test_insert_record.py +110 -0
- cumulusci/tasks/salesforce/tests/test_install_package_version.py +464 -0
- cumulusci/tasks/salesforce/tests/test_network_member_group.py +444 -0
- cumulusci/tasks/salesforce/tests/test_nonsourcetracking.py +235 -0
- cumulusci/tasks/salesforce/tests/test_org_settings.py +407 -0
- cumulusci/tasks/salesforce/tests/test_profiles.py +202 -0
- cumulusci/tasks/salesforce/tests/test_retrieve_profile.py +287 -0
- cumulusci/tasks/salesforce/tests/test_salesforce_files.py +228 -0
- cumulusci/tasks/salesforce/tests/test_sourcetracking.py +350 -0
- cumulusci/tasks/salesforce/tests/test_trigger_handlers.py +300 -0
- cumulusci/tasks/salesforce/tests/test_update_dependencies.py +509 -0
- cumulusci/tasks/salesforce/tests/util.py +79 -0
- cumulusci/tasks/salesforce/trigger_handlers.py +119 -0
- cumulusci/tasks/salesforce/uninstall_packaged_incremental.py +136 -0
- cumulusci/tasks/salesforce/update_dependencies.py +290 -0
- cumulusci/tasks/salesforce/update_profile.py +339 -0
- cumulusci/tasks/salesforce/users/permsets.py +227 -0
- cumulusci/tasks/salesforce/users/photos.py +162 -0
- cumulusci/tasks/salesforce/users/tests/photo.mock.txt +1 -0
- cumulusci/tasks/salesforce/users/tests/test_permsets.py +950 -0
- cumulusci/tasks/salesforce/users/tests/test_photos.py +373 -0
- cumulusci/tasks/sample_data/capture_sample_data.py +77 -0
- cumulusci/tasks/sample_data/load_sample_data.py +85 -0
- cumulusci/tasks/sample_data/test_capture_sample_data.py +117 -0
- cumulusci/tasks/sample_data/test_load_sample_data.py +121 -0
- cumulusci/tasks/sfdx.py +83 -0
- cumulusci/tasks/tests/__init__.py +1 -0
- cumulusci/tasks/tests/conftest.py +30 -0
- cumulusci/tasks/tests/test_command.py +129 -0
- cumulusci/tasks/tests/test_connectedapp.py +236 -0
- cumulusci/tasks/tests/test_create_package_version.py +847 -0
- cumulusci/tasks/tests/test_datadictionary.py +1575 -0
- cumulusci/tasks/tests/test_dx_convert_from.py +60 -0
- cumulusci/tasks/tests/test_metadeploy.py +624 -0
- cumulusci/tasks/tests/test_metaxml.py +99 -0
- cumulusci/tasks/tests/test_promote_package_version.py +488 -0
- cumulusci/tasks/tests/test_pushfails.py +96 -0
- cumulusci/tasks/tests/test_salesforce.py +72 -0
- cumulusci/tasks/tests/test_sfdx.py +105 -0
- cumulusci/tasks/tests/test_util.py +207 -0
- cumulusci/tasks/util.py +261 -0
- cumulusci/tasks/vcs/__init__.py +19 -0
- cumulusci/tasks/vcs/commit_status.py +58 -0
- cumulusci/tasks/vcs/create_commit_status.py +37 -0
- cumulusci/tasks/vcs/download_extract.py +199 -0
- cumulusci/tasks/vcs/merge.py +298 -0
- cumulusci/tasks/vcs/publish.py +207 -0
- cumulusci/tasks/vcs/pull_request.py +9 -0
- cumulusci/tasks/vcs/release.py +134 -0
- cumulusci/tasks/vcs/release_report.py +105 -0
- cumulusci/tasks/vcs/tag.py +31 -0
- cumulusci/tasks/vcs/tests/github/test_commit_status.py +196 -0
- cumulusci/tasks/vcs/tests/github/test_download_extract.py +896 -0
- cumulusci/tasks/vcs/tests/github/test_merge.py +1118 -0
- cumulusci/tasks/vcs/tests/github/test_publish.py +823 -0
- cumulusci/tasks/vcs/tests/github/test_pull_request.py +29 -0
- cumulusci/tasks/vcs/tests/github/test_release.py +390 -0
- cumulusci/tasks/vcs/tests/github/test_release_report.py +109 -0
- cumulusci/tasks/vcs/tests/github/test_tag.py +90 -0
- cumulusci/tasks/vlocity/exceptions.py +2 -0
- cumulusci/tasks/vlocity/tests/test_vlocity.py +283 -0
- cumulusci/tasks/vlocity/vlocity.py +342 -0
- cumulusci/tests/__init__.py +1 -0
- cumulusci/tests/cassettes/GET_sobjects_Account_PersonAccount_describe.yaml +18 -0
- cumulusci/tests/cassettes/TestIntegrationInfrastructure.test_integration_tests.yaml +19 -0
- cumulusci/tests/pytest_plugins/pytest_sf_orgconnect.py +307 -0
- cumulusci/tests/pytest_plugins/pytest_sf_vcr.py +275 -0
- cumulusci/tests/pytest_plugins/pytest_sf_vcr_serializer.py +160 -0
- cumulusci/tests/pytest_plugins/pytest_typeguard.py +5 -0
- cumulusci/tests/pytest_plugins/test_vcr_string_compressor.py +49 -0
- cumulusci/tests/pytest_plugins/vcr_string_compressor.py +97 -0
- cumulusci/tests/shared_cassettes/GET_sobjects_Account_describe.yaml +18 -0
- cumulusci/tests/shared_cassettes/GET_sobjects_Case_describe.yaml +18 -0
- cumulusci/tests/shared_cassettes/GET_sobjects_Contact_describe.yaml +4838 -0
- cumulusci/tests/shared_cassettes/GET_sobjects_Custom__c_describe.yaml +242 -0
- cumulusci/tests/shared_cassettes/GET_sobjects_Event_describe.yaml +19 -0
- cumulusci/tests/shared_cassettes/GET_sobjects_Global_describe.yaml +1338 -0
- cumulusci/tests/shared_cassettes/GET_sobjects_Lead_describe.yaml +18 -0
- cumulusci/tests/shared_cassettes/GET_sobjects_OpportunityContactRole_describe.yaml +34 -0
- cumulusci/tests/shared_cassettes/GET_sobjects_Opportunity_describe.yaml +1261 -0
- cumulusci/tests/shared_cassettes/GET_sobjects_Organization.yaml +49 -0
- cumulusci/tests/shared_cassettes/vcr_string_templates/batchInfoList_xml.tpl +15 -0
- cumulusci/tests/shared_cassettes/vcr_string_templates/batchInfo_xml.tpl +13 -0
- cumulusci/tests/shared_cassettes/vcr_string_templates/jobInfo_insert_xml.tpl +24 -0
- cumulusci/tests/shared_cassettes/vcr_string_templates/jobInfo_upsert_xml.tpl +25 -0
- cumulusci/tests/test_entry_points.py +20 -0
- cumulusci/tests/test_integration_infrastructure.py +131 -0
- cumulusci/tests/test_main.py +9 -0
- cumulusci/tests/test_schema.py +32 -0
- cumulusci/tests/test_utils.py +657 -0
- cumulusci/tests/test_vcr_serializer.py +134 -0
- cumulusci/tests/uncompressed_cassette.yaml +83 -0
- cumulusci/tests/util.py +344 -0
- cumulusci/utils/__init__.py +731 -0
- cumulusci/utils/classutils.py +9 -0
- cumulusci/utils/collections.py +32 -0
- cumulusci/utils/deprecation.py +11 -0
- cumulusci/utils/encryption.py +31 -0
- cumulusci/utils/fileutils.py +295 -0
- cumulusci/utils/git.py +142 -0
- cumulusci/utils/http/multi_request.py +214 -0
- cumulusci/utils/http/requests_utils.py +103 -0
- cumulusci/utils/http/tests/cassettes/ManualEditTestCompositeParallelSalesforce.test_http_headers.yaml +32 -0
- cumulusci/utils/http/tests/cassettes/TestCompositeParallelSalesforce.test_composite_parallel_salesforce.yaml +65 -0
- cumulusci/utils/http/tests/cassettes/TestCompositeParallelSalesforce.test_errors.yaml +24 -0
- cumulusci/utils/http/tests/cassettes/TestCompositeParallelSalesforce.test_reference_ids.yaml +49 -0
- cumulusci/utils/http/tests/test_multi_request.py +255 -0
- cumulusci/utils/iterators.py +21 -0
- cumulusci/utils/logging.py +128 -0
- cumulusci/utils/metaprogramming.py +10 -0
- cumulusci/utils/options.py +138 -0
- cumulusci/utils/parallel/queries_in_parallel/run_queries_in_parallel.py +29 -0
- cumulusci/utils/parallel/queries_in_parallel/tests/test_run_queries_in_parallel.py +50 -0
- cumulusci/utils/parallel/task_worker_queues/parallel_worker.py +238 -0
- cumulusci/utils/parallel/task_worker_queues/parallel_worker_queue.py +243 -0
- cumulusci/utils/parallel/task_worker_queues/tests/test_parallel_worker.py +353 -0
- cumulusci/utils/salesforce/count_sobjects.py +46 -0
- cumulusci/utils/salesforce/soql.py +17 -0
- cumulusci/utils/salesforce/tests/cassettes/ManualEdit_TestCountSObjects.test_count_sobjects__network_errors.yaml +23 -0
- cumulusci/utils/salesforce/tests/cassettes/TestCountSObjects.test_count_sobjects__errors.yaml +33 -0
- cumulusci/utils/salesforce/tests/cassettes/TestCountSObjects.test_count_sobjects_simple.yaml +29 -0
- cumulusci/utils/salesforce/tests/test_count_sobjects.py +29 -0
- cumulusci/utils/salesforce/tests/test_soql.py +30 -0
- cumulusci/utils/tests/cassettes/ManualEditTestDescribeOrg.test_minimal_schema.yaml +36 -0
- cumulusci/utils/tests/cassettes/ManualEdit_test_describe_to_sql.yaml +191 -0
- cumulusci/utils/tests/test_fileutils.py +284 -0
- cumulusci/utils/tests/test_git.py +85 -0
- cumulusci/utils/tests/test_logging.py +70 -0
- cumulusci/utils/tests/test_option_parsing.py +188 -0
- cumulusci/utils/tests/test_org_schema.py +691 -0
- cumulusci/utils/tests/test_org_schema_models.py +79 -0
- cumulusci/utils/tests/test_waiting.py +25 -0
- cumulusci/utils/version_strings.py +391 -0
- cumulusci/utils/waiting.py +42 -0
- cumulusci/utils/xml/__init__.py +91 -0
- cumulusci/utils/xml/metadata_tree.py +299 -0
- cumulusci/utils/xml/robot_xml.py +114 -0
- cumulusci/utils/xml/salesforce_encoding.py +100 -0
- cumulusci/utils/xml/test/test_metadata_tree.py +251 -0
- cumulusci/utils/xml/test/test_salesforce_encoding.py +173 -0
- cumulusci/utils/yaml/cumulusci_yml.py +401 -0
- cumulusci/utils/yaml/model_parser.py +156 -0
- cumulusci/utils/yaml/safer_loader.py +74 -0
- cumulusci/utils/yaml/tests/bad_cci.yml +5 -0
- cumulusci/utils/yaml/tests/cassettes/TestCumulusciYml.test_validate_url__with_errors.yaml +20 -0
- cumulusci/utils/yaml/tests/test_cumulusci_yml.py +286 -0
- cumulusci/utils/yaml/tests/test_model_parser.py +175 -0
- cumulusci/utils/yaml/tests/test_safer_loader.py +88 -0
- cumulusci/utils/ziputils.py +61 -0
- cumulusci/vcs/base.py +143 -0
- cumulusci/vcs/bootstrap.py +272 -0
- cumulusci/vcs/github/__init__.py +24 -0
- cumulusci/vcs/github/adapter.py +689 -0
- cumulusci/vcs/github/release_notes/generator.py +219 -0
- cumulusci/vcs/github/release_notes/parser.py +151 -0
- cumulusci/vcs/github/release_notes/provider.py +143 -0
- cumulusci/vcs/github/service.py +569 -0
- cumulusci/vcs/github/tests/test_adapter.py +138 -0
- cumulusci/vcs/github/tests/test_service.py +408 -0
- cumulusci/vcs/models.py +586 -0
- cumulusci/vcs/tests/conftest.py +41 -0
- cumulusci/vcs/tests/dummy_service.py +241 -0
- cumulusci/vcs/tests/test_vcs_base.py +687 -0
- cumulusci/vcs/tests/test_vcs_bootstrap.py +727 -0
- cumulusci/vcs/utils/__init__.py +31 -0
- cumulusci/vcs/vcs_source.py +287 -0
- cumulusci_plus-5.0.0.dist-info/METADATA +145 -0
- cumulusci_plus-5.0.0.dist-info/RECORD +744 -0
- cumulusci_plus-5.0.0.dist-info/WHEEL +4 -0
- cumulusci_plus-5.0.0.dist-info/entry_points.txt +3 -0
- cumulusci_plus-5.0.0.dist-info/licenses/AUTHORS.rst +41 -0
- cumulusci_plus-5.0.0.dist-info/licenses/LICENSE +30 -0
|
@@ -0,0 +1,1895 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
import json
|
|
3
|
+
import os
|
|
4
|
+
import pathlib
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
from tempfile import TemporaryDirectory
|
|
7
|
+
from unittest import mock
|
|
8
|
+
|
|
9
|
+
import pytest
|
|
10
|
+
import responses
|
|
11
|
+
import yaml
|
|
12
|
+
from github3.exceptions import NotFoundError
|
|
13
|
+
from simple_salesforce.exceptions import SalesforceError
|
|
14
|
+
|
|
15
|
+
from cumulusci.core.config import (
|
|
16
|
+
BaseConfig,
|
|
17
|
+
BaseProjectConfig,
|
|
18
|
+
BaseTaskFlowConfig,
|
|
19
|
+
OrgConfig,
|
|
20
|
+
ServiceConfig,
|
|
21
|
+
UniversalConfig,
|
|
22
|
+
)
|
|
23
|
+
from cumulusci.core.config.org_config import VersionInfo
|
|
24
|
+
from cumulusci.core.dependencies.dependencies import (
|
|
25
|
+
PackageNamespaceVersionDependency,
|
|
26
|
+
PackageVersionIdDependency,
|
|
27
|
+
)
|
|
28
|
+
from cumulusci.core.exceptions import (
|
|
29
|
+
ConfigError,
|
|
30
|
+
CumulusCIException,
|
|
31
|
+
DependencyResolutionError,
|
|
32
|
+
FlowNotFoundError,
|
|
33
|
+
KeychainNotFound,
|
|
34
|
+
NamespaceNotFoundError,
|
|
35
|
+
ServiceNotConfigured,
|
|
36
|
+
TaskNotFoundError,
|
|
37
|
+
VcsException,
|
|
38
|
+
)
|
|
39
|
+
from cumulusci.core.keychain.base_project_keychain import (
|
|
40
|
+
DEFAULT_CONNECTED_APP,
|
|
41
|
+
BaseProjectKeychain,
|
|
42
|
+
)
|
|
43
|
+
from cumulusci.core.source import LocalFolderSource
|
|
44
|
+
from cumulusci.tests.util import CURRENT_SF_API_VERSION, DummyKeychain
|
|
45
|
+
from cumulusci.utils import temporary_dir, touch
|
|
46
|
+
from cumulusci.utils.version_strings import StrictVersion
|
|
47
|
+
from cumulusci.utils.yaml.cumulusci_yml import GitHubSourceModel, LocalFolderSourceModel
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
class FakeConfig(BaseConfig):
|
|
51
|
+
foo: dict
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
class TestBaseConfig:
|
|
55
|
+
def test_getattr_toplevel_key(self):
|
|
56
|
+
config = FakeConfig()
|
|
57
|
+
config.config = {"foo": "bar"}
|
|
58
|
+
assert config.foo == "bar"
|
|
59
|
+
|
|
60
|
+
def test_getattr_toplevel_key_missing(self):
|
|
61
|
+
config = BaseConfig()
|
|
62
|
+
config.config = {}
|
|
63
|
+
with mock.patch(
|
|
64
|
+
"cumulusci.core.config.base_config.STRICT_GETATTR", False
|
|
65
|
+
), pytest.warns(DeprecationWarning, match="foo"):
|
|
66
|
+
assert config.foo is None
|
|
67
|
+
with mock.patch(
|
|
68
|
+
"cumulusci.core.config.base_config.STRICT_GETATTR", True
|
|
69
|
+
), pytest.deprecated_call(), pytest.raises(AssertionError):
|
|
70
|
+
assert config.foo is None
|
|
71
|
+
|
|
72
|
+
def test_getattr_child_key(self):
|
|
73
|
+
config = FakeConfig()
|
|
74
|
+
config.config = {"foo": {"bar": "baz"}}
|
|
75
|
+
assert config.foo__bar == "baz"
|
|
76
|
+
|
|
77
|
+
def test_strict_getattr(self):
|
|
78
|
+
config = FakeConfig()
|
|
79
|
+
config.config = {"foo": {"bar": "baz"}}
|
|
80
|
+
with mock.patch(
|
|
81
|
+
"cumulusci.core.config.base_config.STRICT_GETATTR", "True"
|
|
82
|
+
), mock.patch("warnings.warn"), pytest.raises(AssertionError):
|
|
83
|
+
print(config.jfiesojfieoj)
|
|
84
|
+
|
|
85
|
+
def test_getattr_child_parent_key_missing(self):
|
|
86
|
+
config = FakeConfig()
|
|
87
|
+
config.config = {}
|
|
88
|
+
assert config.foo__bar is None
|
|
89
|
+
|
|
90
|
+
def test_getattr_child_key_missing(self):
|
|
91
|
+
config = FakeConfig()
|
|
92
|
+
config.config = {"foo": {}}
|
|
93
|
+
assert config.foo__bar is None
|
|
94
|
+
|
|
95
|
+
def test_getattr_default_toplevel(self):
|
|
96
|
+
config = FakeConfig()
|
|
97
|
+
config.config = {"foo": "bar"}
|
|
98
|
+
config.defaults = {"foo": "default"}
|
|
99
|
+
assert config.foo == "bar"
|
|
100
|
+
|
|
101
|
+
def test_getattr_default_toplevel_missing_default(self):
|
|
102
|
+
config = FakeConfig()
|
|
103
|
+
config.config = {"foo": "bar"}
|
|
104
|
+
config.defaults = {}
|
|
105
|
+
assert config.foo == "bar"
|
|
106
|
+
|
|
107
|
+
def test_getattr_default_toplevel_missing_config(self):
|
|
108
|
+
config = FakeConfig()
|
|
109
|
+
config.config = {}
|
|
110
|
+
config.defaults = {"foo": "default"}
|
|
111
|
+
assert config.foo == "default"
|
|
112
|
+
|
|
113
|
+
def test_getattr_default_child(self):
|
|
114
|
+
config = FakeConfig()
|
|
115
|
+
config.config = {"foo": {"bar": "baz"}}
|
|
116
|
+
config.defaults = {"foo__bar": "default"}
|
|
117
|
+
assert config.foo__bar == "baz"
|
|
118
|
+
|
|
119
|
+
def test_getattr_default_child_missing_default(self):
|
|
120
|
+
config = FakeConfig()
|
|
121
|
+
config.config = {"foo": {"bar": "baz"}}
|
|
122
|
+
config.defaults = {}
|
|
123
|
+
assert config.foo__bar == "baz"
|
|
124
|
+
|
|
125
|
+
def test_getattr_default_child_missing_config(self):
|
|
126
|
+
config = FakeConfig()
|
|
127
|
+
config.config = {}
|
|
128
|
+
config.defaults = {"foo__bar": "default"}
|
|
129
|
+
assert config.foo__bar == "default"
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
class DummyContents(object):
|
|
133
|
+
def __init__(self, content):
|
|
134
|
+
self.decoded = content
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
class DummyResponse(object):
|
|
138
|
+
def __init__(self, content, status_code):
|
|
139
|
+
self.content = content
|
|
140
|
+
self.status_code = status_code
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
class DummyRepository(mock.Mock):
|
|
144
|
+
default_branch = "main"
|
|
145
|
+
_api = "http://"
|
|
146
|
+
|
|
147
|
+
def __init__(self, owner, name, contents, releases=None, commits=None, **kwargs):
|
|
148
|
+
"""Passing kwargs to workaround python/cpython#83759"""
|
|
149
|
+
super().__init__()
|
|
150
|
+
self.owner = owner
|
|
151
|
+
self.name = name
|
|
152
|
+
self.html_url = f"https://github.com/{owner}/{name}"
|
|
153
|
+
self.clone_url = self.html_url
|
|
154
|
+
self.session = mock.MagicMock()
|
|
155
|
+
self._contents = contents
|
|
156
|
+
self._releases = releases or []
|
|
157
|
+
self._commits = commits or []
|
|
158
|
+
self._tag_message = kwargs.get(
|
|
159
|
+
"tag_message", "Mock tag message for testing purposes"
|
|
160
|
+
)
|
|
161
|
+
|
|
162
|
+
def file_contents(self, path, **kw):
|
|
163
|
+
try:
|
|
164
|
+
return self._contents[path]
|
|
165
|
+
except KeyError:
|
|
166
|
+
raise AssertionError(f"Accessed unexpected file: {path}")
|
|
167
|
+
|
|
168
|
+
def directory_contents(self, path, **kw):
|
|
169
|
+
try:
|
|
170
|
+
return self._contents[path]
|
|
171
|
+
except KeyError:
|
|
172
|
+
raise NotFoundError(
|
|
173
|
+
DummyResponse(f"Accessed unexpected directory: {path}", 404)
|
|
174
|
+
)
|
|
175
|
+
|
|
176
|
+
def _build_url(self, *args, **kw):
|
|
177
|
+
return self._api
|
|
178
|
+
|
|
179
|
+
def releases(self):
|
|
180
|
+
return iter(self._releases)
|
|
181
|
+
|
|
182
|
+
def latest_release(self):
|
|
183
|
+
for release in self._releases:
|
|
184
|
+
if release.tag_name.startswith("release/"):
|
|
185
|
+
return release
|
|
186
|
+
raise NotFoundError(DummyResponse("", 404))
|
|
187
|
+
|
|
188
|
+
def release_from_tag(self, tag_name):
|
|
189
|
+
for release in self._releases:
|
|
190
|
+
if release.tag_name == tag_name:
|
|
191
|
+
return release
|
|
192
|
+
raise NotFoundError(DummyResponse("", 404))
|
|
193
|
+
|
|
194
|
+
def branch(self, name):
|
|
195
|
+
branch = mock.Mock()
|
|
196
|
+
branch.commit.sha = "commit_sha"
|
|
197
|
+
branch.name = name
|
|
198
|
+
return branch
|
|
199
|
+
|
|
200
|
+
def tag(self, sha):
|
|
201
|
+
tag = mock.Mock()
|
|
202
|
+
tag.object.sha = "tag_sha"
|
|
203
|
+
tag.message = self.tag_message or ""
|
|
204
|
+
return tag
|
|
205
|
+
|
|
206
|
+
def ref(self, s):
|
|
207
|
+
ref = mock.Mock()
|
|
208
|
+
ref.object.sha = "ref_sha"
|
|
209
|
+
return ref
|
|
210
|
+
|
|
211
|
+
def commit(self, c):
|
|
212
|
+
if c in self._commits:
|
|
213
|
+
return self._commits[c]
|
|
214
|
+
|
|
215
|
+
raise NotFoundError(DummyResponse("", 404))
|
|
216
|
+
|
|
217
|
+
@property
|
|
218
|
+
def tag_message(self):
|
|
219
|
+
"""A mock property to simulate the tag message."""
|
|
220
|
+
return self._tag_message
|
|
221
|
+
|
|
222
|
+
@tag_message.setter
|
|
223
|
+
def tag_message(self, message):
|
|
224
|
+
"""A mock setter to change the tag message."""
|
|
225
|
+
self._tag_message = message
|
|
226
|
+
|
|
227
|
+
|
|
228
|
+
class DummyRelease(object):
|
|
229
|
+
def __init__(self, tag_name, name=None):
|
|
230
|
+
self.tag_name = tag_name
|
|
231
|
+
self.name = name
|
|
232
|
+
|
|
233
|
+
|
|
234
|
+
class DummyGithub(object):
|
|
235
|
+
def __init__(self, repositories):
|
|
236
|
+
self.repositories = repositories
|
|
237
|
+
|
|
238
|
+
def repository(self, owner, name):
|
|
239
|
+
try:
|
|
240
|
+
return self.repositories[name]
|
|
241
|
+
except KeyError:
|
|
242
|
+
raise AssertionError(f"Unexpected repository: {name}")
|
|
243
|
+
|
|
244
|
+
|
|
245
|
+
class DummyGithubRepository(object):
|
|
246
|
+
"""A wrapper around DummyRepository that follows the AbstractRepo interface"""
|
|
247
|
+
|
|
248
|
+
def __init__(self, dummy_repo, **kwargs):
|
|
249
|
+
self.repo = dummy_repo # This contains the actual DummyRepository
|
|
250
|
+
self.logger = kwargs.get("logger")
|
|
251
|
+
self.project_config = kwargs.get("project_config")
|
|
252
|
+
self.repo_name = dummy_repo.name
|
|
253
|
+
self.repo_owner = dummy_repo.owner
|
|
254
|
+
self.repo_url = dummy_repo.html_url
|
|
255
|
+
|
|
256
|
+
def latest_release(self):
|
|
257
|
+
"""Return the latest release from the wrapped repository"""
|
|
258
|
+
return self.repo.latest_release()
|
|
259
|
+
|
|
260
|
+
def releases(self):
|
|
261
|
+
"""Return releases from the wrapped repository"""
|
|
262
|
+
return self.repo.releases()
|
|
263
|
+
|
|
264
|
+
def release_from_tag(self, tag_name):
|
|
265
|
+
"""Return release for a specific tag from the wrapped repository"""
|
|
266
|
+
return self.repo.release_from_tag(tag_name)
|
|
267
|
+
|
|
268
|
+
|
|
269
|
+
class DummyService(object):
|
|
270
|
+
"""A dummy VCS service that returns DummyGithubRepository instances"""
|
|
271
|
+
|
|
272
|
+
def __init__(self, dummy_github, dummy_project_config=None):
|
|
273
|
+
self.dummy_github = dummy_github
|
|
274
|
+
self.logger = None
|
|
275
|
+
self.project_config = dummy_project_config
|
|
276
|
+
|
|
277
|
+
def get_repository(self, options=None):
|
|
278
|
+
"""Return a DummyGithubRepository wrapping the DummyRepository"""
|
|
279
|
+
# For the test, we'll use the CumulusCI repository
|
|
280
|
+
dummy_repo = self.dummy_github.repositories["CumulusCI"]
|
|
281
|
+
if dummy_repo is None:
|
|
282
|
+
return None # This will cause the get_repo() method to raise an exception
|
|
283
|
+
return DummyGithubRepository(
|
|
284
|
+
dummy_repo, logger=self.logger, project_config=self.project_config
|
|
285
|
+
)
|
|
286
|
+
|
|
287
|
+
|
|
288
|
+
class TestBaseProjectConfig:
|
|
289
|
+
maxDiff = None
|
|
290
|
+
|
|
291
|
+
def _make_github(self):
|
|
292
|
+
CUMULUSCI_TEST_REPO = DummyRepository(
|
|
293
|
+
"SFDO-Tooling",
|
|
294
|
+
"CumulusCI-Test",
|
|
295
|
+
{
|
|
296
|
+
"cumulusci.yml": DummyContents(
|
|
297
|
+
b"""
|
|
298
|
+
project:
|
|
299
|
+
name: CumulusCI-Test
|
|
300
|
+
package:
|
|
301
|
+
name: CumulusCI-Test
|
|
302
|
+
namespace: ccitest
|
|
303
|
+
git:
|
|
304
|
+
repo_url: https://github.com/SFDO-Tooling/CumulusCI-Test
|
|
305
|
+
dependencies:
|
|
306
|
+
- github: https://github.com/SFDO-Tooling/CumulusCI-Test-Dep
|
|
307
|
+
service: github
|
|
308
|
+
"""
|
|
309
|
+
),
|
|
310
|
+
"unpackaged/pre": {"pre": {}, "skip": {}},
|
|
311
|
+
"src": {"src": ""},
|
|
312
|
+
"unpackaged/post": {"post": {}, "skip": {}},
|
|
313
|
+
},
|
|
314
|
+
)
|
|
315
|
+
|
|
316
|
+
CUMULUSCI_TEST_DEP_REPO = DummyRepository(
|
|
317
|
+
"SFDO-Tooling",
|
|
318
|
+
"CumulusCI-Test-Dep",
|
|
319
|
+
{
|
|
320
|
+
"cumulusci.yml": DummyContents(
|
|
321
|
+
b"""
|
|
322
|
+
project:
|
|
323
|
+
name: CumulusCI-Test-Dep
|
|
324
|
+
package:
|
|
325
|
+
name: CumulusCI-Test-Dep
|
|
326
|
+
namespace: ccitestdep
|
|
327
|
+
git:
|
|
328
|
+
repo_url: https://github.com/SFDO-Tooling/CumulusCI-Test-Dep
|
|
329
|
+
service:
|
|
330
|
+
service_type: github
|
|
331
|
+
service_alias: alias
|
|
332
|
+
"""
|
|
333
|
+
),
|
|
334
|
+
"unpackaged/pre": {},
|
|
335
|
+
"src": {},
|
|
336
|
+
"unpackaged/post": {},
|
|
337
|
+
},
|
|
338
|
+
[DummyRelease("release/2.0", "2.0")],
|
|
339
|
+
)
|
|
340
|
+
|
|
341
|
+
CUMULUSCI_REPO = DummyRepository(
|
|
342
|
+
"SFDO-Tooling",
|
|
343
|
+
"CumulusCI",
|
|
344
|
+
{},
|
|
345
|
+
[
|
|
346
|
+
DummyRelease("release/1.1", "1.1"),
|
|
347
|
+
DummyRelease("beta-wrongprefix", "wrong"),
|
|
348
|
+
DummyRelease("release/1.0", "1.0"),
|
|
349
|
+
DummyRelease("beta/1.0-Beta_2", "1.0 (Beta 2)"),
|
|
350
|
+
DummyRelease("beta/1.0-Beta_1", "1.0 (Beta 1)"),
|
|
351
|
+
],
|
|
352
|
+
)
|
|
353
|
+
|
|
354
|
+
return DummyGithub(
|
|
355
|
+
{
|
|
356
|
+
"CumulusCI": CUMULUSCI_REPO,
|
|
357
|
+
"CumulusCI-Test": CUMULUSCI_TEST_REPO,
|
|
358
|
+
"CumulusCI-Test-Dep": CUMULUSCI_TEST_DEP_REPO,
|
|
359
|
+
}
|
|
360
|
+
)
|
|
361
|
+
|
|
362
|
+
def test_config_global(self):
|
|
363
|
+
universal_config = UniversalConfig()
|
|
364
|
+
universal_config.config_global = {}
|
|
365
|
+
config = BaseProjectConfig(universal_config)
|
|
366
|
+
assert universal_config.config_global is config.config_global
|
|
367
|
+
|
|
368
|
+
def test_config_universal(self):
|
|
369
|
+
universal_config = UniversalConfig()
|
|
370
|
+
config = BaseProjectConfig(universal_config)
|
|
371
|
+
assert universal_config.config_universal is config.config_universal
|
|
372
|
+
|
|
373
|
+
def test_repo_info(self):
|
|
374
|
+
env = {
|
|
375
|
+
"CUMULUSCI_AUTO_DETECT": "1",
|
|
376
|
+
"HEROKU_TEST_RUN_ID": "TEST1",
|
|
377
|
+
"HEROKU_TEST_RUN_BRANCH": "main",
|
|
378
|
+
"HEROKU_TEST_RUN_COMMIT_VERSION": "HEAD",
|
|
379
|
+
"CUMULUSCI_REPO_BRANCH": "feature/test",
|
|
380
|
+
"CUMULUSCI_REPO_COMMIT": "HEAD~1",
|
|
381
|
+
"CUMULUSCI_REPO_ROOT": ".",
|
|
382
|
+
"CUMULUSCI_REPO_URL": "https://github.com/SFDO-Tooling/CumulusCI-Test.git",
|
|
383
|
+
}
|
|
384
|
+
with mock.patch.dict(os.environ, env):
|
|
385
|
+
config = BaseProjectConfig(UniversalConfig())
|
|
386
|
+
result = config.repo_info
|
|
387
|
+
assert {
|
|
388
|
+
"ci": "heroku",
|
|
389
|
+
"name": "CumulusCI-Test",
|
|
390
|
+
"owner": "SFDO-Tooling",
|
|
391
|
+
"domain": "github.com",
|
|
392
|
+
"branch": "feature/test",
|
|
393
|
+
"commit": "HEAD~1",
|
|
394
|
+
"root": ".",
|
|
395
|
+
"url": "https://github.com/SFDO-Tooling/CumulusCI-Test.git",
|
|
396
|
+
} == result
|
|
397
|
+
|
|
398
|
+
def test_repo_info_missing_env(self):
|
|
399
|
+
env = {
|
|
400
|
+
"CUMULUSCI_AUTO_DETECT": "1",
|
|
401
|
+
"HEROKU_TEST_RUN_ID": "TEST1",
|
|
402
|
+
"HEROKU_TEST_RUN_BRANCH": "main",
|
|
403
|
+
"HEROKU_TEST_RUN_COMMIT_VERSION": "HEAD",
|
|
404
|
+
"CUMULUSCI_REPO_BRANCH": "feature/test",
|
|
405
|
+
"CUMULUSCI_REPO_COMMIT": "HEAD~1",
|
|
406
|
+
"CUMULUSCI_REPO_ROOT": ".",
|
|
407
|
+
}
|
|
408
|
+
with mock.patch.dict(os.environ, env):
|
|
409
|
+
with pytest.raises(ConfigError):
|
|
410
|
+
config = BaseProjectConfig(UniversalConfig())
|
|
411
|
+
config.repo_info
|
|
412
|
+
|
|
413
|
+
def test_repo_root_from_env(self):
|
|
414
|
+
config = BaseProjectConfig(UniversalConfig())
|
|
415
|
+
config._repo_info = {"root": "."}
|
|
416
|
+
assert config.repo_root == "."
|
|
417
|
+
|
|
418
|
+
def test_server_domain_from_repo_info(self):
|
|
419
|
+
config = BaseProjectConfig(UniversalConfig())
|
|
420
|
+
assert config.server_domain == "github.com"
|
|
421
|
+
|
|
422
|
+
def test_server_domain_no_repo_root(self):
|
|
423
|
+
config = BaseProjectConfig(UniversalConfig())
|
|
424
|
+
with temporary_dir():
|
|
425
|
+
assert config.server_domain is None
|
|
426
|
+
|
|
427
|
+
def test_repo_name_from_repo_info(self):
|
|
428
|
+
config = BaseProjectConfig(UniversalConfig())
|
|
429
|
+
config._repo_info = {"name": "CumulusCI"}
|
|
430
|
+
assert config.repo_name == "CumulusCI"
|
|
431
|
+
|
|
432
|
+
def test_repo_name_no_repo_root(self):
|
|
433
|
+
config = BaseProjectConfig(UniversalConfig())
|
|
434
|
+
with temporary_dir():
|
|
435
|
+
assert config.repo_name is None
|
|
436
|
+
|
|
437
|
+
def test_repo_name_from_git(self):
|
|
438
|
+
config = BaseProjectConfig(UniversalConfig())
|
|
439
|
+
assert config.repo_name == "CumulusCI"
|
|
440
|
+
|
|
441
|
+
def test_repo_url_from_repo_info(self):
|
|
442
|
+
config = BaseProjectConfig(UniversalConfig())
|
|
443
|
+
config._repo_info = {"url": "https://github.com/SFDO-Tooling/CumulusCI"}
|
|
444
|
+
assert config.repo_url == "https://github.com/SFDO-Tooling/CumulusCI"
|
|
445
|
+
|
|
446
|
+
def test_lookup_repo_branch(self):
|
|
447
|
+
config = BaseProjectConfig(UniversalConfig())
|
|
448
|
+
config._repo_info = {"branch": "foo-bar-baz"}
|
|
449
|
+
assert config.lookup("repo_branch") == "foo-bar-baz"
|
|
450
|
+
|
|
451
|
+
def test_repo_url_no_repo_root(self):
|
|
452
|
+
config = BaseProjectConfig(UniversalConfig())
|
|
453
|
+
with temporary_dir():
|
|
454
|
+
assert config.repo_url is None
|
|
455
|
+
|
|
456
|
+
@mock.patch("cumulusci.core.config.project_config.git_path")
|
|
457
|
+
def test_repo_url_from_git(self, git_path):
|
|
458
|
+
git_config_file = "git_config"
|
|
459
|
+
git_path.return_value = git_config_file
|
|
460
|
+
repo_url = "https://github.com/foo/bar.git"
|
|
461
|
+
with open(git_config_file, "w") as f:
|
|
462
|
+
f.writelines(['[remote "origin"]\n' f"\turl = {repo_url}"])
|
|
463
|
+
|
|
464
|
+
config = BaseProjectConfig(UniversalConfig())
|
|
465
|
+
assert repo_url == config.repo_url
|
|
466
|
+
|
|
467
|
+
os.remove(git_config_file)
|
|
468
|
+
|
|
469
|
+
def test_repo_owner_from_repo_info(self):
|
|
470
|
+
config = BaseProjectConfig(UniversalConfig())
|
|
471
|
+
config._repo_info = {"owner": "SFDO-Tooling"}
|
|
472
|
+
assert config.repo_owner == "SFDO-Tooling"
|
|
473
|
+
|
|
474
|
+
def test_repo_owner_no_repo_root(self):
|
|
475
|
+
config = BaseProjectConfig(UniversalConfig())
|
|
476
|
+
with temporary_dir():
|
|
477
|
+
assert config.repo_owner is None
|
|
478
|
+
|
|
479
|
+
def test_repo_branch_from_repo_info(self):
|
|
480
|
+
config = BaseProjectConfig(UniversalConfig())
|
|
481
|
+
config._repo_info = {"branch": "main"}
|
|
482
|
+
assert config.repo_branch == "main"
|
|
483
|
+
|
|
484
|
+
def test_repo_branch_no_repo_root(self):
|
|
485
|
+
config = BaseProjectConfig(UniversalConfig())
|
|
486
|
+
with temporary_dir():
|
|
487
|
+
assert config.repo_branch is None
|
|
488
|
+
|
|
489
|
+
def test_repo_commit_from_repo_info(self):
|
|
490
|
+
config = BaseProjectConfig(UniversalConfig())
|
|
491
|
+
config._repo_info = {"commit": "abcdef"}
|
|
492
|
+
assert config.repo_commit == "abcdef"
|
|
493
|
+
|
|
494
|
+
def test_repo_commit_no_repo_root(self):
|
|
495
|
+
config = BaseProjectConfig(UniversalConfig())
|
|
496
|
+
with temporary_dir():
|
|
497
|
+
assert config.repo_commit is None
|
|
498
|
+
|
|
499
|
+
def test_repo_commit_no_repo_branch(self):
|
|
500
|
+
config = BaseProjectConfig(UniversalConfig())
|
|
501
|
+
with temporary_dir() as d:
|
|
502
|
+
os.mkdir(os.path.join(d, ".git"))
|
|
503
|
+
with open(os.path.join(d, ".git", "HEAD"), "w") as f:
|
|
504
|
+
f.write("abcdef")
|
|
505
|
+
|
|
506
|
+
assert config.repo_commit == "abcdef"
|
|
507
|
+
|
|
508
|
+
def test_repo_commit_packed_refs(self):
|
|
509
|
+
config = BaseProjectConfig(UniversalConfig())
|
|
510
|
+
with temporary_dir() as d:
|
|
511
|
+
os.system("git init")
|
|
512
|
+
with open(os.path.join(d, ".git", "HEAD"), "w") as f:
|
|
513
|
+
f.write("ref: refs/heads/main\n")
|
|
514
|
+
with open(os.path.join(d, ".git", "packed-refs"), "w") as f:
|
|
515
|
+
f.write("# pack-refs with: peeled fully-peeled sorted\n")
|
|
516
|
+
f.write("#\n")
|
|
517
|
+
f.write(
|
|
518
|
+
"8ce67f4519190cd1ec9785105168e21b9599bc27 refs/remotes/origin/main\n"
|
|
519
|
+
)
|
|
520
|
+
|
|
521
|
+
assert config.repo_commit is not None
|
|
522
|
+
|
|
523
|
+
@mock.patch("cumulusci.vcs.bootstrap.get_repo_from_url")
|
|
524
|
+
def test_get_repo_from_url(self, mock_get_repo):
|
|
525
|
+
config = BaseProjectConfig(
|
|
526
|
+
UniversalConfig(),
|
|
527
|
+
{},
|
|
528
|
+
)
|
|
529
|
+
|
|
530
|
+
mock_repo = mock.Mock()
|
|
531
|
+
mock_get_repo.return_value = mock_repo
|
|
532
|
+
|
|
533
|
+
result = config.get_repo_from_url("https://github.com/Test/TestRepo")
|
|
534
|
+
|
|
535
|
+
# Assert the result is the same repo returned by the bootstrap function
|
|
536
|
+
assert result == mock_repo
|
|
537
|
+
|
|
538
|
+
# Assert the bootstrap function was called with the correct arguments
|
|
539
|
+
mock_get_repo.assert_called_once_with(
|
|
540
|
+
config, "https://github.com/Test/TestRepo"
|
|
541
|
+
)
|
|
542
|
+
|
|
543
|
+
# Assert that the logger was set on the returned repo
|
|
544
|
+
assert mock_repo.logger == config.logger
|
|
545
|
+
|
|
546
|
+
def test_get_latest_tag(self):
|
|
547
|
+
config = BaseProjectConfig(
|
|
548
|
+
UniversalConfig(),
|
|
549
|
+
{
|
|
550
|
+
"project": {
|
|
551
|
+
"git": {"prefix_beta": "beta/", "prefix_release": "release/"}
|
|
552
|
+
}
|
|
553
|
+
},
|
|
554
|
+
)
|
|
555
|
+
# Mock the repo service to return our DummyService
|
|
556
|
+
dummy_github = self._make_github()
|
|
557
|
+
dummy_service = DummyService(dummy_github, config)
|
|
558
|
+
dummy_service.logger = config.logger
|
|
559
|
+
config._repo_info = {"vcs_service": dummy_service}
|
|
560
|
+
|
|
561
|
+
result = config.get_latest_tag()
|
|
562
|
+
assert result == "release/1.1"
|
|
563
|
+
|
|
564
|
+
def test_get_package_data(self):
|
|
565
|
+
config = BaseProjectConfig(
|
|
566
|
+
UniversalConfig(),
|
|
567
|
+
{
|
|
568
|
+
"project": {
|
|
569
|
+
"package": {"namespace": "foo"},
|
|
570
|
+
}
|
|
571
|
+
},
|
|
572
|
+
)
|
|
573
|
+
|
|
574
|
+
assert BaseProjectConfig.get_package_data(config) == (
|
|
575
|
+
"Package",
|
|
576
|
+
"foo",
|
|
577
|
+
)
|
|
578
|
+
|
|
579
|
+
def test_get_latest_tag_matching_prefix(self):
|
|
580
|
+
config = BaseProjectConfig(
|
|
581
|
+
UniversalConfig(),
|
|
582
|
+
{"project": {"git": {"prefix_beta": "beta/", "prefix_release": "rel/"}}},
|
|
583
|
+
)
|
|
584
|
+
github = self._make_github()
|
|
585
|
+
github.repositories["CumulusCI"]._releases.append(
|
|
586
|
+
DummyRelease("rel/0.9", "0.9")
|
|
587
|
+
)
|
|
588
|
+
dummy_service = DummyService(github, config)
|
|
589
|
+
config._repo_info = {"vcs_service": dummy_service}
|
|
590
|
+
|
|
591
|
+
result = config.get_latest_tag()
|
|
592
|
+
assert result == "rel/0.9"
|
|
593
|
+
|
|
594
|
+
def test_get_latest_tag_beta(self):
|
|
595
|
+
config = BaseProjectConfig(
|
|
596
|
+
UniversalConfig(),
|
|
597
|
+
{
|
|
598
|
+
"project": {
|
|
599
|
+
"git": {"prefix_beta": "beta/", "prefix_release": "release/"}
|
|
600
|
+
}
|
|
601
|
+
},
|
|
602
|
+
)
|
|
603
|
+
dummy_github = self._make_github()
|
|
604
|
+
dummy_service = DummyService(dummy_github, config)
|
|
605
|
+
dummy_service.logger = config.logger
|
|
606
|
+
config._repo_info = {"vcs_service": dummy_service}
|
|
607
|
+
|
|
608
|
+
result = config.get_latest_tag(beta=True)
|
|
609
|
+
assert result == "beta/1.0-Beta_2"
|
|
610
|
+
|
|
611
|
+
def test_get_latest_tag__beta_not_found(self):
|
|
612
|
+
config = BaseProjectConfig(UniversalConfig())
|
|
613
|
+
github = self._make_github()
|
|
614
|
+
github.repositories["CumulusCI"]._releases = []
|
|
615
|
+
dummy_service = DummyService(github, config)
|
|
616
|
+
dummy_service.logger = config.logger
|
|
617
|
+
config._repo_info = {"vcs_service": dummy_service}
|
|
618
|
+
|
|
619
|
+
with pytest.raises(VcsException):
|
|
620
|
+
config.get_latest_tag(beta=True)
|
|
621
|
+
|
|
622
|
+
def test_get_latest_tag__repo_not_found(self):
|
|
623
|
+
config = BaseProjectConfig(UniversalConfig())
|
|
624
|
+
github = self._make_github()
|
|
625
|
+
github.repositories["CumulusCI"] = None
|
|
626
|
+
dummy_service = DummyService(github, config)
|
|
627
|
+
dummy_service.logger = config.logger
|
|
628
|
+
config._repo_info = {"vcs_service": dummy_service}
|
|
629
|
+
|
|
630
|
+
with pytest.raises(VcsException):
|
|
631
|
+
config.get_latest_tag()
|
|
632
|
+
|
|
633
|
+
def test_get_latest_tag__release_not_found(self):
|
|
634
|
+
config = BaseProjectConfig(UniversalConfig())
|
|
635
|
+
github = self._make_github()
|
|
636
|
+
github.repositories["CumulusCI"]._releases = []
|
|
637
|
+
dummy_service = DummyService(github, config)
|
|
638
|
+
dummy_service.logger = config.logger
|
|
639
|
+
config._repo_info = {"vcs_service": dummy_service}
|
|
640
|
+
|
|
641
|
+
with pytest.raises(VcsException):
|
|
642
|
+
config.get_latest_tag()
|
|
643
|
+
|
|
644
|
+
def test_get_latest_version(self):
|
|
645
|
+
config = BaseProjectConfig(
|
|
646
|
+
UniversalConfig(),
|
|
647
|
+
{
|
|
648
|
+
"project": {
|
|
649
|
+
"git": {"prefix_beta": "beta/", "prefix_release": "release/"}
|
|
650
|
+
}
|
|
651
|
+
},
|
|
652
|
+
)
|
|
653
|
+
dummy_github = self._make_github()
|
|
654
|
+
dummy_service = DummyService(dummy_github, config)
|
|
655
|
+
dummy_service.logger = config.logger
|
|
656
|
+
config._repo_info = {"vcs_service": dummy_service}
|
|
657
|
+
|
|
658
|
+
result = config.get_latest_version()
|
|
659
|
+
assert result == "1.1"
|
|
660
|
+
|
|
661
|
+
def test_get_latest_version_beta(self):
|
|
662
|
+
config = BaseProjectConfig(
|
|
663
|
+
UniversalConfig(),
|
|
664
|
+
{
|
|
665
|
+
"project": {
|
|
666
|
+
"git": {"prefix_beta": "beta/", "prefix_release": "release/"}
|
|
667
|
+
}
|
|
668
|
+
},
|
|
669
|
+
)
|
|
670
|
+
dummy_github = self._make_github()
|
|
671
|
+
dummy_service = DummyService(dummy_github, config)
|
|
672
|
+
dummy_service.logger = config.logger
|
|
673
|
+
config._repo_info = {"vcs_service": dummy_service}
|
|
674
|
+
|
|
675
|
+
result = config.get_latest_version(beta=True)
|
|
676
|
+
assert result == "1.0 (Beta 2)"
|
|
677
|
+
|
|
678
|
+
def test_get_previous_version(self):
|
|
679
|
+
config = BaseProjectConfig(
|
|
680
|
+
UniversalConfig(),
|
|
681
|
+
{
|
|
682
|
+
"project": {
|
|
683
|
+
"git": {"prefix_beta": "beta/", "prefix_release": "release/"}
|
|
684
|
+
}
|
|
685
|
+
},
|
|
686
|
+
)
|
|
687
|
+
dummy_github = self._make_github()
|
|
688
|
+
dummy_service = DummyService(dummy_github, config)
|
|
689
|
+
dummy_service.logger = config.logger
|
|
690
|
+
config._repo_info = {"vcs_service": dummy_service}
|
|
691
|
+
|
|
692
|
+
result = config.get_previous_version()
|
|
693
|
+
assert result == "1.0"
|
|
694
|
+
|
|
695
|
+
def test_config_project_path_no_repo_root(self):
|
|
696
|
+
config = BaseProjectConfig(UniversalConfig())
|
|
697
|
+
with temporary_dir():
|
|
698
|
+
assert config.config_project_path is None
|
|
699
|
+
|
|
700
|
+
def test_get_tag_for_version(self):
|
|
701
|
+
config = BaseProjectConfig(
|
|
702
|
+
UniversalConfig(), {"project": {"git": {"prefix_release": "release/"}}}
|
|
703
|
+
)
|
|
704
|
+
assert config.get_tag_for_version("beta/", "1.0") == "beta/1.0"
|
|
705
|
+
|
|
706
|
+
def test_get_tag_for_version__1gp_beta(self):
|
|
707
|
+
config = BaseProjectConfig(
|
|
708
|
+
UniversalConfig(), {"project": {"git": {"prefix_beta": "beta/"}}}
|
|
709
|
+
)
|
|
710
|
+
assert config.get_tag_for_version("beta/", "1.0 (Beta 1)") == "beta/1.0-Beta_1"
|
|
711
|
+
|
|
712
|
+
def test_get_tag_for_version__with_tag_prefix_option(self):
|
|
713
|
+
config = BaseProjectConfig(UniversalConfig(), {})
|
|
714
|
+
assert config.get_tag_for_version("custom/", "1.0") == "custom/1.0"
|
|
715
|
+
|
|
716
|
+
def test_get_version_for_tag(self):
|
|
717
|
+
config = BaseProjectConfig(
|
|
718
|
+
UniversalConfig(),
|
|
719
|
+
{
|
|
720
|
+
"project": {
|
|
721
|
+
"git": {"prefix_beta": "beta/", "prefix_release": "release/"}
|
|
722
|
+
}
|
|
723
|
+
},
|
|
724
|
+
)
|
|
725
|
+
assert config.get_version_for_tag("release/1.0") == "1.0"
|
|
726
|
+
|
|
727
|
+
def test_get_version_for_tag_invalid_beta(self):
|
|
728
|
+
config = BaseProjectConfig(
|
|
729
|
+
UniversalConfig(),
|
|
730
|
+
{
|
|
731
|
+
"project": {
|
|
732
|
+
"git": {"prefix_beta": "beta/", "prefix_release": "release/"}
|
|
733
|
+
}
|
|
734
|
+
},
|
|
735
|
+
)
|
|
736
|
+
assert config.get_version_for_tag("beta/invalid-format") is None
|
|
737
|
+
|
|
738
|
+
def test_check_keychain(self):
|
|
739
|
+
config = BaseProjectConfig(UniversalConfig())
|
|
740
|
+
with pytest.raises(KeychainNotFound):
|
|
741
|
+
config._check_keychain()
|
|
742
|
+
|
|
743
|
+
def test_get_task__included_source(self):
|
|
744
|
+
universal_config = UniversalConfig()
|
|
745
|
+
with temporary_dir() as d:
|
|
746
|
+
touch("cumulusci.yml")
|
|
747
|
+
project_config = BaseProjectConfig(
|
|
748
|
+
universal_config,
|
|
749
|
+
{"sources": {"test": {"path": d}}},
|
|
750
|
+
repo_info={"root": Path(__file__).parent.absolute()},
|
|
751
|
+
)
|
|
752
|
+
task_config = project_config.get_task("test:log")
|
|
753
|
+
assert task_config.project_config is not project_config
|
|
754
|
+
assert isinstance(task_config.project_config.source, LocalFolderSource)
|
|
755
|
+
|
|
756
|
+
def test_get_flow__included_source(self):
|
|
757
|
+
universal_config = UniversalConfig()
|
|
758
|
+
with temporary_dir() as d:
|
|
759
|
+
touch("cumulusci.yml")
|
|
760
|
+
project_config = BaseProjectConfig(
|
|
761
|
+
universal_config,
|
|
762
|
+
{"sources": {"test": {"path": d}}},
|
|
763
|
+
repo_info={"root": Path(__file__).parent.absolute()},
|
|
764
|
+
)
|
|
765
|
+
flow_config = project_config.get_flow("test:dev_org")
|
|
766
|
+
assert flow_config.project_config is not project_config
|
|
767
|
+
assert isinstance(flow_config.project_config.source, LocalFolderSource)
|
|
768
|
+
|
|
769
|
+
def test_get_namespace__not_found(self):
|
|
770
|
+
universal_config = UniversalConfig()
|
|
771
|
+
project_config = BaseProjectConfig(universal_config)
|
|
772
|
+
with pytest.raises(NamespaceNotFoundError):
|
|
773
|
+
project_config.get_namespace("test")
|
|
774
|
+
|
|
775
|
+
def test_include_source__bad_spec(self):
|
|
776
|
+
universal_config = UniversalConfig()
|
|
777
|
+
project_config = BaseProjectConfig(
|
|
778
|
+
universal_config, {"sources": {"test": {"foo": "some_nonsense"}}}
|
|
779
|
+
)
|
|
780
|
+
with pytest.raises(ValueError, match="Invalid source spec"):
|
|
781
|
+
project_config.include_source(project_config.get_namespace("test"))
|
|
782
|
+
|
|
783
|
+
def test_include_source__cached(self):
|
|
784
|
+
universal_config = UniversalConfig()
|
|
785
|
+
project_config = BaseProjectConfig(
|
|
786
|
+
universal_config,
|
|
787
|
+
repo_info={"root": os.getcwd()},
|
|
788
|
+
)
|
|
789
|
+
with temporary_dir() as d:
|
|
790
|
+
touch("cumulusci.yml")
|
|
791
|
+
other1 = project_config.include_source(LocalFolderSourceModel(path=d))
|
|
792
|
+
other2 = project_config.include_source(LocalFolderSourceModel(path=d))
|
|
793
|
+
assert other1 is other2
|
|
794
|
+
|
|
795
|
+
@mock.patch("cumulusci.core.config.project_config.VCSSource")
|
|
796
|
+
def test_include_source__github(self, source):
|
|
797
|
+
source.create = expected_result = mock.Mock()
|
|
798
|
+
expected_result.fetch.return_value.repo_root = "/whatever"
|
|
799
|
+
source.create.return_value = expected_result
|
|
800
|
+
universal_config = UniversalConfig()
|
|
801
|
+
project_config = BaseProjectConfig(universal_config)
|
|
802
|
+
other_config = project_config.include_source(
|
|
803
|
+
GitHubSourceModel(github="foo/bar")
|
|
804
|
+
)
|
|
805
|
+
assert other_config.source is expected_result
|
|
806
|
+
|
|
807
|
+
def test_relpath(self):
|
|
808
|
+
universal_config = UniversalConfig()
|
|
809
|
+
project_config = BaseProjectConfig(universal_config)
|
|
810
|
+
assert project_config.relpath(os.path.abspath(".")) == "."
|
|
811
|
+
|
|
812
|
+
def test_validate_package_api_version_valid(self):
|
|
813
|
+
"""We stringify the float 46.0 as this is what will occur when
|
|
814
|
+
it is formatted into API URLS. This also negates the need to
|
|
815
|
+
test an explicit string (i.e. if this passes we know that '46.0'
|
|
816
|
+
will also pass)."""
|
|
817
|
+
project_config = BaseProjectConfig(UniversalConfig())
|
|
818
|
+
project_config.config["project"]["package"]["api_version"] = str(46.0)
|
|
819
|
+
project_config._validate_package_api_format()
|
|
820
|
+
|
|
821
|
+
def test_validate_package_api_version_invalid(self):
|
|
822
|
+
project_config = BaseProjectConfig(UniversalConfig())
|
|
823
|
+
project_config.config["project"]["package"]["api_version"] = str([1, 2, 3])
|
|
824
|
+
with pytest.raises(ConfigError):
|
|
825
|
+
project_config._validate_package_api_format()
|
|
826
|
+
|
|
827
|
+
project_config.config["project"]["package"]["api_version"] = "9"
|
|
828
|
+
with pytest.raises(ConfigError):
|
|
829
|
+
project_config._validate_package_api_format()
|
|
830
|
+
|
|
831
|
+
project_config.config["project"]["package"]["api_version"] = "9.0"
|
|
832
|
+
with pytest.raises(ConfigError):
|
|
833
|
+
project_config._validate_package_api_format()
|
|
834
|
+
|
|
835
|
+
project_config.config["project"]["package"]["api_version"] = "45"
|
|
836
|
+
with pytest.raises(ConfigError):
|
|
837
|
+
project_config._validate_package_api_format()
|
|
838
|
+
|
|
839
|
+
project_config.config["project"]["package"]["api_version"] = "45."
|
|
840
|
+
with pytest.raises(ConfigError):
|
|
841
|
+
project_config._validate_package_api_format()
|
|
842
|
+
|
|
843
|
+
project_config.config["project"]["package"]["api_version"] = "45.00"
|
|
844
|
+
with pytest.raises(ConfigError):
|
|
845
|
+
project_config._validate_package_api_format()
|
|
846
|
+
|
|
847
|
+
@mock.patch("cumulusci.core.config.project_config.git_path")
|
|
848
|
+
def test_git_config_remote_origin_line(self, git_path):
|
|
849
|
+
git_config_file = "test_git_config_file"
|
|
850
|
+
git_path.return_value = git_config_file
|
|
851
|
+
|
|
852
|
+
with open(git_config_file, "w") as f:
|
|
853
|
+
f.writelines(
|
|
854
|
+
[
|
|
855
|
+
'[branch "feature-1"]\n',
|
|
856
|
+
"\tremote = origin\n",
|
|
857
|
+
"\tmerge = refs/heads/feature-1\n",
|
|
858
|
+
'[remote "origin"]\n',
|
|
859
|
+
"\tfetch = +refs/heads/*:refs/remotes/origin/*\n",
|
|
860
|
+
]
|
|
861
|
+
)
|
|
862
|
+
|
|
863
|
+
project_config = BaseProjectConfig(UniversalConfig())
|
|
864
|
+
actual_line = project_config.git_config_remote_origin_url()
|
|
865
|
+
assert actual_line is None # no url under [remote "origin"]
|
|
866
|
+
|
|
867
|
+
with open(git_config_file, "a") as f:
|
|
868
|
+
f.write("\turl = some.url.here\n")
|
|
869
|
+
|
|
870
|
+
actual_line = project_config.git_config_remote_origin_url()
|
|
871
|
+
assert actual_line == "some.url.here"
|
|
872
|
+
|
|
873
|
+
os.remove(git_config_file)
|
|
874
|
+
actual_line = project_config.git_config_remote_origin_url()
|
|
875
|
+
assert actual_line is None # no config file present
|
|
876
|
+
|
|
877
|
+
def test_default_package_path(self):
|
|
878
|
+
config = BaseProjectConfig(UniversalConfig())
|
|
879
|
+
assert str(config.default_package_path.relative_to(config.repo_root)) == "src"
|
|
880
|
+
|
|
881
|
+
def test_default_package_path__sfdx(self):
|
|
882
|
+
with temporary_dir() as path:
|
|
883
|
+
pathlib.Path(path, ".git").mkdir()
|
|
884
|
+
with pathlib.Path(path, "cumulusci.yml").open("w") as f:
|
|
885
|
+
yaml.dump({"project": {"source_format": "sfdx"}}, f)
|
|
886
|
+
with pathlib.Path(path, "sfdx-project.json").open("w") as f:
|
|
887
|
+
json.dump(
|
|
888
|
+
{"packageDirectories": [{"path": "force-app", "default": True}]}, f
|
|
889
|
+
)
|
|
890
|
+
config = BaseProjectConfig(UniversalConfig())
|
|
891
|
+
assert (
|
|
892
|
+
str(config.default_package_path.relative_to(config.repo_root))
|
|
893
|
+
== "force-app"
|
|
894
|
+
)
|
|
895
|
+
|
|
896
|
+
|
|
897
|
+
class TestBaseTaskFlowConfig:
|
|
898
|
+
def setup_method(self):
|
|
899
|
+
self.task_flow_config = BaseTaskFlowConfig(
|
|
900
|
+
{
|
|
901
|
+
"tasks": {
|
|
902
|
+
"deploy": {"description": "Deploy Task", "class_path": "my.path"},
|
|
903
|
+
"no_path": {"description": "Only a Description"},
|
|
904
|
+
"manage": {},
|
|
905
|
+
"control": {},
|
|
906
|
+
},
|
|
907
|
+
"flows": {
|
|
908
|
+
"coffee": {"description": "Coffee Flow"},
|
|
909
|
+
"juice": {"description": "Juice Flow"},
|
|
910
|
+
},
|
|
911
|
+
}
|
|
912
|
+
)
|
|
913
|
+
|
|
914
|
+
def test_list_tasks(self):
|
|
915
|
+
tasks = self.task_flow_config.list_tasks()
|
|
916
|
+
assert len(tasks) == 4
|
|
917
|
+
deploy = [task for task in tasks if task["name"] == "deploy"][0]
|
|
918
|
+
assert deploy["description"] == "Deploy Task"
|
|
919
|
+
|
|
920
|
+
def test_get_task(self):
|
|
921
|
+
task = self.task_flow_config.get_task("deploy")
|
|
922
|
+
assert isinstance(task, BaseConfig)
|
|
923
|
+
assert ("description", "Deploy Task") in task.config.items()
|
|
924
|
+
|
|
925
|
+
def test_get_task__no_class_path(self):
|
|
926
|
+
with pytest.raises(
|
|
927
|
+
CumulusCIException, match="Task has no class_path defined: no_path"
|
|
928
|
+
):
|
|
929
|
+
self.task_flow_config.get_task("no_path")
|
|
930
|
+
|
|
931
|
+
def test_get_task__no_config_found(self):
|
|
932
|
+
with pytest.raises(
|
|
933
|
+
CumulusCIException, match="No configuration found for task: manage"
|
|
934
|
+
):
|
|
935
|
+
self.task_flow_config.get_task("manage")
|
|
936
|
+
|
|
937
|
+
def test_no_task__no_suggestion(self):
|
|
938
|
+
with pytest.raises(
|
|
939
|
+
TaskNotFoundError, match="Task not found: robotic_superstar"
|
|
940
|
+
):
|
|
941
|
+
self.task_flow_config.get_task("robotic_superstar")
|
|
942
|
+
|
|
943
|
+
def test_no_task__with_suggestion(self):
|
|
944
|
+
with pytest.raises(
|
|
945
|
+
TaskNotFoundError, match='Task not found: mange. Did you mean "manage"?'
|
|
946
|
+
):
|
|
947
|
+
self.task_flow_config.get_task("mange")
|
|
948
|
+
|
|
949
|
+
def test_get_flow(self):
|
|
950
|
+
flow = self.task_flow_config.get_flow("coffee")
|
|
951
|
+
assert isinstance(flow, BaseConfig)
|
|
952
|
+
assert ("description", "Coffee Flow") in flow.config.items()
|
|
953
|
+
|
|
954
|
+
def test_no_flow(self):
|
|
955
|
+
with pytest.raises(FlowNotFoundError):
|
|
956
|
+
self.task_flow_config.get_flow("water")
|
|
957
|
+
|
|
958
|
+
def test_list_flows(self):
|
|
959
|
+
flows = self.task_flow_config.list_flows()
|
|
960
|
+
assert len(flows) == 2
|
|
961
|
+
coffee = [flow for flow in flows if flow["name"] == "coffee"][0]
|
|
962
|
+
assert coffee["description"] == "Coffee Flow"
|
|
963
|
+
|
|
964
|
+
def test_suggested_name(self):
|
|
965
|
+
flows = self.task_flow_config.flows
|
|
966
|
+
assert len(flows) == 2
|
|
967
|
+
error_msg = self.task_flow_config.get_suggested_name("bofee", flows)
|
|
968
|
+
assert "coffee" in error_msg
|
|
969
|
+
|
|
970
|
+
|
|
971
|
+
class TestOrgConfig:
|
|
972
|
+
@mock.patch("cumulusci.core.config.org_config.OrgConfig.OAuth2Client")
|
|
973
|
+
def test_refresh_oauth_token(self, OAuth2Client):
|
|
974
|
+
config = OrgConfig(
|
|
975
|
+
{
|
|
976
|
+
"refresh_token": mock.sentinel.refresh_token,
|
|
977
|
+
"instance_url": "http://instance_url_111.com",
|
|
978
|
+
},
|
|
979
|
+
"test",
|
|
980
|
+
)
|
|
981
|
+
project_config = BaseProjectConfig(UniversalConfig())
|
|
982
|
+
keychain = BaseProjectKeychain(project_config, None)
|
|
983
|
+
config._load_userinfo = mock.Mock()
|
|
984
|
+
config._load_orginfo = mock.Mock()
|
|
985
|
+
|
|
986
|
+
refresh_token = mock.Mock(return_value={"access_token": "asdf"})
|
|
987
|
+
OAuth2Client.return_value = mock.Mock(refresh_token=refresh_token)
|
|
988
|
+
|
|
989
|
+
config.refresh_oauth_token(keychain)
|
|
990
|
+
|
|
991
|
+
client_config = OAuth2Client.call_args[0][0]
|
|
992
|
+
assert client_config.client_id == DEFAULT_CONNECTED_APP.client_id
|
|
993
|
+
refresh_token.assert_called_once_with(mock.sentinel.refresh_token)
|
|
994
|
+
|
|
995
|
+
@mock.patch("cumulusci.core.config.org_config.OrgConfig.OAuth2Client")
|
|
996
|
+
def test_refresh_oauth_token__other_connected_app(self, OAuth2Client):
|
|
997
|
+
config = OrgConfig(
|
|
998
|
+
{
|
|
999
|
+
"connected_app": "other",
|
|
1000
|
+
"refresh_token": mock.sentinel.refresh_token,
|
|
1001
|
+
"instance_url": "http://instance_url_111.com",
|
|
1002
|
+
},
|
|
1003
|
+
"test",
|
|
1004
|
+
)
|
|
1005
|
+
project_config = BaseProjectConfig(UniversalConfig())
|
|
1006
|
+
keychain = BaseProjectKeychain(project_config, None)
|
|
1007
|
+
keychain.set_service(
|
|
1008
|
+
"connected_app",
|
|
1009
|
+
"other",
|
|
1010
|
+
ServiceConfig(
|
|
1011
|
+
{
|
|
1012
|
+
"login_url": "https://other",
|
|
1013
|
+
"callback_url": "http://localhost:8080/callback",
|
|
1014
|
+
"client_id": "OTHER_ID",
|
|
1015
|
+
"client_secret": "OTHER_SECRET",
|
|
1016
|
+
}
|
|
1017
|
+
),
|
|
1018
|
+
)
|
|
1019
|
+
config._load_userinfo = mock.Mock()
|
|
1020
|
+
config._load_orginfo = mock.Mock()
|
|
1021
|
+
|
|
1022
|
+
refresh_token = mock.Mock(return_value={"access_token": "asdf"})
|
|
1023
|
+
OAuth2Client.return_value = mock.Mock(refresh_token=refresh_token)
|
|
1024
|
+
|
|
1025
|
+
config.refresh_oauth_token(keychain)
|
|
1026
|
+
|
|
1027
|
+
client_config = OAuth2Client.call_args[0][0]
|
|
1028
|
+
assert client_config.client_id == "OTHER_ID"
|
|
1029
|
+
refresh_token.assert_called_once_with(mock.sentinel.refresh_token)
|
|
1030
|
+
|
|
1031
|
+
@responses.activate
|
|
1032
|
+
def test_load_user_info__bad_json(self):
|
|
1033
|
+
config = OrgConfig(
|
|
1034
|
+
{
|
|
1035
|
+
"refresh_token": mock.sentinel.refresh_token,
|
|
1036
|
+
"instance_url": "http://instance_url_111.com",
|
|
1037
|
+
},
|
|
1038
|
+
"test",
|
|
1039
|
+
)
|
|
1040
|
+
keychain = mock.Mock()
|
|
1041
|
+
keychain.get_service.return_value = mock.Mock(
|
|
1042
|
+
client_id="asdf", client_secret="asdf"
|
|
1043
|
+
)
|
|
1044
|
+
|
|
1045
|
+
responses.add(
|
|
1046
|
+
responses.POST, "http://instance_url_111.com/services/oauth2/token"
|
|
1047
|
+
)
|
|
1048
|
+
with pytest.raises(CumulusCIException) as e:
|
|
1049
|
+
config.refresh_oauth_token(keychain)
|
|
1050
|
+
assert "Cannot decode" in str(e.value)
|
|
1051
|
+
|
|
1052
|
+
def test_refresh_oauth_token_no_connected_app(self):
|
|
1053
|
+
config = OrgConfig({}, "test")
|
|
1054
|
+
with pytest.raises(AttributeError):
|
|
1055
|
+
config.refresh_oauth_token(None)
|
|
1056
|
+
|
|
1057
|
+
def test_refresh_oauth_token__bad_connected_app(self):
|
|
1058
|
+
org_config = OrgConfig({"connected_app": "bogus"}, "test")
|
|
1059
|
+
project_config = BaseProjectConfig(UniversalConfig())
|
|
1060
|
+
keychain = BaseProjectKeychain(project_config, None)
|
|
1061
|
+
with pytest.raises(ServiceNotConfigured, match="no longer configured"):
|
|
1062
|
+
org_config.refresh_oauth_token(keychain)
|
|
1063
|
+
|
|
1064
|
+
@mock.patch("jwt.encode", mock.Mock(return_value="JWT"))
|
|
1065
|
+
@responses.activate
|
|
1066
|
+
def test_refresh_oauth_token__jwt(self):
|
|
1067
|
+
responses.add(
|
|
1068
|
+
"POST",
|
|
1069
|
+
"https://login.salesforce.com/services/oauth2/token",
|
|
1070
|
+
json={
|
|
1071
|
+
"access_token": "TOKEN",
|
|
1072
|
+
"instance_url": "https://na00.salesforce.com",
|
|
1073
|
+
},
|
|
1074
|
+
)
|
|
1075
|
+
with mock.patch.dict(
|
|
1076
|
+
os.environ,
|
|
1077
|
+
{"SFDX_CLIENT_ID": "some client id", "SFDX_HUB_KEY": "some private key"},
|
|
1078
|
+
):
|
|
1079
|
+
config = OrgConfig({}, "test")
|
|
1080
|
+
config._load_userinfo = mock.Mock()
|
|
1081
|
+
config._load_orginfo = mock.Mock()
|
|
1082
|
+
config.refresh_oauth_token(None)
|
|
1083
|
+
assert config.access_token == "TOKEN"
|
|
1084
|
+
|
|
1085
|
+
@mock.patch("jwt.encode", mock.Mock(return_value="JWT"))
|
|
1086
|
+
@responses.activate
|
|
1087
|
+
def test_refresh_oauth_token__jwt_sandbox(self):
|
|
1088
|
+
responses.add(
|
|
1089
|
+
"POST",
|
|
1090
|
+
"https://cs00.salesforce.com/services/oauth2/token",
|
|
1091
|
+
json={
|
|
1092
|
+
"access_token": "TOKEN",
|
|
1093
|
+
"instance_url": "https://cs00.salesforce.com",
|
|
1094
|
+
},
|
|
1095
|
+
)
|
|
1096
|
+
with mock.patch.dict(
|
|
1097
|
+
os.environ,
|
|
1098
|
+
{"SFDX_CLIENT_ID": "some client id", "SFDX_HUB_KEY": "some private key"},
|
|
1099
|
+
):
|
|
1100
|
+
config = OrgConfig(
|
|
1101
|
+
{
|
|
1102
|
+
"instance_url": "https://cs00.salesforce.com",
|
|
1103
|
+
},
|
|
1104
|
+
"test",
|
|
1105
|
+
)
|
|
1106
|
+
config._load_userinfo = mock.Mock()
|
|
1107
|
+
config._load_orginfo = mock.Mock()
|
|
1108
|
+
config.refresh_oauth_token(None)
|
|
1109
|
+
assert config.access_token == "TOKEN"
|
|
1110
|
+
|
|
1111
|
+
@mock.patch("jwt.encode", mock.Mock(return_value="JWT"))
|
|
1112
|
+
@responses.activate
|
|
1113
|
+
def test_refresh_oauth_token__jwt_sandbox_instanceless_url(self):
|
|
1114
|
+
responses.add(
|
|
1115
|
+
"POST",
|
|
1116
|
+
"https://nonobvious--sandbox.my.salesforce.com/services/oauth2/token",
|
|
1117
|
+
json={
|
|
1118
|
+
"access_token": "TOKEN",
|
|
1119
|
+
"instance_url": "https://nonobvious--sandbox.my.salesforce.com",
|
|
1120
|
+
},
|
|
1121
|
+
)
|
|
1122
|
+
with mock.patch.dict(
|
|
1123
|
+
os.environ,
|
|
1124
|
+
{"SFDX_CLIENT_ID": "some client id", "SFDX_HUB_KEY": "some private key"},
|
|
1125
|
+
):
|
|
1126
|
+
config = OrgConfig(
|
|
1127
|
+
{
|
|
1128
|
+
"instance_url": "https://nonobvious--sandbox.my.salesforce.com",
|
|
1129
|
+
"id": "https://test.salesforce.com/asdf",
|
|
1130
|
+
},
|
|
1131
|
+
"test",
|
|
1132
|
+
)
|
|
1133
|
+
config._load_userinfo = mock.Mock()
|
|
1134
|
+
config._load_orginfo = mock.Mock()
|
|
1135
|
+
config.refresh_oauth_token(None)
|
|
1136
|
+
assert config.access_token == "TOKEN"
|
|
1137
|
+
|
|
1138
|
+
def test_lightning_base_url__instance(self):
|
|
1139
|
+
config = OrgConfig({"instance_url": "https://na01.salesforce.com"}, "test")
|
|
1140
|
+
assert config.lightning_base_url == "https://na01.lightning.force.com"
|
|
1141
|
+
|
|
1142
|
+
def test_lightning_base_url__scratch_org(self):
|
|
1143
|
+
config = OrgConfig(
|
|
1144
|
+
{"instance_url": "https://foo.cs42.my.salesforce.com"}, "test"
|
|
1145
|
+
)
|
|
1146
|
+
assert config.lightning_base_url == "https://foo.lightning.force.com"
|
|
1147
|
+
|
|
1148
|
+
def test_lightning_base_url__mydomain(self):
|
|
1149
|
+
config = OrgConfig({"instance_url": "https://foo.my.salesforce.com"}, "test")
|
|
1150
|
+
assert config.lightning_base_url == "https://foo.lightning.force.com"
|
|
1151
|
+
|
|
1152
|
+
@responses.activate
|
|
1153
|
+
def test_get_salesforce_version(self):
|
|
1154
|
+
responses.add(
|
|
1155
|
+
"GET",
|
|
1156
|
+
"https://na01.salesforce.com/services/data",
|
|
1157
|
+
json=[{"version": CURRENT_SF_API_VERSION}],
|
|
1158
|
+
)
|
|
1159
|
+
config = OrgConfig({"instance_url": "https://na01.salesforce.com"}, "test")
|
|
1160
|
+
config.access_token = "TOKEN"
|
|
1161
|
+
assert config.latest_api_version == CURRENT_SF_API_VERSION
|
|
1162
|
+
|
|
1163
|
+
@responses.activate
|
|
1164
|
+
def test_get_salesforce_version_bad_json(self):
|
|
1165
|
+
responses.add("GET", "https://na01.salesforce.com/services/data", "NOTJSON!")
|
|
1166
|
+
config = OrgConfig({"instance_url": "https://na01.salesforce.com"}, "test")
|
|
1167
|
+
config.access_token = "TOKEN"
|
|
1168
|
+
with pytest.raises(CumulusCIException) as e:
|
|
1169
|
+
assert config.latest_api_version == "42.0"
|
|
1170
|
+
assert "NOTJSON" in str(e.value)
|
|
1171
|
+
|
|
1172
|
+
@responses.activate
|
|
1173
|
+
def test_get_salesforce_version_weird_json(self):
|
|
1174
|
+
responses.add(
|
|
1175
|
+
"GET", "https://na01.salesforce.com/services/data", json=["NOTADICT"]
|
|
1176
|
+
)
|
|
1177
|
+
config = OrgConfig({"instance_url": "https://na01.salesforce.com"}, "test")
|
|
1178
|
+
config.access_token = "TOKEN"
|
|
1179
|
+
with pytest.raises(CumulusCIException) as e:
|
|
1180
|
+
assert config.latest_api_version == "42.0"
|
|
1181
|
+
assert "NOTADICT" in str(e.value)
|
|
1182
|
+
|
|
1183
|
+
def test_start_url(self):
|
|
1184
|
+
config = OrgConfig(
|
|
1185
|
+
{"instance_url": "https://na01.salesforce.com", "access_token": "TOKEN"},
|
|
1186
|
+
"test",
|
|
1187
|
+
)
|
|
1188
|
+
assert (
|
|
1189
|
+
"https://na01.salesforce.com/secur/frontdoor.jsp?sid=TOKEN"
|
|
1190
|
+
== config.start_url
|
|
1191
|
+
)
|
|
1192
|
+
|
|
1193
|
+
def test_user_id(self):
|
|
1194
|
+
config = OrgConfig({"id": "org/user"}, "test")
|
|
1195
|
+
assert config.user_id == "user"
|
|
1196
|
+
|
|
1197
|
+
def test_can_delete(self):
|
|
1198
|
+
config = OrgConfig({}, "test")
|
|
1199
|
+
assert not config.can_delete()
|
|
1200
|
+
|
|
1201
|
+
@responses.activate
|
|
1202
|
+
def test_load_orginfo(self):
|
|
1203
|
+
config = OrgConfig(
|
|
1204
|
+
{
|
|
1205
|
+
"instance_url": "https://example.com",
|
|
1206
|
+
"access_token": "TOKEN",
|
|
1207
|
+
"id": "OODxxxxxxxxxxxx/user",
|
|
1208
|
+
},
|
|
1209
|
+
"test",
|
|
1210
|
+
)
|
|
1211
|
+
responses.add(
|
|
1212
|
+
"GET",
|
|
1213
|
+
"https://example.com/services/data",
|
|
1214
|
+
json=[{"version": CURRENT_SF_API_VERSION}],
|
|
1215
|
+
)
|
|
1216
|
+
|
|
1217
|
+
responses.add(
|
|
1218
|
+
"GET",
|
|
1219
|
+
f"https://example.com/services/data/v{CURRENT_SF_API_VERSION}/sobjects/Organization/OODxxxxxxxxxxxx",
|
|
1220
|
+
json={
|
|
1221
|
+
"OrganizationType": "Enterprise Edition",
|
|
1222
|
+
"IsSandbox": False,
|
|
1223
|
+
"InstanceName": "cs420",
|
|
1224
|
+
"NamespacePrefix": "ns",
|
|
1225
|
+
},
|
|
1226
|
+
)
|
|
1227
|
+
|
|
1228
|
+
config._load_orginfo()
|
|
1229
|
+
|
|
1230
|
+
assert config.org_type == "Enterprise Edition"
|
|
1231
|
+
assert config.is_sandbox is False
|
|
1232
|
+
assert config.organization_sobject is not None
|
|
1233
|
+
assert config.namespace == "ns"
|
|
1234
|
+
|
|
1235
|
+
@responses.activate
|
|
1236
|
+
def test_get_community_info__cached(self):
|
|
1237
|
+
"""Verify that get_community_info returns data from the cache"""
|
|
1238
|
+
config = OrgConfig({}, "test")
|
|
1239
|
+
config._community_info_cache = {"Kōkua": {"name": "Kōkua"}}
|
|
1240
|
+
info = config.get_community_info("Kōkua")
|
|
1241
|
+
assert info["name"] == "Kōkua"
|
|
1242
|
+
|
|
1243
|
+
@responses.activate
|
|
1244
|
+
def test_get_community_info__fetch_if_not_in_cache(self):
|
|
1245
|
+
"""Verify that the internal cache is automatically refreshed
|
|
1246
|
+
|
|
1247
|
+
The cache should be refreshed automatically if the requested community
|
|
1248
|
+
is not in the cache.
|
|
1249
|
+
"""
|
|
1250
|
+
responses.add(
|
|
1251
|
+
"GET",
|
|
1252
|
+
"https://test/services/data",
|
|
1253
|
+
json=[{"version": CURRENT_SF_API_VERSION}],
|
|
1254
|
+
)
|
|
1255
|
+
|
|
1256
|
+
responses.add(
|
|
1257
|
+
"GET",
|
|
1258
|
+
f"https://test/services/data/v{CURRENT_SF_API_VERSION}/connect/communities",
|
|
1259
|
+
json={"communities": [{"name": "Kōkua"}]},
|
|
1260
|
+
)
|
|
1261
|
+
|
|
1262
|
+
config = OrgConfig(
|
|
1263
|
+
{"instance_url": "https://test", "access_token": "TOKEN"}, "test"
|
|
1264
|
+
)
|
|
1265
|
+
config._community_info_cache = {}
|
|
1266
|
+
info = config.get_community_info("Kōkua")
|
|
1267
|
+
assert info["name"] == "Kōkua"
|
|
1268
|
+
|
|
1269
|
+
@mock.patch("cumulusci.core.config.org_config.OrgConfig._fetch_community_info")
|
|
1270
|
+
def test_community_info_force_refresh(self, mock_fetch):
|
|
1271
|
+
"""Verify that the force_refresh parameter has an effect"""
|
|
1272
|
+
mock_fetch.return_value = {"Kōkua": {"name": "Kōkua"}}
|
|
1273
|
+
config = OrgConfig({}, "test")
|
|
1274
|
+
|
|
1275
|
+
# With the cache seeded with the target community, first
|
|
1276
|
+
# verify that the cache isn't refreshed automatically
|
|
1277
|
+
config._community_info_cache = {"Kōkua": {"name": "Kōkua"}}
|
|
1278
|
+
config.get_community_info("Kōkua")
|
|
1279
|
+
mock_fetch.assert_not_called()
|
|
1280
|
+
|
|
1281
|
+
# Now, set force_refresh and make sure it is refreshed
|
|
1282
|
+
config.get_community_info("Kōkua", force_refresh=True)
|
|
1283
|
+
mock_fetch.assert_called()
|
|
1284
|
+
|
|
1285
|
+
@mock.patch("cumulusci.core.config.org_config.OrgConfig._fetch_community_info")
|
|
1286
|
+
def test_community_info_exception(self, mock_fetch):
|
|
1287
|
+
"""Verify an exception is thrown when the community doesn't exist"""
|
|
1288
|
+
config = OrgConfig({}, "test")
|
|
1289
|
+
expected_exception = "Unable to find community information for 'bogus'"
|
|
1290
|
+
with pytest.raises(Exception, match=expected_exception):
|
|
1291
|
+
config.get_community_info("bogus")
|
|
1292
|
+
|
|
1293
|
+
MOCK_TOOLING_PACKAGE_RESULTS = [
|
|
1294
|
+
{
|
|
1295
|
+
"size": 2,
|
|
1296
|
+
"totalSize": 2,
|
|
1297
|
+
"done": True,
|
|
1298
|
+
"records": [
|
|
1299
|
+
{
|
|
1300
|
+
"SubscriberPackage": {
|
|
1301
|
+
"Id": "03350000000DEz4AAG",
|
|
1302
|
+
"NamespacePrefix": "GW_Volunteers",
|
|
1303
|
+
"Name": "PKG1",
|
|
1304
|
+
},
|
|
1305
|
+
"SubscriberPackageVersionId": "04t1T00000070yqQAA",
|
|
1306
|
+
},
|
|
1307
|
+
{
|
|
1308
|
+
"SubscriberPackage": {
|
|
1309
|
+
"Id": "03350000000DEz5AAG",
|
|
1310
|
+
"NamespacePrefix": "GW_Volunteers",
|
|
1311
|
+
"Name": "PKG2",
|
|
1312
|
+
},
|
|
1313
|
+
"SubscriberPackageVersionId": "04t000000000001AAA",
|
|
1314
|
+
},
|
|
1315
|
+
{
|
|
1316
|
+
"SubscriberPackage": {
|
|
1317
|
+
"Id": "03350000000DEz7AAG",
|
|
1318
|
+
"NamespacePrefix": "TESTY",
|
|
1319
|
+
"Name": "PKG3",
|
|
1320
|
+
},
|
|
1321
|
+
"SubscriberPackageVersionId": "04t000000000002AAA",
|
|
1322
|
+
},
|
|
1323
|
+
{
|
|
1324
|
+
"SubscriberPackage": {
|
|
1325
|
+
"Id": "03350000000DEz4AAG",
|
|
1326
|
+
"NamespacePrefix": "blah",
|
|
1327
|
+
"Name": "PKG4",
|
|
1328
|
+
},
|
|
1329
|
+
"SubscriberPackageVersionId": "04t0000000BOGUSAAA",
|
|
1330
|
+
},
|
|
1331
|
+
{
|
|
1332
|
+
"SubscriberPackage": {
|
|
1333
|
+
"Id": "03350000000DEz8AAG",
|
|
1334
|
+
"NamespacePrefix": "error",
|
|
1335
|
+
"Name": "PKG5",
|
|
1336
|
+
},
|
|
1337
|
+
"SubscriberPackageVersionId": "04t0000000ERRORAAA",
|
|
1338
|
+
},
|
|
1339
|
+
],
|
|
1340
|
+
},
|
|
1341
|
+
{
|
|
1342
|
+
"size": 1,
|
|
1343
|
+
"totalSize": 1,
|
|
1344
|
+
"done": True,
|
|
1345
|
+
"records": [
|
|
1346
|
+
{
|
|
1347
|
+
"Id": "04t1T00000070yqQAA",
|
|
1348
|
+
"MajorVersion": 3,
|
|
1349
|
+
"MinorVersion": 119,
|
|
1350
|
+
"PatchVersion": 0,
|
|
1351
|
+
"BuildNumber": 5,
|
|
1352
|
+
"IsBeta": False,
|
|
1353
|
+
}
|
|
1354
|
+
],
|
|
1355
|
+
},
|
|
1356
|
+
{
|
|
1357
|
+
"size": 1,
|
|
1358
|
+
"totalSize": 1,
|
|
1359
|
+
"done": True,
|
|
1360
|
+
"records": [
|
|
1361
|
+
{
|
|
1362
|
+
"Id": "04t000000000001AAA",
|
|
1363
|
+
"MajorVersion": 12,
|
|
1364
|
+
"MinorVersion": 0,
|
|
1365
|
+
"PatchVersion": 1,
|
|
1366
|
+
"BuildNumber": 1,
|
|
1367
|
+
"IsBeta": False,
|
|
1368
|
+
}
|
|
1369
|
+
],
|
|
1370
|
+
},
|
|
1371
|
+
{
|
|
1372
|
+
"size": 1,
|
|
1373
|
+
"totalSize": 1,
|
|
1374
|
+
"done": True,
|
|
1375
|
+
"records": [
|
|
1376
|
+
{
|
|
1377
|
+
"Id": "04t000000000002AAA",
|
|
1378
|
+
"MajorVersion": 1,
|
|
1379
|
+
"MinorVersion": 10,
|
|
1380
|
+
"PatchVersion": 0,
|
|
1381
|
+
"BuildNumber": 5,
|
|
1382
|
+
"IsBeta": True,
|
|
1383
|
+
}
|
|
1384
|
+
],
|
|
1385
|
+
},
|
|
1386
|
+
{"size": 0, "totalSize": 0, "done": True, "records": []},
|
|
1387
|
+
SalesforceError(None, None, None, None),
|
|
1388
|
+
]
|
|
1389
|
+
|
|
1390
|
+
@mock.patch("cumulusci.core.config.org_config.OrgConfig.salesforce_client")
|
|
1391
|
+
def test_installed_packages(self, sf):
|
|
1392
|
+
config = OrgConfig({}, "test")
|
|
1393
|
+
sf.restful.side_effect = self.MOCK_TOOLING_PACKAGE_RESULTS
|
|
1394
|
+
|
|
1395
|
+
expected = {
|
|
1396
|
+
"GW_Volunteers": [
|
|
1397
|
+
VersionInfo("04t1T00000070yqQAA", StrictVersion("3.119")),
|
|
1398
|
+
VersionInfo("04t000000000001AAA", StrictVersion("12.0.1")),
|
|
1399
|
+
],
|
|
1400
|
+
"PKG1": [
|
|
1401
|
+
VersionInfo("04t1T00000070yqQAA", StrictVersion("3.119")),
|
|
1402
|
+
],
|
|
1403
|
+
"GW_Volunteers@3.119": [
|
|
1404
|
+
VersionInfo("04t1T00000070yqQAA", StrictVersion("3.119"))
|
|
1405
|
+
],
|
|
1406
|
+
"GW_Volunteers@12.0.1": [
|
|
1407
|
+
VersionInfo("04t000000000001AAA", StrictVersion("12.0.1"))
|
|
1408
|
+
],
|
|
1409
|
+
"PKG2": [VersionInfo("04t000000000001AAA", StrictVersion("12.0.1"))],
|
|
1410
|
+
"TESTY": [VersionInfo("04t000000000002AAA", StrictVersion("1.10.0b5"))],
|
|
1411
|
+
"TESTY@1.10b5": [
|
|
1412
|
+
VersionInfo("04t000000000002AAA", StrictVersion("1.10.0b5"))
|
|
1413
|
+
],
|
|
1414
|
+
"03350000000DEz4AAG": [
|
|
1415
|
+
VersionInfo("04t1T00000070yqQAA", StrictVersion("3.119"))
|
|
1416
|
+
],
|
|
1417
|
+
"03350000000DEz5AAG": [
|
|
1418
|
+
VersionInfo("04t000000000001AAA", StrictVersion("12.0.1"))
|
|
1419
|
+
],
|
|
1420
|
+
"03350000000DEz7AAG": [
|
|
1421
|
+
VersionInfo("04t000000000002AAA", StrictVersion("1.10.0b5"))
|
|
1422
|
+
],
|
|
1423
|
+
"PKG3": [
|
|
1424
|
+
VersionInfo(id="04t000000000002AAA", number=StrictVersion("1.10b5"))
|
|
1425
|
+
],
|
|
1426
|
+
}
|
|
1427
|
+
# get it twice so we can make sure it is cached
|
|
1428
|
+
assert config.installed_packages == expected
|
|
1429
|
+
assert config.installed_packages == expected
|
|
1430
|
+
sf.restful.assert_called()
|
|
1431
|
+
|
|
1432
|
+
sf.restful.reset_mock()
|
|
1433
|
+
sf.restful.side_effect = self.MOCK_TOOLING_PACKAGE_RESULTS
|
|
1434
|
+
config.reset_installed_packages()
|
|
1435
|
+
assert config.installed_packages == expected
|
|
1436
|
+
sf.restful.assert_called()
|
|
1437
|
+
|
|
1438
|
+
@mock.patch("cumulusci.core.config.org_config.OrgConfig.salesforce_client")
|
|
1439
|
+
def test_has_minimum_package_version(self, sf):
|
|
1440
|
+
config = OrgConfig({}, "test")
|
|
1441
|
+
sf.restful.side_effect = self.MOCK_TOOLING_PACKAGE_RESULTS
|
|
1442
|
+
|
|
1443
|
+
assert config.has_minimum_package_version("TESTY", "1.9")
|
|
1444
|
+
assert config.has_minimum_package_version("TESTY", "1.10b5")
|
|
1445
|
+
assert not config.has_minimum_package_version("TESTY", "1.10b6")
|
|
1446
|
+
assert not config.has_minimum_package_version("TESTY", "1.10")
|
|
1447
|
+
assert not config.has_minimum_package_version("npsp", "1.0")
|
|
1448
|
+
|
|
1449
|
+
assert config.has_minimum_package_version("03350000000DEz4AAG", "3.119")
|
|
1450
|
+
|
|
1451
|
+
with pytest.raises(CumulusCIException):
|
|
1452
|
+
config.has_minimum_package_version("GW_Volunteers", "1.0")
|
|
1453
|
+
|
|
1454
|
+
def test_orginfo_cache_dir_global(self):
|
|
1455
|
+
config = OrgConfig(
|
|
1456
|
+
{
|
|
1457
|
+
"instance_url": "http://zombo.com/welcome",
|
|
1458
|
+
"username": "test-example@example.com",
|
|
1459
|
+
},
|
|
1460
|
+
"test",
|
|
1461
|
+
keychain=DummyKeychain(),
|
|
1462
|
+
global_org=True,
|
|
1463
|
+
)
|
|
1464
|
+
with TemporaryDirectory() as t:
|
|
1465
|
+
with mock.patch(
|
|
1466
|
+
"cumulusci.tests.util.DummyKeychain.global_config_dir", Path(t)
|
|
1467
|
+
):
|
|
1468
|
+
with config.get_orginfo_cache_dir("foo") as directory:
|
|
1469
|
+
assert directory.exists()
|
|
1470
|
+
assert str(t) in directory, (t, directory)
|
|
1471
|
+
assert (
|
|
1472
|
+
str(directory)
|
|
1473
|
+
.replace("\\", "/")
|
|
1474
|
+
.endswith("orginfo/zombo.com__test-example__example.com/foo")
|
|
1475
|
+
), str(directory).replace("\\", "/")
|
|
1476
|
+
foo = directory / "Foo.txt"
|
|
1477
|
+
with foo.open("w") as f:
|
|
1478
|
+
f.write("Bar")
|
|
1479
|
+
with foo.open("r") as f:
|
|
1480
|
+
assert f.read() == "Bar"
|
|
1481
|
+
|
|
1482
|
+
def test_orginfo_cache_dir_local(self):
|
|
1483
|
+
config = OrgConfig(
|
|
1484
|
+
{
|
|
1485
|
+
"instance_url": "http://zombo.com/welcome",
|
|
1486
|
+
"username": "test-example@example.com",
|
|
1487
|
+
},
|
|
1488
|
+
"test",
|
|
1489
|
+
keychain=DummyKeychain(),
|
|
1490
|
+
global_org=False,
|
|
1491
|
+
)
|
|
1492
|
+
with TemporaryDirectory() as t:
|
|
1493
|
+
with mock.patch("cumulusci.tests.util.DummyKeychain.cache_dir", Path(t)):
|
|
1494
|
+
|
|
1495
|
+
with config.get_orginfo_cache_dir("bar") as directory:
|
|
1496
|
+
assert str(t) in directory, (t, directory)
|
|
1497
|
+
assert (
|
|
1498
|
+
str(directory)
|
|
1499
|
+
.replace("\\", "/")
|
|
1500
|
+
.endswith("orginfo/zombo.com__test-example__example.com/bar")
|
|
1501
|
+
)
|
|
1502
|
+
assert directory.exists()
|
|
1503
|
+
foo = directory / "Foo.txt"
|
|
1504
|
+
with foo.open("w") as f:
|
|
1505
|
+
f.write("Bar")
|
|
1506
|
+
with foo.open("r") as f:
|
|
1507
|
+
assert f.read() == "Bar"
|
|
1508
|
+
|
|
1509
|
+
@responses.activate
|
|
1510
|
+
def test_is_person_accounts_enabled__not_enabled(self):
|
|
1511
|
+
config = OrgConfig(
|
|
1512
|
+
{
|
|
1513
|
+
"instance_url": "https://example.com",
|
|
1514
|
+
"access_token": "TOKEN",
|
|
1515
|
+
"id": "OODxxxxxxxxxxxx/user",
|
|
1516
|
+
},
|
|
1517
|
+
"test",
|
|
1518
|
+
)
|
|
1519
|
+
assert (
|
|
1520
|
+
config._is_person_accounts_enabled is None
|
|
1521
|
+
), "_is_person_accounts_enabled should be initialized as None"
|
|
1522
|
+
|
|
1523
|
+
responses.add(
|
|
1524
|
+
"GET",
|
|
1525
|
+
"https://example.com/services/data",
|
|
1526
|
+
json=[{"version": CURRENT_SF_API_VERSION}],
|
|
1527
|
+
)
|
|
1528
|
+
|
|
1529
|
+
responses.add(
|
|
1530
|
+
"GET",
|
|
1531
|
+
f"https://example.com/services/data/v{CURRENT_SF_API_VERSION}/sobjects/Account/describe",
|
|
1532
|
+
json={"fields": [{"name": "Id"}]},
|
|
1533
|
+
)
|
|
1534
|
+
|
|
1535
|
+
# Verify checks describe if _is_person_accounts_enabled is None.
|
|
1536
|
+
actual = config.is_person_accounts_enabled
|
|
1537
|
+
|
|
1538
|
+
assert actual is False, ""
|
|
1539
|
+
assert actual == config._is_person_accounts_enabled
|
|
1540
|
+
|
|
1541
|
+
# Verify subsequent calls return cached value.
|
|
1542
|
+
config._is_person_accounts_enabled = True
|
|
1543
|
+
|
|
1544
|
+
assert config._is_person_accounts_enabled == config.is_person_accounts_enabled
|
|
1545
|
+
|
|
1546
|
+
@responses.activate
|
|
1547
|
+
def test_is_person_accounts_enabled__is_enabled(self):
|
|
1548
|
+
config = OrgConfig(
|
|
1549
|
+
{
|
|
1550
|
+
"instance_url": "https://example.com",
|
|
1551
|
+
"access_token": "TOKEN",
|
|
1552
|
+
"id": "OODxxxxxxxxxxxx/user",
|
|
1553
|
+
},
|
|
1554
|
+
"test",
|
|
1555
|
+
)
|
|
1556
|
+
assert (
|
|
1557
|
+
config._is_person_accounts_enabled is None
|
|
1558
|
+
), "_is_person_accounts_enabled should be initialized as None"
|
|
1559
|
+
|
|
1560
|
+
responses.add(
|
|
1561
|
+
"GET",
|
|
1562
|
+
"https://example.com/services/data",
|
|
1563
|
+
json=[{"version": CURRENT_SF_API_VERSION}],
|
|
1564
|
+
)
|
|
1565
|
+
|
|
1566
|
+
responses.add(
|
|
1567
|
+
"GET",
|
|
1568
|
+
f"https://example.com/services/data/v{CURRENT_SF_API_VERSION}/sobjects/Account/describe",
|
|
1569
|
+
json={"fields": [{"name": "Id"}, {"name": "IsPersonAccount"}]},
|
|
1570
|
+
)
|
|
1571
|
+
|
|
1572
|
+
# Verify checks describe if _is_person_accounts_enabled is None.
|
|
1573
|
+
actual = config.is_person_accounts_enabled
|
|
1574
|
+
|
|
1575
|
+
assert actual is True, ""
|
|
1576
|
+
assert actual == config._is_person_accounts_enabled
|
|
1577
|
+
|
|
1578
|
+
# Verify subsequent calls return cached value.
|
|
1579
|
+
config._is_person_accounts_enabled = False
|
|
1580
|
+
|
|
1581
|
+
assert config._is_person_accounts_enabled == config.is_person_accounts_enabled
|
|
1582
|
+
|
|
1583
|
+
@responses.activate
|
|
1584
|
+
def test_is_multi_currency_enabled__not_enabled(self):
|
|
1585
|
+
config = OrgConfig(
|
|
1586
|
+
{
|
|
1587
|
+
"instance_url": "https://example.com",
|
|
1588
|
+
"access_token": "TOKEN",
|
|
1589
|
+
"id": "OODxxxxxxxxxxxx/user",
|
|
1590
|
+
},
|
|
1591
|
+
"test",
|
|
1592
|
+
)
|
|
1593
|
+
assert (
|
|
1594
|
+
config._multiple_currencies_is_enabled is False
|
|
1595
|
+
), "_multiple_currencies_is_enabled should be initialized as False"
|
|
1596
|
+
|
|
1597
|
+
# Login call.
|
|
1598
|
+
responses.add(
|
|
1599
|
+
"GET",
|
|
1600
|
+
"https://example.com/services/data",
|
|
1601
|
+
json=[{"version": CURRENT_SF_API_VERSION}],
|
|
1602
|
+
)
|
|
1603
|
+
|
|
1604
|
+
# CurrencyType describe() call.
|
|
1605
|
+
# Since Multiple Currencies is not enabled, CurrencyType Sobject is not exposed.
|
|
1606
|
+
# Therefore, the describe call will result in a 404.
|
|
1607
|
+
responses.add(
|
|
1608
|
+
"GET",
|
|
1609
|
+
f"https://example.com/services/data/v{CURRENT_SF_API_VERSION}/sobjects/CurrencyType/describe",
|
|
1610
|
+
status=404,
|
|
1611
|
+
json={
|
|
1612
|
+
"errorCode": "NOT_FOUND",
|
|
1613
|
+
"message": "The requested resource does not exist",
|
|
1614
|
+
},
|
|
1615
|
+
)
|
|
1616
|
+
|
|
1617
|
+
# Add a second 404 to demonstrate we always check the describe until we detect Multiple Currencies is enabled. From then on, we cache the fact that Multiple Currencies is enabled knowing Multiple Currencies cannot be disabled.
|
|
1618
|
+
responses.add(
|
|
1619
|
+
"GET",
|
|
1620
|
+
f"https://example.com/services/data/v{CURRENT_SF_API_VERSION}/sobjects/CurrencyType/describe",
|
|
1621
|
+
status=404,
|
|
1622
|
+
json={
|
|
1623
|
+
"errorCode": "NOT_FOUND",
|
|
1624
|
+
"message": "The requested resource does not exist",
|
|
1625
|
+
},
|
|
1626
|
+
)
|
|
1627
|
+
|
|
1628
|
+
# Check 1: is_multiple_currencies_enabled should be False since the CurrencyType describe gives a 404.
|
|
1629
|
+
actual = config.is_multiple_currencies_enabled
|
|
1630
|
+
assert (
|
|
1631
|
+
actual is False
|
|
1632
|
+
), "config.is_multiple_currencies_enabled should be False since the CurrencyType describe returns a 404."
|
|
1633
|
+
assert (
|
|
1634
|
+
config._multiple_currencies_is_enabled is False
|
|
1635
|
+
), "config._multiple_currencies_is_enabled should still be False since the CurrencyType describe returns a 404."
|
|
1636
|
+
|
|
1637
|
+
# Check 2: We should still get the CurrencyType describe since we never cached that multiple currencies is enabled.
|
|
1638
|
+
actual = config.is_multiple_currencies_enabled
|
|
1639
|
+
assert (
|
|
1640
|
+
actual is False
|
|
1641
|
+
), "config.is_multiple_currencies_enabled should be False since the CurrencyType describe returns a 404."
|
|
1642
|
+
assert (
|
|
1643
|
+
config._multiple_currencies_is_enabled is False
|
|
1644
|
+
), "config._multiple_currencies_is_enabled should still be False since the CurrencyType describe returns a 404."
|
|
1645
|
+
|
|
1646
|
+
# We should have made 3 calls: 1 token call + 2 describe calls
|
|
1647
|
+
assert len(responses.calls) == 1 + 2
|
|
1648
|
+
|
|
1649
|
+
@responses.activate
|
|
1650
|
+
def test_is_multi_currency_enabled__is_enabled(self):
|
|
1651
|
+
config = OrgConfig(
|
|
1652
|
+
{
|
|
1653
|
+
"instance_url": "https://example.com",
|
|
1654
|
+
"access_token": "TOKEN",
|
|
1655
|
+
"id": "OODxxxxxxxxxxxx/user",
|
|
1656
|
+
},
|
|
1657
|
+
"test",
|
|
1658
|
+
)
|
|
1659
|
+
|
|
1660
|
+
assert (
|
|
1661
|
+
config._multiple_currencies_is_enabled is False
|
|
1662
|
+
), "_multiple_currencies_is_enabled should be initialized as False"
|
|
1663
|
+
|
|
1664
|
+
# Token call.
|
|
1665
|
+
responses.add(
|
|
1666
|
+
"GET",
|
|
1667
|
+
"https://example.com/services/data",
|
|
1668
|
+
json=[{"version": CURRENT_SF_API_VERSION}],
|
|
1669
|
+
)
|
|
1670
|
+
|
|
1671
|
+
# CurrencyType describe() call.
|
|
1672
|
+
# Since Multiple Currencies is enabled, so the describe call returns a 200.
|
|
1673
|
+
responses.add(
|
|
1674
|
+
"GET",
|
|
1675
|
+
f"https://example.com/services/data/v{CURRENT_SF_API_VERSION}/sobjects/CurrencyType/describe",
|
|
1676
|
+
json={
|
|
1677
|
+
# The actual payload doesn't matter; only matters is we get a 200.
|
|
1678
|
+
},
|
|
1679
|
+
)
|
|
1680
|
+
|
|
1681
|
+
# Check 1: is_multiple_currencies_enabled should be True since the CurrencyType describe gives a 200.
|
|
1682
|
+
actual = config.is_multiple_currencies_enabled
|
|
1683
|
+
assert (
|
|
1684
|
+
actual is True
|
|
1685
|
+
), "config.is_multiple_currencies_enabled should be True since the CurrencyType describe returns a 200."
|
|
1686
|
+
assert (
|
|
1687
|
+
config._multiple_currencies_is_enabled is True
|
|
1688
|
+
), "config._multiple_currencies_is_enabled should be True since the CurrencyType describe returns a 200."
|
|
1689
|
+
|
|
1690
|
+
# Check 2: We should have cached that Multiple Currencies is enabled, so we should not make a 2nd descrobe call. This is ok to cache since Multiple Currencies cannot be disabled.
|
|
1691
|
+
actual = config.is_multiple_currencies_enabled
|
|
1692
|
+
assert (
|
|
1693
|
+
actual is True
|
|
1694
|
+
), "config.is_multiple_currencies_enabled should be True since the our cached value in _multiple_currencies_is_enabled is True."
|
|
1695
|
+
assert (
|
|
1696
|
+
config._multiple_currencies_is_enabled is True
|
|
1697
|
+
), "config._multiple_currencies_is_enabled should still be True."
|
|
1698
|
+
|
|
1699
|
+
# We should have made 2 calls: 1 token call + 1 describe call
|
|
1700
|
+
assert len(responses.calls) == 1 + 1
|
|
1701
|
+
|
|
1702
|
+
@responses.activate
|
|
1703
|
+
def test_is_advanced_currency_management_enabled__multiple_currencies_not_enabled(
|
|
1704
|
+
self,
|
|
1705
|
+
):
|
|
1706
|
+
config = OrgConfig(
|
|
1707
|
+
{
|
|
1708
|
+
"instance_url": "https://example.com",
|
|
1709
|
+
"access_token": "TOKEN",
|
|
1710
|
+
"id": "OODxxxxxxxxxxxx/user",
|
|
1711
|
+
},
|
|
1712
|
+
"test",
|
|
1713
|
+
)
|
|
1714
|
+
|
|
1715
|
+
# Token call.
|
|
1716
|
+
responses.add(
|
|
1717
|
+
"GET",
|
|
1718
|
+
"https://example.com/services/data",
|
|
1719
|
+
json=[{"version": CURRENT_SF_API_VERSION}],
|
|
1720
|
+
)
|
|
1721
|
+
|
|
1722
|
+
# DatedConversionRate describe() call.
|
|
1723
|
+
# Since Multiple Currencies is not enabled, DatedConversionRate Sobject is not exposed.
|
|
1724
|
+
# Therefore, the describe call will result in a 404.
|
|
1725
|
+
responses.add(
|
|
1726
|
+
"GET",
|
|
1727
|
+
f"https://example.com/services/data/v{CURRENT_SF_API_VERSION}/sobjects/DatedConversionRate/describe",
|
|
1728
|
+
status=404,
|
|
1729
|
+
json={
|
|
1730
|
+
"errorCode": "NOT_FOUND",
|
|
1731
|
+
"message": "The requested resource does not exist",
|
|
1732
|
+
},
|
|
1733
|
+
)
|
|
1734
|
+
|
|
1735
|
+
# is_advanced_currency_management_enabled should be False since:
|
|
1736
|
+
# - DatedConversionRate describe gives a 404 implying the Sobject is not exposed becuase Multiple Currencies is not enabled.
|
|
1737
|
+
actual = config.is_advanced_currency_management_enabled
|
|
1738
|
+
assert (
|
|
1739
|
+
actual is False
|
|
1740
|
+
), "config.is_advanced_currency_management_enabled should be False since the describe gives a 404."
|
|
1741
|
+
|
|
1742
|
+
# We should have made 2 calls: 1 token call + 1 describe call
|
|
1743
|
+
assert len(responses.calls) == 1 + 1
|
|
1744
|
+
|
|
1745
|
+
@responses.activate
|
|
1746
|
+
def test_is_advanced_currency_management_enabled__multiple_currencies_enabled__acm_not_enabled(
|
|
1747
|
+
self,
|
|
1748
|
+
):
|
|
1749
|
+
config = OrgConfig(
|
|
1750
|
+
{
|
|
1751
|
+
"instance_url": "https://example.com",
|
|
1752
|
+
"access_token": "TOKEN",
|
|
1753
|
+
"id": "OODxxxxxxxxxxxx/user",
|
|
1754
|
+
},
|
|
1755
|
+
"test",
|
|
1756
|
+
)
|
|
1757
|
+
|
|
1758
|
+
# Token call.
|
|
1759
|
+
responses.add(
|
|
1760
|
+
"GET",
|
|
1761
|
+
"https://example.com/services/data",
|
|
1762
|
+
json=[{"version": CURRENT_SF_API_VERSION}],
|
|
1763
|
+
)
|
|
1764
|
+
|
|
1765
|
+
# DatedConversionRate describe() call.
|
|
1766
|
+
# Since Multiple Currencies is enabled, so the describe call returns a 200.
|
|
1767
|
+
# However, ACM is not enabled so DatedConversionRate is not createable.
|
|
1768
|
+
responses.add(
|
|
1769
|
+
"GET",
|
|
1770
|
+
f"https://example.com/services/data/v{CURRENT_SF_API_VERSION}/sobjects/DatedConversionRate/describe",
|
|
1771
|
+
json={"createable": False},
|
|
1772
|
+
)
|
|
1773
|
+
|
|
1774
|
+
# is_advanced_currency_management_enabled should be False:
|
|
1775
|
+
# - DatedConversionRate describe gives a 200, so the Sobject is exposed (because Multiple Currencies is enabled).
|
|
1776
|
+
# - But DatedConversionRate is not creatable implying ACM is not enabled.
|
|
1777
|
+
actual = config.is_advanced_currency_management_enabled
|
|
1778
|
+
assert (
|
|
1779
|
+
actual is False
|
|
1780
|
+
), 'config.is_advanced_currency_management_enabled should be False since though the describe gives a 200, the describe is not "createable".'
|
|
1781
|
+
|
|
1782
|
+
# We should have made 2 calls: 1 token call + 1 describe call
|
|
1783
|
+
assert len(responses.calls) == 1 + 1
|
|
1784
|
+
|
|
1785
|
+
@responses.activate
|
|
1786
|
+
def test_is_advanced_currency_management_enabled__multiple_currencies_enabled__acm_enabled(
|
|
1787
|
+
self,
|
|
1788
|
+
):
|
|
1789
|
+
config = OrgConfig(
|
|
1790
|
+
{
|
|
1791
|
+
"instance_url": "https://example.com",
|
|
1792
|
+
"access_token": "TOKEN",
|
|
1793
|
+
"id": "OODxxxxxxxxxxxx/user",
|
|
1794
|
+
},
|
|
1795
|
+
"test",
|
|
1796
|
+
)
|
|
1797
|
+
|
|
1798
|
+
# Token call.
|
|
1799
|
+
responses.add(
|
|
1800
|
+
"GET",
|
|
1801
|
+
"https://example.com/services/data",
|
|
1802
|
+
json=[{"version": CURRENT_SF_API_VERSION}],
|
|
1803
|
+
)
|
|
1804
|
+
|
|
1805
|
+
# DatedConversionRate describe() call.
|
|
1806
|
+
# Since Multiple Currencies is enabled, so the describe call returns a 200.
|
|
1807
|
+
# However, ACM is not enabled so DatedConversionRate is not createable.
|
|
1808
|
+
responses.add(
|
|
1809
|
+
"GET",
|
|
1810
|
+
f"https://example.com/services/data/v{CURRENT_SF_API_VERSION}/sobjects/DatedConversionRate/describe",
|
|
1811
|
+
json={"createable": True},
|
|
1812
|
+
)
|
|
1813
|
+
|
|
1814
|
+
# is_advanced_currency_management_enabled should be False:
|
|
1815
|
+
# - DatedConversionRate describe gives a 200, so the Sobject is exposed (because Multiple Currencies is enabled).
|
|
1816
|
+
# - But DatedConversionRate is not creatable implying ACM is not enabled.
|
|
1817
|
+
actual = config.is_advanced_currency_management_enabled
|
|
1818
|
+
assert (
|
|
1819
|
+
actual is True
|
|
1820
|
+
), 'config.is_advanced_currency_management_enabled should be False since both the describe gives a 200 and the describe is "createable".'
|
|
1821
|
+
|
|
1822
|
+
# We should have made 2 calls: 1 token call + 1 describe call
|
|
1823
|
+
assert len(responses.calls) == 1 + 1
|
|
1824
|
+
|
|
1825
|
+
@responses.activate
|
|
1826
|
+
def test_is_survey_advanced_features_enabled(self):
|
|
1827
|
+
config = OrgConfig(
|
|
1828
|
+
{
|
|
1829
|
+
"instance_url": "https://example.com",
|
|
1830
|
+
"access_token": "TOKEN",
|
|
1831
|
+
"id": "OODxxxxxxxxxxxx/user",
|
|
1832
|
+
},
|
|
1833
|
+
"test",
|
|
1834
|
+
)
|
|
1835
|
+
|
|
1836
|
+
# Token call.
|
|
1837
|
+
responses.add(
|
|
1838
|
+
"GET",
|
|
1839
|
+
"https://example.com/services/data",
|
|
1840
|
+
json=[{"version": CURRENT_SF_API_VERSION}],
|
|
1841
|
+
)
|
|
1842
|
+
|
|
1843
|
+
# describe()
|
|
1844
|
+
responses.add(
|
|
1845
|
+
"GET",
|
|
1846
|
+
f"https://example.com/services/data/v{CURRENT_SF_API_VERSION}/sobjects/PermissionSet/describe",
|
|
1847
|
+
json={"fields": [{"name": "PermissionsAllowSurveyAdvancedFeatures"}]},
|
|
1848
|
+
)
|
|
1849
|
+
|
|
1850
|
+
assert config.is_survey_advanced_features_enabled
|
|
1851
|
+
|
|
1852
|
+
@responses.activate
|
|
1853
|
+
def test_is_survey_advanced_features_enabled__not_enabled(self):
|
|
1854
|
+
config = OrgConfig(
|
|
1855
|
+
{
|
|
1856
|
+
"instance_url": "https://example.com",
|
|
1857
|
+
"access_token": "TOKEN",
|
|
1858
|
+
"id": "OODxxxxxxxxxxxx/user",
|
|
1859
|
+
},
|
|
1860
|
+
"test",
|
|
1861
|
+
)
|
|
1862
|
+
|
|
1863
|
+
# Token call.
|
|
1864
|
+
responses.add(
|
|
1865
|
+
"GET",
|
|
1866
|
+
"https://example.com/services/data",
|
|
1867
|
+
json=[{"version": CURRENT_SF_API_VERSION}],
|
|
1868
|
+
)
|
|
1869
|
+
|
|
1870
|
+
# describe()
|
|
1871
|
+
responses.add(
|
|
1872
|
+
"GET",
|
|
1873
|
+
f"https://example.com/services/data/v{CURRENT_SF_API_VERSION}/sobjects/PermissionSet/describe",
|
|
1874
|
+
json={"fields": [{"name": "foo"}]},
|
|
1875
|
+
)
|
|
1876
|
+
|
|
1877
|
+
assert not config.is_survey_advanced_features_enabled
|
|
1878
|
+
|
|
1879
|
+
def test_resolve_04t_dependencies(self):
|
|
1880
|
+
config = OrgConfig({}, "test")
|
|
1881
|
+
config._installed_packages = {
|
|
1882
|
+
"dep@1.0": [VersionInfo("04t000000000001AAA", "1.0")]
|
|
1883
|
+
}
|
|
1884
|
+
result = config.resolve_04t_dependencies(
|
|
1885
|
+
[PackageNamespaceVersionDependency(namespace="dep", version="1.0")]
|
|
1886
|
+
)
|
|
1887
|
+
assert result == [PackageVersionIdDependency(version_id="04t000000000001AAA")]
|
|
1888
|
+
|
|
1889
|
+
def test_resolve_04t_dependencies__not_installed(self):
|
|
1890
|
+
config = OrgConfig({}, "test")
|
|
1891
|
+
config._installed_packages = {}
|
|
1892
|
+
with pytest.raises(DependencyResolutionError):
|
|
1893
|
+
config.resolve_04t_dependencies(
|
|
1894
|
+
[PackageNamespaceVersionDependency(namespace="dep", version="1.0")]
|
|
1895
|
+
)
|