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,1228 @@
|
|
|
1
|
+
import datetime
|
|
2
|
+
import json
|
|
3
|
+
import os
|
|
4
|
+
import re
|
|
5
|
+
import sys
|
|
6
|
+
import tempfile
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
from unittest import mock
|
|
9
|
+
|
|
10
|
+
import pytest
|
|
11
|
+
|
|
12
|
+
from cumulusci.core import utils
|
|
13
|
+
from cumulusci.core.config import (
|
|
14
|
+
BaseConfig,
|
|
15
|
+
OrgConfig,
|
|
16
|
+
ScratchOrgConfig,
|
|
17
|
+
ServiceConfig,
|
|
18
|
+
UniversalConfig,
|
|
19
|
+
)
|
|
20
|
+
from cumulusci.core.config.marketing_cloud_service_config import (
|
|
21
|
+
MarketingCloudServiceConfig,
|
|
22
|
+
)
|
|
23
|
+
from cumulusci.core.config.sfdx_org_config import SfdxOrgConfig
|
|
24
|
+
from cumulusci.core.exceptions import (
|
|
25
|
+
ConfigError,
|
|
26
|
+
CumulusCIException,
|
|
27
|
+
CumulusCIUsageError,
|
|
28
|
+
KeychainKeyNotFound,
|
|
29
|
+
OrgCannotBeLoaded,
|
|
30
|
+
OrgNotFound,
|
|
31
|
+
ServiceCannotBeLoaded,
|
|
32
|
+
ServiceNotConfigured,
|
|
33
|
+
ServiceNotValid,
|
|
34
|
+
)
|
|
35
|
+
from cumulusci.core.keychain import EncryptedFileProjectKeychain
|
|
36
|
+
from cumulusci.core.keychain.base_project_keychain import (
|
|
37
|
+
DEFAULT_CONNECTED_APP,
|
|
38
|
+
DEFAULT_CONNECTED_APP_NAME,
|
|
39
|
+
)
|
|
40
|
+
from cumulusci.core.keychain.encrypted_file_project_keychain import (
|
|
41
|
+
SERVICE_ORG_FILE_MODE,
|
|
42
|
+
GlobalOrg,
|
|
43
|
+
)
|
|
44
|
+
from cumulusci.core.keychain.serialization import (
|
|
45
|
+
_simplify_config,
|
|
46
|
+
load_config_from_json_or_pickle,
|
|
47
|
+
)
|
|
48
|
+
from cumulusci.core.tests.utils import EnvironmentVarGuard
|
|
49
|
+
from cumulusci.utils import temporary_dir
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
@pytest.fixture(params=[True, False])
|
|
53
|
+
def withdifferentformats(request):
|
|
54
|
+
SHOULD_SAVE_AS_JSON = request.param
|
|
55
|
+
with mock.patch(
|
|
56
|
+
"cumulusci.core.keychain.serialization.SHOULD_SAVE_AS_JSON", request.param
|
|
57
|
+
):
|
|
58
|
+
yield "json" if SHOULD_SAVE_AS_JSON else "pickle"
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
@pytest.fixture()
|
|
62
|
+
def keychain(project_config, key) -> EncryptedFileProjectKeychain:
|
|
63
|
+
keychain = EncryptedFileProjectKeychain(project_config, key)
|
|
64
|
+
assert keychain.project_config == project_config
|
|
65
|
+
assert keychain.key == key
|
|
66
|
+
return keychain
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
class TestEncryptedFileProjectKeychain:
|
|
70
|
+
|
|
71
|
+
project_name = "TestProject"
|
|
72
|
+
|
|
73
|
+
def _write_file(self, filepath, contents):
|
|
74
|
+
with open(filepath, "w") as f:
|
|
75
|
+
f.write(contents)
|
|
76
|
+
|
|
77
|
+
def _default_org_path(self, keychain: EncryptedFileProjectKeychain):
|
|
78
|
+
return Path(keychain.global_config_dir, "TestProject/DEFAULT_ORG.txt")
|
|
79
|
+
|
|
80
|
+
def test_cache_dir(self, keychain):
|
|
81
|
+
assert keychain.cache_dir.name == ".cci"
|
|
82
|
+
|
|
83
|
+
def test_raise_service_not_configured(self, keychain):
|
|
84
|
+
with pytest.raises(ServiceNotConfigured):
|
|
85
|
+
keychain._raise_service_not_configured("test-name")
|
|
86
|
+
|
|
87
|
+
#######################################
|
|
88
|
+
# Orgs #
|
|
89
|
+
#######################################
|
|
90
|
+
|
|
91
|
+
def test_set_and_get_org__global(
|
|
92
|
+
self, keychain, org_config, key, withdifferentformats
|
|
93
|
+
):
|
|
94
|
+
org_config.global_org = True
|
|
95
|
+
keychain.set_org(org_config, True)
|
|
96
|
+
assert list(keychain.orgs.keys()) == ["test"]
|
|
97
|
+
assert _simplify_config(keychain.get_org("test").config) == org_config.config
|
|
98
|
+
|
|
99
|
+
def test_get_org__with_config_properly_overridden(
|
|
100
|
+
self, keychain, scratch_org_config
|
|
101
|
+
):
|
|
102
|
+
days = 16
|
|
103
|
+
config_file = "./foo/bar/baz"
|
|
104
|
+
scratch_org_config.global_org = True
|
|
105
|
+
# the orgs encrypted file has the default value for days and config_file
|
|
106
|
+
keychain.set_org(scratch_org_config, True)
|
|
107
|
+
# but this particular scratch org has days and config_file specified via cumulusci.yml
|
|
108
|
+
keychain.project_config.config = {
|
|
109
|
+
"orgs": {"scratch": {"test": {"days": days, "config_file": config_file}}}
|
|
110
|
+
}
|
|
111
|
+
org = keychain.get_org("test")
|
|
112
|
+
|
|
113
|
+
# ensure what is configured in cumulusci.yml is what is loaded into the config
|
|
114
|
+
assert org.config["days"] == days
|
|
115
|
+
assert org.config["config_file"] == config_file
|
|
116
|
+
|
|
117
|
+
def test_get_org__not_found(self, keychain):
|
|
118
|
+
org_name = "mythical"
|
|
119
|
+
error_message = f"Org with name '{org_name}' does not exist."
|
|
120
|
+
with pytest.raises(OrgNotFound, match=error_message):
|
|
121
|
+
keychain.get_org(org_name)
|
|
122
|
+
|
|
123
|
+
def test_set_org__no_key_should_save_to_unencrypted_file(
|
|
124
|
+
self, keychain, org_config
|
|
125
|
+
):
|
|
126
|
+
keychain.key = None
|
|
127
|
+
keychain.set_org(org_config)
|
|
128
|
+
|
|
129
|
+
filepath = Path(keychain.project_local_dir, "test.org")
|
|
130
|
+
with open(filepath, "rb") as f:
|
|
131
|
+
assert load_config_from_json_or_pickle(f.read()) == {
|
|
132
|
+
**org_config.config,
|
|
133
|
+
# still the default for now
|
|
134
|
+
"serialization_format": "pickle",
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
def test_set_org__should_not_save_when_environment_project_keychain_set(
|
|
138
|
+
self, keychain, org_config, withdifferentformats
|
|
139
|
+
):
|
|
140
|
+
with temporary_dir() as temp:
|
|
141
|
+
env = EnvironmentVarGuard()
|
|
142
|
+
with EnvironmentVarGuard() as env:
|
|
143
|
+
env.set("CUMULUSCI_KEYCHAIN_CLASS", "EnvironmentProjectKeychain")
|
|
144
|
+
with mock.patch.object(
|
|
145
|
+
EncryptedFileProjectKeychain, "project_local_dir", temp
|
|
146
|
+
):
|
|
147
|
+
keychain.set_org(org_config, global_org=False)
|
|
148
|
+
|
|
149
|
+
actual_org = keychain.get_org("test")
|
|
150
|
+
assert actual_org.config == {
|
|
151
|
+
**org_config.config,
|
|
152
|
+
"serialization_format": withdifferentformats,
|
|
153
|
+
}
|
|
154
|
+
assert not Path(temp, "test.org").is_file()
|
|
155
|
+
|
|
156
|
+
@mock.patch("cumulusci.core.keychain.encrypted_file_project_keychain.open")
|
|
157
|
+
def test_save_org_when_no_project_local_dir_present(
|
|
158
|
+
self, mock_open, keychain, org_config
|
|
159
|
+
):
|
|
160
|
+
with mock.patch.object(EncryptedFileProjectKeychain, "project_local_dir", None):
|
|
161
|
+
keychain._save_org("alias", org_config, global_org=False)
|
|
162
|
+
assert mock_open.call_count == 0
|
|
163
|
+
|
|
164
|
+
def test_load_files__org_empty(self, keychain):
|
|
165
|
+
self._write_file(
|
|
166
|
+
Path(keychain.global_config_dir, "test.org"),
|
|
167
|
+
keychain._get_config_bytes(BaseConfig({"foo": "bar"})).decode("utf-8"),
|
|
168
|
+
)
|
|
169
|
+
|
|
170
|
+
del keychain.config["orgs"]
|
|
171
|
+
keychain._load_orgs()
|
|
172
|
+
assert "foo" in keychain.get_org("test").config
|
|
173
|
+
assert keychain.get_org("test").keychain == keychain
|
|
174
|
+
|
|
175
|
+
def test_remove_org(self, keychain, org_config):
|
|
176
|
+
keychain.set_org(org_config)
|
|
177
|
+
keychain.remove_org("test")
|
|
178
|
+
assert "test" not in keychain.orgs
|
|
179
|
+
|
|
180
|
+
def test_remove_org__not_found(self, keychain):
|
|
181
|
+
keychain.orgs["test"] = mock.Mock()
|
|
182
|
+
with pytest.raises(OrgNotFound):
|
|
183
|
+
keychain.remove_org("test")
|
|
184
|
+
|
|
185
|
+
def test_remove_org__global_not_found(self, keychain):
|
|
186
|
+
keychain.orgs["test"] = mock.Mock()
|
|
187
|
+
with pytest.raises(OrgNotFound):
|
|
188
|
+
keychain.remove_org("test", True)
|
|
189
|
+
|
|
190
|
+
def test_set_and_get_org_local_should_not_shadow_global(
|
|
191
|
+
self, keychain, org_config, project_config, key, withdifferentformats
|
|
192
|
+
):
|
|
193
|
+
global_org = True
|
|
194
|
+
org_config.global_org = global_org
|
|
195
|
+
keychain.set_org(org_config, global_org)
|
|
196
|
+
assert ["test"] == list(keychain.orgs.keys())
|
|
197
|
+
assert isinstance(keychain.orgs["test"], GlobalOrg), keychain.orgs["test"]
|
|
198
|
+
test_config = keychain.get_org("test").config
|
|
199
|
+
assert {
|
|
200
|
+
**org_config.config,
|
|
201
|
+
"serialization_format": withdifferentformats,
|
|
202
|
+
} == test_config
|
|
203
|
+
|
|
204
|
+
org_filepath = Path(keychain.global_config_dir, "test.org")
|
|
205
|
+
assert org_filepath.exists()
|
|
206
|
+
|
|
207
|
+
# os.stat returns something different on windows
|
|
208
|
+
# so only check this on osx/linux
|
|
209
|
+
if not sys.platform.startswith("win"):
|
|
210
|
+
# ensure expected file permissions
|
|
211
|
+
stat_result = os.stat(org_filepath)
|
|
212
|
+
actual_mode = oct(stat_result.st_mode & 0o777)
|
|
213
|
+
assert actual_mode == oct(SERVICE_ORG_FILE_MODE)
|
|
214
|
+
|
|
215
|
+
# check that it saves to the right place
|
|
216
|
+
with mock.patch(
|
|
217
|
+
"cumulusci.core.keychain.encrypted_file_project_keychain.os.open"
|
|
218
|
+
) as o:
|
|
219
|
+
org_config.save()
|
|
220
|
+
opened_file = o.mock_calls[0][1][0]
|
|
221
|
+
assert opened_file.parent.name == ".cumulusci"
|
|
222
|
+
assert opened_file.name == "test.org"
|
|
223
|
+
|
|
224
|
+
# check that it can be loaded in a fresh keychain
|
|
225
|
+
new_keychain = EncryptedFileProjectKeychain(project_config, key)
|
|
226
|
+
org_config = new_keychain.get_org("test")
|
|
227
|
+
assert org_config.global_org
|
|
228
|
+
|
|
229
|
+
def test_get_default_org__with_files(self, keychain, org_config):
|
|
230
|
+
org_config = OrgConfig(org_config.config.copy(), "test", keychain=keychain)
|
|
231
|
+
org_config.save()
|
|
232
|
+
with open(self._default_org_path(keychain), "w") as f:
|
|
233
|
+
f.write("test")
|
|
234
|
+
try:
|
|
235
|
+
assert (
|
|
236
|
+
_simplify_config(keychain.get_default_org()[1].config)
|
|
237
|
+
== org_config.config
|
|
238
|
+
)
|
|
239
|
+
finally:
|
|
240
|
+
self._default_org_path(keychain).unlink()
|
|
241
|
+
|
|
242
|
+
def test_get_default_org__with_files__missing_org(self, keychain):
|
|
243
|
+
with open(self._default_org_path(keychain), "w") as f:
|
|
244
|
+
f.write("should_not_exist")
|
|
245
|
+
assert self._default_org_path(keychain).exists()
|
|
246
|
+
assert keychain.get_default_org() == (None, None)
|
|
247
|
+
assert not self._default_org_path(keychain).exists()
|
|
248
|
+
|
|
249
|
+
@mock.patch("sarge.Command")
|
|
250
|
+
def test_set_default_org__with_files(self, Command, keychain, org_config):
|
|
251
|
+
org_config = OrgConfig(org_config.config.copy(), "test")
|
|
252
|
+
keychain.set_org(org_config)
|
|
253
|
+
keychain.set_default_org("test")
|
|
254
|
+
with open(self._default_org_path(keychain)) as f:
|
|
255
|
+
assert f.read() == "test"
|
|
256
|
+
self._default_org_path(keychain).unlink()
|
|
257
|
+
|
|
258
|
+
@mock.patch("sarge.Command")
|
|
259
|
+
def test_unset_default_org__with_files(self, Command, keychain, org_config):
|
|
260
|
+
org_config = org_config.config.copy()
|
|
261
|
+
org_config = OrgConfig(org_config, "test")
|
|
262
|
+
keychain.set_org(org_config)
|
|
263
|
+
keychain.set_default_org("test")
|
|
264
|
+
keychain.unset_default_org()
|
|
265
|
+
assert keychain.get_default_org()[1] is None
|
|
266
|
+
assert not self._default_org_path(keychain).exists()
|
|
267
|
+
|
|
268
|
+
# old way of finding defaults used contents of the files themselves
|
|
269
|
+
# we should preserve backwards compatibiliity for a few months
|
|
270
|
+
def test_get_default_org__file_missing_fallback(self, keychain, org_config):
|
|
271
|
+
org_config = OrgConfig(org_config.config.copy(), "test", keychain=keychain)
|
|
272
|
+
org_config.config["default"] = True
|
|
273
|
+
org_config.save()
|
|
274
|
+
assert (
|
|
275
|
+
_simplify_config(keychain.get_default_org()[1].config) == org_config.config
|
|
276
|
+
)
|
|
277
|
+
|
|
278
|
+
def test_get_default_org__outside_project(self, keychain):
|
|
279
|
+
assert keychain.get_default_org() == (None, None)
|
|
280
|
+
|
|
281
|
+
def test_load_orgs_from_environment(self, keychain, org_config):
|
|
282
|
+
scratch_config = org_config.config.copy()
|
|
283
|
+
scratch_config["scratch"] = True
|
|
284
|
+
env = EnvironmentVarGuard()
|
|
285
|
+
with EnvironmentVarGuard() as env:
|
|
286
|
+
env.set(
|
|
287
|
+
f"{keychain.env_org_var_prefix}dev",
|
|
288
|
+
json.dumps(scratch_config),
|
|
289
|
+
)
|
|
290
|
+
env.set(
|
|
291
|
+
f"{keychain.env_org_var_prefix}devhub",
|
|
292
|
+
json.dumps(org_config.config),
|
|
293
|
+
)
|
|
294
|
+
keychain._load_orgs_from_environment()
|
|
295
|
+
|
|
296
|
+
actual_config = keychain.get_org("dev")
|
|
297
|
+
assert _simplify_config(actual_config.config) == scratch_config
|
|
298
|
+
actual_config = keychain.get_org("devhub")
|
|
299
|
+
assert _simplify_config(actual_config.config) == org_config.config
|
|
300
|
+
|
|
301
|
+
def test_load_orgs_from_environment__empty_throws_error(self, keychain, org_config):
|
|
302
|
+
env = EnvironmentVarGuard()
|
|
303
|
+
with EnvironmentVarGuard() as env:
|
|
304
|
+
env.set(
|
|
305
|
+
f"{keychain.env_org_var_prefix}dev",
|
|
306
|
+
"",
|
|
307
|
+
)
|
|
308
|
+
with pytest.raises(OrgCannotBeLoaded):
|
|
309
|
+
keychain._load_orgs_from_environment()
|
|
310
|
+
|
|
311
|
+
def test_load_orgs_from_environment__invalid_json_throws_error(
|
|
312
|
+
self, keychain, org_config
|
|
313
|
+
):
|
|
314
|
+
env = EnvironmentVarGuard()
|
|
315
|
+
with EnvironmentVarGuard() as env:
|
|
316
|
+
env.set(
|
|
317
|
+
f"{keychain.env_org_var_prefix}dev",
|
|
318
|
+
"['foo',]",
|
|
319
|
+
)
|
|
320
|
+
with pytest.raises(OrgCannotBeLoaded):
|
|
321
|
+
keychain._load_orgs_from_environment()
|
|
322
|
+
|
|
323
|
+
#######################################
|
|
324
|
+
# Services #
|
|
325
|
+
#######################################
|
|
326
|
+
|
|
327
|
+
def test_load_service_files(self, keychain):
|
|
328
|
+
github_service_path = Path(f"{keychain.global_config_dir}/services/github")
|
|
329
|
+
self._write_file(
|
|
330
|
+
Path(github_service_path / "alias.service"),
|
|
331
|
+
keychain._get_config_bytes(BaseConfig({"name": "foo"})).decode("utf-8"),
|
|
332
|
+
)
|
|
333
|
+
|
|
334
|
+
# keychain.config["services"] = {}
|
|
335
|
+
|
|
336
|
+
with mock.patch.object(
|
|
337
|
+
EncryptedFileProjectKeychain,
|
|
338
|
+
"global_config_dir",
|
|
339
|
+
keychain.global_config_dir,
|
|
340
|
+
):
|
|
341
|
+
keychain._load_service_files()
|
|
342
|
+
github_service = keychain.get_service("github", "alias")
|
|
343
|
+
assert "foo" in github_service.config["name"]
|
|
344
|
+
|
|
345
|
+
def test_load_services__from_env(self, keychain):
|
|
346
|
+
service_config_one = ServiceConfig(
|
|
347
|
+
{"name": "foo1", "password": "1234", "token": "1234"}
|
|
348
|
+
)
|
|
349
|
+
service_config_two = ServiceConfig(
|
|
350
|
+
{"name": "foo2", "password": "5678", "token": "5678"}
|
|
351
|
+
)
|
|
352
|
+
with EnvironmentVarGuard() as env:
|
|
353
|
+
env.set(
|
|
354
|
+
f"{keychain.env_service_var_prefix}github",
|
|
355
|
+
json.dumps(service_config_one.config),
|
|
356
|
+
)
|
|
357
|
+
env.set(
|
|
358
|
+
f"{keychain.env_service_var_prefix}github__OTHER",
|
|
359
|
+
json.dumps(service_config_two.config),
|
|
360
|
+
)
|
|
361
|
+
with pytest.raises(ServiceNotConfigured):
|
|
362
|
+
keychain.get_service("github")
|
|
363
|
+
|
|
364
|
+
keychain._load_services_from_environment()
|
|
365
|
+
|
|
366
|
+
gh_service = keychain.get_service("github")
|
|
367
|
+
# this also confirms the default service is set appropriately
|
|
368
|
+
assert _simplify_config(gh_service.config) == service_config_one.config
|
|
369
|
+
gh_service = keychain.get_service("github", "env-OTHER")
|
|
370
|
+
assert _simplify_config(gh_service.config) == service_config_two.config
|
|
371
|
+
|
|
372
|
+
def test_load_services_from_env__same_name_throws_error(self, keychain):
|
|
373
|
+
keychain.logger = mock.Mock()
|
|
374
|
+
service_prefix = EncryptedFileProjectKeychain.env_service_var_prefix
|
|
375
|
+
service_config = ServiceConfig(
|
|
376
|
+
{"name": "foo", "password": "1234", "token": "1234"}
|
|
377
|
+
)
|
|
378
|
+
with EnvironmentVarGuard() as env:
|
|
379
|
+
env.set(f"{service_prefix}github", json.dumps(service_config.config))
|
|
380
|
+
env.set(f"{service_prefix}github", json.dumps(service_config.config))
|
|
381
|
+
keychain._load_services_from_environment()
|
|
382
|
+
|
|
383
|
+
assert 1 == 1
|
|
384
|
+
|
|
385
|
+
def test_load_services_from_env__empty_throws_error(self, keychain):
|
|
386
|
+
service_prefix = EncryptedFileProjectKeychain.env_service_var_prefix
|
|
387
|
+
env = EnvironmentVarGuard()
|
|
388
|
+
with EnvironmentVarGuard() as env:
|
|
389
|
+
env.set(
|
|
390
|
+
f"{service_prefix}github",
|
|
391
|
+
"",
|
|
392
|
+
)
|
|
393
|
+
with pytest.raises(ServiceCannotBeLoaded):
|
|
394
|
+
keychain._load_services_from_environment()
|
|
395
|
+
|
|
396
|
+
def test_load_services_from_env__invalid_json_throws_error(self, keychain):
|
|
397
|
+
service_prefix = EncryptedFileProjectKeychain.env_service_var_prefix
|
|
398
|
+
env = EnvironmentVarGuard()
|
|
399
|
+
with EnvironmentVarGuard() as env:
|
|
400
|
+
env.set(
|
|
401
|
+
f"{service_prefix}github",
|
|
402
|
+
"['foo',]",
|
|
403
|
+
)
|
|
404
|
+
with pytest.raises(ServiceCannotBeLoaded):
|
|
405
|
+
keychain._load_services_from_environment()
|
|
406
|
+
|
|
407
|
+
def test_get_service__built_in_connected_app(self, keychain):
|
|
408
|
+
built_in_connected_app = keychain.get_service("connected_app")
|
|
409
|
+
assert built_in_connected_app is DEFAULT_CONNECTED_APP
|
|
410
|
+
|
|
411
|
+
def test_get_service__with_class_path(self, keychain, service_config):
|
|
412
|
+
encrypted = keychain._get_config_bytes(service_config)
|
|
413
|
+
keychain.config["services"]["marketing_cloud"] = {"foo": encrypted}
|
|
414
|
+
mc_service = keychain._get_service("marketing_cloud", "foo")
|
|
415
|
+
|
|
416
|
+
assert isinstance(mc_service, MarketingCloudServiceConfig)
|
|
417
|
+
|
|
418
|
+
@mock.patch("cumulusci.core.keychain.encrypted_file_project_keychain.import_class")
|
|
419
|
+
def test_get_service__bad_class_path(self, import_class, keychain, service_config):
|
|
420
|
+
import_class.side_effect = AttributeError
|
|
421
|
+
encrypted = keychain._get_config_bytes(service_config)
|
|
422
|
+
keychain.config["services"]["marketing_cloud"] = {"foo": encrypted}
|
|
423
|
+
|
|
424
|
+
error_message = "Unrecognized class_path for service: cumulusci.core.config.marketing_cloud_service_config.MarketingCloudServiceConfig"
|
|
425
|
+
with pytest.raises(CumulusCIException, match=error_message):
|
|
426
|
+
keychain._get_service("marketing_cloud", "foo")
|
|
427
|
+
|
|
428
|
+
def test_get_service__does_not_exist(self, keychain, service_config):
|
|
429
|
+
keychain.set_service("github", "alias", service_config)
|
|
430
|
+
error_message = "No service of type github exists with the name: does-not-exist"
|
|
431
|
+
with pytest.raises(ServiceNotConfigured, match=error_message):
|
|
432
|
+
keychain.get_service("github", "does-not-exist")
|
|
433
|
+
|
|
434
|
+
@pytest.mark.skipif(
|
|
435
|
+
sys.platform.startswith("win"),
|
|
436
|
+
reason="Windows has a different value returned by os.stat",
|
|
437
|
+
)
|
|
438
|
+
def test_set_service_github(self, keychain, service_config):
|
|
439
|
+
keychain.set_service("github", "alias", service_config)
|
|
440
|
+
default_github_service = keychain.get_service("github")
|
|
441
|
+
|
|
442
|
+
service_filepath = Path(
|
|
443
|
+
keychain.global_config_dir, "services/github/alias.service"
|
|
444
|
+
)
|
|
445
|
+
assert service_filepath.is_file()
|
|
446
|
+
|
|
447
|
+
if not sys.platform.startswith("win"):
|
|
448
|
+
# ensure expected file permissions
|
|
449
|
+
stat_result = os.stat(service_filepath)
|
|
450
|
+
actual_mode = oct(stat_result.st_mode & 0o777)
|
|
451
|
+
assert actual_mode == oct(SERVICE_ORG_FILE_MODE)
|
|
452
|
+
|
|
453
|
+
assert default_github_service.config == {
|
|
454
|
+
**service_config.config,
|
|
455
|
+
"token": "test123",
|
|
456
|
+
"serialization_format": "pickle",
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
@pytest.mark.skipif(
|
|
460
|
+
not sys.platform.startswith("win"),
|
|
461
|
+
reason="Windows has a different value returned by os.stat",
|
|
462
|
+
)
|
|
463
|
+
def test_set_service_github__windows_has_correct_file_perms(
|
|
464
|
+
self, keychain, service_config
|
|
465
|
+
):
|
|
466
|
+
keychain.set_service("github", "alias", service_config)
|
|
467
|
+
|
|
468
|
+
service_filepath = Path(
|
|
469
|
+
keychain.global_config_dir, "services/github/alias.service"
|
|
470
|
+
)
|
|
471
|
+
assert service_filepath.is_file()
|
|
472
|
+
|
|
473
|
+
# ensure expected file permissions
|
|
474
|
+
stat_result = os.stat(service_filepath)
|
|
475
|
+
actual_mode = oct(stat_result.st_mode & 0o777)
|
|
476
|
+
assert actual_mode == "0o666"
|
|
477
|
+
|
|
478
|
+
def test_set_service__cannot_overwrite_default_connected_app(self, keychain):
|
|
479
|
+
connected_app_config = ServiceConfig({"test": "foo"})
|
|
480
|
+
error_message = re.escape(
|
|
481
|
+
f"You cannot use the name {DEFAULT_CONNECTED_APP_NAME} for a connected app service. Please select a different name."
|
|
482
|
+
)
|
|
483
|
+
with pytest.raises(ServiceNotValid, match=error_message):
|
|
484
|
+
keychain.set_service(
|
|
485
|
+
"connected_app", DEFAULT_CONNECTED_APP_NAME, connected_app_config
|
|
486
|
+
)
|
|
487
|
+
|
|
488
|
+
def test_set_service__first_should_be_default(self, keychain):
|
|
489
|
+
keychain.set_service("github", "foo_github", ServiceConfig({"name": "foo"}))
|
|
490
|
+
keychain.set_service("github", "bar_github", ServiceConfig({"name": "bar"}))
|
|
491
|
+
|
|
492
|
+
github_service = keychain.get_service("github")
|
|
493
|
+
assert _simplify_config(github_service.config) == {"name": "foo"}
|
|
494
|
+
|
|
495
|
+
def test_set_default_service(self, keychain, withdifferentformats):
|
|
496
|
+
keychain.set_service("github", "foo_github", ServiceConfig({"name": "foo"}))
|
|
497
|
+
keychain.set_service("github", "bar_github", ServiceConfig({"name": "bar"}))
|
|
498
|
+
|
|
499
|
+
github_service = keychain.get_service("github")
|
|
500
|
+
assert _simplify_config(github_service.config) == {"name": "foo"}
|
|
501
|
+
# now set default to bar
|
|
502
|
+
keychain.set_default_service("github", "bar_github")
|
|
503
|
+
github_service = keychain.get_service("github")
|
|
504
|
+
assert _simplify_config(github_service.config) == {"name": "bar"}
|
|
505
|
+
|
|
506
|
+
def test_set_default_service__service_alredy_default(
|
|
507
|
+
self, keychain, withdifferentformats
|
|
508
|
+
):
|
|
509
|
+
keychain.set_service("github", "foo_github", ServiceConfig({"name": "foo"}))
|
|
510
|
+
github_service = keychain.get_service("github")
|
|
511
|
+
assert github_service.config == {
|
|
512
|
+
"name": "foo",
|
|
513
|
+
"serialization_format": withdifferentformats,
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
keychain.set_default_service("github", "foo_github")
|
|
517
|
+
|
|
518
|
+
github_service = keychain.get_service("github")
|
|
519
|
+
assert github_service.config == {
|
|
520
|
+
"name": "foo",
|
|
521
|
+
"serialization_format": withdifferentformats,
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
def test_set_default_service__no_such_service(self, keychain):
|
|
525
|
+
with pytest.raises(ServiceNotConfigured):
|
|
526
|
+
keychain.set_default_service("fooey", "alias")
|
|
527
|
+
|
|
528
|
+
def test_set_default_service__no_such_alias(self, keychain):
|
|
529
|
+
keychain.set_service("github", "foo_github", ServiceConfig({"name": "foo"}))
|
|
530
|
+
with pytest.raises(ServiceNotConfigured):
|
|
531
|
+
keychain.set_default_service("github", "wrong_alias")
|
|
532
|
+
|
|
533
|
+
def test_save_default_service__global_default_service(self, keychain):
|
|
534
|
+
with open(Path(keychain.global_config_dir, "DEFAULT_SERVICES.json"), "w") as f:
|
|
535
|
+
f.write(
|
|
536
|
+
json.dumps({"devhub": "current_default", "github": "current_default"})
|
|
537
|
+
)
|
|
538
|
+
|
|
539
|
+
keychain._save_default_service("github", "new_default", project=False)
|
|
540
|
+
|
|
541
|
+
with open(Path(keychain.global_config_dir, "DEFAULT_SERVICES.json"), "r") as f:
|
|
542
|
+
default_services = json.loads(f.read())
|
|
543
|
+
|
|
544
|
+
assert default_services["devhub"] == "current_default"
|
|
545
|
+
assert default_services["github"] == "new_default"
|
|
546
|
+
|
|
547
|
+
with open(Path(keychain.project_local_dir, "DEFAULT_SERVICES.json"), "r") as f:
|
|
548
|
+
project_defaults = json.loads(f.read())
|
|
549
|
+
|
|
550
|
+
assert project_defaults == {}
|
|
551
|
+
|
|
552
|
+
def test_save_default_service__project_default_service(self, keychain):
|
|
553
|
+
with open(Path(keychain.project_local_dir, "DEFAULT_SERVICES.json"), "w") as f:
|
|
554
|
+
f.write(
|
|
555
|
+
json.dumps({"devhub": "current_default", "github": "current_default"})
|
|
556
|
+
)
|
|
557
|
+
|
|
558
|
+
keychain._save_default_service("github", "new_default", project=True)
|
|
559
|
+
|
|
560
|
+
with open(Path(keychain.project_local_dir, "DEFAULT_SERVICES.json"), "r") as f:
|
|
561
|
+
default_services = json.loads(f.read())
|
|
562
|
+
|
|
563
|
+
assert default_services["devhub"] == "current_default"
|
|
564
|
+
assert default_services["github"] == "new_default"
|
|
565
|
+
|
|
566
|
+
with open(Path(keychain.global_config_dir, "DEFAULT_SERVICES.json"), "r") as f:
|
|
567
|
+
global_defaults = json.loads(f.read())
|
|
568
|
+
|
|
569
|
+
assert global_defaults == {}
|
|
570
|
+
|
|
571
|
+
def test_iter_local_project_dirs(self, keychain):
|
|
572
|
+
cci_home_dir = Path(keychain.global_config_dir)
|
|
573
|
+
(cci_home_dir / "logs").mkdir()
|
|
574
|
+
(cci_home_dir / "chewy").mkdir()
|
|
575
|
+
(cci_home_dir / "yoshi").mkdir()
|
|
576
|
+
# services/ and TestProject/ are also presenct in ~/.cumulusci
|
|
577
|
+
|
|
578
|
+
local_project_dirs = list(keychain._iter_local_project_dirs())
|
|
579
|
+
assert cci_home_dir / "TestProject" in local_project_dirs
|
|
580
|
+
assert cci_home_dir / "chewy" in local_project_dirs
|
|
581
|
+
assert cci_home_dir / "yoshi" in local_project_dirs
|
|
582
|
+
|
|
583
|
+
def test_create_default_service_file__file_already_exists(self, key):
|
|
584
|
+
home_dir = tempfile.mkdtemp()
|
|
585
|
+
cci_home_dir = Path(f"{home_dir}/.cumulusci")
|
|
586
|
+
cci_home_dir.mkdir(parents=True)
|
|
587
|
+
local_project_dir = cci_home_dir / "test-project"
|
|
588
|
+
local_project_dir.mkdir()
|
|
589
|
+
self._write_file(cci_home_dir / "DEFAULT_SERVICES.json", json.dumps({}))
|
|
590
|
+
with mock.patch.object(
|
|
591
|
+
EncryptedFileProjectKeychain, "global_config_dir", cci_home_dir
|
|
592
|
+
):
|
|
593
|
+
keychain = EncryptedFileProjectKeychain(UniversalConfig(), key)
|
|
594
|
+
keychain._create_default_service_files()
|
|
595
|
+
|
|
596
|
+
assert not (local_project_dir / "DEFAULT_SERVICE.json").is_file()
|
|
597
|
+
|
|
598
|
+
def test_create_default_services_files__without_project_service(self, key):
|
|
599
|
+
home_dir = tempfile.mkdtemp()
|
|
600
|
+
cci_home_dir = Path(f"{home_dir}/.cumulusci")
|
|
601
|
+
cci_home_dir.mkdir(parents=True)
|
|
602
|
+
|
|
603
|
+
self._write_file(cci_home_dir / "devhub.service", "<encrypted devhub config>")
|
|
604
|
+
self._write_file(cci_home_dir / "github.service", "<encrypted github config>")
|
|
605
|
+
|
|
606
|
+
# local project dir without a .service file
|
|
607
|
+
(cci_home_dir / "test-project").mkdir()
|
|
608
|
+
# we should ignore everything in the log dir
|
|
609
|
+
log_dir = cci_home_dir / "logs"
|
|
610
|
+
log_dir.mkdir()
|
|
611
|
+
self._write_file(log_dir / "connected_app.service", "<encrypted config>")
|
|
612
|
+
|
|
613
|
+
with mock.patch.object(
|
|
614
|
+
EncryptedFileProjectKeychain, "global_config_dir", cci_home_dir
|
|
615
|
+
):
|
|
616
|
+
# _create_default_services_files() invoked via __init__
|
|
617
|
+
EncryptedFileProjectKeychain(UniversalConfig(), key)
|
|
618
|
+
|
|
619
|
+
default_services_file = cci_home_dir / "DEFAULT_SERVICES.json"
|
|
620
|
+
with open(default_services_file, "r") as f:
|
|
621
|
+
default_services = json.loads(f.read())
|
|
622
|
+
|
|
623
|
+
assert len(default_services.keys()) == 2 # we shouldn't get connected_app
|
|
624
|
+
assert default_services["devhub"] == "global"
|
|
625
|
+
assert default_services["github"] == "global"
|
|
626
|
+
|
|
627
|
+
project_default_services_file = Path(
|
|
628
|
+
f"{cci_home_dir}/test-project/DEFAULT_SERVICES.json"
|
|
629
|
+
)
|
|
630
|
+
with open(project_default_services_file, "r") as f:
|
|
631
|
+
assert json.loads(f.read()) == {}
|
|
632
|
+
|
|
633
|
+
def test_create_default_services_files__with_project_service(self, key):
|
|
634
|
+
home_dir = tempfile.mkdtemp()
|
|
635
|
+
cci_home_dir = Path(f"{home_dir}/.cumulusci")
|
|
636
|
+
cci_home_dir.mkdir(parents=True)
|
|
637
|
+
|
|
638
|
+
self._write_file(cci_home_dir / "devhub.service", "<encrypted devhub config>")
|
|
639
|
+
self._write_file(cci_home_dir / "github.service", "<encrypted github config>")
|
|
640
|
+
|
|
641
|
+
project_path = cci_home_dir / "test-project"
|
|
642
|
+
project_path.mkdir(parents=True)
|
|
643
|
+
self._write_file(project_path / "github.service", "project level github config")
|
|
644
|
+
|
|
645
|
+
with mock.patch.object(
|
|
646
|
+
EncryptedFileProjectKeychain, "global_config_dir", cci_home_dir
|
|
647
|
+
):
|
|
648
|
+
with mock.patch.object(
|
|
649
|
+
EncryptedFileProjectKeychain, "project_local_dir", project_path
|
|
650
|
+
):
|
|
651
|
+
# _create_default_services_files invoked via __init__
|
|
652
|
+
EncryptedFileProjectKeychain(UniversalConfig(), key)
|
|
653
|
+
|
|
654
|
+
global_default_services_file = Path(f"{cci_home_dir}/DEFAULT_SERVICES.json")
|
|
655
|
+
assert global_default_services_file.is_file()
|
|
656
|
+
with open(global_default_services_file, "r") as f:
|
|
657
|
+
global_defaults = json.loads(f.read())
|
|
658
|
+
|
|
659
|
+
assert len(global_defaults.keys()) == 2
|
|
660
|
+
assert global_defaults["github"] == "global"
|
|
661
|
+
assert global_defaults["devhub"] == "global"
|
|
662
|
+
|
|
663
|
+
project_default_services_file = project_path / "DEFAULT_SERVICES.json"
|
|
664
|
+
assert project_default_services_file.is_file()
|
|
665
|
+
with open(project_default_services_file, "r") as f:
|
|
666
|
+
default_services = json.loads(f.read())
|
|
667
|
+
|
|
668
|
+
assert len(default_services.keys()) == 1
|
|
669
|
+
assert default_services["github"] == "test-project"
|
|
670
|
+
|
|
671
|
+
def test_create_services_dir_structure(self, key):
|
|
672
|
+
service_types = list(UniversalConfig().config["services"].keys())
|
|
673
|
+
num_services = len(service_types)
|
|
674
|
+
|
|
675
|
+
# _create_services_dir_structure() is invoked via constructor
|
|
676
|
+
keychain = EncryptedFileProjectKeychain(UniversalConfig(), key)
|
|
677
|
+
|
|
678
|
+
services_path = Path(f"{keychain.global_config_dir}/services")
|
|
679
|
+
for path in Path.iterdir(services_path):
|
|
680
|
+
if path.name in service_types:
|
|
681
|
+
assert Path.is_dir(path)
|
|
682
|
+
service_types.remove(path.name)
|
|
683
|
+
|
|
684
|
+
assert len(service_types) == 0
|
|
685
|
+
|
|
686
|
+
# explicitly invoke a second time to test idempotency
|
|
687
|
+
keychain._create_services_dir_structure()
|
|
688
|
+
# make sure no new dirs appeared
|
|
689
|
+
assert num_services == len(list(Path.iterdir(services_path)))
|
|
690
|
+
|
|
691
|
+
def test_migrate_services(self, key, project_config):
|
|
692
|
+
home_dir = tempfile.mkdtemp()
|
|
693
|
+
cci_home_dir = Path(f"{home_dir}/.cumulusci")
|
|
694
|
+
cci_home_dir.mkdir(parents=True)
|
|
695
|
+
|
|
696
|
+
self._write_file(cci_home_dir / "github.service", "github config")
|
|
697
|
+
self._write_file(cci_home_dir / "foo.service", "foo config")
|
|
698
|
+
|
|
699
|
+
local_proj_dir = cci_home_dir / "test-project"
|
|
700
|
+
local_proj_dir.mkdir()
|
|
701
|
+
self._write_file(local_proj_dir / "github.service", "github2 config")
|
|
702
|
+
|
|
703
|
+
with mock.patch.object(
|
|
704
|
+
EncryptedFileProjectKeychain, "global_config_dir", cci_home_dir
|
|
705
|
+
):
|
|
706
|
+
# _migrade_services() invoked via __init__
|
|
707
|
+
EncryptedFileProjectKeychain(project_config, key)
|
|
708
|
+
|
|
709
|
+
assert not Path.is_file(cci_home_dir / "github.service")
|
|
710
|
+
|
|
711
|
+
new_github_service_file = cci_home_dir / "services/github/global.service"
|
|
712
|
+
assert new_github_service_file.is_file()
|
|
713
|
+
|
|
714
|
+
# ensure expected file contents
|
|
715
|
+
with open(cci_home_dir / "services/github/global.service") as f:
|
|
716
|
+
assert f.read() == "github config"
|
|
717
|
+
|
|
718
|
+
assert not Path.is_file(cci_home_dir / "test-project/devhub.service")
|
|
719
|
+
assert (cci_home_dir / "services/github/TestProject.service").is_file()
|
|
720
|
+
with open(cci_home_dir / "services/github/TestProject.service") as f:
|
|
721
|
+
assert f.read() == "github2 config"
|
|
722
|
+
|
|
723
|
+
# unrecognized services should be left alone
|
|
724
|
+
assert (cci_home_dir / "foo.service").is_file()
|
|
725
|
+
|
|
726
|
+
def test_migrate_services__warn_duplicate_default_service(
|
|
727
|
+
self, project_config, key
|
|
728
|
+
):
|
|
729
|
+
home_dir = tempfile.mkdtemp()
|
|
730
|
+
cci_home_dir = Path(f"{home_dir}/.cumulusci")
|
|
731
|
+
cci_home_dir.mkdir(parents=True)
|
|
732
|
+
|
|
733
|
+
# make unnamed devhub service
|
|
734
|
+
legacy_devhub_service = Path(f"{cci_home_dir}/devhub.service")
|
|
735
|
+
self._write_file(legacy_devhub_service, "legacy config")
|
|
736
|
+
# make existing default aliased devhub service
|
|
737
|
+
named_devhub_service = Path(f"{cci_home_dir}/services/devhub/")
|
|
738
|
+
named_devhub_service.mkdir(parents=True)
|
|
739
|
+
self._write_file(f"{named_devhub_service}/global.service", "migrated config")
|
|
740
|
+
|
|
741
|
+
with mock.patch.object(
|
|
742
|
+
EncryptedFileProjectKeychain, "global_config_dir", cci_home_dir
|
|
743
|
+
):
|
|
744
|
+
# _migrate_services() invoked via __init__
|
|
745
|
+
keychain = EncryptedFileProjectKeychain(project_config, key)
|
|
746
|
+
keychain._migrate_services_from_dir(cci_home_dir)
|
|
747
|
+
|
|
748
|
+
# ensure this file wasn't removed
|
|
749
|
+
assert legacy_devhub_service.is_file()
|
|
750
|
+
# ensure contents of migrated are unchanged
|
|
751
|
+
with open(named_devhub_service / "global.service", "r") as f:
|
|
752
|
+
assert f.read() == "migrated config"
|
|
753
|
+
|
|
754
|
+
def test_rename_service(self, keychain, service_config, withdifferentformats):
|
|
755
|
+
home_dir = tempfile.mkdtemp()
|
|
756
|
+
|
|
757
|
+
cci_home_dir = Path(f"{home_dir}/.cumulusci")
|
|
758
|
+
cci_home_dir.mkdir()
|
|
759
|
+
with open(cci_home_dir / "DEFAULT_SERVICES.json", "w") as f:
|
|
760
|
+
f.write(json.dumps({"github": "old_alias"}))
|
|
761
|
+
|
|
762
|
+
local_project_dir = cci_home_dir / "test-project"
|
|
763
|
+
local_project_dir.mkdir()
|
|
764
|
+
with open(local_project_dir / "DEFAULT_SERVICES.json", "w") as f:
|
|
765
|
+
f.write(json.dumps({"github": "old_alias"}))
|
|
766
|
+
|
|
767
|
+
github_services_dir = Path(f"{home_dir}/.cumulusci/services/github")
|
|
768
|
+
github_services_dir.mkdir(parents=True)
|
|
769
|
+
self._write_file(github_services_dir / "old_alias.service", "github config")
|
|
770
|
+
|
|
771
|
+
encrypted = keychain._get_config_bytes(service_config)
|
|
772
|
+
keychain.services = {"github": {"old_alias": encrypted}}
|
|
773
|
+
with mock.patch.object(
|
|
774
|
+
EncryptedFileProjectKeychain, "global_config_dir", cci_home_dir
|
|
775
|
+
):
|
|
776
|
+
keychain.rename_service("github", "old_alias", "new_alias")
|
|
777
|
+
|
|
778
|
+
# Getting old alias should fail
|
|
779
|
+
with pytest.raises(ServiceNotConfigured):
|
|
780
|
+
keychain.get_service("github", "old_alias")
|
|
781
|
+
|
|
782
|
+
# Validate new alias has same contents as original
|
|
783
|
+
assert keychain.get_service("github", "new_alias").config == {
|
|
784
|
+
**service_config.config,
|
|
785
|
+
"token": "test123",
|
|
786
|
+
"serialization_format": withdifferentformats,
|
|
787
|
+
}
|
|
788
|
+
# Old service file should be gone
|
|
789
|
+
assert not (github_services_dir / "old_alias.service").is_file()
|
|
790
|
+
# New service file should be present
|
|
791
|
+
assert (github_services_dir / "new_alias.service").is_file()
|
|
792
|
+
|
|
793
|
+
# DEFAULT_SERVICES.json files should have updated aliases
|
|
794
|
+
with open(local_project_dir / "DEFAULT_SERVICES.json", "r") as f:
|
|
795
|
+
assert json.loads(f.read()) == {"github": "new_alias"}
|
|
796
|
+
|
|
797
|
+
with open(cci_home_dir / "DEFAULT_SERVICES.json", "r") as f:
|
|
798
|
+
assert json.loads(f.read()) == {"github": "new_alias"}
|
|
799
|
+
|
|
800
|
+
def test_rename_service__new_alias_already_exists(self, keychain):
|
|
801
|
+
keychain.services = {
|
|
802
|
+
"github": {"old-alias": "old config", "new-alias": "new config"}
|
|
803
|
+
}
|
|
804
|
+
with pytest.raises(
|
|
805
|
+
CumulusCIUsageError,
|
|
806
|
+
match="A service of type github already exists with name: new-alias",
|
|
807
|
+
):
|
|
808
|
+
keychain.rename_service("github", "old-alias", "new-alias")
|
|
809
|
+
|
|
810
|
+
def test_rename_service__invalid_service_type(self, keychain, service_config):
|
|
811
|
+
keychain.services = {"github": {"alias": "config"}}
|
|
812
|
+
with pytest.raises(ServiceNotConfigured):
|
|
813
|
+
keychain.rename_service("does-not-exist", "old-alias", "new-alias")
|
|
814
|
+
|
|
815
|
+
def test_rename_service__invalid_service_alias(self, keychain, service_config):
|
|
816
|
+
keychain.services = {"github": {"current_alias": "some config"}}
|
|
817
|
+
with pytest.raises(ServiceNotConfigured):
|
|
818
|
+
keychain.rename_service("github", "does-not-exist", "new_alias")
|
|
819
|
+
|
|
820
|
+
def test_rename_alias_in_default_service_file__no_default_present(self, keychain):
|
|
821
|
+
filepath = Path(f"{keychain.global_config_dir}/DEFAULT_SERVICES.json")
|
|
822
|
+
with open(filepath, "w") as f:
|
|
823
|
+
f.write(json.dumps({"saucelabs": "default_alias"}))
|
|
824
|
+
keychain._rename_alias_in_default_service_file(
|
|
825
|
+
filepath, "github", "current_alias", "new_alias"
|
|
826
|
+
)
|
|
827
|
+
|
|
828
|
+
with open(filepath, "r") as f:
|
|
829
|
+
assert json.loads(f.read()) == {"saucelabs": "default_alias"}
|
|
830
|
+
|
|
831
|
+
def test_cannot_rename_cumulusci_default_connected_app(self, keychain):
|
|
832
|
+
error_message = (
|
|
833
|
+
"You cannot rename the connected app service that is provided by CumulusCI."
|
|
834
|
+
)
|
|
835
|
+
with pytest.raises(CumulusCIException, match=error_message):
|
|
836
|
+
keychain.rename_service(
|
|
837
|
+
"connected_app", DEFAULT_CONNECTED_APP_NAME, "new_alias"
|
|
838
|
+
)
|
|
839
|
+
|
|
840
|
+
def test_remove_service(self, keychain, service_config):
|
|
841
|
+
home_dir = tempfile.mkdtemp()
|
|
842
|
+
|
|
843
|
+
cci_home_dir = Path(f"{home_dir}/.cumulusci")
|
|
844
|
+
cci_home_dir.mkdir()
|
|
845
|
+
with open(cci_home_dir / "DEFAULT_SERVICES.json", "w") as f:
|
|
846
|
+
f.write(json.dumps({"github": "alias"}))
|
|
847
|
+
|
|
848
|
+
local_project_dir = cci_home_dir / "test-project"
|
|
849
|
+
local_project_dir.mkdir()
|
|
850
|
+
with open(local_project_dir / "DEFAULT_SERVICES.json", "w") as f:
|
|
851
|
+
f.write(json.dumps({"github": "alias"}))
|
|
852
|
+
|
|
853
|
+
github_services_dir = Path(f"{home_dir}/.cumulusci/services/github")
|
|
854
|
+
github_services_dir.mkdir(parents=True)
|
|
855
|
+
self._write_file(github_services_dir / "alias.service", "github config")
|
|
856
|
+
|
|
857
|
+
encrypted = keychain._get_config_bytes(service_config)
|
|
858
|
+
keychain.services = {"github": {"alias": encrypted}}
|
|
859
|
+
keychain._default_services["github"] = "alias"
|
|
860
|
+
with mock.patch.object(
|
|
861
|
+
EncryptedFileProjectKeychain, "global_config_dir", cci_home_dir
|
|
862
|
+
):
|
|
863
|
+
keychain.remove_service("github", "alias")
|
|
864
|
+
|
|
865
|
+
# loaded service is removed
|
|
866
|
+
assert "old-alias" not in keychain.services["github"]
|
|
867
|
+
# corresponding .service file is gone
|
|
868
|
+
assert not (github_services_dir / "alias.service").is_file()
|
|
869
|
+
# references in DEFAULT_SERVICES.json are gone
|
|
870
|
+
with open(cci_home_dir / "DEFAULT_SERVICES.json", "r") as f:
|
|
871
|
+
assert json.loads(f.read()) == {}
|
|
872
|
+
with open(local_project_dir / "DEFAULT_SERVICES.json", "r") as f:
|
|
873
|
+
assert json.loads(f.read()) == {}
|
|
874
|
+
# default service is unset
|
|
875
|
+
assert "github" not in keychain._default_services
|
|
876
|
+
|
|
877
|
+
def test_remove_service__other_set_as_default(self, keychain, service_config):
|
|
878
|
+
home_dir = tempfile.mkdtemp()
|
|
879
|
+
|
|
880
|
+
cci_home_dir = Path(f"{home_dir}/.cumulusci")
|
|
881
|
+
cci_home_dir.mkdir()
|
|
882
|
+
with open(cci_home_dir / "DEFAULT_SERVICES.json", "w") as f:
|
|
883
|
+
f.write(json.dumps({"github": "alias"}))
|
|
884
|
+
|
|
885
|
+
local_project_dir = cci_home_dir / "test-project"
|
|
886
|
+
local_project_dir.mkdir()
|
|
887
|
+
with open(local_project_dir / "DEFAULT_SERVICES.json", "w") as f:
|
|
888
|
+
f.write(json.dumps({"github": "alias"}))
|
|
889
|
+
|
|
890
|
+
github_services_dir = Path(f"{home_dir}/.cumulusci/services/github")
|
|
891
|
+
github_services_dir.mkdir(parents=True)
|
|
892
|
+
self._write_file(github_services_dir / "alias.service", "github config")
|
|
893
|
+
|
|
894
|
+
encrypted = keychain._get_config_bytes(service_config)
|
|
895
|
+
keychain.services = {"github": {"alias": encrypted, "other-service": encrypted}}
|
|
896
|
+
keychain._default_services["github"] = "alias"
|
|
897
|
+
with mock.patch.object(
|
|
898
|
+
EncryptedFileProjectKeychain, "global_config_dir", cci_home_dir
|
|
899
|
+
):
|
|
900
|
+
keychain.remove_service("github", "alias")
|
|
901
|
+
|
|
902
|
+
# the one other service should be the default
|
|
903
|
+
assert keychain._default_services["github"] == "other-service"
|
|
904
|
+
|
|
905
|
+
def test_remove_service__cannot_remove_default_connected_app(self, keychain):
|
|
906
|
+
error_message = (
|
|
907
|
+
f"Unable to remove connected app service: {DEFAULT_CONNECTED_APP_NAME}. "
|
|
908
|
+
"This connected app is provided by CumulusCI and cannot be removed."
|
|
909
|
+
)
|
|
910
|
+
with pytest.raises(CumulusCIException, match=error_message):
|
|
911
|
+
keychain.remove_service("connected_app", DEFAULT_CONNECTED_APP_NAME)
|
|
912
|
+
|
|
913
|
+
def test_load_default_connected_app(self, keychain):
|
|
914
|
+
keychain._load_default_connected_app()
|
|
915
|
+
assert (
|
|
916
|
+
DEFAULT_CONNECTED_APP_NAME in keychain.config["services"]["connected_app"]
|
|
917
|
+
)
|
|
918
|
+
assert (
|
|
919
|
+
keychain.config["services"]["connected_app"][DEFAULT_CONNECTED_APP_NAME]
|
|
920
|
+
== DEFAULT_CONNECTED_APP
|
|
921
|
+
)
|
|
922
|
+
|
|
923
|
+
def test_default_connected_app_should_be_default_after_loading(self, keychain):
|
|
924
|
+
keychain._load_default_connected_app()
|
|
925
|
+
assert keychain._default_services["connected_app"] == DEFAULT_CONNECTED_APP_NAME
|
|
926
|
+
|
|
927
|
+
def test_load_default_services(self, keychain):
|
|
928
|
+
expected_defaults = {
|
|
929
|
+
"github": "github_alias",
|
|
930
|
+
"metaci": "metaci_alias",
|
|
931
|
+
"devhub": "devhub_alias",
|
|
932
|
+
"connected_app": "connected_app__alias",
|
|
933
|
+
}
|
|
934
|
+
filepath = Path(f"{keychain.global_config_dir}/DEFAULT_SERVICES.json")
|
|
935
|
+
with open(filepath, "w") as f:
|
|
936
|
+
f.write(json.dumps(expected_defaults))
|
|
937
|
+
|
|
938
|
+
keychain._load_default_services()
|
|
939
|
+
assert keychain._default_services["connected_app"] != DEFAULT_CONNECTED_APP_NAME
|
|
940
|
+
assert keychain._default_services == expected_defaults
|
|
941
|
+
|
|
942
|
+
def test_read_default_services(self, keychain):
|
|
943
|
+
expected_defaults = {
|
|
944
|
+
"github": "github_alias",
|
|
945
|
+
"metaci": "metaci_alias",
|
|
946
|
+
"devhub": "devhub_alias",
|
|
947
|
+
"connected_app": "connected_app__alias",
|
|
948
|
+
}
|
|
949
|
+
filepath = Path(f"{keychain.global_config_dir}/DEFAULT_SERVICES.json")
|
|
950
|
+
with open(filepath, "w") as f:
|
|
951
|
+
f.write(json.dumps(expected_defaults))
|
|
952
|
+
|
|
953
|
+
actual_defaults = keychain._read_default_services(filepath)
|
|
954
|
+
assert actual_defaults == expected_defaults
|
|
955
|
+
|
|
956
|
+
def test_read_default_services__file_does_not_exist(self, keychain):
|
|
957
|
+
assert keychain._read_default_services(Path("not-a-valid-filepath")) == {}
|
|
958
|
+
|
|
959
|
+
def test_write_default_services(self, keychain):
|
|
960
|
+
expected_defaults = {
|
|
961
|
+
"github": "github_alias",
|
|
962
|
+
"apextestdb": "apextestdb_alias",
|
|
963
|
+
"metaci": "metaci_alias",
|
|
964
|
+
"devhub": "devhub_alias",
|
|
965
|
+
"connected_app": "connected_app__alias",
|
|
966
|
+
}
|
|
967
|
+
filepath = Path(f"{keychain.global_config_dir}/DEFAULT_SERVICES.json")
|
|
968
|
+
keychain._write_default_services(filepath, expected_defaults)
|
|
969
|
+
|
|
970
|
+
with open(filepath, "r") as f:
|
|
971
|
+
actual_defaults = json.loads(f.read())
|
|
972
|
+
assert actual_defaults == expected_defaults
|
|
973
|
+
|
|
974
|
+
def test_write_default_services__bad_filename(self, keychain):
|
|
975
|
+
filepath = Path("DEFAULT_THINGS.json")
|
|
976
|
+
with pytest.raises(CumulusCIException):
|
|
977
|
+
keychain._write_default_services(filepath, {})
|
|
978
|
+
|
|
979
|
+
def test_decrypt_config__no_config(self, keychain):
|
|
980
|
+
config = keychain._decrypt_config(OrgConfig, None, extra=["test", keychain])
|
|
981
|
+
assert config.__class__ == OrgConfig
|
|
982
|
+
assert config.config == {}
|
|
983
|
+
assert config.keychain == keychain
|
|
984
|
+
|
|
985
|
+
def test_decrypt_config__no_config_2(self, keychain):
|
|
986
|
+
config = keychain._decrypt_config(BaseConfig, None)
|
|
987
|
+
assert config.__class__ == BaseConfig
|
|
988
|
+
assert config.config == {}
|
|
989
|
+
|
|
990
|
+
def test_decrypt_config__Python_2_warning(self, keychain, caplog):
|
|
991
|
+
config = keychain.cleanup_Python_2_configs({"a": "b"})
|
|
992
|
+
assert config == {"a": "b"}
|
|
993
|
+
assert len(caplog.records) == 0
|
|
994
|
+
|
|
995
|
+
config = keychain.cleanup_Python_2_configs({"a": b"b", b"c": "d"})
|
|
996
|
+
assert config == {"a": "b", "c": "d"}
|
|
997
|
+
assert len(caplog.records) == 2
|
|
998
|
+
|
|
999
|
+
def test_decrypt_config__wrong_key(self, keychain, org_config):
|
|
1000
|
+
keychain.set_org(org_config, False)
|
|
1001
|
+
keychain.key = "x" * 16
|
|
1002
|
+
with pytest.raises(KeychainKeyNotFound):
|
|
1003
|
+
keychain.get_org("test")
|
|
1004
|
+
|
|
1005
|
+
def test_validate_key__wrong_length(self, project_config):
|
|
1006
|
+
with pytest.raises(ConfigError):
|
|
1007
|
+
EncryptedFileProjectKeychain(project_config, "1")
|
|
1008
|
+
|
|
1009
|
+
def test_validate_key__no_key(self, project_config):
|
|
1010
|
+
keychain = EncryptedFileProjectKeychain(project_config, None)
|
|
1011
|
+
assert keychain._validate_key() is None
|
|
1012
|
+
|
|
1013
|
+
def test_construct_config(self, keychain):
|
|
1014
|
+
result = keychain._construct_config(
|
|
1015
|
+
None, [{"scratch": "scratch org"}, "org_name"]
|
|
1016
|
+
)
|
|
1017
|
+
assert isinstance(result, ScratchOrgConfig)
|
|
1018
|
+
result = keychain._construct_config(None, [{"sfdx": True}, "org_name"])
|
|
1019
|
+
assert isinstance(result, SfdxOrgConfig)
|
|
1020
|
+
|
|
1021
|
+
def test_new_service_type_creates_expected_directory(
|
|
1022
|
+
self, keychain, service_config
|
|
1023
|
+
):
|
|
1024
|
+
home_dir = tempfile.mkdtemp()
|
|
1025
|
+
cci_home_dir = Path(f"{home_dir}/.cumulusci")
|
|
1026
|
+
cci_home_dir.mkdir()
|
|
1027
|
+
services_dir = cci_home_dir / "services"
|
|
1028
|
+
services_dir.mkdir()
|
|
1029
|
+
|
|
1030
|
+
encrypted = keychain._get_config_bytes(service_config)
|
|
1031
|
+
with mock.patch.object(
|
|
1032
|
+
EncryptedFileProjectKeychain, "global_config_dir", cci_home_dir
|
|
1033
|
+
):
|
|
1034
|
+
keychain._save_encrypted_service("new_service_type", "alias", encrypted)
|
|
1035
|
+
|
|
1036
|
+
assert (services_dir / "new_service_type").is_dir()
|
|
1037
|
+
|
|
1038
|
+
@pytest.mark.parametrize(
|
|
1039
|
+
"val, expected",
|
|
1040
|
+
(
|
|
1041
|
+
("CUMULUSCI_SERVICE_github", ("github", "env")),
|
|
1042
|
+
("CUMULUSCI_SERVICE_github__alias", ("github", "env-alias")),
|
|
1043
|
+
("CUMULUSCI_SERVICE_connected_app", ("connected_app", "env")),
|
|
1044
|
+
("CUMULUSCI_SERVICE_connected_app__alias", ("connected_app", "env-alias")),
|
|
1045
|
+
),
|
|
1046
|
+
)
|
|
1047
|
+
def test_get_env_service_type_and_name(self, val, expected, project_config):
|
|
1048
|
+
keychain = EncryptedFileProjectKeychain(project_config, "0123456789abcdef")
|
|
1049
|
+
actual_type, actual_name = keychain._get_env_service_type_and_name(val)
|
|
1050
|
+
assert (actual_type, actual_name) == expected
|
|
1051
|
+
|
|
1052
|
+
def test_backwards_compatability_with_EnvironmentProjectKeychain(
|
|
1053
|
+
self, project_config, key
|
|
1054
|
+
):
|
|
1055
|
+
"""Ensure we don't break backwards compatability for people still using EnvironmentProjectKeychain"""
|
|
1056
|
+
from cumulusci.core.keychain.environment_project_keychain import (
|
|
1057
|
+
EnvironmentProjectKeychain,
|
|
1058
|
+
)
|
|
1059
|
+
|
|
1060
|
+
assert EnvironmentProjectKeychain is EncryptedFileProjectKeychain
|
|
1061
|
+
|
|
1062
|
+
|
|
1063
|
+
def _touch_test_org_file(directory):
|
|
1064
|
+
org_dir = directory / "orginfo/something.something.saleforce.com"
|
|
1065
|
+
org_dir.mkdir(parents=True)
|
|
1066
|
+
(org_dir / "testfile.json").touch()
|
|
1067
|
+
return org_dir
|
|
1068
|
+
|
|
1069
|
+
|
|
1070
|
+
class TestCleanupOrgCacheDir:
|
|
1071
|
+
def test_cleanup_cache_dir(self, keychain):
|
|
1072
|
+
keychain.set_org(
|
|
1073
|
+
OrgConfig({"instance_url": "http://foo.my.salesforce.com/"}, "dev"), False
|
|
1074
|
+
)
|
|
1075
|
+
keychain.set_org(
|
|
1076
|
+
OrgConfig({"instance_url": "http://bar.my.salesforce.com/"}, "qa"), False
|
|
1077
|
+
)
|
|
1078
|
+
|
|
1079
|
+
temp_for_global = tempfile.mkdtemp()
|
|
1080
|
+
with mock.patch.object(
|
|
1081
|
+
EncryptedFileProjectKeychain, "global_config_dir", Path(temp_for_global)
|
|
1082
|
+
):
|
|
1083
|
+
global_org_dir = _touch_test_org_file(keychain.global_config_dir)
|
|
1084
|
+
temp_for_project = tempfile.mkdtemp()
|
|
1085
|
+
keychain.project_config = mock.Mock()
|
|
1086
|
+
|
|
1087
|
+
cache_dir = keychain.project_config.cache_dir = Path(temp_for_project)
|
|
1088
|
+
project_org_dir = _touch_test_org_file(cache_dir)
|
|
1089
|
+
with mock.patch(
|
|
1090
|
+
"cumulusci.core.keychain.encrypted_file_project_keychain.rmtree"
|
|
1091
|
+
) as rmtree:
|
|
1092
|
+
keychain.cleanup_org_cache_dirs()
|
|
1093
|
+
rmtree.assert_has_calls(
|
|
1094
|
+
[mock.call(global_org_dir), mock.call(project_org_dir)],
|
|
1095
|
+
any_order=True,
|
|
1096
|
+
)
|
|
1097
|
+
|
|
1098
|
+
def test_cleanup_cache_dir__no_project_config(self, keychain):
|
|
1099
|
+
keychain.project_config = None
|
|
1100
|
+
with mock.patch(
|
|
1101
|
+
"cumulusci.core.keychain.encrypted_file_project_keychain.rmtree"
|
|
1102
|
+
) as rmtree:
|
|
1103
|
+
keychain.cleanup_org_cache_dirs()
|
|
1104
|
+
assert not rmtree.mock_calls, rmtree.mock_calls
|
|
1105
|
+
|
|
1106
|
+
def test_cleanup_cache_dir_nothing_to_cleanup(self, keychain):
|
|
1107
|
+
keychain.set_org(
|
|
1108
|
+
OrgConfig({"instance_url": "http://foo.my.salesforce.com/"}, "dev"), False
|
|
1109
|
+
)
|
|
1110
|
+
|
|
1111
|
+
keychain.project_config = mock.Mock()
|
|
1112
|
+
temp_for_global = tempfile.mkdtemp()
|
|
1113
|
+
with mock.patch.object(
|
|
1114
|
+
EncryptedFileProjectKeychain, "global_config_dir", Path(temp_for_global)
|
|
1115
|
+
):
|
|
1116
|
+
temp_for_project = tempfile.mkdtemp()
|
|
1117
|
+
cache_dir = keychain.project_config.cache_dir = Path(temp_for_project)
|
|
1118
|
+
org_dir = cache_dir / "orginfo/foo.my.salesforce.com"
|
|
1119
|
+
org_dir.mkdir(parents=True)
|
|
1120
|
+
(org_dir / "schema.json").touch()
|
|
1121
|
+
with mock.patch(
|
|
1122
|
+
"cumulusci.core.keychain.encrypted_file_project_keychain.rmtree"
|
|
1123
|
+
) as rmtree:
|
|
1124
|
+
keychain.cleanup_org_cache_dirs()
|
|
1125
|
+
assert not rmtree.mock_calls, rmtree.mock_calls
|
|
1126
|
+
|
|
1127
|
+
duration = (
|
|
1128
|
+
(59, "59s"),
|
|
1129
|
+
(70, "1m:10s"),
|
|
1130
|
+
(119, "1m:59s"),
|
|
1131
|
+
(65, "1m:5s"),
|
|
1132
|
+
(4000, "1h:6m:40s"),
|
|
1133
|
+
(7199, "1h:59m:59s"),
|
|
1134
|
+
)
|
|
1135
|
+
|
|
1136
|
+
@pytest.mark.parametrize("val,expected", duration)
|
|
1137
|
+
def test_time_delta(self, val, expected):
|
|
1138
|
+
formatted = utils.format_duration(datetime.timedelta(seconds=val))
|
|
1139
|
+
assert formatted == expected, (formatted, expected)
|
|
1140
|
+
|
|
1141
|
+
def test_set_and_get_org_with_dates__json(
|
|
1142
|
+
self, keychain, org_config, key, withdifferentformats
|
|
1143
|
+
):
|
|
1144
|
+
org_config.global_org = True
|
|
1145
|
+
keychain.key = key
|
|
1146
|
+
|
|
1147
|
+
custom_datetime = datetime.datetime.now()
|
|
1148
|
+
custom_date = datetime.datetime.now().date()
|
|
1149
|
+
org_config.config["custom_datetime"] = custom_datetime
|
|
1150
|
+
org_config.config["custom_date"] = custom_date
|
|
1151
|
+
|
|
1152
|
+
keychain.set_org(org_config, True)
|
|
1153
|
+
assert list(keychain.orgs.keys()) == ["test"]
|
|
1154
|
+
config = keychain.get_org("test").config
|
|
1155
|
+
assert config == {
|
|
1156
|
+
**org_config.config,
|
|
1157
|
+
"serialization_format": withdifferentformats,
|
|
1158
|
+
}
|
|
1159
|
+
assert config["custom_datetime"] == custom_datetime
|
|
1160
|
+
assert config["custom_date"] == custom_date
|
|
1161
|
+
|
|
1162
|
+
@mock.patch("cumulusci.core.keychain.serialization.SHOULD_SAVE_AS_JSON", True)
|
|
1163
|
+
def test_set_and_get_org_with_bad_datatypes(self, keychain, org_config, key):
|
|
1164
|
+
org_config.global_org = True
|
|
1165
|
+
keychain.key = None
|
|
1166
|
+
with mock.patch("pickle.dumps") as dumps:
|
|
1167
|
+
dumps.return_value = b"xx"
|
|
1168
|
+
org_config.config["good"] = 25
|
|
1169
|
+
|
|
1170
|
+
keychain.set_org(org_config, True)
|
|
1171
|
+
assert not dumps.mock_calls
|
|
1172
|
+
|
|
1173
|
+
org_config.config["bad"] = 25j
|
|
1174
|
+
|
|
1175
|
+
keychain.set_org(org_config, True)
|
|
1176
|
+
dumps.assert_called_once_with(
|
|
1177
|
+
{"foo": "bar", "good": 25, "bad": 25j}, protocol=mock.ANY
|
|
1178
|
+
)
|
|
1179
|
+
|
|
1180
|
+
def test_set_and_get_service_with_dates__global(
|
|
1181
|
+
self, keychain, key, withdifferentformats
|
|
1182
|
+
):
|
|
1183
|
+
service_config = ServiceConfig(
|
|
1184
|
+
{"name": "foo1", "password": "1234", "token": "1234"}
|
|
1185
|
+
)
|
|
1186
|
+
|
|
1187
|
+
keychain.key = key
|
|
1188
|
+
|
|
1189
|
+
custom_datetime = datetime.datetime.now()
|
|
1190
|
+
custom_date = datetime.datetime.now().date()
|
|
1191
|
+
service_config.config["custom_datetime"] = custom_datetime
|
|
1192
|
+
service_config.config["custom_date"] = custom_date
|
|
1193
|
+
|
|
1194
|
+
keychain.set_service("github", "alias", service_config)
|
|
1195
|
+
config = keychain.get_service("github").config
|
|
1196
|
+
assert config == {
|
|
1197
|
+
**service_config.config,
|
|
1198
|
+
"serialization_format": withdifferentformats,
|
|
1199
|
+
}
|
|
1200
|
+
assert config["custom_datetime"] == custom_datetime
|
|
1201
|
+
assert config["custom_date"] == custom_date
|
|
1202
|
+
|
|
1203
|
+
def test_migration_pickle_to_json(
|
|
1204
|
+
self, keychain, patch_home_and_env, project_config
|
|
1205
|
+
):
|
|
1206
|
+
# This all runs in fake-home, not real home.
|
|
1207
|
+
with mock.patch(
|
|
1208
|
+
"cumulusci.core.keychain.serialization.SHOULD_SAVE_AS_JSON", False
|
|
1209
|
+
):
|
|
1210
|
+
org_config = OrgConfig(
|
|
1211
|
+
{"password": "Kltpzyxm"}, "test_migration", keychain=keychain
|
|
1212
|
+
)
|
|
1213
|
+
org_config.save()
|
|
1214
|
+
assert Path(keychain.project_local_dir, "test_migration.org").exists()
|
|
1215
|
+
del keychain.config["orgs"]
|
|
1216
|
+
keychain._load_orgs()
|
|
1217
|
+
assert keychain.get_org("test_migration").password == "Kltpzyxm"
|
|
1218
|
+
assert keychain.get_org("test_migration").serialization_format == "pickle"
|
|
1219
|
+
|
|
1220
|
+
with mock.patch(
|
|
1221
|
+
"cumulusci.core.keychain.serialization.SHOULD_SAVE_AS_JSON", True
|
|
1222
|
+
):
|
|
1223
|
+
org_config.save()
|
|
1224
|
+
assert Path(keychain.project_local_dir, "test_migration.org").exists()
|
|
1225
|
+
del keychain.config["orgs"]
|
|
1226
|
+
keychain._load_orgs()
|
|
1227
|
+
assert keychain.get_org("test_migration").password == "Kltpzyxm"
|
|
1228
|
+
assert keychain.get_org("test_migration").serialization_format == "json"
|