├── .changes ├── 1.0.0.md ├── 1.1.0.md ├── 1.10.0.md ├── 1.11.0.md ├── 1.12.0.md ├── 1.13.0-alpha.1.md ├── 1.13.0-beta.1.md ├── 1.13.0.md ├── 1.13.1.md ├── 1.2.0.md ├── 1.3.0.md ├── 1.4.0.md ├── 1.5.0.md ├── 1.5.1.md ├── 1.6.0.md ├── 1.7.0.md ├── 1.8.0.md ├── 1.9.0.md └── unreleased │ └── .gitkeep ├── .changie.yaml ├── .copywrite.hcl ├── .github ├── CODEOWNERS ├── CONTRIBUTING.md ├── ISSUE_TEMPLATE │ ├── bug_report.md │ ├── config.yml │ ├── documentation_suggestion.md │ └── feature_request.md ├── dependabot.yml ├── pull_request_template.md └── workflows │ ├── ci-changie.yml │ ├── ci-github-actions.yml │ ├── ci-go.yml │ ├── ci-goreleaser.yml │ ├── compliance.yml │ ├── issue-comment-triage.yml │ ├── lock.yml │ └── release.yml ├── .gitignore ├── .golangci.yml ├── .goreleaser.yml ├── CHANGELOG.md ├── LICENSE ├── META.d └── _summary.yaml ├── Makefile ├── README.md ├── compare ├── doc.go ├── value_comparer.go ├── values_differ.go ├── values_differ_test.go ├── values_same.go └── values_same_test.go ├── config ├── config.go ├── constraints.go ├── directory.go ├── directory_test.go ├── doc.go ├── file.go ├── file_test.go ├── variable.go └── variable_test.go ├── echoprovider ├── doc.go ├── server.go ├── server_test.go └── tftypes.go ├── go.mod ├── go.sum ├── helper ├── acctest │ ├── random.go │ └── random_test.go ├── logging │ └── logging.go └── resource │ ├── additional_cli_options.go │ ├── environment_variables.go │ ├── error.go │ ├── id.go │ ├── id_test.go │ ├── importstate │ ├── examplecloud_test.go │ ├── import_block_as_first_step_test.go │ ├── import_block_for_resource_with_a_dependency_test.go │ ├── import_block_in_config_directory_test.go │ ├── import_block_in_config_file_test.go │ ├── import_block_with_id_test.go │ ├── import_block_with_resource_identity_test.go │ ├── import_command_as_first_step_test.go │ ├── import_command_with_id_test.go │ ├── testdata │ │ ├── 1 │ │ │ └── examplecloud_container.tf │ │ ├── 2 │ │ │ └── examplecloud_container.tf │ │ ├── 2_with_exact_import_config │ │ │ └── examplecloud_container.tf │ │ └── examplecloud_container_with_exact_import_config_with_id.tf │ └── types_test.go │ ├── json.go │ ├── plan_checks.go │ ├── plan_checks_test.go │ ├── plugin.go │ ├── plugin_test.go │ ├── state.go │ ├── state_checks.go │ ├── state_checks_test.go │ ├── state_shim.go │ ├── state_shim_test.go │ ├── state_test.go │ ├── testcase_providers.go │ ├── testcase_providers_test.go │ ├── testcase_test.go │ ├── testcase_validate.go │ ├── testcase_validate_test.go │ ├── testdata │ ├── TestTest_ConfigDirectory_TestNameDirectory │ │ └── random.tf │ ├── TestTest_ConfigDirectory_TestNameDirectory_AttributeDoesNotExist │ │ └── random.tf │ ├── TestTest_ConfigDirectory_TestNameDirectory_AttributeDoesNotExist_MultipleFiles │ │ ├── provider.tf │ │ ├── random.tf │ │ └── terraform.tf │ ├── TestTest_ConfigDirectory_TestNameDirectory_AttributeDoesNotExist_MultipleFiles_Vars │ │ ├── provider.tf │ │ ├── random.tf │ │ ├── terraform.tf │ │ └── vars.tf │ ├── TestTest_ConfigDirectory_TestNameDirectory_AttributeDoesNotExist_Vars │ │ ├── random.tf │ │ └── vars.tf │ ├── TestTest_ConfigDirectory_TestNameDirectory_MultipleFiles │ │ ├── provider.tf │ │ ├── random.tf │ │ └── terraform.tf │ ├── TestTest_ConfigDirectory_TestNameDirectory_MultipleFiles_Vars │ │ ├── provider.tf │ │ ├── random.tf │ │ ├── terraform.tf │ │ └── vars.tf │ ├── TestTest_ConfigDirectory_TestNameDirectory_Vars │ │ ├── random.tf │ │ └── vars.tf │ ├── TestTest_ConfigDirectory_TestStepDirectory │ │ └── 1 │ │ │ └── random.tf │ ├── TestTest_ConfigDirectory_TestStepDirectory_AttributeDoesNotExist │ │ └── 1 │ │ │ └── random.tf │ ├── TestTest_ConfigDirectory_TestStepDirectory_AttributeDoesNotExist_MultipleFiles │ │ └── 1 │ │ │ ├── provider.tf │ │ │ ├── random.tf │ │ │ └── terraform.tf │ ├── TestTest_ConfigDirectory_TestStepDirectory_AttributeDoesNotExist_MultipleFiles_Vars │ │ └── 1 │ │ │ ├── provider.tf │ │ │ ├── random.tf │ │ │ ├── terraform.tf │ │ │ └── vars.tf │ ├── TestTest_ConfigDirectory_TestStepDirectory_AttributeDoesNotExist_Vars │ │ └── 1 │ │ │ ├── random.tf │ │ │ └── vars.tf │ ├── TestTest_ConfigDirectory_TestStepDirectory_MultipleFiles │ │ └── 1 │ │ │ ├── provider.tf │ │ │ ├── random.tf │ │ │ └── terraform.tf │ ├── TestTest_ConfigDirectory_TestStepDirectory_MultipleFiles_StepNotHardcoded │ │ ├── 1 │ │ │ ├── provider_1.tf │ │ │ ├── random_1.tf │ │ │ └── terraform_1.tf │ │ └── 2 │ │ │ ├── provider_2.tf │ │ │ ├── random_2.tf │ │ │ └── terraform_2.tf │ ├── TestTest_ConfigDirectory_TestStepDirectory_MultipleFiles_Vars │ │ └── 1 │ │ │ ├── provider.tf │ │ │ ├── random.tf │ │ │ ├── terraform.tf │ │ │ └── vars.tf │ ├── TestTest_ConfigDirectory_TestStepDirectory_MultipleFiles_Vars_StepNotHardcoded │ │ ├── 1 │ │ │ ├── provider_1.tf │ │ │ ├── random_1.tf │ │ │ ├── terraform_1.tf │ │ │ └── vars_1.tf │ │ └── 2 │ │ │ ├── provider_2.tf │ │ │ ├── random2.tf │ │ │ ├── terraform_2.tf │ │ │ └── vars_2.tf │ ├── TestTest_ConfigDirectory_TestStepDirectory_StepNotHardcoded │ │ ├── 1 │ │ │ └── random_1.tf │ │ └── 2 │ │ │ └── random_2.tf │ ├── TestTest_ConfigDirectory_TestStepDirectory_Vars │ │ └── 1 │ │ │ ├── random.tf │ │ │ └── vars.tf │ ├── TestTest_ConfigFile_TestNameFile │ │ └── random.tf │ ├── TestTest_ConfigFile_TestNameFile_AttributeDoesNotExist │ │ └── random.tf │ ├── TestTest_ConfigFile_TestNameFile_AttributeDoesNotExist_Vars │ │ └── random.tf │ ├── TestTest_ConfigFile_TestNameFile_Vars │ │ └── random.tf │ ├── TestTest_ConfigFile_TestStepFile │ │ └── 1 │ │ │ └── random.tf │ ├── TestTest_ConfigFile_TestStepFile_AttributeDoesNotExist │ │ └── 1 │ │ │ └── random.tf │ ├── TestTest_ConfigFile_TestStepFile_AttributeDoesNotExist_Vars │ │ └── 1 │ │ │ └── random.tf │ ├── TestTest_ConfigFile_TestStepFile_Vars │ │ └── 1 │ │ │ └── random.tf │ ├── TestTest_TestStep_ProviderFactories_ConfigDirectory_TestNameDirectory │ │ └── random.tf │ ├── TestTest_TestStep_ProviderFactories_ConfigDirectory_TestStepDirectory │ │ └── 1 │ │ │ └── random.tf │ ├── TestTest_TestStep_ProviderFactories_ConfigFile_TestNameFile │ │ └── random.tf │ ├── TestTest_TestStep_ProviderFactories_ConfigFile_TestStepFile │ │ └── 1 │ │ │ └── random.tf │ └── fixtures │ │ ├── random_id │ │ └── random.tf │ │ ├── random_password_3.2.0 │ │ └── random.tf │ │ ├── random_password_3.2.0_multiple_files │ │ ├── provider.tf │ │ ├── random.tf │ │ └── terraform.tf │ │ ├── random_password_3.2.0_multiple_files_vars │ │ ├── provider.tf │ │ ├── random.tf │ │ ├── terraform.tf │ │ └── vars.tf │ │ ├── random_password_3.2.0_vars │ │ ├── random.tf │ │ └── vars.tf │ │ ├── random_password_3.2.0_vars_single_file │ │ └── random.tf │ │ ├── random_password_3.5.1 │ │ └── random.tf │ │ ├── random_password_3.5.1_multiple_files │ │ ├── provider.tf │ │ ├── random.tf │ │ └── terraform.tf │ │ ├── random_password_3.5.1_multiple_files_vars │ │ ├── provider.tf │ │ ├── random.tf │ │ ├── terraform.tf │ │ └── vars.tf │ │ ├── random_password_3.5.1_vars │ │ ├── random.tf │ │ └── vars.tf │ │ └── random_password_3.5.1_vars_single_file │ │ └── random.tf │ ├── testing.go │ ├── testing_config.go │ ├── testing_example_test.go │ ├── testing_new.go │ ├── testing_new_config.go │ ├── testing_new_config_test.go │ ├── testing_new_import_state.go │ ├── testing_new_refresh_state.go │ ├── testing_new_refresh_state_test.go │ ├── testing_new_test.go │ ├── testing_sets.go │ ├── testing_sets_example_test.go │ ├── testing_sets_test.go │ ├── testing_test.go │ ├── teststep_providers.go │ ├── teststep_providers_test.go │ ├── teststep_test.go │ ├── teststep_validate.go │ ├── teststep_validate_test.go │ ├── tfversion_checks.go │ ├── tfversion_checks_test.go │ ├── wait.go │ └── wait_test.go ├── internal ├── addrs │ ├── doc.go │ ├── instance_key.go │ ├── module.go │ └── module_instance.go ├── configs │ ├── configschema │ │ ├── coerce_value.go │ │ ├── coerce_value_test.go │ │ ├── doc.go │ │ ├── empty_value.go │ │ ├── empty_value_test.go │ │ ├── implied_type.go │ │ ├── implied_type_test.go │ │ ├── nestingmode_string.go │ │ └── schema.go │ └── hcl2shim │ │ ├── flatmap.go │ │ ├── flatmap_test.go │ │ ├── paths.go │ │ ├── paths_test.go │ │ ├── values.go │ │ ├── values_equiv.go │ │ ├── values_equiv_test.go │ │ └── values_test.go ├── logging │ ├── context.go │ ├── context_test.go │ ├── environment_variables.go │ ├── helper_resource.go │ ├── helper_resource_test.go │ └── keys.go ├── plugintest │ ├── config.go │ ├── doc.go │ ├── environment_variables.go │ ├── guard.go │ ├── helper.go │ ├── util.go │ ├── util_test.go │ ├── working_dir.go │ └── working_dir_json_test.go ├── testing │ ├── doc.go │ ├── testprovider │ │ ├── datasource.go │ │ ├── doc.go │ │ ├── provider.go │ │ ├── provider_protov5.go │ │ └── resource.go │ └── testsdk │ │ ├── datasource │ │ ├── datasource.go │ │ └── doc.go │ │ ├── doc.go │ │ ├── provider │ │ ├── doc.go │ │ ├── provider.go │ │ └── provider_protov5.go │ │ ├── providerserver │ │ ├── datasources.go │ │ ├── doc.go │ │ ├── providerserver.go │ │ ├── providerserver_protov5.go │ │ ├── resources.go │ │ ├── schema.go │ │ └── tftypes.go │ │ └── resource │ │ ├── doc.go │ │ └── resource.go ├── teststep │ ├── config.go │ ├── config_test.go │ ├── directory.go │ ├── directory_test.go │ ├── file.go │ ├── file_test.go │ ├── string.go │ ├── string_test.go │ └── testdata │ │ ├── empty_dir │ │ └── .gitignore │ │ ├── empty_file │ │ └── main.tf │ │ ├── main.tf │ │ ├── provider_block_quoted_with_attributes │ │ └── main.tf │ │ ├── provider_block_quoted_with_attributes_no_spaces │ │ └── main.tf │ │ ├── provider_block_quoted_without_attributes │ │ └── main.tf │ │ ├── provider_block_quoted_without_attributes_no_spaces │ │ └── main.tf │ │ ├── provider_block_unquoted_with_attributes │ │ └── main.tf │ │ ├── provider_block_unquoted_with_attributes_no_trailing_space │ │ └── main.tf │ │ ├── provider_block_unquoted_without_attributes │ │ └── main.tf │ │ ├── provider_block_unquoted_without_attributes_no_trailing_space │ │ └── main.tf │ │ ├── provider_meta_attribute │ │ └── main.tf │ │ ├── provider_object_attribute │ │ └── main.tf │ │ ├── provider_string_attribute │ │ └── main.tf │ │ ├── random │ │ └── random.tf │ │ ├── random_multiple_files │ │ ├── provider.tf │ │ ├── random.tf │ │ └── terraform.tf │ │ ├── terraform_block │ │ └── main.tf │ │ ├── terraform_block_no_space │ │ └── main.tf │ │ ├── terraform_meta_attribute │ │ └── main.tf │ │ ├── terraform_object_attribute │ │ └── main.tf │ │ └── terraform_string_attribute │ │ └── main.tf └── tfdiags │ ├── config_traversals.go │ ├── contextual.go │ ├── contextual_test.go │ ├── diagnostic.go │ ├── diagnostic_base.go │ ├── diagnostics.go │ ├── diagnostics_test.go │ ├── doc.go │ ├── error.go │ ├── severity_string.go │ └── simple_warning.go ├── knownvalue ├── bool.go ├── bool_func.go ├── bool_func_test.go ├── bool_test.go ├── check.go ├── doc.go ├── float32.go ├── float32_func.go ├── float32_func_test.go ├── float32_test.go ├── float64.go ├── float64_func.go ├── float64_func_test.go ├── float64_test.go ├── int32.go ├── int32_func.go ├── int32_func_test.go ├── int32_test.go ├── int64.go ├── int64_func.go ├── int64_func_test.go ├── int64_test.go ├── list.go ├── list_partial.go ├── list_partial_test.go ├── list_size.go ├── list_size_test.go ├── list_test.go ├── map.go ├── map_partial.go ├── map_partial_test.go ├── map_size.go ├── map_size_test.go ├── map_test.go ├── not_null.go ├── not_null_test.go ├── null.go ├── null_test.go ├── number.go ├── number_func.go ├── number_func_test.go ├── number_test.go ├── object.go ├── object_partial.go ├── object_partial_test.go ├── object_test.go ├── set.go ├── set_partial.go ├── set_partial_test.go ├── set_size.go ├── set_size_test.go ├── set_test.go ├── string.go ├── string_func.go ├── string_func_test.go ├── string_regexp.go ├── string_regexp_test.go ├── string_test.go ├── tuple.go ├── tuple_partial.go ├── tuple_partial_test.go ├── tuple_size.go ├── tuple_size_test.go └── tuple_test.go ├── plancheck ├── deferred_reason.go ├── doc.go ├── expect_deferred_change.go ├── expect_deferred_change_test.go ├── expect_empty_plan.go ├── expect_empty_plan_test.go ├── expect_known_output_value.go ├── expect_known_output_value_at_path.go ├── expect_known_output_value_at_path_test.go ├── expect_known_output_value_test.go ├── expect_known_value.go ├── expect_known_value_test.go ├── expect_no_deferred_changes.go ├── expect_no_deferred_changes_test.go ├── expect_non_empty_plan.go ├── expect_non_empty_plan_test.go ├── expect_null_output_value.go ├── expect_null_output_value_at_path.go ├── expect_null_output_value_at_path_test.go ├── expect_null_output_value_test.go ├── expect_resource_action.go ├── expect_resource_action_test.go ├── expect_sensitive_value.go ├── expect_sensitive_value_test.go ├── expect_unknown_output_value.go ├── expect_unknown_output_value_at_path.go ├── expect_unknown_output_value_at_path_test.go ├── expect_unknown_output_value_test.go ├── expect_unknown_value.go ├── expect_unknown_value_test.go ├── plan_check.go └── resource_action.go ├── statecheck ├── compare_value.go ├── compare_value_collection.go ├── compare_value_collection_test.go ├── compare_value_pairs.go ├── compare_value_pairs_test.go ├── compare_value_test.go ├── doc.go ├── expect_identity.go ├── expect_identity_example_test.go ├── expect_identity_test.go ├── expect_identity_value.go ├── expect_identity_value_example_test.go ├── expect_identity_value_matches_state.go ├── expect_identity_value_matches_state_at_path.go ├── expect_identity_value_matches_state_at_path_example_test.go ├── expect_identity_value_matches_state_at_path_test.go ├── expect_identity_value_matches_state_example_test.go ├── expect_identity_value_matches_state_test.go ├── expect_identity_value_test.go ├── expect_known_output_value.go ├── expect_known_output_value_at_path.go ├── expect_known_output_value_at_path_example_test.go ├── expect_known_output_value_at_path_test.go ├── expect_known_output_value_example_test.go ├── expect_known_output_value_test.go ├── expect_known_value.go ├── expect_known_value_example_test.go ├── expect_known_value_test.go ├── expect_sensitive_value.go ├── expect_sensitive_value_test.go └── state_check.go ├── terraform ├── diff.go ├── diff_test.go ├── instancetype.go ├── instancetype_string.go ├── resource.go ├── resource_address.go ├── resource_address_test.go ├── resource_mode.go ├── resource_mode_string.go ├── resource_provider.go ├── resource_test.go ├── schemas.go ├── state.go ├── state_filter.go ├── state_test.go ├── unknown_value_walk.go ├── unknown_value_walk_test.go ├── util.go └── util_test.go ├── tfjsonpath ├── doc.go ├── path.go ├── path_test.go └── step.go ├── tfversion ├── all.go ├── all_test.go ├── any.go ├── any_test.go ├── doc.go ├── require_above.go ├── require_above_test.go ├── require_below.go ├── require_below_test.go ├── require_between.go ├── require_between_test.go ├── require_not.go ├── require_not_test.go ├── skip_above.go ├── skip_above_test.go ├── skip_below.go ├── skip_below_test.go ├── skip_between.go ├── skip_between_test.go ├── skip_if.go ├── skip_if_not_alpha.go ├── skip_if_not_alpha_test.go ├── skip_if_not_prerelease.go ├── skip_if_not_prerelease_test.go ├── skip_if_test.go ├── version_check.go └── versions.go ├── tools ├── copywrite.go ├── go.mod └── go.sum └── website ├── Makefile ├── README.md ├── data └── plugin-testing-nav-data.json ├── docs └── plugin │ └── testing │ ├── acceptance-tests │ ├── configuration.mdx │ ├── ephemeral-resources.mdx │ ├── index.mdx │ ├── known-value-checks │ │ ├── bool.mdx │ │ ├── custom.mdx │ │ ├── float32.mdx │ │ ├── float64.mdx │ │ ├── index.mdx │ │ ├── int32.mdx │ │ ├── int64.mdx │ │ ├── list.mdx │ │ ├── map.mdx │ │ ├── not-null.mdx │ │ ├── null.mdx │ │ ├── number.mdx │ │ ├── object.mdx │ │ ├── set.mdx │ │ ├── string.mdx │ │ └── tuple.mdx │ ├── plan-checks │ │ ├── custom.mdx │ │ ├── index.mdx │ │ ├── output.mdx │ │ └── resource.mdx │ ├── state-checks │ │ ├── custom.mdx │ │ ├── index.mdx │ │ ├── output.mdx │ │ └── resource.mdx │ ├── sweepers.mdx │ ├── testcase.mdx │ ├── teststep.mdx │ ├── tfjson-paths.mdx │ ├── tfversion-checks.mdx │ └── value-comparers │ │ └── index.mdx │ ├── index.mdx │ ├── migrating.mdx │ ├── testing-patterns.mdx │ └── unit-testing.mdx ├── img └── .gitkeep ├── package-lock.json ├── package.json └── scripts ├── should-build.sh ├── website-build.sh └── website-start.sh /.changes/1.0.0.md: -------------------------------------------------------------------------------- 1 | ## 1.0.0 (January 10, 2023) 2 | 3 | NOTES: 4 | 5 | * Same testing functionality as that of terraform-plugin-sdk v2.24.1, repacked in a standalone repository ([#24](https://github.com/hashicorp/terraform-plugin-testing/issues/24)) 6 | 7 | -------------------------------------------------------------------------------- /.changes/1.1.0.md: -------------------------------------------------------------------------------- 1 | ## 1.1.0 (February 06, 2023) 2 | 3 | FEATURES: 4 | 5 | * helper/resource: Added `TF_ACC_PERSIST_WORKING_DIR` environment variable to allow persisting of Terraform files generated during each test step ([#18](https://github.com/hashicorp/terraform-plugin-testing/issues/18)) 6 | * helper/resource: Added `TestCase` type `WorkingDir` field to allow specifying the base directory where testing files used by the testing module are generated ([#18](https://github.com/hashicorp/terraform-plugin-testing/issues/18)) 7 | 8 | -------------------------------------------------------------------------------- /.changes/1.10.0.md: -------------------------------------------------------------------------------- 1 | ## 1.10.0 (August 08, 2024) 2 | 3 | NOTES: 4 | 5 | * compare: The `compare` package is considered experimental and may be altered or removed in a subsequent release ([#330](https://github.com/hashicorp/terraform-plugin-testing/issues/330)) 6 | * statecheck: `CompareValue`, `CompareValueCollection`, and `CompareValuePairs` state checks are considered experimental and may be altered or removed in a subsequent release. ([#330](https://github.com/hashicorp/terraform-plugin-testing/issues/330)) 7 | 8 | FEATURES: 9 | 10 | * compare: Introduced new `compare` package, which contains interfaces and implementations for value comparisons in state checks. ([#330](https://github.com/hashicorp/terraform-plugin-testing/issues/330)) 11 | * statecheck: Added `CompareValue` state check, which compares sequential values of the specified attribute at the given managed resource, or data source, using the supplied value comparer. ([#330](https://github.com/hashicorp/terraform-plugin-testing/issues/330)) 12 | * statecheck: Added `CompareValueCollection` state check, which compares each item in the specified collection (e.g., list, set) attribute, with the second specified attribute at the given managed resources, or data sources, using the supplied value comparer. ([#330](https://github.com/hashicorp/terraform-plugin-testing/issues/330)) 13 | * statecheck: Added `CompareValuePairs` state check, which compares the specified attributes at the given managed resources, or data sources, using the supplied value comparer. ([#330](https://github.com/hashicorp/terraform-plugin-testing/issues/330)) 14 | 15 | -------------------------------------------------------------------------------- /.changes/1.11.0.md: -------------------------------------------------------------------------------- 1 | ## 1.11.0 (November 19, 2024) 2 | 3 | NOTES: 4 | 5 | * all: This Go module has been updated to Go 1.22 per the [Go support policy](https://go.dev/doc/devel/release#policy). It is recommended to review the [Go 1.22 release notes](https://go.dev/doc/go1.22) before upgrading. Any consumers building on earlier Go versions may experience errors. ([#371](https://github.com/hashicorp/terraform-plugin-testing/issues/371)) 6 | * echoprovider: The `echoprovider` package is considered experimental and may be altered or removed in a subsequent release ([#389](https://github.com/hashicorp/terraform-plugin-testing/issues/389)) 7 | 8 | FEATURES: 9 | 10 | * tfversion: Added `SkipIfNotAlpha` version check for testing experimental features of alpha Terraform builds. ([#388](https://github.com/hashicorp/terraform-plugin-testing/issues/388)) 11 | * echoprovider: Introduced new `echoprovider` package, which contains a v6 Terraform provider that can be used to test ephemeral resource data. ([#389](https://github.com/hashicorp/terraform-plugin-testing/issues/389)) 12 | 13 | -------------------------------------------------------------------------------- /.changes/1.12.0.md: -------------------------------------------------------------------------------- 1 | ## 1.12.0 (March 18, 2025) 2 | 3 | NOTES: 4 | 5 | * all: This Go module has been updated to Go 1.23 per the [Go support policy](https://go.dev/doc/devel/release#policy). It is recommended to review the [Go 1.23 release notes](https://go.dev/doc/go1.23) before upgrading. Any consumers building on earlier Go versions may experience errors. ([#454](https://github.com/hashicorp/terraform-plugin-testing/issues/454)) 6 | 7 | FEATURES: 8 | 9 | * knownvalue: added function checks for custom validation of resource attribute or output values. ([#412](https://github.com/hashicorp/terraform-plugin-testing/issues/412)) 10 | 11 | ENHANCEMENTS: 12 | 13 | * knownvalue: Updated the `ObjectExact` error message to report extra/missing attributes from the actual object. ([#451](https://github.com/hashicorp/terraform-plugin-testing/issues/451)) 14 | * plancheck: Improved the unknown value plan check error messages to include a known value if one exists. ([#450](https://github.com/hashicorp/terraform-plugin-testing/issues/450)) 15 | 16 | BUG FIXES: 17 | 18 | * plancheck: Fixed bug with all unknown value plan checks where a valid path would return a "path not found" error. ([#450](https://github.com/hashicorp/terraform-plugin-testing/issues/450)) 19 | 20 | -------------------------------------------------------------------------------- /.changes/1.13.0-alpha.1.md: -------------------------------------------------------------------------------- 1 | ## 1.13.0-alpha.1 (March 27, 2025) 2 | 3 | NOTES: 4 | 5 | * This alpha pre-release contains testing utilities for managed resource identity, which can be used with `Terraform v1.12.0-alpha20250319`, to assert identity data stored during apply workflows. A managed resource in a provider can read/store identity data using the `terraform-plugin-framework@v1.15.0-alpha.1` or `terraform-plugin-sdk/v2@v2.37.0-alpha.1` Go modules. To assert identity data stored by a provider in state, use the `statecheck.ExpectIdentity` state check. ([#470](https://github.com/hashicorp/terraform-plugin-testing/issues/470)) 6 | 7 | -------------------------------------------------------------------------------- /.changes/1.13.0-beta.1.md: -------------------------------------------------------------------------------- 1 | ## 1.13.0-beta.1 (April 18, 2025) 2 | 3 | BREAKING CHANGES: 4 | 5 | * importstate: `ImportStatePersist` and `ImportStateVerify` are not supported for plannable import (`ImportBlockWith*`) and will return an error ([#476](https://github.com/hashicorp/terraform-plugin-testing/issues/476)) 6 | * importstate: renamed `ImportStateWithId` to `ImportStateWithID` and renamed `ImportCommandWithId` to `ImportCommandWithID`. ([#465](https://github.com/hashicorp/terraform-plugin-testing/issues/465)) 7 | 8 | NOTES: 9 | 10 | * This beta pre-release adds support for managed resource identity, which can be used with Terraform v1.12.0-beta2. Acceptance tests can use the `ImportBlockWithResourceIdentity` kind to exercise the import of a managed resource using its resource identity object values instead of using a string identifier. ([#480](https://github.com/hashicorp/terraform-plugin-testing/issues/480)) 11 | 12 | BUG FIXES: 13 | 14 | * importstate: plannable import (`ImportBlockWith*`) fixed for a resource with a dependency ([#476](https://github.com/hashicorp/terraform-plugin-testing/issues/476)) 15 | 16 | -------------------------------------------------------------------------------- /.changes/1.13.1.md: -------------------------------------------------------------------------------- 1 | ## 1.13.1 (May 21, 2025) 2 | 3 | BUG FIXES: 4 | 5 | * echoprovider: Fixed bug where Terraform v1.12+ would return an error message indicating the provider doesn't support `GetResourceIdentitySchemas`. ([#512](https://github.com/hashicorp/terraform-plugin-testing/issues/512)) 6 | 7 | -------------------------------------------------------------------------------- /.changes/1.4.0.md: -------------------------------------------------------------------------------- 1 | ## 1.4.0 (July 24, 2023) 2 | 3 | FEATURES: 4 | 5 | * tfjsonpath: Introduced new `tfjsonpath` package which contains methods that allow traversal of Terraform JSON data ([#154](https://github.com/hashicorp/terraform-plugin-testing/issues/154)) 6 | * plancheck: Added `ExpectUnknownValue` built-in plan check, which asserts that a given attribute has an unknown value ([#154](https://github.com/hashicorp/terraform-plugin-testing/issues/154)) 7 | * plancheck: Added `ExpectSensitiveValue` built-in plan check, which asserts that a given attribute has a sensitive value ([#154](https://github.com/hashicorp/terraform-plugin-testing/issues/154)) 8 | 9 | -------------------------------------------------------------------------------- /.changes/1.5.0.md: -------------------------------------------------------------------------------- 1 | ## 1.5.0 (August 31, 2023) 2 | 3 | FEATURES: 4 | 5 | * config: Introduced new `config` package which contains interfaces and helper functions for working with native Terraform configuration and variables ([#153](https://github.com/hashicorp/terraform-plugin-testing/issues/153)) 6 | * helper/resource: Added `TestStep.ConfigDirectory` to allow specifying a directory containing Terraform configuration for use during acceptance tests ([#153](https://github.com/hashicorp/terraform-plugin-testing/issues/153)) 7 | * helper/resource: Added `TestStep.ConfigFile` to allow specifying a file containing Terraform configuration for use during acceptance tests ([#153](https://github.com/hashicorp/terraform-plugin-testing/issues/153)) 8 | * helper/resource: Added `TestStep.ConfigVariables` to allow specifying Terraform variables for use with Terraform configuration during acceptance tests ([#153](https://github.com/hashicorp/terraform-plugin-testing/issues/153)) 9 | * helper/resource: Removed data resource and managed resource `id` attribute requirement ([#84](https://github.com/hashicorp/terraform-plugin-testing/issues/84)) 10 | 11 | ENHANCEMENTS: 12 | 13 | * helper/resource: Added `TestStep` type `ImportStateVerifyIdentifierAttribute` field, which can override the default `id` attribute used for matching prior resource state with imported resource state ([#84](https://github.com/hashicorp/terraform-plugin-testing/issues/84)) 14 | 15 | -------------------------------------------------------------------------------- /.changes/1.5.1.md: -------------------------------------------------------------------------------- 1 | ## 1.5.1 (August 31, 2023) 2 | 3 | BUG FIXES: 4 | 5 | * helper/resource: Fix regression by allowing providers to be defined both at the `TestCase` level, and within `TestStep.Config` ([#177](https://github.com/hashicorp/terraform-plugin-testing/issues/177)) 6 | 7 | -------------------------------------------------------------------------------- /.changes/1.8.0.md: -------------------------------------------------------------------------------- 1 | ## 1.8.0 (May 17, 2024) 2 | 3 | FEATURES: 4 | 5 | * plancheck: Added `ExpectDeferredChange` and `ExpectNoDeferredChanges` checks for experimental deferred action support. ([#331](https://github.com/hashicorp/terraform-plugin-testing/issues/331)) 6 | * tfversion: Added `SkipIfNotPrerelease` version check for testing experimental features of prerelease Terraform builds. ([#331](https://github.com/hashicorp/terraform-plugin-testing/issues/331)) 7 | 8 | ENHANCEMENTS: 9 | 10 | * helper/acctest: Improve scope of IPv4/IPv6 random address generation in RandIpAddress() ([#305](https://github.com/hashicorp/terraform-plugin-testing/issues/305)) 11 | * knownvalue: Add `TupleExact`, `TuplePartial` and `TupleSizeExact` checks for dynamic value testing. ([#312](https://github.com/hashicorp/terraform-plugin-testing/issues/312)) 12 | * tfversion: Ensured Terraform CLI prerelease versions are considered semantically equal to patch versions in built-in checks to match the Terraform CLI versioning policy ([#303](https://github.com/hashicorp/terraform-plugin-testing/issues/303)) 13 | * helper/resource: Added `(TestCase).AdditionalCLIOptions` with `AllowDeferral` option for plan and apply commands. ([#331](https://github.com/hashicorp/terraform-plugin-testing/issues/331)) 14 | 15 | BUG FIXES: 16 | 17 | * helper/resource: Fix panic in output state shimming when a tuple is present. ([#310](https://github.com/hashicorp/terraform-plugin-testing/issues/310)) 18 | * tfversion: Fixed `RequireBelow` ignoring equal versioning to fail a test ([#303](https://github.com/hashicorp/terraform-plugin-testing/issues/303)) 19 | 20 | -------------------------------------------------------------------------------- /.changes/1.9.0.md: -------------------------------------------------------------------------------- 1 | ## 1.9.0 (July 09, 2024) 2 | 3 | ENHANCEMENTS: 4 | 5 | * knownvalue: Add `Int32Exact` check for int32 value testing. ([#356](https://github.com/hashicorp/terraform-plugin-testing/issues/356)) 6 | * knownvalue: Add `Float32Exact` check for float32 value testing. ([#356](https://github.com/hashicorp/terraform-plugin-testing/issues/356)) 7 | 8 | -------------------------------------------------------------------------------- /.changes/unreleased/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hashicorp/terraform-plugin-testing/f93511e1eeb942e64370f53368659a834e0ab9e8/.changes/unreleased/.gitkeep -------------------------------------------------------------------------------- /.changie.yaml: -------------------------------------------------------------------------------- 1 | # DO NOT EDIT - This GitHub Workflow is managed by automation 2 | # https://github.com/hashicorp/terraform-devex-repos 3 | changesDir: .changes 4 | unreleasedDir: unreleased 5 | changelogPath: CHANGELOG.md 6 | versionExt: md 7 | versionFormat: '## {{.Version}} ({{.Time.Format "January 02, 2006"}})' 8 | kindFormat: '{{.Kind}}:' 9 | changeFormat: '* {{.Body}} ([#{{.Custom.Issue}}](https://github.com/hashicorp/terraform-plugin-testing/issues/{{.Custom.Issue}}))' 10 | custom: 11 | - key: Issue 12 | label: Issue/PR Number 13 | type: int 14 | minInt: 1 15 | kinds: 16 | - label: BREAKING CHANGES 17 | - label: NOTES 18 | - label: FEATURES 19 | - label: ENHANCEMENTS 20 | - label: BUG FIXES 21 | newlines: 22 | afterKind: 1 23 | beforeKind: 1 24 | endOfVersion: 2 25 | -------------------------------------------------------------------------------- /.copywrite.hcl: -------------------------------------------------------------------------------- 1 | schema_version = 1 2 | 3 | project { 4 | license = "MPL-2.0" 5 | copyright_year = 2014 6 | 7 | header_ignore = [ 8 | # internal catalog metadata (prose) 9 | "META.d/**/*.yaml", 10 | 11 | # changie tooling configuration and CHANGELOG entries (prose) 12 | ".changes/unreleased/**", 13 | ".changie.yaml", 14 | 15 | # GitHub issue template configuration 16 | ".github/ISSUE_TEMPLATE/*.yml", 17 | 18 | # GitHub Actions workflow-specific configurations 19 | ".github/labeler-*.yml", 20 | 21 | # golangci-lint tooling configuration 22 | ".golangci.yml", 23 | 24 | # GoReleaser tooling configuration 25 | ".goreleaser.yml", 26 | 27 | # Release Engineering tooling configuration 28 | ".release/*.hcl", 29 | ] 30 | } 31 | -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | * @hashicorp/terraform-core-plugins 2 | 3 | # engineering and web presence get notified of, and can approve changes to web tooling, but not content. 4 | 5 | /website/ @hashicorp/web-presence @hashicorp/terraform-core-plugins 6 | /website/data/ 7 | /website/public/ 8 | /website/content/ 9 | 10 | # education and engineering get notified of, and can approve changes to web content. 11 | 12 | /website/data/ @hashicorp/team-docs-packer-and-terraform @hashicorp/terraform-core-plugins 13 | /website/public/ @hashicorp/team-docs-packer-and-terraform @hashicorp/terraform-core-plugins 14 | /website/content/ @hashicorp/team-docs-packer-and-terraform @hashicorp/terraform-core-plugins 15 | /website/docs/ @hashicorp/team-docs-packer-and-terraform @hashicorp/terraform-core-plugins 16 | /website/img/ @hashicorp/team-docs-packer-and-terraform @hashicorp/terraform-core-plugins 17 | /website/README.md @hashicorp/team-docs-packer-and-terraform @hashicorp/terraform-core-plugins -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: 🐛 Bug report 3 | about: Let us know about an unexpected error, a crash, or an incorrect behavior. 4 | labels: bug 5 | --- 6 | 7 | ### terraform-plugin-testing version 8 | 17 | 18 | ``` 19 | insert version here 20 | ``` 21 | 22 | ### Relevant provider source code 23 | 24 | 28 | ```go 29 | 30 | // insert code here 31 | 32 | ``` 33 | 34 | ### Terraform Configuration Files 35 | 38 | 39 | ```tf 40 | # insert config here 41 | ``` 42 | 43 | 44 | ### Expected Behavior 45 | 48 | 49 | ### Actual Behavior 50 | 53 | 54 | ### Steps to Reproduce 55 | 60 | 61 | ### References 62 | 67 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | # Copyright (c) HashiCorp, Inc. 2 | # SPDX-License-Identifier: MPL-2.0 3 | 4 | blank_issues_enabled: false 5 | contact_links: 6 | - name: Terraform Language or Workflow Feedback and Questions 7 | url: https://github.com/hashicorp/terraform/issues/new/choose 8 | about: Terraform Core has its own repository, any language (HCL) or workflow related issues or questions should be directed there. 9 | - name: ❓ Provider Development Questions 10 | url: https://discuss.hashicorp.com/c/terraform-providers/tf-plugin-sdk 11 | about: Please ask and answer questions about provider development through the Plugin SDK Community Forum. 12 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/documentation_suggestion.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Documentation suggestion 3 | about: Suggest new documentation or an enhancement to existing documentation. 4 | labels: documentation 5 | --- 6 | 7 | ### Does this documentation exist? 8 | 9 | * [ ] This is new documentation 10 | * [ ] This is an enhancement to existing documentation 11 | 12 | ### Where would you expect to find this documentation? 13 | 14 | * [ ] On terraform.io 15 | * [ ] In the GoDoc for this module 16 | * [ ] In this repo as a markdown file 17 | * [ ] Somewhere else 18 | 19 | #### Details 20 | 21 | 26 | 27 | ### Description 28 | 35 | 36 | ### References 37 | 44 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # Copyright (c) HashiCorp, Inc. 2 | # SPDX-License-Identifier: MPL-2.0 3 | 4 | version: 2 5 | updates: 6 | - package-ecosystem: "gomod" 7 | directory: "/" 8 | schedule: 9 | interval: "daily" 10 | - package-ecosystem: "gomod" 11 | directory: "/tools" 12 | schedule: 13 | interval: "daily" 14 | - package-ecosystem: "github-actions" 15 | directory: "/" 16 | schedule: 17 | interval: "daily" 18 | -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | ## Related Issue 2 | 3 | Fixes # 4 | 5 | ## Description 6 | 7 | In plain English, describe your approach to addressing the issue linked above. For example, if you made a particular design decision, let us know why you chose this path instead of another solution. 8 | 9 | 10 | ## Rollback Plan 11 | 12 | - [ ] If a change needs to be reverted, we will roll out an update to the code within 7 days. 13 | 14 | ## Changes to Security Controls 15 | 16 | Are there any changes to security controls (access controls, encryption, logging) in this pull request? If so, explain. 17 | -------------------------------------------------------------------------------- /.github/workflows/ci-changie.yml: -------------------------------------------------------------------------------- 1 | # DO NOT EDIT - This GitHub Workflow is managed by automation 2 | # https://github.com/hashicorp/terraform-devex-repos 3 | 4 | # Continuous integration handling for changie 5 | name: ci-changie 6 | 7 | on: 8 | pull_request: 9 | paths: 10 | - .changes/unreleased/*.yaml 11 | - .changie.yaml 12 | - .github/workflows/ci-changie.yml 13 | 14 | permissions: 15 | contents: read 16 | 17 | jobs: 18 | check: 19 | runs-on: ubuntu-latest 20 | steps: 21 | # Ensure terraform-devex-repos is updated on version changes. 22 | - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 23 | # Ensure terraform-devex-repos is updated on version changes. 24 | - uses: miniscruff/changie-action@6dcc2533cac0495148ed4046c438487e4dceaa23 # v2.0.0 25 | with: 26 | version: latest 27 | args: batch patch --dry-run 28 | -------------------------------------------------------------------------------- /.github/workflows/ci-github-actions.yml: -------------------------------------------------------------------------------- 1 | # Continuous integration handling for GitHub Actions workflows 2 | name: ci-github-actions 3 | 4 | on: 5 | pull_request: 6 | paths: 7 | - .github/workflows/*.yml 8 | 9 | permissions: 10 | contents: read 11 | 12 | jobs: 13 | actionlint: 14 | runs-on: ubuntu-latest 15 | steps: 16 | - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 17 | - uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5.5.0 18 | with: 19 | go-version-file: 'go.mod' 20 | - run: go install github.com/rhysd/actionlint/cmd/actionlint@latest 21 | - run: actionlint 22 | -------------------------------------------------------------------------------- /.github/workflows/ci-goreleaser.yml: -------------------------------------------------------------------------------- 1 | # Continuous integration handling for GoReleaser 2 | name: ci-goreleaser 3 | 4 | on: 5 | pull_request: 6 | paths: 7 | - .github/workflows/ci-goreleaser.yml 8 | - .goreleaser.yml 9 | 10 | permissions: 11 | contents: read 12 | 13 | jobs: 14 | check: 15 | runs-on: ubuntu-latest 16 | steps: 17 | - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 18 | - uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5.5.0 19 | with: 20 | go-version-file: 'go.mod' 21 | - uses: goreleaser/goreleaser-action@9c156ee8a17a598857849441385a2041ef570552 # v6.3.0 22 | with: 23 | args: check 24 | -------------------------------------------------------------------------------- /.github/workflows/compliance.yml: -------------------------------------------------------------------------------- 1 | name: compliance 2 | 3 | on: 4 | pull_request: 5 | 6 | permissions: 7 | contents: read 8 | 9 | jobs: 10 | # Reference: ENGSRV-059 11 | copywrite: 12 | runs-on: ubuntu-latest 13 | steps: 14 | - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 15 | - uses: hashicorp/setup-copywrite@32638da2d4e81d56a0764aa1547882fc4d209636 # v1.1.3 16 | - run: copywrite headers --plan 17 | - run: copywrite license --plan 18 | -------------------------------------------------------------------------------- /.github/workflows/issue-comment-triage.yml: -------------------------------------------------------------------------------- 1 | # DO NOT EDIT - This GitHub Workflow is managed by automation 2 | # https://github.com/hashicorp/terraform-devex-repos 3 | name: Issue Comment Triage 4 | 5 | on: 6 | issue_comment: 7 | types: [created] 8 | 9 | jobs: 10 | issue_comment_triage: 11 | runs-on: ubuntu-latest 12 | env: 13 | # issue_comment events are triggered by comments on issues and pull requests. Checking the 14 | # value of github.event.issue.pull_request tells us whether the issue is an issue or is 15 | # actually a pull request, allowing us to dynamically set the gh subcommand: 16 | # https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#issue_comment-on-issues-only-or-pull-requests-only 17 | COMMAND: ${{ github.event.issue.pull_request && 'pr' || 'issue' }} 18 | GH_TOKEN: ${{ github.token }} 19 | steps: 20 | - name: 'Remove waiting-response on comment' 21 | run: gh ${{ env.COMMAND }} edit ${{ github.event.issue.html_url }} --remove-label waiting-response 22 | -------------------------------------------------------------------------------- /.github/workflows/lock.yml: -------------------------------------------------------------------------------- 1 | # DO NOT EDIT - This GitHub Workflow is managed by automation 2 | # https://github.com/hashicorp/terraform-devex-repos 3 | name: 'Lock Threads' 4 | 5 | on: 6 | schedule: 7 | - cron: '29 22 * * *' 8 | 9 | jobs: 10 | lock: 11 | runs-on: ubuntu-latest 12 | steps: 13 | # NOTE: When TSCCR updates the GitHub action version, update the template workflow file to avoid drift: 14 | # https://github.com/hashicorp/terraform-devex-repos/blob/main/modules/repo/workflows/lock.tftpl 15 | - uses: dessant/lock-threads@1bf7ec25051fe7c00bdd17e6a7cf3d7bfb7dc771 # v5.0.1 16 | with: 17 | github-token: ${{ github.token }} 18 | issue-inactive-days: '30' 19 | issue-lock-reason: resolved 20 | pr-inactive-days: '30' 21 | pr-lock-reason: resolved 22 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | website-preview 3 | 4 | .idea 5 | .vscode/settings.json 6 | -------------------------------------------------------------------------------- /.golangci.yml: -------------------------------------------------------------------------------- 1 | version: "2" 2 | linters: 3 | default: none 4 | enable: 5 | - copyloopvar 6 | - durationcheck 7 | - errcheck 8 | - forcetypeassert 9 | - govet 10 | - ineffassign 11 | - makezero 12 | - misspell 13 | - nilerr 14 | - paralleltest 15 | - predeclared 16 | - staticcheck 17 | - unconvert 18 | - unparam 19 | - unused 20 | - usetesting 21 | exclusions: 22 | generated: lax 23 | presets: 24 | - comments 25 | - common-false-positives 26 | - legacy 27 | - std-error-handling 28 | paths: 29 | - third_party$ 30 | - builtin$ 31 | - examples$ 32 | settings: 33 | staticcheck: 34 | checks: 35 | - all 36 | - '-QF1001' # "could apply De Morgan's law" -- https://staticcheck.dev/docs/checks/#QF1001 37 | - '-QF1002' # "could use tagged switch" -- https://staticcheck.dev/docs/checks/#QF1002 38 | - '-QF1004' # "could use strings.ReplaceAll instead" -- https://staticcheck.dev/docs/checks/#QF1004 39 | - '-QF1008' # "could remove embedded field "Block" from selector" -- https://staticcheck.dev/docs/checks/#QF1008 40 | - '-ST1003' # example: "const autoTFVarsJson should be autoTFVarsJSON" -- https://staticcheck.dev/docs/checks/#ST1003 41 | - '-ST1005' # "error strings should not end with punctuation or newlines" -- https://staticcheck.dev/docs/checks/#ST1005 42 | - '-ST1016' # example: "methods on the same type should have the same receiver name (seen 2x "r", 2x "s")" -- https://staticcheck.dev/docs/checks/#ST1016 43 | issues: 44 | max-issues-per-linter: 0 45 | max-same-issues: 0 46 | formatters: 47 | enable: 48 | - gofmt 49 | exclusions: 50 | generated: lax 51 | paths: 52 | - third_party$ 53 | - builtin$ 54 | - examples$ 55 | -------------------------------------------------------------------------------- /.goreleaser.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | project_name: terraform-plugin-testing 3 | builds: 4 | - skip: true 5 | milestones: 6 | - close: true 7 | release: 8 | prerelease: auto 9 | ids: 10 | - 'none' 11 | -------------------------------------------------------------------------------- /META.d/_summary.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | schema: 1.1 3 | 4 | partition: tf-ecosystem 5 | 6 | summary: 7 | owner: team-tf-core-plugins 8 | description: | 9 | Module for testing Terraform providers 10 | visibility: public 11 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # Generate copywrite headers 2 | generate: 3 | cd tools; go generate ./... 4 | 5 | # Run this if working on the website locally to run in watch mode. 6 | .PHONY: website 7 | website: 8 | $(MAKE) -C website website 9 | 10 | # Use this if you have run `website/build-local` to use the locally built image. 11 | .PHONY: website/local 12 | website/local: 13 | $(MAKE) -C website website/local 14 | 15 | # Run this to generate a new local Docker image. 16 | .PHONY: website/build-local 17 | website/build-local: 18 | $(MAKE) -C website website/build-local -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![PkgGoDev](https://pkg.go.dev/badge/github.com/hashicorp/terraform-plugin-log)](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-testing) 2 | 3 | # terraform-plugin-testing 4 | 5 | terraform-plugin-testing is a helper module for testing Terraform providers. Terraform acceptance tests use real Terraform configurations to exercise the code in real plan, apply, refresh, and destroy life cycles. 6 | When run from the root of a Terraform Provider codebase, Terraform’s testing framework compiles the current provider in-memory and executes the provided configuration in developer defined steps, creating infrastructure along the way. 7 | 8 | ## Go Compatibility 9 | 10 | This project follows the [support policy](https://golang.org/doc/devel/release.html#policy) of Go as its support policy. The two latest major releases of Go are supported by the project. 11 | 12 | Currently, that means Go **1.23** or later must be used when including this project as a dependency. 13 | 14 | ## Documentation 15 | 16 | Visit the [Testing Terraform Plugins docs](https://developer.hashicorp.com/terraform/plugin/testing) to learn about how to best use this helper module. 17 | 18 | ## Contributing 19 | 20 | See [`.github/CONTRIBUTING.md`](https://github.com/hashicorp/terraform-plugin-testing/blob/main/.github/CONTRIBUTING.md) 21 | 22 | ## License 23 | 24 | [Mozilla Public License v2.0](https://github.com/hashicorp/terraform-plugin-testing/blob/main/LICENSE) 25 | -------------------------------------------------------------------------------- /compare/doc.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) HashiCorp, Inc. 2 | // SPDX-License-Identifier: MPL-2.0 3 | 4 | // Package compare contains the value comparer interface, and types implementing the value comparer interface. 5 | package compare 6 | -------------------------------------------------------------------------------- /compare/value_comparer.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) HashiCorp, Inc. 2 | // SPDX-License-Identifier: MPL-2.0 3 | 4 | package compare 5 | 6 | // ValueComparer defines an interface that is implemented to run comparison logic on multiple values. Individual 7 | // implementations determine how the comparison is performed (e.g., values differ, values equal). 8 | type ValueComparer interface { 9 | // CompareValues should assert the given known values against any expectations. 10 | // Values are always ordered in the order they were added. Use the error 11 | // return to signal unexpected values or implementation errors. 12 | CompareValues(values ...any) error 13 | } 14 | -------------------------------------------------------------------------------- /compare/values_differ.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) HashiCorp, Inc. 2 | // SPDX-License-Identifier: MPL-2.0 3 | 4 | package compare 5 | 6 | import ( 7 | "fmt" 8 | "reflect" 9 | ) 10 | 11 | var _ ValueComparer = valuesDiffer{} 12 | 13 | type valuesDiffer struct{} 14 | 15 | // CompareValues determines whether each value in the sequence of the supplied values 16 | // differs from the preceding value. 17 | func (v valuesDiffer) CompareValues(values ...any) error { 18 | for i := 1; i < len(values); i++ { 19 | if reflect.DeepEqual(values[i-1], values[i]) { 20 | return fmt.Errorf("expected values to differ, but they are the same: %v == %v", values[i-1], values[i]) 21 | } 22 | } 23 | 24 | return nil 25 | } 26 | 27 | // ValuesDiffer returns a ValueComparer for asserting that each value in the sequence of 28 | // the values supplied to the CompareValues method differs from the preceding value. 29 | func ValuesDiffer() valuesDiffer { 30 | return valuesDiffer{} 31 | } 32 | -------------------------------------------------------------------------------- /compare/values_differ_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) HashiCorp, Inc. 2 | // SPDX-License-Identifier: MPL-2.0 3 | 4 | package compare_test 5 | 6 | import ( 7 | "fmt" 8 | "testing" 9 | 10 | "github.com/google/go-cmp/cmp" 11 | 12 | "github.com/hashicorp/terraform-plugin-testing/compare" 13 | ) 14 | 15 | func TestValuesDiffer_CompareValues(t *testing.T) { 16 | t.Parallel() 17 | 18 | testCases := map[string]struct { 19 | in []any 20 | expectedError error 21 | }{ 22 | "nil": {}, 23 | "single-value": { 24 | in: []any{"str"}, 25 | }, 26 | "non-matching-sequential-values": { 27 | in: []any{"str", "other_str", "str"}, 28 | }, 29 | "matching-values-string": { 30 | in: []any{"str", "other_str", "other_str"}, 31 | expectedError: fmt.Errorf("expected values to differ, but they are the same: other_str == other_str"), 32 | }, 33 | "matching-values-slice": { 34 | in: []any{ 35 | []any{"other_str"}, 36 | []any{"other_str"}, 37 | }, 38 | expectedError: fmt.Errorf("expected values to differ, but they are the same: [other_str] == [other_str]"), 39 | }, 40 | "matching-values-map": { 41 | in: []any{ 42 | map[string]any{"a": "other_str"}, 43 | map[string]any{"a": "other_str"}, 44 | }, 45 | expectedError: fmt.Errorf("expected values to differ, but they are the same: map[a:other_str] == map[a:other_str]"), 46 | }, 47 | } 48 | 49 | for name, testCase := range testCases { 50 | t.Run(name, func(t *testing.T) { 51 | t.Parallel() 52 | 53 | err := compare.ValuesDiffer().CompareValues(testCase.in...) 54 | 55 | if diff := cmp.Diff(err, testCase.expectedError, equateErrorMessage); diff != "" { 56 | t.Errorf("unexpected difference: %s", diff) 57 | } 58 | }) 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /compare/values_same.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) HashiCorp, Inc. 2 | // SPDX-License-Identifier: MPL-2.0 3 | 4 | package compare 5 | 6 | import ( 7 | "fmt" 8 | "reflect" 9 | ) 10 | 11 | var _ ValueComparer = valuesSame{} 12 | 13 | type valuesSame struct{} 14 | 15 | // CompareValues determines whether each value in the sequence of the supplied values 16 | // is the same as the preceding value. 17 | func (v valuesSame) CompareValues(values ...any) error { 18 | for i := 1; i < len(values); i++ { 19 | if !reflect.DeepEqual(values[i-1], values[i]) { 20 | return fmt.Errorf("expected values to be the same, but they differ: %v != %v", values[i-1], values[i]) 21 | } 22 | } 23 | 24 | return nil 25 | } 26 | 27 | // ValuesSame returns a ValueComparer for asserting that each value in the sequence of 28 | // the values supplied to the CompareValues method is the same as the preceding value. 29 | func ValuesSame() valuesSame { 30 | return valuesSame{} 31 | } 32 | -------------------------------------------------------------------------------- /config/config.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) HashiCorp, Inc. 2 | // SPDX-License-Identifier: MPL-2.0 3 | 4 | package config 5 | 6 | // TestStepConfigFunc is the callback type used with acceptance tests to 7 | // specify a string which either identifies a directory containing 8 | // Terraform configuration files, or a file that contains Terraform 9 | // configuration. 10 | type TestStepConfigFunc func(TestStepConfigRequest) string 11 | 12 | // TestStepConfigRequest defines the request supplied to types 13 | // implementing TestStepConfigFunc. StepNumber is one-based 14 | // and is used in the predefined helper functions: 15 | // 16 | // - [config.TestStepDirectory] 17 | // - [config.TestStepFile]. 18 | // 19 | // TestName is used in the predefined helper functions: 20 | // 21 | // - [config.TestNameDirectory] 22 | // - [config.TestStepDirectory] 23 | // - [config.TestNameFile] 24 | // - [config.TestStepFile] 25 | type TestStepConfigRequest struct { 26 | StepNumber int 27 | TestName string 28 | } 29 | 30 | // Exec executes TestStepConfigFunc if it is not nil, otherwise an 31 | // empty string is returned. 32 | func (f TestStepConfigFunc) Exec(req TestStepConfigRequest) string { 33 | if f != nil { 34 | return f(req) 35 | } 36 | 37 | return "" 38 | } 39 | -------------------------------------------------------------------------------- /config/constraints.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) HashiCorp, Inc. 2 | // SPDX-License-Identifier: MPL-2.0 3 | 4 | package config 5 | 6 | // anyFloat is a constraint that permits any floating-point type. This type 7 | // definition is copied rather than depending on x/exp/constraints since the 8 | // dependency is otherwise unneeded, the definition is relatively trivial and 9 | // static, and the Go language maintainers are not sure if/where these will live 10 | // in the standard library. 11 | // 12 | // Reference: https://github.com/golang/go/issues/61914 13 | type anyFloat interface { 14 | ~float32 | ~float64 15 | } 16 | 17 | // anyInteger is a constraint that permits any integer type. This type 18 | // definition is copied rather than depending on x/exp/constraints since the 19 | // dependency is otherwise unneeded, the definition is relatively trivial and 20 | // static, and the Go language maintainers are not sure if/where these will live 21 | // in the standard library. 22 | // 23 | // Reference: https://github.com/golang/go/issues/61914 24 | type anyInteger interface { 25 | ~int | ~int8 | ~int16 | ~int32 | ~int64 | 26 | ~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 | ~uintptr 27 | } 28 | -------------------------------------------------------------------------------- /config/directory_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) HashiCorp, Inc. 2 | // SPDX-License-Identifier: MPL-2.0 3 | 4 | package config_test 5 | 6 | import ( 7 | "testing" 8 | 9 | "github.com/hashicorp/terraform-plugin-testing/config" 10 | ) 11 | 12 | func TestTestStepConfigFunc_Exec_Directory(t *testing.T) { 13 | t.Parallel() 14 | 15 | testCases := map[string]struct { 16 | testStepConfigFunc config.TestStepConfigFunc 17 | testStepConfigRequest config.TestStepConfigRequest 18 | expected string 19 | }{ 20 | "static_directory": { 21 | testStepConfigFunc: config.StaticDirectory("name_of_directory"), 22 | expected: "name_of_directory", 23 | }, 24 | "test_name_directory": { 25 | testStepConfigFunc: config.TestNameDirectory(), 26 | testStepConfigRequest: config.TestStepConfigRequest{ 27 | TestName: "TestTestStepConfigFunc_Exec", 28 | }, 29 | expected: "testdata/TestTestStepConfigFunc_Exec", 30 | }, 31 | "test_step_directory": { 32 | testStepConfigFunc: config.TestStepDirectory(), 33 | testStepConfigRequest: config.TestStepConfigRequest{ 34 | StepNumber: 1, 35 | TestName: "TestTestStepConfigFunc_Exec", 36 | }, 37 | expected: "testdata/TestTestStepConfigFunc_Exec/1", 38 | }, 39 | } 40 | 41 | for name, testCase := range testCases { 42 | t.Run(name, func(t *testing.T) { 43 | t.Parallel() 44 | 45 | got := testCase.testStepConfigFunc.Exec(testCase.testStepConfigRequest) 46 | 47 | if testCase.expected != got { 48 | t.Errorf("expected %s, got %s", testCase.expected, got) 49 | } 50 | }) 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /config/doc.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) HashiCorp, Inc. 2 | // SPDX-License-Identifier: MPL-2.0 3 | 4 | // Package config implements functionality for supporting native 5 | // Terraform configuration and variables for testing purposes. 6 | package config 7 | -------------------------------------------------------------------------------- /config/file_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) HashiCorp, Inc. 2 | // SPDX-License-Identifier: MPL-2.0 3 | 4 | package config_test 5 | 6 | import ( 7 | "testing" 8 | 9 | "github.com/hashicorp/terraform-plugin-testing/config" 10 | ) 11 | 12 | func TestTestStepConfigFunc_Exec_File(t *testing.T) { 13 | t.Parallel() 14 | 15 | testCases := map[string]struct { 16 | testStepConfigFunc config.TestStepConfigFunc 17 | testStepConfigRequest config.TestStepConfigRequest 18 | expected string 19 | }{ 20 | "static_file": { 21 | testStepConfigFunc: config.StaticFile("name_of_file"), 22 | expected: "name_of_file", 23 | }, 24 | "test_name_file": { 25 | testStepConfigFunc: config.TestNameFile("test.tf"), 26 | testStepConfigRequest: config.TestStepConfigRequest{ 27 | TestName: "TestTestStepConfigFunc_Exec", 28 | }, 29 | expected: "testdata/TestTestStepConfigFunc_Exec/test.tf", 30 | }, 31 | "test_step_file": { 32 | testStepConfigFunc: config.TestStepFile("test.tf"), 33 | testStepConfigRequest: config.TestStepConfigRequest{ 34 | StepNumber: 1, 35 | TestName: "TestTestStepConfigFunc_Exec", 36 | }, 37 | expected: "testdata/TestTestStepConfigFunc_Exec/1/test.tf", 38 | }, 39 | } 40 | 41 | for name, testCase := range testCases { 42 | t.Run(name, func(t *testing.T) { 43 | t.Parallel() 44 | 45 | got := testCase.testStepConfigFunc.Exec(testCase.testStepConfigRequest) 46 | 47 | if testCase.expected != got { 48 | t.Errorf("expected %s, got %s", testCase.expected, got) 49 | } 50 | }) 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /echoprovider/doc.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) HashiCorp, Inc. 2 | // SPDX-License-Identifier: MPL-2.0 3 | 4 | // Package echoprovider contains a protocol v6 Terraform provider that can be used to transfer data from 5 | // provider configuration to state via a managed resource. This is only meant for provider acceptance testing 6 | // of data that cannot be stored in Terraform artifacts (plan/state), such as an ephemeral resource. 7 | // 8 | // Example Usage: 9 | // 10 | // // Ephemeral resource that is under test 11 | // ephemeral "examplecloud_thing" "this" { 12 | // name = "thing-one" 13 | // } 14 | // 15 | // provider "echo" { 16 | // data = ephemeral.examplecloud_thing.this 17 | // } 18 | // 19 | // resource "echo" "test" {} // The `echo.test.data` attribute will contain the ephemeral data from `ephemeral.examplecloud_thing.this` 20 | package echoprovider 21 | -------------------------------------------------------------------------------- /helper/resource/additional_cli_options.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) HashiCorp, Inc. 2 | // SPDX-License-Identifier: MPL-2.0 3 | 4 | package resource 5 | 6 | // AdditionalCLIOptions allows an intentionally limited set of options to be passed 7 | // to the Terraform CLI when executing test steps. 8 | type AdditionalCLIOptions struct { 9 | // Apply represents options to be passed to the `terraform apply` command. 10 | Apply ApplyOptions 11 | 12 | // Plan represents options to be passed to the `terraform plan` command. 13 | Plan PlanOptions 14 | } 15 | 16 | // ApplyOptions represents options to be passed to the `terraform apply` command. 17 | type ApplyOptions struct { 18 | // AllowDeferral will pass the experimental `-allow-deferral` flag to the apply command. 19 | AllowDeferral bool 20 | } 21 | 22 | // PlanOptions represents options to be passed to the `terraform plan` command. 23 | type PlanOptions struct { 24 | // AllowDeferral will pass the experimental `-allow-deferral` flag to the plan command. 25 | AllowDeferral bool 26 | 27 | // NoRefresh will pass the `-refresh=false` flag to the plan command. 28 | NoRefresh bool 29 | } 30 | -------------------------------------------------------------------------------- /helper/resource/environment_variables.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) HashiCorp, Inc. 2 | // SPDX-License-Identifier: MPL-2.0 3 | 4 | package resource 5 | 6 | // Environment variables for acceptance testing. Additional environment 7 | // variable constants can be found in the internal/plugintest package. 8 | const ( 9 | // Environment variable to enable acceptance tests using this package's 10 | // ParallelTest and Test functions whose TestCase does not enable the 11 | // IsUnitTest field. Defaults to disabled, in which each test will call 12 | // (*testing.T).Skip(). Can be set to any value to enable acceptance tests, 13 | // however "1" is conventional. 14 | EnvTfAcc = "TF_ACC" 15 | 16 | // Environment variable with hostname for the provider under acceptance 17 | // test. The hostname is the first portion of the full provider source 18 | // address, such as "example.com" in example.com/myorg/myprovider. Defaults 19 | // to "registry.terraform.io". 20 | // 21 | // Only required if any Terraform configuration set via the TestStep 22 | // type Config field includes a provider source, such as the terraform 23 | // configuration block required_providers attribute. 24 | EnvTfAccProviderHost = "TF_ACC_PROVIDER_HOST" 25 | 26 | // Environment variable with namespace for the provider under acceptance 27 | // test. The namespace is the second portion of the full provider source 28 | // address, such as "myorg" in registry.terraform.io/myorg/myprovider. 29 | // Defaults to "-" for Terraform 0.12-0.13 compatibility and "hashicorp". 30 | // 31 | // Only required if any Terraform configuration set via the TestStep 32 | // type Config field includes a provider source, such as the terraform 33 | // configuration block required_providers attribute. 34 | EnvTfAccProviderNamespace = "TF_ACC_PROVIDER_NAMESPACE" 35 | ) 36 | -------------------------------------------------------------------------------- /helper/resource/importstate/import_block_for_resource_with_a_dependency_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) HashiCorp, Inc. 2 | // SPDX-License-Identifier: MPL-2.0 3 | 4 | package importstate_test 5 | 6 | import ( 7 | "testing" 8 | 9 | "github.com/hashicorp/terraform-plugin-go/tfprotov6" 10 | r "github.com/hashicorp/terraform-plugin-testing/helper/resource" 11 | "github.com/hashicorp/terraform-plugin-testing/internal/testing/testprovider" 12 | "github.com/hashicorp/terraform-plugin-testing/internal/testing/testsdk/providerserver" 13 | "github.com/hashicorp/terraform-plugin-testing/tfversion" 14 | ) 15 | 16 | func TestImportBlockForResourceWithADependency(t *testing.T) { 17 | t.Parallel() 18 | 19 | config := ` 20 | resource "examplecloud_zone" "zone" { 21 | name = "example.net" 22 | } 23 | 24 | resource "examplecloud_zone_record" "record" { 25 | zone_id = examplecloud_zone.zone.id 26 | name = "www" 27 | } 28 | ` 29 | r.UnitTest(t, r.TestCase{ 30 | TerraformVersionChecks: []tfversion.TerraformVersionCheck{ 31 | tfversion.SkipBelow(tfversion.Version1_5_0), // ImportBlockWithID requires Terraform 1.5.0 or later 32 | }, 33 | ProtoV6ProviderFactories: map[string]func() (tfprotov6.ProviderServer, error){ 34 | "examplecloud": providerserver.NewProviderServer(testprovider.Provider{ 35 | Resources: map[string]testprovider.Resource{ 36 | "examplecloud_zone": examplecloudZone(), 37 | "examplecloud_zone_record": examplecloudZoneRecord(), 38 | }, 39 | }), 40 | }, 41 | Steps: []r.TestStep{ 42 | { 43 | Config: config, 44 | }, 45 | { 46 | ImportState: true, 47 | ImportStateKind: r.ImportBlockWithID, 48 | ResourceName: "examplecloud_zone_record.record", 49 | }, 50 | }, 51 | }) 52 | } 53 | -------------------------------------------------------------------------------- /helper/resource/importstate/testdata/1/examplecloud_container.tf: -------------------------------------------------------------------------------- 1 | # Copyright (c) HashiCorp, Inc. 2 | # SPDX-License-Identifier: MPL-2.0 3 | 4 | resource "examplecloud_container" "test" { 5 | name = "somevalue" 6 | location = "westeurope" 7 | } 8 | -------------------------------------------------------------------------------- /helper/resource/importstate/testdata/2/examplecloud_container.tf: -------------------------------------------------------------------------------- 1 | # Copyright (c) HashiCorp, Inc. 2 | # SPDX-License-Identifier: MPL-2.0 3 | 4 | resource "examplecloud_container" "test" { 5 | name = "somevalue" 6 | location = "westeurope" 7 | } 8 | -------------------------------------------------------------------------------- /helper/resource/importstate/testdata/2_with_exact_import_config/examplecloud_container.tf: -------------------------------------------------------------------------------- 1 | # Copyright (c) HashiCorp, Inc. 2 | # SPDX-License-Identifier: MPL-2.0 3 | 4 | resource "examplecloud_container" "test" { 5 | name = "somevalue" 6 | location = "westeurope" 7 | } 8 | 9 | import { 10 | to = examplecloud_container.test 11 | id = "examplecloud_container.test" 12 | } 13 | -------------------------------------------------------------------------------- /helper/resource/importstate/testdata/examplecloud_container_with_exact_import_config_with_id.tf: -------------------------------------------------------------------------------- 1 | # Copyright (c) HashiCorp, Inc. 2 | # SPDX-License-Identifier: MPL-2.0 3 | 4 | resource "examplecloud_container" "test" { 5 | name = "somevalue" 6 | location = "westeurope" 7 | } 8 | 9 | import { 10 | to = examplecloud_container.test 11 | id = "examplecloud_container.test" 12 | } 13 | -------------------------------------------------------------------------------- /helper/resource/importstate/types_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) HashiCorp, Inc. 2 | // SPDX-License-Identifier: MPL-2.0 3 | 4 | package importstate_test 5 | 6 | import ( 7 | "github.com/hashicorp/terraform-plugin-go/tfprotov6" 8 | "github.com/hashicorp/terraform-plugin-go/tftypes" 9 | ) 10 | 11 | func RequiredBoolAttribute(name string) *tfprotov6.SchemaAttribute { 12 | return &tfprotov6.SchemaAttribute{ 13 | Name: name, 14 | Type: tftypes.Bool, 15 | Required: true, 16 | } 17 | } 18 | 19 | func OptionalComputedListAttribute(name string, elementType tftypes.Type) *tfprotov6.SchemaAttribute { 20 | return &tfprotov6.SchemaAttribute{ 21 | Name: name, 22 | Type: tftypes.List{ElementType: elementType}, 23 | Optional: true, 24 | Computed: true, 25 | } 26 | } 27 | 28 | func RequiredListAttribute(name string, elementType tftypes.Type) *tfprotov6.SchemaAttribute { 29 | return &tfprotov6.SchemaAttribute{ 30 | Name: name, 31 | Type: tftypes.List{ElementType: elementType}, 32 | Required: true, 33 | } 34 | } 35 | 36 | func RequiredNumberAttribute(name string) *tfprotov6.SchemaAttribute { 37 | return &tfprotov6.SchemaAttribute{ 38 | Name: name, 39 | Type: tftypes.Number, 40 | Required: true, 41 | } 42 | } 43 | 44 | func ComputedStringAttribute(name string) *tfprotov6.SchemaAttribute { 45 | return &tfprotov6.SchemaAttribute{ 46 | Name: name, 47 | Type: tftypes.String, 48 | Computed: true, 49 | } 50 | } 51 | 52 | func OptionalStringAttribute(name string) *tfprotov6.SchemaAttribute { 53 | return &tfprotov6.SchemaAttribute{ 54 | Name: name, 55 | Type: tftypes.String, 56 | Optional: true, 57 | } 58 | } 59 | 60 | func RequiredStringAttribute(name string) *tfprotov6.SchemaAttribute { 61 | return &tfprotov6.SchemaAttribute{ 62 | Name: name, 63 | Type: tftypes.String, 64 | Required: true, 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /helper/resource/json.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) HashiCorp, Inc. 2 | // SPDX-License-Identifier: MPL-2.0 3 | 4 | package resource 5 | 6 | import ( 7 | "bytes" 8 | "encoding/json" 9 | ) 10 | 11 | func unmarshalJSON(data []byte, v interface{}) error { 12 | dec := json.NewDecoder(bytes.NewReader(data)) 13 | dec.UseNumber() 14 | return dec.Decode(v) 15 | } 16 | -------------------------------------------------------------------------------- /helper/resource/plan_checks.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) HashiCorp, Inc. 2 | // SPDX-License-Identifier: MPL-2.0 3 | 4 | package resource 5 | 6 | import ( 7 | "context" 8 | "errors" 9 | 10 | tfjson "github.com/hashicorp/terraform-json" 11 | "github.com/hashicorp/terraform-plugin-testing/plancheck" 12 | "github.com/mitchellh/go-testing-interface" 13 | ) 14 | 15 | func runPlanChecks(ctx context.Context, t testing.T, plan *tfjson.Plan, planChecks []plancheck.PlanCheck) error { 16 | t.Helper() 17 | 18 | var result []error 19 | 20 | for _, planCheck := range planChecks { 21 | resp := plancheck.CheckPlanResponse{} 22 | planCheck.CheckPlan(ctx, plancheck.CheckPlanRequest{Plan: plan}, &resp) 23 | 24 | result = append(result, resp.Error) 25 | } 26 | 27 | return errors.Join(result...) 28 | } 29 | -------------------------------------------------------------------------------- /helper/resource/plan_checks_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) HashiCorp, Inc. 2 | // SPDX-License-Identifier: MPL-2.0 3 | 4 | package resource 5 | 6 | import ( 7 | "context" 8 | 9 | "github.com/hashicorp/terraform-plugin-testing/plancheck" 10 | ) 11 | 12 | var _ plancheck.PlanCheck = &planCheckSpy{} 13 | 14 | type planCheckSpy struct { 15 | err error 16 | called bool 17 | } 18 | 19 | func (s *planCheckSpy) CheckPlan(ctx context.Context, req plancheck.CheckPlanRequest, resp *plancheck.CheckPlanResponse) { 20 | s.called = true 21 | resp.Error = s.err 22 | } 23 | -------------------------------------------------------------------------------- /helper/resource/state_checks.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) HashiCorp, Inc. 2 | // SPDX-License-Identifier: MPL-2.0 3 | 4 | package resource 5 | 6 | import ( 7 | "context" 8 | "errors" 9 | 10 | tfjson "github.com/hashicorp/terraform-json" 11 | "github.com/mitchellh/go-testing-interface" 12 | 13 | "github.com/hashicorp/terraform-plugin-testing/statecheck" 14 | ) 15 | 16 | func runStateChecks(ctx context.Context, t testing.T, state *tfjson.State, stateChecks []statecheck.StateCheck) error { 17 | t.Helper() 18 | 19 | var result []error 20 | 21 | for _, stateCheck := range stateChecks { 22 | resp := statecheck.CheckStateResponse{} 23 | stateCheck.CheckState(ctx, statecheck.CheckStateRequest{State: state}, &resp) 24 | 25 | result = append(result, resp.Error) 26 | } 27 | 28 | return errors.Join(result...) 29 | } 30 | -------------------------------------------------------------------------------- /helper/resource/state_checks_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) HashiCorp, Inc. 2 | // SPDX-License-Identifier: MPL-2.0 3 | 4 | package resource 5 | 6 | import ( 7 | "context" 8 | 9 | "github.com/hashicorp/terraform-plugin-testing/statecheck" 10 | ) 11 | 12 | var _ statecheck.StateCheck = &stateCheckSpy{} 13 | 14 | type stateCheckSpy struct { 15 | err error 16 | called bool 17 | } 18 | 19 | func (s *stateCheckSpy) CheckState(ctx context.Context, req statecheck.CheckStateRequest, resp *statecheck.CheckStateResponse) { 20 | s.called = true 21 | resp.Error = s.err 22 | } 23 | -------------------------------------------------------------------------------- /helper/resource/testdata/TestTest_ConfigDirectory_TestNameDirectory/random.tf: -------------------------------------------------------------------------------- 1 | # Copyright (c) HashiCorp, Inc. 2 | # SPDX-License-Identifier: MPL-2.0 3 | 4 | terraform { 5 | required_providers { 6 | random = { 7 | source = "registry.terraform.io/hashicorp/random" 8 | version = "3.5.1" 9 | } 10 | } 11 | } 12 | 13 | provider "random" {} 14 | 15 | resource "random_password" "test" { 16 | length = 8 17 | 18 | numeric = false 19 | } -------------------------------------------------------------------------------- /helper/resource/testdata/TestTest_ConfigDirectory_TestNameDirectory_AttributeDoesNotExist/random.tf: -------------------------------------------------------------------------------- 1 | # Copyright (c) HashiCorp, Inc. 2 | # SPDX-License-Identifier: MPL-2.0 3 | 4 | terraform { 5 | required_providers { 6 | random = { 7 | source = "registry.terraform.io/hashicorp/random" 8 | version = "3.2.0" 9 | } 10 | } 11 | } 12 | 13 | provider "random" {} 14 | 15 | resource "random_password" "test" { 16 | length = 8 17 | 18 | numeric = false 19 | } -------------------------------------------------------------------------------- /helper/resource/testdata/TestTest_ConfigDirectory_TestNameDirectory_AttributeDoesNotExist_MultipleFiles/provider.tf: -------------------------------------------------------------------------------- 1 | # Copyright (c) HashiCorp, Inc. 2 | # SPDX-License-Identifier: MPL-2.0 3 | 4 | provider "random" {} -------------------------------------------------------------------------------- /helper/resource/testdata/TestTest_ConfigDirectory_TestNameDirectory_AttributeDoesNotExist_MultipleFiles/random.tf: -------------------------------------------------------------------------------- 1 | # Copyright (c) HashiCorp, Inc. 2 | # SPDX-License-Identifier: MPL-2.0 3 | 4 | resource "random_password" "test" { 5 | length = 8 6 | 7 | numeric = false 8 | } -------------------------------------------------------------------------------- /helper/resource/testdata/TestTest_ConfigDirectory_TestNameDirectory_AttributeDoesNotExist_MultipleFiles/terraform.tf: -------------------------------------------------------------------------------- 1 | # Copyright (c) HashiCorp, Inc. 2 | # SPDX-License-Identifier: MPL-2.0 3 | 4 | terraform { 5 | required_providers { 6 | random = { 7 | source = "registry.terraform.io/hashicorp/random" 8 | version = "3.2.0" 9 | } 10 | } 11 | } -------------------------------------------------------------------------------- /helper/resource/testdata/TestTest_ConfigDirectory_TestNameDirectory_AttributeDoesNotExist_MultipleFiles_Vars/provider.tf: -------------------------------------------------------------------------------- 1 | # Copyright (c) HashiCorp, Inc. 2 | # SPDX-License-Identifier: MPL-2.0 3 | 4 | provider "random" {} -------------------------------------------------------------------------------- /helper/resource/testdata/TestTest_ConfigDirectory_TestNameDirectory_AttributeDoesNotExist_MultipleFiles_Vars/random.tf: -------------------------------------------------------------------------------- 1 | # Copyright (c) HashiCorp, Inc. 2 | # SPDX-License-Identifier: MPL-2.0 3 | 4 | resource "random_password" "test" { 5 | length = var.length 6 | 7 | numeric = var.numeric 8 | } -------------------------------------------------------------------------------- /helper/resource/testdata/TestTest_ConfigDirectory_TestNameDirectory_AttributeDoesNotExist_MultipleFiles_Vars/terraform.tf: -------------------------------------------------------------------------------- 1 | # Copyright (c) HashiCorp, Inc. 2 | # SPDX-License-Identifier: MPL-2.0 3 | 4 | terraform { 5 | required_providers { 6 | random = { 7 | source = "registry.terraform.io/hashicorp/random" 8 | version = "3.2.0" 9 | } 10 | } 11 | } -------------------------------------------------------------------------------- /helper/resource/testdata/TestTest_ConfigDirectory_TestNameDirectory_AttributeDoesNotExist_MultipleFiles_Vars/vars.tf: -------------------------------------------------------------------------------- 1 | # Copyright (c) HashiCorp, Inc. 2 | # SPDX-License-Identifier: MPL-2.0 3 | 4 | variable "length" { 5 | type = number 6 | } 7 | 8 | variable "numeric" { 9 | type = bool 10 | } -------------------------------------------------------------------------------- /helper/resource/testdata/TestTest_ConfigDirectory_TestNameDirectory_AttributeDoesNotExist_Vars/random.tf: -------------------------------------------------------------------------------- 1 | # Copyright (c) HashiCorp, Inc. 2 | # SPDX-License-Identifier: MPL-2.0 3 | 4 | terraform { 5 | required_providers { 6 | random = { 7 | source = "registry.terraform.io/hashicorp/random" 8 | version = "3.2.0" 9 | } 10 | } 11 | } 12 | 13 | provider "random" {} 14 | 15 | resource "random_password" "test" { 16 | length = var.length 17 | 18 | numeric = var.numeric 19 | } -------------------------------------------------------------------------------- /helper/resource/testdata/TestTest_ConfigDirectory_TestNameDirectory_AttributeDoesNotExist_Vars/vars.tf: -------------------------------------------------------------------------------- 1 | # Copyright (c) HashiCorp, Inc. 2 | # SPDX-License-Identifier: MPL-2.0 3 | 4 | variable "length" { 5 | type = number 6 | } 7 | 8 | variable "numeric" { 9 | type = bool 10 | } -------------------------------------------------------------------------------- /helper/resource/testdata/TestTest_ConfigDirectory_TestNameDirectory_MultipleFiles/provider.tf: -------------------------------------------------------------------------------- 1 | # Copyright (c) HashiCorp, Inc. 2 | # SPDX-License-Identifier: MPL-2.0 3 | 4 | provider "random" {} -------------------------------------------------------------------------------- /helper/resource/testdata/TestTest_ConfigDirectory_TestNameDirectory_MultipleFiles/random.tf: -------------------------------------------------------------------------------- 1 | # Copyright (c) HashiCorp, Inc. 2 | # SPDX-License-Identifier: MPL-2.0 3 | 4 | resource "random_password" "test" { 5 | length = 8 6 | 7 | numeric = false 8 | } -------------------------------------------------------------------------------- /helper/resource/testdata/TestTest_ConfigDirectory_TestNameDirectory_MultipleFiles/terraform.tf: -------------------------------------------------------------------------------- 1 | # Copyright (c) HashiCorp, Inc. 2 | # SPDX-License-Identifier: MPL-2.0 3 | 4 | terraform { 5 | required_providers { 6 | random = { 7 | source = "registry.terraform.io/hashicorp/random" 8 | version = "3.5.1" 9 | } 10 | } 11 | } -------------------------------------------------------------------------------- /helper/resource/testdata/TestTest_ConfigDirectory_TestNameDirectory_MultipleFiles_Vars/provider.tf: -------------------------------------------------------------------------------- 1 | # Copyright (c) HashiCorp, Inc. 2 | # SPDX-License-Identifier: MPL-2.0 3 | 4 | provider "random" {} -------------------------------------------------------------------------------- /helper/resource/testdata/TestTest_ConfigDirectory_TestNameDirectory_MultipleFiles_Vars/random.tf: -------------------------------------------------------------------------------- 1 | # Copyright (c) HashiCorp, Inc. 2 | # SPDX-License-Identifier: MPL-2.0 3 | 4 | resource "random_password" "test" { 5 | length = 8 6 | 7 | numeric = false 8 | } -------------------------------------------------------------------------------- /helper/resource/testdata/TestTest_ConfigDirectory_TestNameDirectory_MultipleFiles_Vars/terraform.tf: -------------------------------------------------------------------------------- 1 | # Copyright (c) HashiCorp, Inc. 2 | # SPDX-License-Identifier: MPL-2.0 3 | 4 | terraform { 5 | required_providers { 6 | random = { 7 | source = "registry.terraform.io/hashicorp/random" 8 | version = "3.5.1" 9 | } 10 | } 11 | } -------------------------------------------------------------------------------- /helper/resource/testdata/TestTest_ConfigDirectory_TestNameDirectory_MultipleFiles_Vars/vars.tf: -------------------------------------------------------------------------------- 1 | # Copyright (c) HashiCorp, Inc. 2 | # SPDX-License-Identifier: MPL-2.0 3 | 4 | variable "length" { 5 | type = number 6 | } 7 | 8 | variable "numeric" { 9 | type = bool 10 | } -------------------------------------------------------------------------------- /helper/resource/testdata/TestTest_ConfigDirectory_TestNameDirectory_Vars/random.tf: -------------------------------------------------------------------------------- 1 | # Copyright (c) HashiCorp, Inc. 2 | # SPDX-License-Identifier: MPL-2.0 3 | 4 | terraform { 5 | required_providers { 6 | random = { 7 | source = "registry.terraform.io/hashicorp/random" 8 | version = "3.5.1" 9 | } 10 | } 11 | } 12 | 13 | provider "random" {} 14 | 15 | resource "random_password" "test" { 16 | length = var.length 17 | 18 | numeric = var.numeric 19 | } -------------------------------------------------------------------------------- /helper/resource/testdata/TestTest_ConfigDirectory_TestNameDirectory_Vars/vars.tf: -------------------------------------------------------------------------------- 1 | # Copyright (c) HashiCorp, Inc. 2 | # SPDX-License-Identifier: MPL-2.0 3 | 4 | variable "length" { 5 | type = number 6 | } 7 | 8 | variable "numeric" { 9 | type = bool 10 | } -------------------------------------------------------------------------------- /helper/resource/testdata/TestTest_ConfigDirectory_TestStepDirectory/1/random.tf: -------------------------------------------------------------------------------- 1 | # Copyright (c) HashiCorp, Inc. 2 | # SPDX-License-Identifier: MPL-2.0 3 | 4 | terraform { 5 | required_providers { 6 | random = { 7 | source = "registry.terraform.io/hashicorp/random" 8 | version = "3.5.1" 9 | } 10 | } 11 | } 12 | 13 | provider "random" {} 14 | 15 | resource "random_password" "test" { 16 | length = 8 17 | 18 | numeric = false 19 | } -------------------------------------------------------------------------------- /helper/resource/testdata/TestTest_ConfigDirectory_TestStepDirectory_AttributeDoesNotExist/1/random.tf: -------------------------------------------------------------------------------- 1 | # Copyright (c) HashiCorp, Inc. 2 | # SPDX-License-Identifier: MPL-2.0 3 | 4 | terraform { 5 | required_providers { 6 | random = { 7 | source = "registry.terraform.io/hashicorp/random" 8 | version = "3.2.0" 9 | } 10 | } 11 | } 12 | 13 | provider "random" {} 14 | 15 | resource "random_password" "test" { 16 | length = 8 17 | 18 | numeric = false 19 | } -------------------------------------------------------------------------------- /helper/resource/testdata/TestTest_ConfigDirectory_TestStepDirectory_AttributeDoesNotExist_MultipleFiles/1/provider.tf: -------------------------------------------------------------------------------- 1 | # Copyright (c) HashiCorp, Inc. 2 | # SPDX-License-Identifier: MPL-2.0 3 | 4 | provider "random" {} -------------------------------------------------------------------------------- /helper/resource/testdata/TestTest_ConfigDirectory_TestStepDirectory_AttributeDoesNotExist_MultipleFiles/1/random.tf: -------------------------------------------------------------------------------- 1 | # Copyright (c) HashiCorp, Inc. 2 | # SPDX-License-Identifier: MPL-2.0 3 | 4 | resource "random_password" "test" { 5 | length = 8 6 | 7 | numeric = false 8 | } -------------------------------------------------------------------------------- /helper/resource/testdata/TestTest_ConfigDirectory_TestStepDirectory_AttributeDoesNotExist_MultipleFiles/1/terraform.tf: -------------------------------------------------------------------------------- 1 | # Copyright (c) HashiCorp, Inc. 2 | # SPDX-License-Identifier: MPL-2.0 3 | 4 | terraform { 5 | required_providers { 6 | random = { 7 | source = "registry.terraform.io/hashicorp/random" 8 | version = "3.2.0" 9 | } 10 | } 11 | } -------------------------------------------------------------------------------- /helper/resource/testdata/TestTest_ConfigDirectory_TestStepDirectory_AttributeDoesNotExist_MultipleFiles_Vars/1/provider.tf: -------------------------------------------------------------------------------- 1 | # Copyright (c) HashiCorp, Inc. 2 | # SPDX-License-Identifier: MPL-2.0 3 | 4 | provider "random" {} -------------------------------------------------------------------------------- /helper/resource/testdata/TestTest_ConfigDirectory_TestStepDirectory_AttributeDoesNotExist_MultipleFiles_Vars/1/random.tf: -------------------------------------------------------------------------------- 1 | # Copyright (c) HashiCorp, Inc. 2 | # SPDX-License-Identifier: MPL-2.0 3 | 4 | resource "random_password" "test" { 5 | length = var.length 6 | 7 | numeric = var.numeric 8 | } -------------------------------------------------------------------------------- /helper/resource/testdata/TestTest_ConfigDirectory_TestStepDirectory_AttributeDoesNotExist_MultipleFiles_Vars/1/terraform.tf: -------------------------------------------------------------------------------- 1 | # Copyright (c) HashiCorp, Inc. 2 | # SPDX-License-Identifier: MPL-2.0 3 | 4 | terraform { 5 | required_providers { 6 | random = { 7 | source = "registry.terraform.io/hashicorp/random" 8 | version = "3.2.0" 9 | } 10 | } 11 | } -------------------------------------------------------------------------------- /helper/resource/testdata/TestTest_ConfigDirectory_TestStepDirectory_AttributeDoesNotExist_MultipleFiles_Vars/1/vars.tf: -------------------------------------------------------------------------------- 1 | # Copyright (c) HashiCorp, Inc. 2 | # SPDX-License-Identifier: MPL-2.0 3 | 4 | variable "length" { 5 | type = number 6 | } 7 | 8 | variable "numeric" { 9 | type = bool 10 | } -------------------------------------------------------------------------------- /helper/resource/testdata/TestTest_ConfigDirectory_TestStepDirectory_AttributeDoesNotExist_Vars/1/random.tf: -------------------------------------------------------------------------------- 1 | # Copyright (c) HashiCorp, Inc. 2 | # SPDX-License-Identifier: MPL-2.0 3 | 4 | terraform { 5 | required_providers { 6 | random = { 7 | source = "registry.terraform.io/hashicorp/random" 8 | version = "3.2.0" 9 | } 10 | } 11 | } 12 | 13 | provider "random" {} 14 | 15 | resource "random_password" "test" { 16 | length = var.length 17 | 18 | numeric = var.numeric 19 | } -------------------------------------------------------------------------------- /helper/resource/testdata/TestTest_ConfigDirectory_TestStepDirectory_AttributeDoesNotExist_Vars/1/vars.tf: -------------------------------------------------------------------------------- 1 | # Copyright (c) HashiCorp, Inc. 2 | # SPDX-License-Identifier: MPL-2.0 3 | 4 | variable "length" { 5 | type = number 6 | } 7 | 8 | variable "numeric" { 9 | type = bool 10 | } -------------------------------------------------------------------------------- /helper/resource/testdata/TestTest_ConfigDirectory_TestStepDirectory_MultipleFiles/1/provider.tf: -------------------------------------------------------------------------------- 1 | # Copyright (c) HashiCorp, Inc. 2 | # SPDX-License-Identifier: MPL-2.0 3 | 4 | provider "random" {} -------------------------------------------------------------------------------- /helper/resource/testdata/TestTest_ConfigDirectory_TestStepDirectory_MultipleFiles/1/random.tf: -------------------------------------------------------------------------------- 1 | # Copyright (c) HashiCorp, Inc. 2 | # SPDX-License-Identifier: MPL-2.0 3 | 4 | resource "random_password" "test" { 5 | length = 8 6 | 7 | numeric = false 8 | } -------------------------------------------------------------------------------- /helper/resource/testdata/TestTest_ConfigDirectory_TestStepDirectory_MultipleFiles/1/terraform.tf: -------------------------------------------------------------------------------- 1 | # Copyright (c) HashiCorp, Inc. 2 | # SPDX-License-Identifier: MPL-2.0 3 | 4 | terraform { 5 | required_providers { 6 | random = { 7 | source = "registry.terraform.io/hashicorp/random" 8 | version = "3.5.1" 9 | } 10 | } 11 | } -------------------------------------------------------------------------------- /helper/resource/testdata/TestTest_ConfigDirectory_TestStepDirectory_MultipleFiles_StepNotHardcoded/1/provider_1.tf: -------------------------------------------------------------------------------- 1 | # Copyright (c) HashiCorp, Inc. 2 | # SPDX-License-Identifier: MPL-2.0 3 | 4 | provider "random" {} -------------------------------------------------------------------------------- /helper/resource/testdata/TestTest_ConfigDirectory_TestStepDirectory_MultipleFiles_StepNotHardcoded/1/random_1.tf: -------------------------------------------------------------------------------- 1 | # Copyright (c) HashiCorp, Inc. 2 | # SPDX-License-Identifier: MPL-2.0 3 | 4 | resource "random_password" "test" { 5 | length = 8 6 | 7 | numeric = false 8 | } -------------------------------------------------------------------------------- /helper/resource/testdata/TestTest_ConfigDirectory_TestStepDirectory_MultipleFiles_StepNotHardcoded/1/terraform_1.tf: -------------------------------------------------------------------------------- 1 | # Copyright (c) HashiCorp, Inc. 2 | # SPDX-License-Identifier: MPL-2.0 3 | 4 | terraform { 5 | required_providers { 6 | random = { 7 | source = "registry.terraform.io/hashicorp/random" 8 | version = "3.2.0" 9 | } 10 | } 11 | } -------------------------------------------------------------------------------- /helper/resource/testdata/TestTest_ConfigDirectory_TestStepDirectory_MultipleFiles_StepNotHardcoded/2/provider_2.tf: -------------------------------------------------------------------------------- 1 | # Copyright (c) HashiCorp, Inc. 2 | # SPDX-License-Identifier: MPL-2.0 3 | 4 | provider "random" {} -------------------------------------------------------------------------------- /helper/resource/testdata/TestTest_ConfigDirectory_TestStepDirectory_MultipleFiles_StepNotHardcoded/2/random_2.tf: -------------------------------------------------------------------------------- 1 | # Copyright (c) HashiCorp, Inc. 2 | # SPDX-License-Identifier: MPL-2.0 3 | 4 | resource "random_password" "test" { 5 | length = 9 6 | 7 | numeric = false 8 | } -------------------------------------------------------------------------------- /helper/resource/testdata/TestTest_ConfigDirectory_TestStepDirectory_MultipleFiles_StepNotHardcoded/2/terraform_2.tf: -------------------------------------------------------------------------------- 1 | # Copyright (c) HashiCorp, Inc. 2 | # SPDX-License-Identifier: MPL-2.0 3 | 4 | terraform { 5 | required_providers { 6 | random = { 7 | source = "registry.terraform.io/hashicorp/random" 8 | version = "3.5.1" 9 | } 10 | } 11 | } -------------------------------------------------------------------------------- /helper/resource/testdata/TestTest_ConfigDirectory_TestStepDirectory_MultipleFiles_Vars/1/provider.tf: -------------------------------------------------------------------------------- 1 | # Copyright (c) HashiCorp, Inc. 2 | # SPDX-License-Identifier: MPL-2.0 3 | 4 | provider "random" {} -------------------------------------------------------------------------------- /helper/resource/testdata/TestTest_ConfigDirectory_TestStepDirectory_MultipleFiles_Vars/1/random.tf: -------------------------------------------------------------------------------- 1 | # Copyright (c) HashiCorp, Inc. 2 | # SPDX-License-Identifier: MPL-2.0 3 | 4 | resource "random_password" "test" { 5 | length = var.length 6 | 7 | numeric = var.numeric 8 | } -------------------------------------------------------------------------------- /helper/resource/testdata/TestTest_ConfigDirectory_TestStepDirectory_MultipleFiles_Vars/1/terraform.tf: -------------------------------------------------------------------------------- 1 | # Copyright (c) HashiCorp, Inc. 2 | # SPDX-License-Identifier: MPL-2.0 3 | 4 | terraform { 5 | required_providers { 6 | random = { 7 | source = "registry.terraform.io/hashicorp/random" 8 | version = "3.5.1" 9 | } 10 | } 11 | } -------------------------------------------------------------------------------- /helper/resource/testdata/TestTest_ConfigDirectory_TestStepDirectory_MultipleFiles_Vars/1/vars.tf: -------------------------------------------------------------------------------- 1 | # Copyright (c) HashiCorp, Inc. 2 | # SPDX-License-Identifier: MPL-2.0 3 | 4 | variable "length" { 5 | type = number 6 | } 7 | 8 | variable "numeric" { 9 | type = bool 10 | } -------------------------------------------------------------------------------- /helper/resource/testdata/TestTest_ConfigDirectory_TestStepDirectory_MultipleFiles_Vars_StepNotHardcoded/1/provider_1.tf: -------------------------------------------------------------------------------- 1 | # Copyright (c) HashiCorp, Inc. 2 | # SPDX-License-Identifier: MPL-2.0 3 | 4 | provider "random" {} -------------------------------------------------------------------------------- /helper/resource/testdata/TestTest_ConfigDirectory_TestStepDirectory_MultipleFiles_Vars_StepNotHardcoded/1/random_1.tf: -------------------------------------------------------------------------------- 1 | # Copyright (c) HashiCorp, Inc. 2 | # SPDX-License-Identifier: MPL-2.0 3 | 4 | resource "random_password" "test" { 5 | length = var.length 6 | 7 | numeric = var.numeric 8 | } -------------------------------------------------------------------------------- /helper/resource/testdata/TestTest_ConfigDirectory_TestStepDirectory_MultipleFiles_Vars_StepNotHardcoded/1/terraform_1.tf: -------------------------------------------------------------------------------- 1 | # Copyright (c) HashiCorp, Inc. 2 | # SPDX-License-Identifier: MPL-2.0 3 | 4 | terraform { 5 | required_providers { 6 | random = { 7 | source = "registry.terraform.io/hashicorp/random" 8 | version = "3.2.0" 9 | } 10 | } 11 | } -------------------------------------------------------------------------------- /helper/resource/testdata/TestTest_ConfigDirectory_TestStepDirectory_MultipleFiles_Vars_StepNotHardcoded/1/vars_1.tf: -------------------------------------------------------------------------------- 1 | # Copyright (c) HashiCorp, Inc. 2 | # SPDX-License-Identifier: MPL-2.0 3 | 4 | variable "length" { 5 | type = number 6 | } 7 | 8 | variable "numeric" { 9 | type = bool 10 | } -------------------------------------------------------------------------------- /helper/resource/testdata/TestTest_ConfigDirectory_TestStepDirectory_MultipleFiles_Vars_StepNotHardcoded/2/provider_2.tf: -------------------------------------------------------------------------------- 1 | # Copyright (c) HashiCorp, Inc. 2 | # SPDX-License-Identifier: MPL-2.0 3 | 4 | provider "random" {} -------------------------------------------------------------------------------- /helper/resource/testdata/TestTest_ConfigDirectory_TestStepDirectory_MultipleFiles_Vars_StepNotHardcoded/2/random2.tf: -------------------------------------------------------------------------------- 1 | # Copyright (c) HashiCorp, Inc. 2 | # SPDX-License-Identifier: MPL-2.0 3 | 4 | resource "random_password" "test" { 5 | length = var.length 6 | 7 | numeric = var.numeric 8 | } -------------------------------------------------------------------------------- /helper/resource/testdata/TestTest_ConfigDirectory_TestStepDirectory_MultipleFiles_Vars_StepNotHardcoded/2/terraform_2.tf: -------------------------------------------------------------------------------- 1 | # Copyright (c) HashiCorp, Inc. 2 | # SPDX-License-Identifier: MPL-2.0 3 | 4 | terraform { 5 | required_providers { 6 | random = { 7 | source = "registry.terraform.io/hashicorp/random" 8 | version = "3.5.1" 9 | } 10 | } 11 | } -------------------------------------------------------------------------------- /helper/resource/testdata/TestTest_ConfigDirectory_TestStepDirectory_MultipleFiles_Vars_StepNotHardcoded/2/vars_2.tf: -------------------------------------------------------------------------------- 1 | # Copyright (c) HashiCorp, Inc. 2 | # SPDX-License-Identifier: MPL-2.0 3 | 4 | variable "length" { 5 | type = number 6 | } 7 | 8 | variable "numeric" { 9 | type = bool 10 | } -------------------------------------------------------------------------------- /helper/resource/testdata/TestTest_ConfigDirectory_TestStepDirectory_StepNotHardcoded/1/random_1.tf: -------------------------------------------------------------------------------- 1 | # Copyright (c) HashiCorp, Inc. 2 | # SPDX-License-Identifier: MPL-2.0 3 | 4 | terraform { 5 | required_providers { 6 | random = { 7 | source = "registry.terraform.io/hashicorp/random" 8 | version = "3.2.0" 9 | } 10 | } 11 | } 12 | 13 | provider "random" {} 14 | 15 | resource "random_password" "test" { 16 | length = 8 17 | 18 | numeric = false 19 | } -------------------------------------------------------------------------------- /helper/resource/testdata/TestTest_ConfigDirectory_TestStepDirectory_StepNotHardcoded/2/random_2.tf: -------------------------------------------------------------------------------- 1 | # Copyright (c) HashiCorp, Inc. 2 | # SPDX-License-Identifier: MPL-2.0 3 | 4 | terraform { 5 | required_providers { 6 | random = { 7 | source = "registry.terraform.io/hashicorp/random" 8 | version = "3.5.1" 9 | } 10 | } 11 | } 12 | 13 | provider "random" {} 14 | 15 | resource "random_password" "test" { 16 | length = 9 17 | 18 | numeric = false 19 | } -------------------------------------------------------------------------------- /helper/resource/testdata/TestTest_ConfigDirectory_TestStepDirectory_Vars/1/random.tf: -------------------------------------------------------------------------------- 1 | # Copyright (c) HashiCorp, Inc. 2 | # SPDX-License-Identifier: MPL-2.0 3 | 4 | terraform { 5 | required_providers { 6 | random = { 7 | source = "registry.terraform.io/hashicorp/random" 8 | version = "3.5.1" 9 | } 10 | } 11 | } 12 | 13 | provider "random" {} 14 | 15 | resource "random_password" "test" { 16 | length = var.length 17 | 18 | numeric = var.numeric 19 | } -------------------------------------------------------------------------------- /helper/resource/testdata/TestTest_ConfigDirectory_TestStepDirectory_Vars/1/vars.tf: -------------------------------------------------------------------------------- 1 | # Copyright (c) HashiCorp, Inc. 2 | # SPDX-License-Identifier: MPL-2.0 3 | 4 | variable "length" { 5 | type = number 6 | } 7 | 8 | variable "numeric" { 9 | type = bool 10 | } -------------------------------------------------------------------------------- /helper/resource/testdata/TestTest_ConfigFile_TestNameFile/random.tf: -------------------------------------------------------------------------------- 1 | # Copyright (c) HashiCorp, Inc. 2 | # SPDX-License-Identifier: MPL-2.0 3 | 4 | terraform { 5 | required_providers { 6 | random = { 7 | source = "registry.terraform.io/hashicorp/random" 8 | version = "3.5.1" 9 | } 10 | } 11 | } 12 | 13 | provider "random" {} 14 | 15 | resource "random_password" "test" { 16 | length = 8 17 | 18 | numeric = false 19 | } -------------------------------------------------------------------------------- /helper/resource/testdata/TestTest_ConfigFile_TestNameFile_AttributeDoesNotExist/random.tf: -------------------------------------------------------------------------------- 1 | # Copyright (c) HashiCorp, Inc. 2 | # SPDX-License-Identifier: MPL-2.0 3 | 4 | terraform { 5 | required_providers { 6 | random = { 7 | source = "registry.terraform.io/hashicorp/random" 8 | version = "3.2.0" 9 | } 10 | } 11 | } 12 | 13 | provider "random" {} 14 | 15 | resource "random_password" "test" { 16 | length = 8 17 | 18 | numeric = false 19 | } -------------------------------------------------------------------------------- /helper/resource/testdata/TestTest_ConfigFile_TestNameFile_AttributeDoesNotExist_Vars/random.tf: -------------------------------------------------------------------------------- 1 | # Copyright (c) HashiCorp, Inc. 2 | # SPDX-License-Identifier: MPL-2.0 3 | 4 | terraform { 5 | required_providers { 6 | random = { 7 | source = "registry.terraform.io/hashicorp/random" 8 | version = "3.2.0" 9 | } 10 | } 11 | } 12 | 13 | provider "random" {} 14 | 15 | resource "random_password" "test" { 16 | length = var.length 17 | 18 | numeric = var.numeric 19 | } 20 | 21 | variable "length" { 22 | type = number 23 | } 24 | 25 | variable "numeric" { 26 | type = bool 27 | } 28 | -------------------------------------------------------------------------------- /helper/resource/testdata/TestTest_ConfigFile_TestNameFile_Vars/random.tf: -------------------------------------------------------------------------------- 1 | # Copyright (c) HashiCorp, Inc. 2 | # SPDX-License-Identifier: MPL-2.0 3 | 4 | terraform { 5 | required_providers { 6 | random = { 7 | source = "registry.terraform.io/hashicorp/random" 8 | version = "3.5.1" 9 | } 10 | } 11 | } 12 | 13 | provider "random" {} 14 | 15 | resource "random_password" "test" { 16 | length = var.length 17 | 18 | numeric = var.numeric 19 | } 20 | 21 | variable "length" { 22 | type = number 23 | } 24 | 25 | variable "numeric" { 26 | type = bool 27 | } -------------------------------------------------------------------------------- /helper/resource/testdata/TestTest_ConfigFile_TestStepFile/1/random.tf: -------------------------------------------------------------------------------- 1 | # Copyright (c) HashiCorp, Inc. 2 | # SPDX-License-Identifier: MPL-2.0 3 | 4 | terraform { 5 | required_providers { 6 | random = { 7 | source = "registry.terraform.io/hashicorp/random" 8 | version = "3.5.1" 9 | } 10 | } 11 | } 12 | 13 | provider "random" {} 14 | 15 | resource "random_password" "test" { 16 | length = 8 17 | 18 | numeric = false 19 | } -------------------------------------------------------------------------------- /helper/resource/testdata/TestTest_ConfigFile_TestStepFile_AttributeDoesNotExist/1/random.tf: -------------------------------------------------------------------------------- 1 | # Copyright (c) HashiCorp, Inc. 2 | # SPDX-License-Identifier: MPL-2.0 3 | 4 | terraform { 5 | required_providers { 6 | random = { 7 | source = "registry.terraform.io/hashicorp/random" 8 | version = "3.2.0" 9 | } 10 | } 11 | } 12 | 13 | provider "random" {} 14 | 15 | resource "random_password" "test" { 16 | length = 8 17 | 18 | numeric = false 19 | } -------------------------------------------------------------------------------- /helper/resource/testdata/TestTest_ConfigFile_TestStepFile_AttributeDoesNotExist_Vars/1/random.tf: -------------------------------------------------------------------------------- 1 | # Copyright (c) HashiCorp, Inc. 2 | # SPDX-License-Identifier: MPL-2.0 3 | 4 | terraform { 5 | required_providers { 6 | random = { 7 | source = "registry.terraform.io/hashicorp/random" 8 | version = "3.2.0" 9 | } 10 | } 11 | } 12 | 13 | provider "random" {} 14 | 15 | resource "random_password" "test" { 16 | length = var.length 17 | 18 | numeric = var.numeric 19 | } 20 | 21 | variable "length" { 22 | type = number 23 | } 24 | 25 | variable "numeric" { 26 | type = bool 27 | } -------------------------------------------------------------------------------- /helper/resource/testdata/TestTest_ConfigFile_TestStepFile_Vars/1/random.tf: -------------------------------------------------------------------------------- 1 | # Copyright (c) HashiCorp, Inc. 2 | # SPDX-License-Identifier: MPL-2.0 3 | 4 | terraform { 5 | required_providers { 6 | random = { 7 | source = "registry.terraform.io/hashicorp/random" 8 | version = "3.5.1" 9 | } 10 | } 11 | } 12 | 13 | provider "random" {} 14 | 15 | resource "random_password" "test" { 16 | length = var.length 17 | 18 | numeric = var.numeric 19 | } 20 | 21 | variable "length" { 22 | type = number 23 | } 24 | 25 | variable "numeric" { 26 | type = bool 27 | } -------------------------------------------------------------------------------- /helper/resource/testdata/TestTest_TestStep_ProviderFactories_ConfigDirectory_TestNameDirectory/random.tf: -------------------------------------------------------------------------------- 1 | # Copyright (c) HashiCorp, Inc. 2 | # SPDX-License-Identifier: MPL-2.0 3 | 4 | resource "random_id" "test" {} -------------------------------------------------------------------------------- /helper/resource/testdata/TestTest_TestStep_ProviderFactories_ConfigDirectory_TestStepDirectory/1/random.tf: -------------------------------------------------------------------------------- 1 | # Copyright (c) HashiCorp, Inc. 2 | # SPDX-License-Identifier: MPL-2.0 3 | 4 | resource "random_id" "test" {} -------------------------------------------------------------------------------- /helper/resource/testdata/TestTest_TestStep_ProviderFactories_ConfigFile_TestNameFile/random.tf: -------------------------------------------------------------------------------- 1 | # Copyright (c) HashiCorp, Inc. 2 | # SPDX-License-Identifier: MPL-2.0 3 | 4 | resource "random_id" "test" {} -------------------------------------------------------------------------------- /helper/resource/testdata/TestTest_TestStep_ProviderFactories_ConfigFile_TestStepFile/1/random.tf: -------------------------------------------------------------------------------- 1 | # Copyright (c) HashiCorp, Inc. 2 | # SPDX-License-Identifier: MPL-2.0 3 | 4 | resource "random_id" "test" {} -------------------------------------------------------------------------------- /helper/resource/testdata/fixtures/random_id/random.tf: -------------------------------------------------------------------------------- 1 | # Copyright (c) HashiCorp, Inc. 2 | # SPDX-License-Identifier: MPL-2.0 3 | 4 | resource "random_id" "test" {} -------------------------------------------------------------------------------- /helper/resource/testdata/fixtures/random_password_3.2.0/random.tf: -------------------------------------------------------------------------------- 1 | # Copyright (c) HashiCorp, Inc. 2 | # SPDX-License-Identifier: MPL-2.0 3 | 4 | terraform { 5 | required_providers { 6 | random = { 7 | source = "registry.terraform.io/hashicorp/random" 8 | version = "3.2.0" 9 | } 10 | } 11 | } 12 | 13 | provider "random" {} 14 | 15 | resource "random_password" "test" { 16 | length = 8 17 | 18 | numeric = false 19 | } -------------------------------------------------------------------------------- /helper/resource/testdata/fixtures/random_password_3.2.0_multiple_files/provider.tf: -------------------------------------------------------------------------------- 1 | # Copyright (c) HashiCorp, Inc. 2 | # SPDX-License-Identifier: MPL-2.0 3 | 4 | provider "random" {} -------------------------------------------------------------------------------- /helper/resource/testdata/fixtures/random_password_3.2.0_multiple_files/random.tf: -------------------------------------------------------------------------------- 1 | # Copyright (c) HashiCorp, Inc. 2 | # SPDX-License-Identifier: MPL-2.0 3 | 4 | resource "random_password" "test" { 5 | length = 8 6 | 7 | numeric = false 8 | } -------------------------------------------------------------------------------- /helper/resource/testdata/fixtures/random_password_3.2.0_multiple_files/terraform.tf: -------------------------------------------------------------------------------- 1 | # Copyright (c) HashiCorp, Inc. 2 | # SPDX-License-Identifier: MPL-2.0 3 | 4 | terraform { 5 | required_providers { 6 | random = { 7 | source = "registry.terraform.io/hashicorp/random" 8 | version = "3.2.0" 9 | } 10 | } 11 | } -------------------------------------------------------------------------------- /helper/resource/testdata/fixtures/random_password_3.2.0_multiple_files_vars/provider.tf: -------------------------------------------------------------------------------- 1 | # Copyright (c) HashiCorp, Inc. 2 | # SPDX-License-Identifier: MPL-2.0 3 | 4 | provider "random" {} -------------------------------------------------------------------------------- /helper/resource/testdata/fixtures/random_password_3.2.0_multiple_files_vars/random.tf: -------------------------------------------------------------------------------- 1 | # Copyright (c) HashiCorp, Inc. 2 | # SPDX-License-Identifier: MPL-2.0 3 | 4 | resource "random_password" "test" { 5 | length = var.length 6 | 7 | numeric = var.numeric 8 | } -------------------------------------------------------------------------------- /helper/resource/testdata/fixtures/random_password_3.2.0_multiple_files_vars/terraform.tf: -------------------------------------------------------------------------------- 1 | # Copyright (c) HashiCorp, Inc. 2 | # SPDX-License-Identifier: MPL-2.0 3 | 4 | terraform { 5 | required_providers { 6 | random = { 7 | source = "registry.terraform.io/hashicorp/random" 8 | version = "3.2.0" 9 | } 10 | } 11 | } -------------------------------------------------------------------------------- /helper/resource/testdata/fixtures/random_password_3.2.0_multiple_files_vars/vars.tf: -------------------------------------------------------------------------------- 1 | # Copyright (c) HashiCorp, Inc. 2 | # SPDX-License-Identifier: MPL-2.0 3 | 4 | variable "length" { 5 | type = number 6 | } 7 | 8 | variable "numeric" { 9 | type = bool 10 | } -------------------------------------------------------------------------------- /helper/resource/testdata/fixtures/random_password_3.2.0_vars/random.tf: -------------------------------------------------------------------------------- 1 | # Copyright (c) HashiCorp, Inc. 2 | # SPDX-License-Identifier: MPL-2.0 3 | 4 | terraform { 5 | required_providers { 6 | random = { 7 | source = "registry.terraform.io/hashicorp/random" 8 | version = "3.2.0" 9 | } 10 | } 11 | } 12 | 13 | provider "random" {} 14 | 15 | resource "random_password" "test" { 16 | length = var.length 17 | 18 | numeric = var.numeric 19 | } -------------------------------------------------------------------------------- /helper/resource/testdata/fixtures/random_password_3.2.0_vars/vars.tf: -------------------------------------------------------------------------------- 1 | # Copyright (c) HashiCorp, Inc. 2 | # SPDX-License-Identifier: MPL-2.0 3 | 4 | variable "length" { 5 | type = number 6 | } 7 | 8 | variable "numeric" { 9 | type = bool 10 | } -------------------------------------------------------------------------------- /helper/resource/testdata/fixtures/random_password_3.2.0_vars_single_file/random.tf: -------------------------------------------------------------------------------- 1 | # Copyright (c) HashiCorp, Inc. 2 | # SPDX-License-Identifier: MPL-2.0 3 | 4 | terraform { 5 | required_providers { 6 | random = { 7 | source = "registry.terraform.io/hashicorp/random" 8 | version = "3.2.0" 9 | } 10 | } 11 | } 12 | 13 | provider "random" {} 14 | 15 | resource "random_password" "test" { 16 | length = var.length 17 | 18 | numeric = var.numeric 19 | } 20 | 21 | variable "length" { 22 | type = number 23 | } 24 | 25 | variable "numeric" { 26 | type = bool 27 | } -------------------------------------------------------------------------------- /helper/resource/testdata/fixtures/random_password_3.5.1/random.tf: -------------------------------------------------------------------------------- 1 | # Copyright (c) HashiCorp, Inc. 2 | # SPDX-License-Identifier: MPL-2.0 3 | 4 | terraform { 5 | required_providers { 6 | random = { 7 | source = "registry.terraform.io/hashicorp/random" 8 | version = "3.5.1" 9 | } 10 | } 11 | } 12 | 13 | provider "random" {} 14 | 15 | resource "random_password" "test" { 16 | length = 8 17 | 18 | numeric = false 19 | } -------------------------------------------------------------------------------- /helper/resource/testdata/fixtures/random_password_3.5.1_multiple_files/provider.tf: -------------------------------------------------------------------------------- 1 | # Copyright (c) HashiCorp, Inc. 2 | # SPDX-License-Identifier: MPL-2.0 3 | 4 | provider "random" {} -------------------------------------------------------------------------------- /helper/resource/testdata/fixtures/random_password_3.5.1_multiple_files/random.tf: -------------------------------------------------------------------------------- 1 | # Copyright (c) HashiCorp, Inc. 2 | # SPDX-License-Identifier: MPL-2.0 3 | 4 | resource "random_password" "test" { 5 | length = 8 6 | 7 | numeric = false 8 | } -------------------------------------------------------------------------------- /helper/resource/testdata/fixtures/random_password_3.5.1_multiple_files/terraform.tf: -------------------------------------------------------------------------------- 1 | # Copyright (c) HashiCorp, Inc. 2 | # SPDX-License-Identifier: MPL-2.0 3 | 4 | terraform { 5 | required_providers { 6 | random = { 7 | source = "registry.terraform.io/hashicorp/random" 8 | version = "3.5.1" 9 | } 10 | } 11 | } -------------------------------------------------------------------------------- /helper/resource/testdata/fixtures/random_password_3.5.1_multiple_files_vars/provider.tf: -------------------------------------------------------------------------------- 1 | # Copyright (c) HashiCorp, Inc. 2 | # SPDX-License-Identifier: MPL-2.0 3 | 4 | provider "random" {} -------------------------------------------------------------------------------- /helper/resource/testdata/fixtures/random_password_3.5.1_multiple_files_vars/random.tf: -------------------------------------------------------------------------------- 1 | # Copyright (c) HashiCorp, Inc. 2 | # SPDX-License-Identifier: MPL-2.0 3 | 4 | resource "random_password" "test" { 5 | length = var.length 6 | 7 | numeric = var.numeric 8 | } -------------------------------------------------------------------------------- /helper/resource/testdata/fixtures/random_password_3.5.1_multiple_files_vars/terraform.tf: -------------------------------------------------------------------------------- 1 | # Copyright (c) HashiCorp, Inc. 2 | # SPDX-License-Identifier: MPL-2.0 3 | 4 | terraform { 5 | required_providers { 6 | random = { 7 | source = "registry.terraform.io/hashicorp/random" 8 | version = "3.5.1" 9 | } 10 | } 11 | } -------------------------------------------------------------------------------- /helper/resource/testdata/fixtures/random_password_3.5.1_multiple_files_vars/vars.tf: -------------------------------------------------------------------------------- 1 | # Copyright (c) HashiCorp, Inc. 2 | # SPDX-License-Identifier: MPL-2.0 3 | 4 | variable "length" { 5 | type = number 6 | } 7 | 8 | variable "numeric" { 9 | type = bool 10 | } -------------------------------------------------------------------------------- /helper/resource/testdata/fixtures/random_password_3.5.1_vars/random.tf: -------------------------------------------------------------------------------- 1 | # Copyright (c) HashiCorp, Inc. 2 | # SPDX-License-Identifier: MPL-2.0 3 | 4 | terraform { 5 | required_providers { 6 | random = { 7 | source = "registry.terraform.io/hashicorp/random" 8 | version = "3.5.1" 9 | } 10 | } 11 | } 12 | 13 | provider "random" {} 14 | 15 | resource "random_password" "test" { 16 | length = var.length 17 | 18 | numeric = var.numeric 19 | } -------------------------------------------------------------------------------- /helper/resource/testdata/fixtures/random_password_3.5.1_vars/vars.tf: -------------------------------------------------------------------------------- 1 | # Copyright (c) HashiCorp, Inc. 2 | # SPDX-License-Identifier: MPL-2.0 3 | 4 | variable "length" { 5 | type = number 6 | } 7 | 8 | variable "numeric" { 9 | type = bool 10 | } -------------------------------------------------------------------------------- /helper/resource/testdata/fixtures/random_password_3.5.1_vars_single_file/random.tf: -------------------------------------------------------------------------------- 1 | # Copyright (c) HashiCorp, Inc. 2 | # SPDX-License-Identifier: MPL-2.0 3 | 4 | terraform { 5 | required_providers { 6 | random = { 7 | source = "registry.terraform.io/hashicorp/random" 8 | version = "3.5.1" 9 | } 10 | } 11 | } 12 | 13 | provider "random" {} 14 | 15 | resource "random_password" "test" { 16 | length = var.length 17 | 18 | numeric = var.numeric 19 | } 20 | 21 | variable "length" { 22 | type = number 23 | } 24 | 25 | variable "numeric" { 26 | type = bool 27 | } -------------------------------------------------------------------------------- /helper/resource/testing_config.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) HashiCorp, Inc. 2 | // SPDX-License-Identifier: MPL-2.0 3 | 4 | package resource 5 | 6 | import ( 7 | "context" 8 | "fmt" 9 | 10 | "github.com/hashicorp/terraform-plugin-testing/internal/logging" 11 | 12 | "github.com/hashicorp/terraform-plugin-testing/internal/plugintest" 13 | ) 14 | 15 | func testStepTaint(ctx context.Context, step TestStep, wd *plugintest.WorkingDir) error { 16 | if len(step.Taint) == 0 { 17 | return nil 18 | } 19 | 20 | logging.HelperResourceTrace(ctx, fmt.Sprintf("Using TestStep Taint: %v", step.Taint)) 21 | 22 | for _, p := range step.Taint { 23 | err := wd.Taint(ctx, p) 24 | if err != nil { 25 | return fmt.Errorf("error tainting resource: %s", err) 26 | } 27 | } 28 | return nil 29 | } 30 | -------------------------------------------------------------------------------- /helper/resource/tfversion_checks.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) HashiCorp, Inc. 2 | // SPDX-License-Identifier: MPL-2.0 3 | 4 | package resource 5 | 6 | import ( 7 | "context" 8 | 9 | "github.com/hashicorp/go-version" 10 | "github.com/mitchellh/go-testing-interface" 11 | 12 | "github.com/hashicorp/terraform-plugin-testing/tfversion" 13 | ) 14 | 15 | func runTFVersionChecks(ctx context.Context, t testing.T, terraformVersion *version.Version, terraformVersionChecks []tfversion.TerraformVersionCheck) { 16 | t.Helper() 17 | 18 | for _, tfVersionCheck := range terraformVersionChecks { 19 | resp := tfversion.CheckTerraformVersionResponse{} 20 | tfVersionCheck.CheckTerraformVersion(ctx, tfversion.CheckTerraformVersionRequest{TerraformVersion: terraformVersion}, &resp) 21 | 22 | if resp.Error != nil { 23 | t.Fatalf(resp.Error.Error()) 24 | } 25 | 26 | if resp.Skip != "" { 27 | t.Skip(resp.Skip) 28 | } 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /internal/addrs/doc.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) HashiCorp, Inc. 2 | // SPDX-License-Identifier: MPL-2.0 3 | 4 | // Package addrs contains types that represent "addresses", which are 5 | // references to specific objects within a Terraform configuration or 6 | // state. 7 | // 8 | // All addresses have string representations based on HCL traversal syntax 9 | // which should be used in the user-interface, and also in-memory 10 | // representations that can be used internally. 11 | // 12 | // For object types that exist within Terraform modules a pair of types is 13 | // used. The "local" part of the address is represented by a type, and then 14 | // an absolute path to that object in the context of its module is represented 15 | // by a type of the same name with an "Abs" prefix added, for "absolute". 16 | // 17 | // All types within this package should be treated as immutable, even if this 18 | // is not enforced by the Go compiler. It is always an implementation error 19 | // to modify an address object in-place after it is initially constructed. 20 | package addrs 21 | -------------------------------------------------------------------------------- /internal/addrs/instance_key.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) HashiCorp, Inc. 2 | // SPDX-License-Identifier: MPL-2.0 3 | 4 | package addrs 5 | 6 | import ( 7 | "fmt" 8 | ) 9 | 10 | // instanceKey represents the key of an instance within an object that 11 | // contains multiple instances due to using "count" or "for_each" arguments 12 | // in configuration. 13 | // 14 | // intKey and stringKey are the two implementations of this type. No other 15 | // implementations are allowed. The single instance of an object that _isn't_ 16 | // using "count" or "for_each" is represented by NoKey, which is a nil 17 | // InstanceKey. 18 | type instanceKey interface { 19 | instanceKeySigil() 20 | String() string 21 | } 22 | 23 | // NoKey represents the absence of an instanceKey, for the single instance 24 | // of a configuration object that does not use "count" or "for_each" at all. 25 | var NoKey instanceKey 26 | 27 | // intKey is the InstanceKey representation representing integer indices, as 28 | // used when the "count" argument is specified or if for_each is used with 29 | // a sequence type. 30 | type intKey int 31 | 32 | func (k intKey) instanceKeySigil() { 33 | } 34 | 35 | func (k intKey) String() string { 36 | return fmt.Sprintf("[%d]", int(k)) 37 | } 38 | 39 | // stringKey is the InstanceKey representation representing string indices, as 40 | // used when the "for_each" argument is specified with a map or object type. 41 | type stringKey string 42 | 43 | func (k stringKey) instanceKeySigil() { 44 | } 45 | 46 | func (k stringKey) String() string { 47 | // FIXME: This isn't _quite_ right because Go's quoted string syntax is 48 | // slightly different than HCL's, but we'll accept it for now. 49 | return fmt.Sprintf("[%q]", string(k)) 50 | } 51 | -------------------------------------------------------------------------------- /internal/addrs/module.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) HashiCorp, Inc. 2 | // SPDX-License-Identifier: MPL-2.0 3 | 4 | package addrs 5 | 6 | // Module is an address for a module call within configuration. This is 7 | // the static counterpart of ModuleInstance, representing a traversal through 8 | // the static module call tree in configuration and does not take into account 9 | // the potentially-multiple instances of a module that might be created by 10 | // "count" and "for_each" arguments within those calls. 11 | // 12 | // This type should be used only in very specialized cases when working with 13 | // the static module call tree. Type ModuleInstance is appropriate in more cases. 14 | // 15 | // Although Module is a slice, it should be treated as immutable after creation. 16 | type Module []string 17 | -------------------------------------------------------------------------------- /internal/configs/configschema/doc.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) HashiCorp, Inc. 2 | // SPDX-License-Identifier: MPL-2.0 3 | 4 | // Package configschema contains types for describing the expected structure 5 | // of a configuration block whose shape is not known until runtime. 6 | // 7 | // For example, this is used to describe the expected contents of a resource 8 | // configuration block, which is defined by the corresponding provider plugin 9 | // and thus not compiled into Terraform core. 10 | // 11 | // A configschema primarily describes the shape of configuration, but it is 12 | // also suitable for use with other structures derived from the configuration, 13 | // such as the cached state of a resource or a resource diff. 14 | // 15 | // This package should not be confused with the package helper/schema, which 16 | // is the higher-level helper library used to implement providers themselves. 17 | package configschema 18 | -------------------------------------------------------------------------------- /internal/configs/configschema/nestingmode_string.go: -------------------------------------------------------------------------------- 1 | // Code generated by "stringer -type=NestingMode"; DO NOT EDIT. 2 | 3 | package configschema 4 | 5 | import "strconv" 6 | 7 | func _() { 8 | // An "invalid array index" compiler error signifies that the constant values have changed. 9 | // Re-run the stringer command to generate them again. 10 | var x [1]struct{} 11 | _ = x[nestingModeInvalid-0] 12 | _ = x[NestingSingle-1] 13 | _ = x[NestingGroup-2] 14 | _ = x[NestingList-3] 15 | _ = x[NestingSet-4] 16 | _ = x[NestingMap-5] 17 | } 18 | 19 | const _NestingMode_name = "nestingModeInvalidNestingSingleNestingGroupNestingListNestingSetNestingMap" 20 | 21 | var _NestingMode_index = [...]uint8{0, 18, 31, 43, 54, 64, 74} 22 | 23 | func (i NestingMode) String() string { 24 | if i < 0 || i >= NestingMode(len(_NestingMode_index)-1) { 25 | return "NestingMode(" + strconv.FormatInt(int64(i), 10) + ")" 26 | } 27 | return _NestingMode_name[_NestingMode_index[i]:_NestingMode_index[i+1]] 28 | } 29 | -------------------------------------------------------------------------------- /internal/logging/environment_variables.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) HashiCorp, Inc. 2 | // SPDX-License-Identifier: MPL-2.0 3 | 4 | package logging 5 | 6 | // Environment variables. 7 | const ( 8 | // EnvTfLogSdk is an environment variable that sets the logging level of 9 | // the root SDK logger, while the provider is under test. In "production" 10 | // usage, this environment variable is handled by terraform-plugin-go. 11 | // 12 | // Terraform CLI's logging must be explicitly turned on before this 13 | // environment varable can be used to reduce the SDK logging levels. It 14 | // cannot be used to show only SDK logging unless all other logging levels 15 | // are turned off. 16 | EnvTfLogSdk = "TF_LOG_SDK" 17 | 18 | // EnvTfLogSdkHelperResource is an environment variable that sets the logging 19 | // level of SDK helper/resource loggers. Infers root SDK logging level, if 20 | // unset. 21 | EnvTfLogSdkHelperResource = "TF_LOG_SDK_HELPER_RESOURCE" 22 | ) 23 | -------------------------------------------------------------------------------- /internal/logging/helper_resource.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) HashiCorp, Inc. 2 | // SPDX-License-Identifier: MPL-2.0 3 | 4 | package logging 5 | 6 | import ( 7 | "context" 8 | 9 | "github.com/hashicorp/terraform-plugin-log/tfsdklog" 10 | ) 11 | 12 | const ( 13 | // SubsystemHelperResource is the tfsdklog subsystem name for helper/resource. 14 | SubsystemHelperResource = "helper_resource" 15 | ) 16 | 17 | // HelperResourceTrace emits a helper/resource subsystem log at TRACE level. 18 | func HelperResourceTrace(ctx context.Context, msg string, additionalFields ...map[string]interface{}) { 19 | tfsdklog.SubsystemTrace(ctx, SubsystemHelperResource, msg, additionalFields...) 20 | } 21 | 22 | // HelperResourceDebug emits a helper/resource subsystem log at DEBUG level. 23 | func HelperResourceDebug(ctx context.Context, msg string, additionalFields ...map[string]interface{}) { 24 | tfsdklog.SubsystemDebug(ctx, SubsystemHelperResource, msg, additionalFields...) 25 | } 26 | 27 | // HelperResourceWarn emits a helper/resource subsystem log at WARN level. 28 | func HelperResourceWarn(ctx context.Context, msg string, additionalFields ...map[string]interface{}) { 29 | tfsdklog.SubsystemWarn(ctx, SubsystemHelperResource, msg, additionalFields...) 30 | } 31 | 32 | // HelperResourceError emits a helper/resource subsystem log at ERROR level. 33 | func HelperResourceError(ctx context.Context, msg string, additionalFields ...map[string]interface{}) { 34 | tfsdklog.SubsystemError(ctx, SubsystemHelperResource, msg, additionalFields...) 35 | } 36 | -------------------------------------------------------------------------------- /internal/plugintest/doc.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) HashiCorp, Inc. 2 | // SPDX-License-Identifier: MPL-2.0 3 | 4 | // Package plugintest contains utilities to help with writing tests for 5 | // Terraform plugins. 6 | // 7 | // This is not a package for testing configurations or modules written in the 8 | // Terraform language. It is for testing the plugins that allow Terraform to 9 | // manage various cloud services and other APIs. 10 | package plugintest 11 | -------------------------------------------------------------------------------- /internal/plugintest/guard.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) HashiCorp, Inc. 2 | // SPDX-License-Identifier: MPL-2.0 3 | 4 | package plugintest 5 | 6 | import ( 7 | "fmt" 8 | ) 9 | 10 | // TestControl is an interface requiring a subset of *testing.T which is used 11 | // by the test guards and helpers in this package. Most callers can simply 12 | // pass their *testing.T value here, but the interface allows other 13 | // implementations to potentially be provided instead, for example to allow 14 | // meta-testing (testing of the test utilities themselves). 15 | // 16 | // This interface also describes the subset of normal test functionality the 17 | // guards and helpers can perform: they can only create log lines, fail tests, 18 | // and skip tests. All other test control is the responsibility of the main 19 | // test code. 20 | type TestControl interface { 21 | Helper() 22 | Log(args ...interface{}) 23 | FailNow() 24 | SkipNow() 25 | Name() string 26 | } 27 | 28 | // testingT wraps a TestControl to recover some of the convenience behaviors 29 | // that would normally come from a real *testing.T, so we can keep TestControl 30 | // small while still having these conveniences. This is an abstraction 31 | // inversion, but accepted because it makes the public API more convenient 32 | // without any considerable disadvantage. 33 | type testingT struct { 34 | TestControl 35 | } 36 | 37 | func (t testingT) Logf(f string, args ...interface{}) { 38 | t.Helper() 39 | t.Log(fmt.Sprintf(f, args...)) 40 | } 41 | 42 | func (t testingT) Fatalf(f string, args ...interface{}) { 43 | t.Helper() 44 | t.Log(fmt.Sprintf(f, args...)) 45 | t.FailNow() 46 | } 47 | 48 | func (t testingT) Skipf(f string, args ...interface{}) { 49 | t.Helper() 50 | t.Log(fmt.Sprintf(f, args...)) 51 | t.SkipNow() 52 | } 53 | -------------------------------------------------------------------------------- /internal/testing/doc.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) HashiCorp, Inc. 2 | // SPDX-License-Identifier: MPL-2.0 3 | 4 | // Package testing contains functionality and helpers for unit testing within 5 | // this Go module. 6 | package testing 7 | -------------------------------------------------------------------------------- /internal/testing/testprovider/datasource.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) HashiCorp, Inc. 2 | // SPDX-License-Identifier: MPL-2.0 3 | 4 | package testprovider 5 | 6 | import ( 7 | "context" 8 | 9 | "github.com/hashicorp/terraform-plugin-testing/internal/testing/testsdk/datasource" 10 | ) 11 | 12 | var _ datasource.DataSource = DataSource{} 13 | 14 | type DataSource struct { 15 | ReadResponse *datasource.ReadResponse 16 | SchemaResponse *datasource.SchemaResponse 17 | ValidateConfigResponse *datasource.ValidateConfigResponse 18 | } 19 | 20 | func (d DataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) { 21 | if d.ReadResponse != nil { 22 | resp.Diagnostics = d.ReadResponse.Diagnostics 23 | resp.State = d.ReadResponse.State 24 | } 25 | } 26 | 27 | func (d DataSource) Schema(ctx context.Context, req datasource.SchemaRequest, resp *datasource.SchemaResponse) { 28 | if d.SchemaResponse != nil { 29 | resp.Diagnostics = d.SchemaResponse.Diagnostics 30 | resp.Schema = d.SchemaResponse.Schema 31 | } 32 | } 33 | 34 | func (d DataSource) ValidateConfig(ctx context.Context, req datasource.ValidateConfigRequest, resp *datasource.ValidateConfigResponse) { 35 | if d.ValidateConfigResponse != nil { 36 | resp.Diagnostics = d.ValidateConfigResponse.Diagnostics 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /internal/testing/testprovider/doc.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) HashiCorp, Inc. 2 | // SPDX-License-Identifier: MPL-2.0 3 | 4 | // Package testprovider is a declarative provider for implementing unit testing 5 | // within this Go module. 6 | package testprovider 7 | -------------------------------------------------------------------------------- /internal/testing/testsdk/datasource/datasource.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) HashiCorp, Inc. 2 | // SPDX-License-Identifier: MPL-2.0 3 | 4 | package datasource 5 | 6 | import ( 7 | "context" 8 | 9 | "github.com/hashicorp/terraform-plugin-go/tfprotov6" 10 | "github.com/hashicorp/terraform-plugin-go/tftypes" 11 | ) 12 | 13 | type DataSource interface { 14 | Read(context.Context, ReadRequest, *ReadResponse) 15 | Schema(context.Context, SchemaRequest, *SchemaResponse) 16 | ValidateConfig(context.Context, ValidateConfigRequest, *ValidateConfigResponse) 17 | } 18 | 19 | type ReadRequest struct { 20 | Config tftypes.Value 21 | } 22 | 23 | type ReadResponse struct { 24 | Diagnostics []*tfprotov6.Diagnostic 25 | State tftypes.Value 26 | } 27 | 28 | type SchemaRequest struct{} 29 | 30 | type SchemaResponse struct { 31 | Diagnostics []*tfprotov6.Diagnostic 32 | Schema *tfprotov6.Schema 33 | } 34 | 35 | type ValidateConfigRequest struct { 36 | Config tftypes.Value 37 | } 38 | 39 | type ValidateConfigResponse struct { 40 | Diagnostics []*tfprotov6.Diagnostic 41 | } 42 | -------------------------------------------------------------------------------- /internal/testing/testsdk/datasource/doc.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) HashiCorp, Inc. 2 | // SPDX-License-Identifier: MPL-2.0 3 | 4 | // Package datasource provides testsdk handling of the data resource concept. 5 | package datasource 6 | -------------------------------------------------------------------------------- /internal/testing/testsdk/doc.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) HashiCorp, Inc. 2 | // SPDX-License-Identifier: MPL-2.0 3 | 4 | // Package testsdk provides a lightweight terraform-plugin-go SDK for 5 | // implementing unit testing within this Go module. 6 | package testsdk 7 | -------------------------------------------------------------------------------- /internal/testing/testsdk/provider/doc.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) HashiCorp, Inc. 2 | // SPDX-License-Identifier: MPL-2.0 3 | 4 | // Package provider provides testsdk handling of the provider concept. 5 | package provider 6 | -------------------------------------------------------------------------------- /internal/testing/testsdk/provider/provider.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) HashiCorp, Inc. 2 | // SPDX-License-Identifier: MPL-2.0 3 | 4 | package provider 5 | 6 | import ( 7 | "context" 8 | 9 | "github.com/hashicorp/terraform-plugin-go/tfprotov6" 10 | "github.com/hashicorp/terraform-plugin-go/tftypes" 11 | "github.com/hashicorp/terraform-plugin-testing/internal/testing/testsdk/datasource" 12 | "github.com/hashicorp/terraform-plugin-testing/internal/testing/testsdk/resource" 13 | ) 14 | 15 | type Provider interface { 16 | Configure(context.Context, ConfigureRequest, *ConfigureResponse) 17 | DataSourcesMap() map[string]datasource.DataSource 18 | ResourcesMap() map[string]resource.Resource 19 | Schema(context.Context, SchemaRequest, *SchemaResponse) 20 | Stop(context.Context, StopRequest, *StopResponse) 21 | ValidateConfig(context.Context, ValidateConfigRequest, *ValidateConfigResponse) 22 | } 23 | 24 | type ConfigureRequest struct { 25 | Config tftypes.Value 26 | } 27 | 28 | type ConfigureResponse struct { 29 | Diagnostics []*tfprotov6.Diagnostic 30 | } 31 | 32 | type SchemaRequest struct{} 33 | 34 | type SchemaResponse struct { 35 | Diagnostics []*tfprotov6.Diagnostic 36 | Schema *tfprotov6.Schema 37 | } 38 | 39 | type StopRequest struct{} 40 | 41 | type StopResponse struct { 42 | Error error 43 | } 44 | 45 | type ValidateConfigRequest struct { 46 | Config tftypes.Value 47 | } 48 | 49 | type ValidateConfigResponse struct { 50 | Diagnostics []*tfprotov6.Diagnostic 51 | } 52 | -------------------------------------------------------------------------------- /internal/testing/testsdk/provider/provider_protov5.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) HashiCorp, Inc. 2 | // SPDX-License-Identifier: MPL-2.0 3 | 4 | package provider 5 | 6 | import ( 7 | "context" 8 | 9 | "github.com/hashicorp/terraform-plugin-go/tfprotov5" 10 | "github.com/hashicorp/terraform-plugin-go/tfprotov6" 11 | "github.com/hashicorp/terraform-plugin-go/tftypes" 12 | 13 | "github.com/hashicorp/terraform-plugin-testing/internal/testing/testsdk/datasource" 14 | "github.com/hashicorp/terraform-plugin-testing/internal/testing/testsdk/resource" 15 | ) 16 | 17 | type Protov5Provider interface { 18 | Configure(context.Context, Protov5ConfigureRequest, *Protov5ConfigureResponse) 19 | DataSourcesMap() map[string]datasource.DataSource 20 | ResourcesMap() map[string]resource.Resource 21 | Schema(context.Context, Protov5SchemaRequest, *Protov5SchemaResponse) 22 | Stop(context.Context, Protov5StopRequest, *Protov5StopResponse) 23 | ValidateConfig(context.Context, Protov5ValidateConfigRequest, *Protov5ValidateConfigResponse) 24 | } 25 | 26 | type Protov5ConfigureRequest struct { 27 | Config tftypes.Value 28 | } 29 | 30 | type Protov5ConfigureResponse struct { 31 | Diagnostics []*tfprotov6.Diagnostic 32 | } 33 | 34 | type Protov5SchemaRequest struct{} 35 | 36 | type Protov5SchemaResponse struct { 37 | Diagnostics []*tfprotov5.Diagnostic 38 | Schema *tfprotov5.Schema 39 | } 40 | 41 | type Protov5StopRequest struct{} 42 | 43 | type Protov5StopResponse struct { 44 | Error error 45 | } 46 | 47 | type Protov5ValidateConfigRequest struct { 48 | Config tftypes.Value 49 | } 50 | 51 | type Protov5ValidateConfigResponse struct { 52 | Diagnostics []*tfprotov5.Diagnostic 53 | } 54 | -------------------------------------------------------------------------------- /internal/testing/testsdk/providerserver/datasources.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) HashiCorp, Inc. 2 | // SPDX-License-Identifier: MPL-2.0 3 | 4 | package providerserver 5 | 6 | import ( 7 | "github.com/hashicorp/terraform-plugin-go/tfprotov6" 8 | "github.com/hashicorp/terraform-plugin-testing/internal/testing/testsdk/datasource" 9 | "github.com/hashicorp/terraform-plugin-testing/internal/testing/testsdk/provider" 10 | ) 11 | 12 | func ProviderDataSource(p provider.Provider, typeName string) (datasource.DataSource, *tfprotov6.Diagnostic) { 13 | d, ok := p.DataSourcesMap()[typeName] 14 | 15 | if !ok { 16 | return nil, &tfprotov6.Diagnostic{ 17 | Severity: tfprotov6.DiagnosticSeverityError, 18 | Summary: "Missing Data Source Type", 19 | Detail: "The provider does not define the data source type: " + typeName, 20 | } 21 | } 22 | 23 | return d, nil 24 | } 25 | -------------------------------------------------------------------------------- /internal/testing/testsdk/providerserver/doc.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) HashiCorp, Inc. 2 | // SPDX-License-Identifier: MPL-2.0 3 | 4 | // Package providerserver provides testsdk handling of serving a provider. 5 | package providerserver 6 | -------------------------------------------------------------------------------- /internal/testing/testsdk/providerserver/resources.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) HashiCorp, Inc. 2 | // SPDX-License-Identifier: MPL-2.0 3 | 4 | package providerserver 5 | 6 | import ( 7 | "github.com/hashicorp/terraform-plugin-go/tfprotov6" 8 | "github.com/hashicorp/terraform-plugin-testing/internal/testing/testsdk/provider" 9 | "github.com/hashicorp/terraform-plugin-testing/internal/testing/testsdk/resource" 10 | ) 11 | 12 | func ProviderResource(p provider.Provider, typeName string) (resource.Resource, *tfprotov6.Diagnostic) { 13 | r, ok := p.ResourcesMap()[typeName] 14 | 15 | if !ok { 16 | return nil, &tfprotov6.Diagnostic{ 17 | Severity: tfprotov6.DiagnosticSeverityError, 18 | Summary: "Missing Resource Type", 19 | Detail: "The provider does not define the resource type: " + typeName, 20 | } 21 | } 22 | 23 | return r, nil 24 | } 25 | -------------------------------------------------------------------------------- /internal/testing/testsdk/providerserver/schema.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) HashiCorp, Inc. 2 | // SPDX-License-Identifier: MPL-2.0 3 | 4 | package providerserver 5 | 6 | import ( 7 | "github.com/hashicorp/terraform-plugin-go/tfprotov6" 8 | "github.com/hashicorp/terraform-plugin-go/tftypes" 9 | ) 10 | 11 | func SchemaAttributeAtPath(schema *tfprotov6.Schema, path *tftypes.AttributePath) *tfprotov6.SchemaAttribute { 12 | if schema == nil || schema.Block == nil || path == nil || len(path.Steps()) == 0 { 13 | return nil 14 | } 15 | 16 | steps := path.Steps() 17 | nextStep := steps[0] 18 | remainingSteps := steps[1:] 19 | 20 | switch nextStep := nextStep.(type) { 21 | case tftypes.AttributeName: 22 | for _, attribute := range schema.Block.Attributes { 23 | if attribute == nil { 24 | continue 25 | } 26 | 27 | if attribute.Name != string(nextStep) { 28 | continue 29 | } 30 | 31 | if len(remainingSteps) == 0 { 32 | return attribute 33 | } 34 | 35 | // If needed, recursive attribute.NestedType handling would go here. 36 | } 37 | 38 | for _, block := range schema.Block.BlockTypes { 39 | if block == nil { 40 | continue 41 | } 42 | 43 | if block.TypeName != string(nextStep) { 44 | continue 45 | } 46 | 47 | // Blocks cannot be computed. 48 | if len(remainingSteps) == 0 { 49 | return nil 50 | } 51 | 52 | // If needed, recursive block handling would go here. 53 | } 54 | } 55 | 56 | return nil 57 | } 58 | -------------------------------------------------------------------------------- /internal/testing/testsdk/resource/doc.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) HashiCorp, Inc. 2 | // SPDX-License-Identifier: MPL-2.0 3 | 4 | // Package resource provides testsdk handling of the managed resource concept. 5 | package resource 6 | -------------------------------------------------------------------------------- /internal/teststep/testdata/empty_dir/.gitignore: -------------------------------------------------------------------------------- 1 | # Ignore everything in this directory 2 | * 3 | # Except this file 4 | !.gitignore -------------------------------------------------------------------------------- /internal/teststep/testdata/empty_file/main.tf: -------------------------------------------------------------------------------- 1 | # Copyright (c) HashiCorp, Inc. 2 | # SPDX-License-Identifier: MPL-2.0 3 | 4 | -------------------------------------------------------------------------------- /internal/teststep/testdata/main.tf: -------------------------------------------------------------------------------- 1 | # Copyright (c) HashiCorp, Inc. 2 | # SPDX-License-Identifier: MPL-2.0 3 | 4 | // Hello world 5 | -------------------------------------------------------------------------------- /internal/teststep/testdata/provider_block_quoted_with_attributes/main.tf: -------------------------------------------------------------------------------- 1 | # Copyright (c) HashiCorp, Inc. 2 | # SPDX-License-Identifier: MPL-2.0 3 | 4 | provider "test" { 5 | test = true 6 | } 7 | 8 | resource "test_test" "test" {} -------------------------------------------------------------------------------- /internal/teststep/testdata/provider_block_quoted_with_attributes_no_spaces/main.tf: -------------------------------------------------------------------------------- 1 | # Copyright (c) HashiCorp, Inc. 2 | # SPDX-License-Identifier: MPL-2.0 3 | 4 | provider"test"{ 5 | test = true 6 | } 7 | 8 | resource "test_test" "test" {} -------------------------------------------------------------------------------- /internal/teststep/testdata/provider_block_quoted_without_attributes/main.tf: -------------------------------------------------------------------------------- 1 | # Copyright (c) HashiCorp, Inc. 2 | # SPDX-License-Identifier: MPL-2.0 3 | 4 | provider "test" {} 5 | 6 | resource "test_test" "test" {} -------------------------------------------------------------------------------- /internal/teststep/testdata/provider_block_quoted_without_attributes_no_spaces/main.tf: -------------------------------------------------------------------------------- 1 | # Copyright (c) HashiCorp, Inc. 2 | # SPDX-License-Identifier: MPL-2.0 3 | 4 | provider"test"{} 5 | 6 | resource "test_test" "test" {} -------------------------------------------------------------------------------- /internal/teststep/testdata/provider_block_unquoted_with_attributes/main.tf: -------------------------------------------------------------------------------- 1 | # Copyright (c) HashiCorp, Inc. 2 | # SPDX-License-Identifier: MPL-2.0 3 | 4 | provider test { 5 | test = true 6 | } 7 | 8 | resource "test_test" "test" {} -------------------------------------------------------------------------------- /internal/teststep/testdata/provider_block_unquoted_with_attributes_no_trailing_space/main.tf: -------------------------------------------------------------------------------- 1 | # Copyright (c) HashiCorp, Inc. 2 | # SPDX-License-Identifier: MPL-2.0 3 | 4 | provider test{ 5 | test = true 6 | } 7 | 8 | resource "test_test" "test" {} -------------------------------------------------------------------------------- /internal/teststep/testdata/provider_block_unquoted_without_attributes/main.tf: -------------------------------------------------------------------------------- 1 | # Copyright (c) HashiCorp, Inc. 2 | # SPDX-License-Identifier: MPL-2.0 3 | 4 | provider test {} 5 | 6 | resource "test_test" "test" {} -------------------------------------------------------------------------------- /internal/teststep/testdata/provider_block_unquoted_without_attributes_no_trailing_space/main.tf: -------------------------------------------------------------------------------- 1 | # Copyright (c) HashiCorp, Inc. 2 | # SPDX-License-Identifier: MPL-2.0 3 | 4 | provider test{} 5 | 6 | resource "test_test" "test" {} -------------------------------------------------------------------------------- /internal/teststep/testdata/provider_meta_attribute/main.tf: -------------------------------------------------------------------------------- 1 | # Copyright (c) HashiCorp, Inc. 2 | # SPDX-License-Identifier: MPL-2.0 3 | 4 | resource "test_test" "test" { 5 | provider = test.test 6 | } -------------------------------------------------------------------------------- /internal/teststep/testdata/provider_object_attribute/main.tf: -------------------------------------------------------------------------------- 1 | # Copyright (c) HashiCorp, Inc. 2 | # SPDX-License-Identifier: MPL-2.0 3 | 4 | resource "test_test" "test" { 5 | test = { 6 | provider = { 7 | test = true 8 | } 9 | } 10 | } -------------------------------------------------------------------------------- /internal/teststep/testdata/provider_string_attribute/main.tf: -------------------------------------------------------------------------------- 1 | # Copyright (c) HashiCorp, Inc. 2 | # SPDX-License-Identifier: MPL-2.0 3 | 4 | resource "test_test" "test" { 5 | test = { 6 | provider = "test" 7 | } 8 | } -------------------------------------------------------------------------------- /internal/teststep/testdata/random/random.tf: -------------------------------------------------------------------------------- 1 | # Copyright (c) HashiCorp, Inc. 2 | # SPDX-License-Identifier: MPL-2.0 3 | 4 | terraform { 5 | required_providers { 6 | random = { 7 | source = "registry.terraform.io/hashicorp/random" 8 | version = "3.5.1" 9 | } 10 | } 11 | } 12 | 13 | provider "random" {} 14 | 15 | resource "random_password" "test" { 16 | length = 8 17 | 18 | numeric = false 19 | } -------------------------------------------------------------------------------- /internal/teststep/testdata/random_multiple_files/provider.tf: -------------------------------------------------------------------------------- 1 | # Copyright (c) HashiCorp, Inc. 2 | # SPDX-License-Identifier: MPL-2.0 3 | 4 | provider "random" {} -------------------------------------------------------------------------------- /internal/teststep/testdata/random_multiple_files/random.tf: -------------------------------------------------------------------------------- 1 | # Copyright (c) HashiCorp, Inc. 2 | # SPDX-License-Identifier: MPL-2.0 3 | 4 | resource "random_password" "test" { 5 | length = 8 6 | 7 | numeric = false 8 | } -------------------------------------------------------------------------------- /internal/teststep/testdata/random_multiple_files/terraform.tf: -------------------------------------------------------------------------------- 1 | # Copyright (c) HashiCorp, Inc. 2 | # SPDX-License-Identifier: MPL-2.0 3 | 4 | terraform { 5 | required_providers { 6 | random = { 7 | source = "registry.terraform.io/hashicorp/random" 8 | version = "3.5.1" 9 | } 10 | } 11 | } -------------------------------------------------------------------------------- /internal/teststep/testdata/terraform_block/main.tf: -------------------------------------------------------------------------------- 1 | # Copyright (c) HashiCorp, Inc. 2 | # SPDX-License-Identifier: MPL-2.0 3 | 4 | terraform { 5 | test = true 6 | } 7 | 8 | resource "test_test" "test" {} -------------------------------------------------------------------------------- /internal/teststep/testdata/terraform_block_no_space/main.tf: -------------------------------------------------------------------------------- 1 | # Copyright (c) HashiCorp, Inc. 2 | # SPDX-License-Identifier: MPL-2.0 3 | 4 | terraform{ 5 | test = true 6 | } 7 | 8 | resource "test_test" "test" {} -------------------------------------------------------------------------------- /internal/teststep/testdata/terraform_meta_attribute/main.tf: -------------------------------------------------------------------------------- 1 | # Copyright (c) HashiCorp, Inc. 2 | # SPDX-License-Identifier: MPL-2.0 3 | 4 | resource "test_test" "test" { 5 | terraform = test.test 6 | } -------------------------------------------------------------------------------- /internal/teststep/testdata/terraform_object_attribute/main.tf: -------------------------------------------------------------------------------- 1 | # Copyright (c) HashiCorp, Inc. 2 | # SPDX-License-Identifier: MPL-2.0 3 | 4 | resource "test_test" "test" { 5 | test = { 6 | terraform = { 7 | test = true 8 | } 9 | } 10 | } -------------------------------------------------------------------------------- /internal/teststep/testdata/terraform_string_attribute/main.tf: -------------------------------------------------------------------------------- 1 | # Copyright (c) HashiCorp, Inc. 2 | # SPDX-License-Identifier: MPL-2.0 3 | 4 | resource "test_test" "test" { 5 | test = { 6 | terraform = "test" 7 | } 8 | } -------------------------------------------------------------------------------- /internal/tfdiags/config_traversals.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) HashiCorp, Inc. 2 | // SPDX-License-Identifier: MPL-2.0 3 | 4 | package tfdiags 5 | 6 | import ( 7 | "bytes" 8 | "fmt" 9 | "strconv" 10 | 11 | "github.com/hashicorp/go-cty/cty" 12 | ) 13 | 14 | // FormatCtyPath is a helper function to produce a user-friendly string 15 | // representation of a cty.Path. The result uses a syntax similar to the 16 | // HCL expression language in the hope of it being familiar to users. 17 | func FormatCtyPath(path cty.Path) string { 18 | var buf bytes.Buffer 19 | for _, step := range path { 20 | switch ts := step.(type) { 21 | case cty.GetAttrStep: 22 | fmt.Fprintf(&buf, ".%s", ts.Name) 23 | case cty.IndexStep: 24 | buf.WriteByte('[') 25 | key := ts.Key 26 | keyTy := key.Type() 27 | switch { 28 | case key.IsNull(): 29 | buf.WriteString("null") 30 | case !key.IsKnown(): 31 | buf.WriteString("(not yet known)") 32 | case keyTy == cty.Number: 33 | bf := key.AsBigFloat() 34 | buf.WriteString(bf.Text('g', -1)) 35 | case keyTy == cty.String: 36 | buf.WriteString(strconv.Quote(key.AsString())) 37 | default: 38 | buf.WriteString("...") 39 | } 40 | buf.WriteByte(']') 41 | } 42 | } 43 | return buf.String() 44 | } 45 | 46 | // FormatError is a helper function to produce a user-friendly string 47 | // representation of certain special error types that we might want to 48 | // include in diagnostic messages. 49 | // 50 | // This currently has special behavior only for cty.PathError, where a 51 | // non-empty path is rendered in a HCL-like syntax as context. 52 | func FormatError(err error) string { 53 | perr, ok := err.(cty.PathError) 54 | if !ok || len(perr.Path) == 0 { 55 | return err.Error() 56 | } 57 | 58 | return fmt.Sprintf("%s: %s", FormatCtyPath(perr.Path), perr.Error()) 59 | } 60 | -------------------------------------------------------------------------------- /internal/tfdiags/contextual_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) HashiCorp, Inc. 2 | // SPDX-License-Identifier: MPL-2.0 3 | 4 | package tfdiags 5 | 6 | import ( 7 | "reflect" 8 | "testing" 9 | 10 | "github.com/hashicorp/go-cty/cty" 11 | ) 12 | 13 | func TestGetAttribute(t *testing.T) { 14 | t.Parallel() 15 | 16 | path := cty.Path{ 17 | cty.GetAttrStep{Name: "foo"}, 18 | cty.IndexStep{Key: cty.NumberIntVal(0)}, 19 | cty.GetAttrStep{Name: "bar"}, 20 | } 21 | 22 | d := AttributeValue( 23 | Error, 24 | "foo[0].bar", 25 | "detail", 26 | path, 27 | ) 28 | 29 | p := GetAttribute(d) 30 | if !reflect.DeepEqual(path, p) { 31 | t.Fatalf("paths don't match:\nexpected: %#v\ngot: %#v", path, p) 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /internal/tfdiags/diagnostic.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) HashiCorp, Inc. 2 | // SPDX-License-Identifier: MPL-2.0 3 | 4 | package tfdiags 5 | 6 | type Diagnostic interface { 7 | Severity() Severity 8 | Description() Description 9 | } 10 | 11 | type Severity rune 12 | 13 | // This code was previously generated with a go:generate directive calling: 14 | // go run golang.org/x/tools/cmd/stringer -type=Severity 15 | // However, it is now considered frozen and the tooling dependency has been 16 | // removed. The String method can be manually updated if necessary. 17 | 18 | const ( 19 | Error Severity = 'E' 20 | Warning Severity = 'W' 21 | ) 22 | 23 | type Description struct { 24 | Summary string 25 | Detail string 26 | } 27 | -------------------------------------------------------------------------------- /internal/tfdiags/diagnostic_base.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) HashiCorp, Inc. 2 | // SPDX-License-Identifier: MPL-2.0 3 | 4 | package tfdiags 5 | 6 | // diagnosticBase can be embedded in other diagnostic structs to get 7 | // default implementations of Severity and Description. This type also 8 | // has default implementations of Source that return no source 9 | // location or expression-related information, so embedders should generally 10 | // override those method to return more useful results where possible. 11 | type diagnosticBase struct { 12 | severity Severity 13 | summary string 14 | detail string 15 | } 16 | 17 | func (d diagnosticBase) Severity() Severity { 18 | return d.severity 19 | } 20 | 21 | func (d diagnosticBase) Description() Description { 22 | return Description{ 23 | Summary: d.summary, 24 | Detail: d.detail, 25 | } 26 | } 27 | 28 | func Diag(sev Severity, summary, detail string) Diagnostic { 29 | return &diagnosticBase{ 30 | severity: sev, 31 | summary: summary, 32 | detail: detail, 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /internal/tfdiags/doc.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) HashiCorp, Inc. 2 | // SPDX-License-Identifier: MPL-2.0 3 | 4 | // Package tfdiags is a utility package for representing errors and 5 | // warnings in a manner that allows us to produce good messages for the 6 | // user. 7 | // 8 | // "diag" is short for "diagnostics", and is meant as a general word for 9 | // feedback to a user about potential or actual problems. 10 | // 11 | // A design goal for this package is for it to be able to provide rich 12 | // messaging where possible but to also be pragmatic about dealing with 13 | // generic errors produced by system components that _can't_ provide 14 | // such rich messaging. As a consequence, the main types in this package -- 15 | // Diagnostics and Diagnostic -- are designed so that they can be "smuggled" 16 | // over an error channel and then be unpacked at the other end, so that 17 | // error diagnostics (at least) can transit through APIs that are not 18 | // aware of this package. 19 | package tfdiags 20 | -------------------------------------------------------------------------------- /internal/tfdiags/error.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) HashiCorp, Inc. 2 | // SPDX-License-Identifier: MPL-2.0 3 | 4 | package tfdiags 5 | 6 | // nativeError is a Diagnostic implementation that wraps a normal Go error 7 | type nativeError struct { 8 | err error 9 | } 10 | 11 | var _ Diagnostic = nativeError{} 12 | 13 | func (e nativeError) Severity() Severity { 14 | return Error 15 | } 16 | 17 | func (e nativeError) Description() Description { 18 | return Description{ 19 | Summary: FormatError(e.err), 20 | } 21 | } 22 | 23 | func FromError(err error) Diagnostic { 24 | return &nativeError{ 25 | err: err, 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /internal/tfdiags/severity_string.go: -------------------------------------------------------------------------------- 1 | // Code generated by "stringer -type=Severity"; DO NOT EDIT. 2 | 3 | package tfdiags 4 | 5 | import "strconv" 6 | 7 | func _() { 8 | // An "invalid array index" compiler error signifies that the constant values have changed. 9 | // Re-run the stringer command to generate them again. 10 | var x [1]struct{} 11 | _ = x[Error-69] 12 | _ = x[Warning-87] 13 | } 14 | 15 | const ( 16 | _Severity_name_0 = "Error" 17 | _Severity_name_1 = "Warning" 18 | ) 19 | 20 | func (i Severity) String() string { 21 | switch { 22 | case i == 69: 23 | return _Severity_name_0 24 | case i == 87: 25 | return _Severity_name_1 26 | default: 27 | return "Severity(" + strconv.FormatInt(int64(i), 10) + ")" 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /internal/tfdiags/simple_warning.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) HashiCorp, Inc. 2 | // SPDX-License-Identifier: MPL-2.0 3 | 4 | package tfdiags 5 | 6 | type simpleWarning string 7 | 8 | var _ Diagnostic = simpleWarning("") 9 | 10 | // SimpleWarning constructs a simple (summary-only) warning diagnostic. 11 | func SimpleWarning(msg string) Diagnostic { 12 | return simpleWarning(msg) 13 | } 14 | 15 | func (e simpleWarning) Severity() Severity { 16 | return Warning 17 | } 18 | 19 | func (e simpleWarning) Description() Description { 20 | return Description{ 21 | Summary: string(e), 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /knownvalue/bool.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) HashiCorp, Inc. 2 | // SPDX-License-Identifier: MPL-2.0 3 | 4 | package knownvalue 5 | 6 | import ( 7 | "fmt" 8 | "strconv" 9 | ) 10 | 11 | var _ Check = boolValue{} 12 | 13 | type boolValue struct { 14 | value bool 15 | } 16 | 17 | // CheckValue determines whether the passed value is of type bool, and 18 | // contains a matching bool value. 19 | func (v boolValue) CheckValue(other any) error { 20 | otherVal, ok := other.(bool) 21 | 22 | if !ok { 23 | return fmt.Errorf("expected bool value for Bool check, got: %T", other) 24 | } 25 | 26 | if otherVal != v.value { 27 | return fmt.Errorf("expected value %t for Bool check, got: %t", v.value, otherVal) 28 | } 29 | 30 | return nil 31 | } 32 | 33 | // String returns the string representation of the bool value. 34 | func (v boolValue) String() string { 35 | return strconv.FormatBool(v.value) 36 | } 37 | 38 | // Bool returns a Check for asserting equality between the 39 | // supplied bool and the value passed to the CheckValue method. 40 | func Bool(value bool) boolValue { 41 | return boolValue{ 42 | value: value, 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /knownvalue/bool_func.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) HashiCorp, Inc. 2 | // SPDX-License-Identifier: MPL-2.0 3 | 4 | package knownvalue 5 | 6 | import "fmt" 7 | 8 | var _ Check = boolFunc{} 9 | 10 | type boolFunc struct { 11 | checkFunc func(v bool) error 12 | } 13 | 14 | // CheckValue determines whether the passed value is of type bool, and 15 | // returns no error from the provided check function 16 | func (v boolFunc) CheckValue(other any) error { 17 | val, ok := other.(bool) 18 | 19 | if !ok { 20 | return fmt.Errorf("expected bool value for BoolFunc check, got: %T", other) 21 | } 22 | 23 | return v.checkFunc(val) 24 | } 25 | 26 | // String returns the bool representation of the value. 27 | func (v boolFunc) String() string { 28 | // Validation is up the the implementer of the function, so there are no 29 | // bool literal or regex comparers to print here 30 | return "BoolFunc" 31 | } 32 | 33 | // BoolFunc returns a Check for passing the bool value in state 34 | // to the provided check function 35 | func BoolFunc(fn func(v bool) error) boolFunc { 36 | return boolFunc{ 37 | checkFunc: fn, 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /knownvalue/check.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) HashiCorp, Inc. 2 | // SPDX-License-Identifier: MPL-2.0 3 | 4 | package knownvalue 5 | 6 | // Check defines an interface that is implemented to determine whether type and value match. Individual 7 | // implementations determine how the match is performed (e.g., exact match, partial match). 8 | type Check interface { 9 | // CheckValue should assert the given known value against any expectations. Use the error 10 | // return to signal unexpected values or implementation errors. 11 | CheckValue(value any) error 12 | // String should return a string representation of the type and value. 13 | String() string 14 | } 15 | -------------------------------------------------------------------------------- /knownvalue/doc.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) HashiCorp, Inc. 2 | // SPDX-License-Identifier: MPL-2.0 3 | 4 | // Package knownvalue contains the known value interface, and types implementing the known value interface. 5 | package knownvalue 6 | -------------------------------------------------------------------------------- /knownvalue/float32.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) HashiCorp, Inc. 2 | // SPDX-License-Identifier: MPL-2.0 3 | 4 | package knownvalue 5 | 6 | import ( 7 | "encoding/json" 8 | "fmt" 9 | "strconv" 10 | ) 11 | 12 | var _ Check = float32Exact{} 13 | 14 | type float32Exact struct { 15 | value float32 16 | } 17 | 18 | // CheckValue determines whether the passed value is of type float32, and 19 | // contains a matching float32 value. 20 | func (v float32Exact) CheckValue(other any) error { 21 | jsonNum, ok := other.(json.Number) 22 | 23 | if !ok { 24 | return fmt.Errorf("expected json.Number value for Float32Exact check, got: %T", other) 25 | } 26 | 27 | otherVal, err := strconv.ParseFloat(string(jsonNum), 32) 28 | 29 | if err != nil { 30 | return fmt.Errorf("expected json.Number to be parseable as float32 value for Float32Exact check: %s", err) 31 | } 32 | 33 | if float32(otherVal) != v.value { 34 | return fmt.Errorf("expected value %s for Float32Exact check, got: %s", v.String(), strconv.FormatFloat(otherVal, 'f', -1, 32)) 35 | } 36 | 37 | return nil 38 | } 39 | 40 | // String returns the string representation of the float32 value. 41 | func (v float32Exact) String() string { 42 | return strconv.FormatFloat(float64(v.value), 'f', -1, 32) 43 | } 44 | 45 | // Float32Exact returns a Check for asserting equality between the 46 | // supplied float32 and the value passed to the CheckValue method. 47 | func Float32Exact(value float32) float32Exact { 48 | return float32Exact{ 49 | value: value, 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /knownvalue/float32_func.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) HashiCorp, Inc. 2 | // SPDX-License-Identifier: MPL-2.0 3 | 4 | package knownvalue 5 | 6 | import ( 7 | "encoding/json" 8 | "fmt" 9 | "strconv" 10 | ) 11 | 12 | var _ Check = float32Func{} 13 | 14 | type float32Func struct { 15 | checkFunc func(v float32) error 16 | } 17 | 18 | // CheckValue determines whether the passed value is of type float32, and 19 | // returns no error from the provided check function 20 | func (v float32Func) CheckValue(other any) error { 21 | jsonNum, ok := other.(json.Number) 22 | 23 | if !ok { 24 | return fmt.Errorf("expected json.Number value for Float32Func check, got: %T", other) 25 | } 26 | 27 | otherVal, err := strconv.ParseFloat(string(jsonNum), 32) 28 | if err != nil { 29 | return fmt.Errorf("expected json.Number to be parseable as float32 value for Float32Func check: %s", err) 30 | } 31 | 32 | return v.checkFunc(float32(otherVal)) 33 | } 34 | 35 | // String returns the float32 representation of the value. 36 | func (v float32Func) String() string { 37 | // Validation is up the the implementer of the function, so there are no 38 | // float32 literal or regex comparers to print here 39 | return "Float32Func" 40 | } 41 | 42 | // Float32Func returns a Check for passing the float32 value in state 43 | // to the provided check function 44 | func Float32Func(fn func(v float32) error) float32Func { 45 | return float32Func{ 46 | checkFunc: fn, 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /knownvalue/float64.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) HashiCorp, Inc. 2 | // SPDX-License-Identifier: MPL-2.0 3 | 4 | package knownvalue 5 | 6 | import ( 7 | "encoding/json" 8 | "fmt" 9 | "strconv" 10 | ) 11 | 12 | var _ Check = float64Exact{} 13 | 14 | type float64Exact struct { 15 | value float64 16 | } 17 | 18 | // CheckValue determines whether the passed value is of type float64, and 19 | // contains a matching float64 value. 20 | func (v float64Exact) CheckValue(other any) error { 21 | jsonNum, ok := other.(json.Number) 22 | 23 | if !ok { 24 | return fmt.Errorf("expected json.Number value for Float64Exact check, got: %T", other) 25 | } 26 | 27 | otherVal, err := jsonNum.Float64() 28 | 29 | if err != nil { 30 | return fmt.Errorf("expected json.Number to be parseable as float64 value for Float64Exact check: %s", err) 31 | } 32 | 33 | if otherVal != v.value { 34 | return fmt.Errorf("expected value %s for Float64Exact check, got: %s", v.String(), strconv.FormatFloat(otherVal, 'f', -1, 64)) 35 | } 36 | 37 | return nil 38 | } 39 | 40 | // String returns the string representation of the float64 value. 41 | func (v float64Exact) String() string { 42 | return strconv.FormatFloat(v.value, 'f', -1, 64) 43 | } 44 | 45 | // Float64Exact returns a Check for asserting equality between the 46 | // supplied float64 and the value passed to the CheckValue method. 47 | func Float64Exact(value float64) float64Exact { 48 | return float64Exact{ 49 | value: value, 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /knownvalue/float64_func.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) HashiCorp, Inc. 2 | // SPDX-License-Identifier: MPL-2.0 3 | 4 | package knownvalue 5 | 6 | import ( 7 | "encoding/json" 8 | "fmt" 9 | "strconv" 10 | ) 11 | 12 | var _ Check = float64Func{} 13 | 14 | type float64Func struct { 15 | checkFunc func(v float64) error 16 | } 17 | 18 | // CheckValue determines whether the passed value is of type float64, and 19 | // returns no error from the provided check function 20 | func (v float64Func) CheckValue(other any) error { 21 | jsonNum, ok := other.(json.Number) 22 | 23 | if !ok { 24 | return fmt.Errorf("expected json.Number value for Float64Func check, got: %T", other) 25 | } 26 | 27 | otherVal, err := strconv.ParseFloat(string(jsonNum), 64) 28 | if err != nil { 29 | return fmt.Errorf("expected json.Number to be parseable as float64 value for Float64Func check: %s", err) 30 | } 31 | 32 | return v.checkFunc(otherVal) 33 | } 34 | 35 | // String returns the float64 representation of the value. 36 | func (v float64Func) String() string { 37 | // Validation is up the the implementer of the function, so there are no 38 | // float64 literal or regex comparers to print here 39 | return "Float64Func" 40 | } 41 | 42 | // Float64Func returns a Check for passing the float64 value in state 43 | // to the provided check function 44 | func Float64Func(fn func(v float64) error) float64Func { 45 | return float64Func{ 46 | checkFunc: fn, 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /knownvalue/int32.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) HashiCorp, Inc. 2 | // SPDX-License-Identifier: MPL-2.0 3 | 4 | package knownvalue 5 | 6 | import ( 7 | "encoding/json" 8 | "fmt" 9 | "strconv" 10 | ) 11 | 12 | var _ Check = int32Exact{} 13 | 14 | type int32Exact struct { 15 | value int32 16 | } 17 | 18 | // CheckValue determines whether the passed value is of type int32, and 19 | // contains a matching int32 value. 20 | func (v int32Exact) CheckValue(other any) error { 21 | jsonNum, ok := other.(json.Number) 22 | 23 | if !ok { 24 | return fmt.Errorf("expected json.Number value for Int32Exact check, got: %T", other) 25 | } 26 | 27 | otherVal, err := strconv.ParseInt(string(jsonNum), 10, 32) 28 | 29 | if err != nil { 30 | return fmt.Errorf("expected json.Number to be parseable as int32 value for Int32Exact check: %s", err) 31 | } 32 | 33 | if int32(otherVal) != v.value { 34 | return fmt.Errorf("expected value %d for Int32Exact check, got: %d", v.value, otherVal) 35 | } 36 | 37 | return nil 38 | } 39 | 40 | // String returns the string representation of the int32 value. 41 | func (v int32Exact) String() string { 42 | return strconv.FormatInt(int64(v.value), 10) 43 | } 44 | 45 | // Int32Exact returns a Check for asserting equality between the 46 | // supplied int32 and the value passed to the CheckValue method. 47 | func Int32Exact(value int32) int32Exact { 48 | return int32Exact{ 49 | value: value, 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /knownvalue/int32_func.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) HashiCorp, Inc. 2 | // SPDX-License-Identifier: MPL-2.0 3 | 4 | package knownvalue 5 | 6 | import ( 7 | "encoding/json" 8 | "fmt" 9 | "strconv" 10 | ) 11 | 12 | var _ Check = int32Func{} 13 | 14 | type int32Func struct { 15 | checkFunc func(v int32) error 16 | } 17 | 18 | // CheckValue determines whether the passed value is of type int32, and 19 | // returns no error from the provided check function 20 | func (v int32Func) CheckValue(other any) error { 21 | jsonNum, ok := other.(json.Number) 22 | 23 | if !ok { 24 | return fmt.Errorf("expected json.Number value for Int32Func check, got: %T", other) 25 | } 26 | 27 | otherVal, err := strconv.ParseInt(string(jsonNum), 10, 32) 28 | if err != nil { 29 | return fmt.Errorf("expected json.Number to be parseable as int32 value for Int32Func check: %s", err) 30 | } 31 | 32 | return v.checkFunc(int32(otherVal)) 33 | } 34 | 35 | // String returns the int32 representation of the value. 36 | func (v int32Func) String() string { 37 | // Validation is up the the implementer of the function, so there are no 38 | // int32 literal or regex comparers to print here 39 | return "Int32Func" 40 | } 41 | 42 | // Int32Func returns a Check for passing the int32 value in state 43 | // to the provided check function 44 | func Int32Func(fn func(v int32) error) int32Func { 45 | return int32Func{ 46 | checkFunc: fn, 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /knownvalue/int64.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) HashiCorp, Inc. 2 | // SPDX-License-Identifier: MPL-2.0 3 | 4 | package knownvalue 5 | 6 | import ( 7 | "encoding/json" 8 | "fmt" 9 | "strconv" 10 | ) 11 | 12 | var _ Check = int64Exact{} 13 | 14 | type int64Exact struct { 15 | value int64 16 | } 17 | 18 | // CheckValue determines whether the passed value is of type int64, and 19 | // contains a matching int64 value. 20 | func (v int64Exact) CheckValue(other any) error { 21 | jsonNum, ok := other.(json.Number) 22 | 23 | if !ok { 24 | return fmt.Errorf("expected json.Number value for Int64Exact check, got: %T", other) 25 | } 26 | 27 | otherVal, err := jsonNum.Int64() 28 | 29 | if err != nil { 30 | return fmt.Errorf("expected json.Number to be parseable as int64 value for Int64Exact check: %s", err) 31 | } 32 | 33 | if otherVal != v.value { 34 | return fmt.Errorf("expected value %d for Int64Exact check, got: %d", v.value, otherVal) 35 | } 36 | 37 | return nil 38 | } 39 | 40 | // String returns the string representation of the int64 value. 41 | func (v int64Exact) String() string { 42 | return strconv.FormatInt(v.value, 10) 43 | } 44 | 45 | // Int64Exact returns a Check for asserting equality between the 46 | // supplied int64 and the value passed to the CheckValue method. 47 | func Int64Exact(value int64) int64Exact { 48 | return int64Exact{ 49 | value: value, 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /knownvalue/int64_func.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) HashiCorp, Inc. 2 | // SPDX-License-Identifier: MPL-2.0 3 | 4 | package knownvalue 5 | 6 | import ( 7 | "encoding/json" 8 | "fmt" 9 | "strconv" 10 | ) 11 | 12 | var _ Check = int64Func{} 13 | 14 | type int64Func struct { 15 | checkFunc func(v int64) error 16 | } 17 | 18 | // CheckValue determines whether the passed value is of type int64, and 19 | // returns no error from the provided check function 20 | func (v int64Func) CheckValue(other any) error { 21 | jsonNum, ok := other.(json.Number) 22 | 23 | if !ok { 24 | return fmt.Errorf("expected json.Number value for Int64Func check, got: %T", other) 25 | } 26 | 27 | otherVal, err := strconv.ParseInt(string(jsonNum), 10, 64) 28 | if err != nil { 29 | return fmt.Errorf("expected json.Number to be parseable as int64 value for Int64Func check: %s", err) 30 | } 31 | 32 | return v.checkFunc(otherVal) 33 | } 34 | 35 | // String returns the int64 representation of the value. 36 | func (v int64Func) String() string { 37 | // Validation is up the the implementer of the function, so there are no 38 | // int64 literal or regex comparers to print here 39 | return "Int64Func" 40 | } 41 | 42 | // Int64Func returns a Check for passing the int64 value in state 43 | // to the provided check function 44 | func Int64Func(fn func(v int64) error) int64Func { 45 | return int64Func{ 46 | checkFunc: fn, 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /knownvalue/list.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) HashiCorp, Inc. 2 | // SPDX-License-Identifier: MPL-2.0 3 | 4 | package knownvalue 5 | 6 | import ( 7 | "fmt" 8 | ) 9 | 10 | var _ Check = listExact{} 11 | 12 | type listExact struct { 13 | value []Check 14 | } 15 | 16 | // CheckValue determines whether the passed value is of type []any, and 17 | // contains matching slice entries in the same sequence. 18 | func (v listExact) CheckValue(other any) error { 19 | otherVal, ok := other.([]any) 20 | 21 | if !ok { 22 | return fmt.Errorf("expected []any value for ListExact check, got: %T", other) 23 | } 24 | 25 | if len(otherVal) != len(v.value) { 26 | expectedElements := "elements" 27 | actualElements := "elements" 28 | 29 | if len(v.value) == 1 { 30 | expectedElements = "element" 31 | } 32 | 33 | if len(otherVal) == 1 { 34 | actualElements = "element" 35 | } 36 | 37 | return fmt.Errorf("expected %d %s for ListExact check, got %d %s", len(v.value), expectedElements, len(otherVal), actualElements) 38 | } 39 | 40 | for i := 0; i < len(v.value); i++ { 41 | if err := v.value[i].CheckValue(otherVal[i]); err != nil { 42 | return fmt.Errorf("list element index %d: %s", i, err) 43 | } 44 | } 45 | 46 | return nil 47 | } 48 | 49 | // String returns the string representation of the value. 50 | func (v listExact) String() string { 51 | var listVals []string 52 | 53 | for _, val := range v.value { 54 | listVals = append(listVals, val.String()) 55 | } 56 | 57 | return fmt.Sprintf("%s", listVals) 58 | } 59 | 60 | // ListExact returns a Check for asserting equality between the 61 | // supplied []Check and the value passed to the CheckValue method. 62 | // This is an order-dependent check. 63 | func ListExact(value []Check) listExact { 64 | return listExact{ 65 | value: value, 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /knownvalue/list_size.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) HashiCorp, Inc. 2 | // SPDX-License-Identifier: MPL-2.0 3 | 4 | package knownvalue 5 | 6 | import ( 7 | "fmt" 8 | "strconv" 9 | ) 10 | 11 | var _ Check = listSizeExact{} 12 | 13 | type listSizeExact struct { 14 | size int 15 | } 16 | 17 | // CheckValue verifies that the passed value is a list, map, object, 18 | // or set, and contains a matching number of elements. 19 | func (v listSizeExact) CheckValue(other any) error { 20 | otherVal, ok := other.([]any) 21 | 22 | if !ok { 23 | return fmt.Errorf("expected []any value for ListSizeExact check, got: %T", other) 24 | } 25 | 26 | if len(otherVal) != v.size { 27 | expectedElements := "elements" 28 | actualElements := "elements" 29 | 30 | if v.size == 1 { 31 | expectedElements = "element" 32 | } 33 | 34 | if len(otherVal) == 1 { 35 | actualElements = "element" 36 | } 37 | 38 | return fmt.Errorf("expected %d %s for ListSizeExact check, got %d %s", v.size, expectedElements, len(otherVal), actualElements) 39 | } 40 | 41 | return nil 42 | } 43 | 44 | // String returns the string representation of the value. 45 | func (v listSizeExact) String() string { 46 | return strconv.FormatInt(int64(v.size), 10) 47 | } 48 | 49 | // ListSizeExact returns a Check for asserting that 50 | // a list has size elements. 51 | func ListSizeExact(size int) listSizeExact { 52 | return listSizeExact{ 53 | size: size, 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /knownvalue/map_size.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) HashiCorp, Inc. 2 | // SPDX-License-Identifier: MPL-2.0 3 | 4 | package knownvalue 5 | 6 | import ( 7 | "fmt" 8 | "strconv" 9 | ) 10 | 11 | var _ Check = mapSizeExact{} 12 | 13 | type mapSizeExact struct { 14 | size int 15 | } 16 | 17 | // CheckValue verifies that the passed value is a list, map, object, 18 | // or set, and contains a matching number of elements. 19 | func (v mapSizeExact) CheckValue(other any) error { 20 | otherVal, ok := other.(map[string]any) 21 | 22 | if !ok { 23 | return fmt.Errorf("expected map[string]any value for MapSizeExact check, got: %T", other) 24 | } 25 | 26 | if len(otherVal) != v.size { 27 | expectedElements := "elements" 28 | actualElements := "elements" 29 | 30 | if v.size == 1 { 31 | expectedElements = "element" 32 | } 33 | 34 | if len(otherVal) == 1 { 35 | actualElements = "element" 36 | } 37 | 38 | return fmt.Errorf("expected %d %s for MapSizeExact check, got %d %s", v.size, expectedElements, len(otherVal), actualElements) 39 | } 40 | 41 | return nil 42 | } 43 | 44 | // String returns the string representation of the value. 45 | func (v mapSizeExact) String() string { 46 | return strconv.Itoa(v.size) 47 | } 48 | 49 | // MapSizeExact returns a Check for asserting that 50 | // a map has size elements. 51 | func MapSizeExact(size int) mapSizeExact { 52 | return mapSizeExact{ 53 | size: size, 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /knownvalue/not_null.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) HashiCorp, Inc. 2 | // SPDX-License-Identifier: MPL-2.0 3 | 4 | package knownvalue 5 | 6 | import ( 7 | "fmt" 8 | ) 9 | 10 | var _ Check = notNull{} 11 | 12 | type notNull struct{} 13 | 14 | // CheckValue determines whether the passed value is nil. 15 | func (v notNull) CheckValue(other any) error { 16 | if other == nil { 17 | return fmt.Errorf("expected non-nil value for NotNull check, got: %T", other) 18 | } 19 | 20 | return nil 21 | } 22 | 23 | // String returns the string representation of notNull. 24 | func (v notNull) String() string { 25 | return "not-null" 26 | } 27 | 28 | // NotNull returns a Check for asserting the value passed 29 | // to the CheckValue method is not nil. 30 | func NotNull() notNull { 31 | return notNull{} 32 | } 33 | -------------------------------------------------------------------------------- /knownvalue/not_null_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) HashiCorp, Inc. 2 | // SPDX-License-Identifier: MPL-2.0 3 | 4 | package knownvalue_test 5 | 6 | import ( 7 | "fmt" 8 | "testing" 9 | 10 | "github.com/google/go-cmp/cmp" 11 | 12 | "github.com/hashicorp/terraform-plugin-testing/knownvalue" 13 | ) 14 | 15 | func TestNotNullValue_CheckValue(t *testing.T) { 16 | t.Parallel() 17 | 18 | testCases := map[string]struct { 19 | self knownvalue.Check 20 | other any 21 | expectedError error 22 | }{ 23 | "zero-nil": { 24 | self: knownvalue.NotNull(), 25 | expectedError: fmt.Errorf("expected non-nil value for NotNull check, got: "), 26 | }, 27 | "not-nil": { 28 | self: knownvalue.NotNull(), 29 | other: nil, 30 | expectedError: fmt.Errorf("expected non-nil value for NotNull check, got: "), 31 | }, 32 | "equal": { 33 | self: knownvalue.NotNull(), 34 | other: true, 35 | }, 36 | } 37 | 38 | for name, testCase := range testCases { 39 | t.Run(name, func(t *testing.T) { 40 | t.Parallel() 41 | 42 | got := testCase.self.CheckValue(testCase.other) 43 | 44 | if diff := cmp.Diff(got, testCase.expectedError, equateErrorMessage); diff != "" { 45 | t.Errorf("unexpected difference: %s", diff) 46 | } 47 | }) 48 | } 49 | } 50 | 51 | func TestNotNullValue_String(t *testing.T) { 52 | t.Parallel() 53 | 54 | got := knownvalue.NotNull().String() 55 | 56 | if diff := cmp.Diff(got, "not-null"); diff != "" { 57 | t.Errorf("unexpected difference: %s", diff) 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /knownvalue/null.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) HashiCorp, Inc. 2 | // SPDX-License-Identifier: MPL-2.0 3 | 4 | package knownvalue 5 | 6 | import ( 7 | "fmt" 8 | ) 9 | 10 | var _ Check = null{} 11 | 12 | type null struct{} 13 | 14 | // CheckValue determines whether the passed value is nil. 15 | func (v null) CheckValue(other any) error { 16 | if other != nil { 17 | return fmt.Errorf("expected nil value for Null check, got: %T", other) 18 | } 19 | 20 | return nil 21 | } 22 | 23 | // String returns the string representation of null. 24 | func (v null) String() string { 25 | return "null" 26 | } 27 | 28 | // Null returns a Check for asserting the value passed 29 | // to the CheckValue method is nil. 30 | func Null() null { 31 | return null{} 32 | } 33 | -------------------------------------------------------------------------------- /knownvalue/null_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) HashiCorp, Inc. 2 | // SPDX-License-Identifier: MPL-2.0 3 | 4 | package knownvalue_test 5 | 6 | import ( 7 | "fmt" 8 | "testing" 9 | 10 | "github.com/google/go-cmp/cmp" 11 | 12 | "github.com/hashicorp/terraform-plugin-testing/knownvalue" 13 | ) 14 | 15 | func TestNullValue_CheckValue(t *testing.T) { 16 | t.Parallel() 17 | 18 | testCases := map[string]struct { 19 | self knownvalue.Check 20 | other any 21 | expectedError error 22 | }{ 23 | "zero-nil": { 24 | self: knownvalue.Null(), 25 | }, 26 | "not-nil": { 27 | self: knownvalue.Null(), 28 | other: false, 29 | expectedError: fmt.Errorf("expected nil value for Null check, got: bool"), 30 | }, 31 | "equal": { 32 | self: knownvalue.Null(), 33 | other: nil, 34 | }, 35 | } 36 | 37 | for name, testCase := range testCases { 38 | t.Run(name, func(t *testing.T) { 39 | t.Parallel() 40 | 41 | got := testCase.self.CheckValue(testCase.other) 42 | 43 | if diff := cmp.Diff(got, testCase.expectedError, equateErrorMessage); diff != "" { 44 | t.Errorf("unexpected difference: %s", diff) 45 | } 46 | }) 47 | } 48 | } 49 | 50 | func TestNullValue_String(t *testing.T) { 51 | t.Parallel() 52 | 53 | got := knownvalue.Null().String() 54 | 55 | if diff := cmp.Diff(got, "null"); diff != "" { 56 | t.Errorf("unexpected difference: %s", diff) 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /knownvalue/number.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) HashiCorp, Inc. 2 | // SPDX-License-Identifier: MPL-2.0 3 | 4 | package knownvalue 5 | 6 | import ( 7 | "encoding/json" 8 | "fmt" 9 | "math/big" 10 | ) 11 | 12 | var _ Check = numberExact{} 13 | 14 | type numberExact struct { 15 | value *big.Float 16 | } 17 | 18 | // CheckValue determines whether the passed value is of type *big.Float, and 19 | // contains a matching *big.Float value. 20 | func (v numberExact) CheckValue(other any) error { 21 | if v.value == nil { 22 | return fmt.Errorf("value in NumberExact check is nil") 23 | } 24 | 25 | jsonNum, ok := other.(json.Number) 26 | 27 | if !ok { 28 | return fmt.Errorf("expected json.Number value for NumberExact check, got: %T", other) 29 | } 30 | 31 | otherVal, _, err := big.ParseFloat(jsonNum.String(), 10, 512, big.ToNearestEven) 32 | 33 | if err != nil { 34 | return fmt.Errorf("expected json.Number to be parseable as big.Float value for NumberExact check: %s", err) 35 | } 36 | 37 | if v.value.Cmp(otherVal) != 0 { 38 | return fmt.Errorf("expected value %s for NumberExact check, got: %s", v.String(), otherVal.Text('f', -1)) 39 | } 40 | 41 | return nil 42 | } 43 | 44 | // String returns the string representation of the *big.Float value. 45 | func (v numberExact) String() string { 46 | return v.value.Text('f', -1) 47 | } 48 | 49 | // NumberExact returns a Check for asserting equality between the 50 | // supplied *big.Float and the value passed to the CheckValue method. 51 | // The CheckValue method uses 512-bit precision to perform this assertion. 52 | func NumberExact(value *big.Float) numberExact { 53 | return numberExact{ 54 | value: value, 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /knownvalue/number_func.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) HashiCorp, Inc. 2 | // SPDX-License-Identifier: MPL-2.0 3 | 4 | package knownvalue 5 | 6 | import ( 7 | "encoding/json" 8 | "fmt" 9 | "math/big" 10 | ) 11 | 12 | var _ Check = numberFunc{} 13 | 14 | type numberFunc struct { 15 | checkFunc func(v *big.Float) error 16 | } 17 | 18 | // CheckValue determines whether the passed value is of type int64, and 19 | // returns no error from the provided check function 20 | func (v numberFunc) CheckValue(other any) error { 21 | jsonNum, ok := other.(json.Number) 22 | 23 | if !ok { 24 | return fmt.Errorf("expected json.Number value for NumberFunc check, got: %T", other) 25 | } 26 | 27 | otherVal, _, err := big.ParseFloat(jsonNum.String(), 10, 512, big.ToNearestEven) 28 | if err != nil { 29 | return fmt.Errorf("expected json.Number to be parseable as big.Float value for NumberFunc check: %s", err) 30 | } 31 | 32 | return v.checkFunc(otherVal) 33 | } 34 | 35 | // String returns the int64 representation of the value. 36 | func (v numberFunc) String() string { 37 | // Validation is up the the implementer of the function, so there are no 38 | // int64 literal or regex comparers to print here 39 | return "NumberFunc" 40 | } 41 | 42 | // NumberFunc returns a Check for passing the int64 value in state 43 | // to the provided check function 44 | func NumberFunc(fn func(v *big.Float) error) numberFunc { 45 | return numberFunc{ 46 | checkFunc: fn, 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /knownvalue/set_partial.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) HashiCorp, Inc. 2 | // SPDX-License-Identifier: MPL-2.0 3 | 4 | package knownvalue 5 | 6 | import ( 7 | "fmt" 8 | ) 9 | 10 | var _ Check = setPartial{} 11 | 12 | type setPartial struct { 13 | value []Check 14 | } 15 | 16 | // CheckValue determines whether the passed value is of type []any, and 17 | // contains matching slice entries in any sequence. 18 | func (v setPartial) CheckValue(other any) error { 19 | otherVal, ok := other.([]any) 20 | 21 | if !ok { 22 | return fmt.Errorf("expected []any value for SetPartial check, got: %T", other) 23 | } 24 | 25 | otherValCopy := make([]any, len(otherVal)) 26 | 27 | copy(otherValCopy, otherVal) 28 | 29 | for i := 0; i < len(v.value); i++ { 30 | err := fmt.Errorf("missing value %s for SetPartial check", v.value[i].String()) 31 | 32 | for j := 0; j < len(otherValCopy); j++ { 33 | checkValueErr := v.value[i].CheckValue(otherValCopy[j]) 34 | 35 | if checkValueErr == nil { 36 | otherValCopy[j] = otherValCopy[len(otherValCopy)-1] 37 | otherValCopy = otherValCopy[:len(otherValCopy)-1] 38 | 39 | err = nil 40 | 41 | break 42 | } 43 | } 44 | 45 | if err != nil { 46 | return err 47 | } 48 | } 49 | 50 | return nil 51 | } 52 | 53 | // String returns the string representation of the value. 54 | func (v setPartial) String() string { 55 | var setVals []string 56 | 57 | for _, val := range v.value { 58 | setVals = append(setVals, val.String()) 59 | } 60 | 61 | return fmt.Sprintf("%s", setVals) 62 | } 63 | 64 | // SetPartial returns a Check for asserting partial equality between the 65 | // supplied []Check and the value passed to the CheckValue method. Only the 66 | // elements defined within the supplied []Check are checked. This is an 67 | // order-independent check. 68 | func SetPartial(value []Check) setPartial { 69 | return setPartial{ 70 | value: value, 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /knownvalue/set_size.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) HashiCorp, Inc. 2 | // SPDX-License-Identifier: MPL-2.0 3 | 4 | package knownvalue 5 | 6 | import ( 7 | "fmt" 8 | "strconv" 9 | ) 10 | 11 | var _ Check = setSizeExact{} 12 | 13 | type setSizeExact struct { 14 | size int 15 | } 16 | 17 | // CheckValue verifies that the passed value is a list, map, object, 18 | // or set, and contains a matching number of elements. 19 | func (v setSizeExact) CheckValue(other any) error { 20 | otherVal, ok := other.([]any) 21 | 22 | if !ok { 23 | return fmt.Errorf("expected []any value for SetElementExact check, got: %T", other) 24 | } 25 | 26 | if len(otherVal) != v.size { 27 | expectedElements := "elements" 28 | actualElements := "elements" 29 | 30 | if v.size == 1 { 31 | expectedElements = "element" 32 | } 33 | 34 | if len(otherVal) == 1 { 35 | actualElements = "element" 36 | } 37 | 38 | return fmt.Errorf("expected %d %s for SetElementExact check, got %d %s", v.size, expectedElements, len(otherVal), actualElements) 39 | } 40 | 41 | return nil 42 | } 43 | 44 | // String returns the string representation of the value. 45 | func (v setSizeExact) String() string { 46 | return strconv.FormatInt(int64(v.size), 10) 47 | } 48 | 49 | // SetSizeExact returns a Check for asserting that 50 | // a set has size elements. 51 | func SetSizeExact(size int) setSizeExact { 52 | return setSizeExact{ 53 | size: size, 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /knownvalue/string.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) HashiCorp, Inc. 2 | // SPDX-License-Identifier: MPL-2.0 3 | 4 | package knownvalue 5 | 6 | import "fmt" 7 | 8 | var _ Check = stringExact{} 9 | 10 | type stringExact struct { 11 | value string 12 | } 13 | 14 | // CheckValue determines whether the passed value is of type string, and 15 | // contains a matching sequence of bytes. 16 | func (v stringExact) CheckValue(other any) error { 17 | otherVal, ok := other.(string) 18 | 19 | if !ok { 20 | return fmt.Errorf("expected string value for StringExact check, got: %T", other) 21 | } 22 | 23 | if otherVal != v.value { 24 | return fmt.Errorf("expected value %s for StringExact check, got: %s", v.value, otherVal) 25 | } 26 | 27 | return nil 28 | } 29 | 30 | // String returns the string representation of the value. 31 | func (v stringExact) String() string { 32 | return v.value 33 | } 34 | 35 | // StringExact returns a Check for asserting equality between the 36 | // supplied string and a value passed to the CheckValue method. 37 | func StringExact(value string) stringExact { 38 | return stringExact{ 39 | value: value, 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /knownvalue/string_func.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) HashiCorp, Inc. 2 | // SPDX-License-Identifier: MPL-2.0 3 | 4 | package knownvalue 5 | 6 | import "fmt" 7 | 8 | var _ Check = stringFunc{} 9 | 10 | type stringFunc struct { 11 | checkFunc func(v string) error 12 | } 13 | 14 | // CheckValue determines whether the passed value is of type string, and 15 | // returns no error from the provided check function 16 | func (v stringFunc) CheckValue(value any) error { 17 | val, ok := value.(string) 18 | 19 | if !ok { 20 | return fmt.Errorf("expected string value for StringFunc check, got: %T", value) 21 | } 22 | 23 | return v.checkFunc(val) 24 | } 25 | 26 | // String returns the string representation of the value. 27 | func (v stringFunc) String() string { 28 | // Validation is up the the implementer of the function, so there are no 29 | // string literal or regex comparers to print here 30 | return "StringFunc" 31 | } 32 | 33 | // StringFunc returns a Check for passing the string value in state 34 | // to the provided check function 35 | func StringFunc(fn func(v string) error) stringFunc { 36 | return stringFunc{ 37 | checkFunc: fn, 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /knownvalue/string_regexp.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) HashiCorp, Inc. 2 | // SPDX-License-Identifier: MPL-2.0 3 | 4 | package knownvalue 5 | 6 | import ( 7 | "fmt" 8 | "regexp" 9 | ) 10 | 11 | var _ Check = stringRegexp{} 12 | 13 | type stringRegexp struct { 14 | regex *regexp.Regexp 15 | } 16 | 17 | // CheckValue determines whether the passed value is of type string, and 18 | // contains a sequence of bytes that match the regular expression supplied 19 | // to StringRegexp. 20 | func (v stringRegexp) CheckValue(other any) error { 21 | otherVal, ok := other.(string) 22 | 23 | if !ok { 24 | return fmt.Errorf("expected string value for StringRegexp check, got: %T", other) 25 | } 26 | 27 | if !v.regex.MatchString(otherVal) { 28 | return fmt.Errorf("expected regex match %s for StringRegexp check, got: %s", v.regex.String(), otherVal) 29 | } 30 | 31 | return nil 32 | } 33 | 34 | // String returns the string representation of the value. 35 | func (v stringRegexp) String() string { 36 | return v.regex.String() 37 | } 38 | 39 | // StringRegexp returns a Check for asserting equality between the 40 | // supplied regular expression and a value passed to the CheckValue method. 41 | func StringRegexp(regex *regexp.Regexp) stringRegexp { 42 | return stringRegexp{ 43 | regex: regex, 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /knownvalue/tuple.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) HashiCorp, Inc. 2 | // SPDX-License-Identifier: MPL-2.0 3 | 4 | package knownvalue 5 | 6 | import ( 7 | "fmt" 8 | ) 9 | 10 | var _ Check = tupleExact{} 11 | 12 | type tupleExact struct { 13 | value []Check 14 | } 15 | 16 | // CheckValue determines whether the passed value is of type []any, and 17 | // contains matching slice entries in the same sequence. 18 | func (v tupleExact) CheckValue(other any) error { 19 | otherVal, ok := other.([]any) 20 | 21 | if !ok { 22 | return fmt.Errorf("expected []any value for TupleExact check, got: %T", other) 23 | } 24 | 25 | if len(otherVal) != len(v.value) { 26 | expectedElements := "elements" 27 | actualElements := "elements" 28 | 29 | if len(v.value) == 1 { 30 | expectedElements = "element" 31 | } 32 | 33 | if len(otherVal) == 1 { 34 | actualElements = "element" 35 | } 36 | 37 | return fmt.Errorf("expected %d %s for TupleExact check, got %d %s", len(v.value), expectedElements, len(otherVal), actualElements) 38 | } 39 | 40 | for i := 0; i < len(v.value); i++ { 41 | if err := v.value[i].CheckValue(otherVal[i]); err != nil { 42 | return fmt.Errorf("tuple element index %d: %s", i, err) 43 | } 44 | } 45 | 46 | return nil 47 | } 48 | 49 | // String returns the string representation of the value. 50 | func (v tupleExact) String() string { 51 | var tupleVals []string 52 | 53 | for _, val := range v.value { 54 | tupleVals = append(tupleVals, val.String()) 55 | } 56 | 57 | return fmt.Sprintf("%s", tupleVals) 58 | } 59 | 60 | // TupleExact returns a Check for asserting equality between the 61 | // supplied []Check and the value passed to the CheckValue method. 62 | // This is an order-dependent check. 63 | func TupleExact(value []Check) tupleExact { 64 | return tupleExact{ 65 | value: value, 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /knownvalue/tuple_size.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) HashiCorp, Inc. 2 | // SPDX-License-Identifier: MPL-2.0 3 | 4 | package knownvalue 5 | 6 | import ( 7 | "fmt" 8 | "strconv" 9 | ) 10 | 11 | var _ Check = tupleSizeExact{} 12 | 13 | type tupleSizeExact struct { 14 | size int 15 | } 16 | 17 | // CheckValue verifies that the passed value is a tuple, map, object, 18 | // or set, and contains a matching number of elements. 19 | func (v tupleSizeExact) CheckValue(other any) error { 20 | otherVal, ok := other.([]any) 21 | 22 | if !ok { 23 | return fmt.Errorf("expected []any value for TupleSizeExact check, got: %T", other) 24 | } 25 | 26 | if len(otherVal) != v.size { 27 | expectedElements := "elements" 28 | actualElements := "elements" 29 | 30 | if v.size == 1 { 31 | expectedElements = "element" 32 | } 33 | 34 | if len(otherVal) == 1 { 35 | actualElements = "element" 36 | } 37 | 38 | return fmt.Errorf("expected %d %s for TupleSizeExact check, got %d %s", v.size, expectedElements, len(otherVal), actualElements) 39 | } 40 | 41 | return nil 42 | } 43 | 44 | // String returns the string representation of the value. 45 | func (v tupleSizeExact) String() string { 46 | return strconv.FormatInt(int64(v.size), 10) 47 | } 48 | 49 | // TupleSizeExact returns a Check for asserting that 50 | // a tuple has size elements. 51 | func TupleSizeExact(size int) tupleSizeExact { 52 | return tupleSizeExact{ 53 | size: size, 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /plancheck/deferred_reason.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) HashiCorp, Inc. 2 | // SPDX-License-Identifier: MPL-2.0 3 | 4 | package plancheck 5 | 6 | // DeferredReason is a string stored in the plan file which indicates why Terraform 7 | // is deferring a change for a resource. 8 | type DeferredReason string 9 | 10 | const ( 11 | // DeferredReasonResourceConfigUnknown is used to indicate that the resource configuration 12 | // is partially unknown and the real values need to be known before the change can be planned. 13 | DeferredReasonResourceConfigUnknown DeferredReason = "resource_config_unknown" 14 | 15 | // DeferredReasonProviderConfigUnknown is used to indicate that the provider configuration 16 | // is partially unknown and the real values need to be known before the change can be planned. 17 | DeferredReasonProviderConfigUnknown DeferredReason = "provider_config_unknown" 18 | 19 | // DeferredReasonAbsentPrereq is used to indicate that a hard dependency has not been satisfied. 20 | DeferredReasonAbsentPrereq DeferredReason = "absent_prereq" 21 | ) 22 | -------------------------------------------------------------------------------- /plancheck/doc.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) HashiCorp, Inc. 2 | // SPDX-License-Identifier: MPL-2.0 3 | 4 | // Package plancheck contains the plan check interface, request/response structs, and common plan check implementations. 5 | package plancheck 6 | -------------------------------------------------------------------------------- /plancheck/expect_deferred_change.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) HashiCorp, Inc. 2 | // SPDX-License-Identifier: MPL-2.0 3 | 4 | package plancheck 5 | 6 | import ( 7 | "context" 8 | "fmt" 9 | ) 10 | 11 | var _ PlanCheck = expectDeferredChange{} 12 | 13 | type expectDeferredChange struct { 14 | resourceAddress string 15 | reason DeferredReason 16 | } 17 | 18 | // CheckPlan implements the plan check logic. 19 | func (e expectDeferredChange) CheckPlan(ctx context.Context, req CheckPlanRequest, resp *CheckPlanResponse) { 20 | foundResource := false 21 | 22 | for _, dc := range req.Plan.DeferredChanges { 23 | if dc.ResourceChange == nil || e.resourceAddress != dc.ResourceChange.Address { 24 | continue 25 | } 26 | 27 | if e.reason != DeferredReason(dc.Reason) { 28 | resp.Error = fmt.Errorf("'%s' - expected %q, got deferred reason: %q", dc.ResourceChange.Address, e.reason, dc.Reason) 29 | return 30 | } 31 | 32 | foundResource = true 33 | break 34 | } 35 | 36 | if !foundResource { 37 | resp.Error = fmt.Errorf("%s - No deferred changes found for resource", e.resourceAddress) 38 | return 39 | } 40 | } 41 | 42 | // ExpectDeferredChange returns a plan check that asserts that a given resource will have a 43 | // deferred change in the plan with the given reason. 44 | func ExpectDeferredChange(resourceAddress string, reason DeferredReason) PlanCheck { 45 | return expectDeferredChange{ 46 | resourceAddress: resourceAddress, 47 | reason: reason, 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /plancheck/expect_empty_plan.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) HashiCorp, Inc. 2 | // SPDX-License-Identifier: MPL-2.0 3 | 4 | package plancheck 5 | 6 | import ( 7 | "context" 8 | "errors" 9 | "fmt" 10 | ) 11 | 12 | var _ PlanCheck = expectEmptyPlan{} 13 | 14 | type expectEmptyPlan struct{} 15 | 16 | // CheckPlan implements the plan check logic. 17 | func (e expectEmptyPlan) CheckPlan(ctx context.Context, req CheckPlanRequest, resp *CheckPlanResponse) { 18 | var result []error 19 | 20 | for output, change := range req.Plan.OutputChanges { 21 | if !change.Actions.NoOp() { 22 | result = append(result, fmt.Errorf("expected empty plan, but output %q has planned action(s): %v", output, change.Actions)) 23 | } 24 | } 25 | 26 | for _, rc := range req.Plan.ResourceChanges { 27 | if !rc.Change.Actions.NoOp() { 28 | result = append(result, fmt.Errorf("expected empty plan, but %s has planned action(s): %v", rc.Address, rc.Change.Actions)) 29 | } 30 | } 31 | 32 | resp.Error = errors.Join(result...) 33 | } 34 | 35 | // ExpectEmptyPlan returns a plan check that asserts that there are no output or resource changes in the plan. 36 | // All output and resource changes found will be aggregated and returned in a plan check error. 37 | func ExpectEmptyPlan() PlanCheck { 38 | return expectEmptyPlan{} 39 | } 40 | -------------------------------------------------------------------------------- /plancheck/expect_known_output_value.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) HashiCorp, Inc. 2 | // SPDX-License-Identifier: MPL-2.0 3 | 4 | package plancheck 5 | 6 | import ( 7 | "context" 8 | "fmt" 9 | 10 | tfjson "github.com/hashicorp/terraform-json" 11 | 12 | "github.com/hashicorp/terraform-plugin-testing/knownvalue" 13 | "github.com/hashicorp/terraform-plugin-testing/tfjsonpath" 14 | ) 15 | 16 | // Resource Plan Check 17 | var _ PlanCheck = expectKnownOutputValue{} 18 | 19 | type expectKnownOutputValue struct { 20 | outputAddress string 21 | knownValue knownvalue.Check 22 | } 23 | 24 | // CheckPlan implements the plan check logic. 25 | func (e expectKnownOutputValue) CheckPlan(ctx context.Context, req CheckPlanRequest, resp *CheckPlanResponse) { 26 | var change *tfjson.Change 27 | 28 | if req.Plan == nil { 29 | resp.Error = fmt.Errorf("plan is nil") 30 | } 31 | 32 | for address, oc := range req.Plan.OutputChanges { 33 | if e.outputAddress == address { 34 | change = oc 35 | 36 | break 37 | } 38 | } 39 | 40 | if change == nil { 41 | resp.Error = fmt.Errorf("%s - Output not found in plan", e.outputAddress) 42 | 43 | return 44 | } 45 | 46 | result, err := tfjsonpath.Traverse(change.After, tfjsonpath.Path{}) 47 | 48 | if err != nil { 49 | resp.Error = err 50 | 51 | return 52 | } 53 | 54 | if err := e.knownValue.CheckValue(result); err != nil { 55 | resp.Error = fmt.Errorf("error checking value for output at path: %s, err: %s", e.outputAddress, err) 56 | 57 | return 58 | } 59 | } 60 | 61 | // ExpectKnownOutputValue returns a plan check that asserts that the specified value 62 | // has a known type, and value. 63 | func ExpectKnownOutputValue(outputAddress string, knownValue knownvalue.Check) PlanCheck { 64 | return expectKnownOutputValue{ 65 | outputAddress: outputAddress, 66 | knownValue: knownValue, 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /plancheck/expect_no_deferred_changes.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) HashiCorp, Inc. 2 | // SPDX-License-Identifier: MPL-2.0 3 | 4 | package plancheck 5 | 6 | import ( 7 | "context" 8 | "errors" 9 | "fmt" 10 | ) 11 | 12 | var _ PlanCheck = expectNoDeferredChanges{} 13 | 14 | type expectNoDeferredChanges struct{} 15 | 16 | // CheckPlan implements the plan check logic. 17 | func (e expectNoDeferredChanges) CheckPlan(ctx context.Context, req CheckPlanRequest, resp *CheckPlanResponse) { 18 | if len(req.Plan.DeferredChanges) == 0 { 19 | return 20 | } 21 | 22 | var result []error 23 | for _, deferred := range req.Plan.DeferredChanges { 24 | resourceAddress := "unknown" 25 | if deferred.ResourceChange != nil { 26 | resourceAddress = deferred.ResourceChange.Address 27 | } 28 | 29 | result = append(result, fmt.Errorf("expected no deferred changes, but resource %q is deferred with reason: %q", resourceAddress, deferred.Reason)) 30 | } 31 | 32 | resp.Error = errors.Join(result...) 33 | if resp.Error != nil { 34 | return 35 | } 36 | 37 | if req.Plan.Complete == nil { 38 | resp.Error = errors.New("expected plan to be marked as complete, but complete field was not set in plan (nil). This indicates that the plan was created with a version of Terraform older than 1.8, which does not support the complete field.") 39 | return 40 | } 41 | 42 | if !*req.Plan.Complete { 43 | resp.Error = errors.New("expected plan to be marked as complete, but complete was \"false\", indicating that at least one more plan/apply round is needed to converge.") 44 | return 45 | } 46 | } 47 | 48 | // ExpectNoDeferredChanges returns a plan check that asserts that there are no deferred changes 49 | // for any resources in the plan. 50 | func ExpectNoDeferredChanges() PlanCheck { 51 | return expectNoDeferredChanges{} 52 | } 53 | -------------------------------------------------------------------------------- /plancheck/expect_non_empty_plan.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) HashiCorp, Inc. 2 | // SPDX-License-Identifier: MPL-2.0 3 | 4 | package plancheck 5 | 6 | import ( 7 | "context" 8 | "errors" 9 | ) 10 | 11 | var _ PlanCheck = expectNonEmptyPlan{} 12 | 13 | type expectNonEmptyPlan struct{} 14 | 15 | // CheckPlan implements the plan check logic. 16 | func (e expectNonEmptyPlan) CheckPlan(ctx context.Context, req CheckPlanRequest, resp *CheckPlanResponse) { 17 | for _, change := range req.Plan.OutputChanges { 18 | if !change.Actions.NoOp() { 19 | return 20 | } 21 | } 22 | 23 | for _, rc := range req.Plan.ResourceChanges { 24 | if !rc.Change.Actions.NoOp() { 25 | return 26 | } 27 | } 28 | 29 | resp.Error = errors.New("expected a non-empty plan, but got an empty plan") 30 | } 31 | 32 | // ExpectNonEmptyPlan returns a plan check that asserts there is at least one output or resource change in the plan. 33 | func ExpectNonEmptyPlan() PlanCheck { 34 | return expectNonEmptyPlan{} 35 | } 36 | -------------------------------------------------------------------------------- /plancheck/plan_check.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) HashiCorp, Inc. 2 | // SPDX-License-Identifier: MPL-2.0 3 | 4 | package plancheck 5 | 6 | import ( 7 | "context" 8 | 9 | tfjson "github.com/hashicorp/terraform-json" 10 | ) 11 | 12 | // PlanCheck defines an interface for implementing test logic that checks a plan file and then returns an error 13 | // if the plan file does not match what is expected. 14 | type PlanCheck interface { 15 | // CheckPlan should perform the plan check. 16 | CheckPlan(context.Context, CheckPlanRequest, *CheckPlanResponse) 17 | } 18 | 19 | // CheckPlanRequest is a request for an invoke of the CheckPlan function. 20 | type CheckPlanRequest struct { 21 | // Plan represents a parsed plan file, retrieved via the `terraform show -json` command. 22 | Plan *tfjson.Plan 23 | } 24 | 25 | // CheckPlanResponse is a response to an invoke of the CheckPlan function. 26 | type CheckPlanResponse struct { 27 | // Error is used to report the failure of a plan check assertion and is combined with other PlanCheck errors 28 | // to be reported as a test failure. 29 | Error error 30 | } 31 | -------------------------------------------------------------------------------- /statecheck/doc.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) HashiCorp, Inc. 2 | // SPDX-License-Identifier: MPL-2.0 3 | 4 | // Package statecheck contains the state check interface, request/response structs, and common state check implementations. 5 | package statecheck 6 | -------------------------------------------------------------------------------- /statecheck/expect_identity_example_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) HashiCorp, Inc. 2 | // SPDX-License-Identifier: MPL-2.0 3 | 4 | package statecheck_test 5 | 6 | import ( 7 | "testing" 8 | 9 | "github.com/hashicorp/terraform-plugin-testing/helper/resource" 10 | "github.com/hashicorp/terraform-plugin-testing/knownvalue" 11 | "github.com/hashicorp/terraform-plugin-testing/statecheck" 12 | "github.com/hashicorp/terraform-plugin-testing/tfversion" 13 | ) 14 | 15 | func ExampleExpectIdentity() { 16 | // A typical test would accept *testing.T as a function parameter, for instance `func TestSomething(t *testing.T) { ... }`. 17 | t := &testing.T{} 18 | t.Parallel() 19 | 20 | resource.Test(t, resource.TestCase{ 21 | // Resource identity support is only available in Terraform v1.12+ 22 | TerraformVersionChecks: []tfversion.TerraformVersionCheck{ 23 | tfversion.SkipBelow(tfversion.Version1_12_0), 24 | }, 25 | // Provider definition omitted. Assuming "test_resource" has an identity schema with "id" and "name" string attributes 26 | Steps: []resource.TestStep{ 27 | { 28 | Config: `resource "test_resource" "one" {}`, 29 | ConfigStateChecks: []statecheck.StateCheck{ 30 | statecheck.ExpectIdentity( 31 | "test_resource.one", 32 | map[string]knownvalue.Check{ 33 | "id": knownvalue.StringExact("id-123"), 34 | "name": knownvalue.StringExact("John Doe"), 35 | }, 36 | ), 37 | }, 38 | }, 39 | }, 40 | }) 41 | } 42 | -------------------------------------------------------------------------------- /statecheck/expect_identity_value_example_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) HashiCorp, Inc. 2 | // SPDX-License-Identifier: MPL-2.0 3 | 4 | package statecheck_test 5 | 6 | import ( 7 | "testing" 8 | 9 | "github.com/hashicorp/terraform-plugin-testing/helper/resource" 10 | "github.com/hashicorp/terraform-plugin-testing/knownvalue" 11 | "github.com/hashicorp/terraform-plugin-testing/statecheck" 12 | "github.com/hashicorp/terraform-plugin-testing/tfjsonpath" 13 | "github.com/hashicorp/terraform-plugin-testing/tfversion" 14 | ) 15 | 16 | func ExampleExpectIdentityValue() { 17 | // A typical test would accept *testing.T as a function parameter, for instance `func TestSomething(t *testing.T) { ... }`. 18 | t := &testing.T{} 19 | t.Parallel() 20 | 21 | resource.Test(t, resource.TestCase{ 22 | // Resource identity support is only available in Terraform v1.12+ 23 | TerraformVersionChecks: []tfversion.TerraformVersionCheck{ 24 | tfversion.SkipBelow(tfversion.Version1_12_0), 25 | }, 26 | // Provider definition omitted. Assuming "test_resource" has an identity schema with an "id" string attribute 27 | Steps: []resource.TestStep{ 28 | { 29 | Config: `resource "test_resource" "one" {}`, 30 | ConfigStateChecks: []statecheck.StateCheck{ 31 | statecheck.ExpectIdentityValue( 32 | "test_resource.one", 33 | tfjsonpath.New("id"), 34 | knownvalue.StringExact("id-123"), 35 | ), 36 | }, 37 | }, 38 | }, 39 | }) 40 | } 41 | -------------------------------------------------------------------------------- /statecheck/expect_identity_value_matches_state_at_path_example_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) HashiCorp, Inc. 2 | // SPDX-License-Identifier: MPL-2.0 3 | 4 | package statecheck_test 5 | 6 | import ( 7 | "testing" 8 | 9 | "github.com/hashicorp/terraform-plugin-testing/helper/resource" 10 | "github.com/hashicorp/terraform-plugin-testing/statecheck" 11 | "github.com/hashicorp/terraform-plugin-testing/tfjsonpath" 12 | "github.com/hashicorp/terraform-plugin-testing/tfversion" 13 | ) 14 | 15 | func ExampleExpectIdentityValueMatchesStateAtPath() { 16 | // A typical test would accept *testing.T as a function parameter, for instance `func TestSomething(t *testing.T) { ... }`. 17 | t := &testing.T{} 18 | t.Parallel() 19 | 20 | resource.Test(t, resource.TestCase{ 21 | // Resource identity support is only available in Terraform v1.12+ 22 | TerraformVersionChecks: []tfversion.TerraformVersionCheck{ 23 | tfversion.SkipBelow(tfversion.Version1_12_0), 24 | }, 25 | // Provider definition omitted. Assuming "test_resource": 26 | // - Has an identity schema with an "identity_id" string attribute 27 | // - Has a resource schema with an "state_id" string attribute 28 | Steps: []resource.TestStep{ 29 | { 30 | Config: `resource "test_resource" "one" {}`, 31 | ConfigStateChecks: []statecheck.StateCheck{ 32 | // The identity attribute at "identity_id" and state attribute at "state_id" must match 33 | statecheck.ExpectIdentityValueMatchesStateAtPath( 34 | "test_resource.one", 35 | tfjsonpath.New("identity_id"), 36 | tfjsonpath.New("state_id"), 37 | ), 38 | }, 39 | }, 40 | }, 41 | }) 42 | } 43 | -------------------------------------------------------------------------------- /statecheck/expect_identity_value_matches_state_example_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) HashiCorp, Inc. 2 | // SPDX-License-Identifier: MPL-2.0 3 | 4 | package statecheck_test 5 | 6 | import ( 7 | "testing" 8 | 9 | "github.com/hashicorp/terraform-plugin-testing/helper/resource" 10 | "github.com/hashicorp/terraform-plugin-testing/statecheck" 11 | "github.com/hashicorp/terraform-plugin-testing/tfjsonpath" 12 | "github.com/hashicorp/terraform-plugin-testing/tfversion" 13 | ) 14 | 15 | func ExampleExpectIdentityValueMatchesState() { 16 | // A typical test would accept *testing.T as a function parameter, for instance `func TestSomething(t *testing.T) { ... }`. 17 | t := &testing.T{} 18 | t.Parallel() 19 | 20 | resource.Test(t, resource.TestCase{ 21 | // Resource identity support is only available in Terraform v1.12+ 22 | TerraformVersionChecks: []tfversion.TerraformVersionCheck{ 23 | tfversion.SkipBelow(tfversion.Version1_12_0), 24 | }, 25 | // Provider definition omitted. Assuming "test_resource": 26 | // - Has an identity schema with an "id" string attribute 27 | // - Has a resource schema with an "id" string attribute 28 | Steps: []resource.TestStep{ 29 | { 30 | Config: `resource "test_resource" "one" {}`, 31 | ConfigStateChecks: []statecheck.StateCheck{ 32 | // The identity attribute and state attribute at "id" must match 33 | statecheck.ExpectIdentityValueMatchesState("test_resource.one", tfjsonpath.New("id")), 34 | }, 35 | }, 36 | }, 37 | }) 38 | } 39 | -------------------------------------------------------------------------------- /statecheck/expect_known_output_value_at_path_example_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) HashiCorp, Inc. 2 | // SPDX-License-Identifier: MPL-2.0 3 | 4 | package statecheck_test 5 | 6 | import ( 7 | "testing" 8 | 9 | "github.com/hashicorp/terraform-plugin-testing/helper/resource" 10 | "github.com/hashicorp/terraform-plugin-testing/knownvalue" 11 | "github.com/hashicorp/terraform-plugin-testing/statecheck" 12 | "github.com/hashicorp/terraform-plugin-testing/tfjsonpath" 13 | ) 14 | 15 | func ExampleExpectKnownOutputValueAtPath() { 16 | // A typical test would accept *testing.T as a function parameter, for instance `func TestSomething(t *testing.T) { ... }`. 17 | t := &testing.T{} 18 | t.Parallel() 19 | 20 | resource.Test(t, resource.TestCase{ 21 | // Provider definition omitted. 22 | Steps: []resource.TestStep{ 23 | { 24 | Config: `resource "test_resource" "one" { 25 | bool_attribute = true 26 | } 27 | 28 | output test_resource_one_output { 29 | value = test_resource.one 30 | } 31 | `, 32 | ConfigStateChecks: []statecheck.StateCheck{ 33 | statecheck.ExpectKnownOutputValueAtPath( 34 | "test_resource_one_output", 35 | tfjsonpath.New("bool_attribute"), 36 | knownvalue.Bool(true), 37 | ), 38 | }, 39 | }, 40 | }, 41 | }) 42 | } 43 | -------------------------------------------------------------------------------- /statecheck/expect_known_output_value_example_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) HashiCorp, Inc. 2 | // SPDX-License-Identifier: MPL-2.0 3 | 4 | package statecheck_test 5 | 6 | import ( 7 | "testing" 8 | 9 | "github.com/hashicorp/terraform-plugin-testing/helper/resource" 10 | "github.com/hashicorp/terraform-plugin-testing/knownvalue" 11 | "github.com/hashicorp/terraform-plugin-testing/statecheck" 12 | ) 13 | 14 | func ExampleExpectKnownOutputValue() { 15 | // A typical test would accept *testing.T as a function parameter, for instance `func TestSomething(t *testing.T) { ... }`. 16 | t := &testing.T{} 17 | t.Parallel() 18 | 19 | resource.Test(t, resource.TestCase{ 20 | // Provider definition omitted. 21 | Steps: []resource.TestStep{ 22 | { 23 | Config: `resource "test_resource" "one" { 24 | bool_attribute = true 25 | } 26 | 27 | output bool_output { 28 | value = test_resource.one.bool_attribute 29 | } 30 | `, 31 | ConfigStateChecks: []statecheck.StateCheck{ 32 | statecheck.ExpectKnownOutputValue( 33 | "bool_output", 34 | knownvalue.Bool(true), 35 | ), 36 | }, 37 | }, 38 | }, 39 | }) 40 | } 41 | -------------------------------------------------------------------------------- /statecheck/expect_known_value_example_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) HashiCorp, Inc. 2 | // SPDX-License-Identifier: MPL-2.0 3 | 4 | package statecheck_test 5 | 6 | import ( 7 | "testing" 8 | 9 | "github.com/hashicorp/terraform-plugin-testing/helper/resource" 10 | "github.com/hashicorp/terraform-plugin-testing/knownvalue" 11 | "github.com/hashicorp/terraform-plugin-testing/statecheck" 12 | "github.com/hashicorp/terraform-plugin-testing/tfjsonpath" 13 | ) 14 | 15 | func ExampleExpectKnownValue() { 16 | // A typical test would accept *testing.T as a function parameter, for instance `func TestSomething(t *testing.T) { ... }`. 17 | t := &testing.T{} 18 | t.Parallel() 19 | 20 | resource.Test(t, resource.TestCase{ 21 | // Provider definition omitted. 22 | Steps: []resource.TestStep{ 23 | { 24 | Config: `resource "test_resource" "one" { 25 | bool_attribute = true 26 | } 27 | `, 28 | ConfigStateChecks: []statecheck.StateCheck{ 29 | statecheck.ExpectKnownValue( 30 | "test_resource.one", 31 | tfjsonpath.New("bool_attribute"), 32 | knownvalue.Bool(true), 33 | ), 34 | }, 35 | }, 36 | }, 37 | }) 38 | } 39 | -------------------------------------------------------------------------------- /statecheck/state_check.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) HashiCorp, Inc. 2 | // SPDX-License-Identifier: MPL-2.0 3 | 4 | package statecheck 5 | 6 | import ( 7 | "context" 8 | 9 | tfjson "github.com/hashicorp/terraform-json" 10 | ) 11 | 12 | // StateCheck defines an interface for implementing test logic that checks a state file and then returns an error 13 | // if the state file does not match what is expected. 14 | type StateCheck interface { 15 | // CheckState should perform the state check. 16 | CheckState(context.Context, CheckStateRequest, *CheckStateResponse) 17 | } 18 | 19 | // CheckStateRequest is a request for an invoke of the CheckState function. 20 | type CheckStateRequest struct { 21 | // State represents a parsed state file, retrieved via the `terraform show -json` command. 22 | State *tfjson.State 23 | } 24 | 25 | // CheckStateResponse is a response to an invoke of the CheckState function. 26 | type CheckStateResponse struct { 27 | // Error is used to report the failure of a state check assertion and is combined with other StateCheck errors 28 | // to be reported as a test failure. 29 | Error error 30 | } 31 | -------------------------------------------------------------------------------- /terraform/instancetype.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) HashiCorp, Inc. 2 | // SPDX-License-Identifier: MPL-2.0 3 | 4 | package terraform 5 | 6 | // This code was previously generated with a go:generate directive calling: 7 | // go run golang.org/x/tools/cmd/stringer -type=instanceType instancetype.go 8 | // However, it is now considered frozen and the tooling dependency has been 9 | // removed. The String method can be manually updated if necessary. 10 | 11 | // instanceType is an enum of the various types of instances store in the State 12 | type instanceType int 13 | 14 | const ( 15 | typeInvalid instanceType = iota 16 | typePrimary 17 | typeTainted 18 | typeDeposed 19 | ) 20 | -------------------------------------------------------------------------------- /terraform/instancetype_string.go: -------------------------------------------------------------------------------- 1 | // Code generated by "stringer -type=instanceType instancetype.go"; DO NOT EDIT. 2 | 3 | package terraform 4 | 5 | import "strconv" 6 | 7 | func _() { 8 | // An "invalid array index" compiler error signifies that the constant values have changed. 9 | // Re-run the stringer command to generate them again. 10 | var x [1]struct{} 11 | _ = x[typeInvalid-0] 12 | _ = x[typePrimary-1] 13 | _ = x[typeTainted-2] 14 | _ = x[typeDeposed-3] 15 | } 16 | 17 | const _instanceType_name = "typeInvalidtypePrimarytypeTaintedtypeDeposed" 18 | 19 | var _instanceType_index = [...]uint8{0, 11, 22, 33, 44} 20 | 21 | func (i instanceType) String() string { 22 | if i < 0 || i >= instanceType(len(_instanceType_index)-1) { 23 | return "instanceType(" + strconv.FormatInt(int64(i), 10) + ")" 24 | } 25 | return _instanceType_name[_instanceType_index[i]:_instanceType_index[i+1]] 26 | } 27 | -------------------------------------------------------------------------------- /terraform/resource_mode.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) HashiCorp, Inc. 2 | // SPDX-License-Identifier: MPL-2.0 3 | 4 | package terraform 5 | 6 | // This code was previously generated with a go:generate directive calling: 7 | // go run golang.org/x/tools/cmd/stringer -type=ResourceMode -output=resource_mode_string.go resource_mode.go 8 | // However, it is now considered frozen and the tooling dependency has been 9 | // removed. The String method can be manually updated if necessary. 10 | 11 | // ResourceMode is deprecated, use addrs.ResourceMode instead. 12 | // It has been preserved for backwards compatibility. 13 | type ResourceMode int 14 | 15 | const ( 16 | ManagedResourceMode ResourceMode = iota 17 | DataResourceMode 18 | ) 19 | -------------------------------------------------------------------------------- /terraform/resource_mode_string.go: -------------------------------------------------------------------------------- 1 | // Code generated by "stringer -type=ResourceMode -output=resource_mode_string.go resource_mode.go"; DO NOT EDIT. 2 | 3 | package terraform 4 | 5 | import "strconv" 6 | 7 | func _() { 8 | // An "invalid array index" compiler error signifies that the constant values have changed. 9 | // Re-run the stringer command to generate them again. 10 | var x [1]struct{} 11 | _ = x[ManagedResourceMode-0] 12 | _ = x[DataResourceMode-1] 13 | } 14 | 15 | const _ResourceMode_name = "ManagedResourceModeDataResourceMode" 16 | 17 | var _ResourceMode_index = [...]uint8{0, 19, 35} 18 | 19 | func (i ResourceMode) String() string { 20 | if i < 0 || i >= ResourceMode(len(_ResourceMode_index)-1) { 21 | return "ResourceMode(" + strconv.FormatInt(int64(i), 10) + ")" 22 | } 23 | return _ResourceMode_name[_ResourceMode_index[i]:_ResourceMode_index[i+1]] 24 | } 25 | -------------------------------------------------------------------------------- /terraform/resource_provider.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) HashiCorp, Inc. 2 | // SPDX-License-Identifier: MPL-2.0 3 | 4 | package terraform 5 | 6 | // ResourceType is a type of resource that a resource provider can manage. 7 | // 8 | // Deprecated: This type is unintentionally exported by this Go module and not 9 | // supported for external consumption. It will be removed in the next major 10 | // version. 11 | type ResourceType struct { 12 | Name string // Name of the resource, example "instance" (no provider prefix) 13 | Importable bool // Whether this resource supports importing 14 | 15 | // SchemaAvailable is set if the provider supports the ProviderSchema, 16 | // ResourceTypeSchema and DataSourceSchema methods. Although it is 17 | // included on each resource type, it's actually a provider-wide setting 18 | // that's smuggled here only because that avoids a breaking change to 19 | // the plugin protocol. 20 | SchemaAvailable bool 21 | } 22 | 23 | // DataSource is a data source that a resource provider implements. 24 | // 25 | // Deprecated: This type is unintentionally exported by this Go module and not 26 | // supported for external consumption. It will be removed in the next major 27 | // version. 28 | type DataSource struct { 29 | Name string 30 | 31 | // SchemaAvailable is set if the provider supports the ProviderSchema, 32 | // ResourceTypeSchema and DataSourceSchema methods. Although it is 33 | // included on each resource type, it's actually a provider-wide setting 34 | // that's smuggled here only because that avoids a breaking change to 35 | // the plugin protocol. 36 | SchemaAvailable bool 37 | } 38 | -------------------------------------------------------------------------------- /terraform/schemas.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) HashiCorp, Inc. 2 | // SPDX-License-Identifier: MPL-2.0 3 | 4 | package terraform 5 | 6 | import ( 7 | "github.com/hashicorp/terraform-plugin-testing/internal/configs/configschema" 8 | ) 9 | 10 | // ProviderSchema represents the schema for a provider's own configuration 11 | // and the configuration for some or all of its resources and data sources. 12 | // 13 | // The completeness of this structure depends on how it was constructed. 14 | // When constructed for a configuration, it will generally include only 15 | // resource types and data sources used by that configuration. 16 | // 17 | // Deprecated: This type is unintentionally exported by this Go module and not 18 | // supported for external consumption. It will be removed in the next major 19 | // version. 20 | type ProviderSchema struct { 21 | Provider *configschema.Block 22 | ResourceTypes map[string]*configschema.Block 23 | DataSources map[string]*configschema.Block 24 | 25 | ResourceTypeSchemaVersions map[string]uint64 26 | } 27 | 28 | // ProviderSchemaRequest is used to describe to a ResourceProvider which 29 | // aspects of schema are required, when calling the GetSchema method. 30 | // 31 | // Deprecated: This type is unintentionally exported by this Go module and not 32 | // supported for external consumption. It will be removed in the next major 33 | // version. 34 | type ProviderSchemaRequest struct { 35 | ResourceTypes []string 36 | DataSources []string 37 | } 38 | -------------------------------------------------------------------------------- /terraform/unknown_value_walk_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) HashiCorp, Inc. 2 | // SPDX-License-Identifier: MPL-2.0 3 | 4 | package terraform 5 | 6 | import ( 7 | "reflect" 8 | "testing" 9 | 10 | "github.com/hashicorp/terraform-plugin-testing/internal/configs/hcl2shim" 11 | ) 12 | 13 | func TestUnknownValueWalk(t *testing.T) { 14 | t.Parallel() 15 | 16 | testCases := map[string]struct { 17 | value any 18 | expected bool 19 | }{ 20 | "primitive": { 21 | value: 42, 22 | expected: false, 23 | }, 24 | "primitive computed": { 25 | value: hcl2shim.UnknownVariableValue, 26 | expected: true, 27 | }, 28 | "list": { 29 | value: []any{ 30 | "foo", 31 | hcl2shim.UnknownVariableValue, 32 | }, 33 | expected: true, 34 | }, 35 | "nested list": { 36 | value: []any{ 37 | "foo", 38 | []any{hcl2shim.UnknownVariableValue}, 39 | }, 40 | expected: true, 41 | }, 42 | "map": { 43 | value: map[string]any{ 44 | "testkey1": "foo", 45 | "testkey2": hcl2shim.UnknownVariableValue, 46 | }, 47 | expected: true, 48 | }, 49 | "nested map": { 50 | value: map[string]any{ 51 | "testkey1": "foo", 52 | "testkey2": map[string]any{"testkey": hcl2shim.UnknownVariableValue}, 53 | }, 54 | expected: true, 55 | }, 56 | } 57 | 58 | for name, testCase := range testCases { 59 | t.Run(name, func(t *testing.T) { 60 | t.Parallel() 61 | 62 | got := unknownValueWalk(reflect.ValueOf(testCase.value)) 63 | 64 | if got != testCase.expected { 65 | t.Errorf("expected: %t, got: %t", testCase.expected, got) 66 | } 67 | }) 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /terraform/util.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) HashiCorp, Inc. 2 | // SPDX-License-Identifier: MPL-2.0 3 | 4 | package terraform 5 | 6 | import ( 7 | "sort" 8 | ) 9 | 10 | // deduplicate a slice of strings 11 | func uniqueStrings(s []string) []string { 12 | if len(s) < 2 { 13 | return s 14 | } 15 | 16 | sort.Strings(s) 17 | result := make([]string, 1, len(s)) 18 | result[0] = s[0] 19 | for i := 1; i < len(s); i++ { 20 | if s[i] != result[len(result)-1] { 21 | result = append(result, s[i]) 22 | } 23 | } 24 | return result 25 | } 26 | -------------------------------------------------------------------------------- /terraform/util_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) HashiCorp, Inc. 2 | // SPDX-License-Identifier: MPL-2.0 3 | 4 | package terraform 5 | 6 | import ( 7 | "fmt" 8 | "reflect" 9 | "testing" 10 | ) 11 | 12 | func TestUniqueStrings(t *testing.T) { 13 | t.Parallel() 14 | 15 | cases := []struct { 16 | Input []string 17 | Expected []string 18 | }{ 19 | { 20 | []string{}, 21 | []string{}, 22 | }, 23 | { 24 | []string{"x"}, 25 | []string{"x"}, 26 | }, 27 | { 28 | []string{"a", "b", "c"}, 29 | []string{"a", "b", "c"}, 30 | }, 31 | { 32 | []string{"a", "a", "a"}, 33 | []string{"a"}, 34 | }, 35 | { 36 | []string{"a", "b", "a", "b", "a", "a"}, 37 | []string{"a", "b"}, 38 | }, 39 | { 40 | []string{"c", "b", "a", "c", "b"}, 41 | []string{"a", "b", "c"}, 42 | }, 43 | } 44 | 45 | for i, tc := range cases { 46 | t.Run(fmt.Sprintf("unique-%d", i), func(t *testing.T) { 47 | t.Parallel() 48 | 49 | actual := uniqueStrings(tc.Input) 50 | if !reflect.DeepEqual(tc.Expected, actual) { 51 | t.Fatalf("Expected: %q\nGot: %q", tc.Expected, actual) 52 | } 53 | }) 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /tfjsonpath/doc.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) HashiCorp, Inc. 2 | // SPDX-License-Identifier: MPL-2.0 3 | 4 | // Package tfjsonpath implements terraform-json path functionality, which defines 5 | // traversals into Terraform JSON data, for testing purposes. 6 | package tfjsonpath 7 | -------------------------------------------------------------------------------- /tfjsonpath/step.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) HashiCorp, Inc. 2 | // SPDX-License-Identifier: MPL-2.0 3 | 4 | package tfjsonpath 5 | 6 | // step represents a traversal type indicating the underlying Go type 7 | // representation for a Terraform JSON value. 8 | type step any 9 | 10 | // MapStep represents a traversal for map[string]any 11 | type MapStep string 12 | 13 | // SliceStep represents a traversal for []any 14 | type SliceStep int 15 | -------------------------------------------------------------------------------- /tfversion/all.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) HashiCorp, Inc. 2 | // SPDX-License-Identifier: MPL-2.0 3 | 4 | package tfversion 5 | 6 | import ( 7 | "context" 8 | ) 9 | 10 | // All will return the first non-nil error or non-empty skip message 11 | // if any of the given checks return a non-nil error or non-empty skip message. 12 | // Otherwise, it will return a nil error and empty skip message (run the test) 13 | // 14 | // Use of All is only necessary when used in conjunction with Any as the 15 | // TerraformVersionChecks field automatically applies a logical AND. 16 | func All(terraformVersionChecks ...TerraformVersionCheck) TerraformVersionCheck { 17 | return allCheck{ 18 | terraformVersionChecks: terraformVersionChecks, 19 | } 20 | } 21 | 22 | // allCheck implements the TerraformVersionCheck interface 23 | type allCheck struct { 24 | terraformVersionChecks []TerraformVersionCheck 25 | } 26 | 27 | // CheckTerraformVersion satisfies the TerraformVersionCheck interface. 28 | func (a allCheck) CheckTerraformVersion(ctx context.Context, req CheckTerraformVersionRequest, resp *CheckTerraformVersionResponse) { 29 | 30 | for _, subCheck := range a.terraformVersionChecks { 31 | checkResp := CheckTerraformVersionResponse{} 32 | 33 | subCheck.CheckTerraformVersion(ctx, CheckTerraformVersionRequest{TerraformVersion: req.TerraformVersion}, &checkResp) 34 | 35 | if checkResp.Error != nil { 36 | resp.Error = checkResp.Error 37 | return 38 | } 39 | 40 | if checkResp.Skip != "" { 41 | resp.Skip = checkResp.Skip 42 | return 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /tfversion/any.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) HashiCorp, Inc. 2 | // SPDX-License-Identifier: MPL-2.0 3 | 4 | package tfversion 5 | 6 | import ( 7 | "context" 8 | "errors" 9 | "strings" 10 | ) 11 | 12 | // Any will return a nil error and empty skip message (run the test) 13 | // if any of the given checks return a nil error and empty skip message. 14 | // Otherwise, it will return all errors and fail the test if any of the given 15 | // checks return a non-nil error, or it will return all skip messages 16 | // and skip (pass) the test. 17 | func Any(terraformVersionChecks ...TerraformVersionCheck) TerraformVersionCheck { 18 | return anyCheck{ 19 | terraformVersionChecks: terraformVersionChecks, 20 | } 21 | } 22 | 23 | // anyCheck implements the TerraformVersionCheck interface 24 | type anyCheck struct { 25 | terraformVersionChecks []TerraformVersionCheck 26 | } 27 | 28 | // CheckTerraformVersion satisfies the TerraformVersionCheck interface. 29 | func (a anyCheck) CheckTerraformVersion(ctx context.Context, req CheckTerraformVersionRequest, resp *CheckTerraformVersionResponse) { 30 | var joinedErrors []error 31 | strBuilder := strings.Builder{} 32 | 33 | for _, subCheck := range a.terraformVersionChecks { 34 | checkResp := CheckTerraformVersionResponse{} 35 | 36 | subCheck.CheckTerraformVersion(ctx, CheckTerraformVersionRequest{TerraformVersion: req.TerraformVersion}, &checkResp) 37 | 38 | if checkResp.Error == nil && checkResp.Skip == "" { 39 | resp.Error = nil 40 | resp.Skip = "" 41 | return 42 | } 43 | 44 | joinedErrors = append(joinedErrors, checkResp.Error) 45 | 46 | if checkResp.Skip != "" { 47 | strBuilder.WriteString(checkResp.Skip) 48 | strBuilder.WriteString("\n") 49 | } 50 | } 51 | 52 | resp.Error = errors.Join(joinedErrors...) 53 | resp.Skip = strings.TrimSpace(strBuilder.String()) 54 | } 55 | -------------------------------------------------------------------------------- /tfversion/doc.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) HashiCorp, Inc. 2 | // SPDX-License-Identifier: MPL-2.0 3 | 4 | // Package tfversion contains the Terraform version check interface, request/response structs, and common version check implementations. 5 | package tfversion 6 | -------------------------------------------------------------------------------- /tfversion/skip_if_not_alpha.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) HashiCorp, Inc. 2 | // SPDX-License-Identifier: MPL-2.0 3 | 4 | package tfversion 5 | 6 | import ( 7 | "context" 8 | "fmt" 9 | "strings" 10 | ) 11 | 12 | // SkipIfNotAlpha will skip (pass) the test if the Terraform CLI 13 | // version is not an alpha prerelease (for example, 1.10.0-alpha20241023). 14 | // 15 | // Alpha builds of Terraform include experimental features, so this version check 16 | // can be used for acceptance testing of experimental features, such as deferred actions. 17 | func SkipIfNotAlpha() TerraformVersionCheck { 18 | return skipIfNotAlphaCheck{} 19 | } 20 | 21 | // skipIfNotAlphaCheck implements the TerraformVersionCheck interface 22 | type skipIfNotAlphaCheck struct{} 23 | 24 | // CheckTerraformVersion satisfies the TerraformVersionCheck interface. 25 | func (s skipIfNotAlphaCheck) CheckTerraformVersion(ctx context.Context, req CheckTerraformVersionRequest, resp *CheckTerraformVersionResponse) { 26 | if strings.Contains(req.TerraformVersion.Prerelease(), "alpha") { 27 | return 28 | } 29 | 30 | resp.Skip = fmt.Sprintf("Terraform CLI version %s is not an alpha build: skipping test.", req.TerraformVersion) 31 | } 32 | -------------------------------------------------------------------------------- /tfversion/skip_if_not_prerelease.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) HashiCorp, Inc. 2 | // SPDX-License-Identifier: MPL-2.0 3 | 4 | package tfversion 5 | 6 | import ( 7 | "context" 8 | "fmt" 9 | ) 10 | 11 | // SkipIfNotPrerelease will skip (pass) the test if the Terraform CLI 12 | // version does not include prerelease information. This will include builds 13 | // of Terraform that are from source. (e.g. 1.8.0-dev) 14 | func SkipIfNotPrerelease() TerraformVersionCheck { 15 | return skipIfNotPrereleaseCheck{} 16 | } 17 | 18 | // skipIfNotPrereleaseCheck implements the TerraformVersionCheck interface 19 | type skipIfNotPrereleaseCheck struct{} 20 | 21 | // CheckTerraformVersion satisfies the TerraformVersionCheck interface. 22 | func (s skipIfNotPrereleaseCheck) CheckTerraformVersion(ctx context.Context, req CheckTerraformVersionRequest, resp *CheckTerraformVersionResponse) { 23 | if req.TerraformVersion.Prerelease() != "" { 24 | return 25 | } 26 | 27 | resp.Skip = fmt.Sprintf("Terraform CLI version %s is not a prerelease build: skipping test.", req.TerraformVersion) 28 | } 29 | -------------------------------------------------------------------------------- /tools/copywrite.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) HashiCorp, Inc. 2 | // SPDX-License-Identifier: MPL-2.0 3 | 4 | //go:build generate 5 | 6 | package tools 7 | 8 | import ( 9 | // copywrite header generation 10 | _ "github.com/hashicorp/copywrite" 11 | ) 12 | 13 | //go:generate go run github.com/hashicorp/copywrite headers -d .. --config ../.copywrite.hcl 14 | -------------------------------------------------------------------------------- /website/docs/plugin/testing/acceptance-tests/known-value-checks/null.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | page_title: 'Plugin Development - Acceptance Testing: Known Values' 3 | description: >- 4 | Null Value Checks for use with Plan Checks or State Checks. 5 | --- 6 | 7 | > [!IMPORTANT] 8 | > **Documentation Update:** Product documentation previously located in `/website` has moved to the [`hashicorp/web-unified-docs`](https://github.com/hashicorp/web-unified-docs) repository, where all product documentation is now centralized. Please make contributions directly to `web-unified-docs`, since changes to `/website` in this repository will not appear on developer.hashicorp.com. 9 | 10 | # Null Known Value Checks 11 | 12 | The known value checks that are available for null values are: 13 | 14 | * [Null](/terraform/plugin/testing/acceptance-tests/known-value-checks/null#null-check) 15 | 16 | ## `Null` Check 17 | 18 | The [Null](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-testing/knownvalue#Null) check tests that a resource attribute, or output value has an exactly matching null value. 19 | 20 | Example usage of [Null](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-testing/knownvalue#Null) in an [ExpectKnownValue](/terraform/plugin/testing/acceptance-tests/state-checks/resource) state check. 21 | 22 | ```go 23 | func TestExpectKnownValue_CheckState_AttributeValueNull(t *testing.T) { 24 | t.Parallel() 25 | 26 | r.Test(t, r.TestCase{ 27 | // Provider definition omitted. 28 | Steps: []r.TestStep{ 29 | { 30 | // Example resource containing a computed attribute named "computed_attribute" 31 | Config: `resource "test_resource" "one" {}`, 32 | ConfigStateChecks: []statecheck.StateCheck{ 33 | statecheck.ExpectKnownValue( 34 | "test_resource.one", 35 | tfjsonpath.New("computed_attribute"), 36 | knownvalue.Null(), 37 | ), 38 | }, 39 | }, 40 | }, 41 | }) 42 | } 43 | ``` 44 | -------------------------------------------------------------------------------- /website/docs/plugin/testing/acceptance-tests/state-checks/index.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | page_title: 'Plugin Development - Acceptance Testing: State Checks' 3 | description: >- 4 | State Checks are test assertions that can inspect state during a TestStep. The testing module 5 | provides built-in State Checks for common use-cases, and custom State Checks can also be implemented. 6 | --- 7 | 8 | > [!IMPORTANT] 9 | > **Documentation Update:** Product documentation previously located in `/website` has moved to the [`hashicorp/web-unified-docs`](https://github.com/hashicorp/web-unified-docs) repository, where all product documentation is now centralized. Please make contributions directly to `web-unified-docs`, since changes to `/website` in this repository will not appear on developer.hashicorp.com. 10 | 11 | # State Checks 12 | 13 | During the **Lifecycle (config)** [mode](/terraform/plugin/testing/acceptance-tests/teststep#test-modes) of a `TestStep`, the testing framework will run `terraform apply`. 14 | 15 | The execution of `terraform apply` results in a [state file](/terraform/language/state), and can be represented by this [JSON format](/terraform/internals/json-format#state-representation). 16 | 17 | A **state check** is a test assertion that inspects the state file. Multiple state checks can be run, all assertion errors returned are aggregated, reported as a test failure, and all test cleanup logic is executed. 18 | 19 | Refer to: 20 | 21 | - [Resource State Checks](/terraform/plugin/testing/acceptance-tests/state-checks/resource) for built-in managed resource and data source state checks. 22 | - [Output State Checks](/terraform/plugin/testing/acceptance-tests/state-checks/output) for built-in output-related state checks. 23 | - [Custom State Checks](/terraform/plugin/testing/acceptance-tests/state-checks/custom) for defining bespoke state checks. 24 | -------------------------------------------------------------------------------- /website/img/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hashicorp/terraform-plugin-testing/f93511e1eeb942e64370f53368659a834e0ab9e8/website/img/.gitkeep -------------------------------------------------------------------------------- /website/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "terraform-plugin-testing-docs-preview", 3 | "private": "true", 4 | "scripts": { 5 | "build": "./scripts/website-build.sh", 6 | "content-check": "hc-content --config base-docs" 7 | }, 8 | "devDependencies": { 9 | "@hashicorp/platform-content-conformance": "^0.0.10", 10 | "next": "^13.5.4" 11 | }, 12 | "engines": { 13 | "npm": ">=7.0.0" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /website/scripts/should-build.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # Copyright (c) HashiCorp, Inc. 3 | # SPDX-License-Identifier: MPL-2.0 4 | 5 | 6 | ###################################################### 7 | # NOTE: This file is managed by the Digital Team's # 8 | # Terraform configuration @ hashicorp/mktg-terraform # 9 | ###################################################### 10 | 11 | # This is run during the website build step to determine if we should skip the build or not. 12 | # More information: https://vercel.com/docs/platform/projects#ignored-build-step 13 | 14 | if [[ "$VERCEL_GIT_COMMIT_REF" == "stable-website" ]] ; then 15 | # Proceed with the build if the branch is stable-website 16 | echo "✅ - Build can proceed" 17 | exit 1; 18 | else 19 | # Check for differences in the website directory 20 | git diff --quiet HEAD^ HEAD ./ 21 | fi -------------------------------------------------------------------------------- /website/scripts/website-start.sh: -------------------------------------------------------------------------------- 1 | # Copyright (c) HashiCorp, Inc. 2 | # SPDX-License-Identifier: MPL-2.0 3 | 4 | ###################################################### 5 | # NOTE: This file is managed by the Digital Team's # 6 | # Terraform configuration @ hashicorp/mktg-terraform # 7 | ###################################################### 8 | 9 | # Repo which we are cloning and executing npm run build:deploy-preview within 10 | REPO_TO_CLONE=dev-portal 11 | # Set the subdirectory name for the dev-portal app 12 | PREVIEW_DIR=website-preview 13 | # The product for which we are building the deploy preview 14 | PRODUCT=terraform-plugin-testing 15 | # Preview mode, controls the UI rendered (either the product site or developer). Can be `io` or `developer` 16 | PREVIEW_MODE=developer 17 | 18 | # Get the git branch of the commit that triggered the deploy preview 19 | # This will power remote image assets in local and deploy previews 20 | CURRENT_GIT_BRANCH=$(git rev-parse --abbrev-ref HEAD) 21 | 22 | # This is where content files live, relative to the website-preview dir. If omitted, "../content" will be used 23 | LOCAL_CONTENT_DIR=../docs 24 | 25 | should_pull=true 26 | 27 | # Clone the dev-portal project, if needed 28 | if [ ! -d "$PREVIEW_DIR" ]; then 29 | echo "⏳ Cloning the $REPO_TO_CLONE repo, this might take a while..." 30 | git clone --depth=1 https://github.com/hashicorp/$REPO_TO_CLONE.git "$PREVIEW_DIR" 31 | should_pull=false 32 | fi 33 | 34 | cd "$PREVIEW_DIR" 35 | 36 | # If the directory already existed, pull to ensure the clone is fresh 37 | if [ "$should_pull" = true ]; then 38 | git pull origin main 39 | fi 40 | 41 | # Run the dev-portal content-repo start script 42 | REPO=$PRODUCT \ 43 | PREVIEW_FROM_REPO=$PRODUCT \ 44 | LOCAL_CONTENT_DIR=$LOCAL_CONTENT_DIR \ 45 | CURRENT_GIT_BRANCH=$CURRENT_GIT_BRANCH \ 46 | PREVIEW_MODE=$PREVIEW_MODE \ 47 | npm run start:local-preview --------------------------------------------------------------------------------