From d6654195c099b81969bb8043e98771cf199ee0f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Cie=C5=9Blak?= Date: Tue, 22 Oct 2024 12:55:46 +0200 Subject: [PATCH] chore: Test support for object renaming (#3130) ## Changes - Added integration tests to show the inconsistency between ShowByID errors - Added acceptance tests to show different behaviors of objects after renaming higher-hierarchy objects - Added generated models for database and schema - Added new test command to Makefile that runs the tests (they're separated from the CI tests by env variable that has to be enabled to run the tests) ## TODO - Pick the important tests that should be running with other acceptance tests - Transform shallow hierarchy tests into parametrized ones (similar to what we have right now in deep hierarchy test). --- Makefile | 3 + .../resourceassert/gen/resource_schema_def.go | 8 + .../bettertestspoc/config/config.go | 13 +- .../config/model/database_model_gen.go | 283 ++++ .../config/model/schema_model_gen.go | 301 ++++ .../config/model/view_model_ext.go | 4 +- pkg/acceptance/helpers/database_client.go | 8 + pkg/acceptance/helpers/schema_client.go | 8 + .../testenvs/testing_environment_variables.go | 5 +- .../object_renaming_acceptance_test.go | 1366 +++++++++++++++++ pkg/sdk/testint/errors_integration_test.go | 146 ++ 11 files changed, 2139 insertions(+), 6 deletions(-) create mode 100644 pkg/acceptance/bettertestspoc/config/model/database_model_gen.go create mode 100644 pkg/acceptance/bettertestspoc/config/model/schema_model_gen.go create mode 100644 pkg/resources/object_renaming_acceptance_test.go create mode 100644 pkg/sdk/testint/errors_integration_test.go diff --git a/Makefile b/Makefile index e81c234249..c8feb876b8 100644 --- a/Makefile +++ b/Makefile @@ -76,6 +76,9 @@ test-architecture: ## check architecture constraints between packages test-client: ## runs test that checks sdk.Client without instrumentedsql SF_TF_NO_INSTRUMENTED_SQL=1 SF_TF_GOSNOWFLAKE_LOG_LEVEL=debug go test ./pkg/sdk/internal/client/... -v +test-object-renaming: ## runs tests in object_renaming_acceptance_test.go + TEST_SF_TF_ENABLE_OBJECT_RENAMING=1 go test ./pkg/resources/object_renaming_acceptace_test.go -v + test-acceptance-%: ## run acceptance tests for the given resource only, e.g. test-acceptance-Warehouse TF_ACC=1 TF_LOG=DEBUG SF_TF_ACC_TEST_CONFIGURE_CLIENT_ONCE=true go test -run ^TestAcc_$*_ -v -timeout=20m ./pkg/resources diff --git a/pkg/acceptance/bettertestspoc/assert/resourceassert/gen/resource_schema_def.go b/pkg/acceptance/bettertestspoc/assert/resourceassert/gen/resource_schema_def.go index 4f618ddfa5..cba3663a57 100644 --- a/pkg/acceptance/bettertestspoc/assert/resourceassert/gen/resource_schema_def.go +++ b/pkg/acceptance/bettertestspoc/assert/resourceassert/gen/resource_schema_def.go @@ -41,6 +41,10 @@ var allResourceSchemaDefs = []ResourceSchemaDef{ name: "View", schema: resources.View().Schema, }, + { + name: "Database", + schema: resources.Database().Schema, + }, { name: "DatabaseRole", schema: resources.DatabaseRole().Schema, @@ -53,6 +57,10 @@ var allResourceSchemaDefs = []ResourceSchemaDef{ name: "RowAccessPolicy", schema: resources.RowAccessPolicy().Schema, }, + { + name: "Schema", + schema: resources.Schema().Schema, + }, { name: "MaskingPolicy", schema: resources.MaskingPolicy().Schema, diff --git a/pkg/acceptance/bettertestspoc/config/config.go b/pkg/acceptance/bettertestspoc/config/config.go index 3135e9a2a8..a47a2a43d2 100644 --- a/pkg/acceptance/bettertestspoc/config/config.go +++ b/pkg/acceptance/bettertestspoc/config/config.go @@ -25,7 +25,7 @@ type ResourceModel interface { SetResourceName(name string) ResourceReference() string DependsOn() []string - SetDependsOn(values []string) + SetDependsOn(values ...string) } type ResourceModelMeta struct { @@ -54,7 +54,7 @@ func (m *ResourceModelMeta) DependsOn() []string { return m.dependsOn } -func (m *ResourceModelMeta) SetDependsOn(values []string) { +func (m *ResourceModelMeta) SetDependsOn(values ...string) { m.dependsOn = values } @@ -99,6 +99,15 @@ func FromModel(t *testing.T, model ResourceModel) string { return s } +func FromModels(t *testing.T, models ...ResourceModel) string { + t.Helper() + var sb strings.Builder + for _, model := range models { + sb.WriteString(FromModel(t, model) + "\n") + } + return sb.String() +} + // ConfigVariablesFromModel constructs config.Variables needed in acceptance tests that are using ConfigVariables in // combination with ConfigDirectory. It's necessary for cases not supported by FromModel, like lists of objects. func ConfigVariablesFromModel(t *testing.T, model ResourceModel) tfconfig.Variables { diff --git a/pkg/acceptance/bettertestspoc/config/model/database_model_gen.go b/pkg/acceptance/bettertestspoc/config/model/database_model_gen.go new file mode 100644 index 0000000000..813b6b96e3 --- /dev/null +++ b/pkg/acceptance/bettertestspoc/config/model/database_model_gen.go @@ -0,0 +1,283 @@ +// Code generated by config model builder generator; DO NOT EDIT. + +package model + +import ( + tfconfig "github.com/hashicorp/terraform-plugin-testing/config" + + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/bettertestspoc/config" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/provider/resources" +) + +type DatabaseModel struct { + Catalog tfconfig.Variable `json:"catalog,omitempty"` + Comment tfconfig.Variable `json:"comment,omitempty"` + DataRetentionTimeInDays tfconfig.Variable `json:"data_retention_time_in_days,omitempty"` + DefaultDdlCollation tfconfig.Variable `json:"default_ddl_collation,omitempty"` + DropPublicSchemaOnCreation tfconfig.Variable `json:"drop_public_schema_on_creation,omitempty"` + EnableConsoleOutput tfconfig.Variable `json:"enable_console_output,omitempty"` + ExternalVolume tfconfig.Variable `json:"external_volume,omitempty"` + FullyQualifiedName tfconfig.Variable `json:"fully_qualified_name,omitempty"` + IsTransient tfconfig.Variable `json:"is_transient,omitempty"` + LogLevel tfconfig.Variable `json:"log_level,omitempty"` + MaxDataExtensionTimeInDays tfconfig.Variable `json:"max_data_extension_time_in_days,omitempty"` + Name tfconfig.Variable `json:"name,omitempty"` + QuotedIdentifiersIgnoreCase tfconfig.Variable `json:"quoted_identifiers_ignore_case,omitempty"` + ReplaceInvalidCharacters tfconfig.Variable `json:"replace_invalid_characters,omitempty"` + Replication tfconfig.Variable `json:"replication,omitempty"` + StorageSerializationPolicy tfconfig.Variable `json:"storage_serialization_policy,omitempty"` + SuspendTaskAfterNumFailures tfconfig.Variable `json:"suspend_task_after_num_failures,omitempty"` + TaskAutoRetryAttempts tfconfig.Variable `json:"task_auto_retry_attempts,omitempty"` + TraceLevel tfconfig.Variable `json:"trace_level,omitempty"` + UserTaskManagedInitialWarehouseSize tfconfig.Variable `json:"user_task_managed_initial_warehouse_size,omitempty"` + UserTaskMinimumTriggerIntervalInSeconds tfconfig.Variable `json:"user_task_minimum_trigger_interval_in_seconds,omitempty"` + UserTaskTimeoutMs tfconfig.Variable `json:"user_task_timeout_ms,omitempty"` + + *config.ResourceModelMeta +} + +///////////////////////////////////////////////// +// Basic builders (resource name and required) // +///////////////////////////////////////////////// + +func Database( + resourceName string, + name string, +) *DatabaseModel { + d := &DatabaseModel{ResourceModelMeta: config.Meta(resourceName, resources.Database)} + d.WithName(name) + return d +} + +func DatabaseWithDefaultMeta( + name string, +) *DatabaseModel { + d := &DatabaseModel{ResourceModelMeta: config.DefaultMeta(resources.Database)} + d.WithName(name) + return d +} + +///////////////////////////////// +// below all the proper values // +///////////////////////////////// + +func (d *DatabaseModel) WithCatalog(catalog string) *DatabaseModel { + d.Catalog = tfconfig.StringVariable(catalog) + return d +} + +func (d *DatabaseModel) WithComment(comment string) *DatabaseModel { + d.Comment = tfconfig.StringVariable(comment) + return d +} + +func (d *DatabaseModel) WithDataRetentionTimeInDays(dataRetentionTimeInDays int) *DatabaseModel { + d.DataRetentionTimeInDays = tfconfig.IntegerVariable(dataRetentionTimeInDays) + return d +} + +func (d *DatabaseModel) WithDefaultDdlCollation(defaultDdlCollation string) *DatabaseModel { + d.DefaultDdlCollation = tfconfig.StringVariable(defaultDdlCollation) + return d +} + +func (d *DatabaseModel) WithDropPublicSchemaOnCreation(dropPublicSchemaOnCreation bool) *DatabaseModel { + d.DropPublicSchemaOnCreation = tfconfig.BoolVariable(dropPublicSchemaOnCreation) + return d +} + +func (d *DatabaseModel) WithEnableConsoleOutput(enableConsoleOutput bool) *DatabaseModel { + d.EnableConsoleOutput = tfconfig.BoolVariable(enableConsoleOutput) + return d +} + +func (d *DatabaseModel) WithExternalVolume(externalVolume string) *DatabaseModel { + d.ExternalVolume = tfconfig.StringVariable(externalVolume) + return d +} + +func (d *DatabaseModel) WithFullyQualifiedName(fullyQualifiedName string) *DatabaseModel { + d.FullyQualifiedName = tfconfig.StringVariable(fullyQualifiedName) + return d +} + +func (d *DatabaseModel) WithIsTransient(isTransient bool) *DatabaseModel { + d.IsTransient = tfconfig.BoolVariable(isTransient) + return d +} + +func (d *DatabaseModel) WithLogLevel(logLevel string) *DatabaseModel { + d.LogLevel = tfconfig.StringVariable(logLevel) + return d +} + +func (d *DatabaseModel) WithMaxDataExtensionTimeInDays(maxDataExtensionTimeInDays int) *DatabaseModel { + d.MaxDataExtensionTimeInDays = tfconfig.IntegerVariable(maxDataExtensionTimeInDays) + return d +} + +func (d *DatabaseModel) WithName(name string) *DatabaseModel { + d.Name = tfconfig.StringVariable(name) + return d +} + +func (d *DatabaseModel) WithQuotedIdentifiersIgnoreCase(quotedIdentifiersIgnoreCase bool) *DatabaseModel { + d.QuotedIdentifiersIgnoreCase = tfconfig.BoolVariable(quotedIdentifiersIgnoreCase) + return d +} + +func (d *DatabaseModel) WithReplaceInvalidCharacters(replaceInvalidCharacters bool) *DatabaseModel { + d.ReplaceInvalidCharacters = tfconfig.BoolVariable(replaceInvalidCharacters) + return d +} + +// replication attribute type is not yet supported, so WithReplication can't be generated + +func (d *DatabaseModel) WithStorageSerializationPolicy(storageSerializationPolicy string) *DatabaseModel { + d.StorageSerializationPolicy = tfconfig.StringVariable(storageSerializationPolicy) + return d +} + +func (d *DatabaseModel) WithSuspendTaskAfterNumFailures(suspendTaskAfterNumFailures int) *DatabaseModel { + d.SuspendTaskAfterNumFailures = tfconfig.IntegerVariable(suspendTaskAfterNumFailures) + return d +} + +func (d *DatabaseModel) WithTaskAutoRetryAttempts(taskAutoRetryAttempts int) *DatabaseModel { + d.TaskAutoRetryAttempts = tfconfig.IntegerVariable(taskAutoRetryAttempts) + return d +} + +func (d *DatabaseModel) WithTraceLevel(traceLevel string) *DatabaseModel { + d.TraceLevel = tfconfig.StringVariable(traceLevel) + return d +} + +func (d *DatabaseModel) WithUserTaskManagedInitialWarehouseSize(userTaskManagedInitialWarehouseSize string) *DatabaseModel { + d.UserTaskManagedInitialWarehouseSize = tfconfig.StringVariable(userTaskManagedInitialWarehouseSize) + return d +} + +func (d *DatabaseModel) WithUserTaskMinimumTriggerIntervalInSeconds(userTaskMinimumTriggerIntervalInSeconds int) *DatabaseModel { + d.UserTaskMinimumTriggerIntervalInSeconds = tfconfig.IntegerVariable(userTaskMinimumTriggerIntervalInSeconds) + return d +} + +func (d *DatabaseModel) WithUserTaskTimeoutMs(userTaskTimeoutMs int) *DatabaseModel { + d.UserTaskTimeoutMs = tfconfig.IntegerVariable(userTaskTimeoutMs) + return d +} + +////////////////////////////////////////// +// below it's possible to set any value // +////////////////////////////////////////// + +func (d *DatabaseModel) WithCatalogValue(value tfconfig.Variable) *DatabaseModel { + d.Catalog = value + return d +} + +func (d *DatabaseModel) WithCommentValue(value tfconfig.Variable) *DatabaseModel { + d.Comment = value + return d +} + +func (d *DatabaseModel) WithDataRetentionTimeInDaysValue(value tfconfig.Variable) *DatabaseModel { + d.DataRetentionTimeInDays = value + return d +} + +func (d *DatabaseModel) WithDefaultDdlCollationValue(value tfconfig.Variable) *DatabaseModel { + d.DefaultDdlCollation = value + return d +} + +func (d *DatabaseModel) WithDropPublicSchemaOnCreationValue(value tfconfig.Variable) *DatabaseModel { + d.DropPublicSchemaOnCreation = value + return d +} + +func (d *DatabaseModel) WithEnableConsoleOutputValue(value tfconfig.Variable) *DatabaseModel { + d.EnableConsoleOutput = value + return d +} + +func (d *DatabaseModel) WithExternalVolumeValue(value tfconfig.Variable) *DatabaseModel { + d.ExternalVolume = value + return d +} + +func (d *DatabaseModel) WithFullyQualifiedNameValue(value tfconfig.Variable) *DatabaseModel { + d.FullyQualifiedName = value + return d +} + +func (d *DatabaseModel) WithIsTransientValue(value tfconfig.Variable) *DatabaseModel { + d.IsTransient = value + return d +} + +func (d *DatabaseModel) WithLogLevelValue(value tfconfig.Variable) *DatabaseModel { + d.LogLevel = value + return d +} + +func (d *DatabaseModel) WithMaxDataExtensionTimeInDaysValue(value tfconfig.Variable) *DatabaseModel { + d.MaxDataExtensionTimeInDays = value + return d +} + +func (d *DatabaseModel) WithNameValue(value tfconfig.Variable) *DatabaseModel { + d.Name = value + return d +} + +func (d *DatabaseModel) WithQuotedIdentifiersIgnoreCaseValue(value tfconfig.Variable) *DatabaseModel { + d.QuotedIdentifiersIgnoreCase = value + return d +} + +func (d *DatabaseModel) WithReplaceInvalidCharactersValue(value tfconfig.Variable) *DatabaseModel { + d.ReplaceInvalidCharacters = value + return d +} + +func (d *DatabaseModel) WithReplicationValue(value tfconfig.Variable) *DatabaseModel { + d.Replication = value + return d +} + +func (d *DatabaseModel) WithStorageSerializationPolicyValue(value tfconfig.Variable) *DatabaseModel { + d.StorageSerializationPolicy = value + return d +} + +func (d *DatabaseModel) WithSuspendTaskAfterNumFailuresValue(value tfconfig.Variable) *DatabaseModel { + d.SuspendTaskAfterNumFailures = value + return d +} + +func (d *DatabaseModel) WithTaskAutoRetryAttemptsValue(value tfconfig.Variable) *DatabaseModel { + d.TaskAutoRetryAttempts = value + return d +} + +func (d *DatabaseModel) WithTraceLevelValue(value tfconfig.Variable) *DatabaseModel { + d.TraceLevel = value + return d +} + +func (d *DatabaseModel) WithUserTaskManagedInitialWarehouseSizeValue(value tfconfig.Variable) *DatabaseModel { + d.UserTaskManagedInitialWarehouseSize = value + return d +} + +func (d *DatabaseModel) WithUserTaskMinimumTriggerIntervalInSecondsValue(value tfconfig.Variable) *DatabaseModel { + d.UserTaskMinimumTriggerIntervalInSeconds = value + return d +} + +func (d *DatabaseModel) WithUserTaskTimeoutMsValue(value tfconfig.Variable) *DatabaseModel { + d.UserTaskTimeoutMs = value + return d +} diff --git a/pkg/acceptance/bettertestspoc/config/model/schema_model_gen.go b/pkg/acceptance/bettertestspoc/config/model/schema_model_gen.go new file mode 100644 index 0000000000..e83ac5058d --- /dev/null +++ b/pkg/acceptance/bettertestspoc/config/model/schema_model_gen.go @@ -0,0 +1,301 @@ +// Code generated by config model builder generator; DO NOT EDIT. + +package model + +import ( + tfconfig "github.com/hashicorp/terraform-plugin-testing/config" + + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/bettertestspoc/config" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/provider/resources" +) + +type SchemaModel struct { + Catalog tfconfig.Variable `json:"catalog,omitempty"` + Comment tfconfig.Variable `json:"comment,omitempty"` + DataRetentionTimeInDays tfconfig.Variable `json:"data_retention_time_in_days,omitempty"` + Database tfconfig.Variable `json:"database,omitempty"` + DefaultDdlCollation tfconfig.Variable `json:"default_ddl_collation,omitempty"` + EnableConsoleOutput tfconfig.Variable `json:"enable_console_output,omitempty"` + ExternalVolume tfconfig.Variable `json:"external_volume,omitempty"` + FullyQualifiedName tfconfig.Variable `json:"fully_qualified_name,omitempty"` + IsTransient tfconfig.Variable `json:"is_transient,omitempty"` + LogLevel tfconfig.Variable `json:"log_level,omitempty"` + MaxDataExtensionTimeInDays tfconfig.Variable `json:"max_data_extension_time_in_days,omitempty"` + Name tfconfig.Variable `json:"name,omitempty"` + PipeExecutionPaused tfconfig.Variable `json:"pipe_execution_paused,omitempty"` + QuotedIdentifiersIgnoreCase tfconfig.Variable `json:"quoted_identifiers_ignore_case,omitempty"` + ReplaceInvalidCharacters tfconfig.Variable `json:"replace_invalid_characters,omitempty"` + StorageSerializationPolicy tfconfig.Variable `json:"storage_serialization_policy,omitempty"` + SuspendTaskAfterNumFailures tfconfig.Variable `json:"suspend_task_after_num_failures,omitempty"` + TaskAutoRetryAttempts tfconfig.Variable `json:"task_auto_retry_attempts,omitempty"` + TraceLevel tfconfig.Variable `json:"trace_level,omitempty"` + UserTaskManagedInitialWarehouseSize tfconfig.Variable `json:"user_task_managed_initial_warehouse_size,omitempty"` + UserTaskMinimumTriggerIntervalInSeconds tfconfig.Variable `json:"user_task_minimum_trigger_interval_in_seconds,omitempty"` + UserTaskTimeoutMs tfconfig.Variable `json:"user_task_timeout_ms,omitempty"` + WithManagedAccess tfconfig.Variable `json:"with_managed_access,omitempty"` + + *config.ResourceModelMeta +} + +///////////////////////////////////////////////// +// Basic builders (resource name and required) // +///////////////////////////////////////////////// + +func Schema( + resourceName string, + database string, + name string, +) *SchemaModel { + s := &SchemaModel{ResourceModelMeta: config.Meta(resourceName, resources.Schema)} + s.WithDatabase(database) + s.WithName(name) + return s +} + +func SchemaWithDefaultMeta( + database string, + name string, +) *SchemaModel { + s := &SchemaModel{ResourceModelMeta: config.DefaultMeta(resources.Schema)} + s.WithDatabase(database) + s.WithName(name) + return s +} + +///////////////////////////////// +// below all the proper values // +///////////////////////////////// + +func (s *SchemaModel) WithCatalog(catalog string) *SchemaModel { + s.Catalog = tfconfig.StringVariable(catalog) + return s +} + +func (s *SchemaModel) WithComment(comment string) *SchemaModel { + s.Comment = tfconfig.StringVariable(comment) + return s +} + +func (s *SchemaModel) WithDataRetentionTimeInDays(dataRetentionTimeInDays int) *SchemaModel { + s.DataRetentionTimeInDays = tfconfig.IntegerVariable(dataRetentionTimeInDays) + return s +} + +func (s *SchemaModel) WithDatabase(database string) *SchemaModel { + s.Database = tfconfig.StringVariable(database) + return s +} + +func (s *SchemaModel) WithDefaultDdlCollation(defaultDdlCollation string) *SchemaModel { + s.DefaultDdlCollation = tfconfig.StringVariable(defaultDdlCollation) + return s +} + +func (s *SchemaModel) WithEnableConsoleOutput(enableConsoleOutput bool) *SchemaModel { + s.EnableConsoleOutput = tfconfig.BoolVariable(enableConsoleOutput) + return s +} + +func (s *SchemaModel) WithExternalVolume(externalVolume string) *SchemaModel { + s.ExternalVolume = tfconfig.StringVariable(externalVolume) + return s +} + +func (s *SchemaModel) WithFullyQualifiedName(fullyQualifiedName string) *SchemaModel { + s.FullyQualifiedName = tfconfig.StringVariable(fullyQualifiedName) + return s +} + +func (s *SchemaModel) WithIsTransient(isTransient string) *SchemaModel { + s.IsTransient = tfconfig.StringVariable(isTransient) + return s +} + +func (s *SchemaModel) WithLogLevel(logLevel string) *SchemaModel { + s.LogLevel = tfconfig.StringVariable(logLevel) + return s +} + +func (s *SchemaModel) WithMaxDataExtensionTimeInDays(maxDataExtensionTimeInDays int) *SchemaModel { + s.MaxDataExtensionTimeInDays = tfconfig.IntegerVariable(maxDataExtensionTimeInDays) + return s +} + +func (s *SchemaModel) WithName(name string) *SchemaModel { + s.Name = tfconfig.StringVariable(name) + return s +} + +func (s *SchemaModel) WithPipeExecutionPaused(pipeExecutionPaused bool) *SchemaModel { + s.PipeExecutionPaused = tfconfig.BoolVariable(pipeExecutionPaused) + return s +} + +func (s *SchemaModel) WithQuotedIdentifiersIgnoreCase(quotedIdentifiersIgnoreCase bool) *SchemaModel { + s.QuotedIdentifiersIgnoreCase = tfconfig.BoolVariable(quotedIdentifiersIgnoreCase) + return s +} + +func (s *SchemaModel) WithReplaceInvalidCharacters(replaceInvalidCharacters bool) *SchemaModel { + s.ReplaceInvalidCharacters = tfconfig.BoolVariable(replaceInvalidCharacters) + return s +} + +func (s *SchemaModel) WithStorageSerializationPolicy(storageSerializationPolicy string) *SchemaModel { + s.StorageSerializationPolicy = tfconfig.StringVariable(storageSerializationPolicy) + return s +} + +func (s *SchemaModel) WithSuspendTaskAfterNumFailures(suspendTaskAfterNumFailures int) *SchemaModel { + s.SuspendTaskAfterNumFailures = tfconfig.IntegerVariable(suspendTaskAfterNumFailures) + return s +} + +func (s *SchemaModel) WithTaskAutoRetryAttempts(taskAutoRetryAttempts int) *SchemaModel { + s.TaskAutoRetryAttempts = tfconfig.IntegerVariable(taskAutoRetryAttempts) + return s +} + +func (s *SchemaModel) WithTraceLevel(traceLevel string) *SchemaModel { + s.TraceLevel = tfconfig.StringVariable(traceLevel) + return s +} + +func (s *SchemaModel) WithUserTaskManagedInitialWarehouseSize(userTaskManagedInitialWarehouseSize string) *SchemaModel { + s.UserTaskManagedInitialWarehouseSize = tfconfig.StringVariable(userTaskManagedInitialWarehouseSize) + return s +} + +func (s *SchemaModel) WithUserTaskMinimumTriggerIntervalInSeconds(userTaskMinimumTriggerIntervalInSeconds int) *SchemaModel { + s.UserTaskMinimumTriggerIntervalInSeconds = tfconfig.IntegerVariable(userTaskMinimumTriggerIntervalInSeconds) + return s +} + +func (s *SchemaModel) WithUserTaskTimeoutMs(userTaskTimeoutMs int) *SchemaModel { + s.UserTaskTimeoutMs = tfconfig.IntegerVariable(userTaskTimeoutMs) + return s +} + +func (s *SchemaModel) WithWithManagedAccess(withManagedAccess string) *SchemaModel { + s.WithManagedAccess = tfconfig.StringVariable(withManagedAccess) + return s +} + +////////////////////////////////////////// +// below it's possible to set any value // +////////////////////////////////////////// + +func (s *SchemaModel) WithCatalogValue(value tfconfig.Variable) *SchemaModel { + s.Catalog = value + return s +} + +func (s *SchemaModel) WithCommentValue(value tfconfig.Variable) *SchemaModel { + s.Comment = value + return s +} + +func (s *SchemaModel) WithDataRetentionTimeInDaysValue(value tfconfig.Variable) *SchemaModel { + s.DataRetentionTimeInDays = value + return s +} + +func (s *SchemaModel) WithDatabaseValue(value tfconfig.Variable) *SchemaModel { + s.Database = value + return s +} + +func (s *SchemaModel) WithDefaultDdlCollationValue(value tfconfig.Variable) *SchemaModel { + s.DefaultDdlCollation = value + return s +} + +func (s *SchemaModel) WithEnableConsoleOutputValue(value tfconfig.Variable) *SchemaModel { + s.EnableConsoleOutput = value + return s +} + +func (s *SchemaModel) WithExternalVolumeValue(value tfconfig.Variable) *SchemaModel { + s.ExternalVolume = value + return s +} + +func (s *SchemaModel) WithFullyQualifiedNameValue(value tfconfig.Variable) *SchemaModel { + s.FullyQualifiedName = value + return s +} + +func (s *SchemaModel) WithIsTransientValue(value tfconfig.Variable) *SchemaModel { + s.IsTransient = value + return s +} + +func (s *SchemaModel) WithLogLevelValue(value tfconfig.Variable) *SchemaModel { + s.LogLevel = value + return s +} + +func (s *SchemaModel) WithMaxDataExtensionTimeInDaysValue(value tfconfig.Variable) *SchemaModel { + s.MaxDataExtensionTimeInDays = value + return s +} + +func (s *SchemaModel) WithNameValue(value tfconfig.Variable) *SchemaModel { + s.Name = value + return s +} + +func (s *SchemaModel) WithPipeExecutionPausedValue(value tfconfig.Variable) *SchemaModel { + s.PipeExecutionPaused = value + return s +} + +func (s *SchemaModel) WithQuotedIdentifiersIgnoreCaseValue(value tfconfig.Variable) *SchemaModel { + s.QuotedIdentifiersIgnoreCase = value + return s +} + +func (s *SchemaModel) WithReplaceInvalidCharactersValue(value tfconfig.Variable) *SchemaModel { + s.ReplaceInvalidCharacters = value + return s +} + +func (s *SchemaModel) WithStorageSerializationPolicyValue(value tfconfig.Variable) *SchemaModel { + s.StorageSerializationPolicy = value + return s +} + +func (s *SchemaModel) WithSuspendTaskAfterNumFailuresValue(value tfconfig.Variable) *SchemaModel { + s.SuspendTaskAfterNumFailures = value + return s +} + +func (s *SchemaModel) WithTaskAutoRetryAttemptsValue(value tfconfig.Variable) *SchemaModel { + s.TaskAutoRetryAttempts = value + return s +} + +func (s *SchemaModel) WithTraceLevelValue(value tfconfig.Variable) *SchemaModel { + s.TraceLevel = value + return s +} + +func (s *SchemaModel) WithUserTaskManagedInitialWarehouseSizeValue(value tfconfig.Variable) *SchemaModel { + s.UserTaskManagedInitialWarehouseSize = value + return s +} + +func (s *SchemaModel) WithUserTaskMinimumTriggerIntervalInSecondsValue(value tfconfig.Variable) *SchemaModel { + s.UserTaskMinimumTriggerIntervalInSeconds = value + return s +} + +func (s *SchemaModel) WithUserTaskTimeoutMsValue(value tfconfig.Variable) *SchemaModel { + s.UserTaskTimeoutMs = value + return s +} + +func (s *SchemaModel) WithWithManagedAccessValue(value tfconfig.Variable) *SchemaModel { + s.WithManagedAccess = value + return s +} diff --git a/pkg/acceptance/bettertestspoc/config/model/view_model_ext.go b/pkg/acceptance/bettertestspoc/config/model/view_model_ext.go index f37cde3e33..db546a67f6 100644 --- a/pkg/acceptance/bettertestspoc/config/model/view_model_ext.go +++ b/pkg/acceptance/bettertestspoc/config/model/view_model_ext.go @@ -1,6 +1,6 @@ package model -func (v *ViewModel) WithDependsOn(values []string) *ViewModel { - v.SetDependsOn(values) +func (v *ViewModel) WithDependsOn(values ...string) *ViewModel { + v.SetDependsOn(values...) return v } diff --git a/pkg/acceptance/helpers/database_client.go b/pkg/acceptance/helpers/database_client.go index a0ea93ebab..4caeb07d90 100644 --- a/pkg/acceptance/helpers/database_client.go +++ b/pkg/acceptance/helpers/database_client.go @@ -48,6 +48,14 @@ func (c *DatabaseClient) CreateDatabaseWithOptions(t *testing.T, id sdk.AccountO return database, c.DropDatabaseFunc(t, id) } +func (c *DatabaseClient) Alter(t *testing.T, id sdk.AccountObjectIdentifier, opts *sdk.AlterDatabaseOptions) { + t.Helper() + ctx := context.Background() + + err := c.client().Alter(ctx, id, opts) + require.NoError(t, err) +} + func (c *DatabaseClient) DropDatabaseFunc(t *testing.T, id sdk.AccountObjectIdentifier) func() { t.Helper() return func() { require.NoError(t, c.DropDatabase(t, id)) } diff --git a/pkg/acceptance/helpers/schema_client.go b/pkg/acceptance/helpers/schema_client.go index 7c58b90560..9b7a26bed2 100644 --- a/pkg/acceptance/helpers/schema_client.go +++ b/pkg/acceptance/helpers/schema_client.go @@ -55,6 +55,14 @@ func (c *SchemaClient) CreateSchemaWithOpts(t *testing.T, id sdk.DatabaseObjectI return schema, c.DropSchemaFunc(t, id) } +func (c *SchemaClient) Alter(t *testing.T, id sdk.DatabaseObjectIdentifier, opts *sdk.AlterSchemaOptions) { + t.Helper() + ctx := context.Background() + + err := c.client().Alter(ctx, id, opts) + require.NoError(t, err) +} + func (c *SchemaClient) DropSchemaFunc(t *testing.T, id sdk.DatabaseObjectIdentifier) func() { t.Helper() ctx := context.Background() diff --git a/pkg/acceptance/testenvs/testing_environment_variables.go b/pkg/acceptance/testenvs/testing_environment_variables.go index 7bc1a3e082..7b6e22a50b 100644 --- a/pkg/acceptance/testenvs/testing_environment_variables.go +++ b/pkg/acceptance/testenvs/testing_environment_variables.go @@ -26,8 +26,9 @@ const ( AzureExternalSasToken env = "TEST_SF_TF_AZURE_EXTERNAL_SAS_TOKEN" // #nosec G101 GcsExternalBuckerUrl env = "TEST_SF_TF_GCS_EXTERNAL_BUCKET_URL" - SkipManagedAccountTest env = "TEST_SF_TF_SKIP_MANAGED_ACCOUNT_TEST" - SkipSamlIntegrationTest env = "TEST_SF_TF_SKIP_SAML_INTEGRATION_TEST" + EnableObjectRenamingTest env = "TEST_SF_TF_ENABLE_OBJECT_RENAMING" + SkipManagedAccountTest env = "TEST_SF_TF_SKIP_MANAGED_ACCOUNT_TEST" + SkipSamlIntegrationTest env = "TEST_SF_TF_SKIP_SAML_INTEGRATION_TEST" EnableAcceptance env = resource.EnvTfAcc EnableSweep env = "TEST_SF_TF_ENABLE_SWEEP" diff --git a/pkg/resources/object_renaming_acceptance_test.go b/pkg/resources/object_renaming_acceptance_test.go new file mode 100644 index 0000000000..a74523e3eb --- /dev/null +++ b/pkg/resources/object_renaming_acceptance_test.go @@ -0,0 +1,1366 @@ +package resources_test + +import ( + "fmt" + "regexp" + "strconv" + "strings" + "testing" + + acc "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/bettertestspoc/config" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/bettertestspoc/config/model" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/testenvs" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/provider/resources" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk" + "github.com/hashicorp/terraform-plugin-testing/helper/resource" + "github.com/hashicorp/terraform-plugin-testing/plancheck" + "github.com/hashicorp/terraform-plugin-testing/tfversion" +) + +/* +The following tests are showing the behavior of the provider in cases where objects higher in the hierarchy +like database or schema are renamed when the objects lower in the hierarchy are in the Terraform configuration. +For more information check TODO(SNOW-1672319): link public document. + +Shallow hierarchy (database + schema) +- is in config - renamed internally - with implicit dependency (works) + +- is in config - renamed internally - without dependency - after rename schema referencing old database name (fails in Read and then it's failing to remove itself in Delete) +- is in config - renamed internally - without dependency - after rename schema referencing new database name (fails in Read and then it's failing to remove itself in Delete) + +- is in config - renamed internally - with depends_on - after rename schema referencing old database name (fails in Read and then it's failing to remove itself in Delete) +- is in config - renamed internally - with depends_on - after rename schema referencing new database name (works) + +- is in config - renamed externally - with implicit dependency - database holding the same name in config (works; creates a new database with a schema next to the already existing renamed database and schema) +- is in config - renamed externally - with implicit dependency - database holding the new name in config (fails to create database, because it already exists) + +- is in config - renamed externally - without dependency - after rename database referencing old name and schema referencing old database name (non-deterministic results depending on the Terraform execution order that seems to be different with every run) +- is in config - renamed externally - without dependency - after rename database referencing old name and schema referencing old database name - check impact of the resource order on the plan (seems to fail pretty consistently in Delete because database is dropped before schema) +- is in config - renamed externally - without dependency - after rename database referencing old name and schema referencing new database name (fails because schema resource tries to create a new schema that already exists in renamed database) +- is in config - renamed externally - without dependency - after rename database referencing new name and schema referencing old database name (fails because database resource tried to create database that already exists) +- is in config - renamed externally - without dependency - after rename database referencing new name and schema referencing new database name (fails because database resource tried to create database that already exists) + +- is in config - renamed externally - with depends_on - after rename database referencing old name and schema referencing old database name (works; creates a new database with a schema next to the already existing renamed database and schema) +- is in config - renamed externally - with depends_on - after rename database referencing old name and schema referencing new database name (fails because schema resource tries to create a new schema that already exists in renamed database) +- is in config - renamed externally - with depends_on - after rename database referencing new name and schema referencing old database name (fails because database resource tried to create database that already exists) +- is in config - renamed externally - with depends_on - after rename database referencing new name and schema referencing new database name (fails because database resource tried to create database that already exists) + +- is not in config - renamed externally - referencing old database name (fails because it tries to create a new schema on non-existing database) +- is not in config - renamed externally - referencing new database name (fails because schema resource tries to create a new schema that already exists in renamed database) + +Deep hierarchy (database + schema + table) + +- are in config - database renamed internally - with database implicit dependency - with no schema dependency - with database to schema implicit dependency (fails because table is created before schema) +- are in config - database renamed internally - with database implicit dependency - with implicit schema dependency - with database to schema implicit dependency (works) +- are in config - database renamed internally - with database implicit dependency - with schema depends_on - with database to schema implicit dependency (works) + +- are in config - database renamed internally - with database implicit dependency - with no schema dependency - with database to schema depends_on dependency (fails because table is created before schema) +- are in config - database renamed internally - with database implicit dependency - with implicit schema dependency - with database to schema depends_on dependency (works) +- are in config - database renamed internally - with database implicit dependency - with schema depends_on - with database to schema depends_on dependency (works) + +- are in config - database renamed internally - with database implicit dependency - with no schema dependency - with database to schema no dependency (fails during delete because database is deleted before schema) +- are in config - database renamed internally - with database implicit dependency - with implicit schema dependency - with database to schema no dependency (fails to drop schema after database rename) +- are in config - database renamed internally - with database implicit dependency - with schema depends_on - with database to schema no dependency (fails to drop schema after database rename) + +- are in config - database renamed internally - with no database dependency - with no schema dependency (fails because table is created before schema) +- are in config - database renamed internally - with no database dependency - with implicit schema dependency (works) +- are in config - database renamed internally - with no database dependency - with schema depends_on (works) + +- are in config - database renamed internally - with database depends_on - with no schema dependency (fails because table is created before schema) +- are in config - database renamed internally - with database depends_on - with implicit schema dependency (works) +- are in config - database renamed internally - with database depends_on - with schema depends_on (works) + +------------------------------------------------------------------------------------------------------------------------ + +- are in config - schema renamed internally - with database implicit dependency - with no schema dependency (fails because table is created before schema) +- are in config - schema renamed internally - with database implicit dependency - with implicit schema dependency (works) +- are in config - schema renamed internally - with database implicit dependency - with schema depends_on (works) + +- are in config - schema renamed internally - with no database dependency - with no schema dependency (fails because table is created before schema) +- are in config - schema renamed internally - with no database dependency - with implicit schema dependency (works) +- are in config - schema renamed internally - with no database dependency - with schema depends_on (works) + +- are in config - schema renamed internally - with database depends_on - with no schema dependency (fails because table is created before schema) +- are in config - schema renamed internally - with database depends_on - with implicit schema dependency (works) +- are in config - schema renamed internally - with database depends_on - with schema depends_on (works) + +------------------------------------------------------------------------------------------------------------------------ + +- are in config - database renamed externally - with database implicit dependency - with no schema dependency (fails because table is created before schema) +- are in config - database renamed externally - with database implicit dependency - with implicit schema dependency (fails because tries to create database when it's already there after rename) +- are in config - database renamed externally - with database implicit dependency - with schema depends_on (fails because tries to create database when it's already there after rename) + +- are in config - database renamed externally - with no database dependency - with no schema dependency (fails because table is created before schema) +- are in config - database renamed externally - with no database dependency - with implicit schema dependency (fails because tries to create database when it's already there after rename) +- are in config - database renamed externally - with no database dependency - with schema depends_on (fails because tries to create database when it's already there after rename) + +- are in config - database renamed externally - with database depends_on - with no schema dependency (fails because table is created before schema) +- are in config - database renamed externally - with database depends_on - with implicit schema dependency (fails because tries to create database when it's already there after rename) +- are in config - database renamed externally - with database depends_on - with schema depends_on (fails because tries to create database when it's already there after rename) + +------------------------------------------------------------------------------------------------------------------------ + +- are in config - schema renamed externally - with database implicit dependency - with no schema dependency (fails because table is created before schema) +- are in config - schema renamed externally - with database implicit dependency - with implicit schema dependency (fails because tries to create database when it's already there after rename) +- are in config - schema renamed externally - with database implicit dependency - with schema depends_on (fails because tries to create database when it's already there after rename) + +- are in config - schema renamed externally - with no database dependency - with no schema dependency (fails because table is created before schema) +- are in config - schema renamed externally - with no database dependency - with implicit schema dependency (fails because tries to create database when it's already there after rename) +- are in config - schema renamed externally - with no database dependency - with schema depends_on (fails because tries to create database when it's already there after rename) + +- are in config - schema renamed externally - with database depends_on - with no schema dependency (fails because table is created before schema) +- are in config - schema renamed externally - with database depends_on - with implicit schema dependency (fails because tries to create database when it's already there after rename) +- are in config - schema renamed externally - with database depends_on - with schema depends_on (fails because tries to create database when it's already there after rename) + +------------------------------------------------------------------------------------------------------------------------ + +- are not in config - database renamed externally - referencing old database name (fails because tries to create table on non-existing database) +- are not in config - database renamed externally - referencing new database name (fails because tries to create table that already exists in the renamed database) + +- are not in config - schema renamed externally - referencing old schema name (fails because tries to create table on non-existing schema) +- are not in config - schema renamed externally - referencing new schema name (fails because tries to create table that already exists in the renamed schema) + +# The list of test cases that were not added: +- (Deep hierarchy) More test cases with varying dependencies between resources +- (Deep hierarchy) Add test cases where old database is referenced to see if hierarchy recreation is possible +- (Deep hierarchy) More test cases could be added when database and schema are renamed at the same time +- (Deep hierarchy) More test cases could be added when either database or schema are in the config +*/ + +type DependencyType string + +const ( + ImplicitDependency DependencyType = "implicit" + DependsOnDependency DependencyType = "depends_on" + NoDependency DependencyType = "no_dependency" +) + +func TestAcc_ShallowHierarchy_IsInConfig_RenamedInternally_WithImplicitDependency(t *testing.T) { + _ = testenvs.GetOrSkipTest(t, testenvs.EnableObjectRenamingTest) + _ = testenvs.GetOrSkipTest(t, testenvs.EnableAcceptance) + acc.TestAccPreCheck(t) + + databaseId := acc.TestClient().Ids.RandomAccountObjectIdentifier() + newDatabaseId := acc.TestClient().Ids.RandomAccountObjectIdentifier() + schemaName := acc.TestClient().Ids.Alpha() + + databaseConfigModel := model.Database("test", databaseId.Name()) + databaseConfigModelWithNewId := model.Database("test", newDatabaseId.Name()) + + resource.Test(t, resource.TestCase{ + ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories, + TerraformVersionChecks: []tfversion.TerraformVersionCheck{ + tfversion.RequireAbove(tfversion.Version1_5_0), + }, + CheckDestroy: acc.CheckDestroy(t, resources.Schema), + Steps: []resource.TestStep{ + { + Config: config.FromModel(t, databaseConfigModel) + configSchemaWithDatabaseReference(databaseConfigModel.ResourceReference(), schemaName), + }, + { + ConfigPlanChecks: resource.ConfigPlanChecks{ + PreApply: []plancheck.PlanCheck{ + plancheck.ExpectResourceAction("snowflake_database.test", plancheck.ResourceActionUpdate), + plancheck.ExpectResourceAction("snowflake_schema.test", plancheck.ResourceActionDestroyBeforeCreate), + }, + }, + Config: config.FromModel(t, databaseConfigModelWithNewId) + configSchemaWithDatabaseReference(databaseConfigModelWithNewId.ResourceReference(), schemaName), + }, + }, + }) +} + +func TestAcc_ShallowHierarchy_IsInConfig_RenamedInternally_WithoutDependency_AfterRenameSchemaReferencingOldDatabaseName(t *testing.T) { + // Error happens during schema's Read operation and then Delete operation (schema cannot be removed). + t.Skip("Not able to handle the error produced by Delete operation that results in test always failing") + _ = testenvs.GetOrSkipTest(t, testenvs.EnableObjectRenamingTest) + _ = testenvs.GetOrSkipTest(t, testenvs.EnableAcceptance) + acc.TestAccPreCheck(t) + + databaseId := acc.TestClient().Ids.RandomAccountObjectIdentifier() + newDatabaseId := acc.TestClient().Ids.RandomAccountObjectIdentifier() + schemaName := acc.TestClient().Ids.Alpha() + + databaseConfigModel := model.Database("test", databaseId.Name()) + databaseConfigModelWithNewId := model.Database("test", newDatabaseId.Name()) + + schemaModelConfig := model.Schema("test", databaseId.Name(), schemaName) + + resource.Test(t, resource.TestCase{ + ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories, + TerraformVersionChecks: []tfversion.TerraformVersionCheck{ + tfversion.RequireAbove(tfversion.Version1_5_0), + }, + CheckDestroy: acc.CheckDestroy(t, resources.Schema), + Steps: []resource.TestStep{ + { + Config: config.FromModels(t, databaseConfigModel, schemaModelConfig), + }, + { + ConfigPlanChecks: resource.ConfigPlanChecks{ + PreApply: []plancheck.PlanCheck{ + plancheck.ExpectResourceAction("snowflake_database.test", plancheck.ResourceActionUpdate), + plancheck.ExpectResourceAction("snowflake_schema.test", plancheck.ResourceActionNoop), + }, + }, + Config: config.FromModels(t, databaseConfigModelWithNewId, schemaModelConfig), + ExpectError: regexp.MustCompile("does not exist or not authorized"), + }, + }, + }) +} + +func TestAcc_ShallowHierarchy_IsInConfig_RenamedInternally_WithoutDependency_AfterRenameSchemaReferencingNewDatabaseName(t *testing.T) { + // Error happens during schema's Read operation and then Delete operation (schema cannot be removed). + t.Skip("Not able to handle the error produced by Delete operation that results in test always failing") + _ = testenvs.GetOrSkipTest(t, testenvs.EnableObjectRenamingTest) + _ = testenvs.GetOrSkipTest(t, testenvs.EnableAcceptance) + acc.TestAccPreCheck(t) + + databaseId := acc.TestClient().Ids.RandomAccountObjectIdentifier() + newDatabaseId := acc.TestClient().Ids.RandomAccountObjectIdentifier() + schemaName := acc.TestClient().Ids.Alpha() + + databaseConfigModel := model.Database("test", databaseId.Name()) + databaseConfigModelWithNewId := model.Database("test", newDatabaseId.Name()) + + schemaModelConfig := model.Schema("test", databaseId.Name(), schemaName) + schemaModelConfigWithNewDatabaseId := model.Schema("test", newDatabaseId.Name(), schemaName) + + resource.Test(t, resource.TestCase{ + ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories, + TerraformVersionChecks: []tfversion.TerraformVersionCheck{ + tfversion.RequireAbove(tfversion.Version1_5_0), + }, + CheckDestroy: acc.CheckDestroy(t, resources.Schema), + Steps: []resource.TestStep{ + { + Config: config.FromModels(t, databaseConfigModel, schemaModelConfig), + }, + { + ConfigPlanChecks: resource.ConfigPlanChecks{ + PreApply: []plancheck.PlanCheck{ + plancheck.ExpectResourceAction("snowflake_database.test", plancheck.ResourceActionUpdate), + plancheck.ExpectResourceAction("snowflake_schema.test", plancheck.ResourceActionDestroyBeforeCreate), + }, + }, + Config: config.FromModels(t, databaseConfigModelWithNewId, schemaModelConfigWithNewDatabaseId), + ExpectError: regexp.MustCompile("does not exist or not authorized"), + }, + }, + }) +} + +func TestAcc_ShallowHierarchy_IsInConfig_RenamedInternally_WithDependsOn_AfterRenameSchemaReferencingOldDatabaseName(t *testing.T) { + // Error happens during schema's Read operation and then Delete operation (schema cannot be removed). + t.Skip("Not able to handle the error produced by Delete operation that results in test always failing") + _ = testenvs.GetOrSkipTest(t, testenvs.EnableObjectRenamingTest) + _ = testenvs.GetOrSkipTest(t, testenvs.EnableAcceptance) + acc.TestAccPreCheck(t) + + databaseId := acc.TestClient().Ids.RandomAccountObjectIdentifier() + newDatabaseId := acc.TestClient().Ids.RandomAccountObjectIdentifier() + schemaName := acc.TestClient().Ids.Alpha() + + databaseConfigModel := model.Database("test", databaseId.Name()) + databaseConfigModelWithNewId := model.Database("test", newDatabaseId.Name()) + + schemaModelConfig := model.Schema("test", databaseId.Name(), schemaName) + schemaModelConfig.SetDependsOn(databaseConfigModel.ResourceReference()) + + resource.Test(t, resource.TestCase{ + ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories, + TerraformVersionChecks: []tfversion.TerraformVersionCheck{ + tfversion.RequireAbove(tfversion.Version1_5_0), + }, + CheckDestroy: resource.ComposeAggregateTestCheckFunc(), + Steps: []resource.TestStep{ + { + Config: config.FromModels(t, databaseConfigModel, schemaModelConfig), + }, + { + ConfigPlanChecks: resource.ConfigPlanChecks{ + PreApply: []plancheck.PlanCheck{ + plancheck.ExpectResourceAction("snowflake_database.test", plancheck.ResourceActionUpdate), + plancheck.ExpectResourceAction("snowflake_schema.test", plancheck.ResourceActionNoop), + }, + }, + Config: config.FromModels(t, databaseConfigModelWithNewId, schemaModelConfig), + ExpectError: regexp.MustCompile("does not exist or not authorized"), + }, + }, + }) +} + +func TestAcc_ShallowHierarchy_IsInConfig_RenamedInternally_WithDependsOn_AfterRenameSchemaReferencingNewDatabaseName(t *testing.T) { + _ = testenvs.GetOrSkipTest(t, testenvs.EnableObjectRenamingTest) + _ = testenvs.GetOrSkipTest(t, testenvs.EnableAcceptance) + acc.TestAccPreCheck(t) + + databaseId := acc.TestClient().Ids.RandomAccountObjectIdentifier() + newDatabaseId := acc.TestClient().Ids.RandomAccountObjectIdentifier() + schemaName := acc.TestClient().Ids.Alpha() + + databaseConfigModel := model.Database("test", databaseId.Name()) + databaseConfigModelWithNewId := model.Database("test", newDatabaseId.Name()) + + schemaModelConfig := model.Schema("test", databaseId.Name(), schemaName) + schemaModelConfig.SetDependsOn(databaseConfigModel.ResourceReference()) + schemaModelConfigWithNewDatabaseId := model.Schema("test", newDatabaseId.Name(), schemaName) + schemaModelConfigWithNewDatabaseId.SetDependsOn(databaseConfigModelWithNewId.ResourceReference()) + + resource.Test(t, resource.TestCase{ + ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories, + TerraformVersionChecks: []tfversion.TerraformVersionCheck{ + tfversion.RequireAbove(tfversion.Version1_5_0), + }, + CheckDestroy: acc.CheckDestroy(t, resources.Schema), + Steps: []resource.TestStep{ + { + Config: config.FromModels(t, databaseConfigModel, schemaModelConfig), + }, + { + ConfigPlanChecks: resource.ConfigPlanChecks{ + PreApply: []plancheck.PlanCheck{ + plancheck.ExpectResourceAction("snowflake_database.test", plancheck.ResourceActionUpdate), + plancheck.ExpectResourceAction("snowflake_schema.test", plancheck.ResourceActionDestroyBeforeCreate), + }, + }, + Config: config.FromModels(t, databaseConfigModelWithNewId, schemaModelConfigWithNewDatabaseId), + }, + }, + }) +} + +func TestAcc_ShallowHierarchy_IsInConfig_RenamedExternally_WithImplicitDependency_DatabaseHoldingTheOldNameInConfig(t *testing.T) { + _ = testenvs.GetOrSkipTest(t, testenvs.EnableObjectRenamingTest) + _ = testenvs.GetOrSkipTest(t, testenvs.EnableAcceptance) + acc.TestAccPreCheck(t) + + databaseId := acc.TestClient().Ids.RandomAccountObjectIdentifier() + newDatabaseId := acc.TestClient().Ids.RandomAccountObjectIdentifier() + schemaName := acc.TestClient().Ids.Alpha() + + databaseConfigModel := model.Database("test", databaseId.Name()) + + resource.Test(t, resource.TestCase{ + ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories, + TerraformVersionChecks: []tfversion.TerraformVersionCheck{ + tfversion.RequireAbove(tfversion.Version1_5_0), + }, + CheckDestroy: acc.CheckDestroy(t, resources.Schema), + Steps: []resource.TestStep{ + { + Config: config.FromModel(t, databaseConfigModel) + configSchemaWithDatabaseReference(databaseConfigModel.ResourceReference(), schemaName), + }, + { + PreConfig: func() { + acc.TestClient().Database.Alter(t, databaseId, &sdk.AlterDatabaseOptions{ + NewName: &newDatabaseId, + }) + t.Cleanup(acc.TestClient().Database.DropDatabaseFunc(t, newDatabaseId)) + }, + ConfigPlanChecks: resource.ConfigPlanChecks{ + PreApply: []plancheck.PlanCheck{ + plancheck.ExpectResourceAction("snowflake_database.test", plancheck.ResourceActionCreate), + plancheck.ExpectResourceAction("snowflake_schema.test", plancheck.ResourceActionCreate), + }, + }, + Config: config.FromModel(t, databaseConfigModel) + configSchemaWithDatabaseReference(databaseConfigModel.ResourceReference(), schemaName), + }, + }, + }) +} + +func TestAcc_ShallowHierarchy_IsInConfig_RenamedExternally_WithImplicitDependency_DatabaseHoldingTheNewNameInConfig(t *testing.T) { + _ = testenvs.GetOrSkipTest(t, testenvs.EnableObjectRenamingTest) + _ = testenvs.GetOrSkipTest(t, testenvs.EnableAcceptance) + acc.TestAccPreCheck(t) + + databaseId := acc.TestClient().Ids.RandomAccountObjectIdentifier() + newDatabaseId := acc.TestClient().Ids.RandomAccountObjectIdentifier() + schemaName := acc.TestClient().Ids.Alpha() + + databaseConfigModel := model.Database("test", databaseId.Name()) + databaseConfigModelWithNewId := model.Database("test", newDatabaseId.Name()) + + resource.Test(t, resource.TestCase{ + ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories, + TerraformVersionChecks: []tfversion.TerraformVersionCheck{ + tfversion.RequireAbove(tfversion.Version1_5_0), + }, + CheckDestroy: acc.CheckDestroy(t, resources.Schema), + Steps: []resource.TestStep{ + { + Config: config.FromModel(t, databaseConfigModel) + configSchemaWithDatabaseReference(databaseConfigModel.ResourceReference(), schemaName), + }, + { + PreConfig: func() { + acc.TestClient().Database.Alter(t, databaseId, &sdk.AlterDatabaseOptions{ + NewName: &newDatabaseId, + }) + t.Cleanup(acc.TestClient().Database.DropDatabaseFunc(t, newDatabaseId)) + }, + ConfigPlanChecks: resource.ConfigPlanChecks{ + PreApply: []plancheck.PlanCheck{ + plancheck.ExpectResourceAction("snowflake_database.test", plancheck.ResourceActionCreate), // Create is expected, because in refresh Read before apply the database is removing the unknown database from the state using d.SetId("") after failed ShowByID + plancheck.ExpectResourceAction("snowflake_schema.test", plancheck.ResourceActionCreate), // Create is expected, because in refresh Read before apply the schema is removing the unknown schema from the state using d.SetId("") after failed ShowByID + }, + }, + Config: config.FromModel(t, databaseConfigModelWithNewId) + configSchemaWithDatabaseReference(databaseConfigModelWithNewId.ResourceReference(), schemaName), + ExpectError: regexp.MustCompile(fmt.Sprintf(`Object '%s' already exists`, newDatabaseId.Name())), + }, + }, + }) +} + +func TestAcc_ShallowHierarchy_IsInConfig_RenamedExternally_WithoutDependency_AfterRenameDatabaseReferencingOldNameAndSchemaReferencingOldDatabaseName(t *testing.T) { + t.Skip("Test results are inconsistent because Terraform execution order is non-deterministic") + _ = testenvs.GetOrSkipTest(t, testenvs.EnableObjectRenamingTest) + _ = testenvs.GetOrSkipTest(t, testenvs.EnableAcceptance) + acc.TestAccPreCheck(t) + + databaseId := acc.TestClient().Ids.RandomAccountObjectIdentifier() + schemaName := acc.TestClient().Ids.Alpha() + + databaseConfigModel := model.Database("test", databaseId.Name()) + schemaModelConfig := model.Schema("test", databaseId.Name(), schemaName) + + resource.Test(t, resource.TestCase{ + ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories, + TerraformVersionChecks: []tfversion.TerraformVersionCheck{ + tfversion.RequireAbove(tfversion.Version1_5_0), + }, + CheckDestroy: acc.CheckDestroy(t, resources.Schema), + Steps: []resource.TestStep{ + { + Config: config.FromModels(t, databaseConfigModel, schemaModelConfig), + }, + { + // This step has inconsistent results, and it depends on the Terraform execution order which seems to be non-deterministic in this case + PreConfig: func() { + newDatabaseId := acc.TestClient().Ids.RandomAccountObjectIdentifier() + acc.TestClient().Database.Alter(t, databaseId, &sdk.AlterDatabaseOptions{NewName: &newDatabaseId}) + t.Cleanup(acc.TestClient().Database.DropDatabaseFunc(t, newDatabaseId)) + }, + ConfigPlanChecks: resource.ConfigPlanChecks{ + PreApply: []plancheck.PlanCheck{ + plancheck.ExpectResourceAction("snowflake_database.test", plancheck.ResourceActionCreate), + plancheck.ExpectResourceAction("snowflake_schema.test", plancheck.ResourceActionCreate), + }, + }, + Config: config.FromModels(t, databaseConfigModel, schemaModelConfig), + // ExpectError: regexp.MustCompile("does not exist or not authorized"), + }, + }, + }) +} + +// This test checks if the order of the configuration resources has any impact on the order of resource execution (it seems to have no impact). +func TestAcc_ShallowHierarchy_IsInConfig_RenamedExternally_WithoutDependency_AfterRenameDatabaseReferencingOldNameAndSchemaReferencingOldDatabaseName_ConfigOrderSwap(t *testing.T) { + t.Skip("Test results are inconsistent because Terraform execution order is non-deterministic") + // Although the above applies, it seems to be consistently failing on delete operation after the test (because the database is dropped before schema). + _ = testenvs.GetOrSkipTest(t, testenvs.EnableObjectRenamingTest) + _ = testenvs.GetOrSkipTest(t, testenvs.EnableAcceptance) + acc.TestAccPreCheck(t) + + databaseId := acc.TestClient().Ids.RandomAccountObjectIdentifier() + schemaName := acc.TestClient().Ids.Alpha() + + databaseConfigModel := model.Database("test", databaseId.Name()) + schemaModelConfig := model.Schema("test", databaseId.Name(), schemaName) + + resource.Test(t, resource.TestCase{ + ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories, + TerraformVersionChecks: []tfversion.TerraformVersionCheck{ + tfversion.RequireAbove(tfversion.Version1_5_0), + }, + CheckDestroy: acc.CheckDestroy(t, resources.Schema), + Steps: []resource.TestStep{ + { + Config: config.FromModels(t, schemaModelConfig, databaseConfigModel), + }, + { + PreConfig: func() { + newDatabaseId := acc.TestClient().Ids.RandomAccountObjectIdentifier() + acc.TestClient().Database.Alter(t, databaseId, &sdk.AlterDatabaseOptions{NewName: &newDatabaseId}) + t.Cleanup(acc.TestClient().Database.DropDatabaseFunc(t, newDatabaseId)) + }, + ConfigPlanChecks: resource.ConfigPlanChecks{ + PreApply: []plancheck.PlanCheck{ + plancheck.ExpectResourceAction("snowflake_database.test", plancheck.ResourceActionCreate), + plancheck.ExpectResourceAction("snowflake_schema.test", plancheck.ResourceActionCreate), + }, + }, + Config: config.FromModels(t, schemaModelConfig, databaseConfigModel), + ExpectError: regexp.MustCompile("does not exist or not authorized"), + }, + }, + }) +} + +func TestAcc_ShallowHierarchy_IsInConfig_RenamedExternally_WithoutDependency_AfterRenameDatabaseReferencingOldNameAndSchemaReferencingNewDatabaseName(t *testing.T) { + _ = testenvs.GetOrSkipTest(t, testenvs.EnableObjectRenamingTest) + _ = testenvs.GetOrSkipTest(t, testenvs.EnableAcceptance) + acc.TestAccPreCheck(t) + + databaseId := acc.TestClient().Ids.RandomAccountObjectIdentifier() + newDatabaseId := acc.TestClient().Ids.RandomAccountObjectIdentifier() + schemaName := acc.TestClient().Ids.Alpha() + + databaseConfigModel := model.Database("test", databaseId.Name()) + schemaModelConfig := model.Schema("test", databaseId.Name(), schemaName) + schemaModelConfigWithNewDatabaseId := model.Schema("test", newDatabaseId.Name(), schemaName) + + resource.Test(t, resource.TestCase{ + ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories, + TerraformVersionChecks: []tfversion.TerraformVersionCheck{ + tfversion.RequireAbove(tfversion.Version1_5_0), + }, + CheckDestroy: acc.CheckDestroy(t, resources.Schema), + Steps: []resource.TestStep{ + { + Config: config.FromModels(t, databaseConfigModel, schemaModelConfig), + }, + { + PreConfig: func() { + acc.TestClient().Database.Alter(t, databaseId, &sdk.AlterDatabaseOptions{NewName: &newDatabaseId}) + t.Cleanup(acc.TestClient().Database.DropDatabaseFunc(t, newDatabaseId)) + }, + ConfigPlanChecks: resource.ConfigPlanChecks{ + PreApply: []plancheck.PlanCheck{ + plancheck.ExpectResourceAction("snowflake_database.test", plancheck.ResourceActionCreate), + plancheck.ExpectResourceAction("snowflake_schema.test", plancheck.ResourceActionCreate), + }, + }, + Config: config.FromModels(t, databaseConfigModel, schemaModelConfigWithNewDatabaseId), + ExpectError: regexp.MustCompile("Failed to create schema"), // already exists (because we try to create a schema on the renamed database that already has the schema that was previously created by terraform and wasn't removed) + }, + }, + }) +} + +func TestAcc_ShallowHierarchy_IsInConfig_RenamedExternally_WithoutDependency_AfterRenameDatabaseReferencingNewNameAndSchemaReferencingOldDatabaseName(t *testing.T) { + _ = testenvs.GetOrSkipTest(t, testenvs.EnableObjectRenamingTest) + _ = testenvs.GetOrSkipTest(t, testenvs.EnableAcceptance) + acc.TestAccPreCheck(t) + + databaseId := acc.TestClient().Ids.RandomAccountObjectIdentifier() + newDatabaseId := acc.TestClient().Ids.RandomAccountObjectIdentifier() + schemaName := acc.TestClient().Ids.Alpha() + + databaseConfigModel := model.Database("test", databaseId.Name()) + databaseConfigModelWithNewId := model.Database("test", newDatabaseId.Name()) + schemaModelConfig := model.Schema("test", databaseId.Name(), schemaName) + + resource.Test(t, resource.TestCase{ + ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories, + TerraformVersionChecks: []tfversion.TerraformVersionCheck{ + tfversion.RequireAbove(tfversion.Version1_5_0), + }, + CheckDestroy: acc.CheckDestroy(t, resources.Schema), + Steps: []resource.TestStep{ + { + Config: config.FromModels(t, databaseConfigModel, schemaModelConfig), + }, + { + PreConfig: func() { + acc.TestClient().Database.Alter(t, databaseId, &sdk.AlterDatabaseOptions{NewName: &newDatabaseId}) + t.Cleanup(acc.TestClient().Database.DropDatabaseFunc(t, newDatabaseId)) + }, + ConfigPlanChecks: resource.ConfigPlanChecks{ + PreApply: []plancheck.PlanCheck{ + plancheck.ExpectResourceAction("snowflake_database.test", plancheck.ResourceActionCreate), + plancheck.ExpectResourceAction("snowflake_schema.test", plancheck.ResourceActionCreate), + }, + }, + Config: config.FromModels(t, databaseConfigModelWithNewId, schemaModelConfig), + ExpectError: regexp.MustCompile(fmt.Sprintf(`Object '%s' already exists`, newDatabaseId.Name())), + }, + }, + }) +} + +func TestAcc_ShallowHierarchy_IsInConfig_RenamedExternally_WithoutDependency_AfterRenameDatabaseReferencingNewNameAndSchemaReferencingNewDatabaseName(t *testing.T) { + _ = testenvs.GetOrSkipTest(t, testenvs.EnableObjectRenamingTest) + _ = testenvs.GetOrSkipTest(t, testenvs.EnableAcceptance) + acc.TestAccPreCheck(t) + + databaseId := acc.TestClient().Ids.RandomAccountObjectIdentifier() + newDatabaseId := acc.TestClient().Ids.RandomAccountObjectIdentifier() + schemaName := acc.TestClient().Ids.Alpha() + + databaseConfigModel := model.Database("test", databaseId.Name()) + databaseConfigModelWithNewId := model.Database("test", newDatabaseId.Name()) + schemaModelConfig := model.Schema("test", databaseId.Name(), schemaName) + schemaModelConfigWithNewDatabaseId := model.Schema("test", newDatabaseId.Name(), schemaName) + + resource.Test(t, resource.TestCase{ + ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories, + TerraformVersionChecks: []tfversion.TerraformVersionCheck{ + tfversion.RequireAbove(tfversion.Version1_5_0), + }, + CheckDestroy: acc.CheckDestroy(t, resources.Schema), + Steps: []resource.TestStep{ + { + Config: config.FromModels(t, databaseConfigModel, schemaModelConfig), + }, + { + PreConfig: func() { + acc.TestClient().Database.Alter(t, databaseId, &sdk.AlterDatabaseOptions{NewName: &newDatabaseId}) + t.Cleanup(acc.TestClient().Database.DropDatabaseFunc(t, newDatabaseId)) + }, + ConfigPlanChecks: resource.ConfigPlanChecks{ + PreApply: []plancheck.PlanCheck{ + plancheck.ExpectResourceAction("snowflake_database.test", plancheck.ResourceActionCreate), + plancheck.ExpectResourceAction("snowflake_schema.test", plancheck.ResourceActionCreate), + }, + }, + Config: config.FromModels(t, databaseConfigModelWithNewId, schemaModelConfigWithNewDatabaseId), + ExpectError: regexp.MustCompile(fmt.Sprintf(`Object '%s' already exists`, newDatabaseId.Name())), + }, + }, + }) +} + +func TestAcc_ShallowHierarchy_IsInConfig_RenamedExternally_WithDependsOn_AfterRenameDatabaseReferencingOldNameAndSchemaReferencingOldDatabaseName(t *testing.T) { + _ = testenvs.GetOrSkipTest(t, testenvs.EnableObjectRenamingTest) + _ = testenvs.GetOrSkipTest(t, testenvs.EnableAcceptance) + acc.TestAccPreCheck(t) + + databaseId := acc.TestClient().Ids.RandomAccountObjectIdentifier() + schemaName := acc.TestClient().Ids.Alpha() + + databaseConfigModel := model.Database("test", databaseId.Name()) + schemaModelConfig := model.Schema("test", databaseId.Name(), schemaName) + schemaModelConfig.SetDependsOn(databaseConfigModel.ResourceReference()) + + resource.Test(t, resource.TestCase{ + ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories, + TerraformVersionChecks: []tfversion.TerraformVersionCheck{ + tfversion.RequireAbove(tfversion.Version1_5_0), + }, + CheckDestroy: acc.CheckDestroy(t, resources.Schema), + Steps: []resource.TestStep{ + { + Config: config.FromModels(t, databaseConfigModel, schemaModelConfig), + }, + { + PreConfig: func() { + newDatabaseId := acc.TestClient().Ids.RandomAccountObjectIdentifier() + acc.TestClient().Database.Alter(t, databaseId, &sdk.AlterDatabaseOptions{NewName: &newDatabaseId}) + t.Cleanup(acc.TestClient().Database.DropDatabaseFunc(t, newDatabaseId)) + }, + ConfigPlanChecks: resource.ConfigPlanChecks{ + PreApply: []plancheck.PlanCheck{ + plancheck.ExpectResourceAction("snowflake_database.test", plancheck.ResourceActionCreate), + plancheck.ExpectResourceAction("snowflake_schema.test", plancheck.ResourceActionCreate), + }, + }, + Config: config.FromModels(t, databaseConfigModel, schemaModelConfig), + }, + }, + }) +} + +func TestAcc_ShallowHierarchy_IsInConfig_RenamedExternally_WithDependsOn_AfterRenameDatabaseReferencingOldNameAndSchemaReferencingNewDatabaseName(t *testing.T) { + _ = testenvs.GetOrSkipTest(t, testenvs.EnableObjectRenamingTest) + _ = testenvs.GetOrSkipTest(t, testenvs.EnableAcceptance) + acc.TestAccPreCheck(t) + + databaseId := acc.TestClient().Ids.RandomAccountObjectIdentifier() + newDatabaseId := acc.TestClient().Ids.RandomAccountObjectIdentifier() + schemaName := acc.TestClient().Ids.Alpha() + + databaseConfigModel := model.Database("test", databaseId.Name()) + schemaModelConfig := model.Schema("test", databaseId.Name(), schemaName) + schemaModelConfig.SetDependsOn(databaseConfigModel.ResourceReference()) + schemaModelConfigWithNewDatabaseId := model.Schema("test", newDatabaseId.Name(), schemaName) + schemaModelConfigWithNewDatabaseId.SetDependsOn(databaseConfigModel.ResourceReference()) + + resource.Test(t, resource.TestCase{ + ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories, + TerraformVersionChecks: []tfversion.TerraformVersionCheck{ + tfversion.RequireAbove(tfversion.Version1_5_0), + }, + CheckDestroy: acc.CheckDestroy(t, resources.Schema), + Steps: []resource.TestStep{ + { + Config: config.FromModels(t, databaseConfigModel, schemaModelConfig), + }, + { + PreConfig: func() { + acc.TestClient().Database.Alter(t, databaseId, &sdk.AlterDatabaseOptions{NewName: &newDatabaseId}) + t.Cleanup(acc.TestClient().Database.DropDatabaseFunc(t, newDatabaseId)) + }, + ConfigPlanChecks: resource.ConfigPlanChecks{ + PreApply: []plancheck.PlanCheck{ + plancheck.ExpectResourceAction("snowflake_database.test", plancheck.ResourceActionCreate), + plancheck.ExpectResourceAction("snowflake_schema.test", plancheck.ResourceActionCreate), + }, + }, + Config: config.FromModels(t, databaseConfigModel, schemaModelConfigWithNewDatabaseId), + ExpectError: regexp.MustCompile("Failed to create schema"), // already exists + }, + }, + }) +} + +func TestAcc_ShallowHierarchy_IsInConfig_RenamedExternally_WithDependsOn_AfterRenameDatabaseReferencingNewNameAndSchemaReferencingOldDatabaseName(t *testing.T) { + _ = testenvs.GetOrSkipTest(t, testenvs.EnableObjectRenamingTest) + _ = testenvs.GetOrSkipTest(t, testenvs.EnableAcceptance) + acc.TestAccPreCheck(t) + + databaseId := acc.TestClient().Ids.RandomAccountObjectIdentifier() + newDatabaseId := acc.TestClient().Ids.RandomAccountObjectIdentifier() + schemaName := acc.TestClient().Ids.Alpha() + + databaseConfigModel := model.Database("test", databaseId.Name()) + databaseConfigModelWithNewId := model.Database("test", newDatabaseId.Name()) + schemaModelConfig := model.Schema("test", databaseId.Name(), schemaName) + schemaModelConfig.SetDependsOn(databaseConfigModel.ResourceReference()) + + resource.Test(t, resource.TestCase{ + ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories, + TerraformVersionChecks: []tfversion.TerraformVersionCheck{ + tfversion.RequireAbove(tfversion.Version1_5_0), + }, + CheckDestroy: acc.CheckDestroy(t, resources.Schema), + Steps: []resource.TestStep{ + { + Config: config.FromModels(t, databaseConfigModel, schemaModelConfig), + }, + { + PreConfig: func() { + acc.TestClient().Database.Alter(t, databaseId, &sdk.AlterDatabaseOptions{NewName: &newDatabaseId}) + t.Cleanup(acc.TestClient().Database.DropDatabaseFunc(t, newDatabaseId)) + }, + ConfigPlanChecks: resource.ConfigPlanChecks{ + PreApply: []plancheck.PlanCheck{ + plancheck.ExpectResourceAction("snowflake_database.test", plancheck.ResourceActionCreate), + plancheck.ExpectResourceAction("snowflake_schema.test", plancheck.ResourceActionCreate), + }, + }, + Config: config.FromModels(t, databaseConfigModelWithNewId, schemaModelConfig), + ExpectError: regexp.MustCompile(fmt.Sprintf(`Object '%s' already exists`, newDatabaseId.Name())), + }, + }, + }) +} + +func TestAcc_ShallowHierarchy_IsInConfig_RenamedExternally_WithDependsOn_AfterRenameDatabaseReferencingNewNameAndSchemaReferencingNewDatabaseName(t *testing.T) { + _ = testenvs.GetOrSkipTest(t, testenvs.EnableObjectRenamingTest) + _ = testenvs.GetOrSkipTest(t, testenvs.EnableAcceptance) + acc.TestAccPreCheck(t) + + databaseId := acc.TestClient().Ids.RandomAccountObjectIdentifier() + newDatabaseId := acc.TestClient().Ids.RandomAccountObjectIdentifier() + schemaName := acc.TestClient().Ids.Alpha() + + databaseConfigModel := model.Database("test", databaseId.Name()) + databaseConfigModelWithNewId := model.Database("test", newDatabaseId.Name()) + schemaModelConfig := model.Schema("test", databaseId.Name(), schemaName) + schemaModelConfig.SetDependsOn(databaseConfigModel.ResourceReference()) + schemaModelConfigWithNewDatabaseId := model.Schema("test", newDatabaseId.Name(), schemaName) + schemaModelConfigWithNewDatabaseId.SetDependsOn(databaseConfigModelWithNewId.ResourceReference()) + + resource.Test(t, resource.TestCase{ + ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories, + TerraformVersionChecks: []tfversion.TerraformVersionCheck{ + tfversion.RequireAbove(tfversion.Version1_5_0), + }, + CheckDestroy: acc.CheckDestroy(t, resources.Schema), + Steps: []resource.TestStep{ + { + Config: config.FromModels(t, databaseConfigModel, schemaModelConfig), + }, + { + PreConfig: func() { + acc.TestClient().Database.Alter(t, databaseId, &sdk.AlterDatabaseOptions{NewName: &newDatabaseId}) + t.Cleanup(acc.TestClient().Database.DropDatabaseFunc(t, newDatabaseId)) + }, + ConfigPlanChecks: resource.ConfigPlanChecks{ + PreApply: []plancheck.PlanCheck{ + plancheck.ExpectResourceAction("snowflake_database.test", plancheck.ResourceActionCreate), + plancheck.ExpectResourceAction("snowflake_schema.test", plancheck.ResourceActionCreate), + }, + }, + Config: config.FromModels(t, databaseConfigModelWithNewId, schemaModelConfigWithNewDatabaseId), + ExpectError: regexp.MustCompile(fmt.Sprintf(`Object '%s' already exists`, newDatabaseId.Name())), + }, + }, + }) +} + +func TestAcc_ShallowHierarchy_IsNotInConfig_RenamedExternally_ReferencingOldName(t *testing.T) { + _ = testenvs.GetOrSkipTest(t, testenvs.EnableObjectRenamingTest) + _ = testenvs.GetOrSkipTest(t, testenvs.EnableAcceptance) + acc.TestAccPreCheck(t) + + databaseId := acc.TestClient().Ids.RandomAccountObjectIdentifier() + newDatabaseId := acc.TestClient().Ids.RandomAccountObjectIdentifier() + schemaName := acc.TestClient().Ids.Alpha() + schemaModelConfig := model.Schema("test", databaseId.Name(), schemaName) + + resource.Test(t, resource.TestCase{ + ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories, + TerraformVersionChecks: []tfversion.TerraformVersionCheck{ + tfversion.RequireAbove(tfversion.Version1_5_0), + }, + CheckDestroy: acc.CheckDestroy(t, resources.Schema), + Steps: []resource.TestStep{ + { + PreConfig: func() { + _, databaseCleanup := acc.TestClient().Database.CreateDatabaseWithIdentifier(t, databaseId) + t.Cleanup(databaseCleanup) + }, + Config: config.FromModel(t, schemaModelConfig), + }, + { + PreConfig: func() { + acc.TestClient().Database.Alter(t, databaseId, &sdk.AlterDatabaseOptions{NewName: &newDatabaseId}) + t.Cleanup(acc.TestClient().Database.DropDatabaseFunc(t, newDatabaseId)) + }, + ConfigPlanChecks: resource.ConfigPlanChecks{ + PreApply: []plancheck.PlanCheck{ + plancheck.ExpectResourceAction("snowflake_schema.test", plancheck.ResourceActionCreate), + }, + }, + Config: config.FromModel(t, schemaModelConfig), + ExpectError: regexp.MustCompile("object does not exist or not authorized"), + }, + }, + }) +} + +func TestAcc_ShallowHierarchy_IsNotInConfig_RenamedExternally_ReferencingNewName(t *testing.T) { + _ = testenvs.GetOrSkipTest(t, testenvs.EnableObjectRenamingTest) + _ = testenvs.GetOrSkipTest(t, testenvs.EnableAcceptance) + acc.TestAccPreCheck(t) + + databaseId := acc.TestClient().Ids.RandomAccountObjectIdentifier() + newDatabaseId := acc.TestClient().Ids.RandomAccountObjectIdentifier() + schemaName := acc.TestClient().Ids.Alpha() + schemaModelConfig := model.Schema("test", databaseId.Name(), schemaName) + schemaModelConfigWithNewDatabaseId := model.Schema("test", newDatabaseId.Name(), schemaName) + + resource.Test(t, resource.TestCase{ + ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories, + TerraformVersionChecks: []tfversion.TerraformVersionCheck{ + tfversion.RequireAbove(tfversion.Version1_5_0), + }, + CheckDestroy: acc.CheckDestroy(t, resources.Schema), + Steps: []resource.TestStep{ + { + PreConfig: func() { + _, databaseCleanup := acc.TestClient().Database.CreateDatabaseWithIdentifier(t, databaseId) + t.Cleanup(databaseCleanup) + }, + Config: config.FromModel(t, schemaModelConfig), + }, + { + PreConfig: func() { + acc.TestClient().Database.Alter(t, databaseId, &sdk.AlterDatabaseOptions{NewName: &newDatabaseId}) + t.Cleanup(acc.TestClient().Database.DropDatabaseFunc(t, newDatabaseId)) + }, + ConfigPlanChecks: resource.ConfigPlanChecks{ + PreApply: []plancheck.PlanCheck{ + plancheck.ExpectResourceAction("snowflake_schema.test", plancheck.ResourceActionCreate), + }, + }, + Config: config.FromModel(t, schemaModelConfigWithNewDatabaseId), + ExpectError: regexp.MustCompile("Failed to create schema"), // already exists + }, + }, + }) +} + +func configSchemaWithDatabaseReference(databaseReference string, schemaName string) string { + return fmt.Sprintf(` +resource "snowflake_schema" "test" { + database = %[1]s.name + name = "%[2]s" +} +`, databaseReference, schemaName) +} + +func TestAcc_DeepHierarchy_AreInConfig_DatabaseRenamedInternally(t *testing.T) { + _ = testenvs.GetOrSkipTest(t, testenvs.EnableObjectRenamingTest) + _ = testenvs.GetOrSkipTest(t, testenvs.EnableAcceptance) + acc.TestAccPreCheck(t) + + testCases := []struct { + DatabaseDependency DependencyType + SchemaDependency DependencyType + DatabaseInSchemaDependency DependencyType + ExpectedFirstStepError *regexp.Regexp + }{ + {DatabaseDependency: ImplicitDependency, SchemaDependency: NoDependency, DatabaseInSchemaDependency: ImplicitDependency, ExpectedFirstStepError: regexp.MustCompile("error creating table")}, // tries to create table before schema + {DatabaseDependency: ImplicitDependency, SchemaDependency: ImplicitDependency, DatabaseInSchemaDependency: ImplicitDependency}, + {DatabaseDependency: ImplicitDependency, SchemaDependency: DependsOnDependency, DatabaseInSchemaDependency: ImplicitDependency}, + + {DatabaseDependency: ImplicitDependency, SchemaDependency: NoDependency, DatabaseInSchemaDependency: DependsOnDependency, ExpectedFirstStepError: regexp.MustCompile("error creating table")}, // tries to create table before schema + {DatabaseDependency: ImplicitDependency, SchemaDependency: ImplicitDependency, DatabaseInSchemaDependency: DependsOnDependency}, + {DatabaseDependency: ImplicitDependency, SchemaDependency: DependsOnDependency, DatabaseInSchemaDependency: DependsOnDependency}, + + // {DatabaseDependency: ImplicitDependency, SchemaDependency: NoDependency, DatabaseInSchemaDependency: NoDependency}, // fails after incorrect execution order (tries to drop schema after database was dropped); cannot assert + // {DatabaseDependency: ImplicitDependency, SchemaDependency: ImplicitDependency, DatabaseInSchemaDependency: NoDependency}, // tries to drop schema after database name was changed; cannot assert + // {DatabaseDependency: ImplicitDependency, SchemaDependency: DependsOnDependency, DatabaseInSchemaDependency: NoDependency}, // tries to drop schema after database name was changed; cannot assert + + {DatabaseDependency: DependsOnDependency, SchemaDependency: NoDependency, DatabaseInSchemaDependency: ImplicitDependency, ExpectedFirstStepError: regexp.MustCompile("error creating table")}, // tries to create table before schema + {DatabaseDependency: DependsOnDependency, SchemaDependency: ImplicitDependency, DatabaseInSchemaDependency: ImplicitDependency}, + {DatabaseDependency: DependsOnDependency, SchemaDependency: DependsOnDependency, DatabaseInSchemaDependency: ImplicitDependency}, + + {DatabaseDependency: NoDependency, SchemaDependency: NoDependency, DatabaseInSchemaDependency: ImplicitDependency, ExpectedFirstStepError: regexp.MustCompile("error creating table")}, // tries to create table before schema + {DatabaseDependency: NoDependency, SchemaDependency: ImplicitDependency, DatabaseInSchemaDependency: ImplicitDependency}, + {DatabaseDependency: NoDependency, SchemaDependency: DependsOnDependency, DatabaseInSchemaDependency: ImplicitDependency}, + } + + for _, testCase := range testCases { + t.Run(fmt.Sprintf("TestAcc_ database dependency: %s, schema dependency: %s, database in schema dependency: %s", testCase.DatabaseDependency, testCase.SchemaDependency, testCase.DatabaseInSchemaDependency), func(t *testing.T) { + databaseId := acc.TestClient().Ids.RandomAccountObjectIdentifier() + newDatabaseId := acc.TestClient().Ids.RandomAccountObjectIdentifier() + schemaName := acc.TestClient().Ids.Alpha() + tableName := acc.TestClient().Ids.Alpha() + + databaseConfigModel := model.Database("test", databaseId.Name()) + databaseConfigModelWithNewId := model.Database("test", newDatabaseId.Name()) + + testSteps := []resource.TestStep{ + { + Config: config.FromModel(t, databaseConfigModel) + + configSchemaWithReferences(t, databaseConfigModel.ResourceReference(), testCase.DatabaseInSchemaDependency, databaseId.Name(), schemaName) + + configTableWithReferences(t, databaseConfigModel.ResourceReference(), testCase.DatabaseDependency, "snowflake_schema.test", testCase.SchemaDependency, databaseId.Name(), schemaName, tableName), + ExpectError: testCase.ExpectedFirstStepError, + }, + } + + if testCase.ExpectedFirstStepError == nil { + testSteps = append(testSteps, + resource.TestStep{ + ConfigPlanChecks: resource.ConfigPlanChecks{ + PreApply: []plancheck.PlanCheck{ + plancheck.ExpectResourceAction("snowflake_database.test", plancheck.ResourceActionUpdate), + plancheck.ExpectResourceAction("snowflake_schema.test", plancheck.ResourceActionDestroyBeforeCreate), + plancheck.ExpectResourceAction("snowflake_table.test", plancheck.ResourceActionDestroyBeforeCreate), + }, + }, + Config: config.FromModel(t, databaseConfigModelWithNewId) + + configSchemaWithReferences(t, databaseConfigModelWithNewId.ResourceReference(), testCase.DatabaseInSchemaDependency, newDatabaseId.Name(), schemaName) + + configTableWithReferences(t, databaseConfigModelWithNewId.ResourceReference(), testCase.DatabaseDependency, "snowflake_schema.test", testCase.SchemaDependency, newDatabaseId.Name(), schemaName, tableName), + }, + ) + } + + resource.Test(t, resource.TestCase{ + ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories, + TerraformVersionChecks: []tfversion.TerraformVersionCheck{ + tfversion.RequireAbove(tfversion.Version1_5_0), + }, + CheckDestroy: acc.CheckDestroy(t, resources.Table), + Steps: testSteps, + }) + }) + } +} + +func TestAcc_DeepHierarchy_AreInConfig_SchemaRenamedInternally(t *testing.T) { + _ = testenvs.GetOrSkipTest(t, testenvs.EnableObjectRenamingTest) + _ = testenvs.GetOrSkipTest(t, testenvs.EnableAcceptance) + acc.TestAccPreCheck(t) + + testCases := []struct { + DatabaseDependency DependencyType + SchemaDependency DependencyType + ExpectedFirstStepError *regexp.Regexp + }{ + {DatabaseDependency: ImplicitDependency, SchemaDependency: NoDependency, ExpectedFirstStepError: regexp.MustCompile("error creating table")}, // tries to create table before schema + {DatabaseDependency: ImplicitDependency, SchemaDependency: ImplicitDependency}, + {DatabaseDependency: ImplicitDependency, SchemaDependency: DependsOnDependency}, + + {DatabaseDependency: DependsOnDependency, SchemaDependency: NoDependency, ExpectedFirstStepError: regexp.MustCompile("error creating table")}, // tries to create table before schema + {DatabaseDependency: DependsOnDependency, SchemaDependency: ImplicitDependency}, + {DatabaseDependency: DependsOnDependency, SchemaDependency: DependsOnDependency}, + + {DatabaseDependency: NoDependency, SchemaDependency: NoDependency, ExpectedFirstStepError: regexp.MustCompile("error creating table")}, // tries to create table before schema + {DatabaseDependency: NoDependency, SchemaDependency: ImplicitDependency}, + {DatabaseDependency: NoDependency, SchemaDependency: DependsOnDependency}, + } + + for _, testCase := range testCases { + t.Run(fmt.Sprintf("TestAcc_ database dependency: %s, schema dependency: %s", testCase.DatabaseDependency, testCase.SchemaDependency), func(t *testing.T) { + databaseId := acc.TestClient().Ids.RandomAccountObjectIdentifier() + schemaName := acc.TestClient().Ids.Alpha() + newSchemaName := acc.TestClient().Ids.Alpha() + tableName := acc.TestClient().Ids.Alpha() + + databaseConfigModel := model.Database("test", databaseId.Name()) + + testSteps := []resource.TestStep{ + { + Config: config.FromModel(t, databaseConfigModel) + + configSchemaWithReferences(t, databaseConfigModel.ResourceReference(), ImplicitDependency, databaseId.Name(), schemaName) + + configTableWithReferences(t, databaseConfigModel.ResourceReference(), testCase.DatabaseDependency, "snowflake_schema.test", testCase.SchemaDependency, databaseId.Name(), schemaName, tableName), + ExpectError: testCase.ExpectedFirstStepError, + }, + } + + if testCase.ExpectedFirstStepError == nil { + testSteps = append(testSteps, + resource.TestStep{ + ConfigPlanChecks: resource.ConfigPlanChecks{ + PreApply: []plancheck.PlanCheck{ + plancheck.ExpectResourceAction("snowflake_database.test", plancheck.ResourceActionNoop), + plancheck.ExpectResourceAction("snowflake_schema.test", plancheck.ResourceActionUpdate), + plancheck.ExpectResourceAction("snowflake_table.test", plancheck.ResourceActionDestroyBeforeCreate), + }, + }, + Config: config.FromModel(t, databaseConfigModel) + + configSchemaWithReferences(t, databaseConfigModel.ResourceReference(), ImplicitDependency, databaseId.Name(), newSchemaName) + + configTableWithReferences(t, databaseConfigModel.ResourceReference(), testCase.DatabaseDependency, "snowflake_schema.test", testCase.SchemaDependency, databaseId.Name(), newSchemaName, tableName), + }, + ) + } + + resource.Test(t, resource.TestCase{ + ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories, + TerraformVersionChecks: []tfversion.TerraformVersionCheck{ + tfversion.RequireAbove(tfversion.Version1_5_0), + }, + CheckDestroy: acc.CheckDestroy(t, resources.Table), + Steps: testSteps, + }) + }) + } +} + +func TestAcc_DeepHierarchy_AreInConfig_DatabaseRenamedExternally(t *testing.T) { + _ = testenvs.GetOrSkipTest(t, testenvs.EnableObjectRenamingTest) + _ = testenvs.GetOrSkipTest(t, testenvs.EnableAcceptance) + acc.TestAccPreCheck(t) + + testCases := []struct { + DatabaseDependency DependencyType + SchemaDependency DependencyType + DatabaseInSchemaDependency DependencyType + ExpectedFirstStepError *regexp.Regexp + ExpectedSecondStepError *regexp.Regexp + }{ + {DatabaseDependency: ImplicitDependency, SchemaDependency: NoDependency, DatabaseInSchemaDependency: ImplicitDependency, ExpectedFirstStepError: regexp.MustCompile("error creating table")}, // tries to create table before schema + {DatabaseDependency: ImplicitDependency, SchemaDependency: ImplicitDependency, DatabaseInSchemaDependency: ImplicitDependency, ExpectedSecondStepError: regexp.MustCompile("already exists")}, // tries to create a database when it's already there + {DatabaseDependency: ImplicitDependency, SchemaDependency: DependsOnDependency, DatabaseInSchemaDependency: ImplicitDependency, ExpectedSecondStepError: regexp.MustCompile("already exists")}, // tries to create a database when it's already there + + {DatabaseDependency: DependsOnDependency, SchemaDependency: NoDependency, DatabaseInSchemaDependency: ImplicitDependency, ExpectedFirstStepError: regexp.MustCompile("error creating table")}, // tries to create table before schema + {DatabaseDependency: DependsOnDependency, SchemaDependency: ImplicitDependency, DatabaseInSchemaDependency: ImplicitDependency, ExpectedSecondStepError: regexp.MustCompile("already exists")}, // tries to create a database when it's already there + {DatabaseDependency: DependsOnDependency, SchemaDependency: DependsOnDependency, DatabaseInSchemaDependency: ImplicitDependency, ExpectedSecondStepError: regexp.MustCompile("already exists")}, // tries to create a database when it's already there + + {DatabaseDependency: NoDependency, SchemaDependency: NoDependency, DatabaseInSchemaDependency: ImplicitDependency, ExpectedFirstStepError: regexp.MustCompile("error creating table")}, // tries to create table before schema + {DatabaseDependency: NoDependency, SchemaDependency: ImplicitDependency, DatabaseInSchemaDependency: ImplicitDependency, ExpectedSecondStepError: regexp.MustCompile("already exists")}, // tries to create a database when it's already there + {DatabaseDependency: NoDependency, SchemaDependency: DependsOnDependency, DatabaseInSchemaDependency: ImplicitDependency, ExpectedSecondStepError: regexp.MustCompile("already exists")}, // tries to create a database when it's already there + } + + for _, testCase := range testCases { + t.Run(fmt.Sprintf("TestAcc_ database dependency: %s, schema dependency: %s, database in schema dependency: %s", testCase.DatabaseDependency, testCase.SchemaDependency, testCase.DatabaseInSchemaDependency), func(t *testing.T) { + databaseId := acc.TestClient().Ids.RandomAccountObjectIdentifier() + newDatabaseId := acc.TestClient().Ids.RandomAccountObjectIdentifier() + schemaName := acc.TestClient().Ids.Alpha() + tableName := acc.TestClient().Ids.Alpha() + + databaseConfigModel := model.Database("test", databaseId.Name()) + databaseConfigModelWithNewId := model.Database("test", newDatabaseId.Name()) + + testSteps := []resource.TestStep{ + { + Config: config.FromModel(t, databaseConfigModel) + + configSchemaWithReferences(t, databaseConfigModel.ResourceReference(), testCase.DatabaseInSchemaDependency, databaseId.Name(), schemaName) + + configTableWithReferences(t, databaseConfigModel.ResourceReference(), testCase.DatabaseDependency, "snowflake_schema.test", testCase.SchemaDependency, databaseId.Name(), schemaName, tableName), + ExpectError: testCase.ExpectedFirstStepError, + }, + } + + if testCase.ExpectedFirstStepError == nil { + testSteps = append(testSteps, resource.TestStep{ + PreConfig: func() { + acc.TestClient().Database.Alter(t, databaseId, &sdk.AlterDatabaseOptions{ + NewName: &newDatabaseId, + }) + }, + Config: config.FromModel(t, databaseConfigModelWithNewId) + + configSchemaWithReferences(t, databaseConfigModelWithNewId.ResourceReference(), testCase.DatabaseInSchemaDependency, newDatabaseId.Name(), schemaName) + + configTableWithReferences(t, databaseConfigModelWithNewId.ResourceReference(), testCase.DatabaseDependency, "snowflake_schema.test", testCase.SchemaDependency, newDatabaseId.Name(), schemaName, tableName), + ExpectError: testCase.ExpectedSecondStepError, + }, + ) + } + + resource.Test(t, resource.TestCase{ + ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories, + TerraformVersionChecks: []tfversion.TerraformVersionCheck{ + tfversion.RequireAbove(tfversion.Version1_5_0), + }, + CheckDestroy: acc.CheckDestroy(t, resources.Table), + Steps: testSteps, + }) + }) + } +} + +func TestAcc_DeepHierarchy_AreInConfig_SchemaRenamedExternally(t *testing.T) { + _ = testenvs.GetOrSkipTest(t, testenvs.EnableObjectRenamingTest) + _ = testenvs.GetOrSkipTest(t, testenvs.EnableAcceptance) + acc.TestAccPreCheck(t) + + testCases := []struct { + DatabaseDependency DependencyType + SchemaDependency DependencyType + DatabaseInSchemaDependency DependencyType + ExpectedFirstStepError *regexp.Regexp + ExpectedSecondStepError *regexp.Regexp + }{ + {DatabaseDependency: ImplicitDependency, SchemaDependency: NoDependency, DatabaseInSchemaDependency: ImplicitDependency, ExpectedFirstStepError: regexp.MustCompile("error creating table")}, // tries to create table before schema + {DatabaseDependency: ImplicitDependency, SchemaDependency: ImplicitDependency, DatabaseInSchemaDependency: ImplicitDependency, ExpectedSecondStepError: regexp.MustCompile("already exists")}, // tries to create a database when it's already there + {DatabaseDependency: ImplicitDependency, SchemaDependency: DependsOnDependency, DatabaseInSchemaDependency: ImplicitDependency, ExpectedSecondStepError: regexp.MustCompile("already exists")}, // tries to create a database when it's already there + + {DatabaseDependency: DependsOnDependency, SchemaDependency: NoDependency, DatabaseInSchemaDependency: ImplicitDependency, ExpectedFirstStepError: regexp.MustCompile("error creating table")}, // tries to create table before schema + {DatabaseDependency: DependsOnDependency, SchemaDependency: ImplicitDependency, DatabaseInSchemaDependency: ImplicitDependency, ExpectedSecondStepError: regexp.MustCompile("already exists")}, // tries to create a database when it's already there + {DatabaseDependency: DependsOnDependency, SchemaDependency: DependsOnDependency, DatabaseInSchemaDependency: ImplicitDependency, ExpectedSecondStepError: regexp.MustCompile("already exists")}, // tries to create a database when it's already there + + {DatabaseDependency: NoDependency, SchemaDependency: NoDependency, DatabaseInSchemaDependency: ImplicitDependency, ExpectedFirstStepError: regexp.MustCompile("error creating table")}, // tries to create table before schema + {DatabaseDependency: NoDependency, SchemaDependency: ImplicitDependency, DatabaseInSchemaDependency: ImplicitDependency, ExpectedSecondStepError: regexp.MustCompile("already exists")}, // tries to create a database when it's already there + {DatabaseDependency: NoDependency, SchemaDependency: DependsOnDependency, DatabaseInSchemaDependency: ImplicitDependency, ExpectedSecondStepError: regexp.MustCompile("already exists")}, // tries to create a database when it's already there + } + + for _, testCase := range testCases { + t.Run(fmt.Sprintf("TestAcc_ database dependency: %s, schema dependency: %s, database in schema dependency: %s", testCase.DatabaseDependency, testCase.SchemaDependency, testCase.DatabaseInSchemaDependency), func(t *testing.T) { + databaseId := acc.TestClient().Ids.RandomAccountObjectIdentifier() + schemaId := acc.TestClient().Ids.RandomDatabaseObjectIdentifierInDatabase(databaseId) + newSchemaId := acc.TestClient().Ids.RandomDatabaseObjectIdentifierInDatabase(databaseId) + tableName := acc.TestClient().Ids.Alpha() + + databaseConfigModel := model.Database("test", databaseId.Name()) + + testSteps := []resource.TestStep{ + { + Config: config.FromModel(t, databaseConfigModel) + + configSchemaWithReferences(t, databaseConfigModel.ResourceReference(), testCase.DatabaseInSchemaDependency, databaseId.Name(), schemaId.Name()) + + configTableWithReferences(t, databaseConfigModel.ResourceReference(), testCase.DatabaseDependency, "snowflake_schema.test", testCase.SchemaDependency, databaseId.Name(), schemaId.Name(), tableName), + ExpectError: testCase.ExpectedFirstStepError, + }, + } + + if testCase.ExpectedFirstStepError == nil { + testSteps = append(testSteps, resource.TestStep{ + PreConfig: func() { + acc.TestClient().Schema.Alter(t, schemaId, &sdk.AlterSchemaOptions{ + NewName: &newSchemaId, + }) + }, + Config: config.FromModel(t, databaseConfigModel) + + configSchemaWithReferences(t, databaseConfigModel.ResourceReference(), testCase.DatabaseInSchemaDependency, databaseId.Name(), newSchemaId.Name()) + + configTableWithReferences(t, databaseConfigModel.ResourceReference(), testCase.DatabaseDependency, "snowflake_schema.test", testCase.SchemaDependency, databaseId.Name(), newSchemaId.Name(), tableName), + ExpectError: testCase.ExpectedSecondStepError, + }, + ) + } + + resource.Test(t, resource.TestCase{ + ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories, + TerraformVersionChecks: []tfversion.TerraformVersionCheck{ + tfversion.RequireAbove(tfversion.Version1_5_0), + }, + CheckDestroy: acc.CheckDestroy(t, resources.Table), + Steps: testSteps, + }) + }) + } +} + +func TestAcc_DeepHierarchy_AreNotInConfig_DatabaseRenamedExternally(t *testing.T) { + _ = testenvs.GetOrSkipTest(t, testenvs.EnableObjectRenamingTest) + _ = testenvs.GetOrSkipTest(t, testenvs.EnableAcceptance) + acc.TestAccPreCheck(t) + + testCases := []struct { + UseNewDatabaseNameAfterRename bool + ExpectedSecondStepError *regexp.Regexp + }{ + {UseNewDatabaseNameAfterRename: true, ExpectedSecondStepError: regexp.MustCompile("already exists")}, + {UseNewDatabaseNameAfterRename: false, ExpectedSecondStepError: regexp.MustCompile("object does not exist or not authorized")}, + } + + for _, testCase := range testCases { + t.Run(fmt.Sprintf("TestAcc_ use new database after rename: %t", testCase.UseNewDatabaseNameAfterRename), func(t *testing.T) { + newDatabaseId := acc.TestClient().Ids.RandomAccountObjectIdentifier() + tableName := acc.TestClient().Ids.Alpha() + + database, databaseCleanup := acc.TestClient().Database.CreateDatabase(t) + t.Cleanup(databaseCleanup) + + // not cleaning up, because the schema will be dropped with the database anyway + schema, _ := acc.TestClient().Schema.CreateSchemaInDatabase(t, database.ID()) + + var secondStepDatabaseName string + if testCase.UseNewDatabaseNameAfterRename { + secondStepDatabaseName = newDatabaseId.Name() + } else { + secondStepDatabaseName = database.ID().Name() + } + + resource.Test(t, resource.TestCase{ + ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories, + TerraformVersionChecks: []tfversion.TerraformVersionCheck{ + tfversion.RequireAbove(tfversion.Version1_5_0), + }, + CheckDestroy: acc.CheckDestroy(t, resources.Table), + Steps: []resource.TestStep{ + { + Config: configTableWithReferences(t, "", NoDependency, "", NoDependency, database.ID().Name(), schema.ID().Name(), tableName), + }, + { + PreConfig: func() { + acc.TestClient().Database.Alter(t, database.ID(), &sdk.AlterDatabaseOptions{ + NewName: &newDatabaseId, + }) + t.Cleanup(acc.TestClient().Database.DropDatabaseFunc(t, newDatabaseId)) + }, + Config: configTableWithReferences(t, "", NoDependency, "", NoDependency, secondStepDatabaseName, schema.ID().Name(), tableName), + ExpectError: testCase.ExpectedSecondStepError, + }, + }, + }) + }) + } +} + +func TestAcc_DeepHierarchy_AreNotInConfig_SchemaRenamedExternally(t *testing.T) { + _ = testenvs.GetOrSkipTest(t, testenvs.EnableObjectRenamingTest) + _ = testenvs.GetOrSkipTest(t, testenvs.EnableAcceptance) + acc.TestAccPreCheck(t) + + testCases := []struct { + UseNewSchemaNameAfterRename bool + ExpectedSecondStepError *regexp.Regexp + }{ + {UseNewSchemaNameAfterRename: true, ExpectedSecondStepError: regexp.MustCompile("already exists")}, + {UseNewSchemaNameAfterRename: false, ExpectedSecondStepError: regexp.MustCompile("object does not exist or not authorized")}, + } + + for _, testCase := range testCases { + t.Run(fmt.Sprintf("TestAcc_ use new database after rename: %t", testCase.UseNewSchemaNameAfterRename), func(t *testing.T) { + database, databaseCleanup := acc.TestClient().Database.CreateDatabase(t) + t.Cleanup(databaseCleanup) + + newSchemaId := acc.TestClient().Ids.RandomDatabaseObjectIdentifierInDatabase(database.ID()) + tableName := acc.TestClient().Ids.Alpha() + + // not cleaning up, because the schema will be dropped with the database anyway + schema, _ := acc.TestClient().Schema.CreateSchemaInDatabase(t, database.ID()) + + var secondStepSchemaName string + if testCase.UseNewSchemaNameAfterRename { + secondStepSchemaName = newSchemaId.Name() + } else { + secondStepSchemaName = schema.ID().Name() + } + + resource.Test(t, resource.TestCase{ + ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories, + TerraformVersionChecks: []tfversion.TerraformVersionCheck{ + tfversion.RequireAbove(tfversion.Version1_5_0), + }, + CheckDestroy: acc.CheckDestroy(t, resources.Table), + Steps: []resource.TestStep{ + { + Config: configTableWithReferences(t, "", NoDependency, "", NoDependency, database.ID().Name(), schema.ID().Name(), tableName), + }, + { + PreConfig: func() { + acc.TestClient().Schema.Alter(t, schema.ID(), &sdk.AlterSchemaOptions{ + NewName: &newSchemaId, + }) + }, + Config: configTableWithReferences(t, "", NoDependency, "", NoDependency, database.ID().Name(), secondStepSchemaName, tableName), + ExpectError: testCase.ExpectedSecondStepError, + }, + }, + }) + }) + } +} + +func configSchemaWithReferences(t *testing.T, databaseReference string, databaseDependencyType DependencyType, databaseName string, schemaName string) string { + t.Helper() + switch databaseDependencyType { + case ImplicitDependency: + return fmt.Sprintf(` +resource "snowflake_schema" "test" { + database = %[1]s.name + name = "%[2]s" +} +`, databaseReference, schemaName) + case DependsOnDependency: + return fmt.Sprintf(` +resource "snowflake_schema" "test" { + depends_on = [%[1]s] + database = "%[2]s" + name = "%[3]s" +} +`, databaseReference, databaseName, schemaName) + case NoDependency: + return fmt.Sprintf(` +resource "snowflake_schema" "test" { + database = "%[1]s" + name = "%[2]s" +} +`, databaseName, schemaName) + default: + t.Fatalf("configSchemaWithReferences: unknown database reference type: %s", databaseDependencyType) + return "" + } +} + +func configTableWithReferences(t *testing.T, databaseReference string, databaseDependencyType DependencyType, schemaReference string, schemaDependencyType DependencyType, databaseName string, schemaName string, tableName string) string { + t.Helper() + builder := new(strings.Builder) + builder.WriteString("resource \"snowflake_table\" \"test\" {\n") + + dependsOn := make([]string, 0) + database := "" + schema := "" + + switch databaseDependencyType { + case ImplicitDependency: + database = fmt.Sprintf("%s.name", databaseReference) + case DependsOnDependency: + dependsOn = append(dependsOn, databaseReference) + database = strconv.Quote(databaseName) + case NoDependency: + database = strconv.Quote(databaseName) + } + + switch schemaDependencyType { + case ImplicitDependency: + schema = fmt.Sprintf("%s.name", schemaReference) + case DependsOnDependency: + dependsOn = append(dependsOn, schemaReference) + schema = strconv.Quote(schemaName) + case NoDependency: + schema = strconv.Quote(schemaName) + } + + if len(dependsOn) > 0 { + builder.WriteString(fmt.Sprintf("depends_on = [%s]\n", strings.Join(dependsOn, ", "))) + } + builder.WriteString(fmt.Sprintf("database = %s\n", database)) + builder.WriteString(fmt.Sprintf("schema = %s\n", schema)) + builder.WriteString(fmt.Sprintf("name = \"%s\"\n", tableName)) + builder.WriteString(` +column { + type = "NUMBER(38,0)" + name = "N" +} +`) + builder.WriteString(`}`) + return builder.String() +} diff --git a/pkg/sdk/testint/errors_integration_test.go b/pkg/sdk/testint/errors_integration_test.go new file mode 100644 index 0000000000..4d01e1f44c --- /dev/null +++ b/pkg/sdk/testint/errors_integration_test.go @@ -0,0 +1,146 @@ +package testint + +import ( + "context" + "testing" + + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk" + "github.com/stretchr/testify/assert" +) + +func schemaObjectShowByIDWrapper[T any](showByIdFn func(context.Context, sdk.SchemaObjectIdentifier) (*T, error)) func(context.Context, sdk.SchemaObjectIdentifier) error { + return func(ctx context.Context, id sdk.SchemaObjectIdentifier) error { + _, err := showByIdFn(ctx, id) + return err + } +} + +func schemaObjectWithArgumentsShowByIDWrapper[T any](showByIdFn func(context.Context, sdk.SchemaObjectIdentifierWithArguments) (*T, error)) func(context.Context, sdk.SchemaObjectIdentifierWithArguments) error { + return func(ctx context.Context, id sdk.SchemaObjectIdentifierWithArguments) error { + _, err := showByIdFn(ctx, id) + return err + } +} + +func TestInt_ShowSchemaObjectInNonExistingDatabase(t *testing.T) { + doesNotExistOrNotAuthorized := sdk.ErrObjectNotExistOrAuthorized.Error() // Database '\"non-existing-database\"' does not exist or not authorized + doesNotExistOrOperationCannotBePerformed := "Object does not exist, or operation cannot be performed" + + testCases := []struct { + ObjectType sdk.ObjectType + ExpectedErr string + ShowFn func(context.Context, sdk.SchemaObjectIdentifier) error + }{ + // Only object types that use IN SCHEMA in their ShowByID implementation + {ObjectType: sdk.ObjectTypeTable, ExpectedErr: doesNotExistOrOperationCannotBePerformed, ShowFn: schemaObjectShowByIDWrapper(testClient(t).Tables.ShowByID)}, + {ObjectType: sdk.ObjectTypeDynamicTable, ExpectedErr: doesNotExistOrOperationCannotBePerformed, ShowFn: schemaObjectShowByIDWrapper(testClient(t).DynamicTables.ShowByID)}, + {ObjectType: sdk.ObjectTypeCortexSearchService, ExpectedErr: doesNotExistOrNotAuthorized, ShowFn: schemaObjectShowByIDWrapper(testClient(t).CortexSearchServices.ShowByID)}, + {ObjectType: sdk.ObjectTypeExternalTable, ExpectedErr: doesNotExistOrOperationCannotBePerformed, ShowFn: schemaObjectShowByIDWrapper(testClient(t).ExternalTables.ShowByID)}, + {ObjectType: sdk.ObjectTypeEventTable, ExpectedErr: doesNotExistOrOperationCannotBePerformed, ShowFn: schemaObjectShowByIDWrapper(testClient(t).EventTables.ShowByID)}, + {ObjectType: sdk.ObjectTypeView, ExpectedErr: doesNotExistOrOperationCannotBePerformed, ShowFn: schemaObjectShowByIDWrapper(testClient(t).Views.ShowByID)}, + {ObjectType: sdk.ObjectTypeMaterializedView, ExpectedErr: doesNotExistOrNotAuthorized, ShowFn: schemaObjectShowByIDWrapper(testClient(t).MaterializedViews.ShowByID)}, + {ObjectType: sdk.ObjectTypeSequence, ExpectedErr: doesNotExistOrNotAuthorized, ShowFn: schemaObjectShowByIDWrapper(testClient(t).Sequences.ShowByID)}, + {ObjectType: sdk.ObjectTypeStream, ExpectedErr: doesNotExistOrNotAuthorized, ShowFn: schemaObjectShowByIDWrapper(testClient(t).Streams.ShowByID)}, + {ObjectType: sdk.ObjectTypeTask, ExpectedErr: doesNotExistOrNotAuthorized, ShowFn: schemaObjectShowByIDWrapper(testClient(t).Tasks.ShowByID)}, + {ObjectType: sdk.ObjectTypeMaskingPolicy, ExpectedErr: doesNotExistOrNotAuthorized, ShowFn: schemaObjectShowByIDWrapper(testClient(t).MaskingPolicies.ShowByID)}, + {ObjectType: sdk.ObjectTypeRowAccessPolicy, ExpectedErr: doesNotExistOrNotAuthorized, ShowFn: schemaObjectShowByIDWrapper(testClient(t).RowAccessPolicies.ShowByID)}, + {ObjectType: sdk.ObjectTypeTag, ExpectedErr: doesNotExistOrNotAuthorized, ShowFn: schemaObjectShowByIDWrapper(testClient(t).Tags.ShowByID)}, + {ObjectType: sdk.ObjectTypeSecret, ExpectedErr: doesNotExistOrNotAuthorized, ShowFn: schemaObjectShowByIDWrapper(testClient(t).Secrets.ShowByID)}, + {ObjectType: sdk.ObjectTypeStage, ExpectedErr: doesNotExistOrOperationCannotBePerformed, ShowFn: schemaObjectShowByIDWrapper(testClient(t).Stages.ShowByID)}, + {ObjectType: sdk.ObjectTypeFileFormat, ExpectedErr: doesNotExistOrOperationCannotBePerformed, ShowFn: schemaObjectShowByIDWrapper(testClient(t).FileFormats.ShowByID)}, + {ObjectType: sdk.ObjectTypePipe, ExpectedErr: doesNotExistOrNotAuthorized, ShowFn: schemaObjectShowByIDWrapper(testClient(t).Pipes.ShowByID)}, + {ObjectType: sdk.ObjectTypeAlert, ExpectedErr: doesNotExistOrNotAuthorized, ShowFn: schemaObjectShowByIDWrapper(testClient(t).Alerts.ShowByID)}, + {ObjectType: sdk.ObjectTypeStreamlit, ExpectedErr: doesNotExistOrOperationCannotBePerformed, ShowFn: schemaObjectShowByIDWrapper(testClient(t).Streamlits.ShowByID)}, + {ObjectType: sdk.ObjectTypeNetworkRule, ExpectedErr: doesNotExistOrOperationCannotBePerformed, ShowFn: schemaObjectShowByIDWrapper(testClient(t).NetworkRules.ShowByID)}, + {ObjectType: sdk.ObjectTypeAuthenticationPolicy, ExpectedErr: doesNotExistOrNotAuthorized, ShowFn: schemaObjectShowByIDWrapper(testClient(t).AuthenticationPolicies.ShowByID)}, + } + + for _, tt := range testCases { + t.Run(tt.ObjectType.String(), func(t *testing.T) { + ctx := context.Background() + err := tt.ShowFn(ctx, sdk.NewSchemaObjectIdentifier("non-existing-database", "non-existing-schema", "non-existing-schema-object")) + assert.ErrorContains(t, err, tt.ExpectedErr) + }) + } + + schemaObjectWithArgumentsTestCases := []struct { + ObjectType sdk.ObjectType + ExpectedErr string + ShowFn func(context.Context, sdk.SchemaObjectIdentifierWithArguments) error + }{ + {ObjectType: sdk.ObjectTypeFunction, ExpectedErr: doesNotExistOrNotAuthorized, ShowFn: schemaObjectWithArgumentsShowByIDWrapper(testClient(t).Functions.ShowByID)}, + {ObjectType: sdk.ObjectTypeExternalFunction, ExpectedErr: doesNotExistOrNotAuthorized, ShowFn: schemaObjectWithArgumentsShowByIDWrapper(testClient(t).ExternalFunctions.ShowByID)}, + {ObjectType: sdk.ObjectTypeProcedure, ExpectedErr: doesNotExistOrNotAuthorized, ShowFn: schemaObjectWithArgumentsShowByIDWrapper(testClient(t).Procedures.ShowByID)}, + } + + for _, tt := range schemaObjectWithArgumentsTestCases { + t.Run(tt.ObjectType.String(), func(t *testing.T) { + ctx := context.Background() + err := tt.ShowFn(ctx, sdk.NewSchemaObjectIdentifierWithArguments("non-existing-database", "non-existing-schema", "non-existing-schema-object")) + assert.ErrorContains(t, err, tt.ExpectedErr) + }) + } +} + +func TestInt_ShowSchemaObjectInNonExistingSchema(t *testing.T) { + doesNotExistOrNotAuthorized := sdk.ErrObjectNotExistOrAuthorized.Error() // Schema '\"non-existing-schema\"' does not exist or not authorized + doesNotExistOrOperationCannotBePerformed := "Object does not exist, or operation cannot be performed" + + database, databaseCleanup := testClientHelper().Database.CreateDatabase(t) + t.Cleanup(databaseCleanup) + + testCases := []struct { + ObjectType sdk.ObjectType + ExpectedErr string + ShowFn func(context.Context, sdk.SchemaObjectIdentifier) error + }{ + // Only object types that use IN SCHEMA in their ShowByID implementation + {ObjectType: sdk.ObjectTypeTable, ExpectedErr: doesNotExistOrOperationCannotBePerformed, ShowFn: schemaObjectShowByIDWrapper(testClient(t).Tables.ShowByID)}, + {ObjectType: sdk.ObjectTypeDynamicTable, ExpectedErr: doesNotExistOrOperationCannotBePerformed, ShowFn: schemaObjectShowByIDWrapper(testClient(t).DynamicTables.ShowByID)}, + {ObjectType: sdk.ObjectTypeCortexSearchService, ExpectedErr: doesNotExistOrNotAuthorized, ShowFn: schemaObjectShowByIDWrapper(testClient(t).CortexSearchServices.ShowByID)}, + {ObjectType: sdk.ObjectTypeExternalTable, ExpectedErr: doesNotExistOrOperationCannotBePerformed, ShowFn: schemaObjectShowByIDWrapper(testClient(t).ExternalTables.ShowByID)}, + {ObjectType: sdk.ObjectTypeEventTable, ExpectedErr: doesNotExistOrOperationCannotBePerformed, ShowFn: schemaObjectShowByIDWrapper(testClient(t).EventTables.ShowByID)}, + {ObjectType: sdk.ObjectTypeView, ExpectedErr: doesNotExistOrOperationCannotBePerformed, ShowFn: schemaObjectShowByIDWrapper(testClient(t).Views.ShowByID)}, + {ObjectType: sdk.ObjectTypeMaterializedView, ExpectedErr: doesNotExistOrNotAuthorized, ShowFn: schemaObjectShowByIDWrapper(testClient(t).MaterializedViews.ShowByID)}, + {ObjectType: sdk.ObjectTypeSequence, ExpectedErr: doesNotExistOrNotAuthorized, ShowFn: schemaObjectShowByIDWrapper(testClient(t).Sequences.ShowByID)}, + {ObjectType: sdk.ObjectTypeStream, ExpectedErr: doesNotExistOrNotAuthorized, ShowFn: schemaObjectShowByIDWrapper(testClient(t).Streams.ShowByID)}, + {ObjectType: sdk.ObjectTypeTask, ExpectedErr: doesNotExistOrNotAuthorized, ShowFn: schemaObjectShowByIDWrapper(testClient(t).Tasks.ShowByID)}, + {ObjectType: sdk.ObjectTypeMaskingPolicy, ExpectedErr: doesNotExistOrNotAuthorized, ShowFn: schemaObjectShowByIDWrapper(testClient(t).MaskingPolicies.ShowByID)}, + {ObjectType: sdk.ObjectTypeRowAccessPolicy, ExpectedErr: doesNotExistOrNotAuthorized, ShowFn: schemaObjectShowByIDWrapper(testClient(t).RowAccessPolicies.ShowByID)}, + {ObjectType: sdk.ObjectTypeTag, ExpectedErr: doesNotExistOrNotAuthorized, ShowFn: schemaObjectShowByIDWrapper(testClient(t).Tags.ShowByID)}, + {ObjectType: sdk.ObjectTypeSecret, ExpectedErr: doesNotExistOrNotAuthorized, ShowFn: schemaObjectShowByIDWrapper(testClient(t).Secrets.ShowByID)}, + {ObjectType: sdk.ObjectTypeStage, ExpectedErr: doesNotExistOrOperationCannotBePerformed, ShowFn: schemaObjectShowByIDWrapper(testClient(t).Stages.ShowByID)}, + {ObjectType: sdk.ObjectTypeFileFormat, ExpectedErr: doesNotExistOrOperationCannotBePerformed, ShowFn: schemaObjectShowByIDWrapper(testClient(t).FileFormats.ShowByID)}, + {ObjectType: sdk.ObjectTypePipe, ExpectedErr: doesNotExistOrNotAuthorized, ShowFn: schemaObjectShowByIDWrapper(testClient(t).Pipes.ShowByID)}, + {ObjectType: sdk.ObjectTypeAlert, ExpectedErr: doesNotExistOrNotAuthorized, ShowFn: schemaObjectShowByIDWrapper(testClient(t).Alerts.ShowByID)}, + {ObjectType: sdk.ObjectTypeStreamlit, ExpectedErr: doesNotExistOrOperationCannotBePerformed, ShowFn: schemaObjectShowByIDWrapper(testClient(t).Streamlits.ShowByID)}, + {ObjectType: sdk.ObjectTypeNetworkRule, ExpectedErr: doesNotExistOrOperationCannotBePerformed, ShowFn: schemaObjectShowByIDWrapper(testClient(t).NetworkRules.ShowByID)}, + {ObjectType: sdk.ObjectTypeAuthenticationPolicy, ExpectedErr: doesNotExistOrNotAuthorized, ShowFn: schemaObjectShowByIDWrapper(testClient(t).AuthenticationPolicies.ShowByID)}, + } + + for _, tt := range testCases { + t.Run(tt.ObjectType.String(), func(t *testing.T) { + ctx := context.Background() + err := tt.ShowFn(ctx, sdk.NewSchemaObjectIdentifier(database.ID().Name(), "non-existing-schema", "non-existing-schema-object")) + assert.ErrorContains(t, err, tt.ExpectedErr) + }) + } + + schemaObjectWithArgumentsTestCases := []struct { + ObjectType sdk.ObjectType + ExpectedErr string + ShowFn func(context.Context, sdk.SchemaObjectIdentifierWithArguments) error + }{ + {ObjectType: sdk.ObjectTypeFunction, ExpectedErr: doesNotExistOrNotAuthorized, ShowFn: schemaObjectWithArgumentsShowByIDWrapper(testClient(t).Functions.ShowByID)}, + {ObjectType: sdk.ObjectTypeExternalFunction, ExpectedErr: doesNotExistOrNotAuthorized, ShowFn: schemaObjectWithArgumentsShowByIDWrapper(testClient(t).ExternalFunctions.ShowByID)}, + {ObjectType: sdk.ObjectTypeProcedure, ExpectedErr: doesNotExistOrNotAuthorized, ShowFn: schemaObjectWithArgumentsShowByIDWrapper(testClient(t).Procedures.ShowByID)}, + } + + for _, tt := range schemaObjectWithArgumentsTestCases { + t.Run(tt.ObjectType.String(), func(t *testing.T) { + ctx := context.Background() + err := tt.ShowFn(ctx, sdk.NewSchemaObjectIdentifierWithArguments(database.ID().Name(), "non-existing-schema", "non-existing-schema-object")) + assert.ErrorContains(t, err, tt.ExpectedErr) + }) + } +}